mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-01-18 22:35:39 +01:00
Merge #21062: refactor: return MempoolAcceptResult from ATMP
53e716ea11[refactor] improve style for touched code (gzhao408)174cb5330a[refactor] const ATMPArgs and non-const Workspace (gzhao408)f82baf0762[refactor] return MempoolAcceptResult (gzhao408)9db10a5506[refactor] clean up logic in testmempoolaccept (gzhao408) Pull request description: This is the first 4 commits of #20833, and does refactoring only. It should be relatively simple to review, and offers a few nice things: - It makes accessing values that don't make sense (e.g. fee) when the tx is invalid an error. - Returning `MempoolAcceptResult` from ATMP makes the interface cleaner. The caller can get a const instead of passing in a mutable "out" param. - We don't have to be iterating through a bunch of lists for package validation, we can just return a `std::vector<MempoolAcceptResult>`. - We don't have to refactor all ATMP call sites again if/when we want to return more stuff from it. ACKs for top commit: MarcoFalke: ACK53e716ea11💿 jnewbery: Code review ACK53e716ea11ariard: Code Review ACK53e716e, I did tweak a bit the touched paths to see if we had good test coverage. Didn't find holes. Tree-SHA512: fa6ec324a08ad9e6e55948615cda324cba176255708bf0a0a0f37cedb7a75311aa334ac6f223be7d8df3c7379502b1081102b9589f9a9afa1713ad3d9ab3c24f
This commit is contained in:
@@ -48,9 +48,8 @@ static void AssembleBlock(benchmark::Bench& bench)
|
||||
LOCK(::cs_main); // Required for ::AcceptToMemoryPool.
|
||||
|
||||
for (const auto& txr : txs) {
|
||||
TxValidationState state;
|
||||
bool ret{::AcceptToMemoryPool(*test_setup.m_node.mempool, state, txr, nullptr /* plTxnReplaced */, false /* bypass_limits */)};
|
||||
assert(ret);
|
||||
const MempoolAcceptResult res = ::AcceptToMemoryPool(*test_setup.m_node.mempool, txr, false /* bypass_limits */);
|
||||
assert(res.m_result_type == MempoolAcceptResult::ResultType::VALID);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2178,10 +2178,10 @@ void PeerManagerImpl::ProcessOrphanTx(std::set<uint256>& orphan_work_set)
|
||||
if (orphan_it == mapOrphanTransactions.end()) continue;
|
||||
|
||||
const CTransactionRef porphanTx = orphan_it->second.tx;
|
||||
TxValidationState state;
|
||||
std::list<CTransactionRef> removed_txn;
|
||||
const MempoolAcceptResult result = AcceptToMemoryPool(m_mempool, porphanTx, false /* bypass_limits */);
|
||||
const TxValidationState& state = result.m_state;
|
||||
|
||||
if (AcceptToMemoryPool(m_mempool, state, porphanTx, &removed_txn, false /* bypass_limits */)) {
|
||||
if (result.m_result_type == MempoolAcceptResult::ResultType::VALID) {
|
||||
LogPrint(BCLog::MEMPOOL, " accepted orphan tx %s\n", orphanHash.ToString());
|
||||
RelayTransaction(orphanHash, porphanTx->GetWitnessHash(), m_connman);
|
||||
for (unsigned int i = 0; i < porphanTx->vout.size(); i++) {
|
||||
@@ -2193,7 +2193,7 @@ void PeerManagerImpl::ProcessOrphanTx(std::set<uint256>& orphan_work_set)
|
||||
}
|
||||
}
|
||||
EraseOrphanTx(orphanHash);
|
||||
for (const CTransactionRef& removedTx : removed_txn) {
|
||||
for (const CTransactionRef& removedTx : result.m_replaced_transactions.value()) {
|
||||
AddToCompactExtraTransactions(removedTx);
|
||||
}
|
||||
break;
|
||||
@@ -3200,10 +3200,10 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
|
||||
return;
|
||||
}
|
||||
|
||||
TxValidationState state;
|
||||
std::list<CTransactionRef> lRemovedTxn;
|
||||
const MempoolAcceptResult result = AcceptToMemoryPool(m_mempool, ptx, false /* bypass_limits */);
|
||||
const TxValidationState& state = result.m_state;
|
||||
|
||||
if (AcceptToMemoryPool(m_mempool, state, ptx, &lRemovedTxn, false /* bypass_limits */)) {
|
||||
if (result.m_result_type == MempoolAcceptResult::ResultType::VALID) {
|
||||
m_mempool.check(&::ChainstateActive().CoinsTip());
|
||||
// As this version of the transaction was acceptable, we can forget about any
|
||||
// requests for it.
|
||||
@@ -3226,7 +3226,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
|
||||
tx.GetHash().ToString(),
|
||||
m_mempool.size(), m_mempool.DynamicMemoryUsage() / 1000);
|
||||
|
||||
for (const CTransactionRef& removedTx : lRemovedTxn) {
|
||||
for (const CTransactionRef& removedTx : result.m_replaced_transactions.value()) {
|
||||
AddToCompactExtraTransactions(removedTx);
|
||||
}
|
||||
|
||||
|
||||
@@ -50,22 +50,22 @@ TransactionError BroadcastTransaction(NodeContext& node, const CTransactionRef t
|
||||
}
|
||||
if (!node.mempool->exists(hashTx)) {
|
||||
// Transaction is not already in the mempool.
|
||||
TxValidationState state;
|
||||
if (max_tx_fee > 0) {
|
||||
// First, call ATMP with test_accept and check the fee. If ATMP
|
||||
// fails here, return error immediately.
|
||||
CAmount fee{0};
|
||||
if (!AcceptToMemoryPool(*node.mempool, state, tx,
|
||||
nullptr /* plTxnReplaced */, false /* bypass_limits */, /* test_accept */ true, &fee)) {
|
||||
return HandleATMPError(state, err_string);
|
||||
} else if (fee > max_tx_fee) {
|
||||
const MempoolAcceptResult result = AcceptToMemoryPool(*node.mempool, tx, false /* bypass_limits */,
|
||||
true /* test_accept */);
|
||||
if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) {
|
||||
return HandleATMPError(result.m_state, err_string);
|
||||
} else if (result.m_base_fees.value() > max_tx_fee) {
|
||||
return TransactionError::MAX_FEE_EXCEEDED;
|
||||
}
|
||||
}
|
||||
// Try to submit the transaction to the mempool.
|
||||
if (!AcceptToMemoryPool(*node.mempool, state, tx,
|
||||
nullptr /* plTxnReplaced */, false /* bypass_limits */)) {
|
||||
return HandleATMPError(state, err_string);
|
||||
const MempoolAcceptResult result = AcceptToMemoryPool(*node.mempool, tx, false /* bypass_limits */,
|
||||
false /* test_accept */);
|
||||
if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) {
|
||||
return HandleATMPError(result.m_state, err_string);
|
||||
}
|
||||
|
||||
// Transaction was accepted to the mempool.
|
||||
|
||||
@@ -946,44 +946,35 @@ static RPCHelpMan testmempoolaccept()
|
||||
result_0.pushKV("txid", tx->GetHash().GetHex());
|
||||
result_0.pushKV("wtxid", tx->GetWitnessHash().GetHex());
|
||||
|
||||
TxValidationState state;
|
||||
bool test_accept_res;
|
||||
CAmount fee{0};
|
||||
{
|
||||
LOCK(cs_main);
|
||||
test_accept_res = AcceptToMemoryPool(mempool, state, std::move(tx),
|
||||
nullptr /* plTxnReplaced */, false /* bypass_limits */, /* test_accept */ true, &fee);
|
||||
}
|
||||
|
||||
// Check that fee does not exceed maximum fee
|
||||
if (test_accept_res && max_raw_tx_fee && fee > max_raw_tx_fee) {
|
||||
result_0.pushKV("allowed", false);
|
||||
result_0.pushKV("reject-reason", "max-fee-exceeded");
|
||||
result.push_back(std::move(result_0));
|
||||
return result;
|
||||
}
|
||||
result_0.pushKV("allowed", test_accept_res);
|
||||
const MempoolAcceptResult accept_result = WITH_LOCK(cs_main, return AcceptToMemoryPool(mempool, std::move(tx),
|
||||
false /* bypass_limits */, /* test_accept */ true));
|
||||
|
||||
// Only return the fee and vsize if the transaction would pass ATMP.
|
||||
// These can be used to calculate the feerate.
|
||||
if (test_accept_res) {
|
||||
result_0.pushKV("vsize", virtual_size);
|
||||
UniValue fees(UniValue::VOBJ);
|
||||
fees.pushKV("base", ValueFromAmount(fee));
|
||||
result_0.pushKV("fees", fees);
|
||||
if (accept_result.m_result_type == MempoolAcceptResult::ResultType::VALID) {
|
||||
const CAmount fee = accept_result.m_base_fees.value();
|
||||
// Check that fee does not exceed maximum fee
|
||||
if (max_raw_tx_fee && fee > max_raw_tx_fee) {
|
||||
result_0.pushKV("allowed", false);
|
||||
result_0.pushKV("reject-reason", "max-fee-exceeded");
|
||||
} else {
|
||||
result_0.pushKV("allowed", true);
|
||||
result_0.pushKV("vsize", virtual_size);
|
||||
UniValue fees(UniValue::VOBJ);
|
||||
fees.pushKV("base", ValueFromAmount(fee));
|
||||
result_0.pushKV("fees", fees);
|
||||
}
|
||||
result.push_back(std::move(result_0));
|
||||
} else {
|
||||
if (state.IsInvalid()) {
|
||||
if (state.GetResult() == TxValidationResult::TX_MISSING_INPUTS) {
|
||||
result_0.pushKV("reject-reason", "missing-inputs");
|
||||
} else {
|
||||
result_0.pushKV("reject-reason", strprintf("%s", state.GetRejectReason()));
|
||||
}
|
||||
result_0.pushKV("allowed", false);
|
||||
const TxValidationState state = accept_result.m_state;
|
||||
if (state.GetResult() == TxValidationResult::TX_MISSING_INPUTS) {
|
||||
result_0.pushKV("reject-reason", "missing-inputs");
|
||||
} else {
|
||||
result_0.pushKV("reject-reason", state.GetRejectReason());
|
||||
}
|
||||
result.push_back(std::move(result_0));
|
||||
}
|
||||
|
||||
result.push_back(std::move(result_0));
|
||||
return result;
|
||||
},
|
||||
};
|
||||
|
||||
@@ -30,25 +30,21 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_reject_coinbase, TestChain100Setup)
|
||||
|
||||
BOOST_CHECK(CTransaction(coinbaseTx).IsCoinBase());
|
||||
|
||||
TxValidationState state;
|
||||
|
||||
LOCK(cs_main);
|
||||
|
||||
unsigned int initialPoolSize = m_node.mempool->size();
|
||||
const MempoolAcceptResult result = AcceptToMemoryPool(*m_node.mempool, MakeTransactionRef(coinbaseTx),
|
||||
true /* bypass_limits */);
|
||||
|
||||
BOOST_CHECK_EQUAL(
|
||||
false,
|
||||
AcceptToMemoryPool(*m_node.mempool, state, MakeTransactionRef(coinbaseTx),
|
||||
nullptr /* plTxnReplaced */,
|
||||
true /* bypass_limits */));
|
||||
BOOST_CHECK(result.m_result_type == MempoolAcceptResult::ResultType::INVALID);
|
||||
|
||||
// Check that the transaction hasn't been added to mempool.
|
||||
BOOST_CHECK_EQUAL(m_node.mempool->size(), initialPoolSize);
|
||||
|
||||
// Check that the validation state reflects the unsuccessful attempt.
|
||||
BOOST_CHECK(state.IsInvalid());
|
||||
BOOST_CHECK_EQUAL(state.GetRejectReason(), "coinbase");
|
||||
BOOST_CHECK(state.GetResult() == TxValidationResult::TX_CONSENSUS);
|
||||
BOOST_CHECK(result.m_state.IsInvalid());
|
||||
BOOST_CHECK_EQUAL(result.m_state.GetRejectReason(), "coinbase");
|
||||
BOOST_CHECK(result.m_state.GetResult() == TxValidationResult::TX_CONSENSUS);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
||||
@@ -28,9 +28,9 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup)
|
||||
const auto ToMemPool = [this](const CMutableTransaction& tx) {
|
||||
LOCK(cs_main);
|
||||
|
||||
TxValidationState state;
|
||||
return AcceptToMemoryPool(*m_node.mempool, state, MakeTransactionRef(tx),
|
||||
nullptr /* plTxnReplaced */, true /* bypass_limits */);
|
||||
const MempoolAcceptResult result = AcceptToMemoryPool(*m_node.mempool, MakeTransactionRef(tx),
|
||||
true /* bypass_limits */);
|
||||
return result.m_result_type == MempoolAcceptResult::ResultType::VALID;
|
||||
};
|
||||
|
||||
// Create a double-spend of mature coinbase txn:
|
||||
|
||||
@@ -283,15 +283,9 @@ BOOST_AUTO_TEST_CASE(mempool_locks_reorg)
|
||||
// Add the txs to the tx pool
|
||||
{
|
||||
LOCK(cs_main);
|
||||
TxValidationState state;
|
||||
std::list<CTransactionRef> plTxnReplaced;
|
||||
for (const auto& tx : txs) {
|
||||
BOOST_REQUIRE(AcceptToMemoryPool(
|
||||
*m_node.mempool,
|
||||
state,
|
||||
tx,
|
||||
&plTxnReplaced,
|
||||
/* bypass_limits */ false));
|
||||
const MempoolAcceptResult result = AcceptToMemoryPool(*m_node.mempool, tx, false /* bypass_limits */);
|
||||
BOOST_REQUIRE(result.m_result_type == MempoolAcceptResult::ResultType::VALID);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -380,10 +380,8 @@ static void UpdateMempoolForReorg(CTxMemPool& mempool, DisconnectedBlockTransact
|
||||
auto it = disconnectpool.queuedTx.get<insertion_order>().rbegin();
|
||||
while (it != disconnectpool.queuedTx.get<insertion_order>().rend()) {
|
||||
// ignore validation errors in resurrected transactions
|
||||
TxValidationState stateDummy;
|
||||
if (!fAddToMempool || (*it)->IsCoinBase() ||
|
||||
!AcceptToMemoryPool(mempool, stateDummy, *it,
|
||||
nullptr /* plTxnReplaced */, true /* bypass_limits */)) {
|
||||
AcceptToMemoryPool(mempool, *it, true /* bypass_limits */).m_result_type != MempoolAcceptResult::ResultType::VALID) {
|
||||
// If the transaction doesn't make it in to the mempool, remove any
|
||||
// transactions that depend on it (which would now be orphans).
|
||||
mempool.removeRecursive(**it, MemPoolRemovalReason::REORG);
|
||||
@@ -463,9 +461,7 @@ public:
|
||||
// around easier.
|
||||
struct ATMPArgs {
|
||||
const CChainParams& m_chainparams;
|
||||
TxValidationState &m_state;
|
||||
const int64_t m_accept_time;
|
||||
std::list<CTransactionRef>* m_replaced_transactions;
|
||||
const bool m_bypass_limits;
|
||||
/*
|
||||
* Return any outpoints which were not previously present in the coins
|
||||
@@ -476,11 +472,10 @@ public:
|
||||
*/
|
||||
std::vector<COutPoint>& m_coins_to_uncache;
|
||||
const bool m_test_accept;
|
||||
CAmount* m_fee_out;
|
||||
};
|
||||
|
||||
// Single transaction acceptance
|
||||
bool AcceptSingleTransaction(const CTransactionRef& ptx, ATMPArgs& args) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
||||
MempoolAcceptResult AcceptSingleTransaction(const CTransactionRef& ptx, ATMPArgs& args) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
||||
|
||||
private:
|
||||
// All the intermediate state that gets passed between the various levels
|
||||
@@ -491,14 +486,17 @@ private:
|
||||
CTxMemPool::setEntries m_all_conflicting;
|
||||
CTxMemPool::setEntries m_ancestors;
|
||||
std::unique_ptr<CTxMemPoolEntry> m_entry;
|
||||
std::list<CTransactionRef> m_replaced_transactions;
|
||||
|
||||
bool m_replacement_transaction;
|
||||
CAmount m_base_fees;
|
||||
CAmount m_modified_fees;
|
||||
CAmount m_conflicting_fees;
|
||||
size_t m_conflicting_size;
|
||||
|
||||
const CTransactionRef& m_ptx;
|
||||
const uint256& m_hash;
|
||||
TxValidationState m_state;
|
||||
};
|
||||
|
||||
// Run the policy checks on a given transaction, excluding any script checks.
|
||||
@@ -509,18 +507,18 @@ private:
|
||||
|
||||
// Run the script checks using our policy flags. As this can be slow, we should
|
||||
// only invoke this on transactions that have otherwise passed policy checks.
|
||||
bool PolicyScriptChecks(ATMPArgs& args, const Workspace& ws, PrecomputedTransactionData& txdata) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs);
|
||||
bool PolicyScriptChecks(const ATMPArgs& args, Workspace& ws, PrecomputedTransactionData& txdata) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs);
|
||||
|
||||
// Re-run the script checks, using consensus flags, and try to cache the
|
||||
// result in the scriptcache. This should be done after
|
||||
// PolicyScriptChecks(). This requires that all inputs either be in our
|
||||
// utxo set or in the mempool.
|
||||
bool ConsensusScriptChecks(ATMPArgs& args, const Workspace& ws, PrecomputedTransactionData &txdata) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs);
|
||||
bool ConsensusScriptChecks(const ATMPArgs& args, Workspace& ws, PrecomputedTransactionData &txdata) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs);
|
||||
|
||||
// Try to add the transaction to the mempool, removing any conflicts first.
|
||||
// Returns true if the transaction is in the mempool after any size
|
||||
// limiting is performed, false otherwise.
|
||||
bool Finalize(ATMPArgs& args, Workspace& ws) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs);
|
||||
bool Finalize(const ATMPArgs& args, Workspace& ws) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs);
|
||||
|
||||
// Compare a package's feerate against minimum allowed.
|
||||
bool CheckFeeRate(size_t package_size, CAmount package_fee, TxValidationState& state) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs)
|
||||
@@ -558,12 +556,12 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
|
||||
const uint256& hash = ws.m_hash;
|
||||
|
||||
// Copy/alias what we need out of args
|
||||
TxValidationState &state = args.m_state;
|
||||
const int64_t nAcceptTime = args.m_accept_time;
|
||||
const bool bypass_limits = args.m_bypass_limits;
|
||||
std::vector<COutPoint>& coins_to_uncache = args.m_coins_to_uncache;
|
||||
|
||||
// Alias what we need out of ws
|
||||
TxValidationState& state = ws.m_state;
|
||||
std::set<uint256>& setConflicts = ws.m_conflicts;
|
||||
CTxMemPool::setEntries& allConflicting = ws.m_all_conflicting;
|
||||
CTxMemPool::setEntries& setAncestors = ws.m_ancestors;
|
||||
@@ -683,16 +681,10 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
|
||||
if (!CheckSequenceLocks(m_pool, tx, STANDARD_LOCKTIME_VERIFY_FLAGS, &lp))
|
||||
return state.Invalid(TxValidationResult::TX_PREMATURE_SPEND, "non-BIP68-final");
|
||||
|
||||
CAmount nFees = 0;
|
||||
if (!Consensus::CheckTxInputs(tx, state, m_view, g_chainman.m_blockman.GetSpendHeight(m_view), nFees)) {
|
||||
if (!Consensus::CheckTxInputs(tx, state, m_view, g_chainman.m_blockman.GetSpendHeight(m_view), ws.m_base_fees)) {
|
||||
return false; // state filled in by CheckTxInputs
|
||||
}
|
||||
|
||||
// If fee_out is passed, return the fee to the caller
|
||||
if (args.m_fee_out) {
|
||||
*args.m_fee_out = nFees;
|
||||
}
|
||||
|
||||
// Check for non-standard pay-to-script-hash in inputs
|
||||
const auto& params = args.m_chainparams.GetConsensus();
|
||||
auto taproot_state = VersionBitsState(::ChainActive().Tip(), params, Consensus::DEPLOYMENT_TAPROOT, versionbitscache);
|
||||
@@ -707,7 +699,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
|
||||
int64_t nSigOpsCost = GetTransactionSigOpCost(tx, m_view, STANDARD_SCRIPT_VERIFY_FLAGS);
|
||||
|
||||
// nModifiedFees includes any fee deltas from PrioritiseTransaction
|
||||
nModifiedFees = nFees;
|
||||
nModifiedFees = ws.m_base_fees;
|
||||
m_pool.ApplyDelta(hash, nModifiedFees);
|
||||
|
||||
// Keep track of transactions that spend a coinbase, which we re-scan
|
||||
@@ -721,7 +713,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
|
||||
}
|
||||
}
|
||||
|
||||
entry.reset(new CTxMemPoolEntry(ptx, nFees, nAcceptTime, ::ChainActive().Height(),
|
||||
entry.reset(new CTxMemPoolEntry(ptx, ws.m_base_fees, nAcceptTime, ::ChainActive().Height(),
|
||||
fSpendsCoinbase, nSigOpsCost, lp));
|
||||
unsigned int nSize = entry->GetTxSize();
|
||||
|
||||
@@ -925,11 +917,10 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MemPoolAccept::PolicyScriptChecks(ATMPArgs& args, const Workspace& ws, PrecomputedTransactionData& txdata)
|
||||
bool MemPoolAccept::PolicyScriptChecks(const ATMPArgs& args, Workspace& ws, PrecomputedTransactionData& txdata)
|
||||
{
|
||||
const CTransaction& tx = *ws.m_ptx;
|
||||
|
||||
TxValidationState &state = args.m_state;
|
||||
TxValidationState& state = ws.m_state;
|
||||
|
||||
constexpr unsigned int scriptVerifyFlags = STANDARD_SCRIPT_VERIFY_FLAGS;
|
||||
|
||||
@@ -952,12 +943,11 @@ bool MemPoolAccept::PolicyScriptChecks(ATMPArgs& args, const Workspace& ws, Prec
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MemPoolAccept::ConsensusScriptChecks(ATMPArgs& args, const Workspace& ws, PrecomputedTransactionData& txdata)
|
||||
bool MemPoolAccept::ConsensusScriptChecks(const ATMPArgs& args, Workspace& ws, PrecomputedTransactionData& txdata)
|
||||
{
|
||||
const CTransaction& tx = *ws.m_ptx;
|
||||
const uint256& hash = ws.m_hash;
|
||||
|
||||
TxValidationState &state = args.m_state;
|
||||
TxValidationState& state = ws.m_state;
|
||||
const CChainParams& chainparams = args.m_chainparams;
|
||||
|
||||
// Check again against the current block tip's script verification
|
||||
@@ -984,11 +974,11 @@ bool MemPoolAccept::ConsensusScriptChecks(ATMPArgs& args, const Workspace& ws, P
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MemPoolAccept::Finalize(ATMPArgs& args, Workspace& ws)
|
||||
bool MemPoolAccept::Finalize(const ATMPArgs& args, Workspace& ws)
|
||||
{
|
||||
const CTransaction& tx = *ws.m_ptx;
|
||||
const uint256& hash = ws.m_hash;
|
||||
TxValidationState &state = args.m_state;
|
||||
TxValidationState& state = ws.m_state;
|
||||
const bool bypass_limits = args.m_bypass_limits;
|
||||
|
||||
CTxMemPool::setEntries& allConflicting = ws.m_all_conflicting;
|
||||
@@ -1007,8 +997,7 @@ bool MemPoolAccept::Finalize(ATMPArgs& args, Workspace& ws)
|
||||
hash.ToString(),
|
||||
FormatMoney(nModifiedFees - nConflictingFees),
|
||||
(int)entry->GetTxSize() - (int)nConflictingSize);
|
||||
if (args.m_replaced_transactions)
|
||||
args.m_replaced_transactions->push_back(it->GetSharedTx());
|
||||
ws.m_replaced_transactions.push_back(it->GetSharedTx());
|
||||
}
|
||||
m_pool.RemoveStaged(allConflicting, false, MemPoolRemovalReason::REPLACED);
|
||||
|
||||
@@ -1031,14 +1020,14 @@ bool MemPoolAccept::Finalize(ATMPArgs& args, Workspace& ws)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MemPoolAccept::AcceptSingleTransaction(const CTransactionRef& ptx, ATMPArgs& args)
|
||||
MempoolAcceptResult MemPoolAccept::AcceptSingleTransaction(const CTransactionRef& ptx, ATMPArgs& args)
|
||||
{
|
||||
AssertLockHeld(cs_main);
|
||||
LOCK(m_pool.cs); // mempool "read lock" (held through GetMainSignals().TransactionAddedToMempool())
|
||||
|
||||
Workspace workspace(ptx);
|
||||
Workspace ws(ptx);
|
||||
|
||||
if (!PreChecks(args, workspace)) return false;
|
||||
if (!PreChecks(args, ws)) return MempoolAcceptResult(ws.m_state);
|
||||
|
||||
// Only compute the precomputed transaction data if we need to verify
|
||||
// scripts (ie, other policy checks pass). We perform the inexpensive
|
||||
@@ -1046,31 +1035,35 @@ bool MemPoolAccept::AcceptSingleTransaction(const CTransactionRef& ptx, ATMPArgs
|
||||
// checks pass, to mitigate CPU exhaustion denial-of-service attacks.
|
||||
PrecomputedTransactionData txdata;
|
||||
|
||||
if (!PolicyScriptChecks(args, workspace, txdata)) return false;
|
||||
if (!PolicyScriptChecks(args, ws, txdata)) return MempoolAcceptResult(ws.m_state);
|
||||
|
||||
if (!ConsensusScriptChecks(args, workspace, txdata)) return false;
|
||||
if (!ConsensusScriptChecks(args, ws, txdata)) return MempoolAcceptResult(ws.m_state);
|
||||
|
||||
// Tx was accepted, but not added
|
||||
if (args.m_test_accept) return true;
|
||||
if (args.m_test_accept) {
|
||||
return MempoolAcceptResult(std::move(ws.m_replaced_transactions), ws.m_base_fees);
|
||||
}
|
||||
|
||||
if (!Finalize(args, workspace)) return false;
|
||||
if (!Finalize(args, ws)) return MempoolAcceptResult(ws.m_state);
|
||||
|
||||
GetMainSignals().TransactionAddedToMempool(ptx, m_pool.GetAndIncrementSequence());
|
||||
|
||||
return true;
|
||||
return MempoolAcceptResult(std::move(ws.m_replaced_transactions), ws.m_base_fees);
|
||||
}
|
||||
|
||||
} // anon namespace
|
||||
|
||||
/** (try to) add transaction to memory pool with a specified acceptance time **/
|
||||
static bool AcceptToMemoryPoolWithTime(const CChainParams& chainparams, CTxMemPool& pool, TxValidationState &state, const CTransactionRef &tx,
|
||||
int64_t nAcceptTime, std::list<CTransactionRef>* plTxnReplaced,
|
||||
bool bypass_limits, bool test_accept, CAmount* fee_out=nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
|
||||
static MempoolAcceptResult AcceptToMemoryPoolWithTime(const CChainParams& chainparams, CTxMemPool& pool,
|
||||
const CTransactionRef &tx, int64_t nAcceptTime,
|
||||
bool bypass_limits, bool test_accept)
|
||||
EXCLUSIVE_LOCKS_REQUIRED(cs_main)
|
||||
{
|
||||
std::vector<COutPoint> coins_to_uncache;
|
||||
MemPoolAccept::ATMPArgs args { chainparams, state, nAcceptTime, plTxnReplaced, bypass_limits, coins_to_uncache, test_accept, fee_out };
|
||||
bool res = MemPoolAccept(pool).AcceptSingleTransaction(tx, args);
|
||||
if (!res) {
|
||||
MemPoolAccept::ATMPArgs args { chainparams, nAcceptTime, bypass_limits, coins_to_uncache, test_accept };
|
||||
|
||||
const MempoolAcceptResult result = MemPoolAccept(pool).AcceptSingleTransaction(tx, args);
|
||||
if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) {
|
||||
// Remove coins that were not present in the coins cache before calling ATMPW;
|
||||
// this is to prevent memory DoS in case we receive a large number of
|
||||
// invalid transactions that attempt to overrun the in-memory coins cache
|
||||
@@ -1082,15 +1075,12 @@ static bool AcceptToMemoryPoolWithTime(const CChainParams& chainparams, CTxMemPo
|
||||
// After we've (potentially) uncached entries, ensure our coins cache is still within its size limits
|
||||
BlockValidationState state_dummy;
|
||||
::ChainstateActive().FlushStateToDisk(chainparams, state_dummy, FlushStateMode::PERIODIC);
|
||||
return res;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool AcceptToMemoryPool(CTxMemPool& pool, TxValidationState &state, const CTransactionRef &tx,
|
||||
std::list<CTransactionRef>* plTxnReplaced,
|
||||
bool bypass_limits, bool test_accept, CAmount* fee_out)
|
||||
MempoolAcceptResult AcceptToMemoryPool(CTxMemPool& pool, const CTransactionRef &tx, bool bypass_limits, bool test_accept)
|
||||
{
|
||||
const CChainParams& chainparams = Params();
|
||||
return AcceptToMemoryPoolWithTime(chainparams, pool, state, tx, GetTime(), plTxnReplaced, bypass_limits, test_accept, fee_out);
|
||||
return AcceptToMemoryPoolWithTime(Params(), pool, tx, GetTime(), bypass_limits, test_accept);
|
||||
}
|
||||
|
||||
CTransactionRef GetTransaction(const CBlockIndex* const block_index, const CTxMemPool* const mempool, const uint256& hash, const Consensus::Params& consensusParams, uint256& hashBlock)
|
||||
@@ -5029,13 +5019,10 @@ bool LoadMempool(CTxMemPool& pool)
|
||||
if (amountdelta) {
|
||||
pool.PrioritiseTransaction(tx->GetHash(), amountdelta);
|
||||
}
|
||||
TxValidationState state;
|
||||
if (nTime > nNow - nExpiryTimeout) {
|
||||
LOCK(cs_main);
|
||||
AcceptToMemoryPoolWithTime(chainparams, pool, state, tx, nTime,
|
||||
nullptr /* plTxnReplaced */, false /* bypass_limits */,
|
||||
false /* test_accept */);
|
||||
if (state.IsValid()) {
|
||||
if (AcceptToMemoryPoolWithTime(chainparams, pool, tx, nTime, false /* bypass_limits */,
|
||||
false /* test_accept */).m_result_type == MempoolAcceptResult::ResultType::VALID) {
|
||||
++count;
|
||||
} else {
|
||||
// mempool may contain the transaction already, e.g. from
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
|
||||
#include <amount.h>
|
||||
#include <coins.h>
|
||||
#include <consensus/validation.h>
|
||||
#include <crypto/common.h> // for ReadLE64
|
||||
#include <fs.h>
|
||||
#include <optional.h>
|
||||
@@ -23,6 +24,7 @@
|
||||
#include <txdb.h>
|
||||
#include <versionbits.h>
|
||||
#include <serialize.h>
|
||||
#include <util/check.h>
|
||||
#include <util/hasher.h>
|
||||
|
||||
#include <atomic>
|
||||
@@ -46,7 +48,6 @@ class CConnman;
|
||||
class CScriptCheck;
|
||||
class CTxMemPool;
|
||||
class ChainstateManager;
|
||||
class TxValidationState;
|
||||
struct ChainTxData;
|
||||
|
||||
struct DisconnectedBlockTransactions;
|
||||
@@ -181,12 +182,46 @@ void UnlinkPrunedFiles(const std::set<int>& setFilesToPrune);
|
||||
/** Prune block files up to a given height */
|
||||
void PruneBlockFilesManual(int nManualPruneHeight);
|
||||
|
||||
/** (try to) add transaction to memory pool
|
||||
* plTxnReplaced will be appended to with all transactions replaced from mempool
|
||||
* @param[out] fee_out optional argument to return tx fee to the caller **/
|
||||
bool AcceptToMemoryPool(CTxMemPool& pool, TxValidationState &state, const CTransactionRef &tx,
|
||||
std::list<CTransactionRef>* plTxnReplaced,
|
||||
bool bypass_limits, bool test_accept=false, CAmount* fee_out=nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
||||
/**
|
||||
* Validation result for a single transaction mempool acceptance.
|
||||
*/
|
||||
struct MempoolAcceptResult {
|
||||
/** Used to indicate the results of mempool validation,
|
||||
* including the possibility of unfinished validation.
|
||||
*/
|
||||
enum class ResultType {
|
||||
VALID, //!> Fully validated, valid.
|
||||
INVALID, //!> Invalid.
|
||||
};
|
||||
ResultType m_result_type;
|
||||
TxValidationState m_state;
|
||||
|
||||
// The following fields are only present when m_result_type = ResultType::VALID
|
||||
/** Mempool transactions replaced by the tx per BIP 125 rules. */
|
||||
std::optional<std::list<CTransactionRef>> m_replaced_transactions;
|
||||
/** Raw base fees. */
|
||||
std::optional<CAmount> m_base_fees;
|
||||
|
||||
/** Constructor for failure case */
|
||||
explicit MempoolAcceptResult(TxValidationState state)
|
||||
: m_result_type(ResultType::INVALID),
|
||||
m_state(state), m_replaced_transactions(nullopt), m_base_fees(nullopt) {
|
||||
Assume(!state.IsValid()); // Can be invalid or error
|
||||
}
|
||||
|
||||
/** Constructor for success case */
|
||||
explicit MempoolAcceptResult(std::list<CTransactionRef>&& replaced_txns, CAmount fees)
|
||||
: m_result_type(ResultType::VALID), m_state(TxValidationState{}),
|
||||
m_replaced_transactions(std::move(replaced_txns)), m_base_fees(fees) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* (Try to) add a transaction to the memory pool.
|
||||
* @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.
|
||||
*/
|
||||
MempoolAcceptResult AcceptToMemoryPool(CTxMemPool& pool, const CTransactionRef& tx,
|
||||
bool bypass_limits, bool test_accept=false) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
||||
|
||||
/** Get the BIP9 state for a given deployment at the current tip. */
|
||||
ThresholdState VersionBitsTipState(const Consensus::Params& params, Consensus::DeploymentPos pos);
|
||||
|
||||
Reference in New Issue
Block a user