mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-02-22 15:04:44 +01:00
Merge bitcoin/bitcoin#24515: Only load BlockMan in BlockMan member functions
f865cf8ded
Add and use BlockManager::GetAllBlockIndices (Carl Dong)28ba0313ea
Add and use CBlockIndexHeightOnlyComparator (Carl Dong)12eb05df63
move-only: Move CBlockIndexWorkComparator to blockstorage (Carl Dong)c600ee3816
Only load BlockMan in BlockMan member functions (Carl Dong)42e56d9b18
style-only: No need for std::pair for vSortedByHeight (Carl Dong)3bbb6fea05
style-only: Various blockstorage.cpp cleanups (Carl Dong)5be9ee3c54
refactor: more const annotations for uses of CBlockIndex* (Anthony Towns) Pull request description: The only important commit is "Only load BlockMan in BlockMan member functions", everything else is all just small style changes. Here's the commit message, reproduced: ``` This commit effectively splits the "load block index itself" logic from "derive Chainstate variables from loaded block index" logic. This means that BlockManager::LoadBlockIndex{,DB} will only load what's relevant to the BlockManager. ``` ACKs for top commit: ajtowns: ACKf865cf8ded
; code review only MarcoFalke: review ACKf865cf8ded
🗂 Tree-SHA512: 7b204d782834e06fd7329d022e2ae860181b4e8105c33bfb928539a4ec24161dc7438a9c4d4ee279dcad77de310c160b997bb8aa18923243d0fd55ccf4ad7c3a
This commit is contained in:
commit
601bfc417d
10 changed files with 155 additions and 130 deletions
|
@ -277,7 +277,7 @@ bool CoinStatsIndex::Rewind(const CBlockIndex* current_tip, const CBlockIndex* n
|
||||||
|
|
||||||
{
|
{
|
||||||
LOCK(cs_main);
|
LOCK(cs_main);
|
||||||
CBlockIndex* iter_tip{m_chainstate->m_blockman.LookupBlockIndex(current_tip->GetBlockHash())};
|
const CBlockIndex* iter_tip{m_chainstate->m_blockman.LookupBlockIndex(current_tip->GetBlockHash())};
|
||||||
const auto& consensus_params{Params().GetConsensus()};
|
const auto& consensus_params{Params().GetConsensus()};
|
||||||
|
|
||||||
do {
|
do {
|
||||||
|
|
|
@ -28,10 +28,45 @@ bool fHavePruned = false;
|
||||||
bool fPruneMode = false;
|
bool fPruneMode = false;
|
||||||
uint64_t nPruneTarget = 0;
|
uint64_t nPruneTarget = 0;
|
||||||
|
|
||||||
|
bool CBlockIndexWorkComparator::operator()(const CBlockIndex* pa, const CBlockIndex* pb) const
|
||||||
|
{
|
||||||
|
// First sort by most total work, ...
|
||||||
|
if (pa->nChainWork > pb->nChainWork) return false;
|
||||||
|
if (pa->nChainWork < pb->nChainWork) return true;
|
||||||
|
|
||||||
|
// ... then by earliest time received, ...
|
||||||
|
if (pa->nSequenceId < pb->nSequenceId) return false;
|
||||||
|
if (pa->nSequenceId > pb->nSequenceId) return true;
|
||||||
|
|
||||||
|
// Use pointer address as tie breaker (should only happen with blocks
|
||||||
|
// loaded from disk, as those all have id 0).
|
||||||
|
if (pa < pb) return false;
|
||||||
|
if (pa > pb) return true;
|
||||||
|
|
||||||
|
// Identical blocks.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CBlockIndexHeightOnlyComparator::operator()(const CBlockIndex* pa, const CBlockIndex* pb) const
|
||||||
|
{
|
||||||
|
return pa->nHeight < pb->nHeight;
|
||||||
|
}
|
||||||
|
|
||||||
static FILE* OpenUndoFile(const FlatFilePos& pos, bool fReadOnly = false);
|
static FILE* OpenUndoFile(const FlatFilePos& pos, bool fReadOnly = false);
|
||||||
static FlatFileSeq BlockFileSeq();
|
static FlatFileSeq BlockFileSeq();
|
||||||
static FlatFileSeq UndoFileSeq();
|
static FlatFileSeq UndoFileSeq();
|
||||||
|
|
||||||
|
std::vector<CBlockIndex*> BlockManager::GetAllBlockIndices()
|
||||||
|
{
|
||||||
|
AssertLockHeld(cs_main);
|
||||||
|
std::vector<CBlockIndex*> rv;
|
||||||
|
rv.reserve(m_block_index.size());
|
||||||
|
for (auto& [_, block_index] : m_block_index) {
|
||||||
|
rv.push_back(&block_index);
|
||||||
|
}
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
CBlockIndex* BlockManager::LookupBlockIndex(const uint256& hash)
|
CBlockIndex* BlockManager::LookupBlockIndex(const uint256& hash)
|
||||||
{
|
{
|
||||||
AssertLockHeld(cs_main);
|
AssertLockHeld(cs_main);
|
||||||
|
@ -203,8 +238,7 @@ CBlockIndex* BlockManager::InsertBlockIndex(const uint256& hash)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return existing or create new
|
const auto [mi, inserted]{m_block_index.try_emplace(hash)};
|
||||||
auto [mi, inserted] = m_block_index.try_emplace(hash);
|
|
||||||
CBlockIndex* pindex = &(*mi).second;
|
CBlockIndex* pindex = &(*mi).second;
|
||||||
if (inserted) {
|
if (inserted) {
|
||||||
pindex->phashBlock = &((*mi).first);
|
pindex->phashBlock = &((*mi).first);
|
||||||
|
@ -212,46 +246,19 @@ CBlockIndex* BlockManager::InsertBlockIndex(const uint256& hash)
|
||||||
return pindex;
|
return pindex;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BlockManager::LoadBlockIndex(
|
bool BlockManager::LoadBlockIndex(const Consensus::Params& consensus_params)
|
||||||
const Consensus::Params& consensus_params,
|
|
||||||
ChainstateManager& chainman)
|
|
||||||
{
|
{
|
||||||
if (!m_block_tree_db->LoadBlockIndexGuts(consensus_params, [this](const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { return this->InsertBlockIndex(hash); })) {
|
if (!m_block_tree_db->LoadBlockIndexGuts(consensus_params, [this](const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { return this->InsertBlockIndex(hash); })) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate nChainWork
|
// Calculate nChainWork
|
||||||
std::vector<std::pair<int, CBlockIndex*>> vSortedByHeight;
|
std::vector<CBlockIndex*> vSortedByHeight{GetAllBlockIndices()};
|
||||||
vSortedByHeight.reserve(m_block_index.size());
|
std::sort(vSortedByHeight.begin(), vSortedByHeight.end(),
|
||||||
for (auto& [_, block_index] : m_block_index) {
|
CBlockIndexHeightOnlyComparator());
|
||||||
CBlockIndex* pindex = &block_index;
|
|
||||||
vSortedByHeight.push_back(std::make_pair(pindex->nHeight, pindex));
|
|
||||||
}
|
|
||||||
sort(vSortedByHeight.begin(), vSortedByHeight.end());
|
|
||||||
|
|
||||||
// Find start of assumed-valid region.
|
for (CBlockIndex* pindex : vSortedByHeight) {
|
||||||
int first_assumed_valid_height = std::numeric_limits<int>::max();
|
|
||||||
|
|
||||||
for (const auto& [height, block] : vSortedByHeight) {
|
|
||||||
if (block->IsAssumedValid()) {
|
|
||||||
auto chainstates = chainman.GetAll();
|
|
||||||
|
|
||||||
// If we encounter an assumed-valid block index entry, ensure that we have
|
|
||||||
// one chainstate that tolerates assumed-valid entries and another that does
|
|
||||||
// not (i.e. the background validation chainstate), since assumed-valid
|
|
||||||
// entries should always be pending validation by a fully-validated chainstate.
|
|
||||||
auto any_chain = [&](auto fnc) { return std::any_of(chainstates.cbegin(), chainstates.cend(), fnc); };
|
|
||||||
assert(any_chain([](auto chainstate) { return chainstate->reliesOnAssumedValid(); }));
|
|
||||||
assert(any_chain([](auto chainstate) { return !chainstate->reliesOnAssumedValid(); }));
|
|
||||||
|
|
||||||
first_assumed_valid_height = height;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const std::pair<int, CBlockIndex*>& item : vSortedByHeight) {
|
|
||||||
if (ShutdownRequested()) return false;
|
if (ShutdownRequested()) return false;
|
||||||
CBlockIndex* pindex = item.second;
|
|
||||||
pindex->nChainWork = (pindex->pprev ? pindex->pprev->nChainWork : 0) + GetBlockProof(*pindex);
|
pindex->nChainWork = (pindex->pprev ? pindex->pprev->nChainWork : 0) + GetBlockProof(*pindex);
|
||||||
pindex->nTimeMax = (pindex->pprev ? std::max(pindex->pprev->nTimeMax, pindex->nTime) : pindex->nTime);
|
pindex->nTimeMax = (pindex->pprev ? std::max(pindex->pprev->nTimeMax, pindex->nTime) : pindex->nTime);
|
||||||
|
|
||||||
|
@ -275,43 +282,6 @@ bool BlockManager::LoadBlockIndex(
|
||||||
pindex->nStatus |= BLOCK_FAILED_CHILD;
|
pindex->nStatus |= BLOCK_FAILED_CHILD;
|
||||||
m_dirty_blockindex.insert(pindex);
|
m_dirty_blockindex.insert(pindex);
|
||||||
}
|
}
|
||||||
if (pindex->IsAssumedValid() ||
|
|
||||||
(pindex->IsValid(BLOCK_VALID_TRANSACTIONS) &&
|
|
||||||
(pindex->HaveTxsDownloaded() || pindex->pprev == nullptr))) {
|
|
||||||
|
|
||||||
// Fill each chainstate's block candidate set. Only add assumed-valid
|
|
||||||
// blocks to the tip candidate set if the chainstate is allowed to rely on
|
|
||||||
// assumed-valid blocks.
|
|
||||||
//
|
|
||||||
// If all setBlockIndexCandidates contained the assumed-valid blocks, the
|
|
||||||
// background chainstate's ActivateBestChain() call would add assumed-valid
|
|
||||||
// blocks to the chain (based on how FindMostWorkChain() works). Obviously
|
|
||||||
// we don't want this since the purpose of the background validation chain
|
|
||||||
// is to validate assued-valid blocks.
|
|
||||||
//
|
|
||||||
// Note: This is considering all blocks whose height is greater or equal to
|
|
||||||
// the first assumed-valid block to be assumed-valid blocks, and excluding
|
|
||||||
// them from the background chainstate's setBlockIndexCandidates set. This
|
|
||||||
// does mean that some blocks which are not technically assumed-valid
|
|
||||||
// (later blocks on a fork beginning before the first assumed-valid block)
|
|
||||||
// might not get added to the background chainstate, but this is ok,
|
|
||||||
// because they will still be attached to the active chainstate if they
|
|
||||||
// actually contain more work.
|
|
||||||
//
|
|
||||||
// Instead of this height-based approach, an earlier attempt was made at
|
|
||||||
// detecting "holistically" whether the block index under consideration
|
|
||||||
// relied on an assumed-valid ancestor, but this proved to be too slow to
|
|
||||||
// be practical.
|
|
||||||
for (CChainState* chainstate : chainman.GetAll()) {
|
|
||||||
if (chainstate->reliesOnAssumedValid() ||
|
|
||||||
pindex->nHeight < first_assumed_valid_height) {
|
|
||||||
chainstate->setBlockIndexCandidates.insert(pindex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (pindex->nStatus & BLOCK_FAILED_MASK && (!chainman.m_best_invalid || pindex->nChainWork > chainman.m_best_invalid->nChainWork)) {
|
|
||||||
chainman.m_best_invalid = pindex;
|
|
||||||
}
|
|
||||||
if (pindex->pprev) {
|
if (pindex->pprev) {
|
||||||
pindex->BuildSkip();
|
pindex->BuildSkip();
|
||||||
}
|
}
|
||||||
|
@ -355,9 +325,9 @@ bool BlockManager::WriteBlockIndexDB()
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BlockManager::LoadBlockIndexDB(ChainstateManager& chainman)
|
bool BlockManager::LoadBlockIndexDB()
|
||||||
{
|
{
|
||||||
if (!LoadBlockIndex(::Params().GetConsensus(), chainman)) {
|
if (!LoadBlockIndex(::Params().GetConsensus())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -382,9 +352,8 @@ bool BlockManager::LoadBlockIndexDB(ChainstateManager& chainman)
|
||||||
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 auto& [_, block_index] : m_block_index) {
|
for (const auto& [_, block_index] : m_block_index) {
|
||||||
const CBlockIndex* pindex = &block_index;
|
if (block_index.nStatus & BLOCK_HAVE_DATA) {
|
||||||
if (pindex->nStatus & BLOCK_HAVE_DATA) {
|
setBlkDataFiles.insert(block_index.nFile);
|
||||||
setBlkDataFiles.insert(pindex->nFile);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (std::set<int>::iterator it = setBlkDataFiles.begin(); it != setBlkDataFiles.end(); it++) {
|
for (std::set<int>::iterator it = setBlkDataFiles.begin(); it != setBlkDataFiles.end(); it++) {
|
||||||
|
@ -408,13 +377,13 @@ bool BlockManager::LoadBlockIndexDB(ChainstateManager& chainman)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
CBlockIndex* BlockManager::GetLastCheckpoint(const CCheckpointData& data)
|
const CBlockIndex* BlockManager::GetLastCheckpoint(const CCheckpointData& data)
|
||||||
{
|
{
|
||||||
const MapCheckpoints& checkpoints = data.mapCheckpoints;
|
const MapCheckpoints& checkpoints = data.mapCheckpoints;
|
||||||
|
|
||||||
for (const MapCheckpoints::value_type& i : reverse_iterate(checkpoints)) {
|
for (const MapCheckpoints::value_type& i : reverse_iterate(checkpoints)) {
|
||||||
const uint256& hash = i.second;
|
const uint256& hash = i.second;
|
||||||
CBlockIndex* pindex = LookupBlockIndex(hash);
|
const CBlockIndex* pindex = LookupBlockIndex(hash);
|
||||||
if (pindex) {
|
if (pindex) {
|
||||||
return pindex;
|
return pindex;
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,6 +62,11 @@ struct CBlockIndexWorkComparator {
|
||||||
bool operator()(const CBlockIndex* pa, const CBlockIndex* pb) const;
|
bool operator()(const CBlockIndex* pa, const CBlockIndex* pb) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct CBlockIndexHeightOnlyComparator {
|
||||||
|
/* Only compares the height of two block indices, doesn't try to tie-break */
|
||||||
|
bool operator()(const CBlockIndex* pa, const CBlockIndex* pb) const;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maintains a tree of blocks (stored in `m_block_index`) which is consulted
|
* Maintains a tree of blocks (stored in `m_block_index`) which is consulted
|
||||||
* to determine where the most-work tip is.
|
* to determine where the most-work tip is.
|
||||||
|
@ -118,6 +123,8 @@ private:
|
||||||
public:
|
public:
|
||||||
BlockMap m_block_index GUARDED_BY(cs_main);
|
BlockMap m_block_index GUARDED_BY(cs_main);
|
||||||
|
|
||||||
|
std::vector<CBlockIndex*> GetAllBlockIndices() EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* All pairs A->B, where A (or one of its ancestors) misses transactions, but B has transactions.
|
* All pairs A->B, where A (or one of its ancestors) misses transactions, but B has transactions.
|
||||||
* Pruned nodes may have entries where B is missing data.
|
* Pruned nodes may have entries where B is missing data.
|
||||||
|
@ -127,16 +134,15 @@ public:
|
||||||
std::unique_ptr<CBlockTreeDB> m_block_tree_db GUARDED_BY(::cs_main);
|
std::unique_ptr<CBlockTreeDB> m_block_tree_db GUARDED_BY(::cs_main);
|
||||||
|
|
||||||
bool WriteBlockIndexDB() EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
|
bool WriteBlockIndexDB() EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
|
||||||
bool LoadBlockIndexDB(ChainstateManager& chainman) EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
|
bool LoadBlockIndexDB() EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load the blocktree off disk and into memory. Populate certain metadata
|
* Load the blocktree off disk and into memory. Populate certain metadata
|
||||||
* per index entry (nStatus, nChainWork, nTimeMax, etc.) as well as peripheral
|
* per index entry (nStatus, nChainWork, nTimeMax, etc.) as well as peripheral
|
||||||
* collections like m_dirty_blockindex.
|
* collections like m_dirty_blockindex.
|
||||||
*/
|
*/
|
||||||
bool LoadBlockIndex(
|
bool LoadBlockIndex(const Consensus::Params& consensus_params)
|
||||||
const Consensus::Params& consensus_params,
|
EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
||||||
ChainstateManager& chainman) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
|
||||||
|
|
||||||
/** Clear all data members. */
|
/** Clear all data members. */
|
||||||
void Unload() EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
void Unload() EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
||||||
|
@ -163,7 +169,7 @@ public:
|
||||||
uint64_t CalculateCurrentUsage();
|
uint64_t CalculateCurrentUsage();
|
||||||
|
|
||||||
//! Returns last CBlockIndex* that is a checkpoint
|
//! Returns last CBlockIndex* that is a checkpoint
|
||||||
CBlockIndex* GetLastCheckpoint(const CCheckpointData& data) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
const CBlockIndex* GetLastCheckpoint(const CCheckpointData& data) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
||||||
|
|
||||||
~BlockManager()
|
~BlockManager()
|
||||||
{
|
{
|
||||||
|
|
|
@ -490,7 +490,7 @@ public:
|
||||||
{
|
{
|
||||||
LOCK(cs_main);
|
LOCK(cs_main);
|
||||||
const CChainState& active = Assert(m_node.chainman)->ActiveChainstate();
|
const CChainState& active = Assert(m_node.chainman)->ActiveChainstate();
|
||||||
if (CBlockIndex* fork = active.FindForkInGlobalIndex(locator)) {
|
if (const CBlockIndex* fork = active.FindForkInGlobalIndex(locator)) {
|
||||||
return fork->nHeight;
|
return fork->nHeight;
|
||||||
}
|
}
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
@ -557,7 +557,7 @@ public:
|
||||||
// used to limit the range, and passing min_height that's too low or
|
// used to limit the range, and passing min_height that's too low or
|
||||||
// max_height that's too high will not crash or change the result.
|
// max_height that's too high will not crash or change the result.
|
||||||
LOCK(::cs_main);
|
LOCK(::cs_main);
|
||||||
if (CBlockIndex* block = chainman().m_blockman.LookupBlockIndex(block_hash)) {
|
if (const CBlockIndex* block = chainman().m_blockman.LookupBlockIndex(block_hash)) {
|
||||||
if (max_height && block->nHeight >= *max_height) block = block->GetAncestor(*max_height);
|
if (max_height && block->nHeight >= *max_height) block = block->GetAncestor(*max_height);
|
||||||
for (; block->nStatus & BLOCK_HAVE_DATA; block = block->pprev) {
|
for (; block->nStatus & BLOCK_HAVE_DATA; block = block->pprev) {
|
||||||
// Check pprev to not segfault if min_height is too low
|
// Check pprev to not segfault if min_height is too low
|
||||||
|
|
|
@ -50,7 +50,7 @@ void RegenerateCommitments(CBlock& block, ChainstateManager& chainman)
|
||||||
tx.vout.erase(tx.vout.begin() + GetWitnessCommitmentIndex(block));
|
tx.vout.erase(tx.vout.begin() + GetWitnessCommitmentIndex(block));
|
||||||
block.vtx.at(0) = MakeTransactionRef(tx);
|
block.vtx.at(0) = MakeTransactionRef(tx);
|
||||||
|
|
||||||
CBlockIndex* prev_block = WITH_LOCK(::cs_main, return chainman.m_blockman.LookupBlockIndex(block.hashPrevBlock));
|
const CBlockIndex* prev_block = WITH_LOCK(::cs_main, return chainman.m_blockman.LookupBlockIndex(block.hashPrevBlock));
|
||||||
GenerateCoinbaseCommitment(block, prev_block, Params().GetConsensus());
|
GenerateCoinbaseCommitment(block, prev_block, Params().GetConsensus());
|
||||||
|
|
||||||
block.hashMerkleRoot = BlockMerkleRoot(block);
|
block.hashMerkleRoot = BlockMerkleRoot(block);
|
||||||
|
|
|
@ -284,8 +284,8 @@ static bool rest_block(const std::any& context,
|
||||||
return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + hashStr);
|
return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + hashStr);
|
||||||
|
|
||||||
CBlock block;
|
CBlock block;
|
||||||
CBlockIndex* pblockindex = nullptr;
|
const CBlockIndex* pblockindex = nullptr;
|
||||||
CBlockIndex* tip = nullptr;
|
const CBlockIndex* tip = nullptr;
|
||||||
{
|
{
|
||||||
ChainstateManager* maybe_chainman = GetChainman(context, req);
|
ChainstateManager* maybe_chainman = GetChainman(context, req);
|
||||||
if (!maybe_chainman) return false;
|
if (!maybe_chainman) return false;
|
||||||
|
|
|
@ -104,7 +104,8 @@ static int ComputeNextBlockAndDepth(const CBlockIndex* tip, const CBlockIndex* b
|
||||||
return blockindex == tip ? 1 : -1;
|
return blockindex == tip ? 1 : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
CBlockIndex* ParseHashOrHeight(const UniValue& param, ChainstateManager& chainman) {
|
static const CBlockIndex* ParseHashOrHeight(const UniValue& param, ChainstateManager& chainman)
|
||||||
|
{
|
||||||
LOCK(::cs_main);
|
LOCK(::cs_main);
|
||||||
CChain& active_chain = chainman.ActiveChain();
|
CChain& active_chain = chainman.ActiveChain();
|
||||||
|
|
||||||
|
@ -121,7 +122,7 @@ CBlockIndex* ParseHashOrHeight(const UniValue& param, ChainstateManager& chainma
|
||||||
return active_chain[height];
|
return active_chain[height];
|
||||||
} else {
|
} else {
|
||||||
const uint256 hash{ParseHashV(param, "hash_or_height")};
|
const uint256 hash{ParseHashV(param, "hash_or_height")};
|
||||||
CBlockIndex* pindex = chainman.m_blockman.LookupBlockIndex(hash);
|
const CBlockIndex* pindex = chainman.m_blockman.LookupBlockIndex(hash);
|
||||||
|
|
||||||
if (!pindex) {
|
if (!pindex) {
|
||||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
|
||||||
|
@ -488,7 +489,7 @@ static RPCHelpMan getblockhash()
|
||||||
if (nHeight < 0 || nHeight > active_chain.Height())
|
if (nHeight < 0 || nHeight > active_chain.Height())
|
||||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range");
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range");
|
||||||
|
|
||||||
CBlockIndex* pblockindex = active_chain[nHeight];
|
const CBlockIndex* pblockindex = active_chain[nHeight];
|
||||||
return pblockindex->GetBlockHash().GetHex();
|
return pblockindex->GetBlockHash().GetHex();
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -766,7 +767,7 @@ static RPCHelpMan pruneblockchain()
|
||||||
// too low to be a block time (corresponds to timestamp from Sep 2001).
|
// too low to be a block time (corresponds to timestamp from Sep 2001).
|
||||||
if (heightParam > 1000000000) {
|
if (heightParam > 1000000000) {
|
||||||
// Add a 2 hour buffer to include blocks which might have had old timestamps
|
// Add a 2 hour buffer to include blocks which might have had old timestamps
|
||||||
CBlockIndex* pindex = active_chain.FindEarliestAtLeast(heightParam - TIMESTAMP_WINDOW, 0);
|
const CBlockIndex* pindex = active_chain.FindEarliestAtLeast(heightParam - TIMESTAMP_WINDOW, 0);
|
||||||
if (!pindex) {
|
if (!pindex) {
|
||||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Could not find block with at least the specified timestamp.");
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "Could not find block with at least the specified timestamp.");
|
||||||
}
|
}
|
||||||
|
@ -860,7 +861,7 @@ static RPCHelpMan gettxoutsetinfo()
|
||||||
{
|
{
|
||||||
UniValue ret(UniValue::VOBJ);
|
UniValue ret(UniValue::VOBJ);
|
||||||
|
|
||||||
CBlockIndex* pindex{nullptr};
|
const CBlockIndex* pindex{nullptr};
|
||||||
const CoinStatsHashType hash_type{request.params[0].isNull() ? CoinStatsHashType::HASH_SERIALIZED : ParseHashType(request.params[0].get_str())};
|
const CoinStatsHashType hash_type{request.params[0].isNull() ? CoinStatsHashType::HASH_SERIALIZED : ParseHashType(request.params[0].get_str())};
|
||||||
CCoinsStats stats{hash_type};
|
CCoinsStats stats{hash_type};
|
||||||
stats.index_requested = request.params[2].isNull() || request.params[2].get_bool();
|
stats.index_requested = request.params[2].isNull() || request.params[2].get_bool();
|
||||||
|
@ -1764,7 +1765,7 @@ static RPCHelpMan getblockstats()
|
||||||
{
|
{
|
||||||
ChainstateManager& chainman = EnsureAnyChainman(request.context);
|
ChainstateManager& chainman = EnsureAnyChainman(request.context);
|
||||||
LOCK(cs_main);
|
LOCK(cs_main);
|
||||||
CBlockIndex* pindex{ParseHashOrHeight(request.params[0], chainman)};
|
const CBlockIndex* pindex{ParseHashOrHeight(request.params[0], chainman)};
|
||||||
CHECK_NONFATAL(pindex != nullptr);
|
CHECK_NONFATAL(pindex != nullptr);
|
||||||
|
|
||||||
std::set<std::string> stats;
|
std::set<std::string> stats;
|
||||||
|
@ -2124,7 +2125,7 @@ static RPCHelpMan scantxoutset()
|
||||||
g_should_abort_scan = false;
|
g_should_abort_scan = false;
|
||||||
int64_t count = 0;
|
int64_t count = 0;
|
||||||
std::unique_ptr<CCoinsViewCursor> pcursor;
|
std::unique_ptr<CCoinsViewCursor> pcursor;
|
||||||
CBlockIndex* tip;
|
const CBlockIndex* tip;
|
||||||
NodeContext& node = EnsureAnyNodeContext(request.context);
|
NodeContext& node = EnsureAnyNodeContext(request.context);
|
||||||
{
|
{
|
||||||
ChainstateManager& chainman = EnsureChainman(node);
|
ChainstateManager& chainman = EnsureChainman(node);
|
||||||
|
@ -2312,7 +2313,7 @@ UniValue CreateUTXOSnapshot(
|
||||||
{
|
{
|
||||||
std::unique_ptr<CCoinsViewCursor> pcursor;
|
std::unique_ptr<CCoinsViewCursor> pcursor;
|
||||||
CCoinsStats stats{CoinStatsHashType::HASH_SERIALIZED};
|
CCoinsStats stats{CoinStatsHashType::HASH_SERIALIZED};
|
||||||
CBlockIndex* tip;
|
const CBlockIndex* tip;
|
||||||
|
|
||||||
{
|
{
|
||||||
// We need to lock cs_main to ensure that the coinsdb isn't written to
|
// We need to lock cs_main to ensure that the coinsdb isn't written to
|
||||||
|
|
|
@ -67,7 +67,7 @@ static void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue&
|
||||||
LOCK(cs_main);
|
LOCK(cs_main);
|
||||||
|
|
||||||
entry.pushKV("blockhash", hashBlock.GetHex());
|
entry.pushKV("blockhash", hashBlock.GetHex());
|
||||||
CBlockIndex* pindex = active_chainstate.m_blockman.LookupBlockIndex(hashBlock);
|
const CBlockIndex* pindex = active_chainstate.m_blockman.LookupBlockIndex(hashBlock);
|
||||||
if (pindex) {
|
if (pindex) {
|
||||||
if (active_chainstate.m_chain.Contains(pindex)) {
|
if (active_chainstate.m_chain.Contains(pindex)) {
|
||||||
entry.pushKV("confirmations", 1 + active_chainstate.m_chain.Height() - pindex->nHeight);
|
entry.pushKV("confirmations", 1 + active_chainstate.m_chain.Height() - pindex->nHeight);
|
||||||
|
@ -207,7 +207,7 @@ static RPCHelpMan getrawtransaction()
|
||||||
|
|
||||||
bool in_active_chain = true;
|
bool in_active_chain = true;
|
||||||
uint256 hash = ParseHashV(request.params[0], "parameter 1");
|
uint256 hash = ParseHashV(request.params[0], "parameter 1");
|
||||||
CBlockIndex* blockindex = nullptr;
|
const CBlockIndex* blockindex = nullptr;
|
||||||
|
|
||||||
if (hash == Params().GenesisBlock().hashMerkleRoot) {
|
if (hash == Params().GenesisBlock().hashMerkleRoot) {
|
||||||
// Special exception for the genesis block coinbase transaction
|
// Special exception for the genesis block coinbase transaction
|
||||||
|
@ -302,7 +302,7 @@ static RPCHelpMan gettxoutproof()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CBlockIndex* pblockindex = nullptr;
|
const CBlockIndex* pblockindex = nullptr;
|
||||||
uint256 hashBlock;
|
uint256 hashBlock;
|
||||||
ChainstateManager& chainman = EnsureAnyChainman(request.context);
|
ChainstateManager& chainman = EnsureAnyChainman(request.context);
|
||||||
if (!request.params[1].isNull()) {
|
if (!request.params[1].isNull()) {
|
||||||
|
|
|
@ -65,21 +65,22 @@
|
||||||
using node::BLOCKFILE_CHUNK_SIZE;
|
using node::BLOCKFILE_CHUNK_SIZE;
|
||||||
using node::BlockManager;
|
using node::BlockManager;
|
||||||
using node::BlockMap;
|
using node::BlockMap;
|
||||||
|
using node::CBlockIndexHeightOnlyComparator;
|
||||||
using node::CBlockIndexWorkComparator;
|
using node::CBlockIndexWorkComparator;
|
||||||
using node::CCoinsStats;
|
using node::CCoinsStats;
|
||||||
using node::CoinStatsHashType;
|
using node::CoinStatsHashType;
|
||||||
|
using node::fHavePruned;
|
||||||
|
using node::fImporting;
|
||||||
|
using node::fPruneMode;
|
||||||
|
using node::fReindex;
|
||||||
using node::GetUTXOStats;
|
using node::GetUTXOStats;
|
||||||
|
using node::nPruneTarget;
|
||||||
using node::OpenBlockFile;
|
using node::OpenBlockFile;
|
||||||
using node::ReadBlockFromDisk;
|
using node::ReadBlockFromDisk;
|
||||||
using node::SnapshotMetadata;
|
using node::SnapshotMetadata;
|
||||||
using node::UNDOFILE_CHUNK_SIZE;
|
using node::UNDOFILE_CHUNK_SIZE;
|
||||||
using node::UndoReadFromDisk;
|
using node::UndoReadFromDisk;
|
||||||
using node::UnlinkPrunedFiles;
|
using node::UnlinkPrunedFiles;
|
||||||
using node::fHavePruned;
|
|
||||||
using node::fImporting;
|
|
||||||
using node::fPruneMode;
|
|
||||||
using node::fReindex;
|
|
||||||
using node::nPruneTarget;
|
|
||||||
|
|
||||||
#define MICRO 0.000001
|
#define MICRO 0.000001
|
||||||
#define MILLI 0.001
|
#define MILLI 0.001
|
||||||
|
@ -107,24 +108,6 @@ const std::vector<std::string> CHECKLEVEL_DOC {
|
||||||
"each level includes the checks of the previous levels",
|
"each level includes the checks of the previous levels",
|
||||||
};
|
};
|
||||||
|
|
||||||
bool CBlockIndexWorkComparator::operator()(const CBlockIndex *pa, const CBlockIndex *pb) const {
|
|
||||||
// First sort by most total work, ...
|
|
||||||
if (pa->nChainWork > pb->nChainWork) return false;
|
|
||||||
if (pa->nChainWork < pb->nChainWork) return true;
|
|
||||||
|
|
||||||
// ... then by earliest time received, ...
|
|
||||||
if (pa->nSequenceId < pb->nSequenceId) return false;
|
|
||||||
if (pa->nSequenceId > pb->nSequenceId) return true;
|
|
||||||
|
|
||||||
// Use pointer address as tie breaker (should only happen with blocks
|
|
||||||
// loaded from disk, as those all have id 0).
|
|
||||||
if (pa < pb) return false;
|
|
||||||
if (pa > pb) return true;
|
|
||||||
|
|
||||||
// Identical blocks.
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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.
|
||||||
|
@ -152,14 +135,14 @@ arith_uint256 nMinimumChainWork;
|
||||||
|
|
||||||
CFeeRate minRelayTxFee = CFeeRate(DEFAULT_MIN_RELAY_TX_FEE);
|
CFeeRate minRelayTxFee = CFeeRate(DEFAULT_MIN_RELAY_TX_FEE);
|
||||||
|
|
||||||
CBlockIndex* CChainState::FindForkInGlobalIndex(const CBlockLocator& locator) const
|
const CBlockIndex* CChainState::FindForkInGlobalIndex(const CBlockLocator& locator) const
|
||||||
{
|
{
|
||||||
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.
|
||||||
for (const uint256& hash : locator.vHave) {
|
for (const uint256& hash : locator.vHave) {
|
||||||
CBlockIndex* pindex{m_blockman.LookupBlockIndex(hash)};
|
const CBlockIndex* pindex{m_blockman.LookupBlockIndex(hash)};
|
||||||
if (pindex) {
|
if (pindex) {
|
||||||
if (m_chain.Contains(pindex)) {
|
if (m_chain.Contains(pindex)) {
|
||||||
return pindex;
|
return pindex;
|
||||||
|
@ -3381,7 +3364,7 @@ static bool ContextualCheckBlockHeader(const CBlockHeader& block, BlockValidatio
|
||||||
// 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 in our
|
// GetLastCheckpoint finds the last checkpoint in MapCheckpoints that's in our
|
||||||
// BlockIndex().
|
// BlockIndex().
|
||||||
CBlockIndex* pcheckpoint = blockman.GetLastCheckpoint(params.Checkpoints());
|
const CBlockIndex* pcheckpoint = blockman.GetLastCheckpoint(params.Checkpoints());
|
||||||
if (pcheckpoint && nHeight < pcheckpoint->nHeight) {
|
if (pcheckpoint && nHeight < pcheckpoint->nHeight) {
|
||||||
LogPrintf("ERROR: %s: forked chain older than last checkpoint (height %d)\n", __func__, nHeight);
|
LogPrintf("ERROR: %s: forked chain older than last checkpoint (height %d)\n", __func__, nHeight);
|
||||||
return state.Invalid(BlockValidationResult::BLOCK_CHECKPOINT, "bad-fork-prior-to-checkpoint");
|
return state.Invalid(BlockValidationResult::BLOCK_CHECKPOINT, "bad-fork-prior-to-checkpoint");
|
||||||
|
@ -4091,8 +4074,74 @@ bool ChainstateManager::LoadBlockIndex()
|
||||||
// Load block index from databases
|
// Load block index from databases
|
||||||
bool needs_init = fReindex;
|
bool needs_init = fReindex;
|
||||||
if (!fReindex) {
|
if (!fReindex) {
|
||||||
bool ret = m_blockman.LoadBlockIndexDB(*this);
|
bool ret = m_blockman.LoadBlockIndexDB();
|
||||||
if (!ret) return false;
|
if (!ret) return false;
|
||||||
|
|
||||||
|
std::vector<CBlockIndex*> vSortedByHeight{m_blockman.GetAllBlockIndices()};
|
||||||
|
std::sort(vSortedByHeight.begin(), vSortedByHeight.end(),
|
||||||
|
CBlockIndexHeightOnlyComparator());
|
||||||
|
|
||||||
|
// Find start of assumed-valid region.
|
||||||
|
int first_assumed_valid_height = std::numeric_limits<int>::max();
|
||||||
|
|
||||||
|
for (const CBlockIndex* block : vSortedByHeight) {
|
||||||
|
if (block->IsAssumedValid()) {
|
||||||
|
auto chainstates = GetAll();
|
||||||
|
|
||||||
|
// If we encounter an assumed-valid block index entry, ensure that we have
|
||||||
|
// one chainstate that tolerates assumed-valid entries and another that does
|
||||||
|
// not (i.e. the background validation chainstate), since assumed-valid
|
||||||
|
// entries should always be pending validation by a fully-validated chainstate.
|
||||||
|
auto any_chain = [&](auto fnc) { return std::any_of(chainstates.cbegin(), chainstates.cend(), fnc); };
|
||||||
|
assert(any_chain([](auto chainstate) { return chainstate->reliesOnAssumedValid(); }));
|
||||||
|
assert(any_chain([](auto chainstate) { return !chainstate->reliesOnAssumedValid(); }));
|
||||||
|
|
||||||
|
first_assumed_valid_height = block->nHeight;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (CBlockIndex* pindex : vSortedByHeight) {
|
||||||
|
if (ShutdownRequested()) return false;
|
||||||
|
if (pindex->IsAssumedValid() ||
|
||||||
|
(pindex->IsValid(BLOCK_VALID_TRANSACTIONS) &&
|
||||||
|
(pindex->HaveTxsDownloaded() || pindex->pprev == nullptr))) {
|
||||||
|
|
||||||
|
// Fill each chainstate's block candidate set. Only add assumed-valid
|
||||||
|
// blocks to the tip candidate set if the chainstate is allowed to rely on
|
||||||
|
// assumed-valid blocks.
|
||||||
|
//
|
||||||
|
// If all setBlockIndexCandidates contained the assumed-valid blocks, the
|
||||||
|
// background chainstate's ActivateBestChain() call would add assumed-valid
|
||||||
|
// blocks to the chain (based on how FindMostWorkChain() works). Obviously
|
||||||
|
// we don't want this since the purpose of the background validation chain
|
||||||
|
// is to validate assued-valid blocks.
|
||||||
|
//
|
||||||
|
// Note: This is considering all blocks whose height is greater or equal to
|
||||||
|
// the first assumed-valid block to be assumed-valid blocks, and excluding
|
||||||
|
// them from the background chainstate's setBlockIndexCandidates set. This
|
||||||
|
// does mean that some blocks which are not technically assumed-valid
|
||||||
|
// (later blocks on a fork beginning before the first assumed-valid block)
|
||||||
|
// might not get added to the background chainstate, but this is ok,
|
||||||
|
// because they will still be attached to the active chainstate if they
|
||||||
|
// actually contain more work.
|
||||||
|
//
|
||||||
|
// Instead of this height-based approach, an earlier attempt was made at
|
||||||
|
// detecting "holistically" whether the block index under consideration
|
||||||
|
// relied on an assumed-valid ancestor, but this proved to be too slow to
|
||||||
|
// be practical.
|
||||||
|
for (CChainState* chainstate : GetAll()) {
|
||||||
|
if (chainstate->reliesOnAssumedValid() ||
|
||||||
|
pindex->nHeight < first_assumed_valid_height) {
|
||||||
|
chainstate->setBlockIndexCandidates.insert(pindex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (pindex->nStatus & BLOCK_FAILED_MASK && (!m_best_invalid || pindex->nChainWork > m_best_invalid->nChainWork)) {
|
||||||
|
m_best_invalid = pindex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
needs_init = m_blockman.m_block_index.empty();
|
needs_init = m_blockman.m_block_index.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4194,7 +4243,7 @@ void CChainState::LoadExternalBlockFile(FILE* fileIn, FlatFilePos* dbp)
|
||||||
}
|
}
|
||||||
|
|
||||||
// process in case the block isn't known yet
|
// process in case the block isn't known yet
|
||||||
CBlockIndex* pindex = m_blockman.LookupBlockIndex(hash);
|
const CBlockIndex* pindex = m_blockman.LookupBlockIndex(hash);
|
||||||
if (!pindex || (pindex->nStatus & BLOCK_HAVE_DATA) == 0) {
|
if (!pindex || (pindex->nStatus & BLOCK_HAVE_DATA) == 0) {
|
||||||
BlockValidationState state;
|
BlockValidationState state;
|
||||||
if (AcceptBlock(pblock, state, nullptr, true, dbp, nullptr)) {
|
if (AcceptBlock(pblock, state, nullptr, true, dbp, nullptr)) {
|
||||||
|
|
|
@ -686,7 +686,7 @@ public:
|
||||||
bool IsInitialBlockDownload() const;
|
bool IsInitialBlockDownload() const;
|
||||||
|
|
||||||
/** Find the last common block of this chain and a locator. */
|
/** Find the last common block of this chain and a locator. */
|
||||||
CBlockIndex* FindForkInGlobalIndex(const CBlockLocator& locator) const EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
const CBlockIndex* FindForkInGlobalIndex(const CBlockLocator& locator) const EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Make various assertions about the state of the block index.
|
* Make various assertions about the state of the block index.
|
||||||
|
@ -829,7 +829,7 @@ private:
|
||||||
bool m_snapshot_validated{false};
|
bool m_snapshot_validated{false};
|
||||||
|
|
||||||
CBlockIndex* m_best_invalid;
|
CBlockIndex* m_best_invalid;
|
||||||
friend bool node::BlockManager::LoadBlockIndex(const Consensus::Params&, ChainstateManager&);
|
friend bool node::BlockManager::LoadBlockIndex(const Consensus::Params&);
|
||||||
|
|
||||||
//! Internal helper for ActivateSnapshot().
|
//! Internal helper for ActivateSnapshot().
|
||||||
[[nodiscard]] bool PopulateAndValidateSnapshot(
|
[[nodiscard]] bool PopulateAndValidateSnapshot(
|
||||||
|
|
Loading…
Add table
Reference in a new issue