Changeset View
Changeset View
Standalone View
Standalone View
src/validation.cpp
Show All 24 Lines | |||||
#include <index/blockfilterindex.h> | #include <index/blockfilterindex.h> | ||||
#include <logging.h> | #include <logging.h> | ||||
#include <logging/timer.h> | #include <logging/timer.h> | ||||
#include <minerfund.h> | #include <minerfund.h> | ||||
#include <node/blockstorage.h> | #include <node/blockstorage.h> | ||||
#include <node/coinstats.h> | #include <node/coinstats.h> | ||||
#include <node/ui_interface.h> | #include <node/ui_interface.h> | ||||
#include <node/utxo_snapshot.h> | #include <node/utxo_snapshot.h> | ||||
#include <parkingpresets.h> | |||||
#include <policy/mempool.h> | #include <policy/mempool.h> | ||||
#include <policy/policy.h> | #include <policy/policy.h> | ||||
#include <policy/settings.h> | #include <policy/settings.h> | ||||
#include <pow/pow.h> | #include <pow/pow.h> | ||||
#include <primitives/block.h> | #include <primitives/block.h> | ||||
#include <primitives/transaction.h> | #include <primitives/transaction.h> | ||||
#include <random.h> | #include <random.h> | ||||
#include <reverse_iterator.h> | #include <reverse_iterator.h> | ||||
▲ Show 20 Lines • Show All 1,725 Lines • ▼ Show 20 Lines | |||||
* Apply the effects of this block (with given index) on the UTXO set | * Apply the effects of this block (with given index) on the UTXO set | ||||
* represented by coins. Validity checks that depend on the UTXO set are also | * represented by coins. Validity checks that depend on the UTXO set are also | ||||
* done; ConnectBlock() can fail if those validity checks fail (among other | * done; ConnectBlock() can fail if those validity checks fail (among other | ||||
* reasons). | * reasons). | ||||
*/ | */ | ||||
bool CChainState::ConnectBlock(const CBlock &block, BlockValidationState &state, | bool CChainState::ConnectBlock(const CBlock &block, BlockValidationState &state, | ||||
CBlockIndex *pindex, CCoinsViewCache &view, | CBlockIndex *pindex, CCoinsViewCache &view, | ||||
BlockValidationOptions options, | BlockValidationOptions options, | ||||
bool fJustCheck) { | Amount *blockFees, bool fJustCheck) { | ||||
AssertLockHeld(cs_main); | AssertLockHeld(cs_main); | ||||
assert(pindex); | assert(pindex); | ||||
const BlockHash block_hash{block.GetHash()}; | const BlockHash block_hash{block.GetHash()}; | ||||
assert(*pindex->phashBlock == block_hash); | assert(*pindex->phashBlock == block_hash); | ||||
int64_t nTimeStart = GetTimeMicros(); | int64_t nTimeStart = GetTimeMicros(); | ||||
▲ Show 20 Lines • Show All 361 Lines • ▼ Show 20 Lines | bool CChainState::ConnectBlock(const CBlock &block, BlockValidationState &state, | ||||
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, | ||||
"bad-cb-amount"); | "bad-cb-amount"); | ||||
} | } | ||||
if (blockFees) { | |||||
*blockFees = nFees; | |||||
} | |||||
if (!CheckMinerFund(consensusParams, pindex, block, nFees)) { | if (!CheckMinerFund(consensusParams, pindex, block, nFees)) { | ||||
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, | return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, | ||||
"bad-cb-minerfund"); | "bad-cb-minerfund"); | ||||
} | } | ||||
if (!control.Wait()) { | if (!control.Wait()) { | ||||
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, | return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, | ||||
"blk-bad-inputs", "parallel script check failed"); | "blk-bad-inputs", "parallel script check failed"); | ||||
▲ Show 20 Lines • Show All 439 Lines • ▼ Show 20 Lines | |||||
* The block is always added to connectTrace (either after loading from disk or | * The block is always added to connectTrace (either after loading from disk or | ||||
* by copying pblock) - if that is not intended, care must be taken to remove | * by copying pblock) - if that is not intended, care must be taken to remove | ||||
* the last entry in blocksConnected in case of failure. | * the last entry in blocksConnected in case of failure. | ||||
*/ | */ | ||||
bool CChainState::ConnectTip(const Config &config, BlockValidationState &state, | bool CChainState::ConnectTip(const Config &config, BlockValidationState &state, | ||||
CBlockIndex *pindexNew, | CBlockIndex *pindexNew, | ||||
const std::shared_ptr<const CBlock> &pblock, | const std::shared_ptr<const CBlock> &pblock, | ||||
ConnectTrace &connectTrace, | ConnectTrace &connectTrace, | ||||
DisconnectedBlockTransactions &disconnectpool) { | DisconnectedBlockTransactions &disconnectpool, | ||||
Amount *blockFees) { | |||||
AssertLockHeld(cs_main); | AssertLockHeld(cs_main); | ||||
if (m_mempool) { | if (m_mempool) { | ||||
AssertLockHeld(m_mempool->cs); | AssertLockHeld(m_mempool->cs); | ||||
} | } | ||||
const Consensus::Params &consensusParams = m_params.GetConsensus(); | const Consensus::Params &consensusParams = m_params.GetConsensus(); | ||||
assert(pindexNew->pprev == m_chain.Tip()); | assert(pindexNew->pprev == m_chain.Tip()); | ||||
Show All 16 Lines | bool CChainState::ConnectTip(const Config &config, BlockValidationState &state, | ||||
int64_t nTime2 = GetTimeMicros(); | int64_t nTime2 = GetTimeMicros(); | ||||
nTimeReadFromDisk += nTime2 - nTime1; | nTimeReadFromDisk += nTime2 - nTime1; | ||||
int64_t nTime3; | int64_t nTime3; | ||||
LogPrint(BCLog::BENCH, " - Load block from disk: %.2fms [%.2fs]\n", | LogPrint(BCLog::BENCH, " - Load block from disk: %.2fms [%.2fs]\n", | ||||
(nTime2 - nTime1) * MILLI, nTimeReadFromDisk * MICRO); | (nTime2 - nTime1) * MILLI, nTimeReadFromDisk * MICRO); | ||||
{ | { | ||||
CCoinsViewCache view(&CoinsTip()); | CCoinsViewCache view(&CoinsTip()); | ||||
bool rv = ConnectBlock(blockConnecting, state, pindexNew, view, | bool rv = ConnectBlock(blockConnecting, state, pindexNew, view, | ||||
BlockValidationOptions(config)); | BlockValidationOptions(config), blockFees); | ||||
GetMainSignals().BlockChecked(blockConnecting, state); | GetMainSignals().BlockChecked(blockConnecting, state); | ||||
if (!rv) { | if (!rv) { | ||||
if (state.IsInvalid()) { | if (state.IsInvalid()) { | ||||
InvalidBlockFound(pindexNew, state); | InvalidBlockFound(pindexNew, state); | ||||
} | } | ||||
return error("%s: ConnectBlock %s failed, %s", __func__, | return error("%s: ConnectBlock %s failed, %s", __func__, | ||||
pindexNew->GetBlockHash().ToString(), | pindexNew->GetBlockHash().ToString(), | ||||
▲ Show 20 Lines • Show All 310 Lines • ▼ Show 20 Lines | while (fContinue && nHeight != pindexMostWork->nHeight) { | ||||
vpindexToConnect.push_back(pindexIter); | vpindexToConnect.push_back(pindexIter); | ||||
pindexIter = pindexIter->pprev; | pindexIter = pindexIter->pprev; | ||||
} | } | ||||
nHeight = nTargetHeight; | nHeight = nTargetHeight; | ||||
// Connect new blocks. | // Connect new blocks. | ||||
for (CBlockIndex *pindexConnect : reverse_iterate(vpindexToConnect)) { | for (CBlockIndex *pindexConnect : reverse_iterate(vpindexToConnect)) { | ||||
if (!ConnectTip(config, state, pindexConnect, | std::shared_ptr<const CBlock> pblockConnect = | ||||
pindexConnect == pindexMostWork | pindexConnect == pindexMostWork | ||||
? pblock | ? pblock | ||||
: std::shared_ptr<const CBlock>(), | : std::shared_ptr<const CBlock>(); | ||||
connectTrace, disconnectpool)) { | // Read block from disk. | ||||
if (!pblockConnect) { | |||||
std::shared_ptr<CBlock> pblockLoad = std::make_shared<CBlock>(); | |||||
if (!ReadBlockFromDisk(*pblockLoad, pindexConnect, | |||||
m_params.GetConsensus())) { | |||||
// A system error occurred (disk space, database error, ...) | |||||
// Make the mempool consistent with the current tip, just in | |||||
// case any observers try to use it before shutdown. | |||||
if (m_mempool) { | |||||
disconnectpool.updateMempoolForReorg(config, *this, | |||||
false, *m_mempool); | |||||
} | |||||
return AbortNode(state, "Failed to read block"); | |||||
} | |||||
pblockConnect = pblockLoad; | |||||
} | |||||
Amount blockFees{Amount::zero()}; | |||||
if (!ConnectTip(config, state, pindexConnect, pblockConnect, | |||||
connectTrace, disconnectpool, &blockFees)) { | |||||
if (state.IsInvalid()) { | if (state.IsInvalid()) { | ||||
// The block violates a consensus rule. | // The block violates a consensus rule. | ||||
if (state.GetResult() != | if (state.GetResult() != | ||||
BlockValidationResult::BLOCK_MUTATED) { | BlockValidationResult::BLOCK_MUTATED) { | ||||
InvalidChainFound(vpindexToConnect.back()); | InvalidChainFound(vpindexToConnect.back()); | ||||
} | } | ||||
state = BlockValidationState(); | state = BlockValidationState(); | ||||
fInvalidFound = true; | fInvalidFound = true; | ||||
fContinue = false; | fContinue = false; | ||||
break; | break; | ||||
} | } | ||||
// A system error occurred (disk space, database error, ...). | // A system error occurred (disk space, database error, ...). | ||||
// Make the mempool consistent with the current tip, just in | // Make the mempool consistent with the current tip, just in | ||||
// case any observers try to use it before shutdown. | // case any observers try to use it before shutdown. | ||||
if (m_mempool) { | if (m_mempool) { | ||||
disconnectpool.updateMempoolForReorg(config, *this, false, | disconnectpool.updateMempoolForReorg(config, *this, false, | ||||
*m_mempool); | *m_mempool); | ||||
} | } | ||||
return false; | return false; | ||||
} else { | } else { | ||||
// Only check block parking presets the first time the block is | |||||
// connected. Avalanche voting can override the parking | |||||
// decision made by these presets. | |||||
const BlockHash blockhash = pindexConnect->GetBlockHash(); | |||||
if (!IsInitialBlockDownload() && | |||||
!m_filterParkingPresetsApplied.contains(blockhash)) { | |||||
m_filterParkingPresetsApplied.insert(blockhash); | |||||
// The block is valid by consensus rules so now we check if | |||||
// the block passes all block preset rules. If not, then | |||||
// park the block. | |||||
const CBlock &connectedBlock = *pblockConnect; | |||||
const auto parkingPresets = GetParkingPresetsForBlock( | |||||
m_params.GetConsensus(), pindexConnect, connectedBlock, | |||||
blockFees); | |||||
if (std::any_of( | |||||
parkingPresets.begin(), parkingPresets.end(), | |||||
[&](const auto preset) { return (*preset)(); })) { | |||||
LogPrintf("Park block %s because it violated a block " | |||||
"policy\n", | |||||
blockhash.ToString()); | |||||
pindexConnect->nStatus = | |||||
pindexConnect->nStatus.withParked(); | |||||
m_blockman.m_dirty_blockindex.insert(pindexConnect); | |||||
// The parked block is still the chaintip so disconnect | |||||
// it. | |||||
if (!DisconnectTip(state, &disconnectpool)) { | |||||
// This is likely a fatal error, but keep the | |||||
// mempool consistent, just in case. Only remove | |||||
// from the mempool in this case. | |||||
if (m_mempool) { | |||||
disconnectpool.updateMempoolForReorg( | |||||
config, *this, false, *m_mempool); | |||||
} | |||||
// If we're unable to disconnect a block during | |||||
// normal operation, then that is a failure of our | |||||
// local system -- we should abort rather than stay | |||||
// on a less work chain. | |||||
AbortNode(state, "Failed to disconnect block; see " | |||||
"debug.log for details"); | |||||
return false; | |||||
} | |||||
fBlocksDisconnected = true; | |||||
// TODO fix this hackiness | |||||
fInvalidFound = true; | |||||
fContinue = false; | |||||
break; | |||||
} | |||||
} | |||||
PruneBlockIndexCandidates(); | PruneBlockIndexCandidates(); | ||||
if (!pindexOldTip || | if (!pindexOldTip || | ||||
m_chain.Tip()->nChainWork > pindexOldTip->nChainWork) { | m_chain.Tip()->nChainWork > pindexOldTip->nChainWork) { | ||||
// We're in a better position than we were. Return | // We're in a better position than we were. Return | ||||
// temporarily to release the lock. | // temporarily to release the lock. | ||||
fContinue = false; | fContinue = false; | ||||
break; | break; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 1,406 Lines • ▼ Show 20 Lines | bool TestBlockValidity(BlockValidationState &state, const CChainParams ¶ms, | ||||
if (!ContextualCheckBlock(block, state, params.GetConsensus(), | if (!ContextualCheckBlock(block, state, params.GetConsensus(), | ||||
pindexPrev)) { | pindexPrev)) { | ||||
return error("%s: Consensus::ContextualCheckBlock: %s", __func__, | return error("%s: Consensus::ContextualCheckBlock: %s", __func__, | ||||
state.ToString()); | state.ToString()); | ||||
} | } | ||||
if (!chainstate.ConnectBlock(block, state, &indexDummy, viewNew, | if (!chainstate.ConnectBlock(block, state, &indexDummy, viewNew, | ||||
validationOptions, true)) { | validationOptions, nullptr, true)) { | ||||
return false; | return false; | ||||
} | } | ||||
assert(state.IsValid()); | assert(state.IsValid()); | ||||
return true; | return true; | ||||
} | } | ||||
/* This function is called from the RPC code for pruneblockchain */ | /* This function is called from the RPC code for pruneblockchain */ | ||||
▲ Show 20 Lines • Show All 1,682 Lines • Show Last 20 Lines |