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:
9 files changed
+135
-133
No files matched your search
@@ -48,9 +48,8 @@ static void AssembleBlock(benchmark::Bench& bench)
|
|||||||
LOCK(::cs_main); // Required for ::AcceptToMemoryPool.
|
LOCK(::cs_main); // Required for ::AcceptToMemoryPool.
|
||||||
|
|
||||||
for (const auto& txr : txs) {
|
for (const auto& txr : txs) {
|
||||||
TxValidationState state;
|
const MempoolAcceptResult res = ::AcceptToMemoryPool(*test_setup.m_node.mempool, txr, false /* bypass_limits */);
|
||||||
bool ret{::AcceptToMemoryPool(*test_setup.m_node.mempool, state, txr, nullptr /* plTxnReplaced */, false /* bypass_limits */)};
|
assert(res.m_result_type == MempoolAcceptResult::ResultType::VALID);
|
||||||
assert(ret);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2178,10 +2178,10 @@ void PeerManagerImpl::ProcessOrphanTx(std::set<uint256>& orphan_work_set)
|
|||||||
if (orphan_it == mapOrphanTransactions.end()) continue;
|
if (orphan_it == mapOrphanTransactions.end()) continue;
|
||||||
|
|
||||||
const CTransactionRef porphanTx = orphan_it->second.tx;
|
const CTransactionRef porphanTx = orphan_it->second.tx;
|
||||||
TxValidationState state;
|
const MempoolAcceptResult result = AcceptToMemoryPool(m_mempool, porphanTx, false /* bypass_limits */);
|
||||||
std::list<CTransactionRef> removed_txn;
|
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());
|
LogPrint(BCLog::MEMPOOL, " accepted orphan tx %s\n", orphanHash.ToString());
|
||||||
RelayTransaction(orphanHash, porphanTx->GetWitnessHash(), m_connman);
|
RelayTransaction(orphanHash, porphanTx->GetWitnessHash(), m_connman);
|
||||||
for (unsigned int i = 0; i < porphanTx->vout.size(); i++) {
|
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);
|
EraseOrphanTx(orphanHash);
|
||||||
for (const CTransactionRef& removedTx : removed_txn) {
|
for (const CTransactionRef& removedTx : result.m_replaced_transactions.value()) {
|
||||||
AddToCompactExtraTransactions(removedTx);
|
AddToCompactExtraTransactions(removedTx);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -3200,10 +3200,10 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
TxValidationState state;
|
const MempoolAcceptResult result = AcceptToMemoryPool(m_mempool, ptx, false /* bypass_limits */);
|
||||||
std::list<CTransactionRef> lRemovedTxn;
|
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());
|
m_mempool.check(&::ChainstateActive().CoinsTip());
|
||||||
// As this version of the transaction was acceptable, we can forget about any
|
// As this version of the transaction was acceptable, we can forget about any
|
||||||
// requests for it.
|
// requests for it.
|
||||||
@@ -3226,7 +3226,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
|
|||||||
tx.GetHash().ToString(),
|
tx.GetHash().ToString(),
|
||||||
m_mempool.size(), m_mempool.DynamicMemoryUsage() / 1000);
|
m_mempool.size(), m_mempool.DynamicMemoryUsage() / 1000);
|
||||||
|
|
||||||
for (const CTransactionRef& removedTx : lRemovedTxn) {
|
for (const CTransactionRef& removedTx : result.m_replaced_transactions.value()) {
|
||||||
AddToCompactExtraTransactions(removedTx);
|
AddToCompactExtraTransactions(removedTx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -50,22 +50,22 @@ TransactionError BroadcastTransaction(NodeContext& node, const CTransactionRef t
|
|||||||
}
|
}
|
||||||
if (!node.mempool->exists(hashTx)) {
|
if (!node.mempool->exists(hashTx)) {
|
||||||
// Transaction is not already in the mempool.
|
// Transaction is not already in the mempool.
|
||||||
TxValidationState state;
|
|
||||||
if (max_tx_fee > 0) {
|
if (max_tx_fee > 0) {
|
||||||
// First, call ATMP with test_accept and check the fee. If ATMP
|
// First, call ATMP with test_accept and check the fee. If ATMP
|
||||||
// fails here, return error immediately.
|
// fails here, return error immediately.
|
||||||
CAmount fee{0};
|
const MempoolAcceptResult result = AcceptToMemoryPool(*node.mempool, tx, false /* bypass_limits */,
|
||||||
if (!AcceptToMemoryPool(*node.mempool, state, tx,
|
true /* test_accept */);
|
||||||
nullptr /* plTxnReplaced */, false /* bypass_limits */, /* test_accept */ true, &fee)) {
|
if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) {
|
||||||
return HandleATMPError(state, err_string);
|
return HandleATMPError(result.m_state, err_string);
|
||||||
} else if (fee > max_tx_fee) {
|
} else if (result.m_base_fees.value() > max_tx_fee) {
|
||||||
return TransactionError::MAX_FEE_EXCEEDED;
|
return TransactionError::MAX_FEE_EXCEEDED;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Try to submit the transaction to the mempool.
|
// Try to submit the transaction to the mempool.
|
||||||
if (!AcceptToMemoryPool(*node.mempool, state, tx,
|
const MempoolAcceptResult result = AcceptToMemoryPool(*node.mempool, tx, false /* bypass_limits */,
|
||||||
nullptr /* plTxnReplaced */, false /* bypass_limits */)) {
|
false /* test_accept */);
|
||||||
return HandleATMPError(state, err_string);
|
if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) {
|
||||||
|
return HandleATMPError(result.m_state, err_string);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transaction was accepted to the mempool.
|
// Transaction was accepted to the mempool.
|
||||||
|
|||||||
+21
-30
@@ -946,44 +946,35 @@ static RPCHelpMan testmempoolaccept()
|
|||||||
result_0.pushKV("txid", tx->GetHash().GetHex());
|
result_0.pushKV("txid", tx->GetHash().GetHex());
|
||||||
result_0.pushKV("wtxid", tx->GetWitnessHash().GetHex());
|
result_0.pushKV("wtxid", tx->GetWitnessHash().GetHex());
|
||||||
|
|
||||||
TxValidationState state;
|
const MempoolAcceptResult accept_result = WITH_LOCK(cs_main, return AcceptToMemoryPool(mempool, std::move(tx),
|
||||||
bool test_accept_res;
|
false /* bypass_limits */, /* test_accept */ true));
|
||||||
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);
|
|
||||||
|
|
||||||
// Only return the fee and vsize if the transaction would pass ATMP.
|
// Only return the fee and vsize if the transaction would pass ATMP.
|
||||||
// These can be used to calculate the feerate.
|
// These can be used to calculate the feerate.
|
||||||
if (test_accept_res) {
|
if (accept_result.m_result_type == MempoolAcceptResult::ResultType::VALID) {
|
||||||
result_0.pushKV("vsize", virtual_size);
|
const CAmount fee = accept_result.m_base_fees.value();
|
||||||
UniValue fees(UniValue::VOBJ);
|
// Check that fee does not exceed maximum fee
|
||||||
fees.pushKV("base", ValueFromAmount(fee));
|
if (max_raw_tx_fee && fee > max_raw_tx_fee) {
|
||||||
result_0.pushKV("fees", fees);
|
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 {
|
} else {
|
||||||
if (state.IsInvalid()) {
|
result_0.pushKV("allowed", false);
|
||||||
if (state.GetResult() == TxValidationResult::TX_MISSING_INPUTS) {
|
const TxValidationState state = accept_result.m_state;
|
||||||
result_0.pushKV("reject-reason", "missing-inputs");
|
if (state.GetResult() == TxValidationResult::TX_MISSING_INPUTS) {
|
||||||
} else {
|
result_0.pushKV("reject-reason", "missing-inputs");
|
||||||
result_0.pushKV("reject-reason", strprintf("%s", state.GetRejectReason()));
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
result_0.pushKV("reject-reason", state.GetRejectReason());
|
result_0.pushKV("reject-reason", state.GetRejectReason());
|
||||||
}
|
}
|
||||||
|
result.push_back(std::move(result_0));
|
||||||
}
|
}
|
||||||
|
|
||||||
result.push_back(std::move(result_0));
|
|
||||||
return result;
|
return result;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -30,25 +30,21 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_reject_coinbase, TestChain100Setup)
|
|||||||
|
|
||||||
BOOST_CHECK(CTransaction(coinbaseTx).IsCoinBase());
|
BOOST_CHECK(CTransaction(coinbaseTx).IsCoinBase());
|
||||||
|
|
||||||
TxValidationState state;
|
|
||||||
|
|
||||||
LOCK(cs_main);
|
LOCK(cs_main);
|
||||||
|
|
||||||
unsigned int initialPoolSize = m_node.mempool->size();
|
unsigned int initialPoolSize = m_node.mempool->size();
|
||||||
|
const MempoolAcceptResult result = AcceptToMemoryPool(*m_node.mempool, MakeTransactionRef(coinbaseTx),
|
||||||
|
true /* bypass_limits */);
|
||||||
|
|
||||||
BOOST_CHECK_EQUAL(
|
BOOST_CHECK(result.m_result_type == MempoolAcceptResult::ResultType::INVALID);
|
||||||
false,
|
|
||||||
AcceptToMemoryPool(*m_node.mempool, state, MakeTransactionRef(coinbaseTx),
|
|
||||||
nullptr /* plTxnReplaced */,
|
|
||||||
true /* bypass_limits */));
|
|
||||||
|
|
||||||
// Check that the transaction hasn't been added to mempool.
|
// Check that the transaction hasn't been added to mempool.
|
||||||
BOOST_CHECK_EQUAL(m_node.mempool->size(), initialPoolSize);
|
BOOST_CHECK_EQUAL(m_node.mempool->size(), initialPoolSize);
|
||||||
|
|
||||||
// Check that the validation state reflects the unsuccessful attempt.
|
// Check that the validation state reflects the unsuccessful attempt.
|
||||||
BOOST_CHECK(state.IsInvalid());
|
BOOST_CHECK(result.m_state.IsInvalid());
|
||||||
BOOST_CHECK_EQUAL(state.GetRejectReason(), "coinbase");
|
BOOST_CHECK_EQUAL(result.m_state.GetRejectReason(), "coinbase");
|
||||||
BOOST_CHECK(state.GetResult() == TxValidationResult::TX_CONSENSUS);
|
BOOST_CHECK(result.m_state.GetResult() == TxValidationResult::TX_CONSENSUS);
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_SUITE_END()
|
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) {
|
const auto ToMemPool = [this](const CMutableTransaction& tx) {
|
||||||
LOCK(cs_main);
|
LOCK(cs_main);
|
||||||
|
|
||||||
TxValidationState state;
|
const MempoolAcceptResult result = AcceptToMemoryPool(*m_node.mempool, MakeTransactionRef(tx),
|
||||||
return AcceptToMemoryPool(*m_node.mempool, state, MakeTransactionRef(tx),
|
true /* bypass_limits */);
|
||||||
nullptr /* plTxnReplaced */, true /* bypass_limits */);
|
return result.m_result_type == MempoolAcceptResult::ResultType::VALID;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create a double-spend of mature coinbase txn:
|
// 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
|
// Add the txs to the tx pool
|
||||||
{
|
{
|
||||||
LOCK(cs_main);
|
LOCK(cs_main);
|
||||||
TxValidationState state;
|
|
||||||
std::list<CTransactionRef> plTxnReplaced;
|
|
||||||
for (const auto& tx : txs) {
|
for (const auto& tx : txs) {
|
||||||
BOOST_REQUIRE(AcceptToMemoryPool(
|
const MempoolAcceptResult result = AcceptToMemoryPool(*m_node.mempool, tx, false /* bypass_limits */);
|
||||||
*m_node.mempool,
|
BOOST_REQUIRE(result.m_result_type == MempoolAcceptResult::ResultType::VALID);
|
||||||
state,
|
|
||||||
tx,
|
|
||||||
&plTxnReplaced,
|
|
||||||
/* bypass_limits */ false));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+42
-55
@@ -380,10 +380,8 @@ static void UpdateMempoolForReorg(CTxMemPool& mempool, DisconnectedBlockTransact
|
|||||||
auto it = disconnectpool.queuedTx.get<insertion_order>().rbegin();
|
auto it = disconnectpool.queuedTx.get<insertion_order>().rbegin();
|
||||||
while (it != disconnectpool.queuedTx.get<insertion_order>().rend()) {
|
while (it != disconnectpool.queuedTx.get<insertion_order>().rend()) {
|
||||||
// ignore validation errors in resurrected transactions
|
// ignore validation errors in resurrected transactions
|
||||||
TxValidationState stateDummy;
|
|
||||||
if (!fAddToMempool || (*it)->IsCoinBase() ||
|
if (!fAddToMempool || (*it)->IsCoinBase() ||
|
||||||
!AcceptToMemoryPool(mempool, stateDummy, *it,
|
AcceptToMemoryPool(mempool, *it, true /* bypass_limits */).m_result_type != MempoolAcceptResult::ResultType::VALID) {
|
||||||
nullptr /* plTxnReplaced */, true /* bypass_limits */)) {
|
|
||||||
// If the transaction doesn't make it in to the mempool, remove any
|
// If the transaction doesn't make it in to the mempool, remove any
|
||||||
// transactions that depend on it (which would now be orphans).
|
// transactions that depend on it (which would now be orphans).
|
||||||
mempool.removeRecursive(**it, MemPoolRemovalReason::REORG);
|
mempool.removeRecursive(**it, MemPoolRemovalReason::REORG);
|
||||||
@@ -463,9 +461,7 @@ public:
|
|||||||
// around easier.
|
// around easier.
|
||||||
struct ATMPArgs {
|
struct ATMPArgs {
|
||||||
const CChainParams& m_chainparams;
|
const CChainParams& m_chainparams;
|
||||||
TxValidationState &m_state;
|
|
||||||
const int64_t m_accept_time;
|
const int64_t m_accept_time;
|
||||||
std::list<CTransactionRef>* m_replaced_transactions;
|
|
||||||
const bool m_bypass_limits;
|
const bool m_bypass_limits;
|
||||||
/*
|
/*
|
||||||
* Return any outpoints which were not previously present in the coins
|
* Return any outpoints which were not previously present in the coins
|
||||||
@@ -476,11 +472,10 @@ public:
|
|||||||
*/
|
*/
|
||||||
std::vector<COutPoint>& m_coins_to_uncache;
|
std::vector<COutPoint>& m_coins_to_uncache;
|
||||||
const bool m_test_accept;
|
const bool m_test_accept;
|
||||||
CAmount* m_fee_out;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Single transaction acceptance
|
// 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:
|
private:
|
||||||
// All the intermediate state that gets passed between the various levels
|
// All the intermediate state that gets passed between the various levels
|
||||||
@@ -491,14 +486,17 @@ private:
|
|||||||
CTxMemPool::setEntries m_all_conflicting;
|
CTxMemPool::setEntries m_all_conflicting;
|
||||||
CTxMemPool::setEntries m_ancestors;
|
CTxMemPool::setEntries m_ancestors;
|
||||||
std::unique_ptr<CTxMemPoolEntry> m_entry;
|
std::unique_ptr<CTxMemPoolEntry> m_entry;
|
||||||
|
std::list<CTransactionRef> m_replaced_transactions;
|
||||||
|
|
||||||
bool m_replacement_transaction;
|
bool m_replacement_transaction;
|
||||||
|
CAmount m_base_fees;
|
||||||
CAmount m_modified_fees;
|
CAmount m_modified_fees;
|
||||||
CAmount m_conflicting_fees;
|
CAmount m_conflicting_fees;
|
||||||
size_t m_conflicting_size;
|
size_t m_conflicting_size;
|
||||||
|
|
||||||
const CTransactionRef& m_ptx;
|
const CTransactionRef& m_ptx;
|
||||||
const uint256& m_hash;
|
const uint256& m_hash;
|
||||||
|
TxValidationState m_state;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Run the policy checks on a given transaction, excluding any script checks.
|
// 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
|
// 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.
|
// 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
|
// Re-run the script checks, using consensus flags, and try to cache the
|
||||||
// result in the scriptcache. This should be done after
|
// result in the scriptcache. This should be done after
|
||||||
// PolicyScriptChecks(). This requires that all inputs either be in our
|
// PolicyScriptChecks(). This requires that all inputs either be in our
|
||||||
// utxo set or in the mempool.
|
// 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.
|
// Try to add the transaction to the mempool, removing any conflicts first.
|
||||||
// Returns true if the transaction is in the mempool after any size
|
// Returns true if the transaction is in the mempool after any size
|
||||||
// limiting is performed, false otherwise.
|
// 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.
|
// 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)
|
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;
|
const uint256& hash = ws.m_hash;
|
||||||
|
|
||||||
// Copy/alias what we need out of args
|
// Copy/alias what we need out of args
|
||||||
TxValidationState &state = args.m_state;
|
|
||||||
const int64_t nAcceptTime = args.m_accept_time;
|
const int64_t nAcceptTime = args.m_accept_time;
|
||||||
const bool bypass_limits = args.m_bypass_limits;
|
const bool bypass_limits = args.m_bypass_limits;
|
||||||
std::vector<COutPoint>& coins_to_uncache = args.m_coins_to_uncache;
|
std::vector<COutPoint>& coins_to_uncache = args.m_coins_to_uncache;
|
||||||
|
|
||||||
// Alias what we need out of ws
|
// Alias what we need out of ws
|
||||||
|
TxValidationState& state = ws.m_state;
|
||||||
std::set<uint256>& setConflicts = ws.m_conflicts;
|
std::set<uint256>& setConflicts = ws.m_conflicts;
|
||||||
CTxMemPool::setEntries& allConflicting = ws.m_all_conflicting;
|
CTxMemPool::setEntries& allConflicting = ws.m_all_conflicting;
|
||||||
CTxMemPool::setEntries& setAncestors = ws.m_ancestors;
|
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))
|
if (!CheckSequenceLocks(m_pool, tx, STANDARD_LOCKTIME_VERIFY_FLAGS, &lp))
|
||||||
return state.Invalid(TxValidationResult::TX_PREMATURE_SPEND, "non-BIP68-final");
|
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), ws.m_base_fees)) {
|
||||||
if (!Consensus::CheckTxInputs(tx, state, m_view, g_chainman.m_blockman.GetSpendHeight(m_view), nFees)) {
|
|
||||||
return false; // state filled in by CheckTxInputs
|
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
|
// Check for non-standard pay-to-script-hash in inputs
|
||||||
const auto& params = args.m_chainparams.GetConsensus();
|
const auto& params = args.m_chainparams.GetConsensus();
|
||||||
auto taproot_state = VersionBitsState(::ChainActive().Tip(), params, Consensus::DEPLOYMENT_TAPROOT, versionbitscache);
|
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);
|
int64_t nSigOpsCost = GetTransactionSigOpCost(tx, m_view, STANDARD_SCRIPT_VERIFY_FLAGS);
|
||||||
|
|
||||||
// nModifiedFees includes any fee deltas from PrioritiseTransaction
|
// nModifiedFees includes any fee deltas from PrioritiseTransaction
|
||||||
nModifiedFees = nFees;
|
nModifiedFees = ws.m_base_fees;
|
||||||
m_pool.ApplyDelta(hash, nModifiedFees);
|
m_pool.ApplyDelta(hash, nModifiedFees);
|
||||||
|
|
||||||
// Keep track of transactions that spend a coinbase, which we re-scan
|
// 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));
|
fSpendsCoinbase, nSigOpsCost, lp));
|
||||||
unsigned int nSize = entry->GetTxSize();
|
unsigned int nSize = entry->GetTxSize();
|
||||||
|
|
||||||
@@ -925,11 +917,10 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
|
|||||||
return true;
|
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;
|
const CTransaction& tx = *ws.m_ptx;
|
||||||
|
TxValidationState& state = ws.m_state;
|
||||||
TxValidationState &state = args.m_state;
|
|
||||||
|
|
||||||
constexpr unsigned int scriptVerifyFlags = STANDARD_SCRIPT_VERIFY_FLAGS;
|
constexpr unsigned int scriptVerifyFlags = STANDARD_SCRIPT_VERIFY_FLAGS;
|
||||||
|
|
||||||
@@ -952,12 +943,11 @@ bool MemPoolAccept::PolicyScriptChecks(ATMPArgs& args, const Workspace& ws, Prec
|
|||||||
return true;
|
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 CTransaction& tx = *ws.m_ptx;
|
||||||
const uint256& hash = ws.m_hash;
|
const uint256& hash = ws.m_hash;
|
||||||
|
TxValidationState& state = ws.m_state;
|
||||||
TxValidationState &state = args.m_state;
|
|
||||||
const CChainParams& chainparams = args.m_chainparams;
|
const CChainParams& chainparams = args.m_chainparams;
|
||||||
|
|
||||||
// Check again against the current block tip's script verification
|
// 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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MemPoolAccept::Finalize(ATMPArgs& args, Workspace& ws)
|
bool MemPoolAccept::Finalize(const ATMPArgs& args, Workspace& ws)
|
||||||
{
|
{
|
||||||
const CTransaction& tx = *ws.m_ptx;
|
const CTransaction& tx = *ws.m_ptx;
|
||||||
const uint256& hash = ws.m_hash;
|
const uint256& hash = ws.m_hash;
|
||||||
TxValidationState &state = args.m_state;
|
TxValidationState& state = ws.m_state;
|
||||||
const bool bypass_limits = args.m_bypass_limits;
|
const bool bypass_limits = args.m_bypass_limits;
|
||||||
|
|
||||||
CTxMemPool::setEntries& allConflicting = ws.m_all_conflicting;
|
CTxMemPool::setEntries& allConflicting = ws.m_all_conflicting;
|
||||||
@@ -1007,8 +997,7 @@ bool MemPoolAccept::Finalize(ATMPArgs& args, Workspace& ws)
|
|||||||
hash.ToString(),
|
hash.ToString(),
|
||||||
FormatMoney(nModifiedFees - nConflictingFees),
|
FormatMoney(nModifiedFees - nConflictingFees),
|
||||||
(int)entry->GetTxSize() - (int)nConflictingSize);
|
(int)entry->GetTxSize() - (int)nConflictingSize);
|
||||||
if (args.m_replaced_transactions)
|
ws.m_replaced_transactions.push_back(it->GetSharedTx());
|
||||||
args.m_replaced_transactions->push_back(it->GetSharedTx());
|
|
||||||
}
|
}
|
||||||
m_pool.RemoveStaged(allConflicting, false, MemPoolRemovalReason::REPLACED);
|
m_pool.RemoveStaged(allConflicting, false, MemPoolRemovalReason::REPLACED);
|
||||||
|
|
||||||
@@ -1031,14 +1020,14 @@ bool MemPoolAccept::Finalize(ATMPArgs& args, Workspace& ws)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MemPoolAccept::AcceptSingleTransaction(const CTransactionRef& ptx, ATMPArgs& args)
|
MempoolAcceptResult MemPoolAccept::AcceptSingleTransaction(const CTransactionRef& ptx, ATMPArgs& args)
|
||||||
{
|
{
|
||||||
AssertLockHeld(cs_main);
|
AssertLockHeld(cs_main);
|
||||||
LOCK(m_pool.cs); // mempool "read lock" (held through GetMainSignals().TransactionAddedToMempool())
|
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
|
// Only compute the precomputed transaction data if we need to verify
|
||||||
// scripts (ie, other policy checks pass). We perform the inexpensive
|
// 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.
|
// checks pass, to mitigate CPU exhaustion denial-of-service attacks.
|
||||||
PrecomputedTransactionData txdata;
|
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
|
// 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());
|
GetMainSignals().TransactionAddedToMempool(ptx, m_pool.GetAndIncrementSequence());
|
||||||
|
|
||||||
return true;
|
return MempoolAcceptResult(std::move(ws.m_replaced_transactions), ws.m_base_fees);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // anon namespace
|
} // anon namespace
|
||||||
|
|
||||||
/** (try to) add transaction to memory pool with a specified acceptance time **/
|
/** (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,
|
static MempoolAcceptResult AcceptToMemoryPoolWithTime(const CChainParams& chainparams, CTxMemPool& pool,
|
||||||
int64_t nAcceptTime, std::list<CTransactionRef>* plTxnReplaced,
|
const CTransactionRef &tx, int64_t nAcceptTime,
|
||||||
bool bypass_limits, bool test_accept, CAmount* fee_out=nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
|
bool bypass_limits, bool test_accept)
|
||||||
|
EXCLUSIVE_LOCKS_REQUIRED(cs_main)
|
||||||
{
|
{
|
||||||
std::vector<COutPoint> coins_to_uncache;
|
std::vector<COutPoint> coins_to_uncache;
|
||||||
MemPoolAccept::ATMPArgs args { chainparams, state, nAcceptTime, plTxnReplaced, bypass_limits, coins_to_uncache, test_accept, fee_out };
|
MemPoolAccept::ATMPArgs args { chainparams, nAcceptTime, bypass_limits, coins_to_uncache, test_accept };
|
||||||
bool res = MemPoolAccept(pool).AcceptSingleTransaction(tx, args);
|
|
||||||
if (!res) {
|
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;
|
// 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
|
// 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
|
// 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
|
// After we've (potentially) uncached entries, ensure our coins cache is still within its size limits
|
||||||
BlockValidationState state_dummy;
|
BlockValidationState state_dummy;
|
||||||
::ChainstateActive().FlushStateToDisk(chainparams, state_dummy, FlushStateMode::PERIODIC);
|
::ChainstateActive().FlushStateToDisk(chainparams, state_dummy, FlushStateMode::PERIODIC);
|
||||||
return res;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AcceptToMemoryPool(CTxMemPool& pool, TxValidationState &state, const CTransactionRef &tx,
|
MempoolAcceptResult AcceptToMemoryPool(CTxMemPool& pool, const CTransactionRef &tx, bool bypass_limits, bool test_accept)
|
||||||
std::list<CTransactionRef>* plTxnReplaced,
|
|
||||||
bool bypass_limits, bool test_accept, CAmount* fee_out)
|
|
||||||
{
|
{
|
||||||
const CChainParams& chainparams = Params();
|
return AcceptToMemoryPoolWithTime(Params(), pool, tx, GetTime(), bypass_limits, test_accept);
|
||||||
return AcceptToMemoryPoolWithTime(chainparams, pool, state, tx, GetTime(), plTxnReplaced, bypass_limits, test_accept, fee_out);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CTransactionRef GetTransaction(const CBlockIndex* const block_index, const CTxMemPool* const mempool, const uint256& hash, const Consensus::Params& consensusParams, uint256& hashBlock)
|
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) {
|
if (amountdelta) {
|
||||||
pool.PrioritiseTransaction(tx->GetHash(), amountdelta);
|
pool.PrioritiseTransaction(tx->GetHash(), amountdelta);
|
||||||
}
|
}
|
||||||
TxValidationState state;
|
|
||||||
if (nTime > nNow - nExpiryTimeout) {
|
if (nTime > nNow - nExpiryTimeout) {
|
||||||
LOCK(cs_main);
|
LOCK(cs_main);
|
||||||
AcceptToMemoryPoolWithTime(chainparams, pool, state, tx, nTime,
|
if (AcceptToMemoryPoolWithTime(chainparams, pool, tx, nTime, false /* bypass_limits */,
|
||||||
nullptr /* plTxnReplaced */, false /* bypass_limits */,
|
false /* test_accept */).m_result_type == MempoolAcceptResult::ResultType::VALID) {
|
||||||
false /* test_accept */);
|
|
||||||
if (state.IsValid()) {
|
|
||||||
++count;
|
++count;
|
||||||
} else {
|
} else {
|
||||||
// mempool may contain the transaction already, e.g. from
|
// mempool may contain the transaction already, e.g. from
|
||||||
|
|||||||
+42
-7
@@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
#include <amount.h>
|
#include <amount.h>
|
||||||
#include <coins.h>
|
#include <coins.h>
|
||||||
|
#include <consensus/validation.h>
|
||||||
#include <crypto/common.h> // for ReadLE64
|
#include <crypto/common.h> // for ReadLE64
|
||||||
#include <fs.h>
|
#include <fs.h>
|
||||||
#include <optional.h>
|
#include <optional.h>
|
||||||
@@ -23,6 +24,7 @@
|
|||||||
#include <txdb.h>
|
#include <txdb.h>
|
||||||
#include <versionbits.h>
|
#include <versionbits.h>
|
||||||
#include <serialize.h>
|
#include <serialize.h>
|
||||||
|
#include <util/check.h>
|
||||||
#include <util/hasher.h>
|
#include <util/hasher.h>
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
@@ -46,7 +48,6 @@ class CConnman;
|
|||||||
class CScriptCheck;
|
class CScriptCheck;
|
||||||
class CTxMemPool;
|
class CTxMemPool;
|
||||||
class ChainstateManager;
|
class ChainstateManager;
|
||||||
class TxValidationState;
|
|
||||||
struct ChainTxData;
|
struct ChainTxData;
|
||||||
|
|
||||||
struct DisconnectedBlockTransactions;
|
struct DisconnectedBlockTransactions;
|
||||||
@@ -181,12 +182,46 @@ void UnlinkPrunedFiles(const std::set<int>& setFilesToPrune);
|
|||||||
/** Prune block files up to a given height */
|
/** Prune block files up to a given height */
|
||||||
void PruneBlockFilesManual(int nManualPruneHeight);
|
void PruneBlockFilesManual(int nManualPruneHeight);
|
||||||
|
|
||||||
/** (try to) add transaction to memory pool
|
/**
|
||||||
* plTxnReplaced will be appended to with all transactions replaced from mempool
|
* Validation result for a single transaction mempool acceptance.
|
||||||
* @param[out] fee_out optional argument to return tx fee to the caller **/
|
*/
|
||||||
bool AcceptToMemoryPool(CTxMemPool& pool, TxValidationState &state, const CTransactionRef &tx,
|
struct MempoolAcceptResult {
|
||||||
std::list<CTransactionRef>* plTxnReplaced,
|
/** Used to indicate the results of mempool validation,
|
||||||
bool bypass_limits, bool test_accept=false, CAmount* fee_out=nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
* 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. */
|
/** Get the BIP9 state for a given deployment at the current tip. */
|
||||||
ThresholdState VersionBitsTipState(const Consensus::Params& params, Consensus::DeploymentPos pos);
|
ThresholdState VersionBitsTipState(const Consensus::Params& params, Consensus::DeploymentPos pos);
|
||||||
|
|||||||
Reference in new issue
Block a user