Changeset View
Changeset View
Standalone View
Standalone View
src/validation.cpp
Show First 20 Lines • Show All 289 Lines • ▼ Show 20 Lines | static bool CheckInputsFromMempoolAndCache( | ||||
const CTransaction &tx, TxValidationState &state, | const CTransaction &tx, TxValidationState &state, | ||||
const CCoinsViewCache &view, const CTxMemPool &pool, const uint32_t flags, | const CCoinsViewCache &view, const CTxMemPool &pool, const uint32_t flags, | ||||
PrecomputedTransactionData &txdata, int &nSigChecksOut) | PrecomputedTransactionData &txdata, int &nSigChecksOut) | ||||
EXCLUSIVE_LOCKS_REQUIRED(cs_main) { | EXCLUSIVE_LOCKS_REQUIRED(cs_main) { | ||||
AssertLockHeld(cs_main); | AssertLockHeld(cs_main); | ||||
// pool.cs should be locked already, but go ahead and re-take the lock here | // pool.cs should be locked already, but go ahead and re-take the lock here | ||||
// to enforce that mempool doesn't change between when we check the view and | // to enforce that mempool doesn't change between when we check the view and | ||||
// when we actually call through to CheckInputs | // when we actually call through to CheckInputScripts | ||||
LOCK(pool.cs); | LOCK(pool.cs); | ||||
assert(!tx.IsCoinBase()); | assert(!tx.IsCoinBase()); | ||||
for (const CTxIn &txin : tx.vin) { | for (const CTxIn &txin : tx.vin) { | ||||
const Coin &coin = view.AccessCoin(txin.prevout); | const Coin &coin = view.AccessCoin(txin.prevout); | ||||
// At this point we haven't actually checked if the coins are all | // AcceptToMemoryPoolWorker has already checked that the coins are | ||||
// available (or shouldn't assume we have, since CheckInputs does). So | // available, so this shouldn't fail. If the inputs are not available | ||||
// we just return failure if the inputs are not available here, and then | // here then return false. | ||||
// only have to check equivalence for available inputs. | |||||
if (coin.IsSpent()) { | if (coin.IsSpent()) { | ||||
return false; | return false; | ||||
} | } | ||||
// Check equivalence for available inputs. | |||||
const CTransactionRef &txFrom = pool.get(txin.prevout.GetTxId()); | const CTransactionRef &txFrom = pool.get(txin.prevout.GetTxId()); | ||||
if (txFrom) { | if (txFrom) { | ||||
assert(txFrom->GetId() == txin.prevout.GetTxId()); | assert(txFrom->GetId() == txin.prevout.GetTxId()); | ||||
assert(txFrom->vout.size() > txin.prevout.GetN()); | assert(txFrom->vout.size() > txin.prevout.GetN()); | ||||
assert(txFrom->vout[txin.prevout.GetN()] == coin.GetTxOut()); | assert(txFrom->vout[txin.prevout.GetN()] == coin.GetTxOut()); | ||||
} else { | } else { | ||||
const Coin &coinFromDisk = | const Coin &coinFromDisk = | ||||
::ChainstateActive().CoinsTip().AccessCoin(txin.prevout); | ::ChainstateActive().CoinsTip().AccessCoin(txin.prevout); | ||||
assert(!coinFromDisk.IsSpent()); | assert(!coinFromDisk.IsSpent()); | ||||
assert(coinFromDisk.GetTxOut() == coin.GetTxOut()); | assert(coinFromDisk.GetTxOut() == coin.GetTxOut()); | ||||
} | } | ||||
} | } | ||||
// Call CheckInputs() to cache signature and script validity against current | // Call CheckInputScripts() to cache signature and script validity against | ||||
// tip consensus rules. | // current tip consensus rules. | ||||
return CheckInputs(tx, state, view, flags, /* cacheSigStore = */ true, | return CheckInputScripts(tx, state, view, flags, /* cacheSigStore = */ true, | ||||
/* cacheFullScriptStore = */ true, txdata, | /* cacheFullScriptStore = */ true, txdata, | ||||
nSigChecksOut); | nSigChecksOut); | ||||
} | } | ||||
namespace { | namespace { | ||||
class MemPoolAccept { | class MemPoolAccept { | ||||
public: | public: | ||||
MemPoolAccept(CTxMemPool &mempool) | MemPoolAccept(CTxMemPool &mempool) | ||||
: m_pool(mempool), m_view(&m_dummy), | : m_pool(mempool), m_view(&m_dummy), | ||||
▲ Show 20 Lines • Show All 252 Lines • ▼ Show 20 Lines | if (nAbsurdFee != Amount::zero() && nFees > nAbsurdFee) { | ||||
"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 = | ||||
ws.m_next_block_script_verify_flags | STANDARD_SCRIPT_VERIFY_FLAGS; | ws.m_next_block_script_verify_flags | STANDARD_SCRIPT_VERIFY_FLAGS; | ||||
PrecomputedTransactionData txdata(tx); | PrecomputedTransactionData txdata(tx); | ||||
if (!CheckInputs(tx, state, m_view, scriptVerifyFlags, true, false, txdata, | if (!CheckInputScripts(tx, state, m_view, scriptVerifyFlags, true, false, | ||||
ws.m_sig_checks_standard)) { | txdata, ws.m_sig_checks_standard)) { | ||||
// State filled in by CheckInputs. | // State filled in by CheckInputScripts | ||||
return false; | return false; | ||||
} | } | ||||
entry.reset(new CTxMemPoolEntry(ptx, nFees, nAcceptTime, | entry.reset(new CTxMemPoolEntry(ptx, nFees, nAcceptTime, | ||||
::ChainActive().Height(), fSpendsCoinbase, | ::ChainActive().Height(), fSpendsCoinbase, | ||||
ws.m_sig_checks_standard, lp)); | ws.m_sig_checks_standard, lp)); | ||||
unsigned int nVirtualSize = entry->GetTxVirtualSize(); | unsigned int nVirtualSize = entry->GetTxVirtualSize(); | ||||
▲ Show 20 Lines • Show All 41 Lines • ▼ Show 20 Lines | bool MemPoolAccept::ConsensusScriptChecks(ATMPArgs &args, Workspace &ws, | ||||
// 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, m_view, m_pool, | if (!CheckInputsFromMempoolAndCache(tx, state, m_view, m_pool, | ||||
ws.m_next_block_script_verify_flags, | ws.m_next_block_script_verify_flags, | ||||
txdata, nSigChecksConsensus)) { | 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! CheckInputScripts 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 (ws.m_sig_checks_standard != 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). | ||||
▲ Show 20 Lines • Show All 460 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
int GetSpendHeight(const CCoinsViewCache &inputs) { | int GetSpendHeight(const CCoinsViewCache &inputs) { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
CBlockIndex *pindexPrev = LookupBlockIndex(inputs.GetBestBlock()); | CBlockIndex *pindexPrev = LookupBlockIndex(inputs.GetBestBlock()); | ||||
return pindexPrev->nHeight + 1; | return pindexPrev->nHeight + 1; | ||||
} | } | ||||
bool CheckInputs(const CTransaction &tx, TxValidationState &state, | bool CheckInputScripts(const CTransaction &tx, TxValidationState &state, | ||||
const CCoinsViewCache &inputs, const uint32_t flags, | const CCoinsViewCache &inputs, const uint32_t flags, | ||||
bool sigCacheStore, bool scriptCacheStore, | bool sigCacheStore, bool scriptCacheStore, | ||||
const PrecomputedTransactionData &txdata, int &nSigChecksOut, | const PrecomputedTransactionData &txdata, | ||||
TxSigCheckLimiter &txLimitSigChecks, | int &nSigChecksOut, TxSigCheckLimiter &txLimitSigChecks, | ||||
CheckInputsLimiter *pBlockLimitSigChecks, | CheckInputsLimiter *pBlockLimitSigChecks, | ||||
std::vector<CScriptCheck> *pvChecks) { | std::vector<CScriptCheck> *pvChecks) { | ||||
AssertLockHeld(cs_main); | AssertLockHeld(cs_main); | ||||
assert(!tx.IsCoinBase()); | assert(!tx.IsCoinBase()); | ||||
if (pvChecks) { | if (pvChecks) { | ||||
pvChecks->reserve(tx.vin.size()); | pvChecks->reserve(tx.vin.size()); | ||||
} | } | ||||
// First check if script executions have been cached with the same flags. | // First check if script executions have been cached with the same flags. | ||||
▲ Show 20 Lines • Show All 771 Lines • ▼ Show 20 Lines | for (const auto &ptx : block.vtx) { | ||||
} | } | ||||
std::vector<CScriptCheck> vChecks; | std::vector<CScriptCheck> vChecks; | ||||
// nSigChecksRet may be accurate (found in cache) or 0 (checks were | // nSigChecksRet may be accurate (found in cache) or 0 (checks were | ||||
// deferred into vChecks). | // deferred into vChecks). | ||||
int nSigChecksRet; | int nSigChecksRet; | ||||
TxValidationState tx_state; | TxValidationState tx_state; | ||||
if (fScriptChecks && | if (fScriptChecks && | ||||
!CheckInputs(tx, tx_state, view, flags, fCacheResults, | !CheckInputScripts(tx, tx_state, view, flags, fCacheResults, | ||||
fCacheResults, PrecomputedTransactionData(tx), | fCacheResults, PrecomputedTransactionData(tx), | ||||
nSigChecksRet, nSigChecksTxLimiters[txIndex], | nSigChecksRet, nSigChecksTxLimiters[txIndex], | ||||
&nSigChecksBlockLimiter, &vChecks)) { | &nSigChecksBlockLimiter, &vChecks)) { | ||||
// Any transaction validation failure in ConnectBlock is a block | // Any transaction validation failure in ConnectBlock is a block | ||||
// consensus failure | // consensus failure | ||||
state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, | state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, | ||||
tx_state.GetRejectReason(), | tx_state.GetRejectReason(), | ||||
tx_state.GetDebugMessage()); | tx_state.GetDebugMessage()); | ||||
return error("ConnectBlock(): CheckInputs on %s failed with %s", | return error( | ||||
"ConnectBlock(): CheckInputScripts on %s failed with %s", | |||||
tx.GetId().ToString(), state.ToString()); | tx.GetId().ToString(), state.ToString()); | ||||
} | } | ||||
control.Add(vChecks); | control.Add(vChecks); | ||||
// Note: this must execute in the same iteration as CheckTxInputs (not | // Note: this must execute in the same iteration as CheckTxInputs (not | ||||
// in a separate loop) in order to detect double spends. However, | // in a separate loop) in order to detect double spends. However, | ||||
// this does not prevent double-spending by duplicated transaction | // this does not prevent double-spending by duplicated transaction | ||||
// inputs in the same transaction (cf. CVE-2018-17144) -- that check is | // inputs in the same transaction (cf. CVE-2018-17144) -- that check is | ||||
▲ Show 20 Lines • Show All 4,063 Lines • Show Last 20 Lines |