|
50 | 50 | #include <validationinterface.h>
|
51 | 51 | #include <warnings.h>
|
52 | 52 |
|
| 53 | +#include <numeric> |
53 | 54 | #include <optional>
|
54 | 55 | #include <string>
|
55 | 56 |
|
@@ -477,6 +478,13 @@ class MemPoolAccept
|
477 | 478 | // Single transaction acceptance
|
478 | 479 | MempoolAcceptResult AcceptSingleTransaction(const CTransactionRef& ptx, ATMPArgs& args) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
479 | 480 |
|
| 481 | + /** |
| 482 | + * Multiple transaction acceptance. Transactions may or may not be interdependent, |
| 483 | + * but must not conflict with each other. Parents must come before children if any |
| 484 | + * dependencies exist, otherwise a TX_MISSING_INPUTS error will be returned. |
| 485 | + */ |
| 486 | + PackageMempoolAcceptResult AcceptMultipleTransactions(const std::vector<CTransactionRef>& txns, ATMPArgs& args) EXCLUSIVE_LOCKS_REQUIRED(cs_main); |
| 487 | + |
480 | 488 | private:
|
481 | 489 | // All the intermediate state that gets passed between the various levels
|
482 | 490 | // of checking a given transaction.
|
@@ -1064,6 +1072,76 @@ MempoolAcceptResult MemPoolAccept::AcceptSingleTransaction(const CTransactionRef
|
1064 | 1072 | return MempoolAcceptResult::Success(std::move(ws.m_replaced_transactions), ws.m_base_fees);
|
1065 | 1073 | }
|
1066 | 1074 |
|
| 1075 | +PackageMempoolAcceptResult MemPoolAccept::AcceptMultipleTransactions(const std::vector<CTransactionRef>& txns, ATMPArgs& args) |
| 1076 | +{ |
| 1077 | + AssertLockHeld(cs_main); |
| 1078 | + |
| 1079 | + PackageValidationState package_state; |
| 1080 | + const unsigned int package_count = txns.size(); |
| 1081 | + |
| 1082 | + std::vector<Workspace> workspaces{}; |
| 1083 | + workspaces.reserve(package_count); |
| 1084 | + std::transform(txns.cbegin(), txns.cend(), std::back_inserter(workspaces), [](const auto& tx) { |
| 1085 | + return Workspace(tx); |
| 1086 | + }); |
| 1087 | + |
| 1088 | + std::map<const uint256, const MempoolAcceptResult> results; |
| 1089 | + { |
| 1090 | + // Don't allow any conflicting transactions, i.e. spending the same inputs, in a package. |
| 1091 | + std::unordered_set<COutPoint, SaltedOutpointHasher> inputs_seen; |
| 1092 | + for (const auto& tx : txns) { |
| 1093 | + for (const auto& input : tx->vin) { |
| 1094 | + if (inputs_seen.find(input.prevout) != inputs_seen.end()) { |
| 1095 | + // This input is also present in another tx in the package. |
| 1096 | + package_state.Invalid(PackageValidationResult::PCKG_POLICY, "conflict-in-package"); |
| 1097 | + return PackageMempoolAcceptResult(package_state, {}); |
| 1098 | + } |
| 1099 | + } |
| 1100 | + // Batch-add all the inputs for a tx at a time. If we added them 1 at a time, we could |
| 1101 | + // catch duplicate inputs within a single tx. This is a more severe, consensus error, |
| 1102 | + // and we want to report that from CheckTransaction instead. |
| 1103 | + std::transform(tx->vin.cbegin(), tx->vin.cend(), std::inserter(inputs_seen, inputs_seen.end()), |
| 1104 | + [](const auto& input) { return input.prevout; }); |
| 1105 | + } |
| 1106 | + } |
| 1107 | + |
| 1108 | + LOCK(m_pool.cs); |
| 1109 | + |
| 1110 | + // Do all PreChecks first and fail fast to avoid running expensive script checks when unnecessary. |
| 1111 | + for (Workspace& ws : workspaces) { |
| 1112 | + if (!PreChecks(args, ws)) { |
| 1113 | + package_state.Invalid(PackageValidationResult::PCKG_TX, "transaction failed"); |
| 1114 | + // Exit early to avoid doing pointless work. Update the failed tx result; the rest are unfinished. |
| 1115 | + results.emplace(ws.m_ptx->GetWitnessHash(), MempoolAcceptResult::Failure(ws.m_state)); |
| 1116 | + return PackageMempoolAcceptResult(package_state, std::move(results)); |
| 1117 | + } |
| 1118 | + // Make the coins created by this transaction available for subsequent transactions in the |
| 1119 | + // package to spend. Since we already checked conflicts in the package and RBFs are |
| 1120 | + // impossible, we don't need to track the coins spent. Note that this logic will need to be |
| 1121 | + // updated if RBFs in packages are allowed in the future. |
| 1122 | + assert(args.disallow_mempool_conflicts); |
| 1123 | + m_viewmempool.PackageAddTransaction(ws.m_ptx); |
| 1124 | + } |
| 1125 | + |
| 1126 | + for (Workspace& ws : workspaces) { |
| 1127 | + PrecomputedTransactionData txdata; |
| 1128 | + if (!PolicyScriptChecks(args, ws, txdata)) { |
| 1129 | + // Exit early to avoid doing pointless work. Update the failed tx result; the rest are unfinished. |
| 1130 | + package_state.Invalid(PackageValidationResult::PCKG_TX, "transaction failed"); |
| 1131 | + results.emplace(ws.m_ptx->GetWitnessHash(), MempoolAcceptResult::Failure(ws.m_state)); |
| 1132 | + return PackageMempoolAcceptResult(package_state, std::move(results)); |
| 1133 | + } |
| 1134 | + if (args.m_test_accept) { |
| 1135 | + // When test_accept=true, transactions that pass PolicyScriptChecks are valid because there are |
| 1136 | + // no further mempool checks (passing PolicyScriptChecks implies passing ConsensusScriptChecks). |
| 1137 | + results.emplace(ws.m_ptx->GetWitnessHash(), |
| 1138 | + MempoolAcceptResult::Success(std::move(ws.m_replaced_transactions), ws.m_base_fees)); |
| 1139 | + } |
| 1140 | + } |
| 1141 | + |
| 1142 | + return PackageMempoolAcceptResult(package_state, std::move(results)); |
| 1143 | +} |
| 1144 | + |
1067 | 1145 | } // anon namespace
|
1068 | 1146 |
|
1069 | 1147 | /** (try to) add transaction to memory pool with a specified acceptance time **/
|
@@ -1101,6 +1179,29 @@ MempoolAcceptResult AcceptToMemoryPool(CChainState& active_chainstate, CTxMemPoo
|
1101 | 1179 | return AcceptToMemoryPoolWithTime(Params(), pool, active_chainstate, tx, GetTime(), bypass_limits, test_accept);
|
1102 | 1180 | }
|
1103 | 1181 |
|
| 1182 | +PackageMempoolAcceptResult ProcessNewPackage(CChainState& active_chainstate, CTxMemPool& pool, |
| 1183 | + const Package& package, bool test_accept) |
| 1184 | +{ |
| 1185 | + AssertLockHeld(cs_main); |
| 1186 | + assert(test_accept); // Only allow package accept dry-runs (testmempoolaccept RPC). |
| 1187 | + assert(!package.empty()); |
| 1188 | + assert(std::all_of(package.cbegin(), package.cend(), [](const auto& tx){return tx != nullptr;})); |
| 1189 | + |
| 1190 | + std::vector<COutPoint> coins_to_uncache; |
| 1191 | + const CChainParams& chainparams = Params(); |
| 1192 | + MemPoolAccept::ATMPArgs args { chainparams, GetTime(), /* bypass_limits */ false, coins_to_uncache, |
| 1193 | + test_accept, /* disallow_mempool_conflicts */ true }; |
| 1194 | + assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate)); |
| 1195 | + const PackageMempoolAcceptResult result = MemPoolAccept(pool, active_chainstate).AcceptMultipleTransactions(package, args); |
| 1196 | + |
| 1197 | + // Uncache coins pertaining to transactions that were not submitted to the mempool. |
| 1198 | + // Ensure the cache is still within its size limits. |
| 1199 | + for (const COutPoint& hashTx : coins_to_uncache) { |
| 1200 | + active_chainstate.CoinsTip().Uncache(hashTx); |
| 1201 | + } |
| 1202 | + return result; |
| 1203 | +} |
| 1204 | + |
1104 | 1205 | CTransactionRef GetTransaction(const CBlockIndex* const block_index, const CTxMemPool* const mempool, const uint256& hash, const Consensus::Params& consensusParams, uint256& hashBlock)
|
1105 | 1206 | {
|
1106 | 1207 | LOCK(cs_main);
|
|
0 commit comments