Changeset View
Changeset View
Standalone View
Standalone View
src/validation.cpp
Show First 20 Lines • Show All 287 Lines • ▼ Show 20 Lines | static bool IsReplayProtectionEnabled(const Consensus::Params ¶ms, | ||||
} | } | ||||
return IsReplayProtectionEnabled(params, pindexPrev->GetMedianTimePast()); | return IsReplayProtectionEnabled(params, pindexPrev->GetMedianTimePast()); | ||||
} | } | ||||
// Used to avoid mempool polluting consensus critical paths if CCoinsViewMempool | // Used to avoid mempool polluting consensus critical paths if CCoinsViewMempool | ||||
// were somehow broken and returning the wrong scriptPubKeys | // were somehow broken and returning the wrong scriptPubKeys | ||||
static bool CheckInputsFromMempoolAndCache( | static bool CheckInputsFromMempoolAndCache( | ||||
const CTransaction &tx, CValidationState &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, | ||||
bool cacheSigStore, PrecomputedTransactionData &txdata, int &nSigChecksOut) | bool cacheSigStore, 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 CheckInputs | ||||
Show All 29 Lines | |||||
/** | /** | ||||
* @param[out] coins_to_uncache Return any outpoints which were not previously | * @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 | * present in the coins cache, but were added as a result of validating the tx | ||||
* for mempool acceptance. This allows the caller | * for mempool acceptance. This allows the caller | ||||
* to optionally remove the cache additions if the associated transaction ends | * to optionally remove the cache additions if the associated transaction ends | ||||
* up being rejected by the mempool. | * up being rejected by the mempool. | ||||
*/ | */ | ||||
static bool | static bool AcceptToMemoryPoolWorker( | ||||
AcceptToMemoryPoolWorker(const Config &config, CTxMemPool &pool, | const Config &config, CTxMemPool &pool, TxValidationState &state, | ||||
CValidationState &state, const CTransactionRef &ptx, | const CTransactionRef &ptx, int64_t nAcceptTime, bool bypass_limits, | ||||
bool *pfMissingInputs, int64_t nAcceptTime, | const Amount nAbsurdFee, std::vector<COutPoint> &coins_to_uncache, | ||||
bool bypass_limits, const Amount nAbsurdFee, | |||||
std::vector<COutPoint> &coins_to_uncache, | |||||
bool test_accept) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { | bool test_accept) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { | ||||
AssertLockHeld(cs_main); | AssertLockHeld(cs_main); | ||||
const Consensus::Params &consensusParams = | const Consensus::Params &consensusParams = | ||||
config.GetChainParams().GetConsensus(); | config.GetChainParams().GetConsensus(); | ||||
const CTransaction &tx = *ptx; | const CTransaction &tx = *ptx; | ||||
const TxId txid = tx.GetId(); | const TxId txid = tx.GetId(); | ||||
// mempool "read lock" (held through | // mempool "read lock" (held through | ||||
// GetMainSignals().TransactionAddedToMempool()) | // GetMainSignals().TransactionAddedToMempool()) | ||||
LOCK(pool.cs); | LOCK(pool.cs); | ||||
if (pfMissingInputs) { | |||||
*pfMissingInputs = false; | |||||
} | |||||
// 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(ValidationInvalidReason::TX_NOT_STANDARD, false, | return state.Invalid(TxValidationResult::TX_NOT_STANDARD, | ||||
REJECT_NONSTANDARD, reason); | REJECT_NONSTANDARD, 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. | ||||
CValidationState ctxState; | TxValidationState ctxState; | ||||
if (!ContextualCheckTransactionForCurrentBlock( | if (!ContextualCheckTransactionForCurrentBlock( | ||||
consensusParams, tx, ctxState, STANDARD_LOCKTIME_VERIFY_FLAGS)) { | consensusParams, 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(ValidationInvalidReason::TX_PREMATURE_SPEND, false, | return state.Invalid(TxValidationResult::TX_PREMATURE_SPEND, | ||||
REJECT_NONSTANDARD, ctxState.GetRejectReason(), | REJECT_NONSTANDARD, 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 (pool.exists(txid)) { | ||||
return state.Invalid(ValidationInvalidReason::TX_CONFLICT, false, | return state.Invalid(TxValidationResult::TX_CONFLICT, REJECT_DUPLICATE, | ||||
REJECT_DUPLICATE, "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 = pool.mapNextTx.find(txin.prevout); | ||||
if (itConflicting != pool.mapNextTx.end()) { | if (itConflicting != pool.mapNextTx.end()) { | ||||
// Disable replacement feature for good | // Disable replacement feature for good | ||||
return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, | return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, | ||||
false, REJECT_DUPLICATE, | REJECT_DUPLICATE, "txn-mempool-conflict"); | ||||
"txn-mempool-conflict"); | |||||
} | } | ||||
} | } | ||||
{ | { | ||||
CCoinsView dummy; | CCoinsView dummy; | ||||
CCoinsViewCache view(&dummy); | CCoinsViewCache view(&dummy); | ||||
LockPoints lp; | LockPoints lp; | ||||
Show All 11 Lines | for (const CTxIn &txin : tx.vin) { | ||||
// 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 (!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 (pcoinsTip->HaveCoinInCache(COutPoint(txid, out))) { | if (pcoinsTip->HaveCoinInCache(COutPoint(txid, out))) { | ||||
return state.Invalid( | return state.Invalid(TxValidationResult::TX_CONFLICT, | ||||
ValidationInvalidReason::TX_CONFLICT, false, | REJECT_DUPLICATE, | ||||
REJECT_DUPLICATE, "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. | ||||
if (pfMissingInputs) { | return state.Invalid(TxValidationResult::TX_MISSING_INPUTS, | ||||
*pfMissingInputs = true; | REJECT_INVALID, | ||||
} | "bad-txns-inputs-missingorspent"); | ||||
// fMissingInputs and !state.IsInvalid() is used to detect this | |||||
// condition, don't set state.Invalid() | |||||
return false; | |||||
} | } | ||||
} | } | ||||
// Are the actual inputs available? | // Are the actual inputs available? | ||||
if (!view.HaveInputs(tx)) { | if (!view.HaveInputs(tx)) { | ||||
return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, | return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, | ||||
false, REJECT_DUPLICATE, | REJECT_DUPLICATE, "bad-txns-inputs-spent"); | ||||
"bad-txns-inputs-spent"); | |||||
} | } | ||||
// Bring the best block into scope. | // Bring the best block into scope. | ||||
view.GetBestBlock(); | 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, so we don't | ||||
// need to keep lock on mempool. | // need to keep lock on mempool. | ||||
view.SetBackend(dummy); | view.SetBackend(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(pool, tx, STANDARD_LOCKTIME_VERIFY_FLAGS, | ||||
&lp)) { | &lp)) { | ||||
return state.Invalid(ValidationInvalidReason::TX_PREMATURE_SPEND, | return state.Invalid(TxValidationResult::TX_PREMATURE_SPEND, | ||||
false, REJECT_NONSTANDARD, "non-BIP68-final"); | REJECT_NONSTANDARD, "non-BIP68-final"); | ||||
} | } | ||||
Amount nFees = Amount::zero(); | Amount nFees = Amount::zero(); | ||||
if (!Consensus::CheckTxInputs(tx, state, view, GetSpendHeight(view), | if (!Consensus::CheckTxInputs(tx, state, view, GetSpendHeight(view), | ||||
nFees)) { | nFees)) { | ||||
return error("%s: Consensus::CheckTxInputs: %s, %s", __func__, | return error("%s: Consensus::CheckTxInputs: %s, %s", __func__, | ||||
tx.GetId().ToString(), FormatStateMessage(state)); | tx.GetId().ToString(), FormatStateMessage(state)); | ||||
} | } | ||||
const uint32_t nextBlockScriptVerifyFlags = | const uint32_t nextBlockScriptVerifyFlags = | ||||
GetNextBlockScriptFlags(consensusParams, ::ChainActive().Tip()); | 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, view, nextBlockScriptVerifyFlags)) { | ||||
return state.Invalid(ValidationInvalidReason::TX_NOT_STANDARD, | return state.Invalid(TxValidationResult::TX_NOT_STANDARD, | ||||
false, REJECT_NONSTANDARD, | REJECT_NONSTANDARD, | ||||
"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; | Amount nModifiedFees = nFees; | ||||
pool.ApplyDelta(txid, nModifiedFees); | 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 | ||||
Show All 9 Lines | for (const CTxIn &txin : tx.vin) { | ||||
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 blocks. | // disconnected 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(ValidationInvalidReason::TX_MEMPOOL_POLICY, | return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, | ||||
false, REJECT_INSUFFICIENTFEE, | REJECT_INSUFFICIENTFEE, | ||||
"min relay fee not met"); | "min relay fee not met"); | ||||
} | } | ||||
if (nAbsurdFee != Amount::zero() && nFees > nAbsurdFee) { | if (nAbsurdFee != Amount::zero() && nFees > nAbsurdFee) { | ||||
return state.Invalid(ValidationInvalidReason::TX_NOT_STANDARD, | return state.Invalid(TxValidationResult::TX_NOT_STANDARD, | ||||
false, REJECT_HIGHFEE, "absurdly-high-fee", | REJECT_HIGHFEE, "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; | nextBlockScriptVerifyFlags | STANDARD_SCRIPT_VERIFY_FLAGS; | ||||
PrecomputedTransactionData txdata(tx); | PrecomputedTransactionData txdata(tx); | ||||
int nSigChecksStandard; | int nSigChecksStandard; | ||||
if (!CheckInputs(tx, state, view, true, scriptVerifyFlags, true, false, | if (!CheckInputs(tx, state, view, true, scriptVerifyFlags, true, false, | ||||
txdata, nSigChecksStandard)) { | txdata, nSigChecksStandard)) { | ||||
// State filled in by CheckInputs. | // State filled in by CheckInputs. | ||||
assert(IsTransactionReason(state.GetReason())); | |||||
return false; | return false; | ||||
} | } | ||||
CTxMemPoolEntry entry(ptx, nFees, nAcceptTime, ::ChainActive().Height(), | CTxMemPoolEntry entry(ptx, nFees, nAcceptTime, ::ChainActive().Height(), | ||||
fSpendsCoinbase, nSigChecksStandard, lp); | fSpendsCoinbase, nSigChecksStandard, lp); | ||||
unsigned int nVirtualSize = entry.GetTxVirtualSize(); | unsigned int nVirtualSize = entry.GetTxVirtualSize(); | ||||
Amount mempoolRejectFee = | Amount mempoolRejectFee = | ||||
pool.GetMinFee( | pool.GetMinFee( | ||||
gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * | 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( | ||||
ValidationInvalidReason::TX_MEMPOOL_POLICY, false, | TxValidationResult::TX_MEMPOOL_POLICY, REJECT_INSUFFICIENTFEE, | ||||
REJECT_INSUFFICIENTFEE, "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; | CTxMemPool::setEntries setAncestors; | ||||
size_t nLimitAncestors = | size_t nLimitAncestors = | ||||
gArgs.GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT); | gArgs.GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT); | ||||
size_t nLimitAncestorSize = | size_t nLimitAncestorSize = | ||||
gArgs.GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT) * | gArgs.GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT) * | ||||
1000; | 1000; | ||||
size_t nLimitDescendants = | size_t nLimitDescendants = | ||||
gArgs.GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT); | gArgs.GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT); | ||||
size_t nLimitDescendantSize = | size_t nLimitDescendantSize = | ||||
gArgs.GetArg("-limitdescendantsize", | gArgs.GetArg("-limitdescendantsize", | ||||
DEFAULT_DESCENDANT_SIZE_LIMIT) * | DEFAULT_DESCENDANT_SIZE_LIMIT) * | ||||
1000; | 1000; | ||||
std::string errString; | std::string errString; | ||||
if (!pool.CalculateMemPoolAncestors( | if (!pool.CalculateMemPoolAncestors( | ||||
entry, setAncestors, nLimitAncestors, nLimitAncestorSize, | entry, setAncestors, nLimitAncestors, nLimitAncestorSize, | ||||
nLimitDescendants, nLimitDescendantSize, errString)) { | nLimitDescendants, nLimitDescendantSize, errString)) { | ||||
return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, | return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, | ||||
false, REJECT_NONSTANDARD, | REJECT_NONSTANDARD, "too-long-mempool-chain", | ||||
"too-long-mempool-chain", errString); | errString); | ||||
} | } | ||||
// 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 | ||||
Show All 34 Lines | for (const CTxIn &txin : tx.vin) { | ||||
// Trim mempool and check if tx was trimmed. | // Trim mempool and check if tx was trimmed. | ||||
if (!bypass_limits) { | if (!bypass_limits) { | ||||
pool.LimitSize( | 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 (!pool.exists(txid)) { | ||||
return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, | return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, | ||||
false, REJECT_INSUFFICIENTFEE, | REJECT_INSUFFICIENTFEE, "mempool full"); | ||||
"mempool full"); | |||||
} | } | ||||
} | } | ||||
} | } | ||||
GetMainSignals().TransactionAddedToMempool(ptx); | GetMainSignals().TransactionAddedToMempool(ptx); | ||||
return true; | return true; | ||||
} | } | ||||
/** | /** | ||||
* (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, | ||||
CValidationState &state, const CTransactionRef &tx, | TxValidationState &state, const CTransactionRef &tx, | ||||
bool *pfMissingInputs, int64_t nAcceptTime, | int64_t nAcceptTime, bool bypass_limits, | ||||
bool bypass_limits, const Amount nAbsurdFee, | const Amount nAbsurdFee, bool test_accept) | ||||
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( | bool res = AcceptToMemoryPoolWorker(config, pool, state, tx, nAcceptTime, | ||||
config, pool, state, tx, pfMissingInputs, nAcceptTime, bypass_limits, | bypass_limits, nAbsurdFee, | ||||
nAbsurdFee, coins_to_uncache, test_accept); | coins_to_uncache, test_accept); | ||||
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) { | ||||
pcoinsTip->Uncache(outpoint); | pcoinsTip->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 | ||||
CValidationState stateDummy; | BlockValidationState stateDummy; | ||||
::ChainstateActive().FlushStateToDisk(config.GetChainParams(), stateDummy, | ::ChainstateActive().FlushStateToDisk(config.GetChainParams(), stateDummy, | ||||
FlushStateMode::PERIODIC); | FlushStateMode::PERIODIC); | ||||
return res; | return res; | ||||
} | } | ||||
bool AcceptToMemoryPool(const Config &config, CTxMemPool &pool, | bool AcceptToMemoryPool(const Config &config, CTxMemPool &pool, | ||||
CValidationState &state, const CTransactionRef &tx, | TxValidationState &state, const CTransactionRef &tx, | ||||
bool *pfMissingInputs, bool bypass_limits, | bool bypass_limits, const Amount nAbsurdFee, | ||||
const Amount nAbsurdFee, bool test_accept) { | bool test_accept) { | ||||
return AcceptToMemoryPoolWithTime(config, pool, state, tx, pfMissingInputs, | return AcceptToMemoryPoolWithTime(config, pool, state, tx, GetTime(), | ||||
GetTime(), bypass_limits, nAbsurdFee, | bypass_limits, nAbsurdFee, test_accept); | ||||
test_accept); | |||||
} | } | ||||
/** | /** | ||||
* Return transaction in txOut, and if it was found inside a block, its hash is | * Return transaction in txOut, and if it was found inside a block, its hash is | ||||
* placed in hashBlock. If blockIndex is provided, the transaction is fetched | * placed in hashBlock. If blockIndex is provided, the transaction is fetched | ||||
* from the corresponding block. | * from the corresponding block. | ||||
*/ | */ | ||||
bool GetTransaction(const TxId &txid, CTransactionRef &txOut, | bool GetTransaction(const TxId &txid, CTransactionRef &txOut, | ||||
▲ Show 20 Lines • Show All 275 Lines • ▼ Show 20 Lines | static void InvalidChainFound(CBlockIndex *pindexNew) | ||||
LogPrintf("%s: current best=%s height=%d log2_work=%.8g date=%s\n", | LogPrintf("%s: current best=%s height=%d log2_work=%.8g date=%s\n", | ||||
__func__, tip->GetBlockHash().ToString(), | __func__, tip->GetBlockHash().ToString(), | ||||
::ChainActive().Height(), | ::ChainActive().Height(), | ||||
log(tip->nChainWork.getdouble()) / log(2.0), | log(tip->nChainWork.getdouble()) / log(2.0), | ||||
FormatISO8601DateTime(tip->GetBlockTime())); | FormatISO8601DateTime(tip->GetBlockTime())); | ||||
} | } | ||||
void CChainState::InvalidBlockFound(CBlockIndex *pindex, | void CChainState::InvalidBlockFound(CBlockIndex *pindex, | ||||
const CValidationState &state) { | const BlockValidationState &state) { | ||||
if (state.GetReason() != ValidationInvalidReason::BLOCK_MUTATED) { | if (state.GetResult() != BlockValidationResult::BLOCK_MUTATED) { | ||||
pindex->nStatus = pindex->nStatus.withFailed(); | pindex->nStatus = pindex->nStatus.withFailed(); | ||||
m_failed_blocks.insert(pindex); | m_failed_blocks.insert(pindex); | ||||
setDirtyBlockIndex.insert(pindex); | setDirtyBlockIndex.insert(pindex); | ||||
InvalidChainFound(pindex); | InvalidChainFound(pindex); | ||||
} | } | ||||
} | } | ||||
void SpendCoins(CCoinsViewCache &view, const CTransaction &tx, CTxUndo &txundo, | void SpendCoins(CCoinsViewCache &view, const CTransaction &tx, CTxUndo &txundo, | ||||
▲ Show 20 Lines • Show All 52 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, CValidationState &state, | bool CheckInputs(const CTransaction &tx, TxValidationState &state, | ||||
const CCoinsViewCache &inputs, bool fScriptChecks, | const CCoinsViewCache &inputs, bool fScriptChecks, | ||||
const uint32_t flags, bool sigCacheStore, | const uint32_t flags, bool sigCacheStore, | ||||
bool scriptCacheStore, | bool scriptCacheStore, | ||||
const PrecomputedTransactionData &txdata, int &nSigChecksOut, | const PrecomputedTransactionData &txdata, int &nSigChecksOut, | ||||
TxSigCheckLimiter &txLimitSigChecks, | TxSigCheckLimiter &txLimitSigChecks, | ||||
CheckInputsLimiter *pBlockLimitSigChecks, | CheckInputsLimiter *pBlockLimitSigChecks, | ||||
std::vector<CScriptCheck> *pvChecks) { | std::vector<CScriptCheck> *pvChecks) { | ||||
AssertLockHeld(cs_main); | AssertLockHeld(cs_main); | ||||
Show All 16 Lines | bool CheckInputs(const CTransaction &tx, TxValidationState &state, | ||||
// Note that this assumes that the inputs provided are correct (ie that the | // Note that this assumes that the inputs provided are correct (ie that the | ||||
// transaction hash which is in tx's prevouts properly commits to the | // transaction hash which is in tx's prevouts properly commits to the | ||||
// scriptPubKey in the inputs view of that transaction). | // scriptPubKey in the inputs view of that transaction). | ||||
ScriptCacheKey hashCacheEntry(tx, flags); | ScriptCacheKey hashCacheEntry(tx, flags); | ||||
if (IsKeyInScriptCache(hashCacheEntry, !scriptCacheStore, nSigChecksOut)) { | if (IsKeyInScriptCache(hashCacheEntry, !scriptCacheStore, nSigChecksOut)) { | ||||
if (!txLimitSigChecks.consume_and_check(nSigChecksOut) || | if (!txLimitSigChecks.consume_and_check(nSigChecksOut) || | ||||
(pBlockLimitSigChecks && | (pBlockLimitSigChecks && | ||||
!pBlockLimitSigChecks->consume_and_check(nSigChecksOut))) { | !pBlockLimitSigChecks->consume_and_check(nSigChecksOut))) { | ||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, | return state.Invalid(TxValidationResult::TX_CONSENSUS, | ||||
REJECT_INVALID, "too-many-sigchecks"); | REJECT_INVALID, "too-many-sigchecks"); | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
int nSigChecksTotal = 0; | int nSigChecksTotal = 0; | ||||
for (size_t i = 0; i < tx.vin.size(); i++) { | for (size_t i = 0; i < tx.vin.size(); i++) { | ||||
Show All 27 Lines | for (size_t i = 0; i < tx.vin.size(); i++) { | ||||
// script verification check. If so, ensure we return | // script verification check. If so, ensure we return | ||||
// NOT_STANDARD instead of CONSENSUS to avoid downstream users | // NOT_STANDARD instead of CONSENSUS to avoid downstream users | ||||
// splitting the network between upgraded and non-upgraded nodes | // splitting the network between upgraded and non-upgraded nodes | ||||
// by banning CONSENSUS-failing data providers. | // by banning CONSENSUS-failing data providers. | ||||
CScriptCheck check2(scriptPubKey, amount, tx, i, mandatoryFlags, | CScriptCheck check2(scriptPubKey, amount, tx, i, mandatoryFlags, | ||||
sigCacheStore, txdata); | sigCacheStore, txdata); | ||||
if (check2()) { | if (check2()) { | ||||
return state.Invalid( | return state.Invalid( | ||||
ValidationInvalidReason::TX_NOT_STANDARD, false, | TxValidationResult::TX_NOT_STANDARD, REJECT_NONSTANDARD, | ||||
REJECT_NONSTANDARD, | |||||
strprintf("non-mandatory-script-verify-flag (%s)", | strprintf("non-mandatory-script-verify-flag (%s)", | ||||
ScriptErrorString(scriptError))); | ScriptErrorString(scriptError))); | ||||
} | } | ||||
// update the error message to reflect the mandatory violation. | // update the error message to reflect the mandatory violation. | ||||
scriptError = check2.GetScriptError(); | scriptError = check2.GetScriptError(); | ||||
} | } | ||||
// MANDATORY flag failures correspond to | // MANDATORY flag failures correspond to | ||||
// ValidationInvalidReason::CONSENSUS. Because CONSENSUS failures | // TxValidationResult::TX_CONSENSUS. Because CONSENSUS failures | ||||
// are the most serious case of validation failures, we may need to | // are the most serious case of validation failures, we may need to | ||||
// consider using RECENT_CONSENSUS_CHANGE for any script failure | // consider using RECENT_CONSENSUS_CHANGE for any script failure | ||||
// that could be due to non-upgraded nodes which we may want to | // that could be due to non-upgraded nodes which we may want to | ||||
// support, to avoid splitting the network (but this depends on the | // support, to avoid splitting the network (but this depends on the | ||||
// details of how net_processing handles such errors). | // details of how net_processing handles such errors). | ||||
return state.Invalid( | return state.Invalid( | ||||
ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, | TxValidationResult::TX_CONSENSUS, REJECT_INVALID, | ||||
strprintf("mandatory-script-verify-flag-failed (%s)", | strprintf("mandatory-script-verify-flag-failed (%s)", | ||||
ScriptErrorString(scriptError))); | ScriptErrorString(scriptError))); | ||||
} | } | ||||
nSigChecksTotal += check.GetScriptExecutionMetrics().nSigChecks; | nSigChecksTotal += check.GetScriptExecutionMetrics().nSigChecks; | ||||
} | } | ||||
nSigChecksOut = nSigChecksTotal; | nSigChecksOut = nSigChecksTotal; | ||||
▲ Show 20 Lines • Show All 79 Lines • ▼ Show 20 Lines | uiInterface.ThreadSafeMessageBox( | ||||
"debug.log for details") | "debug.log for details") | ||||
.translated | .translated | ||||
: userMessage, | : userMessage, | ||||
"", CClientUIInterface::MSG_ERROR); | "", CClientUIInterface::MSG_ERROR); | ||||
StartShutdown(); | StartShutdown(); | ||||
return false; | return false; | ||||
} | } | ||||
static bool AbortNode(CValidationState &state, const std::string &strMessage, | static bool AbortNode(BlockValidationState &state, | ||||
const std::string &strMessage, | |||||
const std::string &userMessage = "") { | const std::string &userMessage = "") { | ||||
AbortNode(strMessage, userMessage); | AbortNode(strMessage, userMessage); | ||||
return state.Error(strMessage); | return state.Error(strMessage); | ||||
} | } | ||||
/** Restore the UTXO in a Coin at a given COutPoint. */ | /** Restore the UTXO in a Coin at a given COutPoint. */ | ||||
DisconnectResult UndoCoinSpend(const Coin &undo, CCoinsViewCache &view, | DisconnectResult UndoCoinSpend(const Coin &undo, CCoinsViewCache &view, | ||||
const COutPoint &out) { | const COutPoint &out) { | ||||
▲ Show 20 Lines • Show All 120 Lines • ▼ Show 20 Lines | static void FlushBlockFile(bool fFinalize = false) { | ||||
status &= BlockFileSeq().Flush(block_pos_old, fFinalize); | status &= BlockFileSeq().Flush(block_pos_old, fFinalize); | ||||
status &= UndoFileSeq().Flush(undo_pos_old, fFinalize); | status &= UndoFileSeq().Flush(undo_pos_old, fFinalize); | ||||
if (!status) { | if (!status) { | ||||
AbortNode("Flushing block file to disk failed. This is likely the " | AbortNode("Flushing block file to disk failed. This is likely the " | ||||
"result of an I/O error."); | "result of an I/O error."); | ||||
} | } | ||||
} | } | ||||
static bool FindUndoPos(CValidationState &state, int nFile, FlatFilePos &pos, | static bool FindUndoPos(BlockValidationState &state, int nFile, | ||||
unsigned int nAddSize); | FlatFilePos &pos, unsigned int nAddSize); | ||||
static bool WriteUndoDataForBlock(const CBlockUndo &blockundo, | static bool WriteUndoDataForBlock(const CBlockUndo &blockundo, | ||||
CValidationState &state, CBlockIndex *pindex, | BlockValidationState &state, | ||||
CBlockIndex *pindex, | |||||
const CChainParams &chainparams) { | const CChainParams &chainparams) { | ||||
// Write undo information to disk | // Write undo information to disk | ||||
if (pindex->GetUndoPos().IsNull()) { | if (pindex->GetUndoPos().IsNull()) { | ||||
FlatFilePos _pos; | FlatFilePos _pos; | ||||
if (!FindUndoPos(state, pindex->nFile, _pos, | if (!FindUndoPos(state, pindex->nFile, _pos, | ||||
::GetSerializeSize(blockundo, CLIENT_VERSION) + 40)) { | ::GetSerializeSize(blockundo, CLIENT_VERSION) + 40)) { | ||||
return error("ConnectBlock(): FindUndoPos failed"); | return error("ConnectBlock(): FindUndoPos failed"); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 119 Lines • ▼ Show 20 Lines | |||||
static int64_t nBlocksTotal = 0; | static int64_t nBlocksTotal = 0; | ||||
/** | /** | ||||
* Apply the effects of this block (with given index) on the UTXO set | * Apply the effects of this block (with given index) on the UTXO set | ||||
* represented by coins. Validity checks that depend on the UTXO set are also | * represented by coins. Validity checks that depend on the UTXO set are also | ||||
* done; ConnectBlock() can fail if those validity checks fail (among other | * done; ConnectBlock() can fail if those validity checks fail (among other | ||||
* reasons). | * reasons). | ||||
*/ | */ | ||||
bool CChainState::ConnectBlock(const CBlock &block, CValidationState &state, | bool CChainState::ConnectBlock(const CBlock &block, BlockValidationState &state, | ||||
CBlockIndex *pindex, CCoinsViewCache &view, | CBlockIndex *pindex, CCoinsViewCache &view, | ||||
const CChainParams ¶ms, | const CChainParams ¶ms, | ||||
BlockValidationOptions options, | BlockValidationOptions options, | ||||
bool fJustCheck) { | bool fJustCheck) { | ||||
AssertLockHeld(cs_main); | AssertLockHeld(cs_main); | ||||
assert(pindex); | assert(pindex); | ||||
assert(*pindex->phashBlock == block.GetHash()); | assert(*pindex->phashBlock == block.GetHash()); | ||||
int64_t nTimeStart = GetTimeMicros(); | int64_t nTimeStart = GetTimeMicros(); | ||||
Show All 10 Lines | bool CChainState::ConnectBlock(const CBlock &block, BlockValidationState &state, | ||||
// change is potentially tricky and issue-specific. | // change is potentially tricky and issue-specific. | ||||
// Also, currently the rule against blocks more than 2 hours in the future | // Also, currently the rule against blocks more than 2 hours in the future | ||||
// is enforced in ContextualCheckBlockHeader(); we wouldn't want to | // is enforced in ContextualCheckBlockHeader(); we wouldn't want to | ||||
// re-enforce that rule here (at least until we make it impossible for | // re-enforce that rule here (at least until we make it impossible for | ||||
// GetAdjustedTime() to go backward). | // GetAdjustedTime() to go backward). | ||||
if (!CheckBlock(block, state, consensusParams, | if (!CheckBlock(block, state, consensusParams, | ||||
options.withCheckPoW(!fJustCheck) | options.withCheckPoW(!fJustCheck) | ||||
.withCheckMerkleRoot(!fJustCheck))) { | .withCheckMerkleRoot(!fJustCheck))) { | ||||
if (state.GetReason() == ValidationInvalidReason::BLOCK_MUTATED) { | if (state.GetResult() == BlockValidationResult::BLOCK_MUTATED) { | ||||
// We don't write down blocks to disk if they may have been | // We don't write down blocks to disk if they may have been | ||||
// corrupted, so this should be impossible unless we're having | // corrupted, so this should be impossible unless we're having | ||||
// hardware problems. | // hardware problems. | ||||
return AbortNode(state, "Corrupt block found indicating potential " | return AbortNode(state, "Corrupt block found indicating potential " | ||||
"hardware failure; shutting down"); | "hardware failure; shutting down"); | ||||
} | } | ||||
return error("%s: Consensus::CheckBlock: %s", __func__, | return error("%s: Consensus::CheckBlock: %s", __func__, | ||||
FormatStateMessage(state)); | FormatStateMessage(state)); | ||||
▲ Show 20 Lines • Show All 97 Lines • ▼ Show 20 Lines | fEnforceBIP30 = | ||||
fEnforceBIP30 && | fEnforceBIP30 && | ||||
(!pindexBIP34height || | (!pindexBIP34height || | ||||
!(pindexBIP34height->GetBlockHash() == consensusParams.BIP34Hash)); | !(pindexBIP34height->GetBlockHash() == consensusParams.BIP34Hash)); | ||||
if (fEnforceBIP30) { | if (fEnforceBIP30) { | ||||
for (const auto &tx : block.vtx) { | for (const auto &tx : block.vtx) { | ||||
for (size_t o = 0; o < tx->vout.size(); o++) { | for (size_t o = 0; o < tx->vout.size(); o++) { | ||||
if (view.HaveCoin(COutPoint(tx->GetId(), o))) { | if (view.HaveCoin(COutPoint(tx->GetId(), o))) { | ||||
return state.Invalid( | LogPrintf("ERROR: ConnectBlock(): tried to overwrite " | ||||
ValidationInvalidReason::CONSENSUS, | "transaction\n"); | ||||
error("ConnectBlock(): tried to overwrite transaction"), | return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, | ||||
REJECT_INVALID, "bad-txns-BIP30"); | REJECT_INVALID, "bad-txns-BIP30"); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
// Start enforcing BIP68 (sequence locks). | // Start enforcing BIP68 (sequence locks). | ||||
int nLockTimeFlags = 0; | int nLockTimeFlags = 0; | ||||
if (pindex->nHeight >= consensusParams.CSVHeight) { | if (pindex->nHeight >= consensusParams.CSVHeight) { | ||||
Show All 39 Lines | try { | ||||
// This error will be thrown from AddCoin if we try to connect a block | // This error will be thrown from AddCoin if we try to connect a block | ||||
// containing duplicate transactions. Such a thing should normally be | // containing duplicate transactions. Such a thing should normally be | ||||
// caught early nowadays (due to ContextualCheckBlock's CTOR | // caught early nowadays (due to ContextualCheckBlock's CTOR | ||||
// enforcement) however some edge cases can escape that: | // enforcement) however some edge cases can escape that: | ||||
// - ContextualCheckBlock does not get re-run after saving the block to | // - ContextualCheckBlock does not get re-run after saving the block to | ||||
// disk, and older versions may have saved a weird block. | // disk, and older versions may have saved a weird block. | ||||
// - its checks are not applied to pre-CTOR chains, which we might visit | // - its checks are not applied to pre-CTOR chains, which we might visit | ||||
// with checkpointing off. | // with checkpointing off. | ||||
return state.Invalid( | LogPrintf("ERROR: ConnectBlock(): tried to overwrite transaction\n"); | ||||
ValidationInvalidReason::CONSENSUS, | return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, | ||||
error("ConnectBlock(): tried to overwrite transaction"), | |||||
REJECT_INVALID, "tx-duplicate"); | REJECT_INVALID, "tx-duplicate"); | ||||
} | } | ||||
size_t txIndex = 0; | size_t txIndex = 0; | ||||
for (const auto &ptx : block.vtx) { | for (const auto &ptx : block.vtx) { | ||||
const CTransaction &tx = *ptx; | const CTransaction &tx = *ptx; | ||||
const bool isCoinBase = tx.IsCoinBase(); | const bool isCoinBase = tx.IsCoinBase(); | ||||
nInputs += tx.vin.size(); | nInputs += tx.vin.size(); | ||||
{ | |||||
Amount txfee = Amount::zero(); | Amount txfee = Amount::zero(); | ||||
if (!isCoinBase && !Consensus::CheckTxInputs(tx, state, view, | TxValidationState tx_state; | ||||
pindex->nHeight, txfee)) { | if (!isCoinBase && | ||||
if (!IsBlockReason(state.GetReason())) { | !Consensus::CheckTxInputs(tx, tx_state, view, pindex->nHeight, | ||||
// CheckTxInputs may return MISSING_INPUTS or PREMATURE_SPEND | txfee)) { | ||||
// but we can't return that, as it's not defined for a block, so | // Any transaction validation failure in ConnectBlock is a block | ||||
// we reset the reason flag to CONSENSUS here. | // consensus failure. | ||||
state.Invalid(ValidationInvalidReason::CONSENSUS, false, | state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, | ||||
state.GetRejectCode(), state.GetRejectReason(), | tx_state.GetRejectCode(), | ||||
state.GetDebugMessage()); | tx_state.GetRejectReason(), | ||||
} | tx_state.GetDebugMessage()); | ||||
return error("%s: Consensus::CheckTxInputs: %s, %s", __func__, | return error("%s: Consensus::CheckTxInputs: %s, %s", __func__, | ||||
tx.GetId().ToString(), FormatStateMessage(state)); | tx.GetId().ToString(), FormatStateMessage(state)); | ||||
} | } | ||||
nFees += txfee; | nFees += txfee; | ||||
} | |||||
if (!MoneyRange(nFees)) { | if (!MoneyRange(nFees)) { | ||||
return state.Invalid( | LogPrintf("ERROR: %s: accumulated fee in the block out of range.\n", | ||||
ValidationInvalidReason::CONSENSUS, | __func__); | ||||
error("%s: accumulated fee in the block out of range.", | return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, | ||||
__func__), | REJECT_INVALID, | ||||
REJECT_INVALID, "bad-txns-accumulated-fee-outofrange"); | "bad-txns-accumulated-fee-outofrange"); | ||||
} | } | ||||
// The following checks do not apply to the coinbase. | // The following checks do not apply to the coinbase. | ||||
if (isCoinBase) { | if (isCoinBase) { | ||||
continue; | continue; | ||||
} | } | ||||
// Check that transaction is BIP68 final BIP68 lock checks (as | // Check that transaction is BIP68 final BIP68 lock checks (as | ||||
// opposed to nLockTime checks) must be in ConnectBlock because they | // opposed to nLockTime checks) must be in ConnectBlock because they | ||||
// require the UTXO set. | // require the UTXO set. | ||||
prevheights.resize(tx.vin.size()); | prevheights.resize(tx.vin.size()); | ||||
for (size_t j = 0; j < tx.vin.size(); j++) { | for (size_t j = 0; j < tx.vin.size(); j++) { | ||||
prevheights[j] = view.AccessCoin(tx.vin[j].prevout).GetHeight(); | prevheights[j] = view.AccessCoin(tx.vin[j].prevout).GetHeight(); | ||||
} | } | ||||
if (!SequenceLocks(tx, nLockTimeFlags, &prevheights, *pindex)) { | if (!SequenceLocks(tx, nLockTimeFlags, &prevheights, *pindex)) { | ||||
return state.Invalid( | LogPrintf("ERROR: %s: contains a non-BIP68-final transaction\n", | ||||
ValidationInvalidReason::CONSENSUS, | __func__); | ||||
error("%s: contains a non-BIP68-final transaction", __func__), | return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, | ||||
REJECT_INVALID, "bad-txns-nonfinal"); | REJECT_INVALID, "bad-txns-nonfinal"); | ||||
} | } | ||||
// Don't cache results if we're actually connecting blocks (still | // Don't cache results if we're actually connecting blocks (still | ||||
// consult the cache, though). | // consult the cache, though). | ||||
bool fCacheResults = fJustCheck; | bool fCacheResults = fJustCheck; | ||||
const bool fEnforceSigCheck = flags & SCRIPT_ENFORCE_SIGCHECKS; | const bool fEnforceSigCheck = flags & SCRIPT_ENFORCE_SIGCHECKS; | ||||
if (!fEnforceSigCheck) { | if (!fEnforceSigCheck) { | ||||
// Historically, there has been transactions with a very high | // Historically, there has been transactions with a very high | ||||
// sigcheck count, so we need to disable this check for such | // sigcheck count, so we need to disable this check for such | ||||
// transactions. | // transactions. | ||||
nSigChecksTxLimiters[txIndex] = TxSigCheckLimiter::getDisabled(); | nSigChecksTxLimiters[txIndex] = TxSigCheckLimiter::getDisabled(); | ||||
} | } | ||||
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; | ||||
if (!CheckInputs(tx, state, view, fScriptChecks, flags, fCacheResults, | TxValidationState tx_state; | ||||
fCacheResults, PrecomputedTransactionData(tx), | if (!CheckInputs(tx, tx_state, view, fScriptChecks, flags, | ||||
nSigChecksRet, nSigChecksTxLimiters[txIndex], | fCacheResults, fCacheResults, | ||||
&nSigChecksBlockLimiter, &vChecks)) { | PrecomputedTransactionData(tx), nSigChecksRet, | ||||
if (state.GetReason() == ValidationInvalidReason::TX_NOT_STANDARD) { | nSigChecksTxLimiters[txIndex], &nSigChecksBlockLimiter, | ||||
// CheckInputs may return NOT_STANDARD for extra flags we | &vChecks)) { | ||||
// passed, but we can't return that, as it's not defined for a | // Any transaction validation failure in ConnectBlock is a block | ||||
// block, so we reset the reason flag to CONSENSUS here. In the | // consensus failure | ||||
// event of a future soft-fork, we may need to consider whether | state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, | ||||
// rewriting to CONSENSUS or RECENT_CONSENSUS_CHANGE would be | |||||
// more appropriate. | |||||
state.Invalid(ValidationInvalidReason::CONSENSUS, false, | |||||
state.GetRejectCode(), state.GetRejectReason(), | state.GetRejectCode(), state.GetRejectReason(), | ||||
state.GetDebugMessage()); | state.GetDebugMessage()); | ||||
} | |||||
return error("ConnectBlock(): CheckInputs on %s failed with %s", | return error("ConnectBlock(): CheckInputs on %s failed with %s", | ||||
tx.GetId().ToString(), FormatStateMessage(state)); | tx.GetId().ToString(), FormatStateMessage(state)); | ||||
} | } | ||||
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, | ||||
Show All 12 Lines | LogPrint(BCLog::BENCH, | ||||
(unsigned)block.vtx.size(), MILLI * (nTime3 - nTime2), | (unsigned)block.vtx.size(), MILLI * (nTime3 - nTime2), | ||||
MILLI * (nTime3 - nTime2) / block.vtx.size(), | MILLI * (nTime3 - nTime2) / block.vtx.size(), | ||||
nInputs <= 1 ? 0 : MILLI * (nTime3 - nTime2) / (nInputs - 1), | nInputs <= 1 ? 0 : MILLI * (nTime3 - nTime2) / (nInputs - 1), | ||||
nTimeConnect * MICRO, nTimeConnect * MILLI / nBlocksTotal); | nTimeConnect * MICRO, nTimeConnect * MILLI / nBlocksTotal); | ||||
Amount blockReward = | Amount blockReward = | ||||
nFees + GetBlockSubsidy(pindex->nHeight, consensusParams); | nFees + GetBlockSubsidy(pindex->nHeight, consensusParams); | ||||
if (block.vtx[0]->GetValueOut() > blockReward) { | if (block.vtx[0]->GetValueOut() > blockReward) { | ||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, | LogPrintf("ERROR: ConnectBlock(): coinbase pays too much (actual=%d vs " | ||||
error("ConnectBlock(): coinbase pays too much " | "limit=%d)\n", | ||||
"(actual=%d vs limit=%d)", | block.vtx[0]->GetValueOut(), blockReward); | ||||
block.vtx[0]->GetValueOut(), blockReward), | return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, | ||||
REJECT_INVALID, "bad-cb-amount"); | REJECT_INVALID, "bad-cb-amount"); | ||||
} | } | ||||
const std::vector<CTxDestination> whitelist = | const std::vector<CTxDestination> whitelist = | ||||
GetMinerFundWhitelist(consensusParams, pindex->pprev); | GetMinerFundWhitelist(consensusParams, pindex->pprev); | ||||
if (!whitelist.empty()) { | if (!whitelist.empty()) { | ||||
const Amount required = blockReward / MINER_FUND_RATIO; | const Amount required = blockReward / MINER_FUND_RATIO; | ||||
Show All 11 Lines | if (!whitelist.empty()) { | ||||
if (std::find(whitelist.begin(), whitelist.end(), address) != | if (std::find(whitelist.begin(), whitelist.end(), address) != | ||||
whitelist.end()) { | whitelist.end()) { | ||||
goto MinerFundSuccess; | goto MinerFundSuccess; | ||||
} | } | ||||
} | } | ||||
// We did not find an output that match the miner fund requirements. | // We did not find an output that match the miner fund requirements. | ||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, | return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, | ||||
REJECT_INVALID, "bad-cb-minerfund"); | REJECT_INVALID, "bad-cb-minerfund"); | ||||
} | } | ||||
MinerFundSuccess: | MinerFundSuccess: | ||||
if (!control.Wait()) { | if (!control.Wait()) { | ||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, | return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, | ||||
REJECT_INVALID, "blk-bad-inputs", | REJECT_INVALID, "blk-bad-inputs", | ||||
"parallel script check failed"); | "parallel script check failed"); | ||||
} | } | ||||
int64_t nTime4 = GetTimeMicros(); | int64_t nTime4 = GetTimeMicros(); | ||||
nTimeVerify += nTime4 - nTime2; | nTimeVerify += nTime4 - nTime2; | ||||
LogPrint( | LogPrint( | ||||
BCLog::BENCH, | BCLog::BENCH, | ||||
Show All 30 Lines | MinerFundSuccess: | ||||
LogPrint(BCLog::BENCH, " - Callbacks: %.2fms [%.2fs (%.2fms/blk)]\n", | LogPrint(BCLog::BENCH, " - Callbacks: %.2fms [%.2fs (%.2fms/blk)]\n", | ||||
MILLI * (nTime6 - nTime5), nTimeCallbacks * MICRO, | MILLI * (nTime6 - nTime5), nTimeCallbacks * MICRO, | ||||
nTimeCallbacks * MILLI / nBlocksTotal); | nTimeCallbacks * MILLI / nBlocksTotal); | ||||
return true; | return true; | ||||
} | } | ||||
bool CChainState::FlushStateToDisk(const CChainParams &chainparams, | bool CChainState::FlushStateToDisk(const CChainParams &chainparams, | ||||
CValidationState &state, FlushStateMode mode, | BlockValidationState &state, | ||||
FlushStateMode mode, | |||||
int nManualPruneHeight) { | int nManualPruneHeight) { | ||||
int64_t nMempoolUsage = g_mempool.DynamicMemoryUsage(); | int64_t nMempoolUsage = g_mempool.DynamicMemoryUsage(); | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
static int64_t nLastWrite = 0; | static int64_t nLastWrite = 0; | ||||
static int64_t nLastFlush = 0; | static int64_t nLastFlush = 0; | ||||
std::set<int> setFilesToPrune; | std::set<int> setFilesToPrune; | ||||
bool full_flush_completed = false; | bool full_flush_completed = false; | ||||
try { | try { | ||||
▲ Show 20 Lines • Show All 129 Lines • ▼ Show 20 Lines | bool CChainState::FlushStateToDisk(const CChainParams &chainparams, | ||||
} catch (const std::runtime_error &e) { | } catch (const std::runtime_error &e) { | ||||
return AbortNode(state, std::string("System error while flushing: ") + | return AbortNode(state, std::string("System error while flushing: ") + | ||||
e.what()); | e.what()); | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
void CChainState::ForceFlushStateToDisk() { | void CChainState::ForceFlushStateToDisk() { | ||||
CValidationState state; | BlockValidationState state; | ||||
const CChainParams &chainparams = Params(); | const CChainParams &chainparams = Params(); | ||||
if (!this->FlushStateToDisk(chainparams, state, FlushStateMode::ALWAYS)) { | if (!this->FlushStateToDisk(chainparams, state, FlushStateMode::ALWAYS)) { | ||||
LogPrintf("%s: failed to flush state (%s)\n", __func__, | LogPrintf("%s: failed to flush state (%s)\n", __func__, | ||||
FormatStateMessage(state)); | FormatStateMessage(state)); | ||||
} | } | ||||
} | } | ||||
void CChainState::PruneAndFlush() { | void CChainState::PruneAndFlush() { | ||||
CValidationState state; | BlockValidationState state; | ||||
fCheckForPruning = true; | fCheckForPruning = true; | ||||
const CChainParams &chainparams = Params(); | const CChainParams &chainparams = Params(); | ||||
if (!this->FlushStateToDisk(chainparams, state, FlushStateMode::NONE)) { | if (!this->FlushStateToDisk(chainparams, state, FlushStateMode::NONE)) { | ||||
LogPrintf("%s: failed to flush state (%s)\n", __func__, | LogPrintf("%s: failed to flush state (%s)\n", __func__, | ||||
FormatStateMessage(state)); | FormatStateMessage(state)); | ||||
} | } | ||||
} | } | ||||
Show All 27 Lines | |||||
* should make the mempool consistent again by calling updateMempoolForReorg. | * should make the mempool consistent again by calling updateMempoolForReorg. | ||||
* with cs_main held. | * with cs_main held. | ||||
* | * | ||||
* If disconnectpool is nullptr, then no disconnected transactions are added to | * If disconnectpool is nullptr, then no disconnected transactions are added to | ||||
* disconnectpool (note that the caller is responsible for mempool consistency | * disconnectpool (note that the caller is responsible for mempool consistency | ||||
* in any case). | * in any case). | ||||
*/ | */ | ||||
bool CChainState::DisconnectTip(const CChainParams ¶ms, | bool CChainState::DisconnectTip(const CChainParams ¶ms, | ||||
CValidationState &state, | BlockValidationState &state, | ||||
DisconnectedBlockTransactions *disconnectpool) { | DisconnectedBlockTransactions *disconnectpool) { | ||||
AssertLockHeld(cs_main); | AssertLockHeld(cs_main); | ||||
CBlockIndex *pindexDelete = m_chain.Tip(); | CBlockIndex *pindexDelete = m_chain.Tip(); | ||||
const Consensus::Params &consensusParams = params.GetConsensus(); | const Consensus::Params &consensusParams = params.GetConsensus(); | ||||
assert(pindexDelete); | assert(pindexDelete); | ||||
// Read block from disk. | // Read block from disk. | ||||
▲ Show 20 Lines • Show All 129 Lines • ▼ Show 20 Lines | void NotifyEntryRemoved(CTransactionRef txRemoved, | ||||
assert(!blocksConnected.back().pindex); | assert(!blocksConnected.back().pindex); | ||||
if (reason == MemPoolRemovalReason::CONFLICT) { | if (reason == MemPoolRemovalReason::CONFLICT) { | ||||
blocksConnected.back().conflictedTxs->emplace_back( | blocksConnected.back().conflictedTxs->emplace_back( | ||||
std::move(txRemoved)); | std::move(txRemoved)); | ||||
} | } | ||||
} | } | ||||
}; | }; | ||||
static bool FinalizeBlockInternal(const Config &config, CValidationState &state, | static bool FinalizeBlockInternal(const Config &config, | ||||
BlockValidationState &state, | |||||
const CBlockIndex *pindex) | const CBlockIndex *pindex) | ||||
EXCLUSIVE_LOCKS_REQUIRED(cs_main) { | EXCLUSIVE_LOCKS_REQUIRED(cs_main) { | ||||
AssertLockHeld(cs_main); | AssertLockHeld(cs_main); | ||||
if (pindex->nStatus.isInvalid()) { | if (pindex->nStatus.isInvalid()) { | ||||
// We try to finalize an invalid block. | // We try to finalize an invalid block. | ||||
return state.Invalid(ValidationInvalidReason::CACHED_INVALID, | LogPrintf("ERROR: %s: Trying to finalize invalid block %s\n", __func__, | ||||
error("%s: Trying to finalize invalid block %s", | pindex->GetBlockHash().ToString()); | ||||
__func__, pindex->GetBlockHash().ToString()), | return state.Invalid(BlockValidationResult::BLOCK_CACHED_INVALID, | ||||
REJECT_INVALID, "finalize-invalid-block"); | REJECT_INVALID, "finalize-invalid-block"); | ||||
} | } | ||||
// Check that the request is consistent with current finalization. | // Check that the request is consistent with current finalization. | ||||
if (pindexFinalized && !AreOnTheSameFork(pindex, pindexFinalized)) { | if (pindexFinalized && !AreOnTheSameFork(pindex, pindexFinalized)) { | ||||
return state.Invalid( | LogPrintf("ERROR: %s: Trying to finalize block %s which conflicts with " | ||||
ValidationInvalidReason::BLOCK_FINALIZATION, | "already finalized block\n", | ||||
error("%s: Trying to finalize block %s which conflicts " | __func__, pindex->GetBlockHash().ToString()); | ||||
"with already finalized block", | return state.Invalid(BlockValidationResult::BLOCK_FINALIZATION, | ||||
__func__, pindex->GetBlockHash().ToString()), | REJECT_AGAINST_FINALIZED, | ||||
REJECT_AGAINST_FINALIZED, "bad-fork-prior-finalized"); | "bad-fork-prior-finalized"); | ||||
} | } | ||||
if (IsBlockFinalized(pindex)) { | if (IsBlockFinalized(pindex)) { | ||||
// The block is already finalized. | // The block is already finalized. | ||||
return true; | return true; | ||||
} | } | ||||
// We have a new block to finalize. | // We have a new block to finalize. | ||||
▲ Show 20 Lines • Show All 52 Lines • ▼ Show 20 Lines | |||||
/** | /** | ||||
* Connect a new block to m_chain. pblock is either nullptr or a pointer to | * Connect a new block to m_chain. pblock is either nullptr or a pointer to | ||||
* a CBlock corresponding to pindexNew, to bypass loading it again from disk. | * a CBlock corresponding to pindexNew, to bypass loading it again from disk. | ||||
* | * | ||||
* The block is always added to connectTrace (either after loading from disk or | * The block is always added to connectTrace (either after loading from disk or | ||||
* by copying pblock) - if that is not intended, care must be taken to remove | * by copying pblock) - if that is not intended, care must be taken to remove | ||||
* the last entry in blocksConnected in case of failure. | * the last entry in blocksConnected in case of failure. | ||||
*/ | */ | ||||
bool CChainState::ConnectTip(const Config &config, CValidationState &state, | bool CChainState::ConnectTip(const Config &config, BlockValidationState &state, | ||||
CBlockIndex *pindexNew, | CBlockIndex *pindexNew, | ||||
const std::shared_ptr<const CBlock> &pblock, | const std::shared_ptr<const CBlock> &pblock, | ||||
ConnectTrace &connectTrace, | ConnectTrace &connectTrace, | ||||
DisconnectedBlockTransactions &disconnectpool) { | DisconnectedBlockTransactions &disconnectpool) { | ||||
AssertLockHeld(cs_main); | AssertLockHeld(cs_main); | ||||
const CChainParams ¶ms = config.GetChainParams(); | const CChainParams ¶ms = config.GetChainParams(); | ||||
const Consensus::Params &consensusParams = params.GetConsensus(); | const Consensus::Params &consensusParams = params.GetConsensus(); | ||||
▲ Show 20 Lines • Show All 289 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
/** | /** | ||||
* Try to make some progress towards making pindexMostWork the active block. | * Try to make some progress towards making pindexMostWork the active block. | ||||
* pblock is either nullptr or a pointer to a CBlock corresponding to | * pblock is either nullptr or a pointer to a CBlock corresponding to | ||||
* pindexMostWork. | * pindexMostWork. | ||||
*/ | */ | ||||
bool CChainState::ActivateBestChainStep( | bool CChainState::ActivateBestChainStep( | ||||
const Config &config, CValidationState &state, CBlockIndex *pindexMostWork, | const Config &config, BlockValidationState &state, | ||||
const std::shared_ptr<const CBlock> &pblock, bool &fInvalidFound, | CBlockIndex *pindexMostWork, const std::shared_ptr<const CBlock> &pblock, | ||||
ConnectTrace &connectTrace) { | bool &fInvalidFound, ConnectTrace &connectTrace) { | ||||
AssertLockHeld(cs_main); | AssertLockHeld(cs_main); | ||||
const CBlockIndex *pindexOldTip = m_chain.Tip(); | const CBlockIndex *pindexOldTip = m_chain.Tip(); | ||||
const CBlockIndex *pindexFork = m_chain.FindFork(pindexMostWork); | const CBlockIndex *pindexFork = m_chain.FindFork(pindexMostWork); | ||||
// Disconnect active blocks which are no longer in the best chain. | // Disconnect active blocks which are no longer in the best chain. | ||||
bool fBlocksDisconnected = false; | bool fBlocksDisconnected = false; | ||||
DisconnectedBlockTransactions disconnectpool; | DisconnectedBlockTransactions disconnectpool; | ||||
Show All 36 Lines | while (fContinue && nHeight != pindexMostWork->nHeight) { | ||||
for (CBlockIndex *pindexConnect : reverse_iterate(vpindexToConnect)) { | for (CBlockIndex *pindexConnect : reverse_iterate(vpindexToConnect)) { | ||||
if (!ConnectTip(config, state, pindexConnect, | if (!ConnectTip(config, state, pindexConnect, | ||||
pindexConnect == pindexMostWork | pindexConnect == pindexMostWork | ||||
? pblock | ? pblock | ||||
: std::shared_ptr<const CBlock>(), | : std::shared_ptr<const CBlock>(), | ||||
connectTrace, disconnectpool)) { | connectTrace, disconnectpool)) { | ||||
if (state.IsInvalid()) { | if (state.IsInvalid()) { | ||||
// The block violates a consensus rule. | // The block violates a consensus rule. | ||||
if (state.GetReason() != | if (state.GetResult() != | ||||
ValidationInvalidReason::BLOCK_MUTATED) { | BlockValidationResult::BLOCK_MUTATED) { | ||||
InvalidChainFound(vpindexToConnect.back()); | InvalidChainFound(vpindexToConnect.back()); | ||||
} | } | ||||
state = BlockValidationState(); | |||||
state = CValidationState(); | |||||
fInvalidFound = true; | fInvalidFound = true; | ||||
fContinue = false; | fContinue = false; | ||||
break; | break; | ||||
} | } | ||||
// A system error occurred (disk space, database error, ...). | // A system error occurred (disk space, database error, ...). | ||||
// Make the mempool consistent with the current tip, just in | // Make the mempool consistent with the current tip, just in | ||||
// case any observers try to use it before shutdown. | // case any observers try to use it before shutdown. | ||||
▲ Show 20 Lines • Show All 62 Lines • ▼ Show 20 Lines | |||||
* or an activated best chain. pblock is either nullptr or a pointer to a block | * or an activated best chain. pblock is either nullptr or a pointer to a block | ||||
* that is already loaded (to avoid loading it again from disk). | * that is already loaded (to avoid loading it again from disk). | ||||
* | * | ||||
* ActivateBestChain is split into steps (see ActivateBestChainStep) so that | * ActivateBestChain is split into steps (see ActivateBestChainStep) so that | ||||
* we avoid holding cs_main for an extended period of time; the length of this | * we avoid holding cs_main for an extended period of time; the length of this | ||||
* call may be quite long during reindexing or a substantial reorg. | * call may be quite long during reindexing or a substantial reorg. | ||||
*/ | */ | ||||
bool CChainState::ActivateBestChain(const Config &config, | bool CChainState::ActivateBestChain(const Config &config, | ||||
CValidationState &state, | BlockValidationState &state, | ||||
std::shared_ptr<const CBlock> pblock) { | std::shared_ptr<const CBlock> pblock) { | ||||
// Note that while we're often called here from ProcessNewBlock, this is | // Note that while we're often called here from ProcessNewBlock, this is | ||||
// far from a guarantee. Things in the P2P/RPC will often end up calling | // far from a guarantee. Things in the P2P/RPC will often end up calling | ||||
// us in the middle of ProcessNewBlock - do not assume pblock is set | // us in the middle of ProcessNewBlock - do not assume pblock is set | ||||
// sanely for performance or correctness! | // sanely for performance or correctness! | ||||
AssertLockNotHeld(cs_main); | AssertLockNotHeld(cs_main); | ||||
const CChainParams ¶ms = config.GetChainParams(); | const CChainParams ¶ms = config.GetChainParams(); | ||||
▲ Show 20 Lines • Show All 119 Lines • ▼ Show 20 Lines | bool CChainState::ActivateBestChain(const Config &config, | ||||
// Write changes periodically to disk, after relay. | // Write changes periodically to disk, after relay. | ||||
if (!FlushStateToDisk(params, state, FlushStateMode::PERIODIC)) { | if (!FlushStateToDisk(params, state, FlushStateMode::PERIODIC)) { | ||||
return false; | return false; | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
bool ActivateBestChain(const Config &config, CValidationState &state, | bool ActivateBestChain(const Config &config, BlockValidationState &state, | ||||
std::shared_ptr<const CBlock> pblock) { | std::shared_ptr<const CBlock> pblock) { | ||||
return ::ChainstateActive().ActivateBestChain(config, state, | return ::ChainstateActive().ActivateBestChain(config, state, | ||||
std::move(pblock)); | std::move(pblock)); | ||||
} | } | ||||
bool CChainState::PreciousBlock(const Config &config, CValidationState &state, | bool CChainState::PreciousBlock(const Config &config, | ||||
BlockValidationState &state, | |||||
CBlockIndex *pindex) { | CBlockIndex *pindex) { | ||||
{ | { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
if (pindex->nChainWork < m_chain.Tip()->nChainWork) { | if (pindex->nChainWork < m_chain.Tip()->nChainWork) { | ||||
// Nothing to do, this block is not at the tip. | // Nothing to do, this block is not at the tip. | ||||
return true; | return true; | ||||
} | } | ||||
Show All 21 Lines | bool CChainState::PreciousBlock(const Config &config, | ||||
setBlockIndexCandidates.insert(pindex); | setBlockIndexCandidates.insert(pindex); | ||||
PruneBlockIndexCandidates(); | PruneBlockIndexCandidates(); | ||||
} | } | ||||
} | } | ||||
return ActivateBestChain(config, state); | return ActivateBestChain(config, state); | ||||
} | } | ||||
bool PreciousBlock(const Config &config, CValidationState &state, | bool PreciousBlock(const Config &config, BlockValidationState &state, | ||||
CBlockIndex *pindex) { | CBlockIndex *pindex) { | ||||
return ::ChainstateActive().PreciousBlock(config, state, pindex); | return ::ChainstateActive().PreciousBlock(config, state, pindex); | ||||
} | } | ||||
bool CChainState::UnwindBlock(const Config &config, CValidationState &state, | bool CChainState::UnwindBlock(const Config &config, BlockValidationState &state, | ||||
CBlockIndex *pindex, bool invalidate) { | CBlockIndex *pindex, bool invalidate) { | ||||
CBlockIndex *to_mark_failed_or_parked = pindex; | CBlockIndex *to_mark_failed_or_parked = pindex; | ||||
bool pindex_was_in_chain = false; | bool pindex_was_in_chain = false; | ||||
int disconnected = 0; | int disconnected = 0; | ||||
// Disconnect (descendants of) pindex, and mark them invalid. | // Disconnect (descendants of) pindex, and mark them invalid. | ||||
while (true) { | while (true) { | ||||
if (ShutdownRequested()) { | if (ShutdownRequested()) { | ||||
▲ Show 20 Lines • Show All 104 Lines • ▼ Show 20 Lines | bool CChainState::UnwindBlock(const Config &config, BlockValidationState &state, | ||||
// Only notify about a new block tip if the active chain was modified. | // Only notify about a new block tip if the active chain was modified. | ||||
if (pindex_was_in_chain) { | if (pindex_was_in_chain) { | ||||
uiInterface.NotifyBlockTip(IsInitialBlockDownload(), | uiInterface.NotifyBlockTip(IsInitialBlockDownload(), | ||||
to_mark_failed_or_parked->pprev); | to_mark_failed_or_parked->pprev); | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
bool FinalizeBlockAndInvalidate(const Config &config, CValidationState &state, | bool FinalizeBlockAndInvalidate(const Config &config, | ||||
BlockValidationState &state, | |||||
CBlockIndex *pindex) { | CBlockIndex *pindex) { | ||||
AssertLockHeld(cs_main); | AssertLockHeld(cs_main); | ||||
if (!FinalizeBlockInternal(config, state, pindex)) { | if (!FinalizeBlockInternal(config, state, pindex)) { | ||||
// state is set by FinalizeBlockInternal. | // state is set by FinalizeBlockInternal. | ||||
return false; | return false; | ||||
} | } | ||||
// We have a valid candidate, make sure it is not parked. | // We have a valid candidate, make sure it is not parked. | ||||
if (pindex->nStatus.isOnParkedChain()) { | if (pindex->nStatus.isOnParkedChain()) { | ||||
UnparkBlock(pindex); | UnparkBlock(pindex); | ||||
} | } | ||||
// If the finalized block is not on the active chain, we may need to rewind. | // If the finalized block is not on the active chain, we may need to rewind. | ||||
if (!::ChainActive().Contains(pindex)) { | if (!::ChainActive().Contains(pindex)) { | ||||
const CBlockIndex *pindexFork = ::ChainActive().FindFork(pindex); | const CBlockIndex *pindexFork = ::ChainActive().FindFork(pindex); | ||||
CBlockIndex *pindexToInvalidate = ::ChainActive().Next(pindexFork); | CBlockIndex *pindexToInvalidate = ::ChainActive().Next(pindexFork); | ||||
if (pindexToInvalidate) { | if (pindexToInvalidate) { | ||||
return InvalidateBlock(config, state, pindexToInvalidate); | return InvalidateBlock(config, state, pindexToInvalidate); | ||||
} | } | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
bool InvalidateBlock(const Config &config, CValidationState &state, | bool InvalidateBlock(const Config &config, BlockValidationState &state, | ||||
CBlockIndex *pindex) { | CBlockIndex *pindex) { | ||||
return ::ChainstateActive().UnwindBlock(config, state, pindex, true); | return ::ChainstateActive().UnwindBlock(config, state, pindex, true); | ||||
} | } | ||||
bool ParkBlock(const Config &config, CValidationState &state, | bool ParkBlock(const Config &config, BlockValidationState &state, | ||||
CBlockIndex *pindex) { | CBlockIndex *pindex) { | ||||
return ::ChainstateActive().UnwindBlock(config, state, pindex, false); | return ::ChainstateActive().UnwindBlock(config, state, pindex, false); | ||||
} | } | ||||
template <typename F> | template <typename F> | ||||
bool CChainState::UpdateFlagsForBlock(CBlockIndex *pindexBase, | bool CChainState::UpdateFlagsForBlock(CBlockIndex *pindexBase, | ||||
CBlockIndex *pindex, F f) { | CBlockIndex *pindex, F f) { | ||||
BlockStatus newStatus = f(pindex->nStatus); | BlockStatus newStatus = f(pindex->nStatus); | ||||
▲ Show 20 Lines • Show All 262 Lines • ▼ Show 20 Lines | if (!fKnown) { | ||||
fCheckForPruning = true; | fCheckForPruning = true; | ||||
} | } | ||||
} | } | ||||
setDirtyFileInfo.insert(nFile); | setDirtyFileInfo.insert(nFile); | ||||
return true; | return true; | ||||
} | } | ||||
static bool FindUndoPos(CValidationState &state, int nFile, FlatFilePos &pos, | static bool FindUndoPos(BlockValidationState &state, int nFile, | ||||
unsigned int nAddSize) { | FlatFilePos &pos, unsigned int nAddSize) { | ||||
pos.nFile = nFile; | pos.nFile = nFile; | ||||
LOCK(cs_LastBlockFile); | LOCK(cs_LastBlockFile); | ||||
pos.nPos = vinfoBlockFile[nFile].nUndoSize; | pos.nPos = vinfoBlockFile[nFile].nUndoSize; | ||||
vinfoBlockFile[nFile].nUndoSize += nAddSize; | vinfoBlockFile[nFile].nUndoSize += nAddSize; | ||||
setDirtyFileInfo.insert(nFile); | setDirtyFileInfo.insert(nFile); | ||||
Show All 14 Lines | |||||
/** | /** | ||||
* Return true if the provided block header is valid. | * Return true if the provided block header is valid. | ||||
* Only verify PoW if blockValidationOptions is configured to do so. | * Only verify PoW if blockValidationOptions is configured to do so. | ||||
* This allows validation of headers on which the PoW hasn't been done. | * This allows validation of headers on which the PoW hasn't been done. | ||||
* For example: to validate template handed to mining software. | * For example: to validate template handed to mining software. | ||||
* Do not call this for any check that depends on the context. | * Do not call this for any check that depends on the context. | ||||
* For context-dependent calls, see ContextualCheckBlockHeader. | * For context-dependent calls, see ContextualCheckBlockHeader. | ||||
*/ | */ | ||||
static bool CheckBlockHeader(const CBlockHeader &block, CValidationState &state, | static bool CheckBlockHeader(const CBlockHeader &block, | ||||
BlockValidationState &state, | |||||
const Consensus::Params ¶ms, | const Consensus::Params ¶ms, | ||||
BlockValidationOptions validationOptions) { | BlockValidationOptions validationOptions) { | ||||
// Check proof of work matches claimed amount | // Check proof of work matches claimed amount | ||||
if (validationOptions.shouldValidatePoW() && | if (validationOptions.shouldValidatePoW() && | ||||
!CheckProofOfWork(block.GetHash(), block.nBits, params)) { | !CheckProofOfWork(block.GetHash(), block.nBits, params)) { | ||||
return state.Invalid(ValidationInvalidReason::BLOCK_INVALID_HEADER, | return state.Invalid(BlockValidationResult::BLOCK_INVALID_HEADER, | ||||
false, REJECT_INVALID, "high-hash", | REJECT_INVALID, "high-hash", | ||||
"proof of work failed"); | "proof of work failed"); | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
bool CheckBlock(const CBlock &block, CValidationState &state, | bool CheckBlock(const CBlock &block, BlockValidationState &state, | ||||
const Consensus::Params ¶ms, | const Consensus::Params ¶ms, | ||||
BlockValidationOptions validationOptions) { | BlockValidationOptions validationOptions) { | ||||
// These are checks that are independent of context. | // These are checks that are independent of context. | ||||
if (block.fChecked) { | if (block.fChecked) { | ||||
return true; | return true; | ||||
} | } | ||||
// Check that the header is valid (particularly PoW). This is mostly | // Check that the header is valid (particularly PoW). This is mostly | ||||
// redundant with the call in AcceptBlockHeader. | // redundant with the call in AcceptBlockHeader. | ||||
if (!CheckBlockHeader(block, state, params, validationOptions)) { | if (!CheckBlockHeader(block, state, params, validationOptions)) { | ||||
return false; | return false; | ||||
} | } | ||||
// Check the merkle root. | // Check the merkle root. | ||||
if (validationOptions.shouldValidateMerkleRoot()) { | if (validationOptions.shouldValidateMerkleRoot()) { | ||||
bool mutated; | bool mutated; | ||||
uint256 hashMerkleRoot2 = BlockMerkleRoot(block, &mutated); | uint256 hashMerkleRoot2 = BlockMerkleRoot(block, &mutated); | ||||
if (block.hashMerkleRoot != hashMerkleRoot2) { | if (block.hashMerkleRoot != hashMerkleRoot2) { | ||||
return state.Invalid(ValidationInvalidReason::BLOCK_MUTATED, false, | return state.Invalid(BlockValidationResult::BLOCK_MUTATED, | ||||
REJECT_INVALID, "bad-txnmrklroot", | REJECT_INVALID, "bad-txnmrklroot", | ||||
"hashMerkleRoot mismatch"); | "hashMerkleRoot mismatch"); | ||||
} | } | ||||
// Check for merkle tree malleability (CVE-2012-2459): repeating | // Check for merkle tree malleability (CVE-2012-2459): repeating | ||||
// sequences of transactions in a block without affecting the merkle | // sequences of transactions in a block without affecting the merkle | ||||
// root of a block, while still invalidating it. | // root of a block, while still invalidating it. | ||||
if (mutated) { | if (mutated) { | ||||
return state.Invalid(ValidationInvalidReason::BLOCK_MUTATED, false, | return state.Invalid(BlockValidationResult::BLOCK_MUTATED, | ||||
REJECT_INVALID, "bad-txns-duplicate", | REJECT_INVALID, "bad-txns-duplicate", | ||||
"duplicate transaction"); | "duplicate transaction"); | ||||
} | } | ||||
} | } | ||||
// All potential-corruption validation must be done before we do any | // All potential-corruption validation must be done before we do any | ||||
// transaction validation, as otherwise we may mark the header as invalid | // transaction validation, as otherwise we may mark the header as invalid | ||||
// because we receive the wrong transactions for it. | // because we receive the wrong transactions for it. | ||||
// First transaction must be coinbase. | // First transaction must be coinbase. | ||||
if (block.vtx.empty()) { | if (block.vtx.empty()) { | ||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, | return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, | ||||
REJECT_INVALID, "bad-cb-missing", | REJECT_INVALID, "bad-cb-missing", | ||||
"first tx is not coinbase"); | "first tx is not coinbase"); | ||||
} | } | ||||
// Size limits. | // Size limits. | ||||
auto nMaxBlockSize = validationOptions.getExcessiveBlockSize(); | auto nMaxBlockSize = validationOptions.getExcessiveBlockSize(); | ||||
// Bail early if there is no way this block is of reasonable size. | // Bail early if there is no way this block is of reasonable size. | ||||
if ((block.vtx.size() * MIN_TRANSACTION_SIZE) > nMaxBlockSize) { | if ((block.vtx.size() * MIN_TRANSACTION_SIZE) > nMaxBlockSize) { | ||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, | return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, | ||||
REJECT_INVALID, "bad-blk-length", | REJECT_INVALID, "bad-blk-length", | ||||
"size limits failed"); | "size limits failed"); | ||||
} | } | ||||
auto currentBlockSize = ::GetSerializeSize(block, PROTOCOL_VERSION); | auto currentBlockSize = ::GetSerializeSize(block, PROTOCOL_VERSION); | ||||
if (currentBlockSize > nMaxBlockSize) { | if (currentBlockSize > nMaxBlockSize) { | ||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, | return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, | ||||
REJECT_INVALID, "bad-blk-length", | REJECT_INVALID, "bad-blk-length", | ||||
"size limits failed"); | "size limits failed"); | ||||
} | } | ||||
// And a valid coinbase. | // And a valid coinbase. | ||||
if (!CheckCoinbase(*block.vtx[0], state)) { | TxValidationState tx_state; | ||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, | if (!CheckCoinbase(*block.vtx[0], tx_state)) { | ||||
state.GetRejectCode(), state.GetRejectReason(), | return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, | ||||
tx_state.GetRejectCode(), | |||||
tx_state.GetRejectReason(), | |||||
strprintf("Coinbase check failed (txid %s) %s", | strprintf("Coinbase check failed (txid %s) %s", | ||||
block.vtx[0]->GetId().ToString(), | block.vtx[0]->GetId().ToString(), | ||||
state.GetDebugMessage())); | tx_state.GetDebugMessage())); | ||||
} | } | ||||
// Check transactions for regularity, skipping the first. Note that this | // Check transactions for regularity, skipping the first. Note that this | ||||
// is the first time we check that all after the first are !IsCoinBase. | // is the first time we check that all after the first are !IsCoinBase. | ||||
for (size_t i = 1; i < block.vtx.size(); i++) { | for (size_t i = 1; i < block.vtx.size(); i++) { | ||||
auto *tx = block.vtx[i].get(); | auto *tx = block.vtx[i].get(); | ||||
if (!CheckRegularTransaction(*tx, state)) { | if (!CheckRegularTransaction(*tx, tx_state)) { | ||||
return state.Invalid( | return state.Invalid( | ||||
ValidationInvalidReason::CONSENSUS, false, | BlockValidationResult::BLOCK_CONSENSUS, | ||||
state.GetRejectCode(), state.GetRejectReason(), | tx_state.GetRejectCode(), tx_state.GetRejectReason(), | ||||
strprintf("Transaction check failed (txid %s) %s", | strprintf("Transaction check failed (txid %s) %s", | ||||
tx->GetId().ToString(), state.GetDebugMessage())); | tx->GetId().ToString(), tx_state.GetDebugMessage())); | ||||
} | } | ||||
} | } | ||||
if (validationOptions.shouldValidatePoW() && | if (validationOptions.shouldValidatePoW() && | ||||
validationOptions.shouldValidateMerkleRoot()) { | validationOptions.shouldValidateMerkleRoot()) { | ||||
block.fChecked = true; | block.fChecked = true; | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
/** | /** | ||||
* Context-dependent validity checks. | * Context-dependent validity checks. | ||||
* By "context", we mean only the previous block headers, but not the UTXO | * By "context", we mean only the previous block headers, but not the UTXO | ||||
* set; UTXO-related validity checks are done in ConnectBlock(). | * set; UTXO-related validity checks are done in ConnectBlock(). | ||||
* NOTE: This function is not currently invoked by ConnectBlock(), so we | * NOTE: This function is not currently invoked by ConnectBlock(), so we | ||||
* should consider upgrade issues if we change which consensus rules are | * should consider upgrade issues if we change which consensus rules are | ||||
* enforced in this function (eg by adding a new consensus rule). See comment | * enforced in this function (eg by adding a new consensus rule). See comment | ||||
* in ConnectBlock(). | * in ConnectBlock(). | ||||
* Note that -reindex-chainstate skips the validation that happens here! | * Note that -reindex-chainstate skips the validation that happens here! | ||||
*/ | */ | ||||
static bool | static bool ContextualCheckBlockHeader(const CChainParams ¶ms, | ||||
ContextualCheckBlockHeader(const CChainParams ¶ms, | const CBlockHeader &block, | ||||
const CBlockHeader &block, CValidationState &state, | BlockValidationState &state, | ||||
const CBlockIndex *pindexPrev, int64_t nAdjustedTime) | const CBlockIndex *pindexPrev, | ||||
int64_t nAdjustedTime) | |||||
EXCLUSIVE_LOCKS_REQUIRED(cs_main) { | EXCLUSIVE_LOCKS_REQUIRED(cs_main) { | ||||
assert(pindexPrev != nullptr); | assert(pindexPrev != nullptr); | ||||
const int nHeight = pindexPrev->nHeight + 1; | const int nHeight = pindexPrev->nHeight + 1; | ||||
// Check proof of work | // Check proof of work | ||||
const Consensus::Params &consensusParams = params.GetConsensus(); | const Consensus::Params &consensusParams = params.GetConsensus(); | ||||
if (block.nBits != | if (block.nBits != | ||||
GetNextWorkRequired(pindexPrev, &block, consensusParams)) { | GetNextWorkRequired(pindexPrev, &block, consensusParams)) { | ||||
LogPrintf("bad bits after height: %d\n", pindexPrev->nHeight); | LogPrintf("bad bits after height: %d\n", pindexPrev->nHeight); | ||||
return state.Invalid(ValidationInvalidReason::BLOCK_INVALID_HEADER, | return state.Invalid(BlockValidationResult::BLOCK_INVALID_HEADER, | ||||
false, REJECT_INVALID, "bad-diffbits", | REJECT_INVALID, "bad-diffbits", | ||||
"incorrect proof of work"); | "incorrect proof of work"); | ||||
} | } | ||||
// Check against checkpoints | // Check against checkpoints | ||||
if (fCheckpointsEnabled) { | if (fCheckpointsEnabled) { | ||||
const CCheckpointData &checkpoints = params.Checkpoints(); | const CCheckpointData &checkpoints = params.Checkpoints(); | ||||
// Check that the block chain matches the known block chain up to a | // Check that the block chain matches the known block chain up to a | ||||
// checkpoint. | // checkpoint. | ||||
if (!Checkpoints::CheckBlock(checkpoints, nHeight, block.GetHash())) { | if (!Checkpoints::CheckBlock(checkpoints, nHeight, block.GetHash())) { | ||||
return state.Invalid( | LogPrintf("ERROR: %s: rejected by checkpoint lock-in at %d\n", | ||||
ValidationInvalidReason::BLOCK_CHECKPOINT, | __func__, nHeight); | ||||
error("%s: rejected by checkpoint lock-in at %d", __func__, | return state.Invalid(BlockValidationResult::BLOCK_CHECKPOINT, | ||||
nHeight), | |||||
REJECT_CHECKPOINT, "checkpoint mismatch"); | REJECT_CHECKPOINT, "checkpoint mismatch"); | ||||
} | } | ||||
// Don't accept any forks from the main chain prior to last checkpoint. | // Don't accept any forks from the main chain prior to last checkpoint. | ||||
// GetLastCheckpoint finds the last checkpoint in MapCheckpoints that's | // GetLastCheckpoint finds the last checkpoint in MapCheckpoints that's | ||||
// in our MapBlockIndex. | // in our MapBlockIndex. | ||||
CBlockIndex *pcheckpoint = Checkpoints::GetLastCheckpoint(checkpoints); | CBlockIndex *pcheckpoint = Checkpoints::GetLastCheckpoint(checkpoints); | ||||
if (pcheckpoint && nHeight < pcheckpoint->nHeight) { | if (pcheckpoint && nHeight < pcheckpoint->nHeight) { | ||||
return state.Invalid( | LogPrintf("ERROR: %s: forked chain older than last checkpoint " | ||||
ValidationInvalidReason::BLOCK_CHECKPOINT, | "(height %d)\n", | ||||
error("%s: forked chain older than last checkpoint (height %d)", | __func__, nHeight); | ||||
__func__, nHeight), | return state.Invalid(BlockValidationResult::BLOCK_CHECKPOINT, | ||||
REJECT_CHECKPOINT, "bad-fork-prior-to-checkpoint"); | REJECT_CHECKPOINT, | ||||
"bad-fork-prior-to-checkpoint"); | |||||
} | } | ||||
} | } | ||||
// Check timestamp against prev | // Check timestamp against prev | ||||
if (block.GetBlockTime() <= pindexPrev->GetMedianTimePast()) { | if (block.GetBlockTime() <= pindexPrev->GetMedianTimePast()) { | ||||
return state.Invalid(ValidationInvalidReason::BLOCK_INVALID_HEADER, | return state.Invalid(BlockValidationResult::BLOCK_INVALID_HEADER, | ||||
false, REJECT_INVALID, "time-too-old", | REJECT_INVALID, "time-too-old", | ||||
"block's timestamp is too early"); | "block's timestamp is too early"); | ||||
} | } | ||||
// Check timestamp | // Check timestamp | ||||
if (block.GetBlockTime() > nAdjustedTime + MAX_FUTURE_BLOCK_TIME) { | if (block.GetBlockTime() > nAdjustedTime + MAX_FUTURE_BLOCK_TIME) { | ||||
return state.Invalid(ValidationInvalidReason::BLOCK_TIME_FUTURE, false, | return state.Invalid(BlockValidationResult::BLOCK_TIME_FUTURE, | ||||
REJECT_INVALID, "time-too-new", | REJECT_INVALID, "time-too-new", | ||||
"block timestamp too far in the future"); | "block timestamp too far in the future"); | ||||
} | } | ||||
// Reject outdated version blocks when 95% (75% on testnet) of the network | // Reject outdated version blocks when 95% (75% on testnet) of the network | ||||
// has upgraded: | // has upgraded: | ||||
// check for version 2, 3 and 4 upgrades | // check for version 2, 3 and 4 upgrades | ||||
if ((block.nVersion < 2 && nHeight >= consensusParams.BIP34Height) || | if ((block.nVersion < 2 && nHeight >= consensusParams.BIP34Height) || | ||||
(block.nVersion < 3 && nHeight >= consensusParams.BIP66Height) || | (block.nVersion < 3 && nHeight >= consensusParams.BIP66Height) || | ||||
(block.nVersion < 4 && nHeight >= consensusParams.BIP65Height)) { | (block.nVersion < 4 && nHeight >= consensusParams.BIP65Height)) { | ||||
return state.Invalid( | return state.Invalid( | ||||
ValidationInvalidReason::BLOCK_INVALID_HEADER, false, | BlockValidationResult::BLOCK_INVALID_HEADER, REJECT_OBSOLETE, | ||||
REJECT_OBSOLETE, strprintf("bad-version(0x%08x)", block.nVersion), | strprintf("bad-version(0x%08x)", block.nVersion), | ||||
strprintf("rejected nVersion=0x%08x block", block.nVersion)); | strprintf("rejected nVersion=0x%08x block", block.nVersion)); | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
bool ContextualCheckTransactionForCurrentBlock(const Consensus::Params ¶ms, | bool ContextualCheckTransactionForCurrentBlock(const Consensus::Params ¶ms, | ||||
const CTransaction &tx, | const CTransaction &tx, | ||||
CValidationState &state, | TxValidationState &state, | ||||
int flags) { | int flags) { | ||||
AssertLockHeld(cs_main); | AssertLockHeld(cs_main); | ||||
// By convention a negative value for flags indicates that the current | // By convention a negative value for flags indicates that the current | ||||
// network-enforced consensus rules should be used. In a future soft-fork | // network-enforced consensus rules should be used. In a future soft-fork | ||||
// scenario that would mean checking which rules would be enforced for the | // scenario that would mean checking which rules would be enforced for the | ||||
// next block and setting the appropriate flags. At the present time no | // next block and setting the appropriate flags. At the present time no | ||||
// soft-forks are scheduled, so no flags are set. | // soft-forks are scheduled, so no flags are set. | ||||
Show All 26 Lines | |||||
/** | /** | ||||
* NOTE: This function is not currently invoked by ConnectBlock(), so we | * NOTE: This function is not currently invoked by ConnectBlock(), so we | ||||
* should consider upgrade issues if we change which consensus rules are | * should consider upgrade issues if we change which consensus rules are | ||||
* enforced in this function (eg by adding a new consensus rule). See comment | * enforced in this function (eg by adding a new consensus rule). See comment | ||||
* in ConnectBlock(). | * in ConnectBlock(). | ||||
* Note that -reindex-chainstate skips the validation that happens here! | * Note that -reindex-chainstate skips the validation that happens here! | ||||
*/ | */ | ||||
static bool ContextualCheckBlock(const CBlock &block, CValidationState &state, | static bool ContextualCheckBlock(const CBlock &block, | ||||
BlockValidationState &state, | |||||
const Consensus::Params ¶ms, | const Consensus::Params ¶ms, | ||||
const CBlockIndex *pindexPrev) { | const CBlockIndex *pindexPrev) { | ||||
const int nHeight = pindexPrev == nullptr ? 0 : pindexPrev->nHeight + 1; | const int nHeight = pindexPrev == nullptr ? 0 : pindexPrev->nHeight + 1; | ||||
// Start enforcing BIP113 (Median Time Past). | // Start enforcing BIP113 (Median Time Past). | ||||
int nLockTimeFlags = 0; | int nLockTimeFlags = 0; | ||||
if (nHeight >= params.CSVHeight) { | if (nHeight >= params.CSVHeight) { | ||||
assert(pindexPrev != nullptr); | assert(pindexPrev != nullptr); | ||||
Show All 18 Lines | static bool ContextualCheckBlock(const CBlock &block, | ||||
// - perform a transaction-sigops check (again, a more strict check will | // - perform a transaction-sigops check (again, a more strict check will | ||||
// happen in ConnectBlock). | // happen in ConnectBlock). | ||||
const CTransaction *prevTx = nullptr; | const CTransaction *prevTx = nullptr; | ||||
for (const auto &ptx : block.vtx) { | for (const auto &ptx : block.vtx) { | ||||
const CTransaction &tx = *ptx; | const CTransaction &tx = *ptx; | ||||
if (fIsMagneticAnomalyEnabled) { | if (fIsMagneticAnomalyEnabled) { | ||||
if (prevTx && (tx.GetId() <= prevTx->GetId())) { | if (prevTx && (tx.GetId() <= prevTx->GetId())) { | ||||
if (tx.GetId() == prevTx->GetId()) { | if (tx.GetId() == prevTx->GetId()) { | ||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, | return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, | ||||
false, REJECT_INVALID, "tx-duplicate", | REJECT_INVALID, "tx-duplicate", | ||||
strprintf("Duplicated transaction %s", | strprintf("Duplicated transaction %s", | ||||
tx.GetId().ToString())); | tx.GetId().ToString())); | ||||
} | } | ||||
return state.Invalid( | return state.Invalid( | ||||
ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, | BlockValidationResult::BLOCK_CONSENSUS, REJECT_INVALID, | ||||
"tx-ordering", | "tx-ordering", | ||||
strprintf("Transaction order is invalid (%s < %s)", | strprintf("Transaction order is invalid (%s < %s)", | ||||
tx.GetId().ToString(), | tx.GetId().ToString(), | ||||
prevTx->GetId().ToString())); | prevTx->GetId().ToString())); | ||||
} | } | ||||
if (prevTx || !tx.IsCoinBase()) { | if (prevTx || !tx.IsCoinBase()) { | ||||
prevTx = &tx; | prevTx = &tx; | ||||
} | } | ||||
} | } | ||||
if (!ContextualCheckTransaction(params, tx, state, nHeight, | TxValidationState tx_state; | ||||
if (!ContextualCheckTransaction(params, tx, tx_state, nHeight, | |||||
nLockTimeCutoff, nMedianTimePast)) { | nLockTimeCutoff, nMedianTimePast)) { | ||||
// state set by ContextualCheckTransaction. | return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, | ||||
return false; | REJECT_INVALID, tx_state.GetRejectReason(), | ||||
tx_state.GetDebugMessage()); | |||||
} | } | ||||
} | } | ||||
// Enforce rule that the coinbase starts with serialized block height | // Enforce rule that the coinbase starts with serialized block height | ||||
if (nHeight >= params.BIP34Height) { | if (nHeight >= params.BIP34Height) { | ||||
CScript expect = CScript() << nHeight; | CScript expect = CScript() << nHeight; | ||||
if (block.vtx[0]->vin[0].scriptSig.size() < expect.size() || | if (block.vtx[0]->vin[0].scriptSig.size() < expect.size() || | ||||
!std::equal(expect.begin(), expect.end(), | !std::equal(expect.begin(), expect.end(), | ||||
block.vtx[0]->vin[0].scriptSig.begin())) { | block.vtx[0]->vin[0].scriptSig.begin())) { | ||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, | return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, | ||||
REJECT_INVALID, "bad-cb-height", | REJECT_INVALID, "bad-cb-height", | ||||
"block height mismatch in coinbase"); | "block height mismatch in coinbase"); | ||||
} | } | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
/** | /** | ||||
* If the provided block header is valid, add it to the block index. | * If the provided block header is valid, add it to the block index. | ||||
* | * | ||||
* Returns true if the block is successfully added to the block index. | * Returns true if the block is successfully added to the block index. | ||||
*/ | */ | ||||
bool CChainState::AcceptBlockHeader(const Config &config, | bool CChainState::AcceptBlockHeader(const Config &config, | ||||
const CBlockHeader &block, | const CBlockHeader &block, | ||||
CValidationState &state, | BlockValidationState &state, | ||||
CBlockIndex **ppindex) { | CBlockIndex **ppindex) { | ||||
AssertLockHeld(cs_main); | AssertLockHeld(cs_main); | ||||
const CChainParams &chainparams = config.GetChainParams(); | const CChainParams &chainparams = config.GetChainParams(); | ||||
// Check for duplicate | // Check for duplicate | ||||
BlockHash hash = block.GetHash(); | BlockHash hash = block.GetHash(); | ||||
BlockMap::iterator miSelf = mapBlockIndex.find(hash); | BlockMap::iterator miSelf = mapBlockIndex.find(hash); | ||||
CBlockIndex *pindex = nullptr; | CBlockIndex *pindex = nullptr; | ||||
if (hash != chainparams.GetConsensus().hashGenesisBlock) { | if (hash != chainparams.GetConsensus().hashGenesisBlock) { | ||||
if (miSelf != mapBlockIndex.end()) { | if (miSelf != mapBlockIndex.end()) { | ||||
// Block header is already known. | // Block header is already known. | ||||
pindex = miSelf->second; | pindex = miSelf->second; | ||||
if (ppindex) { | if (ppindex) { | ||||
*ppindex = pindex; | *ppindex = pindex; | ||||
} | } | ||||
if (pindex->nStatus.isInvalid()) { | if (pindex->nStatus.isInvalid()) { | ||||
return state.Invalid(ValidationInvalidReason::CACHED_INVALID, | LogPrintf("ERROR: %s: block %s is marked invalid\n", __func__, | ||||
error("%s: block %s is marked invalid", | hash.ToString()); | ||||
__func__, hash.ToString()), | return state.Invalid( | ||||
0, "duplicate"); | BlockValidationResult::BLOCK_CACHED_INVALID, 0, | ||||
"duplicate"); | |||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
if (!CheckBlockHeader(block, state, chainparams.GetConsensus(), | if (!CheckBlockHeader(block, state, chainparams.GetConsensus(), | ||||
BlockValidationOptions(config))) { | BlockValidationOptions(config))) { | ||||
return error("%s: Consensus::CheckBlockHeader: %s, %s", __func__, | return error("%s: Consensus::CheckBlockHeader: %s, %s", __func__, | ||||
hash.ToString(), FormatStateMessage(state)); | hash.ToString(), FormatStateMessage(state)); | ||||
} | } | ||||
// Get prev block index | // Get prev block index | ||||
BlockMap::iterator mi = mapBlockIndex.find(block.hashPrevBlock); | BlockMap::iterator mi = mapBlockIndex.find(block.hashPrevBlock); | ||||
if (mi == mapBlockIndex.end()) { | if (mi == mapBlockIndex.end()) { | ||||
return state.Invalid(ValidationInvalidReason::BLOCK_MISSING_PREV, | LogPrintf("ERROR: %s: prev block not found\n", __func__); | ||||
error("%s: prev block not found", __func__), 0, | return state.Invalid(BlockValidationResult::BLOCK_MISSING_PREV, 0, | ||||
"prev-blk-not-found"); | "prev-blk-not-found"); | ||||
} | } | ||||
CBlockIndex *pindexPrev = (*mi).second; | CBlockIndex *pindexPrev = (*mi).second; | ||||
assert(pindexPrev); | assert(pindexPrev); | ||||
if (pindexPrev->nStatus.isInvalid()) { | if (pindexPrev->nStatus.isInvalid()) { | ||||
return state.Invalid(ValidationInvalidReason::BLOCK_INVALID_PREV, | LogPrintf("ERROR: %s: prev block invalid\n", __func__); | ||||
error("%s: prev block invalid", __func__), | return state.Invalid(BlockValidationResult::BLOCK_INVALID_PREV, | ||||
REJECT_INVALID, "bad-prevblk"); | REJECT_INVALID, "bad-prevblk"); | ||||
} | } | ||||
if (!ContextualCheckBlockHeader(chainparams, block, state, pindexPrev, | if (!ContextualCheckBlockHeader(chainparams, block, state, pindexPrev, | ||||
GetAdjustedTime())) { | GetAdjustedTime())) { | ||||
return error("%s: Consensus::ContextualCheckBlockHeader: %s, %s", | return error("%s: Consensus::ContextualCheckBlockHeader: %s, %s", | ||||
__func__, hash.ToString(), FormatStateMessage(state)); | __func__, hash.ToString(), FormatStateMessage(state)); | ||||
} | } | ||||
Show All 28 Lines | if (hash != chainparams.GetConsensus().hashGenesisBlock) { | ||||
assert(failedit->nStatus.hasFailed()); | assert(failedit->nStatus.hasFailed()); | ||||
CBlockIndex *invalid_walk = pindexPrev; | CBlockIndex *invalid_walk = pindexPrev; | ||||
while (invalid_walk != failedit) { | while (invalid_walk != failedit) { | ||||
invalid_walk->nStatus = | invalid_walk->nStatus = | ||||
invalid_walk->nStatus.withFailedParent(); | invalid_walk->nStatus.withFailedParent(); | ||||
setDirtyBlockIndex.insert(invalid_walk); | setDirtyBlockIndex.insert(invalid_walk); | ||||
invalid_walk = invalid_walk->pprev; | invalid_walk = invalid_walk->pprev; | ||||
} | } | ||||
LogPrintf("ERROR: %s: prev block invalid\n", __func__); | |||||
return state.Invalid( | return state.Invalid( | ||||
ValidationInvalidReason::BLOCK_INVALID_PREV, | BlockValidationResult::BLOCK_INVALID_PREV, | ||||
error("%s: prev block invalid", __func__), | |||||
REJECT_INVALID, "bad-prevblk"); | REJECT_INVALID, "bad-prevblk"); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
if (pindex == nullptr) { | if (pindex == nullptr) { | ||||
pindex = AddToBlockIndex(block); | pindex = AddToBlockIndex(block); | ||||
} | } | ||||
if (ppindex) { | if (ppindex) { | ||||
*ppindex = pindex; | *ppindex = pindex; | ||||
} | } | ||||
CheckBlockIndex(chainparams.GetConsensus()); | CheckBlockIndex(chainparams.GetConsensus()); | ||||
return true; | return true; | ||||
} | } | ||||
// Exposed wrapper for AcceptBlockHeader | // Exposed wrapper for AcceptBlockHeader | ||||
bool ProcessNewBlockHeaders(const Config &config, | bool ProcessNewBlockHeaders(const Config &config, | ||||
const std::vector<CBlockHeader> &headers, | const std::vector<CBlockHeader> &headers, | ||||
CValidationState &state, | BlockValidationState &state, | ||||
const CBlockIndex **ppindex, | const CBlockIndex **ppindex) { | ||||
CBlockHeader *first_invalid) { | |||||
if (first_invalid != nullptr) { | |||||
first_invalid->SetNull(); | |||||
} | |||||
{ | { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
for (const CBlockHeader &header : headers) { | for (const CBlockHeader &header : headers) { | ||||
// Use a temp pindex instead of ppindex to avoid a const_cast | // Use a temp pindex instead of ppindex to avoid a const_cast | ||||
CBlockIndex *pindex = nullptr; | CBlockIndex *pindex = nullptr; | ||||
if (!::ChainstateActive().AcceptBlockHeader(config, header, state, | if (!::ChainstateActive().AcceptBlockHeader(config, header, state, | ||||
&pindex)) { | &pindex)) { | ||||
if (first_invalid) { | |||||
*first_invalid = header; | |||||
} | |||||
return false; | return false; | ||||
} | } | ||||
if (ppindex) { | if (ppindex) { | ||||
*ppindex = pindex; | *ppindex = pindex; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
Show All 36 Lines | |||||
* @param[in] fRequested A boolean to indicate if this block was requested | * @param[in] fRequested A boolean to indicate if this block was requested | ||||
* from our peers. | * from our peers. | ||||
* @param[in] dbp If non-null, the disk position of the block. | * @param[in] dbp If non-null, the disk position of the block. | ||||
* @param[in-out] fNewBlock True if block was first received via this call. | * @param[in-out] fNewBlock True if block was first received via this call. | ||||
* @return True if the block is accepted as a valid block and written to disk. | * @return True if the block is accepted as a valid block and written to disk. | ||||
*/ | */ | ||||
bool CChainState::AcceptBlock(const Config &config, | bool CChainState::AcceptBlock(const Config &config, | ||||
const std::shared_ptr<const CBlock> &pblock, | const std::shared_ptr<const CBlock> &pblock, | ||||
CValidationState &state, bool fRequested, | BlockValidationState &state, bool fRequested, | ||||
const FlatFilePos *dbp, bool *fNewBlock) { | const FlatFilePos *dbp, bool *fNewBlock) { | ||||
AssertLockHeld(cs_main); | AssertLockHeld(cs_main); | ||||
const CBlock &block = *pblock; | const CBlock &block = *pblock; | ||||
if (fNewBlock) { | if (fNewBlock) { | ||||
*fNewBlock = false; | *fNewBlock = false; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 76 Lines • ▼ Show 20 Lines | bool CChainState::AcceptBlock(const Config &config, | ||||
} | } | ||||
const CChainParams &chainparams = config.GetChainParams(); | const CChainParams &chainparams = config.GetChainParams(); | ||||
const Consensus::Params &consensusParams = chainparams.GetConsensus(); | const Consensus::Params &consensusParams = chainparams.GetConsensus(); | ||||
if (!CheckBlock(block, state, consensusParams, | if (!CheckBlock(block, state, consensusParams, | ||||
BlockValidationOptions(config)) || | BlockValidationOptions(config)) || | ||||
!ContextualCheckBlock(block, state, consensusParams, pindex->pprev)) { | !ContextualCheckBlock(block, state, consensusParams, pindex->pprev)) { | ||||
assert(IsBlockReason(state.GetReason())); | |||||
if (state.IsInvalid() && | if (state.IsInvalid() && | ||||
state.GetReason() != ValidationInvalidReason::BLOCK_MUTATED) { | state.GetResult() != BlockValidationResult::BLOCK_MUTATED) { | ||||
pindex->nStatus = pindex->nStatus.withFailed(); | pindex->nStatus = pindex->nStatus.withFailed(); | ||||
setDirtyBlockIndex.insert(pindex); | setDirtyBlockIndex.insert(pindex); | ||||
} | } | ||||
return error("%s: %s (block %s)", __func__, FormatStateMessage(state), | return error("%s: %s (block %s)", __func__, FormatStateMessage(state), | ||||
block.GetHash().ToString()); | block.GetHash().ToString()); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 50 Lines • ▼ Show 20 Lines | bool ProcessNewBlock(const Config &config, | ||||
bool fForceProcessing, bool *fNewBlock) { | bool fForceProcessing, bool *fNewBlock) { | ||||
AssertLockNotHeld(cs_main); | AssertLockNotHeld(cs_main); | ||||
{ | { | ||||
if (fNewBlock) { | if (fNewBlock) { | ||||
*fNewBlock = false; | *fNewBlock = false; | ||||
} | } | ||||
CValidationState state; | BlockValidationState state; | ||||
// CheckBlock() does not support multi-threaded block validation | // CheckBlock() does not support multi-threaded block validation | ||||
// because CBlock::fChecked can cause data race. | // because CBlock::fChecked can cause data race. | ||||
// Therefore, the following critical section must include the | // Therefore, the following critical section must include the | ||||
// CheckBlock() call as well. | // CheckBlock() call as well. | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
// Ensure that CheckBlock() passes before calling AcceptBlock, as | // Ensure that CheckBlock() passes before calling AcceptBlock, as | ||||
Show All 12 Lines | AssertLockNotHeld(cs_main); | ||||
return error("%s: AcceptBlock FAILED (%s)", __func__, | return error("%s: AcceptBlock FAILED (%s)", __func__, | ||||
FormatStateMessage(state)); | FormatStateMessage(state)); | ||||
} | } | ||||
} | } | ||||
NotifyHeaderTip(); | NotifyHeaderTip(); | ||||
// Only used to report errors, not invalidity - ignore it | // Only used to report errors, not invalidity - ignore it | ||||
CValidationState state; | BlockValidationState state; | ||||
if (!::ChainstateActive().ActivateBestChain(config, state, pblock)) { | if (!::ChainstateActive().ActivateBestChain(config, state, pblock)) { | ||||
return error("%s: ActivateBestChain failed (%s)", __func__, | return error("%s: ActivateBestChain failed (%s)", __func__, | ||||
FormatStateMessage(state)); | FormatStateMessage(state)); | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
bool TestBlockValidity(CValidationState &state, const CChainParams ¶ms, | bool TestBlockValidity(BlockValidationState &state, const CChainParams ¶ms, | ||||
const CBlock &block, CBlockIndex *pindexPrev, | const CBlock &block, CBlockIndex *pindexPrev, | ||||
BlockValidationOptions validationOptions) { | BlockValidationOptions validationOptions) { | ||||
AssertLockHeld(cs_main); | AssertLockHeld(cs_main); | ||||
assert(pindexPrev && pindexPrev == ::ChainActive().Tip()); | assert(pindexPrev && pindexPrev == ::ChainActive().Tip()); | ||||
CCoinsViewCache viewNew(pcoinsTip.get()); | CCoinsViewCache viewNew(pcoinsTip.get()); | ||||
BlockHash block_hash(block.GetHash()); | BlockHash block_hash(block.GetHash()); | ||||
CBlockIndex indexDummy(block); | CBlockIndex indexDummy(block); | ||||
indexDummy.pprev = pindexPrev; | indexDummy.pprev = pindexPrev; | ||||
▲ Show 20 Lines • Show All 120 Lines • ▼ Show 20 Lines | for (int fileNumber = 0; fileNumber < nLastBlockFile; fileNumber++) { | ||||
count++; | count++; | ||||
} | } | ||||
LogPrintf("Prune (Manual): prune_height=%d removed %d blk/rev pairs\n", | LogPrintf("Prune (Manual): prune_height=%d removed %d blk/rev pairs\n", | ||||
nLastBlockWeCanPrune, count); | nLastBlockWeCanPrune, count); | ||||
} | } | ||||
/* This function is called from the RPC code for pruneblockchain */ | /* This function is called from the RPC code for pruneblockchain */ | ||||
void PruneBlockFilesManual(int nManualPruneHeight) { | void PruneBlockFilesManual(int nManualPruneHeight) { | ||||
CValidationState state; | BlockValidationState state; | ||||
const CChainParams &chainparams = Params(); | const CChainParams &chainparams = Params(); | ||||
if (!::ChainstateActive().FlushStateToDisk( | if (!::ChainstateActive().FlushStateToDisk( | ||||
chainparams, state, FlushStateMode::NONE, nManualPruneHeight)) { | chainparams, state, FlushStateMode::NONE, nManualPruneHeight)) { | ||||
LogPrintf("%s: failed to flush state (%s)\n", __func__, | LogPrintf("%s: failed to flush state (%s)\n", __func__, | ||||
FormatStateMessage(state)); | FormatStateMessage(state)); | ||||
} | } | ||||
} | } | ||||
▲ Show 20 Lines • Show All 316 Lines • ▼ Show 20 Lines | bool CVerifyDB::VerifyDB(const Config &config, CCoinsView *coinsview, | ||||
nCheckLevel = std::max(0, std::min(4, nCheckLevel)); | nCheckLevel = std::max(0, std::min(4, nCheckLevel)); | ||||
LogPrintf("Verifying last %i blocks at level %i\n", nCheckDepth, | LogPrintf("Verifying last %i blocks at level %i\n", nCheckDepth, | ||||
nCheckLevel); | nCheckLevel); | ||||
CCoinsViewCache coins(coinsview); | CCoinsViewCache coins(coinsview); | ||||
CBlockIndex *pindex; | CBlockIndex *pindex; | ||||
CBlockIndex *pindexFailure = nullptr; | CBlockIndex *pindexFailure = nullptr; | ||||
int nGoodTransactions = 0; | int nGoodTransactions = 0; | ||||
CValidationState state; | BlockValidationState state; | ||||
int reportDone = 0; | int reportDone = 0; | ||||
LogPrintfToBeContinued("[0%%]..."); | LogPrintfToBeContinued("[0%%]..."); | ||||
for (pindex = ::ChainActive().Tip(); pindex && pindex->pprev; | for (pindex = ::ChainActive().Tip(); pindex && pindex->pprev; | ||||
pindex = pindex->pprev) { | pindex = pindex->pprev) { | ||||
boost::this_thread::interruption_point(); | boost::this_thread::interruption_point(); | ||||
int percentageDone = | int percentageDone = | ||||
std::max(1, std::min(99, (int)(((double)(::ChainActive().Height() - | std::max(1, std::min(99, (int)(((double)(::ChainActive().Height() - | ||||
pindex->nHeight)) / | pindex->nHeight)) / | ||||
▲ Show 20 Lines • Show All 429 Lines • ▼ Show 20 Lines | try { | ||||
std::make_pair(block.hashPrevBlock, *dbp)); | std::make_pair(block.hashPrevBlock, *dbp)); | ||||
} | } | ||||
continue; | continue; | ||||
} | } | ||||
// process in case the block isn't known yet | // process in case the block isn't known yet | ||||
CBlockIndex *pindex = LookupBlockIndex(hash); | CBlockIndex *pindex = LookupBlockIndex(hash); | ||||
if (!pindex || !pindex->nStatus.hasData()) { | if (!pindex || !pindex->nStatus.hasData()) { | ||||
CValidationState state; | BlockValidationState state; | ||||
if (::ChainstateActive().AcceptBlock( | if (::ChainstateActive().AcceptBlock( | ||||
config, pblock, state, true, dbp, nullptr)) { | config, pblock, state, true, dbp, nullptr)) { | ||||
nLoaded++; | nLoaded++; | ||||
} | } | ||||
if (state.IsError()) { | if (state.IsError()) { | ||||
break; | break; | ||||
} | } | ||||
} else if (hash != chainparams.GetConsensus() | } else if (hash != chainparams.GetConsensus() | ||||
.hashGenesisBlock && | .hashGenesisBlock && | ||||
pindex->nHeight % 1000 == 0) { | pindex->nHeight % 1000 == 0) { | ||||
LogPrint( | LogPrint( | ||||
BCLog::REINDEX, | BCLog::REINDEX, | ||||
"Block Import: already had block %s at height %d\n", | "Block Import: already had block %s at height %d\n", | ||||
hash.ToString(), pindex->nHeight); | hash.ToString(), pindex->nHeight); | ||||
} | } | ||||
} | } | ||||
// Activate the genesis block so normal node progress can | // Activate the genesis block so normal node progress can | ||||
// continue | // continue | ||||
if (hash == chainparams.GetConsensus().hashGenesisBlock) { | if (hash == chainparams.GetConsensus().hashGenesisBlock) { | ||||
CValidationState state; | BlockValidationState state; | ||||
if (!ActivateBestChain(config, state)) { | if (!ActivateBestChain(config, state)) { | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
NotifyHeaderTip(); | NotifyHeaderTip(); | ||||
// Recursively process earlier encountered successors of this | // Recursively process earlier encountered successors of this | ||||
Show All 14 Lines | try { | ||||
if (ReadBlockFromDisk(*pblockrecursive, it->second, | if (ReadBlockFromDisk(*pblockrecursive, it->second, | ||||
chainparams.GetConsensus())) { | chainparams.GetConsensus())) { | ||||
LogPrint( | LogPrint( | ||||
BCLog::REINDEX, | BCLog::REINDEX, | ||||
"%s: Processing out of order child %s of %s\n", | "%s: Processing out of order child %s of %s\n", | ||||
__func__, pblockrecursive->GetHash().ToString(), | __func__, pblockrecursive->GetHash().ToString(), | ||||
head.ToString()); | head.ToString()); | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
CValidationState dummy; | BlockValidationState dummy; | ||||
if (::ChainstateActive().AcceptBlock( | if (::ChainstateActive().AcceptBlock( | ||||
config, pblockrecursive, dummy, true, | config, pblockrecursive, dummy, true, | ||||
&it->second, nullptr)) { | &it->second, nullptr)) { | ||||
nLoaded++; | nLoaded++; | ||||
queue.push_back(pblockrecursive->GetHash()); | queue.push_back(pblockrecursive->GetHash()); | ||||
} | } | ||||
} | } | ||||
range.first++; | range.first++; | ||||
▲ Show 20 Lines • Show All 417 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); | ||||
} | } | ||||
CValidationState state; | TxValidationState state; | ||||
if (nTime + nExpiryTimeout > nNow) { | if (nTime + nExpiryTimeout > nNow) { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
AcceptToMemoryPoolWithTime( | AcceptToMemoryPoolWithTime( | ||||
config, pool, state, tx, nullptr /* pfMissingInputs */, | config, pool, state, tx, nTime, false /* bypass_limits */, | ||||
nTime, false /* bypass_limits */, | |||||
Amount::zero() /* nAbsurdFee */, false /* test_accept */); | Amount::zero() /* nAbsurdFee */, false /* test_accept */); | ||||
if (state.IsValid()) { | 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 | ||||
// 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' | ||||
▲ Show 20 Lines • Show All 130 Lines • Show Last 20 Lines |