Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

backport: merge bitcoin#21727, #22371, #21526, #23174, #23785, #23581, #23974, #22932, #24050, #24515 (blockstorage backports) #6085

Merged
merged 11 commits into from
Jul 23, 2024
Merged
2 changes: 1 addition & 1 deletion src/index/coinstatsindex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ bool CoinStatsIndex::Rewind(const CBlockIndex* current_tip, const CBlockIndex* n

{
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()};

do {
Expand Down
3 changes: 2 additions & 1 deletion src/miner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ int64_t UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParam
return nNewTime - nOldTime;
}

BlockAssembler::Options::Options() {
BlockAssembler::Options::Options()
{
blockMinFeeRate = CFeeRate(DEFAULT_BLOCK_MIN_TX_FEE);
nBlockMaxSize = DEFAULT_BLOCK_MAX_SIZE;
}
Expand Down
129 changes: 50 additions & 79 deletions src/node/blockstorage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,45 @@ bool fAddressIndex = DEFAULT_ADDRESSINDEX;
bool fTimestampIndex = DEFAULT_TIMESTAMPINDEX;
bool fSpentIndex = DEFAULT_SPENTINDEX;

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 FlatFileSeq BlockFileSeq();
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)
{
AssertLockHeld(cs_main);
Expand Down Expand Up @@ -220,60 +255,34 @@ CBlockIndex* BlockManager::InsertBlockIndex(const uint256& hash)
return nullptr;
}

// Return existing or create new
auto [mi, inserted] = m_block_index.try_emplace(hash);
const auto [mi, inserted]{m_block_index.try_emplace(hash)};
CBlockIndex* pindex = &(*mi).second;
if (inserted) {
pindex->phashBlock = &((*mi).first);
}
return pindex;
}

