Changeset View
Changeset View
Standalone View
Standalone View
src/validation.cpp
Show First 20 Lines • Show All 357 Lines • ▼ Show 20 Lines | AcceptToMemoryPoolWorker(const Config &config, CTxMemPool &pool, | ||||
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.DoS(0, false, REJECT_NONSTANDARD, reason); | return state.DoS(0, ValidationInvalidReason::TX_NOT_STANDARD, false, | ||||
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; | CValidationState 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.DoS( | return state.DoS(0, ValidationInvalidReason::TX_NOT_STANDARD, false, | ||||
0, false, REJECT_NONSTANDARD, ctxState.GetRejectReason(), | REJECT_NONSTANDARD, ctxState.GetRejectReason(), | ||||
ctxState.CorruptionPossible(), ctxState.GetDebugMessage()); | ctxState.CorruptionPossible(), | ||||
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(false, REJECT_DUPLICATE, "txn-already-in-mempool"); | return state.Invalid(ValidationInvalidReason::TX_CONFLICT, false, | ||||
REJECT_DUPLICATE, "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(false, REJECT_DUPLICATE, | return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, | ||||
false, REJECT_DUPLICATE, | |||||
"txn-mempool-conflict"); | "txn-mempool-conflict"); | ||||
} | } | ||||
} | } | ||||
{ | { | ||||
CCoinsView dummy; | CCoinsView dummy; | ||||
CCoinsViewCache view(&dummy); | CCoinsViewCache view(&dummy); | ||||
LockPoints lp; | LockPoints lp; | ||||
CCoinsViewMemPool viewMemPool(pcoinsTip.get(), pool); | CCoinsViewMemPool viewMemPool(pcoinsTip.get(), pool); | ||||
view.SetBackend(viewMemPool); | view.SetBackend(viewMemPool); | ||||
// Do all inputs exist? | // Do all inputs exist? | ||||
for (const CTxIn &txin : tx.vin) { | for (const CTxIn &txin : tx.vin) { | ||||
if (!pcoinsTip->HaveCoinInCache(txin.prevout)) { | if (!pcoinsTip->HaveCoinInCache(txin.prevout)) { | ||||
coins_to_uncache.push_back(txin.prevout); | coins_to_uncache.push_back(txin.prevout); | ||||
} | } | ||||
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(false, REJECT_DUPLICATE, | return state.Invalid( | ||||
"txn-already-known"); | ValidationInvalidReason::TX_CONFLICT, false, | ||||
REJECT_DUPLICATE, "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) { | if (pfMissingInputs) { | ||||
*pfMissingInputs = true; | *pfMissingInputs = true; | ||||
} | } | ||||
// fMissingInputs and !state.IsInvalid() is used to detect this | // fMissingInputs and !state.IsInvalid() is used to detect this | ||||
// condition, don't set state.Invalid() | // condition, don't set state.Invalid() | ||||
return false; | return false; | ||||
} | } | ||||
} | } | ||||
// Are the actual inputs available? | // Are the actual inputs available? | ||||
if (!view.HaveInputs(tx)) { | if (!view.HaveInputs(tx)) { | ||||
return state.Invalid(false, REJECT_DUPLICATE, | return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, | ||||
false, 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.DoS(0, false, REJECT_NONSTANDARD, "non-BIP68-final"); | return state.DoS(0, ValidationInvalidReason::TX_NOT_STANDARD, false, | ||||
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(false, REJECT_NONSTANDARD, | return state.Invalid(ValidationInvalidReason::TX_NOT_STANDARD, | ||||
false, 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.DoS(0, false, REJECT_INSUFFICIENTFEE, | return state.DoS(0, ValidationInvalidReason::TX_MEMPOOL_POLICY, | ||||
false, 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(false, REJECT_HIGHFEE, "absurdly-high-fee", | return state.Invalid(ValidationInvalidReason::TX_NOT_STANDARD, | ||||
false, 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; | ||||
Show All 11 Lines | for (const CTxIn &txin : tx.vin) { | ||||
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.DoS( | return state.DoS( | ||||
0, false, REJECT_INSUFFICIENTFEE, "mempool min fee not met", | 0, ValidationInvalidReason::TX_MEMPOOL_POLICY, false, | ||||
false, strprintf("%d < %d", nModifiedFees, mempoolRejectFee)); | REJECT_INSUFFICIENTFEE, "mempool min fee not met", false, | ||||
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.DoS(0, false, REJECT_NONSTANDARD, | return state.DoS(0, ValidationInvalidReason::TX_MEMPOOL_POLICY, | ||||
false, REJECT_NONSTANDARD, | |||||
"too-long-mempool-chain", false, errString); | "too-long-mempool-chain", false, 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 | ||||
Show All 35 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.DoS(0, false, REJECT_INSUFFICIENTFEE, | return state.DoS(0, ValidationInvalidReason::TX_MEMPOOL_POLICY, | ||||
"mempool full"); | false, REJECT_INSUFFICIENTFEE, "mempool full"); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
GetMainSignals().TransactionAddedToMempool(ptx); | GetMainSignals().TransactionAddedToMempool(ptx); | ||||
return true; | return true; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 421 Lines • ▼ Show 20 Lines | bool CheckInputs(const CTransaction &tx, CValidationState &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.DoS(100, false, REJECT_INVALID, "too-many-sigchecks"); | return state.DoS(100, ValidationInvalidReason::CONSENSUS, false, | ||||
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++) { | ||||
const COutPoint &prevout = tx.vin[i].prevout; | const COutPoint &prevout = tx.vin[i].prevout; | ||||
Show All 25 Lines | for (size_t i = 0; i < tx.vin.size(); i++) { | ||||
// Check whether the failure was caused by a non-mandatory | // Check whether the failure was caused by a non-mandatory | ||||
// script verification check. If so, don't trigger DoS | // script verification check. If so, don't trigger DoS | ||||
// protection to avoid splitting the network on the basis of | // protection to avoid splitting the network on the basis of | ||||
// relay policy disagreements. | // relay policy disagreements. | ||||
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( | ||||
false, REJECT_NONSTANDARD, | ValidationInvalidReason::TX_NOT_STANDARD, false, | ||||
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(); | ||||
} | } | ||||
// Failures of other flags indicate a transaction that is invalid in | // Failures of other flags indicate a transaction that is invalid in | ||||
// new blocks, e.g. a invalid P2SH. We DoS ban such nodes as they | // new blocks, e.g. a invalid P2SH. We DoS ban such nodes as they | ||||
// are not following the protocol. That said during an upgrade | // are not following the protocol. That said during an upgrade | ||||
// careful thought should be taken as to the correct behavior - we | // careful thought should be taken as to the correct behavior - we | ||||
// may want to continue peering with non-upgraded nodes even after | // may want to continue peering with non-upgraded nodes even after | ||||
// soft-fork super-majority signaling has occurred. | // soft-fork super-majority signaling has occurred. | ||||
return state.DoS( | return state.DoS( | ||||
100, false, REJECT_INVALID, | 100, ValidationInvalidReason::CONSENSUS, false, 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 498 Lines • ▼ Show 20 Lines | 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.DoS( | return state.DoS( | ||||
100, | 100, ValidationInvalidReason::CONSENSUS, | ||||
error("ConnectBlock(): tried to overwrite transaction"), | error("ConnectBlock(): tried to overwrite transaction"), | ||||
REJECT_INVALID, "bad-txns-BIP30"); | REJECT_INVALID, "bad-txns-BIP30"); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
// Start enforcing BIP68 (sequence locks). | // Start enforcing BIP68 (sequence locks). | ||||
▲ Show 20 Lines • Show All 42 Lines • ▼ Show 20 Lines | try { | ||||
// 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.DoS( | return state.DoS( | ||||
100, error("ConnectBlock(): tried to overwrite transaction"), | 100, ValidationInvalidReason::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, | if (!isCoinBase && !Consensus::CheckTxInputs(tx, state, view, | ||||
pindex->nHeight, txfee)) { | pindex->nHeight, txfee)) { | ||||
if (state.GetReason() == | |||||
ValidationInvalidReason::TX_MISSING_INPUTS) { | |||||
// CheckTxInputs may return MISSING_INPUTS but we can't return | |||||
// that, as it's not defined for a block, so we reset the reason | |||||
// flag to CONSENSUS here. | |||||
state.DoS(100, ValidationInvalidReason::CONSENSUS, false, | |||||
state.GetRejectCode(), state.GetRejectReason(), | |||||
state.CorruptionPossible(), 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.DoS( | return state.DoS( | ||||
100, | 100, ValidationInvalidReason::CONSENSUS, | ||||
error("%s: accumulated fee in the block out of range.", | error("%s: accumulated fee in the block out of range.", | ||||
__func__), | __func__), | ||||
REJECT_INVALID, "bad-txns-accumulated-fee-outofrange"); | REJECT_INVALID, "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.DoS( | return state.DoS( | ||||
100, | 100, ValidationInvalidReason::CONSENSUS, | ||||
error("%s: contains a non-BIP68-final transaction", __func__), | error("%s: contains a non-BIP68-final transaction", __func__), | ||||
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, | if (!CheckInputs(tx, state, view, fScriptChecks, flags, fCacheResults, | ||||
fCacheResults, PrecomputedTransactionData(tx), | fCacheResults, PrecomputedTransactionData(tx), | ||||
nSigChecksRet, nSigChecksTxLimiters[txIndex], | nSigChecksRet, nSigChecksTxLimiters[txIndex], | ||||
&nSigChecksBlockLimiter, &vChecks)) { | &nSigChecksBlockLimiter, &vChecks)) { | ||||
// Parallel CheckInputs shouldn't fail except for this reason, which | if (state.GetReason() == ValidationInvalidReason::TX_NOT_STANDARD) { | ||||
// is banworthy. Use "blk-bad-inputs" to mimic the parallel script | // CheckInputs may return NOT_STANDARD for extra flags we | ||||
// check error. | // passed, but we can't return that, as it's not defined for a | ||||
if (!nSigChecksBlockLimiter.check()) { | // block, so we reset the reason flag to CONSENSUS here. In the | ||||
return state.DoS(100, false, REJECT_INVALID, "blk-bad-inputs", | // event of a future soft-fork, we may need to consider whether | ||||
false, "CheckInputs exceeded SigChecks limit"); | // rewriting to CONSENSUS or RECENT_CONSENSUS_CHANGE would be | ||||
// more appropriate. | |||||
state.DoS(100 - state.GetDoS(), | |||||
ValidationInvalidReason::CONSENSUS, false, | |||||
state.GetRejectCode(), state.GetRejectReason(), | |||||
state.CorruptionPossible(), 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 | ||||
Show All 13 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.DoS(100, | return state.DoS(100, ValidationInvalidReason::CONSENSUS, | ||||
error("ConnectBlock(): coinbase pays too much " | error("ConnectBlock(): coinbase pays too much " | ||||
"(actual=%d vs limit=%d)", | "(actual=%d vs limit=%d)", | ||||
block.vtx[0]->GetValueOut(), blockReward), | block.vtx[0]->GetValueOut(), blockReward), | ||||
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); | ||||
Show All 14 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.DoS(100, false, REJECT_INVALID, "bad-cb-minerfund"); | return state.DoS(100, ValidationInvalidReason::CONSENSUS, false, | ||||
REJECT_INVALID, "bad-cb-minerfund"); | |||||
} | } | ||||
MinerFundSuccess: | MinerFundSuccess: | ||||
if (!control.Wait()) { | if (!control.Wait()) { | ||||
return state.DoS(100, false, REJECT_INVALID, "blk-bad-inputs", false, | return state.DoS(100, ValidationInvalidReason::CONSENSUS, false, | ||||
REJECT_INVALID, "blk-bad-inputs", false, | |||||
"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, | ||||
" - Verify %u txins: %.2fms (%.3fms/txin) [%.2fs (%.2fms/blk)]\n", | " - Verify %u txins: %.2fms (%.3fms/txin) [%.2fs (%.2fms/blk)]\n", | ||||
▲ Show 20 Lines • Show All 380 Lines • ▼ Show 20 Lines | |||||
}; | }; | ||||
static bool FinalizeBlockInternal(const Config &config, CValidationState &state, | static bool FinalizeBlockInternal(const Config &config, CValidationState &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.DoS(100, | return state.Invalid(ValidationInvalidReason::CACHED_INVALID, | ||||
error("%s: Trying to finalize invalid block %s", | error("%s: Trying to finalize invalid block %s", | ||||
__func__, pindex->GetBlockHash().ToString()), | __func__, pindex->GetBlockHash().ToString()), | ||||
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.DoS( | return state.DoS( | ||||
20, | 20, ValidationInvalidReason::BLOCK_FINALIZATION, | ||||
error("%s: Trying to finalize block %s which conflicts " | error("%s: Trying to finalize block %s which conflicts " | ||||
"with already finalized block", | "with already finalized block", | ||||
__func__, pindex->GetBlockHash().ToString()), | __func__, pindex->GetBlockHash().ToString()), | ||||
REJECT_AGAINST_FINALIZED, "bad-fork-prior-finalized"); | REJECT_AGAINST_FINALIZED, "bad-fork-prior-finalized"); | ||||
} | } | ||||
if (IsBlockFinalized(pindex)) { | if (IsBlockFinalized(pindex)) { | ||||
// The block is already finalized. | // The block is already finalized. | ||||
▲ Show 20 Lines • Show All 1,154 Lines • ▼ Show 20 Lines | |||||
* 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, CValidationState &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.DoS(100, false, REJECT_INVALID, "high-hash", false, | return state.DoS(100, ValidationInvalidReason::BLOCK_INVALID_HEADER, | ||||
false, REJECT_INVALID, "high-hash", false, | |||||
"proof of work failed"); | "proof of work failed"); | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
bool CheckBlock(const CBlock &block, CValidationState &state, | bool CheckBlock(const CBlock &block, CValidationState &state, | ||||
const Consensus::Params ¶ms, | const Consensus::Params ¶ms, | ||||
Show All 9 Lines | 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.DoS(100, false, REJECT_INVALID, "bad-txnmrklroot", | return state.DoS(100, ValidationInvalidReason::BLOCK_MUTATED, false, | ||||
true, "hashMerkleRoot mismatch"); | REJECT_INVALID, "bad-txnmrklroot", true, | ||||
"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.DoS(100, false, REJECT_INVALID, "bad-txns-duplicate", | return state.DoS(100, ValidationInvalidReason::BLOCK_MUTATED, false, | ||||
true, "duplicate transaction"); | REJECT_INVALID, "bad-txns-duplicate", true, | ||||
"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.DoS(100, false, REJECT_INVALID, "bad-cb-missing", false, | return state.DoS(100, ValidationInvalidReason::CONSENSUS, false, | ||||
REJECT_INVALID, "bad-cb-missing", false, | |||||
"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.DoS(100, false, REJECT_INVALID, "bad-blk-length", false, | return state.DoS(100, ValidationInvalidReason::CONSENSUS, false, | ||||
REJECT_INVALID, "bad-blk-length", false, | |||||
"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.DoS(100, false, REJECT_INVALID, "bad-blk-length", false, | return state.DoS(100, ValidationInvalidReason::CONSENSUS, false, | ||||
REJECT_INVALID, "bad-blk-length", false, | |||||
"size limits failed"); | "size limits failed"); | ||||
} | } | ||||
// And a valid coinbase. | // And a valid coinbase. | ||||
if (!CheckCoinbase(*block.vtx[0], state)) { | if (!CheckCoinbase(*block.vtx[0], state)) { | ||||
return state.Invalid(false, state.GetRejectCode(), | return state.Invalid(ValidationInvalidReason::CONSENSUS, false, | ||||
state.GetRejectReason(), | state.GetRejectCode(), 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())); | 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, state)) { | ||||
return state.Invalid( | return state.Invalid( | ||||
false, state.GetRejectCode(), state.GetRejectReason(), | ValidationInvalidReason::CONSENSUS, false, | ||||
state.GetRejectCode(), state.GetRejectReason(), | |||||
strprintf("Transaction check failed (txid %s) %s", | strprintf("Transaction check failed (txid %s) %s", | ||||
tx->GetId().ToString(), state.GetDebugMessage())); | tx->GetId().ToString(), state.GetDebugMessage())); | ||||
} | } | ||||
} | } | ||||
if (validationOptions.shouldValidatePoW() && | if (validationOptions.shouldValidatePoW() && | ||||
validationOptions.shouldValidateMerkleRoot()) { | validationOptions.shouldValidateMerkleRoot()) { | ||||
block.fChecked = true; | block.fChecked = true; | ||||
Show All 20 Lines | ContextualCheckBlockHeader(const CChainParams ¶ms, | ||||
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.DoS(100, false, REJECT_INVALID, "bad-diffbits", false, | return state.DoS(100, ValidationInvalidReason::BLOCK_INVALID_HEADER, | ||||
false, REJECT_INVALID, "bad-diffbits", false, | |||||
"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.DoS(100, | return state.DoS(100, ValidationInvalidReason::BLOCK_CHECKPOINT, | ||||
error("%s: rejected by checkpoint lock-in at %d", | error("%s: rejected by checkpoint lock-in at %d", | ||||
__func__, nHeight), | __func__, 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.DoS( | return state.DoS( | ||||
100, | 100, ValidationInvalidReason::BLOCK_CHECKPOINT, | ||||
error("%s: forked chain older than last checkpoint (height %d)", | error("%s: forked chain older than last checkpoint (height %d)", | ||||
__func__, nHeight), | __func__, nHeight), | ||||
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.DoS(100, false, REJECT_INVALID, "time-too-old", | return state.DoS(100, ValidationInvalidReason::BLOCK_INVALID_HEADER, | ||||
false, REJECT_INVALID, "time-too-old", false, | |||||
"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(false, REJECT_INVALID, "time-too-new", | return state.Invalid(ValidationInvalidReason::BLOCK_TIME_FUTURE, false, | ||||
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.DoS( | return state.DoS( | ||||
100, false, REJECT_OBSOLETE, | 100, ValidationInvalidReason::BLOCK_INVALID_HEADER, false, | ||||
strprintf("bad-version(0x%08x)", block.nVersion), false, | REJECT_OBSOLETE, strprintf("bad-version(0x%08x)", block.nVersion), | ||||
strprintf("rejected nVersion=0x%08x block", block.nVersion)); | false, 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, | CValidationState &state, | ||||
▲ Show 20 Lines • Show All 69 Lines • ▼ Show 20 Lines | static bool ContextualCheckBlock(const CBlock &block, CValidationState &state, | ||||
// - 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.DoS(100, false, REJECT_INVALID, "tx-duplicate", | return state.DoS(100, ValidationInvalidReason::CONSENSUS, | ||||
false, REJECT_INVALID, "tx-duplicate", | |||||
false, | false, | ||||
strprintf("Duplicated transaction %s", | strprintf("Duplicated transaction %s", | ||||
tx.GetId().ToString())); | tx.GetId().ToString())); | ||||
} | } | ||||
return state.DoS( | return state.DoS( | ||||
100, false, REJECT_INVALID, "tx-ordering", false, | 100, ValidationInvalidReason::CONSENSUS, false, | ||||
REJECT_INVALID, "tx-ordering", false, | |||||
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, | if (!ContextualCheckTransaction(params, tx, state, nHeight, | ||||
nLockTimeCutoff, nMedianTimePast)) { | nLockTimeCutoff, nMedianTimePast)) { | ||||
// state set by ContextualCheckTransaction. | // state set by ContextualCheckTransaction. | ||||
return false; | return false; | ||||
} | } | ||||
} | } | ||||
// 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.DoS(100, false, REJECT_INVALID, "bad-cb-height", false, | return state.DoS(100, ValidationInvalidReason::CONSENSUS, false, | ||||
REJECT_INVALID, "bad-cb-height", false, | |||||
"block height mismatch in coinbase"); | "block height mismatch in coinbase"); | ||||
} | } | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
/** | /** | ||||
Show All 16 Lines | 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(error("%s: block %s is marked invalid", | return state.Invalid(ValidationInvalidReason::CACHED_INVALID, | ||||
error("%s: block %s is marked invalid", | |||||
__func__, hash.ToString()), | __func__, hash.ToString()), | ||||
0, "duplicate"); | 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.DoS(10, error("%s: prev block not found", __func__), 0, | return state.DoS(10, ValidationInvalidReason::BLOCK_MISSING_PREV, | ||||
error("%s: prev block not found", __func__), 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.DoS(100, error("%s: prev block invalid", __func__), | return state.DoS(100, ValidationInvalidReason::BLOCK_INVALID_PREV, | ||||
error("%s: prev block invalid", __func__), | |||||
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; | ||||
} | } | ||||
return state.DoS(100, | return state.DoS( | ||||
100, ValidationInvalidReason::BLOCK_INVALID_PREV, | |||||
error("%s: prev block invalid", __func__), | 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); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 1,821 Lines • Show Last 20 Lines |