diff --git a/src/Makefile.am b/src/Makefile.am index 3395d9e602fb..6472835899f1 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -279,6 +279,7 @@ BITCOIN_CORE_H = \ interfaces/node.h \ interfaces/wallet.h \ kernel/blockmanager_opts.h \ + kernel/chain.h \ kernel/chainstatemanager_opts.h \ kernel/checks.h \ kernel/coinstats.h \ @@ -562,6 +563,7 @@ libbitcoin_node_a_SOURCES = \ instantsend/lock.cpp \ instantsend/net_instantsend.cpp \ instantsend/signing.cpp \ + kernel/chain.cpp \ kernel/checks.cpp \ kernel/coinstats.cpp \ kernel/context.cpp \ @@ -1285,6 +1287,7 @@ libdashkernel_la_SOURCES = \ init/common.cpp \ instantsend/db.cpp \ instantsend/instantsend.cpp \ + kernel/chain.cpp \ kernel/checks.cpp \ kernel/coinstats.cpp \ kernel/context.cpp \ diff --git a/src/core_write.cpp b/src/core_write.cpp index a7ab773fdc22..e94b0af4add7 100644 --- a/src/core_write.cpp +++ b/src/core_write.cpp @@ -19,7 +19,7 @@ #include #include -#include +#include #include #include diff --git a/src/index/addressindex.cpp b/src/index/addressindex.cpp index e2bf0bc9770f..2a96dde8fac0 100644 --- a/src/index/addressindex.cpp +++ b/src/index/addressindex.cpp @@ -13,6 +13,7 @@ #include #include #include +#include constexpr uint8_t DB_ADDRESSINDEX{'a'}; constexpr uint8_t DB_ADDRESSUNSPENTINDEX{'u'}; @@ -159,25 +160,30 @@ bool AddressIndex::DB::RewindBatch(const std::vector& addres return CDBWrapper::WriteBatch(batch); } -AddressIndex::AddressIndex(size_t n_cache_size, bool f_memory, bool f_wipe) : +AddressIndex::AddressIndex(std::unique_ptr chain, size_t n_cache_size, bool f_memory, bool f_wipe) : + BaseIndex(std::move(chain)), m_db(std::make_unique(n_cache_size, f_memory, f_wipe)) { } AddressIndex::~AddressIndex() = default; -bool AddressIndex::WriteBlock(const CBlock& block, const CBlockIndex* pindex) +bool AddressIndex::CustomAppend(const interfaces::BlockInfo& block) { // Skip genesis block (no inputs to index) - if (pindex->nHeight == 0) { + if (block.height == 0) { return true; } + // pindex variable gives indexing code access to node internals. It + // will be removed in upcoming commit + const CBlockIndex* pindex = WITH_LOCK(cs_main, return m_chainstate->m_blockman.LookupBlockIndex(block.hash)); + // Read undo data for this block to get information about spent outputs CBlockUndo blockundo; if (!node::UndoReadFromDisk(blockundo, pindex)) { return error("%s: Failed to read undo data for block %s at height %d", __func__, - pindex->GetBlockHash().ToString(), pindex->nHeight); + block.hash.ToString(), block.height); } std::vector addressIndex; @@ -185,13 +191,13 @@ bool AddressIndex::WriteBlock(const CBlock& block, const CBlockIndex* pindex) // Process each non-coinbase transaction // blockundo.vtxundo[i] corresponds to block.vtx[i+1] (coinbase is skipped in undo data) - if (blockundo.vtxundo.size() != block.vtx.size() - 1) { + if (blockundo.vtxundo.size() != block.data->vtx.size() - 1) { return error("%s: Undo data size mismatch for block %s (expected %zu, got %zu)", __func__, - pindex->GetBlockHash().ToString(), block.vtx.size() - 1, blockundo.vtxundo.size()); + block.hash.ToString(), block.data->vtx.size() - 1, blockundo.vtxundo.size()); } for (size_t i = 0; i < blockundo.vtxundo.size(); i++) { - const CTransactionRef& tx = block.vtx[i + 1]; // +1 to skip coinbase + const CTransactionRef& tx = block.data->vtx[i + 1]; // +1 to skip coinbase const CTxUndo& txundo = blockundo.vtxundo[i]; const uint256 txhash = tx->GetHash(); @@ -213,7 +219,7 @@ bool AddressIndex::WriteBlock(const CBlock& block, const CBlockIndex* pindex) } // Record spending activity - addressIndex.emplace_back(CAddressIndexKey(address_type, address_bytes, pindex->nHeight, i + 1, txhash, j, true), + addressIndex.emplace_back(CAddressIndexKey(address_type, address_bytes, block.height, i + 1, txhash, j, true), prevout.nValue * -1); // Remove from unspent index @@ -234,17 +240,17 @@ bool AddressIndex::WriteBlock(const CBlock& block, const CBlockIndex* pindex) } // Record receiving activity - addressIndex.emplace_back(CAddressIndexKey(address_type, address_bytes, pindex->nHeight, i + 1, txhash, k, false), + addressIndex.emplace_back(CAddressIndexKey(address_type, address_bytes, block.height, i + 1, txhash, k, false), out.nValue); // Add to unspent index addressUnspentIndex.emplace_back(CAddressUnspentKey(address_type, address_bytes, txhash, k), - CAddressUnspentValue(out.nValue, out.scriptPubKey, pindex->nHeight)); + CAddressUnspentValue(out.nValue, out.scriptPubKey, block.height)); } } // Also process coinbase outputs (receiving activity only) - const CTransactionRef& coinbase = block.vtx[0]; + const CTransactionRef& coinbase = block.data->vtx[0]; const uint256 coinbase_hash = coinbase->GetHash(); for (size_t k = 0; k < coinbase->vout.size(); k++) { const CTxOut& out = coinbase->vout[k]; @@ -256,24 +262,26 @@ bool AddressIndex::WriteBlock(const CBlock& block, const CBlockIndex* pindex) } // Record receiving activity for coinbase - addressIndex.emplace_back(CAddressIndexKey(address_type, address_bytes, pindex->nHeight, 0, coinbase_hash, k, false), + addressIndex.emplace_back(CAddressIndexKey(address_type, address_bytes, block.height, 0, coinbase_hash, k, false), out.nValue); // Add coinbase outputs to unspent index addressUnspentIndex.emplace_back(CAddressUnspentKey(address_type, address_bytes, coinbase_hash, k), - CAddressUnspentValue(out.nValue, out.scriptPubKey, pindex->nHeight)); + CAddressUnspentValue(out.nValue, out.scriptPubKey, block.height)); } return m_db->WriteBatch(addressIndex, addressUnspentIndex); } -bool AddressIndex::Rewind(const CBlockIndex* current_tip, const CBlockIndex* new_tip) +bool AddressIndex::CustomRewind(const interfaces::BlockKey& current_tip, const interfaces::BlockKey& new_tip) { - assert(current_tip->GetAncestor(new_tip->nHeight) == new_tip); + const CBlockIndex* current_tip_index = WITH_LOCK(cs_main, return m_chainstate->m_blockman.LookupBlockIndex(current_tip.hash)); + const CBlockIndex* new_tip_index = WITH_LOCK(cs_main, return m_chainstate->m_blockman.LookupBlockIndex(new_tip.hash)); + assert(current_tip_index->GetAncestor(new_tip_index->nHeight) == new_tip_index); // Rewind the unspent index by processing blocks in reverse // We need to undo all operations from current_tip back to (but not including) new_tip - for (const CBlockIndex* pindex = current_tip; pindex != new_tip; pindex = pindex->pprev) { + for (const CBlockIndex* pindex = current_tip_index; pindex != new_tip_index; pindex = pindex->pprev) { CBlock block; if (!node::ReadBlockFromDisk(block, pindex, Params().GetConsensus())) { return error("%s: Failed to read block %s from disk during rewind", __func__, @@ -387,8 +395,7 @@ bool AddressIndex::Rewind(const CBlockIndex* current_tip, const CBlockIndex* new } } - // Call base class Rewind to update the best block pointer - return BaseIndex::Rewind(current_tip, new_tip); + return true; } BaseIndex::DB& AddressIndex::GetDB() const { return *m_db; } diff --git a/src/index/addressindex.h b/src/index/addressindex.h index 5244ab808828..cce6c70517c4 100644 --- a/src/index/addressindex.h +++ b/src/index/addressindex.h @@ -62,10 +62,10 @@ class AddressIndex final : public BaseIndex bool AllowPrune() const override { return false; } /// Write block data to the index databases - bool WriteBlock(const CBlock& block, const CBlockIndex* pindex) override; + bool CustomAppend(const interfaces::BlockInfo& block) override; /// Custom rewind to handle both transaction history and unspent index - bool Rewind(const CBlockIndex* current_tip, const CBlockIndex* new_tip) override; + bool CustomRewind(const interfaces::BlockKey& current_tip, const interfaces::BlockKey& new_tip) override; BaseIndex::DB& GetDB() const override; @@ -73,7 +73,7 @@ class AddressIndex final : public BaseIndex public: /// Constructs the index, which becomes available to be queried - explicit AddressIndex(size_t n_cache_size, bool f_memory = false, bool f_wipe = false); + explicit AddressIndex(std::unique_ptr chain, size_t n_cache_size, bool f_memory = false, bool f_wipe = false); /// Destructor virtual ~AddressIndex() override; diff --git a/src/index/base.cpp b/src/index/base.cpp index 1016d00088ad..4c4a865ed747 100644 --- a/src/index/base.cpp +++ b/src/index/base.cpp @@ -4,7 +4,10 @@ #include #include +#include +#include #include +#include #include #include #include @@ -31,6 +34,15 @@ void BaseIndex::FatalErrorImpl(const std::string& message) StartShutdown(); } +CBlockLocator GetLocator(interfaces::Chain& chain, const uint256& block_hash) +{ + CBlockLocator locator; + bool found = chain.findBlock(block_hash, interfaces::FoundBlock().locator(locator)); + assert(found); + assert(!locator.IsNull()); + return locator; +} + BaseIndex::DB::DB(const fs::path& path, size_t n_cache_size, bool f_memory, bool f_wipe, bool f_obfuscate) : CDBWrapper(path, n_cache_size, f_memory, f_wipe, f_obfuscate) {} @@ -49,6 +61,9 @@ void BaseIndex::DB::WriteBestBlock(CDBBatch& batch, const CBlockLocator& locator batch.Write(DB_BEST_BLOCK, locator); } +BaseIndex::BaseIndex(std::unique_ptr chain) + : m_chain{std::move(chain)} {} + BaseIndex::~BaseIndex() { Interrupt(); @@ -164,12 +179,15 @@ void BaseIndex::ThreadSync() } CBlock block; + interfaces::BlockInfo block_info = kernel::MakeBlockInfo(pindex); if (!ReadBlockFromDisk(block, pindex, consensus_params)) { FatalError("%s: Failed to read block %s from disk", __func__, pindex->GetBlockHash().ToString()); return; + } else { + block_info.data = █ } - if (!WriteBlock(block, pindex)) { + if (!CustomAppend(block_info)) { FatalError("%s: Failed to write block %s to index database", __func__, pindex->GetBlockHash().ToString()); return; @@ -200,22 +218,20 @@ void BaseIndex::ThreadSync() bool BaseIndex::Commit() { - CDBBatch batch(GetDB()); - if (!CommitInternal(batch) || !GetDB().WriteBatch(batch)) { - return error("%s: Failed to commit latest %s state", __func__, GetName()); - } - return true; -} - -bool BaseIndex::CommitInternal(CDBBatch& batch) -{ - LOCK(cs_main); // Don't commit anything if we haven't indexed any block yet // (this could happen if init is interrupted). - if (m_best_block_index == nullptr) { - return false; + bool ok = m_best_block_index != nullptr; + if (ok) { + CDBBatch batch(GetDB()); + ok = CustomCommit(batch); + if (ok) { + GetDB().WriteBestBlock(batch, GetLocator(*m_chain, m_best_block_index.load()->GetBlockHash())); + ok = GetDB().WriteBatch(batch); + } + } + if (!ok) { + return error("%s: Failed to commit latest %s state", __func__, GetName()); } - GetDB().WriteBestBlock(batch, m_chainstate->m_chain.GetLocator(m_best_block_index)); return true; } @@ -224,6 +240,10 @@ bool BaseIndex::Rewind(const CBlockIndex* current_tip, const CBlockIndex* new_ti assert(current_tip == m_best_block_index); assert(current_tip->GetAncestor(new_tip->nHeight) == new_tip); + if (!CustomRewind({current_tip->GetBlockHash(), current_tip->nHeight}, {new_tip->GetBlockHash(), new_tip->nHeight})) { + return false; + } + // In the case of a reorg, ensure persisted block locator is not stale. // Pruning has a minimum of 288 blocks-to-keep and getting the index // out of sync may be possible but a users fault. @@ -271,8 +291,8 @@ void BaseIndex::BlockConnected(const std::shared_ptr& block, const return; } } - - if (WriteBlock(*block, pindex)) { + interfaces::BlockInfo block_info = kernel::MakeBlockInfo(pindex, block.get()); + if (CustomAppend(block_info)) { // Setting the best block index is intentionally the last step of this // function, so BlockUntilSyncedToCurrentChain callers waiting for the // best block index to be updated can rely on the block being fully @@ -377,13 +397,18 @@ void BaseIndex::Interrupt() m_interrupt(); } -bool BaseIndex::Start(Chainstate& active_chainstate) +bool BaseIndex::Start() { - m_chainstate = &active_chainstate; + // m_chainstate member gives indexing code access to node internals. It is + // removed in followup https://github.com/bitcoin/bitcoin/pull/24230 + m_chainstate = &m_chain->context()->chainman->ActiveChainstate(); // Need to register this ValidationInterface before running Init(), so that // callbacks are not missed if Init sets m_synced to true. RegisterValidationInterface(this); - if (!Init()) { + if (!Init()) return false; + + const CBlockIndex* index = m_best_block_index.load(); + if (!CustomInit(index ? std::make_optional(interfaces::BlockKey{index->GetBlockHash(), index->nHeight}) : std::nullopt)) { return false; } diff --git a/src/index/base.h b/src/index/base.h index 7a7752a92a77..a2e0d941d776 100644 --- a/src/index/base.h +++ b/src/index/base.h @@ -6,6 +6,7 @@ #define BITCOIN_INDEX_BASE_H #include +#include #include #include #include @@ -15,6 +16,9 @@ class CBlock; class CBlockIndex; class Chainstate; +namespace interfaces { +class Chain; +} // namespace interfaces struct IndexSummary { std::string name; @@ -66,6 +70,9 @@ class BaseIndex : public CValidationInterface std::thread m_thread_sync; CThreadInterrupt m_interrupt; + /// Read best block locator and check that data needed to sync has not been pruned. + bool Init(); + /// Sync the index with the block index starting from the current best block. /// Intended to be run in its own thread, m_thread_sync, and can be /// interrupted with m_interrupt. Once the index gets in sync, the m_synced @@ -83,9 +90,13 @@ class BaseIndex : public CValidationInterface /// getting corrupted. bool Commit(); + /// Loop over disconnected blocks and call CustomRewind. + bool Rewind(const CBlockIndex* current_tip, const CBlockIndex* new_tip); + virtual bool AllowPrune() const = 0; protected: + std::unique_ptr m_chain; Chainstate* m_chainstate{nullptr}; void BlockConnected(const std::shared_ptr& block, const CBlockIndex* pindex) override; @@ -94,21 +105,19 @@ class BaseIndex : public CValidationInterface void ChainStateFlushed(const CBlockLocator& locator) override; - const CBlockIndex* CurrentIndex() { return m_best_block_index.load(); }; - /// Initialize internal state from the database and block index. - [[nodiscard]] virtual bool Init(); + [[nodiscard]] virtual bool CustomInit(const std::optional& block) { return true; } /// Write update index entries for a newly connected block. - virtual bool WriteBlock(const CBlock& block, const CBlockIndex* pindex) { return true; } + [[nodiscard]] virtual bool CustomAppend(const interfaces::BlockInfo& block) { return true; } /// Virtual method called internally by Commit that can be overridden to atomically /// commit more index state. - virtual bool CommitInternal(CDBBatch& batch); + virtual bool CustomCommit(CDBBatch& batch) { return true; } /// Rewind index to an earlier chain tip during a chain reorg. The tip must /// be an ancestor of the current best block. - virtual bool Rewind(const CBlockIndex* current_tip, const CBlockIndex* new_tip); + [[nodiscard]] virtual bool CustomRewind(const interfaces::BlockKey& current_tip, const interfaces::BlockKey& new_tip) { return true; } virtual DB& GetDB() const = 0; @@ -128,6 +137,7 @@ class BaseIndex : public CValidationInterface void SetBestBlockIndex(const CBlockIndex* block); public: + BaseIndex(std::unique_ptr chain); /// Destructor interrupts sync thread if running and blocks until it exits. virtual ~BaseIndex(); @@ -142,7 +152,7 @@ class BaseIndex : public CValidationInterface /// Start initializes the sync state and registers the instance as a /// ValidationInterface so that it stays in sync with blockchain updates. - [[nodiscard]] bool Start(Chainstate& active_chainstate); + [[nodiscard]] bool Start(); /// Stops the instance from staying in sync with blockchain updates. void Stop(); diff --git a/src/index/blockfilterindex.cpp b/src/index/blockfilterindex.cpp index 5449a93b0954..ad740fcad97c 100644 --- a/src/index/blockfilterindex.cpp +++ b/src/index/blockfilterindex.cpp @@ -10,6 +10,7 @@ #include #include #include +#include using node::UndoReadFromDisk; @@ -101,9 +102,9 @@ struct DBHashKey { static std::map g_filter_indexes; -BlockFilterIndex::BlockFilterIndex(BlockFilterType filter_type, +BlockFilterIndex::BlockFilterIndex(std::unique_ptr chain, BlockFilterType filter_type, size_t n_cache_size, bool f_memory, bool f_wipe) - : m_filter_type(filter_type) + : BaseIndex(std::move(chain)), m_filter_type(filter_type) { const std::string& filter_name = BlockFilterTypeName(filter_type); if (filter_name.empty()) throw std::invalid_argument("unknown filter_type"); @@ -127,7 +128,7 @@ BlockFilterIndex::BlockFilterIndex(BlockFilterType filter_type, m_filter_fileseq = std::make_unique(std::move(path), "fltr", FLTR_FILE_CHUNK_SIZE); } -bool BlockFilterIndex::Init() +bool BlockFilterIndex::CustomInit(const std::optional& block) { // Check version compatibility first int version = 0; @@ -154,10 +155,10 @@ bool BlockFilterIndex::Init() m_next_filter_pos.nFile = 0; m_next_filter_pos.nPos = 0; } - return BaseIndex::Init(); + return true; } -bool BlockFilterIndex::CommitInternal(CDBBatch& batch) +bool BlockFilterIndex::CustomCommit(CDBBatch& batch) { const FlatFilePos& pos = m_next_filter_pos; @@ -176,7 +177,7 @@ bool BlockFilterIndex::CommitInternal(CDBBatch& batch) } batch.Write(DB_FILTER_POS, pos); - return BaseIndex::CommitInternal(batch); + return true; } bool BlockFilterIndex::ReadFilterFromDisk(const FlatFilePos& pos, const uint256& hash, BlockFilter& filter) const @@ -247,22 +248,25 @@ size_t BlockFilterIndex::WriteFilterToDisk(FlatFilePos& pos, const BlockFilter& return data_size; } -bool BlockFilterIndex::WriteBlock(const CBlock& block, const CBlockIndex* pindex) +bool BlockFilterIndex::CustomAppend(const interfaces::BlockInfo& block) { CBlockUndo block_undo; uint256 prev_header; - if (pindex->nHeight > 0) { + if (block.height > 0) { + // pindex variable gives indexing code access to node internals. It + // will be removed in upcoming commit + const CBlockIndex* pindex = WITH_LOCK(cs_main, return m_chainstate->m_blockman.LookupBlockIndex(block.hash)); if (!UndoReadFromDisk(block_undo, pindex)) { return false; } std::pair read_out; - if (!m_db->Read(DBHeightKey(pindex->nHeight - 1), read_out)) { + if (!m_db->Read(DBHeightKey(block.height - 1), read_out)) { return false; } - uint256 expected_block_hash = pindex->pprev->GetBlockHash(); + uint256 expected_block_hash = *Assert(block.prev_hash); if (read_out.first != expected_block_hash) { return error("%s: previous block header belongs to unexpected block %s; expected %s", __func__, read_out.first.ToString(), expected_block_hash.ToString()); @@ -271,18 +275,18 @@ bool BlockFilterIndex::WriteBlock(const CBlock& block, const CBlockIndex* pindex prev_header = read_out.second.header; } - BlockFilter filter(m_filter_type, block, block_undo); + BlockFilter filter(m_filter_type, *Assert(block.data), block_undo); size_t bytes_written = WriteFilterToDisk(m_next_filter_pos, filter); if (bytes_written == 0) return false; std::pair value; - value.first = pindex->GetBlockHash(); + value.first = block.hash; value.second.hash = filter.GetHash(); value.second.header = filter.ComputeHeader(prev_header); value.second.pos = m_next_filter_pos; - if (!m_db->Write(DBHeightKey(pindex->nHeight), value)) { + if (!m_db->Write(DBHeightKey(block.height), value)) { return false; } @@ -316,17 +320,15 @@ bool BlockFilterIndex::WriteBlock(const CBlock& block, const CBlockIndex* pindex return true; } -bool BlockFilterIndex::Rewind(const CBlockIndex* current_tip, const CBlockIndex* new_tip) +bool BlockFilterIndex::CustomRewind(const interfaces::BlockKey& current_tip, const interfaces::BlockKey& new_tip) { - assert(current_tip->GetAncestor(new_tip->nHeight) == new_tip); - CDBBatch batch(*m_db); std::unique_ptr db_it(m_db->NewIterator()); // During a reorg, we need to copy all filters for blocks that are getting disconnected from the // height index to the hash index so we can still find them when the height index entries are // overwritten. - if (!CopyHeightIndexToHashIndex(*db_it, batch, m_name, new_tip->nHeight, current_tip->nHeight)) { + if (!CopyHeightIndexToHashIndex(*db_it, batch, m_name, new_tip.height, current_tip.height)) { return false; } @@ -336,7 +338,7 @@ bool BlockFilterIndex::Rewind(const CBlockIndex* current_tip, const CBlockIndex* batch.Write(DB_FILTER_POS, m_next_filter_pos); if (!m_db->WriteBatch(batch)) return false; - return BaseIndex::Rewind(current_tip, new_tip); + return true; } static bool LookupOne(const CDBWrapper& db, const CBlockIndex* block_index, DBVal& result) @@ -500,12 +502,12 @@ void ForEachBlockFilterIndex(std::function fn) for (auto& entry : g_filter_indexes) fn(entry.second); } -bool InitBlockFilterIndex(BlockFilterType filter_type, +bool InitBlockFilterIndex(std::function()> make_chain, BlockFilterType filter_type, size_t n_cache_size, bool f_memory, bool f_wipe) { auto result = g_filter_indexes.emplace(std::piecewise_construct, std::forward_as_tuple(filter_type), - std::forward_as_tuple(filter_type, + std::forward_as_tuple(make_chain(), filter_type, n_cache_size, f_memory, f_wipe)); return result.second; } diff --git a/src/index/blockfilterindex.h b/src/index/blockfilterindex.h index 21c425985f17..30a583b34267 100644 --- a/src/index/blockfilterindex.h +++ b/src/index/blockfilterindex.h @@ -47,13 +47,13 @@ class BlockFilterIndex final : public BaseIndex bool AllowPrune() const override { return true; } protected: - bool Init() override; + bool CustomInit(const std::optional& block) override; - bool CommitInternal(CDBBatch& batch) override; + bool CustomCommit(CDBBatch& batch) override; - bool WriteBlock(const CBlock& block, const CBlockIndex* pindex) override; + bool CustomAppend(const interfaces::BlockInfo& block) override; - bool Rewind(const CBlockIndex* current_tip, const CBlockIndex* new_tip) override; + bool CustomRewind(const interfaces::BlockKey& current_tip, const interfaces::BlockKey& new_tip) override; BaseIndex::DB& GetDB() const LIFETIMEBOUND override { return *m_db; } @@ -61,7 +61,7 @@ class BlockFilterIndex final : public BaseIndex public: /** Constructs the index, which becomes available to be queried. */ - explicit BlockFilterIndex(BlockFilterType filter_type, + explicit BlockFilterIndex(std::unique_ptr chain, BlockFilterType filter_type, size_t n_cache_size, bool f_memory = false, bool f_wipe = false); BlockFilterType GetFilterType() const { return m_filter_type; } @@ -94,7 +94,7 @@ void ForEachBlockFilterIndex(std::function fn); * Initialize a block filter index for the given type if one does not already exist. Returns true if * a new index is created and false if one has already been initialized. */ -bool InitBlockFilterIndex(BlockFilterType filter_type, +bool InitBlockFilterIndex(std::function()> make_chain, BlockFilterType filter_type, size_t n_cache_size, bool f_memory = false, bool f_wipe = false); /** diff --git a/src/index/coinstatsindex.cpp b/src/index/coinstatsindex.cpp index 479198ed8629..54481366f250 100644 --- a/src/index/coinstatsindex.cpp +++ b/src/index/coinstatsindex.cpp @@ -104,7 +104,8 @@ struct DBHashKey { std::unique_ptr g_coin_stats_index; -CoinStatsIndex::CoinStatsIndex(size_t n_cache_size, bool f_memory, bool f_wipe) +CoinStatsIndex::CoinStatsIndex(std::unique_ptr chain, size_t n_cache_size, bool f_memory, bool f_wipe) + : BaseIndex(std::move(chain)) { fs::path path{gArgs.GetDataDirNet() / "indexes" / "coinstats"}; fs::create_directories(path); @@ -112,24 +113,28 @@ CoinStatsIndex::CoinStatsIndex(size_t n_cache_size, bool f_memory, bool f_wipe) m_db = std::make_unique(path / "db", n_cache_size, f_memory, f_wipe); } -bool CoinStatsIndex::WriteBlock(const CBlock& block, const CBlockIndex* pindex) +bool CoinStatsIndex::CustomAppend(const interfaces::BlockInfo& block) { CBlockUndo block_undo; + // pindex variable gives indexing code access to node internals. It + // will be removed in upcoming commit + const CBlockIndex* pindex = WITH_LOCK(cs_main, return m_chainstate->m_blockman.LookupBlockIndex(block.hash)); + // TODO: get rid of pindex for GetBlockSubsidy const CAmount block_subsidy{GetBlockSubsidy(pindex, Params().GetConsensus())}; m_total_subsidy += block_subsidy; // Ignore genesis block - if (pindex->nHeight > 0) { + if (block.height > 0) { if (!UndoReadFromDisk(block_undo, pindex)) { return false; } std::pair read_out; - if (!m_db->Read(DBHeightKey(pindex->nHeight - 1), read_out)) { + if (!m_db->Read(DBHeightKey(block.height - 1), read_out)) { return false; } - uint256 expected_block_hash{pindex->pprev->GetBlockHash()}; + uint256 expected_block_hash{*Assert(block.prev_hash)}; if (read_out.first != expected_block_hash) { LogPrintf("WARNING: previous block header belongs to unexpected block %s; expected %s\n", read_out.first.ToString(), expected_block_hash.ToString()); @@ -141,12 +146,13 @@ bool CoinStatsIndex::WriteBlock(const CBlock& block, const CBlockIndex* pindex) } // Add the new utxos created from the block - for (size_t i = 0; i < block.vtx.size(); ++i) { - const auto& tx{block.vtx.at(i)}; + assert(block.data); + for (size_t i = 0; i < block.data->vtx.size(); ++i) { + const auto& tx{block.data->vtx.at(i)}; for (uint32_t j = 0; j < tx->vout.size(); ++j) { const CTxOut& out{tx->vout[j]}; - Coin coin{out, pindex->nHeight, tx->IsCoinBase()}; + Coin coin{out, block.height, tx->IsCoinBase()}; COutPoint outpoint{tx->GetHash(), j}; // Skip unspendable coins @@ -202,7 +208,7 @@ bool CoinStatsIndex::WriteBlock(const CBlock& block, const CBlockIndex* pindex) m_total_unspendables_unclaimed_rewards += unclaimed_rewards; std::pair value; - value.first = pindex->GetBlockHash(); + value.first = block.hash; value.second.transaction_output_count = m_transaction_output_count; value.second.bogo_size = m_bogo_size; value.second.total_amount = m_total_amount; @@ -222,7 +228,7 @@ bool CoinStatsIndex::WriteBlock(const CBlock& block, const CBlockIndex* pindex) // Intentionally do not update DB_MUHASH here so it stays in sync with // DB_BEST_BLOCK, and the index is not corrupted if there is an unclean shutdown. - return m_db->Write(DBHeightKey(pindex->nHeight), value); + return m_db->Write(DBHeightKey(block.height), value); } [[nodiscard]] static bool CopyHeightIndexToHashIndex(CDBIterator& db_it, CDBBatch& batch, @@ -251,17 +257,15 @@ bool CoinStatsIndex::WriteBlock(const CBlock& block, const CBlockIndex* pindex) return true; } -bool CoinStatsIndex::Rewind(const CBlockIndex* current_tip, const CBlockIndex* new_tip) +bool CoinStatsIndex::CustomRewind(const interfaces::BlockKey& current_tip, const interfaces::BlockKey& new_tip) { - assert(current_tip->GetAncestor(new_tip->nHeight) == new_tip); - CDBBatch batch(*m_db); std::unique_ptr db_it(m_db->NewIterator()); // During a reorg, we need to copy all hash digests for blocks that are // getting disconnected from the height index to the hash index so we can // still find them when the height index entries are overwritten. - if (!CopyHeightIndexToHashIndex(*db_it, batch, m_name, new_tip->nHeight, current_tip->nHeight)) { + if (!CopyHeightIndexToHashIndex(*db_it, batch, m_name, new_tip.height, current_tip.height)) { return false; } @@ -269,7 +273,8 @@ bool CoinStatsIndex::Rewind(const CBlockIndex* current_tip, const CBlockIndex* n { LOCK(cs_main); - const CBlockIndex* iter_tip{m_chainstate->m_blockman.LookupBlockIndex(current_tip->GetBlockHash())}; + const CBlockIndex* iter_tip{m_chainstate->m_blockman.LookupBlockIndex(current_tip.hash)}; + const CBlockIndex* new_tip_index{m_chainstate->m_blockman.LookupBlockIndex(new_tip.hash)}; const auto& consensus_params{Params().GetConsensus()}; do { @@ -285,29 +290,29 @@ bool CoinStatsIndex::Rewind(const CBlockIndex* current_tip, const CBlockIndex* n } iter_tip = iter_tip->GetAncestor(iter_tip->nHeight - 1); - } while (new_tip != iter_tip); + } while (new_tip_index != iter_tip); } - return BaseIndex::Rewind(current_tip, new_tip); + return true; } -static bool LookUpOne(const CDBWrapper& db, const CBlockIndex* block_index, DBVal& result) +static bool LookUpOne(const CDBWrapper& db, const interfaces::BlockKey& block, DBVal& result) { // First check if the result is stored under the height index and the value // there matches the block hash. This should be the case if the block is on // the active chain. std::pair read_out; - if (!db.Read(DBHeightKey(block_index->nHeight), read_out)) { + if (!db.Read(DBHeightKey(block.height), read_out)) { return false; } - if (read_out.first == block_index->GetBlockHash()) { + if (read_out.first == block.hash) { result = std::move(read_out.second); return true; } // If value at the height index corresponds to an different block, the // result will be stored in the hash index. - return db.Read(DBHashKey(block_index->GetBlockHash()), result); + return db.Read(DBHashKey(block.hash), result); } std::optional CoinStatsIndex::LookUpStats(const CBlockIndex* block_index) const @@ -316,7 +321,7 @@ std::optional CoinStatsIndex::LookUpStats(const CBlockIndex* block_ stats.index_used = true; DBVal entry; - if (!LookUpOne(*m_db, block_index, entry)) { + if (!LookUpOne(*m_db, {block_index->GetBlockHash(), block_index->nHeight}, entry)) { return std::nullopt; } @@ -337,7 +342,7 @@ std::optional CoinStatsIndex::LookUpStats(const CBlockIndex* block_ return stats; } -bool CoinStatsIndex::Init() +bool CoinStatsIndex::CustomInit(const std::optional& block) { if (!m_db->Read(DB_MUHASH, m_muhash)) { // Check that the cause of the read failure is that the key does not @@ -349,13 +354,9 @@ bool CoinStatsIndex::Init() } } - if (!BaseIndex::Init()) return false; - - const CBlockIndex* pindex{CurrentIndex()}; - - if (pindex) { + if (block) { DBVal entry; - if (!LookUpOne(*m_db, pindex, entry)) { + if (!LookUpOne(*m_db, *block, entry)) { return error("%s: Cannot read current %s state; index may be corrupted", __func__, GetName()); } @@ -382,12 +383,12 @@ bool CoinStatsIndex::Init() return true; } -bool CoinStatsIndex::CommitInternal(CDBBatch& batch) +bool CoinStatsIndex::CustomCommit(CDBBatch& batch) { // DB_MUHASH should always be committed in a batch together with DB_BEST_BLOCK // to prevent an inconsistent state of the DB. batch.Write(DB_MUHASH, m_muhash); - return BaseIndex::CommitInternal(batch); + return true; } // Reverse a single block as part of a reorg diff --git a/src/index/coinstatsindex.h b/src/index/coinstatsindex.h index 1552fbb81661..bf30bf12fc66 100644 --- a/src/index/coinstatsindex.h +++ b/src/index/coinstatsindex.h @@ -41,13 +41,13 @@ class CoinStatsIndex final : public BaseIndex bool AllowPrune() const override { return true; } protected: - bool Init() override; + bool CustomInit(const std::optional& block) override; - bool CommitInternal(CDBBatch& batch) override; + bool CustomCommit(CDBBatch& batch) override; - bool WriteBlock(const CBlock& block, const CBlockIndex* pindex) override; + bool CustomAppend(const interfaces::BlockInfo& block) override; - bool Rewind(const CBlockIndex* current_tip, const CBlockIndex* new_tip) override; + bool CustomRewind(const interfaces::BlockKey& current_tip, const interfaces::BlockKey& new_tip) override; BaseIndex::DB& GetDB() const override { return *m_db; } @@ -55,7 +55,7 @@ class CoinStatsIndex final : public BaseIndex public: // Constructs the index, which becomes available to be queried. - explicit CoinStatsIndex(size_t n_cache_size, bool f_memory = false, bool f_wipe = false); + explicit CoinStatsIndex(std::unique_ptr chain, size_t n_cache_size, bool f_memory = false, bool f_wipe = false); // Look up stats for a specific block using CBlockIndex std::optional LookUpStats(const CBlockIndex* block_index) const; diff --git a/src/index/spentindex.cpp b/src/index/spentindex.cpp index 7db708dabcf1..a12c37a1b553 100644 --- a/src/index/spentindex.cpp +++ b/src/index/spentindex.cpp @@ -15,6 +15,7 @@ #include #include #include +#include constexpr uint8_t DB_SPENTINDEX{'p'}; @@ -53,38 +54,43 @@ bool SpentIndex::DB::EraseSpentIndex(const std::vector& keys) return CDBWrapper::WriteBatch(batch); } -SpentIndex::SpentIndex(size_t n_cache_size, bool f_memory, bool f_wipe) : +SpentIndex::SpentIndex(std::unique_ptr chain, size_t n_cache_size, bool f_memory, bool f_wipe) : + BaseIndex(std::move(chain)), m_db(std::make_unique(n_cache_size, f_memory, f_wipe)) { } SpentIndex::~SpentIndex() = default; -bool SpentIndex::WriteBlock(const CBlock& block, const CBlockIndex* pindex) +bool SpentIndex::CustomAppend(const interfaces::BlockInfo& block) { // Skip genesis block (no inputs to index) - if (pindex->nHeight == 0) { + if (block.height == 0) { return true; } + // pindex variable gives indexing code access to node internals. It + // will be removed in upcoming commit + const CBlockIndex* pindex = WITH_LOCK(cs_main, return m_chainstate->m_blockman.LookupBlockIndex(block.hash)); + // Read undo data for this block to get information about spent outputs CBlockUndo blockundo; if (!node::UndoReadFromDisk(blockundo, pindex)) { return error("%s: Failed to read undo data for block %s at height %d", __func__, - pindex->GetBlockHash().ToString(), pindex->nHeight); + block.hash.ToString(), block.height); } std::vector entries; // Process each non-coinbase transaction // blockundo.vtxundo[i] corresponds to block.vtx[i+1] (coinbase is skipped in undo data) - if (blockundo.vtxundo.size() != block.vtx.size() - 1) { + if (blockundo.vtxundo.size() != block.data->vtx.size() - 1) { return error("%s: Undo data size mismatch for block %s (expected %zu, got %zu)", __func__, - pindex->GetBlockHash().ToString(), block.vtx.size() - 1, blockundo.vtxundo.size()); + block.hash.ToString(), block.data->vtx.size() - 1, blockundo.vtxundo.size()); } for (size_t i = 0; i < blockundo.vtxundo.size(); i++) { - const CTransactionRef& tx = block.vtx[i + 1]; // +1 to skip coinbase + const CTransactionRef& tx = block.data->vtx[i + 1]; // +1 to skip coinbase const CTxUndo& txundo = blockundo.vtxundo[i]; const uint256 txhash = tx->GetHash(); @@ -104,7 +110,7 @@ bool SpentIndex::WriteBlock(const CBlock& block, const CBlockIndex* pindex) // Create spent index entry: spent output -> spending tx info CSpentIndexKey key(input.prevout.hash, input.prevout.n); - CSpentIndexValue value(txhash, j, pindex->nHeight, prevout.nValue, address_type, address_bytes); + CSpentIndexValue value(txhash, j, block.height, prevout.nValue, address_type, address_bytes); entries.emplace_back(key, value); } @@ -113,12 +119,14 @@ bool SpentIndex::WriteBlock(const CBlock& block, const CBlockIndex* pindex) return m_db->WriteBatch(entries); } -bool SpentIndex::Rewind(const CBlockIndex* current_tip, const CBlockIndex* new_tip) +bool SpentIndex::CustomRewind(const interfaces::BlockKey& current_tip, const interfaces::BlockKey& new_tip) { - assert(current_tip->GetAncestor(new_tip->nHeight) == new_tip); + const CBlockIndex* current_tip_index = WITH_LOCK(cs_main, return m_chainstate->m_blockman.LookupBlockIndex(current_tip.hash)); + const CBlockIndex* new_tip_index = WITH_LOCK(cs_main, return m_chainstate->m_blockman.LookupBlockIndex(new_tip.hash)); + assert(current_tip_index->GetAncestor(new_tip_index->nHeight) == new_tip_index); // Erase spent index entries for blocks being rewound - for (const CBlockIndex* pindex = current_tip; pindex != new_tip; pindex = pindex->pprev) { + for (const CBlockIndex* pindex = current_tip_index; pindex != new_tip_index; pindex = pindex->pprev) { // Skip genesis block if (pindex->nHeight == 0) continue; @@ -147,8 +155,7 @@ bool SpentIndex::Rewind(const CBlockIndex* current_tip, const CBlockIndex* new_t } } - // Call base class Rewind to update the best block pointer - return BaseIndex::Rewind(current_tip, new_tip); + return true; } BaseIndex::DB& SpentIndex::GetDB() const { return *m_db; } diff --git a/src/index/spentindex.h b/src/index/spentindex.h index 55478eaefc06..00f10d355fa1 100644 --- a/src/index/spentindex.h +++ b/src/index/spentindex.h @@ -8,14 +8,8 @@ #include #include -#include - static constexpr bool DEFAULT_SPENTINDEX{false}; -struct CSpentIndexTxInfo { - std::map mSpentInfo; -}; - /** * SpentIndex tracks which transactions spend specific outputs. * For each spent output, it records the spending transaction details, @@ -51,10 +45,9 @@ class SpentIndex final : public BaseIndex bool EraseSpentIndex(const std::vector& keys); }; - bool WriteBlock(const CBlock& block, const CBlockIndex* pindex) override; + bool CustomAppend(const interfaces::BlockInfo& block) override; - /// Custom rewind to handle spent index cleanup - bool Rewind(const CBlockIndex* current_tip, const CBlockIndex* new_tip) override; + bool CustomRewind(const interfaces::BlockKey& current_tip, const interfaces::BlockKey& new_tip) override; BaseIndex::DB& GetDB() const override; const char* GetName() const override { return "spentindex"; } @@ -64,7 +57,7 @@ class SpentIndex final : public BaseIndex public: /// Constructs a new SpentIndex. - explicit SpentIndex(size_t n_cache_size, bool f_memory = false, bool f_wipe = false); + explicit SpentIndex(std::unique_ptr chain, size_t n_cache_size, bool f_memory = false, bool f_wipe = false); /// Destructor virtual ~SpentIndex() override; diff --git a/src/index/spentindex_types.h b/src/index/spentindex_types.h index 536453235db0..6140118e95f8 100644 --- a/src/index/spentindex_types.h +++ b/src/index/spentindex_types.h @@ -88,4 +88,8 @@ struct CSpentIndexKeyCompare { using CSpentIndexEntry = std::pair; +struct CSpentIndexTxInfo { + std::map mSpentInfo; +}; + #endif // BITCOIN_INDEX_SPENTINDEX_TYPES_H diff --git a/src/index/timestampindex.cpp b/src/index/timestampindex.cpp index 9ea3ad3e65a6..b0e7f54942c1 100644 --- a/src/index/timestampindex.cpp +++ b/src/index/timestampindex.cpp @@ -8,6 +8,7 @@ #include #include #include +#include constexpr uint8_t DB_TIMESTAMPINDEX{'s'}; @@ -49,31 +50,34 @@ bool TimestampIndex::DB::EraseTimestampIndex(const CTimestampIndexKey& key) return CDBWrapper::Erase(std::make_pair(DB_TIMESTAMPINDEX, key)); } -TimestampIndex::TimestampIndex(size_t n_cache_size, bool f_memory, bool f_wipe) : +TimestampIndex::TimestampIndex(std::unique_ptr chain, size_t n_cache_size, bool f_memory, bool f_wipe) : + BaseIndex(std::move(chain)), m_db(std::make_unique(n_cache_size, f_memory, f_wipe)) { } TimestampIndex::~TimestampIndex() = default; -bool TimestampIndex::WriteBlock(const CBlock& block, const CBlockIndex* pindex) +bool TimestampIndex::CustomAppend(const interfaces::BlockInfo& block) { // Skip genesis block - if (pindex->nHeight == 0) return true; + if (block.height == 0) return true; // Create timestamp index key from block metadata - CTimestampIndexKey key(pindex->nTime, pindex->GetBlockHash()); + CTimestampIndexKey key(block.data->nTime, block.hash); // Write to database return m_db->Write(key); } -bool TimestampIndex::Rewind(const CBlockIndex* current_tip, const CBlockIndex* new_tip) +bool TimestampIndex::CustomRewind(const interfaces::BlockKey& current_tip, const interfaces::BlockKey& new_tip) { - assert(current_tip->GetAncestor(new_tip->nHeight) == new_tip); + const CBlockIndex* current_tip_index = WITH_LOCK(cs_main, return m_chainstate->m_blockman.LookupBlockIndex(current_tip.hash)); + const CBlockIndex* new_tip_index = WITH_LOCK(cs_main, return m_chainstate->m_blockman.LookupBlockIndex(new_tip.hash)); + assert(current_tip_index->GetAncestor(new_tip_index->nHeight) == new_tip_index); // Erase timestamp index entries for blocks being rewound - for (const CBlockIndex* pindex = current_tip; pindex != new_tip; pindex = pindex->pprev) { + for (const CBlockIndex* pindex = current_tip_index; pindex != new_tip_index; pindex = pindex->pprev) { // Skip genesis block if (pindex->nHeight == 0) continue; @@ -84,8 +88,7 @@ bool TimestampIndex::Rewind(const CBlockIndex* current_tip, const CBlockIndex* n } } - // Call base class Rewind to update the best block pointer - return BaseIndex::Rewind(current_tip, new_tip); + return true; } BaseIndex::DB& TimestampIndex::GetDB() const { return *m_db; } diff --git a/src/index/timestampindex.h b/src/index/timestampindex.h index 7b31f84837ac..544b1116c609 100644 --- a/src/index/timestampindex.h +++ b/src/index/timestampindex.h @@ -40,10 +40,10 @@ class TimestampIndex final : public BaseIndex bool EraseTimestampIndex(const CTimestampIndexKey& key); }; - bool WriteBlock(const CBlock& block, const CBlockIndex* pindex) override; + bool CustomAppend(const interfaces::BlockInfo& block) override; /// Custom rewind to handle timestamp index cleanup - bool Rewind(const CBlockIndex* current_tip, const CBlockIndex* new_tip) override; + bool CustomRewind(const interfaces::BlockKey& current_tip, const interfaces::BlockKey& new_tip) override; BaseIndex::DB& GetDB() const override; const char* GetName() const override { return "timestampindex"; } @@ -53,7 +53,7 @@ class TimestampIndex final : public BaseIndex public: /// Constructs a new TimestampIndex. - explicit TimestampIndex(size_t n_cache_size, bool f_memory = false, bool f_wipe = false); + explicit TimestampIndex(std::unique_ptr chain, size_t n_cache_size, bool f_memory = false, bool f_wipe = false); /// Destructor virtual ~TimestampIndex() override; diff --git a/src/index/txindex.cpp b/src/index/txindex.cpp index 1a6a0cf7fe01..de9114cea59b 100644 --- a/src/index/txindex.cpp +++ b/src/index/txindex.cpp @@ -48,23 +48,22 @@ bool TxIndex::DB::WriteTxs(const std::vector>& v_ return WriteBatch(batch); } -TxIndex::TxIndex(size_t n_cache_size, bool f_memory, bool f_wipe) - : m_db(std::make_unique(n_cache_size, f_memory, f_wipe)) +TxIndex::TxIndex(std::unique_ptr chain, size_t n_cache_size, bool f_memory, bool f_wipe) + : BaseIndex(std::move(chain)), m_db(std::make_unique(n_cache_size, f_memory, f_wipe)) {} TxIndex::~TxIndex() = default; -bool TxIndex::WriteBlock(const CBlock& block, const CBlockIndex* pindex) +bool TxIndex::CustomAppend(const interfaces::BlockInfo& block) { // Exclude genesis block transaction because outputs are not spendable. - if (pindex->nHeight == 0) return true; + if (block.height == 0) return true; - CDiskTxPos pos{ - WITH_LOCK(::cs_main, return pindex->GetBlockPos()), - GetSizeOfCompactSize(block.vtx.size())}; + assert(block.data); + CDiskTxPos pos({block.file_number, block.data_pos}, GetSizeOfCompactSize(block.data->vtx.size())); std::vector> vPos; - vPos.reserve(block.vtx.size()); - for (const auto& tx : block.vtx) { + vPos.reserve(block.data->vtx.size()); + for (const auto& tx : block.data->vtx) { vPos.emplace_back(tx->GetHash(), pos); pos.nTxOffset += ::GetSerializeSize(*tx, CLIENT_VERSION); } diff --git a/src/index/txindex.h b/src/index/txindex.h index 032fdd73d8a3..3ef4f3975dec 100644 --- a/src/index/txindex.h +++ b/src/index/txindex.h @@ -25,7 +25,7 @@ class TxIndex final : public BaseIndex bool AllowPrune() const override { return false; } protected: - bool WriteBlock(const CBlock& block, const CBlockIndex* pindex) override; + bool CustomAppend(const interfaces::BlockInfo& block) override; BaseIndex::DB& GetDB() const override; @@ -33,7 +33,7 @@ class TxIndex final : public BaseIndex public: /// Constructs the index, which becomes available to be queried. - explicit TxIndex(size_t n_cache_size, bool f_memory = false, bool f_wipe = false); + explicit TxIndex(std::unique_ptr chain, size_t n_cache_size, bool f_memory = false, bool f_wipe = false); // Destructor is declared because this class contains a unique_ptr to an incomplete type. virtual ~TxIndex() override; diff --git a/src/init.cpp b/src/init.cpp index 372347d54d2f..864dad9e9db7 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -2260,43 +2260,43 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) // ********************************************************* Step 8: start indexers if (args.GetBoolArg("-txindex", DEFAULT_TXINDEX)) { - g_txindex = std::make_unique(cache_sizes.tx_index, false, fReindex); - if (!g_txindex->Start(chainman.ActiveChainstate())) { + g_txindex = std::make_unique(interfaces::MakeChain(node), cache_sizes.tx_index, false, fReindex); + if (!g_txindex->Start()) { return false; } } if (args.GetBoolArg("-addressindex", DEFAULT_ADDRESSINDEX)) { - g_addressindex = std::make_unique(cache_sizes.address_index, false, fReindex); - if (!g_addressindex->Start(chainman.ActiveChainstate())) { + g_addressindex = std::make_unique(interfaces::MakeChain(node), cache_sizes.address_index, false, fReindex); + if (!g_addressindex->Start()) { return false; } } if (args.GetBoolArg("-timestampindex", DEFAULT_TIMESTAMPINDEX)) { - g_timestampindex = std::make_unique(cache_sizes.timestamp_index, false, fReindex); - if (!g_timestampindex->Start(chainman.ActiveChainstate())) { + g_timestampindex = std::make_unique(interfaces::MakeChain(node), cache_sizes.timestamp_index, false, fReindex); + if (!g_timestampindex->Start()) { return false; } } if (args.GetBoolArg("-spentindex", DEFAULT_SPENTINDEX)) { - g_spentindex = std::make_unique(cache_sizes.spent_index, false, fReindex); - if (!g_spentindex->Start(chainman.ActiveChainstate())) { + g_spentindex = std::make_unique(interfaces::MakeChain(node), cache_sizes.spent_index, false, fReindex); + if (!g_spentindex->Start()) { return false; } } for (const auto& filter_type : g_enabled_filter_types) { - InitBlockFilterIndex(filter_type, cache_sizes.filter_index, false, fReindex); - if (!GetBlockFilterIndex(filter_type)->Start(chainman.ActiveChainstate())) { + InitBlockFilterIndex([&]{ return interfaces::MakeChain(node); }, filter_type, cache_sizes.filter_index, false, fReindex); + if (!GetBlockFilterIndex(filter_type)->Start()) { return false; } } if (args.GetBoolArg("-coinstatsindex", DEFAULT_COINSTATSINDEX)) { - g_coin_stats_index = std::make_unique(/* cache size */ 0, false, fReindex); - if (!g_coin_stats_index->Start(chainman.ActiveChainstate())) { + g_coin_stats_index = std::make_unique(interfaces::MakeChain(node), /* cache size */ 0, false, fReindex); + if (!g_coin_stats_index->Start()) { return false; } } diff --git a/src/interfaces/chain.h b/src/interfaces/chain.h index c4bb7b7df0f2..f0faa8daa1c8 100644 --- a/src/interfaces/chain.h +++ b/src/interfaces/chain.h @@ -19,11 +19,10 @@ class ArgsManager; class CBlock; -class CConnman; +class CBlockUndo; class CFeeRate; class CRPCCommand; class CScheduler; -class CTxMemPool; class CFeeRate; class CBlockIndex; class Coin; @@ -49,6 +48,12 @@ namespace interfaces { class Wallet; class Handler; +//! Hash/height pair to help track and identify blocks. +struct BlockKey { + uint256 hash; + int height = -1; +}; + //! Helper for findBlock to selectively return pieces of block data. If block is //! found, data will be returned by setting specified output variables. If block //! is not found, output variables will keep their previous values. @@ -62,6 +67,8 @@ class FoundBlock FoundBlock& mtpTime(int64_t& mtp_time) { m_mtp_time = &mtp_time; return *this; } //! Return whether block is in the active (most-work) chain. FoundBlock& inActiveChain(bool& in_active_chain) { m_in_active_chain = &in_active_chain; return *this; } + //! Return locator if block is in the active chain. + FoundBlock& locator(CBlockLocator& locator) { m_locator = &locator; return *this; } //! Return next block in the active chain if current block is in the active chain. FoundBlock& nextBlock(const FoundBlock& next_block) { m_next_block = &next_block; return *this; } //! Read block data from disk. If the block exists but doesn't have data @@ -74,11 +81,25 @@ class FoundBlock int64_t* m_max_time = nullptr; int64_t* m_mtp_time = nullptr; bool* m_in_active_chain = nullptr; + CBlockLocator* m_locator = nullptr; const FoundBlock* m_next_block = nullptr; CBlock* m_data = nullptr; mutable bool found = false; }; +//! Block data sent with blockConnected, blockDisconnected notifications. +struct BlockInfo { + const uint256& hash; + const uint256* prev_hash = nullptr; + int height = -1; + int file_number = -1; + unsigned data_pos = 0; + const CBlock* data = nullptr; + const CBlockUndo* undo_data = nullptr; + + BlockInfo(const uint256& hash LIFETIMEBOUND) : hash(hash) {} +}; + //! Interface giving clients (wallet processes, maybe other analysis tools in //! the future) ability to access to the chain state, receive notifications, //! estimate fees, and submit transactions. @@ -272,8 +293,8 @@ class Chain virtual ~Notifications() {} virtual void transactionAddedToMempool(const CTransactionRef& tx, int64_t nAcceptTime) {} virtual void transactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRemovalReason reason) {} - virtual void blockConnected(const CBlock& block, int height) {} - virtual void blockDisconnected(const CBlock& block, int height) {} + virtual void blockConnected(const BlockInfo& block) {} + virtual void blockDisconnected(const BlockInfo& block) {} virtual void updatedBlockTip() {} virtual void chainStateFlushed(const CBlockLocator& locator) {} virtual void notifyChainLock(const CBlockIndex* pindexChainLock, const std::shared_ptr& clsig) {} @@ -322,6 +343,10 @@ class Chain //! Return true if an assumed-valid chain is in use. virtual bool hasAssumedValidChain() = 0; + + //! Get internal node context. Useful for testing, but not + //! accessible across processes. + virtual node::NodeContext* context() { return nullptr; } }; //! Interface to let node manage chain clients (wallets, or maybe tools for diff --git a/src/kernel/chain.cpp b/src/kernel/chain.cpp new file mode 100644 index 000000000000..82e77125d7f3 --- /dev/null +++ b/src/kernel/chain.cpp @@ -0,0 +1,26 @@ +// Copyright (c) 2022 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include +#include +#include +#include + +class CBlock; + +namespace kernel { +interfaces::BlockInfo MakeBlockInfo(const CBlockIndex* index, const CBlock* data) +{ + interfaces::BlockInfo info{index ? *index->phashBlock : uint256::ZERO}; + if (index) { + info.prev_hash = index->pprev ? index->pprev->phashBlock : nullptr; + info.height = index->nHeight; + LOCK(::cs_main); + info.file_number = index->nFile; + info.data_pos = index->nDataPos; + } + info.data = data; + return info; +} +} // namespace kernel diff --git a/src/kernel/chain.h b/src/kernel/chain.h new file mode 100644 index 000000000000..f0750f82663f --- /dev/null +++ b/src/kernel/chain.h @@ -0,0 +1,19 @@ +// Copyright (c) 2022 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_KERNEL_CHAIN_H +#define BITCOIN_KERNEL_CHAIN_H + +class CBlock; +class CBlockIndex; +namespace interfaces { +struct BlockInfo; +} // namespace interfaces + +namespace kernel { +//! Return data from block index. +interfaces::BlockInfo MakeBlockInfo(const CBlockIndex* block_index, const CBlock* data = nullptr); +} // namespace kernel + +#endif // BITCOIN_KERNEL_CHAIN_H diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp index a78fd6d8dad1..3633fc3805ef 100644 --- a/src/node/interfaces.cpp +++ b/src/node/interfaces.cpp @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -1080,6 +1081,7 @@ bool FillBlock(const CBlockIndex* index, const FoundBlock& block, UniqueLockGetBlockTimeMax(); if (block.m_mtp_time) *block.m_mtp_time = index->GetMedianTimePast(); if (block.m_in_active_chain) *block.m_in_active_chain = active[index->nHeight] == index; + if (block.m_locator) { *block.m_locator = active.GetLocator(index); } if (block.m_next_block) FillBlock(active[index->nHeight] == index ? active[index->nHeight + 1] : nullptr, *block.m_next_block, lock, active); if (block.m_data) { REVERSE_LOCK(lock); @@ -1105,11 +1107,11 @@ class NotificationsProxy : public CValidationInterface } void BlockConnected(const std::shared_ptr& block, const CBlockIndex* index) override { - m_notifications->blockConnected(*block, index->nHeight); + m_notifications->blockConnected(kernel::MakeBlockInfo(index, block.get())); } void BlockDisconnected(const std::shared_ptr& block, const CBlockIndex* index) override { - m_notifications->blockDisconnected(*block, index->nHeight); + m_notifications->blockDisconnected(kernel::MakeBlockInfo(index, block.get())); } void UpdatedBlockTip(const CBlockIndex* index, const CBlockIndex* fork_index, bool is_ibd) override { @@ -1514,6 +1516,7 @@ class ChainImpl : public Chain return chainman().IsSnapshotActive(); } + NodeContext* context() override { return &m_node; } NodeContext& m_node; }; } // namespace diff --git a/src/test/blockfilter_index_tests.cpp b/src/test/blockfilter_index_tests.cpp index 5e484c88120e..6cb70d3e7f1e 100644 --- a/src/test/blockfilter_index_tests.cpp +++ b/src/test/blockfilter_index_tests.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include