Changeset View
Changeset View
Standalone View
Standalone View
src/validation.cpp
Show First 20 Lines • Show All 355 Lines • ▼ Show 20 Lines | static bool AcceptToMemoryPoolWorker( | ||||
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(TxValidationResult::TX_NOT_STANDARD, | return state.Invalid(TxValidationResult::TX_NOT_STANDARD, 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. | ||||
TxValidationState 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(TxValidationResult::TX_PREMATURE_SPEND, | return state.Invalid(TxValidationResult::TX_PREMATURE_SPEND, | ||||
REJECT_NONSTANDARD, ctxState.GetRejectReason(), | 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(TxValidationResult::TX_CONFLICT, REJECT_DUPLICATE, | return state.Invalid(TxValidationResult::TX_CONFLICT, | ||||
"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(TxValidationResult::TX_MEMPOOL_POLICY, | return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, | ||||
REJECT_DUPLICATE, "txn-mempool-conflict"); | "txn-mempool-conflict"); | ||||
} | } | ||||
} | } | ||||
{ | { | ||||
CCoinsView dummy; | CCoinsView dummy; | ||||
CCoinsViewCache view(&dummy); | CCoinsViewCache view(&dummy); | ||||
LockPoints lp; | LockPoints lp; | ||||
Show All 13 Lines | for (const CTxIn &txin : tx.vin) { | ||||
// 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 (coins_cache.HaveCoinInCache(COutPoint(txid, out))) { | if (coins_cache.HaveCoinInCache(COutPoint(txid, out))) { | ||||
return state.Invalid(TxValidationResult::TX_CONFLICT, | return state.Invalid(TxValidationResult::TX_CONFLICT, | ||||
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. | ||||
return state.Invalid(TxValidationResult::TX_MISSING_INPUTS, | return state.Invalid(TxValidationResult::TX_MISSING_INPUTS, | ||||
REJECT_INVALID, | |||||
"bad-txns-inputs-missingorspent"); | "bad-txns-inputs-missingorspent"); | ||||
} | } | ||||
} | } | ||||
// Are the actual inputs available? | // Are the actual inputs available? | ||||
if (!view.HaveInputs(tx)) { | if (!view.HaveInputs(tx)) { | ||||
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, | return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, | ||||
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(TxValidationResult::TX_PREMATURE_SPEND, | return state.Invalid(TxValidationResult::TX_PREMATURE_SPEND, | ||||
REJECT_NONSTANDARD, "non-BIP68-final"); | "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(TxValidationResult::TX_NOT_STANDARD, | return state.Invalid(TxValidationResult::TX_NOT_STANDARD, | ||||
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 10 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(TxValidationResult::TX_MEMPOOL_POLICY, | return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, | ||||
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(TxValidationResult::TX_NOT_STANDARD, | return state.Invalid(TxValidationResult::TX_NOT_STANDARD, | ||||
REJECT_HIGHFEE, "absurdly-high-fee", | "absurdly-high-fee", | ||||
strprintf("%d > %d", nFees, nAbsurdFee)); | strprintf("%d > %d", nFees, nAbsurdFee)); | ||||
} | } | ||||
// Validate input scripts against standard script flags. | // Validate input scripts against standard script flags. | ||||
const uint32_t scriptVerifyFlags = | const uint32_t scriptVerifyFlags = | ||||
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.Invalid( | return state.Invalid( | ||||
TxValidationResult::TX_MEMPOOL_POLICY, REJECT_INSUFFICIENTFEE, | TxValidationResult::TX_MEMPOOL_POLICY, | ||||
"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(TxValidationResult::TX_MEMPOOL_POLICY, | return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, | ||||
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 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.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, | return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, | ||||
REJECT_INSUFFICIENTFEE, "mempool full"); | "mempool full"); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
GetMainSignals().TransactionAddedToMempool(ptx); | GetMainSignals().TransactionAddedToMempool(ptx); | ||||
return true; | return true; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 398 Lines • ▼ Show 20 Lines | bool CheckInputs(const CTransaction &tx, TxValidationState &state, | ||||
// 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(TxValidationResult::TX_CONSENSUS, | return state.Invalid(TxValidationResult::TX_CONSENSUS, | ||||
REJECT_INVALID, "too-many-sigchecks"); | "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 29 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(coin.GetTxOut(), tx, i, mandatoryFlags, | CScriptCheck check2(coin.GetTxOut(), tx, i, mandatoryFlags, | ||||
sigCacheStore, txdata); | sigCacheStore, txdata); | ||||
if (check2()) { | if (check2()) { | ||||
return state.Invalid( | return state.Invalid( | ||||
TxValidationResult::TX_NOT_STANDARD, REJECT_NONSTANDARD, | TxValidationResult::TX_NOT_STANDARD, | ||||
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 | ||||
// TxValidationResult::TX_CONSENSUS. Because CONSENSUS failures are | // TxValidationResult::TX_CONSENSUS. Because CONSENSUS failures are | ||||
// the most serious case of validation failures, we may need to | // 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( | ||||
TxValidationResult::TX_CONSENSUS, REJECT_INVALID, | TxValidationResult::TX_CONSENSUS, | ||||
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 516 Lines • ▼ Show 20 Lines | bool CChainState::ConnectBlock(const CBlock &block, BlockValidationState &state, | ||||
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))) { | ||||
LogPrintf("ERROR: ConnectBlock(): tried to overwrite " | LogPrintf("ERROR: ConnectBlock(): tried to overwrite " | ||||
"transaction\n"); | "transaction\n"); | ||||
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, | return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, | ||||
REJECT_INVALID, "bad-txns-BIP30"); | "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 20 Lines • Show All 41 Lines • ▼ Show 20 Lines | try { | ||||
// 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. | ||||
LogPrintf("ERROR: ConnectBlock(): tried to overwrite transaction\n"); | LogPrintf("ERROR: ConnectBlock(): tried to overwrite transaction\n"); | ||||
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, | return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, | ||||
REJECT_INVALID, "tx-duplicate"); | "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(); | ||||
TxValidationState tx_state; | TxValidationState tx_state; | ||||
if (!isCoinBase && | if (!isCoinBase && | ||||
!Consensus::CheckTxInputs(tx, tx_state, view, pindex->nHeight, | !Consensus::CheckTxInputs(tx, tx_state, view, pindex->nHeight, | ||||
txfee)) { | txfee)) { | ||||
// Any transaction validation failure in ConnectBlock is a block | // Any transaction validation failure in ConnectBlock is a block | ||||
// consensus failure. | // consensus failure. | ||||
state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, | state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, | ||||
tx_state.GetRejectCode(), | |||||
tx_state.GetRejectReason(), | tx_state.GetRejectReason(), | ||||
tx_state.GetDebugMessage()); | 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)) { | ||||
LogPrintf("ERROR: %s: accumulated fee in the block out of range.\n", | LogPrintf("ERROR: %s: accumulated fee in the block out of range.\n", | ||||
__func__); | __func__); | ||||
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, | return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, | ||||
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)) { | ||||
LogPrintf("ERROR: %s: contains a non-BIP68-final transaction\n", | LogPrintf("ERROR: %s: contains a non-BIP68-final transaction\n", | ||||
__func__); | __func__); | ||||
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, | return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, | ||||
REJECT_INVALID, "bad-txns-nonfinal"); | "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) { | ||||
Show All 11 Lines | for (const auto &ptx : block.vtx) { | ||||
if (fScriptChecks && | if (fScriptChecks && | ||||
!CheckInputs(tx, tx_state, view, flags, fCacheResults, | !CheckInputs(tx, tx_state, view, flags, fCacheResults, | ||||
fCacheResults, PrecomputedTransactionData(tx), | fCacheResults, PrecomputedTransactionData(tx), | ||||
nSigChecksRet, nSigChecksTxLimiters[txIndex], | nSigChecksRet, nSigChecksTxLimiters[txIndex], | ||||
&nSigChecksBlockLimiter, &vChecks)) { | &nSigChecksBlockLimiter, &vChecks)) { | ||||
// Any transaction validation failure in ConnectBlock is a block | // Any transaction validation failure in ConnectBlock is a block | ||||
// consensus failure | // consensus failure | ||||
state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, | state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, | ||||
tx_state.GetRejectCode(), tx_state.GetRejectReason(), | tx_state.GetRejectReason(), | ||||
tx_state.GetDebugMessage()); | tx_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 17 Lines | bool CChainState::ConnectBlock(const CBlock &block, BlockValidationState &state, | ||||
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) { | ||||
LogPrintf("ERROR: ConnectBlock(): coinbase pays too much (actual=%d vs " | LogPrintf("ERROR: ConnectBlock(): coinbase pays too much (actual=%d vs " | ||||
"limit=%d)\n", | "limit=%d)\n", | ||||
block.vtx[0]->GetValueOut(), blockReward); | block.vtx[0]->GetValueOut(), blockReward); | ||||
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, | return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, | ||||
REJECT_INVALID, "bad-cb-amount"); | "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 = GetMinerFundAmount(blockReward); | const Amount required = GetMinerFundAmount(blockReward); | ||||
for (auto &o : block.vtx[0]->vout) { | for (auto &o : block.vtx[0]->vout) { | ||||
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(BlockValidationResult::BLOCK_CONSENSUS, | return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, | ||||
REJECT_INVALID, "bad-cb-minerfund"); | "bad-cb-minerfund"); | ||||
} | } | ||||
MinerFundSuccess: | MinerFundSuccess: | ||||
if (!control.Wait()) { | if (!control.Wait()) { | ||||
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, | return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, | ||||
REJECT_INVALID, "blk-bad-inputs", | "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, | ||||
" - Verify %u txins: %.2fms (%.3fms/txin) [%.2fs (%.2fms/blk)]\n", | " - Verify %u txins: %.2fms (%.3fms/txin) [%.2fs (%.2fms/blk)]\n", | ||||
nInputs - 1, MILLI * (nTime4 - nTime2), | nInputs - 1, MILLI * (nTime4 - nTime2), | ||||
▲ Show 20 Lines • Show All 390 Lines • ▼ Show 20 Lines | bool CChainState::MarkBlockAsFinal(const Config &config, | ||||
BlockValidationState &state, | BlockValidationState &state, | ||||
const CBlockIndex *pindex) { | const CBlockIndex *pindex) { | ||||
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. | ||||
LogPrintf("ERROR: %s: Trying to finalize invalid block %s\n", __func__, | LogPrintf("ERROR: %s: Trying to finalize invalid block %s\n", __func__, | ||||
pindex->GetBlockHash().ToString()); | pindex->GetBlockHash().ToString()); | ||||
return state.Invalid(BlockValidationResult::BLOCK_CACHED_INVALID, | return state.Invalid(BlockValidationResult::BLOCK_CACHED_INVALID, | ||||
REJECT_INVALID, "finalize-invalid-block"); | "finalize-invalid-block"); | ||||
} | } | ||||
// Check that the request is consistent with current finalization. | // Check that the request is consistent with current finalization. | ||||
if (m_finalizedBlockIndex && | if (m_finalizedBlockIndex && | ||||
!AreOnTheSameFork(pindex, m_finalizedBlockIndex)) { | !AreOnTheSameFork(pindex, m_finalizedBlockIndex)) { | ||||
LogPrintf("ERROR: %s: Trying to finalize block %s which conflicts with " | LogPrintf("ERROR: %s: Trying to finalize block %s which conflicts with " | ||||
"already finalized block\n", | "already finalized block\n", | ||||
__func__, pindex->GetBlockHash().ToString()); | __func__, pindex->GetBlockHash().ToString()); | ||||
return state.Invalid(BlockValidationResult::BLOCK_FINALIZATION, | return state.Invalid(BlockValidationResult::BLOCK_FINALIZATION, | ||||
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; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 1,253 Lines • ▼ Show 20 Lines | |||||
static bool CheckBlockHeader(const CBlockHeader &block, | static bool CheckBlockHeader(const CBlockHeader &block, | ||||
BlockValidationState &state, | 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(BlockValidationResult::BLOCK_INVALID_HEADER, | return state.Invalid(BlockValidationResult::BLOCK_INVALID_HEADER, | ||||
REJECT_INVALID, "high-hash", | "high-hash", "proof of work failed"); | ||||
"proof of work failed"); | |||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
bool CheckBlock(const CBlock &block, BlockValidationState &state, | bool CheckBlock(const CBlock &block, BlockValidationState &state, | ||||
const Consensus::Params ¶ms, | const Consensus::Params ¶ms, | ||||
BlockValidationOptions validationOptions) { | BlockValidationOptions validationOptions) { | ||||
Show All 9 Lines | bool CheckBlock(const CBlock &block, BlockValidationState &state, | ||||
} | } | ||||
// 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(BlockValidationResult::BLOCK_MUTATED, | return state.Invalid(BlockValidationResult::BLOCK_MUTATED, | ||||
REJECT_INVALID, "bad-txnmrklroot", | "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(BlockValidationResult::BLOCK_MUTATED, | return state.Invalid(BlockValidationResult::BLOCK_MUTATED, | ||||
REJECT_INVALID, "bad-txns-duplicate", | "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(BlockValidationResult::BLOCK_CONSENSUS, | return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, | ||||
REJECT_INVALID, "bad-cb-missing", | "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(BlockValidationResult::BLOCK_CONSENSUS, | return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, | ||||
REJECT_INVALID, "bad-blk-length", | "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(BlockValidationResult::BLOCK_CONSENSUS, | return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, | ||||
REJECT_INVALID, "bad-blk-length", | "bad-blk-length", "size limits failed"); | ||||
"size limits failed"); | |||||
} | } | ||||
// And a valid coinbase. | // And a valid coinbase. | ||||
TxValidationState tx_state; | TxValidationState tx_state; | ||||
if (!CheckCoinbase(*block.vtx[0], tx_state)) { | if (!CheckCoinbase(*block.vtx[0], tx_state)) { | ||||
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, | return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, | ||||
tx_state.GetRejectCode(), | |||||
tx_state.GetRejectReason(), | 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(), | ||||
tx_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, tx_state)) { | if (!CheckRegularTransaction(*tx, tx_state)) { | ||||
return state.Invalid( | return state.Invalid( | ||||
BlockValidationResult::BLOCK_CONSENSUS, | BlockValidationResult::BLOCK_CONSENSUS, | ||||
tx_state.GetRejectCode(), tx_state.GetRejectReason(), | tx_state.GetRejectReason(), | ||||
strprintf("Transaction check failed (txid %s) %s", | strprintf("Transaction check failed (txid %s) %s", | ||||
tx->GetId().ToString(), tx_state.GetDebugMessage())); | tx->GetId().ToString(), tx_state.GetDebugMessage())); | ||||
} | } | ||||
} | } | ||||
if (validationOptions.shouldValidatePoW() && | if (validationOptions.shouldValidatePoW() && | ||||
validationOptions.shouldValidateMerkleRoot()) { | validationOptions.shouldValidateMerkleRoot()) { | ||||
block.fChecked = true; | block.fChecked = true; | ||||
Show All 20 Lines | static bool ContextualCheckBlockHeader(const CChainParams ¶ms, | ||||
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 | ||||
if (block.nBits != GetNextWorkRequired(pindexPrev, &block, params)) { | if (block.nBits != GetNextWorkRequired(pindexPrev, &block, params)) { | ||||
LogPrintf("bad bits after height: %d\n", pindexPrev->nHeight); | LogPrintf("bad bits after height: %d\n", pindexPrev->nHeight); | ||||
return state.Invalid(BlockValidationResult::BLOCK_INVALID_HEADER, | return state.Invalid(BlockValidationResult::BLOCK_INVALID_HEADER, | ||||
REJECT_INVALID, "bad-diffbits", | "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())) { | ||||
LogPrintf("ERROR: %s: rejected by checkpoint lock-in at %d\n", | LogPrintf("ERROR: %s: rejected by checkpoint lock-in at %d\n", | ||||
__func__, nHeight); | __func__, nHeight); | ||||
return state.Invalid(BlockValidationResult::BLOCK_CHECKPOINT, | return state.Invalid(BlockValidationResult::BLOCK_CHECKPOINT, | ||||
REJECT_CHECKPOINT, "checkpoint mismatch"); | "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 g_blockman.m_block_index. | // in our g_blockman.m_block_index. | ||||
CBlockIndex *pcheckpoint = Checkpoints::GetLastCheckpoint(checkpoints); | CBlockIndex *pcheckpoint = Checkpoints::GetLastCheckpoint(checkpoints); | ||||
if (pcheckpoint && nHeight < pcheckpoint->nHeight) { | if (pcheckpoint && nHeight < pcheckpoint->nHeight) { | ||||
LogPrintf("ERROR: %s: forked chain older than last checkpoint " | LogPrintf("ERROR: %s: forked chain older than last checkpoint " | ||||
"(height %d)\n", | "(height %d)\n", | ||||
__func__, nHeight); | __func__, nHeight); | ||||
return state.Invalid(BlockValidationResult::BLOCK_CHECKPOINT, | return state.Invalid(BlockValidationResult::BLOCK_CHECKPOINT, | ||||
REJECT_CHECKPOINT, | |||||
"bad-fork-prior-to-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(BlockValidationResult::BLOCK_INVALID_HEADER, | return state.Invalid(BlockValidationResult::BLOCK_INVALID_HEADER, | ||||
REJECT_INVALID, "time-too-old", | "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(BlockValidationResult::BLOCK_TIME_FUTURE, | return state.Invalid(BlockValidationResult::BLOCK_TIME_FUTURE, | ||||
REJECT_INVALID, "time-too-new", | "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 | ||||
const Consensus::Params &consensusParams = params.GetConsensus(); | const Consensus::Params &consensusParams = params.GetConsensus(); | ||||
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( | ||||
BlockValidationResult::BLOCK_INVALID_HEADER, REJECT_OBSOLETE, | BlockValidationResult::BLOCK_INVALID_HEADER, | ||||
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, | ||||
▲ Show 20 Lines • Show All 73 Lines • ▼ Show 20 Lines | static bool ContextualCheckBlock(const CBlock &block, | ||||
// 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(BlockValidationResult::BLOCK_CONSENSUS, | return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, | ||||
REJECT_INVALID, "tx-duplicate", | "tx-duplicate", | ||||
strprintf("Duplicated transaction %s", | strprintf("Duplicated transaction %s", | ||||
tx.GetId().ToString())); | tx.GetId().ToString())); | ||||
} | } | ||||
return state.Invalid( | return state.Invalid( | ||||
BlockValidationResult::BLOCK_CONSENSUS, REJECT_INVALID, | BlockValidationResult::BLOCK_CONSENSUS, "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; | ||||
} | } | ||||
} | } | ||||
TxValidationState tx_state; | TxValidationState tx_state; | ||||
if (!ContextualCheckTransaction(params, tx, tx_state, nHeight, | if (!ContextualCheckTransaction(params, tx, tx_state, nHeight, | ||||
nLockTimeCutoff, nMedianTimePast)) { | nLockTimeCutoff, nMedianTimePast)) { | ||||
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, | return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, | ||||
REJECT_INVALID, tx_state.GetRejectReason(), | tx_state.GetRejectReason(), | ||||
tx_state.GetDebugMessage()); | 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(BlockValidationResult::BLOCK_CONSENSUS, | return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, | ||||
REJECT_INVALID, "bad-cb-height", | "bad-cb-height", | ||||
"block height mismatch in coinbase"); | "block height mismatch in coinbase"); | ||||
} | } | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
/** | /** | ||||
Show All 19 Lines | if (hash != chainparams.GetConsensus().hashGenesisBlock) { | ||||
if (ppindex) { | if (ppindex) { | ||||
*ppindex = pindex; | *ppindex = pindex; | ||||
} | } | ||||
if (pindex->nStatus.isInvalid()) { | if (pindex->nStatus.isInvalid()) { | ||||
LogPrintf("ERROR: %s: block %s is marked invalid\n", __func__, | LogPrintf("ERROR: %s: block %s is marked invalid\n", __func__, | ||||
hash.ToString()); | hash.ToString()); | ||||
return state.Invalid( | return state.Invalid( | ||||
BlockValidationResult::BLOCK_CACHED_INVALID, 0, | BlockValidationResult::BLOCK_CACHED_INVALID, "duplicate"); | ||||
"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 = m_block_index.find(block.hashPrevBlock); | BlockMap::iterator mi = m_block_index.find(block.hashPrevBlock); | ||||
if (mi == m_block_index.end()) { | if (mi == m_block_index.end()) { | ||||
LogPrintf("ERROR: %s: prev block not found\n", __func__); | LogPrintf("ERROR: %s: prev block not found\n", __func__); | ||||
return state.Invalid(BlockValidationResult::BLOCK_MISSING_PREV, 0, | return state.Invalid(BlockValidationResult::BLOCK_MISSING_PREV, | ||||
"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()) { | ||||
LogPrintf("ERROR: %s: prev block invalid\n", __func__); | LogPrintf("ERROR: %s: prev block invalid\n", __func__); | ||||
return state.Invalid(BlockValidationResult::BLOCK_INVALID_PREV, | return state.Invalid(BlockValidationResult::BLOCK_INVALID_PREV, | ||||
REJECT_INVALID, "bad-prevblk"); | "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 30 Lines | if (hash != chainparams.GetConsensus().hashGenesisBlock) { | ||||
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__); | LogPrintf("ERROR: %s: prev block invalid\n", __func__); | ||||
return state.Invalid( | return state.Invalid( | ||||
BlockValidationResult::BLOCK_INVALID_PREV, | BlockValidationResult::BLOCK_INVALID_PREV, | ||||
REJECT_INVALID, "bad-prevblk"); | "bad-prevblk"); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
if (pindex == nullptr) { | if (pindex == nullptr) { | ||||
pindex = AddToBlockIndex(block); | pindex = AddToBlockIndex(block); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 1,823 Lines • Show Last 20 Lines |