Changeset View
Changeset View
Standalone View
Standalone View
src/validation.cpp
Show First 20 Lines • Show All 53 Lines • ▼ Show 20 Lines | |||||
#include <optional> | #include <optional> | ||||
#include <string> | #include <string> | ||||
#include <thread> | #include <thread> | ||||
#define MICRO 0.000001 | #define MICRO 0.000001 | ||||
#define MILLI 0.001 | #define MILLI 0.001 | ||||
namespace { | ChainstateManager g_chainman; | ||||
BlockManager g_blockman; | |||||
} // namespace | |||||
std::unique_ptr<CChainState> g_chainstate; | |||||
CChainState &ChainstateActive() { | CChainState &ChainstateActive() { | ||||
assert(g_chainstate); | assert(g_chainman.m_active_chainstate); | ||||
return *g_chainstate; | return *g_chainman.m_active_chainstate; | ||||
} | } | ||||
CChain &ChainActive() { | CChain &ChainActive() { | ||||
assert(g_chainstate); | return ::ChainstateActive().m_chain; | ||||
return g_chainstate->m_chain; | |||||
} | } | ||||
/** | /** | ||||
* Global state | * Global state | ||||
* | * | ||||
* Mutex to guard access to validation specific variables, such as reading | * Mutex to guard access to validation specific variables, such as reading | ||||
* or changing the chainstate. | * or changing the chainstate. | ||||
* | * | ||||
▲ Show 20 Lines • Show All 50 Lines • ▼ Show 20 Lines | |||||
} // namespace | } // namespace | ||||
BlockValidationOptions::BlockValidationOptions(const Config &config) | BlockValidationOptions::BlockValidationOptions(const Config &config) | ||||
: excessiveBlockSize(config.GetMaxBlockSize()), checkPoW(true), | : excessiveBlockSize(config.GetMaxBlockSize()), checkPoW(true), | ||||
checkMerkleRoot(true) {} | checkMerkleRoot(true) {} | ||||
CBlockIndex *LookupBlockIndex(const BlockHash &hash) { | CBlockIndex *LookupBlockIndex(const BlockHash &hash) { | ||||
AssertLockHeld(cs_main); | AssertLockHeld(cs_main); | ||||
BlockMap::const_iterator it = g_blockman.m_block_index.find(hash); | BlockMap::const_iterator it = g_chainman.BlockIndex().find(hash); | ||||
return it == g_blockman.m_block_index.end() ? nullptr : it->second; | return it == g_chainman.BlockIndex().end() ? nullptr : it->second; | ||||
} | } | ||||
CBlockIndex *FindForkInGlobalIndex(const CChain &chain, | CBlockIndex *FindForkInGlobalIndex(const CChain &chain, | ||||
const CBlockLocator &locator) { | const CBlockLocator &locator) { | ||||
AssertLockHeld(cs_main); | AssertLockHeld(cs_main); | ||||
// Find the latest block common to locator and chain - we expect that | // Find the latest block common to locator and chain - we expect that | ||||
// locator.vHave is sorted descending by height. | // locator.vHave is sorted descending by height. | ||||
▲ Show 20 Lines • Show All 583 Lines • ▼ Show 20 Lines | CoinsViews::CoinsViews(std::string ldb_name, size_t cache_size_bytes, | ||||
: m_dbview(GetDataDir() / ldb_name, cache_size_bytes, in_memory, | : m_dbview(GetDataDir() / ldb_name, cache_size_bytes, in_memory, | ||||
should_wipe), | should_wipe), | ||||
m_catcherview(&m_dbview) {} | m_catcherview(&m_dbview) {} | ||||
void CoinsViews::InitCache() { | void CoinsViews::InitCache() { | ||||
m_cacheview = std::make_unique<CCoinsViewCache>(&m_catcherview); | m_cacheview = std::make_unique<CCoinsViewCache>(&m_catcherview); | ||||
} | } | ||||
// NOTE: for now m_blockman is set to a global, but this will be changed | CChainState::CChainState(BlockManager &blockman, | ||||
// in a future commit. | BlockHash from_snapshot_blockhash) | ||||
CChainState::CChainState(BlockHash from_snapshot_blockhash) | : m_blockman(blockman), m_from_snapshot_blockhash(from_snapshot_blockhash) { | ||||
: m_blockman(g_blockman), | } | ||||
m_from_snapshot_blockhash(from_snapshot_blockhash) {} | |||||
void CChainState::InitCoinsDB(size_t cache_size_bytes, bool in_memory, | void CChainState::InitCoinsDB(size_t cache_size_bytes, bool in_memory, | ||||
bool should_wipe, std::string leveldb_name) { | bool should_wipe, std::string leveldb_name) { | ||||
if (!m_from_snapshot_blockhash.IsNull()) { | if (!m_from_snapshot_blockhash.IsNull()) { | ||||
leveldb_name += "_" + m_from_snapshot_blockhash.ToString(); | leveldb_name += "_" + m_from_snapshot_blockhash.ToString(); | ||||
} | } | ||||
m_coins_views = std::make_unique<CoinsViews>(leveldb_name, cache_size_bytes, | m_coins_views = std::make_unique<CoinsViews>(leveldb_name, cache_size_bytes, | ||||
in_memory, should_wipe); | in_memory, should_wipe); | ||||
Show All 35 Lines | bool CChainState::IsInitialBlockDownload() const { | ||||
m_cached_finished_ibd.store(true, std::memory_order_relaxed); | m_cached_finished_ibd.store(true, std::memory_order_relaxed); | ||||
return false; | return false; | ||||
} | } | ||||
static CBlockIndex const *pindexBestForkTip = nullptr; | static CBlockIndex const *pindexBestForkTip = nullptr; | ||||
static CBlockIndex const *pindexBestForkBase = nullptr; | static CBlockIndex const *pindexBestForkBase = nullptr; | ||||
BlockMap &BlockIndex() { | BlockMap &BlockIndex() { | ||||
return g_blockman.m_block_index; | return g_chainman.m_blockman.m_block_index; | ||||
} | } | ||||
static void AlertNotify(const std::string &strMessage) { | static void AlertNotify(const std::string &strMessage) { | ||||
uiInterface.NotifyAlertChanged(); | uiInterface.NotifyAlertChanged(); | ||||
#if defined(HAVE_SYSTEM) | #if defined(HAVE_SYSTEM) | ||||
std::string strCmd = gArgs.GetArg("-alertnotify", ""); | std::string strCmd = gArgs.GetArg("-alertnotify", ""); | ||||
if (strCmd.empty()) { | if (strCmd.empty()) { | ||||
return; | return; | ||||
▲ Show 20 Lines • Show All 2,897 Lines • ▼ Show 20 Lines | if (fCheckpointsEnabled) { | ||||
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, | ||||
"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 BlockIndex(). | ||||
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, | ||||
"bad-fork-prior-to-checkpoint"); | "bad-fork-prior-to-checkpoint"); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 273 Lines • ▼ Show 20 Lines | bool ProcessNewBlockHeaders(const Config &config, | ||||
const std::vector<CBlockHeader> &headers, | const std::vector<CBlockHeader> &headers, | ||||
BlockValidationState &state, | BlockValidationState &state, | ||||
const CBlockIndex **ppindex) { | const CBlockIndex **ppindex) { | ||||
{ | { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
for (const CBlockHeader &header : headers) { | for (const CBlockHeader &header : headers) { | ||||
// Use a temp pindex instead of ppindex to avoid a const_cast | // Use a temp pindex instead of ppindex to avoid a const_cast | ||||
CBlockIndex *pindex = nullptr; | CBlockIndex *pindex = nullptr; | ||||
bool accepted = | bool accepted = g_chainman.m_blockman.AcceptBlockHeader( | ||||
g_blockman.AcceptBlockHeader(config, header, state, &pindex); | config, header, state, &pindex); | ||||
::ChainstateActive().CheckBlockIndex( | ::ChainstateActive().CheckBlockIndex( | ||||
config.GetChainParams().GetConsensus()); | config.GetChainParams().GetConsensus()); | ||||
if (!accepted) { | if (!accepted) { | ||||
return false; | return false; | ||||
} | } | ||||
if (ppindex) { | if (ppindex) { | ||||
▲ Show 20 Lines • Show All 318 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
/** | /** | ||||
* Prune a block file (modify associated database entries) | * Prune a block file (modify associated database entries) | ||||
*/ | */ | ||||
void PruneOneBlockFile(const int fileNumber) { | void PruneOneBlockFile(const int fileNumber) { | ||||
LOCK(cs_LastBlockFile); | LOCK(cs_LastBlockFile); | ||||
for (const auto &entry : g_blockman.m_block_index) { | for (const auto &entry : g_chainman.BlockIndex()) { | ||||
CBlockIndex *pindex = entry.second; | CBlockIndex *pindex = entry.second; | ||||
if (pindex->nFile == fileNumber) { | if (pindex->nFile == fileNumber) { | ||||
pindex->nStatus = pindex->nStatus.withData(false).withUndo(false); | pindex->nStatus = pindex->nStatus.withData(false).withUndo(false); | ||||
pindex->nFile = 0; | pindex->nFile = 0; | ||||
pindex->nDataPos = 0; | pindex->nDataPos = 0; | ||||
pindex->nUndoPos = 0; | pindex->nUndoPos = 0; | ||||
setDirtyBlockIndex.insert(pindex); | setDirtyBlockIndex.insert(pindex); | ||||
// Prune from m_blocks_unlinked -- any block we prune would have | // Prune from m_blocks_unlinked -- any block we prune would have | ||||
// to be downloaded again in order to consider its chain, at which | // to be downloaded again in order to consider its chain, at which | ||||
// point it would be considered as a candidate for | // point it would be considered as a candidate for | ||||
// m_blocks_unlinked or setBlockIndexCandidates. | // m_blocks_unlinked or setBlockIndexCandidates. | ||||
auto range = | auto range = g_chainman.m_blockman.m_blocks_unlinked.equal_range( | ||||
g_blockman.m_blocks_unlinked.equal_range(pindex->pprev); | pindex->pprev); | ||||
while (range.first != range.second) { | while (range.first != range.second) { | ||||
std::multimap<CBlockIndex *, CBlockIndex *>::iterator _it = | std::multimap<CBlockIndex *, CBlockIndex *>::iterator _it = | ||||
range.first; | range.first; | ||||
range.first++; | range.first++; | ||||
if (_it->second == pindex) { | if (_it->second == pindex) { | ||||
g_blockman.m_blocks_unlinked.erase(_it); | g_chainman.m_blockman.m_blocks_unlinked.erase(_it); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
vinfoBlockFile[fileNumber].SetNull(); | vinfoBlockFile[fileNumber].SetNull(); | ||||
setDirtyFileInfo.insert(fileNumber); | setDirtyFileInfo.insert(fileNumber); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 241 Lines • ▼ Show 20 Lines | for (const BlockMap::value_type &entry : m_block_index) { | ||||
delete entry.second; | delete entry.second; | ||||
} | } | ||||
m_block_index.clear(); | m_block_index.clear(); | ||||
} | } | ||||
static bool LoadBlockIndexDB(const Consensus::Params ¶ms) | static bool LoadBlockIndexDB(const Consensus::Params ¶ms) | ||||
EXCLUSIVE_LOCKS_REQUIRED(cs_main) { | EXCLUSIVE_LOCKS_REQUIRED(cs_main) { | ||||
if (!g_blockman.LoadBlockIndex( | if (!g_chainman.m_blockman.LoadBlockIndex( | ||||
params, *pblocktree, | params, *pblocktree, | ||||
::ChainstateActive().setBlockIndexCandidates)) { | ::ChainstateActive().setBlockIndexCandidates)) { | ||||
return false; | return false; | ||||
} | } | ||||
// Load block file info | // Load block file info | ||||
pblocktree->ReadLastBlockFile(nLastBlockFile); | pblocktree->ReadLastBlockFile(nLastBlockFile); | ||||
vinfoBlockFile.resize(nLastBlockFile + 1); | vinfoBlockFile.resize(nLastBlockFile + 1); | ||||
Show All 13 Lines | for (int nFile = nLastBlockFile + 1; true; nFile++) { | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
// Check presence of blk files | // Check presence of blk files | ||||
LogPrintf("Checking all blk files are present...\n"); | LogPrintf("Checking all blk files are present...\n"); | ||||
std::set<int> setBlkDataFiles; | std::set<int> setBlkDataFiles; | ||||
for (const std::pair<const BlockHash, CBlockIndex *> &item : | for (const std::pair<const BlockHash, CBlockIndex *> &item : | ||||
g_blockman.m_block_index) { | g_chainman.BlockIndex()) { | ||||
CBlockIndex *pindex = item.second; | CBlockIndex *pindex = item.second; | ||||
if (pindex->nStatus.hasData()) { | if (pindex->nStatus.hasData()) { | ||||
setBlkDataFiles.insert(pindex->nFile); | setBlkDataFiles.insert(pindex->nFile); | ||||
} | } | ||||
} | } | ||||
for (const int i : setBlkDataFiles) { | for (const int i : setBlkDataFiles) { | ||||
FlatFilePos pos(i, 0); | FlatFilePos pos(i, 0); | ||||
▲ Show 20 Lines • Show All 359 Lines • ▼ Show 20 Lines | void CChainState::UnloadBlockIndex() { | ||||
m_finalizedBlockIndex = nullptr; | m_finalizedBlockIndex = nullptr; | ||||
} | } | ||||
// May NOT be used after any connections are up as much | // May NOT be used after any connections are up as much | ||||
// of the peer-processing logic assumes a consistent | // of the peer-processing logic assumes a consistent | ||||
// block index state | // block index state | ||||
void UnloadBlockIndex() { | void UnloadBlockIndex() { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
::ChainActive().SetTip(nullptr); | g_chainman.Unload(); | ||||
g_blockman.Unload(); | |||||
pindexBestInvalid = nullptr; | pindexBestInvalid = nullptr; | ||||
pindexBestParked = nullptr; | pindexBestParked = nullptr; | ||||
pindexBestHeader = nullptr; | pindexBestHeader = nullptr; | ||||
pindexBestForkTip = nullptr; | pindexBestForkTip = nullptr; | ||||
pindexBestForkBase = nullptr; | pindexBestForkBase = nullptr; | ||||
ResetASERTAnchorBlockCache(); | ResetASERTAnchorBlockCache(); | ||||
g_mempool.clear(); | g_mempool.clear(); | ||||
vinfoBlockFile.clear(); | vinfoBlockFile.clear(); | ||||
nLastBlockFile = 0; | nLastBlockFile = 0; | ||||
setDirtyBlockIndex.clear(); | setDirtyBlockIndex.clear(); | ||||
setDirtyFileInfo.clear(); | setDirtyFileInfo.clear(); | ||||
fHavePruned = false; | fHavePruned = false; | ||||
::ChainstateActive().UnloadBlockIndex(); | |||||
} | } | ||||
bool LoadBlockIndex(const Consensus::Params ¶ms) { | bool LoadBlockIndex(const Consensus::Params ¶ms) { | ||||
// Load block index from databases | // Load block index from databases | ||||
bool needs_init = fReindex; | bool needs_init = fReindex; | ||||
if (!fReindex) { | if (!fReindex) { | ||||
bool ret = LoadBlockIndexDB(params); | bool ret = LoadBlockIndexDB(params); | ||||
if (!ret) { | if (!ret) { | ||||
return false; | return false; | ||||
} | } | ||||
needs_init = g_blockman.m_block_index.empty(); | needs_init = g_chainman.m_blockman.m_block_index.empty(); | ||||
} | } | ||||
if (needs_init) { | if (needs_init) { | ||||
// Everything here is for *new* reindex/DBs. Thus, though | // Everything here is for *new* reindex/DBs. Thus, though | ||||
// LoadBlockIndexDB may have set fReindex if we shut down | // LoadBlockIndexDB may have set fReindex if we shut down | ||||
// mid-reindex previously, we don't check fReindex and | // mid-reindex previously, we don't check fReindex and | ||||
// instead only check it prior to LoadBlockIndexDB to set | // instead only check it prior to LoadBlockIndexDB to set | ||||
// needs_init. | // needs_init. | ||||
▲ Show 20 Lines • Show All 741 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
class CMainCleanup { | class CMainCleanup { | ||||
public: | public: | ||||
CMainCleanup() {} | CMainCleanup() {} | ||||
~CMainCleanup() { | ~CMainCleanup() { | ||||
// block headers | // block headers | ||||
for (const std::pair<const BlockHash, CBlockIndex *> &it : | for (const std::pair<const BlockHash, CBlockIndex *> &it : | ||||
g_blockman.m_block_index) { | g_chainman.BlockIndex()) { | ||||
delete it.second; | delete it.second; | ||||
} | } | ||||
g_blockman.m_block_index.clear(); | g_chainman.BlockIndex().clear(); | ||||
} | } | ||||
}; | }; | ||||
static CMainCleanup instance_of_cmaincleanup; | static CMainCleanup instance_of_cmaincleanup; | ||||
std::optional<BlockHash> ChainstateManager::SnapshotBlockhash() const { | std::optional<BlockHash> ChainstateManager::SnapshotBlockhash() const { | ||||
if (m_active_chainstate != nullptr) { | if (m_active_chainstate != nullptr) { | ||||
// If a snapshot chainstate exists, it will always be our active. | // If a snapshot chainstate exists, it will always be our active. | ||||
return m_active_chainstate->m_from_snapshot_blockhash; | return m_active_chainstate->m_from_snapshot_blockhash; | ||||
Show All 20 Lines | ChainstateManager::InitializeChainstate(const BlockHash &snapshot_blockhash) { | ||||
bool is_snapshot = !snapshot_blockhash.IsNull(); | bool is_snapshot = !snapshot_blockhash.IsNull(); | ||||
std::unique_ptr<CChainState> &to_modify = | std::unique_ptr<CChainState> &to_modify = | ||||
is_snapshot ? m_snapshot_chainstate : m_ibd_chainstate; | is_snapshot ? m_snapshot_chainstate : m_ibd_chainstate; | ||||
if (to_modify) { | if (to_modify) { | ||||
throw std::logic_error("should not be overwriting a chainstate"); | throw std::logic_error("should not be overwriting a chainstate"); | ||||
} | } | ||||
to_modify.reset(new CChainState(snapshot_blockhash)); | to_modify.reset(new CChainState(m_blockman, snapshot_blockhash)); | ||||
// Snapshot chainstates and initial IBD chaintates always become active. | // Snapshot chainstates and initial IBD chaintates always become active. | ||||
if (is_snapshot || (!is_snapshot && !m_active_chainstate)) { | if (is_snapshot || (!is_snapshot && !m_active_chainstate)) { | ||||
LogPrintf("Switching active chainstate to %s\n", to_modify->ToString()); | LogPrintf("Switching active chainstate to %s\n", to_modify->ToString()); | ||||
m_active_chainstate = to_modify.get(); | m_active_chainstate = to_modify.get(); | ||||
} else { | } else { | ||||
throw std::logic_error("unexpected chainstate activation"); | throw std::logic_error("unexpected chainstate activation"); | ||||
} | } | ||||
Show All 23 Lines | bool ChainstateManager::IsBackgroundIBD(CChainState *chainstate) const { | ||||
return (m_snapshot_chainstate && chainstate == m_ibd_chainstate.get()); | return (m_snapshot_chainstate && chainstate == m_ibd_chainstate.get()); | ||||
} | } | ||||
void ChainstateManager::Unload() { | void ChainstateManager::Unload() { | ||||
for (CChainState *chainstate : this->GetAll()) { | for (CChainState *chainstate : this->GetAll()) { | ||||
chainstate->m_chain.SetTip(nullptr); | chainstate->m_chain.SetTip(nullptr); | ||||
chainstate->UnloadBlockIndex(); | chainstate->UnloadBlockIndex(); | ||||
} | } | ||||
m_blockman.Unload(); | |||||
} | } | ||||
void ChainstateManager::Reset() { | void ChainstateManager::Reset() { | ||||
m_ibd_chainstate.reset(); | m_ibd_chainstate.reset(); | ||||
m_snapshot_chainstate.reset(); | m_snapshot_chainstate.reset(); | ||||
m_active_chainstate = nullptr; | m_active_chainstate = nullptr; | ||||
m_snapshot_validated = false; | m_snapshot_validated = false; | ||||
} | } |