@@ -451,6 +451,11 @@ class MemPoolAccept
451
451
* any transaction spending the same inputs as a transaction in the mempool is considered
452
452
* a conflict. */
453
453
const bool m_allow_bip125_replacement;
454
+ /* * When true, the mempool will not be trimmed when individual transactions are submitted in
455
+ * Finalize(). Instead, limits should be enforced at the end to ensure the package is not
456
+ * partially submitted.
457
+ */
458
+ const bool m_package_submission;
454
459
455
460
/* * Parameters for single transaction mempool validation. */
456
461
static ATMPArgs SingleAccept (const CChainParams& chainparams, int64_t accept_time,
@@ -462,6 +467,7 @@ class MemPoolAccept
462
467
/* m_coins_to_uncache */ coins_to_uncache,
463
468
/* m_test_accept */ test_accept,
464
469
/* m_allow_bip125_replacement */ true ,
470
+ /* m_package_submission */ false ,
465
471
};
466
472
}
467
473
@@ -474,6 +480,7 @@ class MemPoolAccept
474
480
/* m_coins_to_uncache */ coins_to_uncache,
475
481
/* m_test_accept */ true ,
476
482
/* m_allow_bip125_replacement */ false ,
483
+ /* m_package_submission */ false , // not submitting to mempool
477
484
};
478
485
}
479
486
@@ -486,6 +493,7 @@ class MemPoolAccept
486
493
/* m_coins_to_uncache */ coins_to_uncache,
487
494
/* m_test_accept */ false ,
488
495
/* m_allow_bip125_replacement */ false ,
496
+ /* m_package_submission */ true ,
489
497
};
490
498
}
491
499
// No default ctor to avoid exposing details to clients and allowing the possibility of
@@ -497,9 +505,9 @@ class MemPoolAccept
497
505
MempoolAcceptResult AcceptSingleTransaction (const CTransactionRef& ptx, ATMPArgs& args) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
498
506
499
507
/* *
500
- * Multiple transaction acceptance. Transactions may or may not be interdependent,
501
- * but must not conflict with each other. Parents must come before children if any
502
- * dependencies exist.
508
+ * Multiple transaction acceptance. Transactions may or may not be interdependent, but must not
509
+ * conflict with each other, and the transactions cannot already be in the mempool. Parents must
510
+ * come before children if any dependencies exist.
503
511
*/
504
512
PackageMempoolAcceptResult AcceptMultipleTransactions (const std::vector<CTransactionRef>& txns, ATMPArgs& args) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
505
513
@@ -581,6 +589,14 @@ class MemPoolAccept
581
589
// limiting is performed, false otherwise.
582
590
bool Finalize (const ATMPArgs& args, Workspace& ws) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs);
583
591
592
+ // Submit all transactions to the mempool and call ConsensusScriptChecks to add to the script
593
+ // cache - should only be called after successful validation of all transactions in the package.
594
+ // The package may end up partially-submitted after size limitting; returns true if all
595
+ // transactions are successfully added to the mempool, false otherwise.
596
+ bool FinalizePackage (const ATMPArgs& args, std::vector<Workspace>& workspaces, PackageValidationState& package_state,
597
+ std::map<const uint256, const MempoolAcceptResult>& results)
598
+ EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs);
599
+
584
600
// Compare a package's feerate against minimum allowed.
585
601
bool CheckFeeRate (size_t package_size, CAmount package_fee, TxValidationState& state) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs)
586
602
{
@@ -992,20 +1008,88 @@ bool MemPoolAccept::Finalize(const ATMPArgs& args, Workspace& ws)
992
1008
// - it's not being re-added during a reorg which bypasses typical mempool fee limits
993
1009
// - the node is not behind
994
1010
// - the transaction is not dependent on any other transactions in the mempool
995
- bool validForFeeEstimation = !bypass_limits && IsCurrentForFeeEstimation (m_active_chainstate) && m_pool.HasNoInputsOf (tx);
1011
+ // - it's not part of a package. Since package relay is not currently supported, this
1012
+ // transaction has not necessarily been accepted to miners' mempools.
1013
+ bool validForFeeEstimation = !bypass_limits && !args.m_package_submission && IsCurrentForFeeEstimation (m_active_chainstate) && m_pool.HasNoInputsOf (tx);
996
1014
997
1015
// Store transaction in memory
998
1016
m_pool.addUnchecked (*entry, ws.m_ancestors , validForFeeEstimation);
999
1017
1000
1018
// trim mempool and check if tx was trimmed
1001
- if (!bypass_limits) {
1019
+ // If we are validating a package, don't trim here because we could evict a previous transaction
1020
+ // in the package. LimitMempoolSize() should be called at the very end to make sure the mempool
1021
+ // is still within limits and package submission happens atomically.
1022
+ if (!args.m_package_submission && !bypass_limits) {
1002
1023
LimitMempoolSize (m_pool, m_active_chainstate.CoinsTip (), gArgs .GetIntArg (" -maxmempool" , DEFAULT_MAX_MEMPOOL_SIZE) * 1000000 , std::chrono::hours{gArgs .GetIntArg (" -mempoolexpiry" , DEFAULT_MEMPOOL_EXPIRY)});
1003
1024
if (!m_pool.exists (GenTxid::Txid (hash)))
1004
1025
return state.Invalid (TxValidationResult::TX_MEMPOOL_POLICY, " mempool full" );
1005
1026
}
1006
1027
return true ;
1007
1028
}
1008
1029
1030
+ bool MemPoolAccept::FinalizePackage (const ATMPArgs& args, std::vector<Workspace>& workspaces,
1031
+ PackageValidationState& package_state,
1032
+ std::map<const uint256, const MempoolAcceptResult>& results)
1033
+ {
1034
+ AssertLockHeld (cs_main);
1035
+ AssertLockHeld (m_pool.cs );
1036
+ bool all_submitted = true ;
1037
+ // ConsensusScriptChecks adds to the script cache and is therefore consensus-critical;
1038
+ // CheckInputsFromMempoolAndCache asserts that transactions only spend coins available from the
1039
+ // mempool or UTXO set. Submit each transaction to the mempool immediately after calling
1040
+ // ConsensusScriptChecks to make the outputs available for subsequent transactions.
1041
+ for (Workspace& ws : workspaces) {
1042
+ if (!ConsensusScriptChecks (args, ws)) {
1043
+ results.emplace (ws.m_ptx ->GetWitnessHash (), MempoolAcceptResult::Failure (ws.m_state ));
1044
+ // Since PolicyScriptChecks() passed, this should never fail.
1045
+ all_submitted = Assume (false );
1046
+ }
1047
+
1048
+ // Re-calculate mempool ancestors to call addUnchecked(). They may have changed since the
1049
+ // last calculation done in PreChecks, since package ancestors have already been submitted.
1050
+ std::string err_string;
1051
+ if (!m_pool.CalculateMemPoolAncestors (*ws.m_entry , ws.m_ancestors , m_limit_ancestors,
1052
+ m_limit_ancestor_size, m_limit_descendants,
1053
+ m_limit_descendant_size, err_string)) {
1054
+ results.emplace (ws.m_ptx ->GetWitnessHash (), MempoolAcceptResult::Failure (ws.m_state ));
1055
+ // Since PreChecks() and PackageMempoolChecks() both enforce limits, this should never fail.
1056
+ all_submitted = Assume (false );
1057
+ }
1058
+ // If we call LimitMempoolSize() for each individual Finalize(), the mempool will not take
1059
+ // the transaction's descendant feerate into account because it hasn't seen them yet. Also,
1060
+ // we risk evicting a transaction that a subsequent package transaction depends on. Instead,
1061
+ // allow the mempool to temporarily bypass limits, the maximum package size) while
1062
+ // submitting transactions individually and then trim at the very end.
1063
+ if (!Finalize (args, ws)) {
1064
+ results.emplace (ws.m_ptx ->GetWitnessHash (), MempoolAcceptResult::Failure (ws.m_state ));
1065
+ // Since LimitMempoolSize() won't be called, this should never fail.
1066
+ all_submitted = Assume (false );
1067
+ }
1068
+ }
1069
+
1070
+ // It may or may not be the case that all the transactions made it into the mempool. Regardless,
1071
+ // make sure we haven't exceeded max mempool size.
1072
+ LimitMempoolSize (m_pool, m_active_chainstate.CoinsTip (),
1073
+ gArgs .GetIntArg (" -maxmempool" , DEFAULT_MAX_MEMPOOL_SIZE) * 1000000 ,
1074
+ std::chrono::hours{gArgs .GetIntArg (" -mempoolexpiry" , DEFAULT_MEMPOOL_EXPIRY)});
1075
+ if (!all_submitted) return false ;
1076
+
1077
+ // Find the wtxids of the transactions that made it into the mempool. Allow partial submission,
1078
+ // but don't report success unless they all made it into the mempool.
1079
+ for (Workspace& ws : workspaces) {
1080
+ if (m_pool.exists (GenTxid::Wtxid (ws.m_ptx ->GetWitnessHash ()))) {
1081
+ results.emplace (ws.m_ptx ->GetWitnessHash (),
1082
+ MempoolAcceptResult::Success (std::move (ws.m_replaced_transactions ), ws.m_vsize , ws.m_base_fees ));
1083
+ GetMainSignals ().TransactionAddedToMempool (ws.m_ptx , m_pool.GetAndIncrementSequence ());
1084
+ } else {
1085
+ all_submitted = false ;
1086
+ ws.m_state .Invalid (TxValidationResult::TX_MEMPOOL_POLICY, " mempool full" );
1087
+ results.emplace (ws.m_ptx ->GetWitnessHash (), MempoolAcceptResult::Failure (ws.m_state ));
1088
+ }
1089
+ }
1090
+ return all_submitted;
1091
+ }
1092
+
1009
1093
MempoolAcceptResult MemPoolAccept::AcceptSingleTransaction (const CTransactionRef& ptx, ATMPArgs& args)
1010
1094
{
1011
1095
AssertLockHeld (cs_main);
@@ -1091,6 +1175,13 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptMultipleTransactions(const std::
1091
1175
}
1092
1176
}
1093
1177
1178
+ if (args.m_test_accept ) return PackageMempoolAcceptResult (package_state, std::move (results));
1179
+
1180
+ if (!FinalizePackage (args, workspaces, package_state, results)) {
1181
+ package_state.Invalid (PackageValidationResult::PCKG_TX, " submission failed" );
1182
+ return PackageMempoolAcceptResult (package_state, std::move (results));
1183
+ }
1184
+
1094
1185
return PackageMempoolAcceptResult (package_state, std::move (results));
1095
1186
}
1096
1187
@@ -1187,7 +1278,6 @@ PackageMempoolAcceptResult ProcessNewPackage(CChainState& active_chainstate, CTx
1187
1278
const Package& package, bool test_accept)
1188
1279
{
1189
1280
AssertLockHeld (cs_main);
1190
- assert (test_accept); // Only allow package accept dry-runs (testmempoolaccept RPC).
1191
1281
assert (!package.empty ());
1192
1282
assert (std::all_of (package.cbegin (), package.cend (), [](const auto & tx){return tx != nullptr ;}));
1193
1283
@@ -1205,9 +1295,14 @@ PackageMempoolAcceptResult ProcessNewPackage(CChainState& active_chainstate, CTx
1205
1295
}();
1206
1296
1207
1297
// Uncache coins pertaining to transactions that were not submitted to the mempool.
1208
- for (const COutPoint& hashTx : coins_to_uncache) {
1209
- active_chainstate.CoinsTip ().Uncache (hashTx);
1298
+ // Ensure the coins cache is still within limits.
1299
+ if (test_accept || result.m_state .IsInvalid ()) {
1300
+ for (const COutPoint& hashTx : coins_to_uncache) {
1301
+ active_chainstate.CoinsTip ().Uncache (hashTx);
1302
+ }
1210
1303
}
1304
+ BlockValidationState state_dummy;
1305
+ active_chainstate.FlushStateToDisk (state_dummy, FlushStateMode::PERIODIC);
1211
1306
return result;
1212
1307
}
1213
1308
0 commit comments