Changeset View
Changeset View
Standalone View
Standalone View
src/validation.cpp
Show First 20 Lines • Show All 341 Lines • ▼ Show 20 Lines | MemPoolAccept(CTxMemPool &mempool, CChainState &active_chainstate) | ||||
m_limit_descendant_size(gArgs.GetArg("-limitdescendantsize", | m_limit_descendant_size(gArgs.GetArg("-limitdescendantsize", | ||||
DEFAULT_DESCENDANT_SIZE_LIMIT) * | DEFAULT_DESCENDANT_SIZE_LIMIT) * | ||||
1000) {} | 1000) {} | ||||
// We put the arguments we're handed into a struct, so we can pass them | // We put the arguments we're handed into a struct, so we can pass them | ||||
// around easier. | // around easier. | ||||
struct ATMPArgs { | struct ATMPArgs { | ||||
const Config &m_config; | const Config &m_config; | ||||
TxValidationState &m_state; | |||||
const int64_t m_accept_time; | const int64_t m_accept_time; | ||||
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 | ||||
* cache, but were added as a result of validating the tx for mempool | * cache, but were added as a result of validating the tx for mempool | ||||
* acceptance. This allows the caller to optionally remove the cache | * acceptance. This allows the caller to optionally remove the cache | ||||
* additions if the associated transaction ends up being rejected by | * additions if the associated transaction ends up being rejected by | ||||
* the mempool. | * the mempool. | ||||
*/ | */ | ||||
std::vector<COutPoint> &m_coins_to_uncache; | std::vector<COutPoint> &m_coins_to_uncache; | ||||
const bool m_test_accept; | const bool m_test_accept; | ||||
Amount m_fee_out; | |||||
}; | }; | ||||
// Single transaction acceptance | // Single transaction acceptance | ||||
MempoolAcceptResult AcceptSingleTransaction(const CTransactionRef &ptx, | MempoolAcceptResult AcceptSingleTransaction(const CTransactionRef &ptx, | ||||
ATMPArgs &args) | ATMPArgs &args) | ||||
EXCLUSIVE_LOCKS_REQUIRED(cs_main); | 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 | ||||
// of checking a given transaction. | // of checking a given transaction. | ||||
struct Workspace { | struct Workspace { | ||||
Workspace(const CTransactionRef &ptx, | Workspace(const CTransactionRef &ptx, | ||||
const uint32_t next_block_script_verify_flags) | const uint32_t next_block_script_verify_flags) | ||||
: m_ptx(ptx), | : m_ptx(ptx), | ||||
m_next_block_script_verify_flags(next_block_script_verify_flags) { | m_next_block_script_verify_flags(next_block_script_verify_flags) { | ||||
} | } | ||||
CTxMemPool::setEntries m_ancestors; | CTxMemPool::setEntries m_ancestors; | ||||
std::unique_ptr<CTxMemPoolEntry> m_entry; | std::unique_ptr<CTxMemPoolEntry> m_entry; | ||||
Amount m_fee_out; | |||||
Amount m_modified_fees; | Amount m_modified_fees; | ||||
const CTransactionRef &m_ptx; | const CTransactionRef &m_ptx; | ||||
TxValidationState m_state; | |||||
// ABC specific flags that are used in both PreChecks and | // ABC specific flags that are used in both PreChecks and | ||||
// ConsensusScriptChecks | // ConsensusScriptChecks | ||||
const uint32_t m_next_block_script_verify_flags; | const uint32_t m_next_block_script_verify_flags; | ||||
int m_sig_checks_standard; | int m_sig_checks_standard; | ||||
}; | }; | ||||
// Run the policy checks on a given transaction, excluding any script | // Run the policy checks on a given transaction, excluding any script | ||||
// checks. Looks up inputs, calculates feerate, considers replacement, | // checks. Looks up inputs, calculates feerate, considers replacement, | ||||
// evaluates package limits, etc. As this function can be invoked for "free" | // 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 | // by a peer, only tests that are fast should be done here (to avoid CPU | ||||
// DoS). | // DoS). | ||||
bool PreChecks(ATMPArgs &args, Workspace &ws) | bool PreChecks(ATMPArgs &args, Workspace &ws) | ||||
EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs); | 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, | bool ConsensusScriptChecks(const ATMPArgs &args, Workspace &ws, | ||||
PrecomputedTransactionData &txdata) | PrecomputedTransactionData &txdata) | ||||
EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs); | 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) | bool Finalize(const ATMPArgs &args, Workspace &ws) | ||||
EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs); | EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs); | ||||
private: | private: | ||||
CTxMemPool &m_pool; | CTxMemPool &m_pool; | ||||
CCoinsViewCache m_view; | CCoinsViewCache m_view; | ||||
CCoinsViewMemPool m_viewmempool; | CCoinsViewMemPool m_viewmempool; | ||||
CCoinsView m_dummy; | CCoinsView m_dummy; | ||||
Show All 9 Lines | |||||
}; | }; | ||||
bool MemPoolAccept::PreChecks(ATMPArgs &args, Workspace &ws) { | bool MemPoolAccept::PreChecks(ATMPArgs &args, Workspace &ws) { | ||||
const CTransactionRef &ptx = ws.m_ptx; | const CTransactionRef &ptx = ws.m_ptx; | ||||
const CTransaction &tx = *ws.m_ptx; | const CTransaction &tx = *ws.m_ptx; | ||||
const TxId &txid = ws.m_ptx->GetId(); | const TxId &txid = ws.m_ptx->GetId(); | ||||
// 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; | |||||
CTxMemPool::setEntries &setAncestors = ws.m_ancestors; | CTxMemPool::setEntries &setAncestors = ws.m_ancestors; | ||||
std::unique_ptr<CTxMemPoolEntry> &entry = ws.m_entry; | std::unique_ptr<CTxMemPoolEntry> &entry = ws.m_entry; | ||||
Amount &nModifiedFees = ws.m_modified_fees; | 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; | ||||
▲ Show 20 Lines • Show All 88 Lines • ▼ Show 20 Lines | bool MemPoolAccept::PreChecks(ATMPArgs &args, Workspace &ws) { | ||||
// CheckSequenceLocks to take a CoinsViewCache instead of create its | // CheckSequenceLocks to take a CoinsViewCache instead of create its | ||||
// own. | // own. | ||||
if (!CheckSequenceLocks(m_active_chainstate, m_pool, tx, | if (!CheckSequenceLocks(m_active_chainstate, m_pool, tx, | ||||
STANDARD_LOCKTIME_VERIFY_FLAGS, &lp)) { | STANDARD_LOCKTIME_VERIFY_FLAGS, &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(); | |||||
if (!Consensus::CheckTxInputs( | if (!Consensus::CheckTxInputs( | ||||
tx, state, m_view, | tx, state, m_view, | ||||
m_active_chainstate.m_blockman.GetSpendHeight(m_view), nFees)) { | m_active_chainstate.m_blockman.GetSpendHeight(m_view), | ||||
ws.m_fee_out)) { | |||||
// state filled in by CheckTxInputs | // state filled in by CheckTxInputs | ||||
return false; | return false; | ||||
} | } | ||||
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 | ||||
if (fRequireStandard && | if (fRequireStandard && | ||||
!AreInputsStandard(tx, m_view, ws.m_next_block_script_verify_flags)) { | !AreInputsStandard(tx, m_view, ws.m_next_block_script_verify_flags)) { | ||||
return state.Invalid(TxValidationResult::TX_INPUTS_NOT_STANDARD, | return state.Invalid(TxValidationResult::TX_INPUTS_NOT_STANDARD, | ||||
"bad-txns-nonstandard-inputs"); | "bad-txns-nonstandard-inputs"); | ||||
} | } | ||||
// nModifiedFees includes any fee deltas from PrioritiseTransaction | // nModifiedFees includes any fee deltas from PrioritiseTransaction | ||||
nModifiedFees = nFees; | nModifiedFees = ws.m_fee_out; | ||||
m_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 = m_view.AccessCoin(txin.prevout); | const Coin &coin = m_view.AccessCoin(txin.prevout); | ||||
if (coin.IsCoinBase()) { | if (coin.IsCoinBase()) { | ||||
Show All 20 Lines | bool MemPoolAccept::PreChecks(ATMPArgs &args, Workspace &ws) { | ||||
PrecomputedTransactionData txdata(tx); | PrecomputedTransactionData txdata(tx); | ||||
if (!CheckInputScripts(tx, state, m_view, scriptVerifyFlags, true, false, | if (!CheckInputScripts(tx, state, m_view, scriptVerifyFlags, true, false, | ||||
txdata, ws.m_sig_checks_standard)) { | txdata, ws.m_sig_checks_standard)) { | ||||
// State filled in by CheckInputScripts | // State filled in by CheckInputScripts | ||||
return false; | return false; | ||||
} | } | ||||
entry.reset(new CTxMemPoolEntry( | entry.reset(new CTxMemPoolEntry( | ||||
ptx, nFees, nAcceptTime, m_active_chainstate.m_chain.Height(), | ptx, ws.m_fee_out, nAcceptTime, m_active_chainstate.m_chain.Height(), | ||||
fSpendsCoinbase, ws.m_sig_checks_standard, lp)); | fSpendsCoinbase, ws.m_sig_checks_standard, lp)); | ||||
unsigned int nVirtualSize = entry->GetTxVirtualSize(); | unsigned int nVirtualSize = entry->GetTxVirtualSize(); | ||||
Amount mempoolRejectFee = | Amount mempoolRejectFee = | ||||
m_pool | m_pool | ||||
.GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * | .GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * | ||||
1000000) | 1000000) | ||||
Show All 11 Lines | if (!m_pool.CalculateMemPoolAncestors( | ||||
*entry, setAncestors, m_limit_ancestors, m_limit_ancestor_size, | *entry, setAncestors, m_limit_ancestors, m_limit_ancestor_size, | ||||
m_limit_descendants, m_limit_descendant_size, 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; | return true; | ||||
} | } | ||||
bool MemPoolAccept::ConsensusScriptChecks(ATMPArgs &args, const Workspace &ws, | bool MemPoolAccept::ConsensusScriptChecks(const ATMPArgs &args, Workspace &ws, | ||||
PrecomputedTransactionData &txdata) { | PrecomputedTransactionData &txdata) { | ||||
const CTransaction &tx = *ws.m_ptx; | const CTransaction &tx = *ws.m_ptx; | ||||
const TxId &txid = tx.GetId(); | const TxId &txid = tx.GetId(); | ||||
TxValidationState &state = ws.m_state; | |||||
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. | ||||
Show All 20 Lines | if (ws.m_sig_checks_standard != nSigChecksConsensus) { | ||||
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()); | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
bool MemPoolAccept::Finalize(ATMPArgs &args, Workspace &ws) { | bool MemPoolAccept::Finalize(const ATMPArgs &args, Workspace &ws) { | ||||
const TxId &txid = ws.m_ptx->GetId(); | const TxId &txid = ws.m_ptx->GetId(); | ||||
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 &setAncestors = ws.m_ancestors; | CTxMemPool::setEntries &setAncestors = ws.m_ancestors; | ||||
std::unique_ptr<CTxMemPoolEntry> &entry = ws.m_entry; | std::unique_ptr<CTxMemPoolEntry> &entry = ws.m_entry; | ||||
// Store transaction in memory. | // Store transaction in memory. | ||||
m_pool.addUnchecked(*entry, setAncestors); | m_pool.addUnchecked(*entry, setAncestors); | ||||
Show All 20 Lines | MemPoolAccept::AcceptSingleTransaction(const CTransactionRef &ptx, | ||||
// GetMainSignals().TransactionAddedToMempool()) | // GetMainSignals().TransactionAddedToMempool()) | ||||
LOCK(m_pool.cs); | LOCK(m_pool.cs); | ||||
Workspace workspace(ptx, GetNextBlockScriptFlags( | Workspace workspace(ptx, GetNextBlockScriptFlags( | ||||
args.m_config.GetChainParams().GetConsensus(), | args.m_config.GetChainParams().GetConsensus(), | ||||
m_active_chainstate.m_chain.Tip())); | m_active_chainstate.m_chain.Tip())); | ||||
if (!PreChecks(args, workspace)) { | if (!PreChecks(args, workspace)) { | ||||
return MempoolAcceptResult(args.m_state); | return MempoolAcceptResult(workspace.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 | ||||
// checks first and avoid hashing and signature verification unless those | // checks first and avoid hashing and signature verification unless those | ||||
// checks pass, to mitigate CPU exhaustion denial-of-service attacks. | // checks pass, to mitigate CPU exhaustion denial-of-service attacks. | ||||
PrecomputedTransactionData txdata(*ptx); | PrecomputedTransactionData txdata(*ptx); | ||||
if (!ConsensusScriptChecks(args, workspace, txdata)) { | if (!ConsensusScriptChecks(args, workspace, txdata)) { | ||||
return MempoolAcceptResult(args.m_state); | return MempoolAcceptResult(workspace.m_state); | ||||
} | } | ||||
// Tx was accepted, but not added | // Tx was accepted, but not added | ||||
if (args.m_test_accept) { | if (args.m_test_accept) { | ||||
return MempoolAcceptResult(args.m_fee_out); | return MempoolAcceptResult(workspace.m_fee_out); | ||||
} | } | ||||
if (!Finalize(args, workspace)) { | if (!Finalize(args, workspace)) { | ||||
return MempoolAcceptResult(args.m_state); | return MempoolAcceptResult(workspace.m_state); | ||||
} | } | ||||
GetMainSignals().TransactionAddedToMempool( | GetMainSignals().TransactionAddedToMempool( | ||||
ptx, m_pool.GetAndIncrementSequence()); | ptx, m_pool.GetAndIncrementSequence()); | ||||
return MempoolAcceptResult(args.m_fee_out); | return MempoolAcceptResult(workspace.m_fee_out); | ||||
} | } | ||||
} // namespace | } // 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 MempoolAcceptResult AcceptToMemoryPoolWithTime( | static MempoolAcceptResult AcceptToMemoryPoolWithTime( | ||||
const Config &config, CTxMemPool &pool, CChainState &active_chainstate, | const Config &config, CTxMemPool &pool, CChainState &active_chainstate, | ||||
const CTransactionRef &tx, int64_t nAcceptTime, bool bypass_limits, | const CTransactionRef &tx, int64_t nAcceptTime, bool bypass_limits, | ||||
bool test_accept) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { | bool test_accept) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { | ||||
AssertLockHeld(cs_main); | AssertLockHeld(cs_main); | ||||
TxValidationState state; | |||||
std::vector<COutPoint> coins_to_uncache; | std::vector<COutPoint> coins_to_uncache; | ||||
MemPoolAccept::ATMPArgs args{ | MemPoolAccept::ATMPArgs args{config, nAcceptTime, bypass_limits, | ||||
config, state, nAcceptTime, bypass_limits, | coins_to_uncache, test_accept}; | ||||
coins_to_uncache, test_accept, {}}; | |||||
const MempoolAcceptResult result = MemPoolAccept(pool, active_chainstate) | const MempoolAcceptResult result = MemPoolAccept(pool, active_chainstate) | ||||
.AcceptSingleTransaction(tx, args); | .AcceptSingleTransaction(tx, args); | ||||
if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) { | if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) { | ||||
// 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`). | ||||
▲ Show 20 Lines • Show All 5,559 Lines • Show Last 20 Lines |