[refactor] return MempoolAcceptResult
This creates a cleaner interface with ATMP, allows us to make results const, and makes accessing values that don't make sense (e.g. fee when tx is invalid) an error.
This commit is contained in:
1 parent
9db10a5506
commit
f82baf0762
9 files changed
+106
-92
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;
|
||||||
@@ -3197,10 +3197,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.
|
||||||
@@ -3223,7 +3223,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.
|
||||||
|
|||||||
@@ -946,18 +946,13 @@ 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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) {
|
||||||
|
const CAmount fee = accept_result.m_base_fees.value();
|
||||||
// Check that fee does not exceed maximum fee
|
// Check that fee does not exceed maximum fee
|
||||||
if (max_raw_tx_fee && fee > max_raw_tx_fee) {
|
if (max_raw_tx_fee && fee > max_raw_tx_fee) {
|
||||||
result_0.pushKV("allowed", false);
|
result_0.pushKV("allowed", false);
|
||||||
@@ -972,6 +967,7 @@ static RPCHelpMan testmempoolaccept()
|
|||||||
result.push_back(std::move(result_0));
|
result.push_back(std::move(result_0));
|
||||||
} else {
|
} else {
|
||||||
result_0.pushKV("allowed", false);
|
result_0.pushKV("allowed", false);
|
||||||
|
const TxValidationState state = accept_result.m_state;
|
||||||
if (state.GetResult() == TxValidationResult::TX_MISSING_INPUTS) {
|
if (state.GetResult() == TxValidationResult::TX_MISSING_INPUTS) {
|
||||||
result_0.pushKV("reject-reason", "missing-inputs");
|
result_0.pushKV("reject-reason", "missing-inputs");
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -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));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+29
-35
@@ -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);
|
||||||
@@ -465,7 +463,7 @@ public:
|
|||||||
const CChainParams& m_chainparams;
|
const CChainParams& m_chainparams;
|
||||||
TxValidationState &m_state;
|
TxValidationState &m_state;
|
||||||
const int64_t m_accept_time;
|
const int64_t m_accept_time;
|
||||||
std::list<CTransactionRef>* m_replaced_transactions;
|
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 +474,11 @@ 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;
|
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
|
||||||
@@ -688,10 +686,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
|
|||||||
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
|
args.m_fee_out = nFees;
|
||||||
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();
|
||||||
@@ -1007,8 +1002,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)
|
args.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 +1025,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 workspace(ptx);
|
||||||
|
|
||||||
if (!PreChecks(args, workspace)) return false;
|
if (!PreChecks(args, workspace)) return MempoolAcceptResult(args.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 +1040,36 @@ 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, workspace, txdata)) return MempoolAcceptResult(args.m_state);
|
||||||
|
|
||||||
if (!ConsensusScriptChecks(args, workspace, txdata)) return false;
|
if (!ConsensusScriptChecks(args, workspace, txdata)) return MempoolAcceptResult(args.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(args.m_replaced_transactions), args.m_fee_out);
|
||||||
|
}
|
||||||
|
|
||||||
if (!Finalize(args, workspace)) return false;
|
if (!Finalize(args, workspace)) return MempoolAcceptResult(args.m_state);
|
||||||
|
|
||||||
GetMainSignals().TransactionAddedToMempool(ptx, m_pool.GetAndIncrementSequence());
|
GetMainSignals().TransactionAddedToMempool(ptx, m_pool.GetAndIncrementSequence());
|
||||||
|
|
||||||
return true;
|
return MempoolAcceptResult(std::move(args.m_replaced_transactions), args.m_fee_out);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // 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)
|
||||||
{
|
{
|
||||||
|
TxValidationState state;
|
||||||
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, state, 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 +1081,13 @@ 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();
|
const CChainParams& chainparams = Params();
|
||||||
return AcceptToMemoryPoolWithTime(chainparams, pool, state, tx, GetTime(), plTxnReplaced, bypass_limits, test_accept, fee_out);
|
return AcceptToMemoryPoolWithTime(chainparams, 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)
|
CTransactionRef GetTransaction(const CBlockIndex* const block_index, const CTxMemPool* const mempool, const uint256& hash, const Consensus::Params& consensusParams, uint256& hashBlock)
|
||||||
@@ -5029,13 +5026,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