Changeset View
Changeset View
Standalone View
Standalone View
src/validation.cpp
Show First 20 Lines • Show All 1,879 Lines • ▼ Show 20 Lines | static uint32_t GetBlockScriptFlags(const CBlockIndex *pindex, | ||||
if (IsDAAEnabled(config, pindex->pprev)) { | if (IsDAAEnabled(config, pindex->pprev)) { | ||||
flags |= SCRIPT_VERIFY_LOW_S; | flags |= SCRIPT_VERIFY_LOW_S; | ||||
flags |= SCRIPT_VERIFY_NULLFAIL; | flags |= SCRIPT_VERIFY_NULLFAIL; | ||||
} | } | ||||
return flags; | return flags; | ||||
} | } | ||||
/** | |||||
* Verifies whether the given block is assumed valid. | |||||
* This will cause the script not to be checked and the | |||||
* commitment not to be checked | |||||
*/ | |||||
static bool IsAssumedValid(const Config &config, CBlockIndex *pindex) { | |||||
AssertLockHeld(cs_main); | |||||
if (!hashAssumeValid.IsNull()) { | |||||
const Consensus::Params &consensusParams = | |||||
config.GetChainParams().GetConsensus(); | |||||
// We've been configured with the hash of a block which has been | |||||
// externally verified to have a valid history. A suitable default value | |||||
// is included with the software and updated from time to time. Because | |||||
// validity relative to a piece of software is an objective fact these | |||||
// defaults can be easily reviewed. This setting doesn't force the | |||||
// selection of any particular chain but makes validating some faster by | |||||
// effectively caching the result of part of the verification. | |||||
BlockMap::const_iterator it = mapBlockIndex.find(hashAssumeValid); | |||||
if (it != mapBlockIndex.end()) { | |||||
if (it->second->GetAncestor(pindex->nHeight) == pindex && | |||||
pindexBestHeader->GetAncestor(pindex->nHeight) == pindex && | |||||
pindexBestHeader->nChainWork >= | |||||
UintToArith256(consensusParams.nMinimumChainWork)) { | |||||
// This block is a member of the assumed verified chain and an | |||||
// ancestor of the best header. The equivalent time check | |||||
// discourages hashpower from extorting the network via DOS | |||||
// attack into accepting an invalid block through telling users | |||||
// they must manually set assumevalid. Requiring a software | |||||
// change or burying the invalid block, regardless of the | |||||
// setting, makes it hard to hide the implication of the demand. | |||||
// This also avoids having release candidates that are hardly | |||||
// doing any signature verification at all in testing without | |||||
// having to artificially set the default assumed verified block | |||||
// further back. The test against nMinimumChainWork prevents the | |||||
// skipping when denied access to any chain at least as good as | |||||
// the expected chain. | |||||
return !(GetBlockProofEquivalentTime( | |||||
*pindexBestHeader, *pindex, *pindexBestHeader, | |||||
consensusParams) <= 60 * 60 * 24 * 7 * 2); | |||||
} | |||||
} | |||||
} | |||||
return false; | |||||
} | |||||
static int64_t nTimeCheck = 0; | static int64_t nTimeCheck = 0; | ||||
static int64_t nTimeForks = 0; | static int64_t nTimeForks = 0; | ||||
static int64_t nTimeVerify = 0; | static int64_t nTimeVerify = 0; | ||||
static int64_t nTimeConnect = 0; | static int64_t nTimeConnect = 0; | ||||
static int64_t nTimeIndex = 0; | static int64_t nTimeIndex = 0; | ||||
static int64_t nTimeCallbacks = 0; | static int64_t nTimeCallbacks = 0; | ||||
static int64_t nTimeTotal = 0; | static int64_t nTimeTotal = 0; | ||||
Show All 28 Lines | static bool ConnectBlock(const Config &config, const CBlock &block, | ||||
if (block.GetHash() == consensusParams.hashGenesisBlock) { | if (block.GetHash() == consensusParams.hashGenesisBlock) { | ||||
if (!fJustCheck) { | if (!fJustCheck) { | ||||
view.SetBestBlock(pindex->GetBlockHash()); | view.SetBestBlock(pindex->GetBlockHash()); | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
bool fScriptChecks = true; | bool fScriptChecks = !IsAssumedValid(config, pindex); | ||||
if (!hashAssumeValid.IsNull()) { | |||||
// We've been configured with the hash of a block which has been | |||||
// externally verified to have a valid history. A suitable default value | |||||
// is included with the software and updated from time to time. Because | |||||
// validity relative to a piece of software is an objective fact these | |||||
// defaults can be easily reviewed. This setting doesn't force the | |||||
// selection of any particular chain but makes validating some faster by | |||||
// effectively caching the result of part of the verification. | |||||
BlockMap::const_iterator it = mapBlockIndex.find(hashAssumeValid); | |||||
if (it != mapBlockIndex.end()) { | |||||
if (it->second->GetAncestor(pindex->nHeight) == pindex && | |||||
pindexBestHeader->GetAncestor(pindex->nHeight) == pindex && | |||||
pindexBestHeader->nChainWork >= | |||||
UintToArith256(consensusParams.nMinimumChainWork)) { | |||||
// This block is a member of the assumed verified chain and an | |||||
// ancestor of the best header. The equivalent time check | |||||
// discourages hashpower from extorting the network via DOS | |||||
// attack into accepting an invalid block through telling users | |||||
// they must manually set assumevalid. Requiring a software | |||||
// change or burying the invalid block, regardless of the | |||||
// setting, makes it hard to hide the implication of the demand. | |||||
// This also avoids having release candidates that are hardly | |||||
// doing any signature verification at all in testing without | |||||
// having to artificially set the default assumed verified block | |||||
// further back. The test against nMinimumChainWork prevents the | |||||
// skipping when denied access to any chain at least as good as | |||||
// the expected chain. | |||||
fScriptChecks = | |||||
(GetBlockProofEquivalentTime( | |||||
*pindexBestHeader, *pindex, *pindexBestHeader, | |||||
consensusParams) <= 60 * 60 * 24 * 7 * 2); | |||||
} | |||||
} | |||||
} | |||||
int64_t nTime1 = GetTimeMicros(); | int64_t nTime1 = GetTimeMicros(); | ||||
nTimeCheck += nTime1 - nTimeStart; | nTimeCheck += nTime1 - nTimeStart; | ||||
LogPrint(BCLog::BENCH, " - Sanity checks: %.2fms [%.2fs]\n", | LogPrint(BCLog::BENCH, " - Sanity checks: %.2fms [%.2fs]\n", | ||||
0.001 * (nTime1 - nTimeStart), nTimeCheck * 0.000001); | 0.001 * (nTime1 - nTimeStart), nTimeCheck * 0.000001); | ||||
// Do not allow blocks that contain transactions which 'overwrite' older | // Do not allow blocks that contain transactions which 'overwrite' older | ||||
// transactions, unless those are already completely spent. If such | // transactions, unless those are already completely spent. If such | ||||
▲ Show 20 Lines • Show All 379 Lines • ▼ Show 20 Lines | try { | ||||
((mode == FLUSH_STATE_ALWAYS || mode == FLUSH_STATE_PERIODIC) && | ((mode == FLUSH_STATE_ALWAYS || mode == FLUSH_STATE_PERIODIC) && | ||||
nNow > | nNow > | ||||
nLastSetChain + (int64_t)DATABASE_WRITE_INTERVAL * 1000000)) { | nLastSetChain + (int64_t)DATABASE_WRITE_INTERVAL * 1000000)) { | ||||
// Update best block in wallet (so we can detect restored wallets). | // Update best block in wallet (so we can detect restored wallets). | ||||
GetMainSignals().SetBestChain(chainActive.GetLocator()); | GetMainSignals().SetBestChain(chainActive.GetLocator()); | ||||
nLastSetChain = nNow; | nLastSetChain = nNow; | ||||
} | } | ||||
} catch (const std::runtime_error &e) { | } catch (const std::runtime_error &e) { | ||||
return AbortNode( | return AbortNode(state, std::string("System error while flushing: ") + | ||||
state, std::string("System error while flushing: ") + e.what()); | e.what()); | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
void FlushStateToDisk() { | void FlushStateToDisk() { | ||||
CValidationState state; | CValidationState state; | ||||
const CChainParams &chainparams = Params(); | const CChainParams &chainparams = Params(); | ||||
FlushStateToDisk(chainparams, state, FLUSH_STATE_ALWAYS); | FlushStateToDisk(chainparams, state, FLUSH_STATE_ALWAYS); | ||||
▲ Show 20 Lines • Show All 108 Lines • ▼ Show 20 Lines | static bool DisconnectTip(const Config &config, CValidationState &state, | ||||
// Read block from disk. | // Read block from disk. | ||||
std::shared_ptr<CBlock> pblock = std::make_shared<CBlock>(); | std::shared_ptr<CBlock> pblock = std::make_shared<CBlock>(); | ||||
CBlock &block = *pblock; | CBlock &block = *pblock; | ||||
if (!ReadBlockFromDisk(block, pindexDelete, config)) { | if (!ReadBlockFromDisk(block, pindexDelete, config)) { | ||||
return AbortNode(state, "Failed to read block"); | return AbortNode(state, "Failed to read block"); | ||||
} | } | ||||
// We can't be skipping the commitment delta, as this would resut in a | |||||
// commitment data out-of-sync with the pcoinsTip cache; in that case flush | |||||
// first | |||||
if (!pcoinsTip->HasCommitmentDelta()) { | |||||
pcoinsTip->Flush(); | |||||
} | |||||
// Apply the block atomically to the chain state. | // Apply the block atomically to the chain state. | ||||
int64_t nStart = GetTimeMicros(); | int64_t nStart = GetTimeMicros(); | ||||
{ | { | ||||
CCoinsViewCache view(pcoinsTip); | CCoinsViewCache view(pcoinsTip); | ||||
view.CalculateCommitment(); | |||||
assert(view.GetBestBlock() == pindexDelete->GetBlockHash()); | assert(view.GetBestBlock() == pindexDelete->GetBlockHash()); | ||||
if (DisconnectBlock(block, pindexDelete, view) != DISCONNECT_OK) { | if (DisconnectBlock(block, pindexDelete, view) != DISCONNECT_OK) { | ||||
return error("DisconnectTip(): DisconnectBlock %s failed", | return error("DisconnectTip(): DisconnectBlock %s failed", | ||||
pindexDelete->GetBlockHash().ToString()); | pindexDelete->GetBlockHash().ToString()); | ||||
} | } | ||||
bool flushed = view.Flush(); | bool flushed = view.Flush(); | ||||
assert(flushed); | assert(flushed); | ||||
} | } | ||||
LogPrint(BCLog::BENCH, "- Disconnect block: %.2fms\n", | LogPrint(BCLog::BENCH, "- Disconnect block: %.2fms\n", | ||||
(GetTimeMicros() - nStart) * 0.001); | (GetTimeMicros() - nStart) * 0.001); | ||||
// Write the chain state to disk, if necessary. | // Write the chain state to disk, if necessary. | ||||
▲ Show 20 Lines • Show All 127 Lines • ▼ Show 20 Lines | if (!pblock) { | ||||
} | } | ||||
pthisBlock = pblockNew; | pthisBlock = pblockNew; | ||||
} else { | } else { | ||||
pthisBlock = pblock; | pthisBlock = pblock; | ||||
} | } | ||||
const CBlock &blockConnecting = *pthisBlock; | const CBlock &blockConnecting = *pthisBlock; | ||||
// If we should start mainting the UTXO commitment | |||||
// we must flush first (except at genesis) | |||||
bool assumeValid = IsAssumedValid(config, pindexNew); | |||||
bool skippingCommitment = !pcoinsTip->HasCommitmentDelta(); | |||||
if (skippingCommitment && !assumeValid) { | |||||
const Consensus::Params &consensusParams = | |||||
config.GetChainParams().GetConsensus(); | |||||
if (pthisBlock->GetHash() != consensusParams.hashGenesisBlock) { | |||||
pcoinsTip->Flush(); | |||||
} | |||||
} | |||||
// Apply the block atomically to the chain state. | // Apply the block atomically to the chain 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) * 0.001, nTimeReadFromDisk * 0.000001); | (nTime2 - nTime1) * 0.001, nTimeReadFromDisk * 0.000001); | ||||
{ | { | ||||
CCoinsViewCache view(pcoinsTip); | CCoinsViewCache view(pcoinsTip); | ||||
if (!assumeValid) { | |||||
view.CalculateCommitment(); | |||||
} | |||||
bool rv = ConnectBlock(config, blockConnecting, state, pindexNew, view); | bool rv = ConnectBlock(config, blockConnecting, state, pindexNew, view); | ||||
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("ConnectTip(): ConnectBlock %s failed", | return error("ConnectTip(): ConnectBlock %s failed", | ||||
pindexNew->GetBlockHash().ToString()); | pindexNew->GetBlockHash().ToString()); | ||||
} | } | ||||
nTime3 = GetTimeMicros(); | nTime3 = GetTimeMicros(); | ||||
nTimeConnectTotal += nTime3 - nTime2; | nTimeConnectTotal += nTime3 - nTime2; | ||||
LogPrint(BCLog::BENCH, " - Connect total: %.2fms [%.2fs]\n", | LogPrint(BCLog::BENCH, " - Connect total: %.2fms [%.2fs]\n", | ||||
(nTime3 - nTime2) * 0.001, nTimeConnectTotal * 0.000001); | (nTime3 - nTime2) * 0.001, nTimeConnectTotal * 0.000001); | ||||
bool flushed = view.Flush(); | bool flushed = view.Flush(); | ||||
assert(flushed); | assert(flushed); | ||||
assert(assumeValid || pcoinsTip->HasCommitmentDelta()); | |||||
} | } | ||||
int64_t nTime4 = GetTimeMicros(); | int64_t nTime4 = GetTimeMicros(); | ||||
nTimeFlush += nTime4 - nTime3; | nTimeFlush += nTime4 - nTime3; | ||||
LogPrint(BCLog::BENCH, " - Flush: %.2fms [%.2fs]\n", | LogPrint(BCLog::BENCH, " - Flush: %.2fms [%.2fs]\n", | ||||
(nTime4 - nTime3) * 0.001, nTimeFlush * 0.000001); | (nTime4 - nTime3) * 0.001, nTimeFlush * 0.000001); | ||||
// Write the chain state to disk, if necessary. | // Write the chain state to disk, if necessary. | ||||
if (!FlushStateToDisk(config.GetChainParams(), state, | if (!FlushStateToDisk(config.GetChainParams(), state, | ||||
FLUSH_STATE_IF_NEEDED)) { | FLUSH_STATE_IF_NEEDED)) { | ||||
return false; | return false; | ||||
} | } | ||||
int64_t nTime5 = GetTimeMicros(); | int64_t nTime5 = GetTimeMicros(); | ||||
nTimeChainState += nTime5 - nTime4; | nTimeChainState += nTime5 - nTime4; | ||||
LogPrint(BCLog::BENCH, " - Writing chainstate: %.2fms [%.2fs]\n", | LogPrint(BCLog::BENCH, " - Writing chainstate: %.2fms [%.2fs]\n", | ||||
▲ Show 20 Lines • Show All 351 Lines • ▼ Show 20 Lines | bool InvalidateBlock(const Config &config, CValidationState &state, | ||||
DisconnectedBlockTransactions disconnectpool; | DisconnectedBlockTransactions disconnectpool; | ||||
while (chainActive.Contains(pindex)) { | while (chainActive.Contains(pindex)) { | ||||
CBlockIndex *pindexWalk = chainActive.Tip(); | CBlockIndex *pindexWalk = chainActive.Tip(); | ||||
pindexWalk->nStatus |= BLOCK_FAILED_CHILD; | pindexWalk->nStatus |= BLOCK_FAILED_CHILD; | ||||
setDirtyBlockIndex.insert(pindexWalk); | setDirtyBlockIndex.insert(pindexWalk); | ||||
setBlockIndexCandidates.erase(pindexWalk); | setBlockIndexCandidates.erase(pindexWalk); | ||||
// ActivateBestChain considers blocks already in chainActive | // ActivateBestChain considers blocks already in chainActive | ||||
// unconditionally valid already, so force disconnect away from it. | // unconditionally valid already, so force disconnect away from it. | ||||
if (!DisconnectTip(config, state, &disconnectpool)) { | if (!DisconnectTip(config, state, &disconnectpool)) { | ||||
// It's probably hopeless to try to make the mempool consistent | // It's probably hopeless to try to make the mempool consistent | ||||
// here if DisconnectTip failed, but we can try. | // here if DisconnectTip failed, but we can try. | ||||
UpdateMempoolForReorg(config, disconnectpool, false); | UpdateMempoolForReorg(config, disconnectpool, false); | ||||
return false; | return false; | ||||
} | } | ||||
} | } | ||||
▲ Show 20 Lines • Show All 1,354 Lines • ▼ Show 20 Lines | bool CVerifyDB::VerifyDB(const Config &config, CCoinsView *coinsview, | ||||
// check level 4: try reconnecting blocks | // check level 4: try reconnecting blocks | ||||
if (nCheckLevel >= 4) { | if (nCheckLevel >= 4) { | ||||
CBlockIndex *pindex = pindexState; | CBlockIndex *pindex = pindexState; | ||||
while (pindex != chainActive.Tip()) { | while (pindex != chainActive.Tip()) { | ||||
boost::this_thread::interruption_point(); | boost::this_thread::interruption_point(); | ||||
uiInterface.ShowProgress( | uiInterface.ShowProgress( | ||||
_("Verifying blocks..."), | _("Verifying blocks..."), | ||||
std::max(1, | std::max( | ||||
std::min(99, | 1, std::min(99, 100 - (int)(((double)(chainActive.Height() - | ||||
100 - (int)(((double)(chainActive.Height() - | |||||
pindex->nHeight)) / | pindex->nHeight)) / | ||||
(double)nCheckDepth * 50)))); | (double)nCheckDepth * 50)))); | ||||
pindex = chainActive.Next(pindex); | pindex = chainActive.Next(pindex); | ||||
CBlock block; | CBlock block; | ||||
if (!ReadBlockFromDisk(block, pindex, config)) { | if (!ReadBlockFromDisk(block, pindex, config)) { | ||||
return error( | return error( | ||||
"VerifyDB(): *** ReadBlockFromDisk failed at %d, hash=%s", | "VerifyDB(): *** ReadBlockFromDisk failed at %d, hash=%s", | ||||
pindex->nHeight, pindex->GetBlockHash().ToString()); | pindex->nHeight, pindex->GetBlockHash().ToString()); | ||||
} | } | ||||
if (!ConnectBlock(config, block, state, pindex, coins)) { | if (!ConnectBlock(config, block, state, pindex, coins)) { | ||||
▲ Show 20 Lines • Show All 904 Lines • Show Last 20 Lines |