aboutsummaryrefslogtreecommitdiff
path: root/src/validation.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/validation.cpp')
-rw-r--r--src/validation.cpp101
1 files changed, 101 insertions, 0 deletions
diff --git a/src/validation.cpp b/src/validation.cpp
index 9165a1f1b3..ad0f636554 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -50,6 +50,7 @@
#include <validationinterface.h>
#include <warnings.h>
+#include <numeric>
#include <optional>
#include <string>
@@ -477,6 +478,13 @@ public:
// Single transaction acceptance
MempoolAcceptResult AcceptSingleTransaction(const CTransactionRef& ptx, ATMPArgs& args) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ /**
+ * Multiple transaction acceptance. Transactions may or may not be interdependent,
+ * but must not conflict with each other. Parents must come before children if any
+ * dependencies exist, otherwise a TX_MISSING_INPUTS error will be returned.
+ */
+ PackageMempoolAcceptResult AcceptMultipleTransactions(const std::vector<CTransactionRef>& txns, ATMPArgs& args) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+
private:
// All the intermediate state that gets passed between the various levels
// of checking a given transaction.
@@ -1064,6 +1072,76 @@ MempoolAcceptResult MemPoolAccept::AcceptSingleTransaction(const CTransactionRef
return MempoolAcceptResult::Success(std::move(ws.m_replaced_transactions), ws.m_base_fees);
}
+PackageMempoolAcceptResult MemPoolAccept::AcceptMultipleTransactions(const std::vector<CTransactionRef>& txns, ATMPArgs& args)
+{
+ AssertLockHeld(cs_main);
+
+ PackageValidationState package_state;
+ const unsigned int package_count = txns.size();
+
+ std::vector<Workspace> workspaces{};
+ workspaces.reserve(package_count);
+ std::transform(txns.cbegin(), txns.cend(), std::back_inserter(workspaces), [](const auto& tx) {
+ return Workspace(tx);
+ });
+
+ std::map<const uint256, const MempoolAcceptResult> results;
+ {
+ // Don't allow any conflicting transactions, i.e. spending the same inputs, in a package.
+ std::unordered_set<COutPoint, SaltedOutpointHasher> inputs_seen;
+ for (const auto& tx : txns) {
+ for (const auto& input : tx->vin) {
+ if (inputs_seen.find(input.prevout) != inputs_seen.end()) {
+ // This input is also present in another tx in the package.
+ package_state.Invalid(PackageValidationResult::PCKG_POLICY, "conflict-in-package");
+ return PackageMempoolAcceptResult(package_state, {});
+ }
+ }
+ // Batch-add all the inputs for a tx at a time. If we added them 1 at a time, we could
+ // catch duplicate inputs within a single tx. This is a more severe, consensus error,
+ // and we want to report that from CheckTransaction instead.
+ std::transform(tx->vin.cbegin(), tx->vin.cend(), std::inserter(inputs_seen, inputs_seen.end()),
+ [](const auto& input) { return input.prevout; });
+ }
+ }
+
+ LOCK(m_pool.cs);
+
+ // Do all PreChecks first and fail fast to avoid running expensive script checks when unnecessary.
+ for (Workspace& ws : workspaces) {
+ if (!PreChecks(args, ws)) {
+ package_state.Invalid(PackageValidationResult::PCKG_TX, "transaction failed");
+ // Exit early to avoid doing pointless work. Update the failed tx result; the rest are unfinished.
+ results.emplace(ws.m_ptx->GetWitnessHash(), MempoolAcceptResult::Failure(ws.m_state));
+ return PackageMempoolAcceptResult(package_state, std::move(results));
+ }
+ // Make the coins created by this transaction available for subsequent transactions in the
+ // package to spend. Since we already checked conflicts in the package and RBFs are
+ // impossible, we don't need to track the coins spent. Note that this logic will need to be
+ // updated if RBFs in packages are allowed in the future.
+ assert(args.disallow_mempool_conflicts);
+ m_viewmempool.PackageAddTransaction(ws.m_ptx);
+ }
+
+ for (Workspace& ws : workspaces) {
+ PrecomputedTransactionData txdata;
+ if (!PolicyScriptChecks(args, ws, txdata)) {
+ // Exit early to avoid doing pointless work. Update the failed tx result; the rest are unfinished.
+ package_state.Invalid(PackageValidationResult::PCKG_TX, "transaction failed");
+ results.emplace(ws.m_ptx->GetWitnessHash(), MempoolAcceptResult::Failure(ws.m_state));
+ return PackageMempoolAcceptResult(package_state, std::move(results));
+ }
+ if (args.m_test_accept) {
+ // When test_accept=true, transactions that pass PolicyScriptChecks are valid because there are
+ // no further mempool checks (passing PolicyScriptChecks implies passing ConsensusScriptChecks).
+ results.emplace(ws.m_ptx->GetWitnessHash(),
+ MempoolAcceptResult::Success(std::move(ws.m_replaced_transactions), ws.m_base_fees));
+ }
+ }
+
+ return PackageMempoolAcceptResult(package_state, std::move(results));
+}
+
} // anon namespace
/** (try to) add transaction to memory pool with a specified acceptance time **/
@@ -1101,6 +1179,29 @@ MempoolAcceptResult AcceptToMemoryPool(CChainState& active_chainstate, CTxMemPoo
return AcceptToMemoryPoolWithTime(Params(), pool, active_chainstate, tx, GetTime(), bypass_limits, test_accept);
}
+PackageMempoolAcceptResult ProcessNewPackage(CChainState& active_chainstate, CTxMemPool& pool,
+ const Package& package, bool test_accept)
+{
+ AssertLockHeld(cs_main);
+ assert(test_accept); // Only allow package accept dry-runs (testmempoolaccept RPC).
+ assert(!package.empty());
+ assert(std::all_of(package.cbegin(), package.cend(), [](const auto& tx){return tx != nullptr;}));
+
+ std::vector<COutPoint> coins_to_uncache;
+ const CChainParams& chainparams = Params();
+ MemPoolAccept::ATMPArgs args { chainparams, GetTime(), /* bypass_limits */ false, coins_to_uncache,
+ test_accept, /* disallow_mempool_conflicts */ true };
+ assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate));
+ const PackageMempoolAcceptResult result = MemPoolAccept(pool, active_chainstate).AcceptMultipleTransactions(package, args);
+
+ // Uncache coins pertaining to transactions that were not submitted to the mempool.
+ // Ensure the cache is still within its size limits.
+ for (const COutPoint& hashTx : coins_to_uncache) {
+ active_chainstate.CoinsTip().Uncache(hashTx);
+ }
+ return result;
+}
+
CTransactionRef GetTransaction(const CBlockIndex* const block_index, const CTxMemPool* const mempool, const uint256& hash, const Consensus::Params& consensusParams, uint256& hashBlock)
{
LOCK(cs_main);