diff --git a/src/bench/block_assemble.cpp b/src/bench/block_assemble.cpp --- a/src/bench/block_assemble.cpp +++ b/src/bench/block_assemble.cpp @@ -48,13 +48,11 @@ } { - // Required for ::AcceptToMemoryPool. LOCK(::cs_main); for (const auto &txr : txs) { - const MempoolAcceptResult res = ::AcceptToMemoryPool( - test_setup.m_node.chainman->ActiveChainstate(), config, - *test_setup.m_node.mempool, txr, false /* bypass_limits */); + const MempoolAcceptResult res = + test_setup.m_node.chainman->ProcessTransaction(txr); assert(res.m_result_type == MempoolAcceptResult::ResultType::VALID); } } diff --git a/src/consensus/validation.h b/src/consensus/validation.h --- a/src/consensus/validation.h +++ b/src/consensus/validation.h @@ -40,6 +40,8 @@ TX_CONFLICT, //! violated mempool's fee/size/descendant/etc limits TX_MEMPOOL_POLICY, + //! this node does not have a mempool so can't validate the transaction + TX_NO_MEMPOOL, }; /** diff --git a/src/net_processing.cpp b/src/net_processing.cpp --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -713,9 +713,9 @@ bool AlreadyHaveTx(const TxId &txid) EXCLUSIVE_LOCKS_REQUIRED(cs_main); /** - * Filter for transactions that were recently rejected by - * AcceptToMemoryPool. These are not rerequested until the chain tip - * changes, at which point the entire filter is reset. + * Filter for transactions that were recently rejected by the mempool. + * These are not rerequested until the chain tip changes, at which point + * the entire filter is reset. * * Without this filter we'd be re-requesting txs from each of our peers, * increasing bandwidth consumption considerably. For instance, with 100 @@ -2036,6 +2036,7 @@ case TxValidationResult::TX_PREMATURE_SPEND: case TxValidationResult::TX_CONFLICT: case TxValidationResult::TX_MEMPOOL_POLICY: + case TxValidationResult::TX_NO_MEMPOOL: break; } if (message != "") { @@ -3103,8 +3104,7 @@ } const MempoolAcceptResult result = - AcceptToMemoryPool(m_chainman.ActiveChainstate(), config, m_mempool, - porphanTx, false /* bypass_limits */); + m_chainman.ProcessTransaction(porphanTx); const TxValidationState &state = result.m_state; if (result.m_result_type == MempoolAcceptResult::ResultType::VALID) { LogPrint(BCLog::MEMPOOL, " accepted orphan tx %s\n", @@ -4363,10 +4363,9 @@ return; } - const MempoolAcceptResult result = - AcceptToMemoryPool(m_chainman.ActiveChainstate(), config, m_mempool, - ptx, false /* bypass_limits */); + const MempoolAcceptResult result = m_chainman.ProcessTransaction(ptx); const TxValidationState &state = result.m_state; + if (result.m_result_type == MempoolAcceptResult::ResultType::VALID) { CChainState &active_chainstate = m_chainman.ActiveChainstate(); m_mempool.check(active_chainstate.CoinsTip(), @@ -4461,8 +4460,8 @@ } // If a tx has been detected by recentRejects, we will have reached - // this point and the tx will have been ignored. Because we haven't run - // the tx through AcceptToMemoryPool, we won't have computed a DoS + // this point and the tx will have been ignored. Because we haven't + // submitted the tx to our mempool, we won't have computed a DoS // score for it or determined exactly why we consider it invalid. // // This means we won't penalize any peer subsequently relaying a DoSy diff --git a/src/node/transaction.cpp b/src/node/transaction.cpp --- a/src/node/transaction.cpp +++ b/src/node/transaction.cpp @@ -68,10 +68,8 @@ if (max_tx_fee > Amount::zero()) { // First, call ATMP with test_accept and check the fee. If ATMP // fails here, return error immediately. - const MempoolAcceptResult result = AcceptToMemoryPool( - node.chainman->ActiveChainstate(), config, *node.mempool, - tx, false /* bypass_limits */, - /* test_accept */ true); + const MempoolAcceptResult result = + node.chainman->ProcessTransaction(tx, /*test_accept=*/true); if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) { return HandleATMPError(result.m_state, err_string); @@ -80,9 +78,8 @@ } } // Try to submit the transaction to the mempool. - const MempoolAcceptResult result = AcceptToMemoryPool( - node.chainman->ActiveChainstate(), config, *node.mempool, tx, - false /* bypass_limits */); + const MempoolAcceptResult result = + node.chainman->ProcessTransaction(tx, /*test_accept=*/false); if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) { return HandleATMPError(result.m_state, err_string); diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -1168,7 +1168,8 @@ NodeContext &node = EnsureAnyNodeContext(request.context); CTxMemPool &mempool = EnsureMemPool(node); - CChainState &chainstate = EnsureChainman(node).ActiveChainstate(); + ChainstateManager &chainman = EnsureChainman(node); + CChainState &chainstate = chainman.ActiveChainstate(); const PackageMempoolAcceptResult package_result = [&] { LOCK(::cs_main); if (txns.size() > 1) { @@ -1177,9 +1178,8 @@ } return PackageMempoolAcceptResult( txns[0]->GetId(), - AcceptToMemoryPool(chainstate, config, mempool, txns[0], - /* bypass_limits */ false, - /* test_accept*/ true)); + chainman.ProcessTransaction(txns[0], + /* test_accept*/ true)); }(); UniValue rpc_result(UniValue::VARR); diff --git a/src/test/txvalidation_tests.cpp b/src/test/txvalidation_tests.cpp --- a/src/test/txvalidation_tests.cpp +++ b/src/test/txvalidation_tests.cpp @@ -39,9 +39,8 @@ LOCK(cs_main); unsigned int initialPoolSize = m_node.mempool->size(); - const MempoolAcceptResult result = AcceptToMemoryPool( - m_node.chainman->ActiveChainstate(), GetConfig(), *m_node.mempool, - MakeTransactionRef(coinbaseTx), false /* bypass_limits */); + const MempoolAcceptResult result = + m_node.chainman->ProcessTransaction(MakeTransactionRef(coinbaseTx)); BOOST_CHECK(result.m_result_type == MempoolAcceptResult::ResultType::INVALID); diff --git a/src/test/txvalidationcache_tests.cpp b/src/test/txvalidationcache_tests.cpp --- a/src/test/txvalidationcache_tests.cpp +++ b/src/test/txvalidationcache_tests.cpp @@ -32,9 +32,8 @@ const auto ToMemPool = [this](const CMutableTransaction &tx) { LOCK(cs_main); - const MempoolAcceptResult result = AcceptToMemoryPool( - m_node.chainman->ActiveChainstate(), GetConfig(), *m_node.mempool, - MakeTransactionRef(tx), false /* bypass_limits */); + const MempoolAcceptResult result = + m_node.chainman->ProcessTransaction(MakeTransactionRef(tx)); return result.m_result_type == MempoolAcceptResult::ResultType::VALID; }; diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp --- a/src/test/util/setup_common.cpp +++ b/src/test/util/setup_common.cpp @@ -364,10 +364,8 @@ // If submit=true, add transaction to the mempool. if (submit) { LOCK(cs_main); - const MempoolAcceptResult result = AcceptToMemoryPool( - m_node.chainman->ActiveChainstate(), GetConfig(), - *m_node.mempool.get(), MakeTransactionRef(mempool_txn), - /* bypass_limits */ false); + const MempoolAcceptResult result = m_node.chainman->ProcessTransaction( + MakeTransactionRef(mempool_txn)); assert(result.m_result_type == MempoolAcceptResult::ResultType::VALID); } diff --git a/src/test/validation_block_tests.cpp b/src/test/validation_block_tests.cpp --- a/src/test/validation_block_tests.cpp +++ b/src/test/validation_block_tests.cpp @@ -331,9 +331,7 @@ LOCK(cs_main); for (const auto &tx : txs) { const MempoolAcceptResult result = - AcceptToMemoryPool(m_node.chainman->ActiveChainstate(), - config, *m_node.mempool, tx, - /* bypass_limits */ false); + m_node.chainman->ProcessTransaction(tx); BOOST_REQUIRE_MESSAGE( result.m_result_type == MempoolAcceptResult::ResultType::VALID, diff --git a/src/validation.h b/src/validation.h --- a/src/validation.h +++ b/src/validation.h @@ -329,11 +329,22 @@ }; /** - * (try to) add transaction to memory pool + * Try to add a transaction to the mempool. This is an internal function and is + * exposed only for testing. Client code should use + * ChainstateManager::ProcessTransaction() * - * @param[in] bypass_limits When true, don't enforce mempool fee limits. - * @param[in] test_accept When true, run validation checks but don't submit - * to mempool. + * @param[in] active_chainstate Reference to the active chainstate. + * @param[in] pool Reference to the node's mempool. + * @param[in] config The global configuration. + * @param[in] tx The transaction to submit for mempool + * acceptance. + * @param[in] bypass_limits When true, don't enforce mempool fee and + * capacity limits. + * @param[in] test_accept When true, run validation checks but don't + * submit to mempool. + * + * @returns a MempoolAcceptResult indicating whether the transaction was + * accepted/rejected with reason. */ MempoolAcceptResult AcceptToMemoryPool(CChainState &active_chainstate, const Config &config, @@ -1379,6 +1390,18 @@ const CBlockIndex **ppindex = nullptr) LOCKS_EXCLUDED(cs_main); + /** + * Try to add a transaction to the memory pool. + * + * @param[in] tx The transaction to submit for mempool + * acceptance. + * @param[in] test_accept When true, run validation checks but don't + * submit to mempool. + */ + [[nodiscard]] MempoolAcceptResult + ProcessTransaction(const CTransactionRef &tx, bool test_accept = false) + EXCLUSIVE_LOCKS_REQUIRED(cs_main); + //! Load the block tree and coins database from disk, initializing state if //! we're running with -reindex bool LoadBlockIndex() EXCLUSIVE_LOCKS_REQUIRED(cs_main); diff --git a/src/validation.cpp b/src/validation.cpp --- a/src/validation.cpp +++ b/src/validation.cpp @@ -4274,6 +4274,24 @@ return true; } +MempoolAcceptResult +ChainstateManager::ProcessTransaction(const CTransactionRef &tx, + bool test_accept) { + CChainState &active_chainstate = ActiveChainstate(); + if (!active_chainstate.m_mempool) { + TxValidationState state; + state.Invalid(TxValidationResult::TX_NO_MEMPOOL, "no-mempool"); + return MempoolAcceptResult::Failure(state); + } + // Use GetConfig() temporarily. It will be removed in a follow-up by + // making AcceptToMemoryPool take a CChainParams instead of a Config. + // This avoids passing an extra Config argument to this function that will + // be removed soon. + return AcceptToMemoryPool(active_chainstate, ::GetConfig(), + *active_chainstate.m_mempool, tx, + /*bypass_limits=*/false, test_accept); +} + bool TestBlockValidity(BlockValidationState &state, const CChainParams ¶ms, CChainState &chainstate, const CBlock &block, CBlockIndex *pindexPrev,