Skip to content

Commit

Permalink
merge bitcoin#22932: Add CBlockIndex lock annotations, guard nStatus/…
Browse files Browse the repository at this point in the history
…nFile/nDataPos/nUndoPos by cs_main
  • Loading branch information
kwvg committed Jul 19, 2024
1 parent e303a4e commit c440304
Show file tree
Hide file tree
Showing 12 changed files with 83 additions and 39 deletions.
31 changes: 23 additions & 8 deletions src/chain.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <consensus/params.h>
#include <flatfile.h>
#include <primitives/block.h>
#include <sync.h>
#include <uint256.h>

#include <vector>
Expand All @@ -34,6 +35,8 @@ static constexpr int64_t TIMESTAMP_WINDOW = MAX_FUTURE_BLOCK_TIME;
*/
static constexpr int64_t MAX_BLOCK_TIME_GAP = 25 * 60;

extern RecursiveMutex cs_main;

class CBlockFileInfo
{
public:
Expand Down Expand Up @@ -155,13 +158,13 @@ class CBlockIndex
int nHeight{0};

//! Which # file this block is stored in (blk?????.dat)
int nFile{0};
int nFile GUARDED_BY(::cs_main){0};

//! Byte offset within blk?????.dat where this block's data is stored
unsigned int nDataPos{0};
unsigned int nDataPos GUARDED_BY(::cs_main){0};

//! Byte offset within rev?????.dat where this block's undo data is stored
unsigned int nUndoPos{0};
unsigned int nUndoPos GUARDED_BY(::cs_main){0};

//! (memory only) Total amount of work (expected number of hashes) in the chain up to and including this block
arith_uint256 nChainWork{};
Expand Down Expand Up @@ -189,7 +192,7 @@ class CBlockIndex
//! load to avoid the block index being spuriously rewound.
//! @sa NeedsRedownload
//! @sa ActivateSnapshot
uint32_t nStatus{0};
uint32_t nStatus GUARDED_BY(::cs_main){0};

//! block header
int32_t nVersion{0};
Expand Down Expand Up @@ -217,7 +220,9 @@ class CBlockIndex
{
}

FlatFilePos GetBlockPos() const {
FlatFilePos GetBlockPos() const EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
{
AssertLockHeld(::cs_main);
FlatFilePos ret;
if (nStatus & BLOCK_HAVE_DATA) {
ret.nFile = nFile;
Expand All @@ -226,7 +231,9 @@ class CBlockIndex
return ret;
}

FlatFilePos GetUndoPos() const {
FlatFilePos GetUndoPos() const EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
{
AssertLockHeld(::cs_main);
FlatFilePos ret;
if (nStatus & BLOCK_HAVE_UNDO) {
ret.nFile = nFile;
Expand Down Expand Up @@ -292,7 +299,9 @@ class CBlockIndex

//! Check whether this block index entry is valid up to the passed validity level.
bool IsValid(enum BlockStatus nUpTo = BLOCK_VALID_TRANSACTIONS) const
EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
{
AssertLockHeld(::cs_main);
assert(!(nUpTo & ~BLOCK_VALID_MASK)); // Only validity flags allowed.
if (nStatus & BLOCK_FAILED_MASK)
return false;
Expand All @@ -301,12 +310,17 @@ class CBlockIndex

//! @returns true if the block is assumed-valid; this means it is queued to be
//! validated by a background chainstate.
bool IsAssumedValid() const { return nStatus & BLOCK_ASSUMED_VALID; }
bool IsAssumedValid() const EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
{
AssertLockHeld(::cs_main);
return nStatus & BLOCK_ASSUMED_VALID;
}

//! Raise the validity level of this block index entry.
//! Returns true if the validity was changed.
bool RaiseValidity(enum BlockStatus nUpTo)
bool RaiseValidity(enum BlockStatus nUpTo) EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
{
AssertLockHeld(::cs_main);
assert(!(nUpTo & ~BLOCK_VALID_MASK)); // Only validity flags allowed.
if (nStatus & BLOCK_FAILED_MASK) return false;

Expand Down Expand Up @@ -357,6 +371,7 @@ class CDiskBlockIndex : public CBlockIndex

SERIALIZE_METHODS(CDiskBlockIndex, obj)
{
LOCK(::cs_main);
int _nVersion = s.GetVersion();
if (!(s.GetType() & SER_GETHASH)) READWRITE(VARINT_MODE(_nVersion, VarIntMode::NONNEGATIVE_SIGNED));

Expand Down
4 changes: 3 additions & 1 deletion src/index/txindex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,9 @@ bool TxIndex::WriteBlock(const CBlock& block, const CBlockIndex* pindex)
// Exclude genesis block transaction because outputs are not spendable.
if (pindex->nHeight == 0) return true;

CDiskTxPos pos(pindex->GetBlockPos(), GetSizeOfCompactSize(block.vtx.size()));
CDiskTxPos pos{
WITH_LOCK(::cs_main, return pindex->GetBlockPos()),
GetSizeOfCompactSize(block.vtx.size())};
std::vector<std::pair<uint256, CDiskTxPos>> vPos;
vPos.reserve(block.vtx.size());
for (const auto& tx : block.vtx) {
Expand Down
5 changes: 4 additions & 1 deletion src/node/blockstorage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,7 @@ CBlockIndex* BlockManager::GetLastCheckpoint(const CCheckpointData& data)

bool IsBlockPruned(const CBlockIndex* pblockindex)
{
AssertLockHeld(::cs_main);
return (fHavePruned && !(pblockindex->nStatus & BLOCK_HAVE_DATA) && pblockindex->nTx > 0);
}

Expand Down Expand Up @@ -550,7 +551,8 @@ static bool UndoWriteToDisk(const CBlockUndo& blockundo, FlatFilePos& pos, const

bool UndoReadFromDisk(CBlockUndo& blockundo, const CBlockIndex* pindex)
{
FlatFilePos pos = pindex->GetUndoPos();
const FlatFilePos pos{WITH_LOCK(::cs_main, return pindex->GetUndoPos())};

if (pos.IsNull()) {
return error("%s: no undo data available", __func__);
}
Expand Down Expand Up @@ -750,6 +752,7 @@ static bool WriteBlockToDisk(const CBlock& block, FlatFilePos& pos, const CMessa

bool BlockManager::WriteUndoDataForBlock(const CBlockUndo& blockundo, BlockValidationState& state, CBlockIndex* pindex, const CChainParams& chainparams)
{
AssertLockHeld(::cs_main);
// Write undo information to disk
if (pindex->GetUndoPos().IsNull()) {
FlatFilePos _pos;
Expand Down
8 changes: 6 additions & 2 deletions src/node/blockstorage.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,14 @@

#include <fs.h>
#include <protocol.h> // For CMessageHeader::MessageStartChars
#include <sync.h>
#include <txdb.h>

#include <cstdint>
#include <vector>

extern RecursiveMutex cs_main;

class CActiveMasternodeManager;
class ArgsManager;
class BlockValidationState;
Expand Down Expand Up @@ -159,7 +162,8 @@ class BlockManager
/** Get block file info entry for one block file */
CBlockFileInfo* GetBlockFileInfo(size_t n);

bool WriteUndoDataForBlock(const CBlockUndo& blockundo, BlockValidationState& state, CBlockIndex* pindex, const CChainParams& chainparams);
bool WriteUndoDataForBlock(const CBlockUndo& blockundo, BlockValidationState& state, CBlockIndex* pindex, const CChainParams& chainparams)
EXCLUSIVE_LOCKS_REQUIRED(::cs_main);

FlatFilePos SaveBlockToDisk(const CBlock& block, int nHeight, CChain& active_chain, const CChainParams& chainparams, const FlatFilePos* dbp);

Expand All @@ -183,7 +187,7 @@ class BlockManager
};

//! Check whether the block associated with this index entry is pruned or not.
bool IsBlockPruned(const CBlockIndex* pblockindex);
bool IsBlockPruned(const CBlockIndex* pblockindex) EXCLUSIVE_LOCKS_REQUIRED(::cs_main);

void CleanupBlockRevFiles();

Expand Down
11 changes: 7 additions & 4 deletions src/rpc/blockchain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* tip, const CBlockIn
UniValue txs(UniValue::VARR);
if (txDetails) {
CBlockUndo blockUndo;
const bool have_undo = !IsBlockPruned(blockindex) && UndoReadFromDisk(blockUndo, blockindex);
const bool have_undo{WITH_LOCK(::cs_main, return !IsBlockPruned(blockindex) && UndoReadFromDisk(blockUndo, blockindex))};
for (size_t i = 0; i < block.vtx.size(); ++i) {
const CTransactionRef& tx = block.vtx.at(i);
// coinbase transaction (i == 0) doesn't have undo data
Expand Down Expand Up @@ -804,7 +804,8 @@ static RPCHelpMan getblockfrompeer()

UniValue result = UniValue::VOBJ;

if (index->nStatus & BLOCK_HAVE_DATA) {
const bool block_has_data = WITH_LOCK(::cs_main, return index->nStatus & BLOCK_HAVE_DATA);
if (block_has_data) {
result.pushKV("warnings", "Block already downloaded");
} else if (const auto err{peerman.FetchBlock(peer_id, *index)}) {
throw JSONRPCError(RPC_MISC_ERROR, err.value());
Expand Down Expand Up @@ -1058,8 +1059,9 @@ static RPCHelpMan getblockheaders()
};
}

static CBlock GetBlockChecked(const CBlockIndex* pblockindex)
static CBlock GetBlockChecked(const CBlockIndex* pblockindex) EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
{
AssertLockHeld(::cs_main);
CBlock block;
if (IsBlockPruned(pblockindex)) {
throw JSONRPCError(RPC_MISC_ERROR, "Block not available (pruned data)");
Expand All @@ -1075,8 +1077,9 @@ static CBlock GetBlockChecked(const CBlockIndex* pblockindex)
return block;
}

static CBlockUndo GetUndoChecked(const CBlockIndex* pblockindex)
static CBlockUndo GetUndoChecked(const CBlockIndex* pblockindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
AssertLockHeld(::cs_main);
CBlockUndo blockUndo;
if (IsBlockPruned(pblockindex)) {
throw JSONRPCError(RPC_MISC_ERROR, "Undo data not available (pruned data)");
Expand Down
3 changes: 2 additions & 1 deletion src/rpc/rawtransaction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,8 @@ static RPCHelpMan getrawtransaction()
if (!tx) {
std::string errmsg;
if (blockindex) {
if (!(blockindex->nStatus & BLOCK_HAVE_DATA)) {
const bool block_has_data = WITH_LOCK(::cs_main, return blockindex->nStatus & BLOCK_HAVE_DATA);
if (!block_has_data) {
throw JSONRPCError(RPC_MISC_ERROR, "Block not available");
}
errmsg = "No such transaction found in the provided block";
Expand Down
23 changes: 13 additions & 10 deletions src/test/fuzz/chain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,18 @@ FUZZ_TARGET(chain)

const uint256 zero{};
disk_block_index->phashBlock = &zero;
(void)disk_block_index->GetBlockHash();
(void)disk_block_index->GetBlockPos();
(void)disk_block_index->GetBlockTime();
(void)disk_block_index->GetBlockTimeMax();
(void)disk_block_index->GetMedianTimePast();
(void)disk_block_index->GetUndoPos();
(void)disk_block_index->HaveTxsDownloaded();
(void)disk_block_index->IsValid();
(void)disk_block_index->ToString();
{
LOCK(::cs_main);
(void)disk_block_index->GetBlockHash();
(void)disk_block_index->GetBlockPos();
(void)disk_block_index->GetBlockTime();
(void)disk_block_index->GetBlockTimeMax();
(void)disk_block_index->GetMedianTimePast();
(void)disk_block_index->GetUndoPos();
(void)disk_block_index->HaveTxsDownloaded();
(void)disk_block_index->IsValid();
(void)disk_block_index->ToString();
}

const CBlockHeader block_header = disk_block_index->GetBlockHeader();
(void)CDiskBlockIndex{*disk_block_index};
Expand All @@ -55,7 +58,7 @@ FUZZ_TARGET(chain)
if (block_status & ~BLOCK_VALID_MASK) {
continue;
}
(void)disk_block_index->RaiseValidity(block_status);
WITH_LOCK(::cs_main, (void)disk_block_index->RaiseValidity(block_status));
}

CBlockIndex block_index{block_header};
Expand Down
1 change: 1 addition & 0 deletions src/test/interfaces_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ BOOST_AUTO_TEST_CASE(findCommonAncestor)

BOOST_AUTO_TEST_CASE(hasBlocks)
{
LOCK(::cs_main);
auto& chain = m_node.chain;
const CChain& active = Assert(m_node.chainman)->ActiveChain();

Expand Down
2 changes: 2 additions & 0 deletions src/test/util/blockfilter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@

bool ComputeFilter(BlockFilterType filter_type, const CBlockIndex* block_index, BlockFilter& filter)
{
LOCK(::cs_main);

CBlock block;
if (!ReadBlockFromDisk(block, block_index->GetBlockPos(), Params().GetConsensus())) {
return false;
Expand Down
3 changes: 2 additions & 1 deletion src/test/validation_chainstatemanager_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager_activate_snapshot, TestChain100Setup)
*chainman.SnapshotBlockhash());

// Ensure that the genesis block was not marked assumed-valid.
BOOST_CHECK(!chainman.ActiveChain().Genesis()->IsAssumedValid());
BOOST_CHECK(WITH_LOCK(::cs_main, return !chainman.ActiveChain().Genesis()->IsAssumedValid()));

const AssumeutxoData& au_data = *ExpectedAssumeutxo(snapshot_height, ::Params());
const CBlockIndex* tip = chainman.ActiveTip();
Expand Down Expand Up @@ -371,6 +371,7 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager_loadblockindex, TestChain100Setup)

// Mark some region of the chain assumed-valid.
for (int i = 0; i <= cs1.m_chain.Height(); ++i) {
LOCK(::cs_main);
auto index = cs1.m_chain[i];

if (i < last_assumed_valid_idx && i >= assumed_valid_start_idx) {
Expand Down
14 changes: 9 additions & 5 deletions src/txdb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -423,19 +423,23 @@ bool CBlockTreeDB::LoadBlockIndexGuts(const Consensus::Params& consensusParams,
CBlockIndex* pindexNew = insertBlockIndex(diskindex.GetBlockHash());
pindexNew->pprev = insertBlockIndex(diskindex.hashPrev);
pindexNew->nHeight = diskindex.nHeight;
pindexNew->nFile = diskindex.nFile;
pindexNew->nDataPos = diskindex.nDataPos;
pindexNew->nUndoPos = diskindex.nUndoPos;
pindexNew->nVersion = diskindex.nVersion;
pindexNew->hashMerkleRoot = diskindex.hashMerkleRoot;
pindexNew->nTime = diskindex.nTime;
pindexNew->nBits = diskindex.nBits;
pindexNew->nNonce = diskindex.nNonce;
pindexNew->nStatus = diskindex.nStatus;
pindexNew->nTx = diskindex.nTx;
{
LOCK(::cs_main);
pindexNew->nFile = diskindex.nFile;
pindexNew->nDataPos = diskindex.nDataPos;
pindexNew->nUndoPos = diskindex.nUndoPos;
pindexNew->nStatus = diskindex.nStatus;
}

if (!CheckProofOfWork(pindexNew->GetBlockHash(), pindexNew->nBits, consensusParams))
if (!CheckProofOfWork(pindexNew->GetBlockHash(), pindexNew->nBits, consensusParams)) {
return error("%s: CheckProofOfWork failed: %s", __func__, pindexNew->ToString());
}

pcursor->Next();
} else {
Expand Down
17 changes: 11 additions & 6 deletions src/wallet/test/wallet_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -140,11 +140,13 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
}

// Prune the older block file.
int file_number;
{
LOCK(cs_main);
Assert(m_node.chainman)->m_blockman.PruneOneBlockFile(oldTip->GetBlockPos().nFile);
file_number = oldTip->GetBlockPos().nFile;
Assert(m_node.chainman)->m_blockman.PruneOneBlockFile(file_number);
}
UnlinkPrunedFiles({oldTip->GetBlockPos().nFile});
UnlinkPrunedFiles({file_number});

// Verify ScanForWalletTransactions only picks transactions in the new block
// file.
Expand All @@ -168,9 +170,10 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
// Prune the remaining block file.
{
LOCK(cs_main);
Assert(m_node.chainman)->m_blockman.PruneOneBlockFile(newTip->GetBlockPos().nFile);
file_number = newTip->GetBlockPos().nFile;
Assert(m_node.chainman)->m_blockman.PruneOneBlockFile(file_number);
}
UnlinkPrunedFiles({newTip->GetBlockPos().nFile});
UnlinkPrunedFiles({file_number});

// Verify ScanForWalletTransactions scans no blocks.
{
Expand Down Expand Up @@ -200,11 +203,13 @@ BOOST_FIXTURE_TEST_CASE(importmulti_rescan, TestChain100Setup)
CBlockIndex* newTip = m_node.chainman->ActiveChain().Tip();

// Prune the older block file.
int file_number;
{
LOCK(cs_main);
Assert(m_node.chainman)->m_blockman.PruneOneBlockFile(oldTip->GetBlockPos().nFile);
file_number = oldTip->GetBlockPos().nFile;
Assert(m_node.chainman)->m_blockman.PruneOneBlockFile(file_number);
}
UnlinkPrunedFiles({oldTip->GetBlockPos().nFile});
UnlinkPrunedFiles({file_number});

// Verify importmulti RPC returns failure for a key whose creation time is
// before the missing block, and success for a key whose creation time is
Expand Down

0 comments on commit c440304

Please sign in to comment.