Changeset View
Changeset View
Standalone View
Standalone View
src/validation.cpp
Show First 20 Lines • Show All 319 Lines • ▼ Show 20 Lines | for (const CTxIn &txin : tx.vin) { | ||||
assert(coinFromDisk.GetTxOut() == coin.GetTxOut()); | assert(coinFromDisk.GetTxOut() == coin.GetTxOut()); | ||||
} | } | ||||
} | } | ||||
return CheckInputs(tx, state, view, flags, cacheSigStore, true, txdata, | return CheckInputs(tx, state, view, flags, cacheSigStore, true, txdata, | ||||
nSigChecksOut); | nSigChecksOut); | ||||
} | } | ||||
/** | namespace { | ||||
* @param[out] coins_to_uncache Return any outpoints which were not previously | |||||
* present in the coins cache, but were added as a result of validating the tx | class MemPoolAccept { | ||||
* for mempool acceptance. This allows the caller | public: | ||||
* to optionally remove the cache additions if the associated transaction ends | MemPoolAccept(CTxMemPool &mempool) | ||||
* up being rejected by the mempool. | : m_pool(mempool), m_view(&m_dummy), | ||||
m_viewmempool(&::ChainstateActive().CoinsTip(), m_pool), | |||||
m_limit_ancestors( | |||||
gArgs.GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT)), | |||||
m_limit_ancestor_size( | |||||
gArgs.GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT) * | |||||
1000), | |||||
m_limit_descendants( | |||||
gArgs.GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT)), | |||||
m_limit_descendant_size(gArgs.GetArg("-limitdescendantsize", | |||||
DEFAULT_DESCENDANT_SIZE_LIMIT) * | |||||
1000) {} | |||||
// We put the arguments we're handed into a struct, so we can pass them | |||||
// around easier. | |||||
struct ATMPArgs { | |||||
const Config &m_config; | |||||
TxValidationState &m_state; | |||||
const int64_t m_accept_time; | |||||
const bool m_bypass_limits; | |||||
const Amount &m_absurd_fee; | |||||
/* | |||||
* Return any outpoints which were not previously present in the coins | |||||
* cache, but were added as a result of validating the tx for mempool | |||||
* acceptance. This allows the caller to optionally remove the cache | |||||
* additions if the associated transaction ends up being rejected by | |||||
* the mempool. | |||||
*/ | */ | ||||
static bool AcceptToMemoryPoolWorker( | std::vector<COutPoint> &m_coins_to_uncache; | ||||
const Config &config, CTxMemPool &pool, TxValidationState &state, | const bool m_test_accept; | ||||
const CTransactionRef &ptx, int64_t nAcceptTime, bool bypass_limits, | }; | ||||
const Amount nAbsurdFee, std::vector<COutPoint> &coins_to_uncache, | |||||
bool test_accept) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { | |||||
AssertLockHeld(cs_main); | |||||
const Consensus::Params &consensusParams = | // Single transaction acceptance | ||||
config.GetChainParams().GetConsensus(); | bool AcceptSingleTransaction(const CTransactionRef &ptx, ATMPArgs &args) | ||||
EXCLUSIVE_LOCKS_REQUIRED(cs_main); | |||||
const CTransaction &tx = *ptx; | private: | ||||
const TxId txid = tx.GetId(); | // All the intermediate state that gets passed between the various levels | ||||
// of checking a given transaction. | |||||
struct Workspace { | |||||
Workspace(const CTransactionRef &ptx, | |||||
const uint32_t next_block_script_verify_flags) | |||||
: m_ptx(ptx), | |||||
m_next_block_script_verify_flags(next_block_script_verify_flags) { | |||||
} | |||||
CTxMemPool::setEntries m_ancestors; | |||||
std::unique_ptr<CTxMemPoolEntry> m_entry; | |||||
Amount m_modified_fees; | |||||
const CTransactionRef &m_ptx; | |||||
// ABC specific flags that are used in both PreChecks and | |||||
// ConsensusScriptChecks | |||||
const uint32_t m_next_block_script_verify_flags; | |||||
int m_sig_checks_standard; | |||||
}; | |||||
// mempool "read lock" (held through | // Run the policy checks on a given transaction, excluding any script | ||||
// GetMainSignals().TransactionAddedToMempool()) | // checks. Looks up inputs, calculates feerate, considers replacement, | ||||
LOCK(pool.cs); | // evaluates package limits, etc. As this function can be invoked for "free" | ||||
// by a peer, only tests that are fast should be done here (to avoid CPU | |||||
// DoS). | |||||
bool PreChecks(ATMPArgs &args, Workspace &ws) | |||||
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, Workspace &ws, | |||||
PrecomputedTransactionData &txdata) | |||||
EXCLUSIVE_LOCKS_REQUIRED(cs_main); | |||||
// 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); | |||||
private: | |||||
CTxMemPool &m_pool; | |||||
CCoinsViewCache m_view; | |||||
CCoinsViewMemPool m_viewmempool; | |||||
CCoinsView m_dummy; | |||||
// The package limits in effect at the time of invocation. | |||||
const size_t m_limit_ancestors; | |||||
const size_t m_limit_ancestor_size; | |||||
// These may be modified while evaluating a transaction (eg to account for | |||||
// in-mempool conflicts; see below). | |||||
size_t m_limit_descendants; | |||||
size_t m_limit_descendant_size; | |||||
}; | |||||
bool MemPoolAccept::PreChecks(ATMPArgs &args, Workspace &ws) { | |||||
const CTransactionRef &ptx = ws.m_ptx; | |||||
const CTransaction &tx = *ws.m_ptx; | |||||
const TxId &txid = ws.m_ptx->GetId(); | |||||
// 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; | |||||
const Amount &nAbsurdFee = args.m_absurd_fee; | |||||
std::vector<COutPoint> &coins_to_uncache = args.m_coins_to_uncache; | |||||
// Alias what we need out of ws | |||||
CTxMemPool::setEntries &setAncestors = ws.m_ancestors; | |||||
std::unique_ptr<CTxMemPoolEntry> &entry = ws.m_entry; | |||||
Amount &nModifiedFees = ws.m_modified_fees; | |||||
// Coinbase is only valid in a block, not as a loose transaction. | // Coinbase is only valid in a block, not as a loose transaction. | ||||
if (!CheckRegularTransaction(tx, state)) { | if (!CheckRegularTransaction(tx, state)) { | ||||
// state filled in by CheckRegularTransaction. | // state filled in by CheckRegularTransaction. | ||||
return false; | return false; | ||||
} | } | ||||
// Rather not work on nonstandard transactions (unless -testnet) | // Rather not work on nonstandard transactions (unless -testnet) | ||||
std::string reason; | std::string reason; | ||||
if (fRequireStandard && !IsStandardTx(tx, reason)) { | if (fRequireStandard && !IsStandardTx(tx, reason)) { | ||||
return state.Invalid(TxValidationResult::TX_NOT_STANDARD, reason); | return state.Invalid(TxValidationResult::TX_NOT_STANDARD, reason); | ||||
} | } | ||||
// Only accept nLockTime-using transactions that can be mined in the next | // Only accept nLockTime-using transactions that can be mined in the next | ||||
// block; we don't want our mempool filled up with transactions that can't | // block; we don't want our mempool filled up with transactions that can't | ||||
// be mined yet. | // be mined yet. | ||||
TxValidationState ctxState; | TxValidationState ctxState; | ||||
if (!ContextualCheckTransactionForCurrentBlock( | if (!ContextualCheckTransactionForCurrentBlock( | ||||
consensusParams, tx, ctxState, STANDARD_LOCKTIME_VERIFY_FLAGS)) { | args.m_config.GetChainParams().GetConsensus(), tx, ctxState, | ||||
STANDARD_LOCKTIME_VERIFY_FLAGS)) { | |||||
// We copy the state from a dummy to ensure we don't increase the | // We copy the state from a dummy to ensure we don't increase the | ||||
// ban score of peer for transaction that could be valid in the future. | // ban score of peer for transaction that could be valid in the future. | ||||
return state.Invalid(TxValidationResult::TX_PREMATURE_SPEND, | return state.Invalid(TxValidationResult::TX_PREMATURE_SPEND, | ||||
ctxState.GetRejectReason(), | ctxState.GetRejectReason(), | ||||
ctxState.GetDebugMessage()); | ctxState.GetDebugMessage()); | ||||
} | } | ||||
// Is it already in the memory pool? | // Is it already in the memory pool? | ||||
if (pool.exists(txid)) { | if (m_pool.exists(txid)) { | ||||
return state.Invalid(TxValidationResult::TX_CONFLICT, | return state.Invalid(TxValidationResult::TX_CONFLICT, | ||||
"txn-already-in-mempool"); | "txn-already-in-mempool"); | ||||
} | } | ||||
// Check for conflicts with in-memory transactions | // Check for conflicts with in-memory transactions | ||||
for (const CTxIn &txin : tx.vin) { | for (const CTxIn &txin : tx.vin) { | ||||
auto itConflicting = pool.mapNextTx.find(txin.prevout); | auto itConflicting = m_pool.mapNextTx.find(txin.prevout); | ||||
if (itConflicting != pool.mapNextTx.end()) { | if (itConflicting != m_pool.mapNextTx.end()) { | ||||
// Disable replacement feature for good | // Disable replacement feature for good | ||||
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, | return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, | ||||
"txn-mempool-conflict"); | "txn-mempool-conflict"); | ||||
} | } | ||||
} | } | ||||
{ | |||||
CCoinsView dummy; | |||||
CCoinsViewCache view(&dummy); | |||||
LockPoints lp; | LockPoints lp; | ||||
CCoinsViewCache &coins_cache = ::ChainstateActive().CoinsTip(); | m_view.SetBackend(m_viewmempool); | ||||
CCoinsViewMemPool viewMemPool(&coins_cache, pool); | |||||
view.SetBackend(viewMemPool); | |||||
CCoinsViewCache &coins_cache = ::ChainstateActive().CoinsTip(); | |||||
// Do all inputs exist? | // Do all inputs exist? | ||||
for (const CTxIn &txin : tx.vin) { | for (const CTxIn &txin : tx.vin) { | ||||
if (!coins_cache.HaveCoinInCache(txin.prevout)) { | if (!coins_cache.HaveCoinInCache(txin.prevout)) { | ||||
coins_to_uncache.push_back(txin.prevout); | coins_to_uncache.push_back(txin.prevout); | ||||
} | } | ||||
// Note: this call may add txin.prevout to the coins cache | // Note: this call may add txin.prevout to the coins cache | ||||
// (CoinsTip().cacheCoins) by way of FetchCoin(). It should be | // (coins_cache.cacheCoins) by way of FetchCoin(). It should be | ||||
// removed later (via coins_to_uncache) if this tx turns out to be | // removed later (via coins_to_uncache) if this tx turns out to be | ||||
// invalid. | // invalid. | ||||
if (!view.HaveCoin(txin.prevout)) { | if (!m_view.HaveCoin(txin.prevout)) { | ||||
// Are inputs missing because we already have the tx? | // Are inputs missing because we already have the tx? | ||||
for (size_t out = 0; out < tx.vout.size(); out++) { | for (size_t out = 0; out < tx.vout.size(); out++) { | ||||
// Optimistically just do efficient check of cache for | // Optimistically just do efficient check of cache for | ||||
// outputs. | // outputs. | ||||
if (coins_cache.HaveCoinInCache(COutPoint(txid, out))) { | if (coins_cache.HaveCoinInCache(COutPoint(txid, out))) { | ||||
return state.Invalid(TxValidationResult::TX_CONFLICT, | return state.Invalid(TxValidationResult::TX_CONFLICT, | ||||
"txn-already-known"); | "txn-already-known"); | ||||
} | } | ||||
} | } | ||||
// Otherwise assume this might be an orphan tx for which we just | // Otherwise assume this might be an orphan tx for which we just | ||||
// haven't seen parents yet. | // haven't seen parents yet. | ||||
return state.Invalid(TxValidationResult::TX_MISSING_INPUTS, | return state.Invalid(TxValidationResult::TX_MISSING_INPUTS, | ||||
"bad-txns-inputs-missingorspent"); | "bad-txns-inputs-missingorspent"); | ||||
} | } | ||||
} | } | ||||
// Are the actual inputs available? | // Are the actual inputs available? | ||||
if (!view.HaveInputs(tx)) { | if (!m_view.HaveInputs(tx)) { | ||||
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, | return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, | ||||
"bad-txns-inputs-spent"); | "bad-txns-inputs-spent"); | ||||
} | } | ||||
// Bring the best block into scope. | // Bring the best block into scope. | ||||
view.GetBestBlock(); | m_view.GetBestBlock(); | ||||
// We have all inputs cached now, so switch back to dummy, so we don't | // we have all inputs cached now, so switch back to dummy (to protect | ||||
// need to keep lock on mempool. | // against bugs where we pull more inputs from disk that miss being | ||||
view.SetBackend(dummy); | // added to coins_to_uncache) | ||||
m_view.SetBackend(m_dummy); | |||||
// Only accept BIP68 sequence locked transactions that can be mined in | // Only accept BIP68 sequence locked transactions that can be mined in | ||||
// the next block; we don't want our mempool filled up with transactions | // the next block; we don't want our mempool filled up with transactions | ||||
// that can't be mined yet. Must keep pool.cs for this unless we change | // that can't be mined yet. Must keep pool.cs for this unless we change | ||||
// CheckSequenceLocks to take a CoinsViewCache instead of create its | // CheckSequenceLocks to take a CoinsViewCache instead of create its | ||||
// own. | // own. | ||||
if (!CheckSequenceLocks(pool, tx, STANDARD_LOCKTIME_VERIFY_FLAGS, | if (!CheckSequenceLocks(m_pool, tx, STANDARD_LOCKTIME_VERIFY_FLAGS, &lp)) { | ||||
&lp)) { | |||||
return state.Invalid(TxValidationResult::TX_PREMATURE_SPEND, | return state.Invalid(TxValidationResult::TX_PREMATURE_SPEND, | ||||
"non-BIP68-final"); | "non-BIP68-final"); | ||||
} | } | ||||
Amount nFees = Amount::zero(); | Amount nFees = Amount::zero(); | ||||
if (!Consensus::CheckTxInputs(tx, state, view, GetSpendHeight(view), | if (!Consensus::CheckTxInputs(tx, state, m_view, GetSpendHeight(m_view), | ||||
nFees)) { | nFees)) { | ||||
return error("%s: Consensus::CheckTxInputs: %s, %s", __func__, | return error("%s: Consensus::CheckTxInputs: %s, %s", __func__, | ||||
tx.GetId().ToString(), state.ToString()); | tx.GetId().ToString(), state.ToString()); | ||||
} | } | ||||
const uint32_t nextBlockScriptVerifyFlags = | |||||
GetNextBlockScriptFlags(consensusParams, ::ChainActive().Tip()); | |||||
// Check for non-standard pay-to-script-hash in inputs | // Check for non-standard pay-to-script-hash in inputs | ||||
if (fRequireStandard && | if (fRequireStandard && | ||||
!AreInputsStandard(tx, view, nextBlockScriptVerifyFlags)) { | !AreInputsStandard(tx, m_view, ws.m_next_block_script_verify_flags)) { | ||||
return state.Invalid(TxValidationResult::TX_NOT_STANDARD, | return state.Invalid(TxValidationResult::TX_NOT_STANDARD, | ||||
"bad-txns-nonstandard-inputs"); | "bad-txns-nonstandard-inputs"); | ||||
} | } | ||||
// nModifiedFees includes any fee deltas from PrioritiseTransaction | // nModifiedFees includes any fee deltas from PrioritiseTransaction | ||||
Amount nModifiedFees = nFees; | nModifiedFees = nFees; | ||||
pool.ApplyDelta(txid, nModifiedFees); | m_pool.ApplyDelta(txid, 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 | ||||
// during reorgs to ensure COINBASE_MATURITY is still met. | // during reorgs to ensure COINBASE_MATURITY is still met. | ||||
bool fSpendsCoinbase = false; | bool fSpendsCoinbase = false; | ||||
for (const CTxIn &txin : tx.vin) { | for (const CTxIn &txin : tx.vin) { | ||||
const Coin &coin = view.AccessCoin(txin.prevout); | const Coin &coin = m_view.AccessCoin(txin.prevout); | ||||
if (coin.IsCoinBase()) { | if (coin.IsCoinBase()) { | ||||
fSpendsCoinbase = true; | fSpendsCoinbase = true; | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
unsigned int nSize = tx.GetTotalSize(); | unsigned int nSize = tx.GetTotalSize(); | ||||
// No transactions are allowed below minRelayTxFee except from | // No transactions are allowed below minRelayTxFee except from disconnected | ||||
// disconnected blocks. | // blocks. | ||||
// Do not change this to use virtualsize without coordinating a network | // Do not change this to use virtualsize without coordinating a network | ||||
// policy upgrade. | // policy upgrade. | ||||
if (!bypass_limits && nModifiedFees < minRelayTxFee.GetFee(nSize)) { | if (!bypass_limits && nModifiedFees < minRelayTxFee.GetFee(nSize)) { | ||||
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, | return state.Invalid( | ||||
"min relay fee not met", | TxValidationResult::TX_MEMPOOL_POLICY, "min relay fee not met", | ||||
strprintf("%d < %d", nModifiedFees, | strprintf("%d < %d", nModifiedFees, ::minRelayTxFee.GetFee(nSize))); | ||||
::minRelayTxFee.GetFee(nSize))); | |||||
} | } | ||||
if (nAbsurdFee != Amount::zero() && nFees > nAbsurdFee) { | if (nAbsurdFee != Amount::zero() && nFees > nAbsurdFee) { | ||||
return state.Invalid(TxValidationResult::TX_NOT_STANDARD, | return state.Invalid(TxValidationResult::TX_NOT_STANDARD, | ||||
"absurdly-high-fee", | "absurdly-high-fee", | ||||
strprintf("%d > %d", nFees, nAbsurdFee)); | strprintf("%d > %d", nFees, nAbsurdFee)); | ||||
} | } | ||||
// Validate input scripts against standard script flags. | // Validate input scripts against standard script flags. | ||||
const uint32_t scriptVerifyFlags = | const uint32_t scriptVerifyFlags = | ||||
nextBlockScriptVerifyFlags | STANDARD_SCRIPT_VERIFY_FLAGS; | ws.m_next_block_script_verify_flags | STANDARD_SCRIPT_VERIFY_FLAGS; | ||||
PrecomputedTransactionData txdata(tx); | PrecomputedTransactionData txdata(tx); | ||||
int nSigChecksStandard; | if (!CheckInputs(tx, state, m_view, scriptVerifyFlags, true, false, txdata, | ||||
if (!CheckInputs(tx, state, view, scriptVerifyFlags, true, false, | ws.m_sig_checks_standard)) { | ||||
txdata, nSigChecksStandard)) { | |||||
// State filled in by CheckInputs. | // State filled in by CheckInputs. | ||||
return false; | return false; | ||||
} | } | ||||
CTxMemPoolEntry entry(ptx, nFees, nAcceptTime, ::ChainActive().Height(), | entry.reset(new CTxMemPoolEntry(ptx, nFees, nAcceptTime, | ||||
fSpendsCoinbase, nSigChecksStandard, lp); | ::ChainActive().Height(), fSpendsCoinbase, | ||||
ws.m_sig_checks_standard, lp)); | |||||
unsigned int nVirtualSize = entry.GetTxVirtualSize(); | unsigned int nVirtualSize = entry->GetTxVirtualSize(); | ||||
Amount mempoolRejectFee = | Amount mempoolRejectFee = | ||||
pool.GetMinFee( | m_pool | ||||
gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * | .GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * | ||||
1000000) | 1000000) | ||||
.GetFee(nVirtualSize); | .GetFee(nVirtualSize); | ||||
if (!bypass_limits && mempoolRejectFee > Amount::zero() && | if (!bypass_limits && mempoolRejectFee > Amount::zero() && | ||||
nModifiedFees < mempoolRejectFee) { | nModifiedFees < mempoolRejectFee) { | ||||
return state.Invalid( | return state.Invalid( | ||||
TxValidationResult::TX_MEMPOOL_POLICY, | TxValidationResult::TX_MEMPOOL_POLICY, "mempool min fee not met", | ||||
"mempool min fee not met", | |||||
strprintf("%d < %d", nModifiedFees, mempoolRejectFee)); | strprintf("%d < %d", nModifiedFees, mempoolRejectFee)); | ||||
} | } | ||||
// Calculate in-mempool ancestors, up to a limit. | // Calculate in-mempool ancestors, up to a limit. | ||||
CTxMemPool::setEntries setAncestors; | |||||
size_t nLimitAncestors = | |||||
gArgs.GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT); | |||||
size_t nLimitAncestorSize = | |||||
gArgs.GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT) * | |||||
1000; | |||||
size_t nLimitDescendants = | |||||
gArgs.GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT); | |||||
size_t nLimitDescendantSize = | |||||
gArgs.GetArg("-limitdescendantsize", | |||||
DEFAULT_DESCENDANT_SIZE_LIMIT) * | |||||
1000; | |||||
std::string errString; | std::string errString; | ||||
if (!pool.CalculateMemPoolAncestors( | if (!m_pool.CalculateMemPoolAncestors( | ||||
entry, setAncestors, nLimitAncestors, nLimitAncestorSize, | *entry, setAncestors, m_limit_ancestors, m_limit_ancestor_size, | ||||
nLimitDescendants, nLimitDescendantSize, errString)) { | m_limit_descendants, m_limit_descendant_size, errString)) { | ||||
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, | return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, | ||||
"too-long-mempool-chain", errString); | "too-long-mempool-chain", errString); | ||||
} | } | ||||
return true; | |||||
} | |||||
bool MemPoolAccept::ConsensusScriptChecks(ATMPArgs &args, Workspace &ws, | |||||
PrecomputedTransactionData &txdata) { | |||||
const CTransaction &tx = *ws.m_ptx; | |||||
const TxId &txid = tx.GetId(); | |||||
TxValidationState &state = args.m_state; | |||||
// Check again against the next block's script verification flags | // Check again against the next block's script verification flags | ||||
// to cache our script execution flags. | // to cache our script execution flags. | ||||
// | // | ||||
// This is also useful in case of bugs in the standard flags that cause | // This is also useful in case of bugs in the standard flags that cause | ||||
// transactions to pass as valid when they're actually invalid. For | // transactions to pass as valid when they're actually invalid. For | ||||
// instance the STRICTENC flag was incorrectly allowing certain CHECKSIG | // instance the STRICTENC flag was incorrectly allowing certain CHECKSIG | ||||
// NOT scripts to pass, even though they were invalid. | // NOT scripts to pass, even though they were invalid. | ||||
// | // | ||||
// There is a similar check in CreateNewBlock() to prevent creating | // There is a similar check in CreateNewBlock() to prevent creating | ||||
// invalid blocks (using TestBlockValidity), however allowing such | // invalid blocks (using TestBlockValidity), however allowing such | ||||
// transactions into the mempool can be exploited as a DoS attack. | // transactions into the mempool can be exploited as a DoS attack. | ||||
int nSigChecksConsensus; | int nSigChecksConsensus; | ||||
if (!CheckInputsFromMempoolAndCache(tx, state, view, pool, | if (!CheckInputsFromMempoolAndCache(tx, state, m_view, m_pool, | ||||
nextBlockScriptVerifyFlags, true, | ws.m_next_block_script_verify_flags, | ||||
txdata, nSigChecksConsensus)) { | true, txdata, nSigChecksConsensus)) { | ||||
// This can occur under some circumstances, if the node receives an | // This can occur under some circumstances, if the node receives an | ||||
// unrequested tx which is invalid due to new consensus rules not | // unrequested tx which is invalid due to new consensus rules not | ||||
// being activated yet (during IBD). | // being activated yet (during IBD). | ||||
return error("%s: BUG! PLEASE REPORT THIS! CheckInputs failed " | return error("%s: BUG! PLEASE REPORT THIS! CheckInputs failed " | ||||
"against next-block but not STANDARD flags %s, %s", | "against next-block but not STANDARD flags %s, %s", | ||||
__func__, txid.ToString(), state.ToString()); | __func__, txid.ToString(), state.ToString()); | ||||
} | } | ||||
if (nSigChecksStandard != nSigChecksConsensus) { | if (ws.m_sig_checks_standard != nSigChecksConsensus) { | ||||
// We can't accept this transaction as we've used the standard count | // We can't accept this transaction as we've used the standard count | ||||
// for the mempool/mining, but the consensus count will be enforced | // for the mempool/mining, but the consensus count will be enforced | ||||
// in validation (we don't want to produce bad block templates). | // in validation (we don't want to produce bad block templates). | ||||
return error( | return error( | ||||
"%s: BUG! PLEASE REPORT THIS! SigChecks count differed between " | "%s: BUG! PLEASE REPORT THIS! SigChecks count differed between " | ||||
"standard and consensus flags in %s", | "standard and consensus flags in %s", | ||||
__func__, txid.ToString()); | __func__, txid.ToString()); | ||||
} | } | ||||
if (test_accept) { | |||||
// Tx was accepted, but not added | |||||
return true; | return true; | ||||
} | } | ||||
bool MemPoolAccept::Finalize(ATMPArgs &args, Workspace &ws) { | |||||
const TxId &txid = ws.m_ptx->GetId(); | |||||
TxValidationState &state = args.m_state; | |||||
const bool bypass_limits = args.m_bypass_limits; | |||||
CTxMemPool::setEntries &setAncestors = ws.m_ancestors; | |||||
std::unique_ptr<CTxMemPoolEntry> &entry = ws.m_entry; | |||||
// Store transaction in memory. | // Store transaction in memory. | ||||
pool.addUnchecked(entry, setAncestors); | m_pool.addUnchecked(*entry, setAncestors); | ||||
// Trim mempool and check if tx was trimmed. | // Trim mempool and check if tx was trimmed. | ||||
if (!bypass_limits) { | if (!bypass_limits) { | ||||
pool.LimitSize( | m_pool.LimitSize( | ||||
gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, | gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, | ||||
std::chrono::hours{ | std::chrono::hours{ | ||||
gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY)}); | gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY)}); | ||||
if (!pool.exists(txid)) { | if (!m_pool.exists(txid)) { | ||||
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, | return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, | ||||
"mempool full"); | "mempool full"); | ||||
} | } | ||||
} | } | ||||
return true; | |||||
} | |||||
bool MemPoolAccept::AcceptSingleTransaction(const CTransactionRef &ptx, | |||||
ATMPArgs &args) { | |||||
AssertLockHeld(cs_main); | |||||
// mempool "read lock" (held through | |||||
// GetMainSignals().TransactionAddedToMempool()) | |||||
LOCK(m_pool.cs); | |||||
Workspace workspace(ptx, GetNextBlockScriptFlags( | |||||
args.m_config.GetChainParams().GetConsensus(), | |||||
::ChainActive().Tip())); | |||||
if (!PreChecks(args, workspace)) { | |||||
return false; | |||||
} | |||||
// Only compute the precomputed transaction data if we need to verify | |||||
// scripts (ie, other policy checks pass). We perform the inexpensive | |||||
// checks first and avoid hashing and signature verification unless those | |||||
// checks pass, to mitigate CPU exhaustion denial-of-service attacks. | |||||
PrecomputedTransactionData txdata(*ptx); | |||||
if (!ConsensusScriptChecks(args, workspace, txdata)) { | |||||
return false; | |||||
} | |||||
// Tx was accepted, but not added | |||||
if (args.m_test_accept) { | |||||
return true; | |||||
} | |||||
if (!Finalize(args, workspace)) { | |||||
return false; | |||||
} | } | ||||
GetMainSignals().TransactionAddedToMempool(ptx); | GetMainSignals().TransactionAddedToMempool(ptx); | ||||
return true; | return true; | ||||
} | } | ||||
} // 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 | static bool | ||||
AcceptToMemoryPoolWithTime(const Config &config, CTxMemPool &pool, | AcceptToMemoryPoolWithTime(const Config &config, CTxMemPool &pool, | ||||
TxValidationState &state, const CTransactionRef &tx, | TxValidationState &state, const CTransactionRef &tx, | ||||
int64_t nAcceptTime, bool bypass_limits, | int64_t nAcceptTime, bool bypass_limits, | ||||
const Amount nAbsurdFee, bool test_accept) | const Amount nAbsurdFee, bool test_accept) | ||||
EXCLUSIVE_LOCKS_REQUIRED(cs_main) { | EXCLUSIVE_LOCKS_REQUIRED(cs_main) { | ||||
AssertLockHeld(cs_main); | AssertLockHeld(cs_main); | ||||
std::vector<COutPoint> coins_to_uncache; | std::vector<COutPoint> coins_to_uncache; | ||||
bool res = AcceptToMemoryPoolWorker(config, pool, state, tx, nAcceptTime, | MemPoolAccept::ATMPArgs args{config, state, nAcceptTime, | ||||
bypass_limits, nAbsurdFee, | bypass_limits, nAbsurdFee, coins_to_uncache, | ||||
coins_to_uncache, test_accept); | test_accept}; | ||||
bool res = MemPoolAccept(pool).AcceptSingleTransaction(tx, args); | |||||
if (!res) { | if (!res) { | ||||
// Remove coins that were not present in the coins cache before calling | // 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 | // ATMPW; this is to prevent memory DoS in case we receive a large | ||||
// number of invalid transactions that attempt to overrun the in-memory | // number of invalid transactions that attempt to overrun the in-memory | ||||
// coins cache | // coins cache | ||||
// (`CCoinsViewCache::cacheCoins`). | // (`CCoinsViewCache::cacheCoins`). | ||||
for (const COutPoint &outpoint : coins_to_uncache) { | for (const COutPoint &outpoint : coins_to_uncache) { | ||||
▲ Show 20 Lines • Show All 5,265 Lines • Show Last 20 Lines |