bool BlockManager::LoadBlockIndex(
const Consensus::Params& consensus_params,
ChainstateManager& chainman)
bool BlockManager::LoadBlockIndex(const Consensus::Params& consensus_params)
{
if (!m_block_tree_db->LoadBlockIndexGuts(consensus_params, [this](const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { return this->InsertBlockIndex(hash); })) {
return false;
}

// Calculate nChainWork
std::vector<std::pair<int, CBlockIndex*>> vSortedByHeight;
vSortedByHeight.reserve(m_block_index.size());
for (auto& [_, block_index] : m_block_index) {
CBlockIndex* pindex = &block_index;
vSortedByHeight.push_back(std::make_pair(pindex->nHeight, pindex));

// build m_blockman.m_prev_block_index
if (pindex->pprev) {
m_prev_block_index.emplace(pindex->pprev->GetBlockHash(), pindex);
if (block_index.pprev) {
m_prev_block_index.emplace(block_index.pprev->GetBlockHash(), &block_index);
}
}
sort(vSortedByHeight.begin(), vSortedByHeight.end());

// Find start of assumed-valid region.
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;
}
}
// Calculate nChainWork
std::vector<CBlockIndex*> vSortedByHeight{GetAllBlockIndices()};
std::sort(vSortedByHeight.begin(), vSortedByHeight.end(),
CBlockIndexHeightOnlyComparator());

for (const std::pair<int, CBlockIndex*>& item : vSortedByHeight) {
for (CBlockIndex* pindex : vSortedByHeight) {
if (ShutdownRequested()) return false;
CBlockIndex* pindex = item.second;
pindex->nChainWork = (pindex->pprev ? pindex->pprev->nChainWork : 0) + GetBlockProof(*pindex);
pindex->nTimeMax = (pindex->pprev ? std::max(pindex->pprev->nTimeMax, pindex->nTime) : pindex->nTime);

Expand All @@ -297,43 +306,6 @@ bool BlockManager::LoadBlockIndex(
pindex->nStatus |= BLOCK_FAILED_CHILD;
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 the background chainstate, but this is ok,
// because they will still be attached to the active chainstate if they
// actually contain more work.
//
// Instad 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) {
pindex->BuildSkip();
}
Expand Down Expand Up @@ -377,9 +349,9 @@ bool BlockManager::WriteBlockIndexDB()
return true;
}

bool BlockManager::LoadBlockIndexDB(ChainstateManager& chainman)
bool BlockManager::LoadBlockIndexDB()
{
if (!LoadBlockIndex(::Params().GetConsensus(), chainman)) {
if (!LoadBlockIndex(::Params().GetConsensus())) {
return false;
}

Expand All @@ -404,9 +376,8 @@ bool BlockManager::LoadBlockIndexDB(ChainstateManager& chainman)
LogPrintf("Checking all blk files are present...\n");
std::set<int> setBlkDataFiles;
for (const auto& [_, block_index] : m_block_index) {
const CBlockIndex* pindex = &block_index;
if (pindex->nStatus & BLOCK_HAVE_DATA) {
setBlkDataFiles.insert(pindex->nFile);
if (block_index.nStatus & BLOCK_HAVE_DATA) {
setBlkDataFiles.insert(block_index.nFile);
}
}
for (std::set<int>::iterator it = setBlkDataFiles.begin(); it != setBlkDataFiles.end(); it++) {
Expand Down Expand Up @@ -442,13 +413,13 @@ bool BlockManager::LoadBlockIndexDB(ChainstateManager& chainman)
return true;
}

CBlockIndex* BlockManager::GetLastCheckpoint(const CCheckpointData& data)
const CBlockIndex* BlockManager::GetLastCheckpoint(const CCheckpointData& data)
{
const MapCheckpoints& checkpoints = data.mapCheckpoints;

for (const MapCheckpoints::value_type& i : reverse_iterate(checkpoints)) {
const uint256& hash = i.second;
CBlockIndex* pindex = LookupBlockIndex(hash);
const CBlockIndex* pindex = LookupBlockIndex(hash);
if (pindex) {
return pindex;
}
Expand Down
16 changes: 11 additions & 5 deletions src/node/blockstorage.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,11 @@ struct CBlockIndexWorkComparator {
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
* to determine where the most-work tip is.
Expand Down Expand Up @@ -131,6 +136,8 @@ class BlockManager
BlockMap m_block_index GUARDED_BY(cs_main);
PrevBlockMap m_prev_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.
* Pruned nodes may have entries where B is missing data.
Expand All @@ -140,16 +147,15 @@ class BlockManager
std::unique_ptr<CBlockTreeDB> m_block_tree_db GUARDED_BY(::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
* per index entry (nStatus, nChainWork, nTimeMax, etc.) as well as peripheral
* collections like m_dirty_blockindex.
*/
bool LoadBlockIndex(
const Consensus::Params& consensus_params,
ChainstateManager& chainman) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
bool LoadBlockIndex(const Consensus::Params& consensus_params)
EXCLUSIVE_LOCKS_REQUIRED(cs_main);

/** Clear all data members. */
void Unload() EXCLUSIVE_LOCKS_REQUIRED(cs_main);
Expand All @@ -176,7 +182,7 @@ class BlockManager
uint64_t CalculateCurrentUsage();

//! 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);

/**
* Return the spend height, which is one more than the inputs.GetBestBlock().
Expand Down
4 changes: 2 additions & 2 deletions src/node/interfaces.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -763,7 +763,7 @@ class ChainImpl : public Chain
{
LOCK(cs_main);
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 std::nullopt;
Expand Down Expand Up @@ -861,7 +861,7 @@ class ChainImpl : public Chain
// 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.
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);
for (; block->nStatus & BLOCK_HAVE_DATA; block = block->pprev) {
// Check pprev to not segfault if min_height is too low
Expand Down
4 changes: 2 additions & 2 deletions src/rest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -271,8 +271,8 @@ static bool rest_block(const CoreContext& context,
return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + hashStr);

CBlock block;
CBlockIndex* pblockindex = nullptr;
CBlockIndex* tip = nullptr;
const CBlockIndex* pblockindex = nullptr;
const CBlockIndex* tip = nullptr;
{
ChainstateManager* maybe_chainman = GetChainman(context, req);
if (!maybe_chainman) return false;
Expand Down
17 changes: 9 additions & 8 deletions src/rpc/blockchain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,8 @@ static int ComputeNextBlockAndDepth(const CBlockIndex* tip, const CBlockIndex* b
return blockindex == tip ? 1 : -1;
}

CBlockIndex* ParseHashOrHeight(const UniValue& param, ChainstateManager& chainman) {
static const CBlockIndex* ParseHashOrHeight(const UniValue& param, ChainstateManager& chainman)
{
LOCK(::cs_main);
CChain& active_chain = chainman.ActiveChain();

Expand All @@ -113,7 +114,7 @@ CBlockIndex* ParseHashOrHeight(const UniValue& param, ChainstateManager& chainma
return active_chain[height];
} else {
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) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
Expand Down Expand Up @@ -874,7 +875,7 @@ static RPCHelpMan getblockhash()
if (nHeight < 0 || nHeight > active_chain.Height())
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();
},
};
Expand Down Expand Up @@ -1304,7 +1305,7 @@ static RPCHelpMan pruneblockchain()
// too low to be a block time (corresponds to timestamp from Sep 2001).
if (heightParam > 1000000000) {
// 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) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Could not find block with at least the specified timestamp.");
}
Expand Down Expand Up @@ -1398,7 +1399,7 @@ static RPCHelpMan gettxoutsetinfo()
{
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())};
CCoinsStats stats{hash_type};
stats.index_requested = request.params[2].isNull() || request.params[2].get_bool();
Expand Down Expand Up @@ -2344,7 +2345,7 @@ static RPCHelpMan getblockstats()

ChainstateManager& chainman = EnsureAnyChainman(request.context);
LOCK(cs_main);
CBlockIndex* pindex{ParseHashOrHeight(request.params[0], chainman)};
const CBlockIndex* pindex{ParseHashOrHeight(request.params[0], chainman)};
CHECK_NONFATAL(pindex != nullptr);

std::set<std::string> stats;
Expand Down Expand Up @@ -2801,7 +2802,7 @@ static RPCHelpMan scantxoutset()
g_should_abort_scan = false;
int64_t count = 0;
std::unique_ptr<CCoinsViewCursor> pcursor;
CBlockIndex* tip;
const CBlockIndex* tip;
NodeContext& node = EnsureAnyNodeContext(request.context);
{
ChainstateManager& chainman = EnsureChainman(node);
Expand Down Expand Up @@ -2981,7 +2982,7 @@ UniValue CreateUTXOSnapshot(NodeContext& node, CChainState& chainstate, CAutoFil
{
std::unique_ptr<CCoinsViewCursor> pcursor;
CCoinsStats stats{CoinStatsHashType::NONE};
CBlockIndex* tip;
const CBlockIndex* tip;

{
// We need to lock cs_main to ensure that the coinsdb isn't written to
Expand Down
4 changes: 2 additions & 2 deletions src/rpc/rawtransaction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ static RPCHelpMan getrawtransaction()

bool in_active_chain = true;
uint256 hash = ParseHashV(request.params[0], "parameter 1");
CBlockIndex* blockindex = nullptr;
const CBlockIndex* blockindex = nullptr;

if (hash == Params().GenesisBlock().hashMerkleRoot) {
// Special exception for the genesis block coinbase transaction
Expand Down Expand Up @@ -610,7 +610,7 @@ static RPCHelpMan gettxoutproof()
}
}

CBlockIndex* pblockindex = nullptr;
const CBlockIndex* pblockindex = nullptr;
uint256 hashBlock;
ChainstateManager& chainman = EnsureAnyChainman(request.context);
if (!request.params[1].isNull()) {
Expand Down
Loading
Loading