Changeset View
Changeset View
Standalone View
Standalone View
src/validation.cpp
Show First 20 Lines • Show All 353 Lines • ▼ Show 20 Lines | struct ATMPArgs { | ||||
* 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; | Amount m_fee_out; | ||||
}; | }; | ||||
// Single transaction acceptance | // Single transaction acceptance | ||||
bool AcceptSingleTransaction(const CTransactionRef &ptx, ATMPArgs &args) | MempoolAcceptResult AcceptSingleTransaction(const CTransactionRef &ptx, | ||||
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) | ||||
▲ Show 20 Lines • Show All 171 Lines • ▼ Show 20 Lines | bool MemPoolAccept::PreChecks(ATMPArgs &args, Workspace &ws) { | ||||
Amount nFees = Amount::zero(); | 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), nFees)) { | ||||
// state filled in by CheckTxInputs | // state filled in by CheckTxInputs | ||||
return false; | return false; | ||||
} | } | ||||
// 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 | ||||
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"); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 126 Lines • ▼ Show 20 Lines | if (!bypass_limits) { | ||||
if (!m_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; | return true; | ||||
} | } | ||||
bool MemPoolAccept::AcceptSingleTransaction(const CTransactionRef &ptx, | MempoolAcceptResult | ||||
MemPoolAccept::AcceptSingleTransaction(const CTransactionRef &ptx, | |||||
ATMPArgs &args) { | ATMPArgs &args) { | ||||
AssertLockHeld(cs_main); | AssertLockHeld(cs_main); | ||||
// mempool "read lock" (held through | // mempool "read lock" (held through | ||||
// 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 false; | 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 | ||||
// 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 false; | return MempoolAcceptResult(args.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 true; | return MempoolAcceptResult(args.m_fee_out); | ||||
} | } | ||||
if (!Finalize(args, workspace)) { | if (!Finalize(args, workspace)) { | ||||
return false; | return MempoolAcceptResult(args.m_state); | ||||
} | } | ||||
GetMainSignals().TransactionAddedToMempool( | GetMainSignals().TransactionAddedToMempool( | ||||
ptx, m_pool.GetAndIncrementSequence()); | ptx, m_pool.GetAndIncrementSequence()); | ||||
return true; | return MempoolAcceptResult(args.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 bool AcceptToMemoryPoolWithTime( | static MempoolAcceptResult AcceptToMemoryPoolWithTime( | ||||
const Config &config, CTxMemPool &pool, CChainState &active_chainstate, | const Config &config, CTxMemPool &pool, CChainState &active_chainstate, | ||||
TxValidationState &state, const CTransactionRef &tx, int64_t nAcceptTime, | const CTransactionRef &tx, int64_t nAcceptTime, bool bypass_limits, | ||||
bool bypass_limits, bool test_accept, Amount *fee_out = nullptr) | bool test_accept) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { | ||||
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, state, nAcceptTime, bypass_limits, | config, state, nAcceptTime, bypass_limits, | ||||
coins_to_uncache, test_accept, fee_out}; | coins_to_uncache, test_accept, {}}; | ||||
bool res = MemPoolAccept(pool, active_chainstate) | const MempoolAcceptResult result = MemPoolAccept(pool, active_chainstate) | ||||
.AcceptSingleTransaction(tx, args); | .AcceptSingleTransaction(tx, args); | ||||
if (!res) { | 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`). | ||||
for (const COutPoint &outpoint : coins_to_uncache) { | for (const COutPoint &outpoint : coins_to_uncache) { | ||||
active_chainstate.CoinsTip().Uncache(outpoint); | active_chainstate.CoinsTip().Uncache(outpoint); | ||||
} | } | ||||
} | } | ||||
// After we've (potentially) uncached entries, ensure our coins cache is | // After we've (potentially) uncached entries, ensure our coins cache is | ||||
// still within its size limits | // still within its size limits | ||||
BlockValidationState stateDummy; | BlockValidationState stateDummy; | ||||
active_chainstate.FlushStateToDisk(config.GetChainParams(), stateDummy, | active_chainstate.FlushStateToDisk(config.GetChainParams(), stateDummy, | ||||
FlushStateMode::PERIODIC); | FlushStateMode::PERIODIC); | ||||
return res; | return result; | ||||
} | } | ||||
bool AcceptToMemoryPool(CChainState &active_chainstate, const Config &config, | MempoolAcceptResult AcceptToMemoryPool(CChainState &active_chainstate, | ||||
CTxMemPool &pool, TxValidationState &state, | const Config &config, CTxMemPool &pool, | ||||
const CTransactionRef &tx, bool bypass_limits, | const CTransactionRef &tx, | ||||
bool test_accept, Amount *fee_out) { | bool bypass_limits, bool test_accept) { | ||||
return AcceptToMemoryPoolWithTime(config, pool, active_chainstate, state, | return AcceptToMemoryPoolWithTime(config, pool, active_chainstate, tx, | ||||
tx, GetTime(), bypass_limits, test_accept, | GetTime(), bypass_limits, test_accept); | ||||
fee_out); | |||||
} | } | ||||
CTransactionRef GetTransaction(const CBlockIndex *const block_index, | CTransactionRef GetTransaction(const CBlockIndex *const block_index, | ||||
const CTxMemPool *const mempool, | const CTxMemPool *const mempool, | ||||
const TxId &txid, | const TxId &txid, | ||||
const Consensus::Params &consensusParams, | const Consensus::Params &consensusParams, | ||||
BlockHash &hashBlock) { | BlockHash &hashBlock) { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
▲ Show 20 Lines • Show All 4,963 Lines • ▼ Show 20 Lines | try { | ||||
file >> tx; | file >> tx; | ||||
file >> nTime; | file >> nTime; | ||||
file >> nFeeDelta; | file >> nFeeDelta; | ||||
Amount amountdelta = nFeeDelta * SATOSHI; | Amount amountdelta = nFeeDelta * SATOSHI; | ||||
if (amountdelta != Amount::zero()) { | if (amountdelta != Amount::zero()) { | ||||
pool.PrioritiseTransaction(tx->GetId(), amountdelta); | pool.PrioritiseTransaction(tx->GetId(), amountdelta); | ||||
} | } | ||||
TxValidationState state; | |||||
if (nTime > nNow - nExpiryTimeout) { | if (nTime > nNow - nExpiryTimeout) { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
AcceptToMemoryPoolWithTime( | if (AcceptToMemoryPoolWithTime( | ||||
config, pool, active_chainstate, state, tx, nTime, | config, pool, active_chainstate, tx, nTime, | ||||
false /* bypass_limits */, false /* test_accept */); | false /* bypass_limits */, false /* test_accept */) | ||||
if (state.IsValid()) { | .m_result_type == | ||||
MempoolAcceptResult::ResultType::VALID) { | |||||
++count; | ++count; | ||||
} else { | } else { | ||||
// mempool may contain the transaction already, e.g. from | // mempool may contain the transaction already, e.g. from | ||||
// wallet(s) having loaded it while we were processing | // wallet(s) having loaded it while we were processing | ||||
// mempool transactions; consider these as valid, instead of | // mempool transactions; consider these as valid, instead of | ||||
// failed, but mark them as 'already there' | // failed, but mark them as 'already there' | ||||
if (pool.exists(tx->GetId())) { | if (pool.exists(tx->GetId())) { | ||||
++already_there; | ++already_there; | ||||
▲ Show 20 Lines • Show All 545 Lines • Show Last 20 Lines |