From 9d3b80ec2d1ad530696d0850b73aed35d2e9d127 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Mon, 15 Jun 2026 14:03:22 +0700 Subject: [PATCH 1/2] feat: make circular dependencies more conflict friendly for backports --- test/lint/lint-circular-dependencies.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/test/lint/lint-circular-dependencies.py b/test/lint/lint-circular-dependencies.py index ab9c1d4f311d..f0a85f251185 100755 --- a/test/lint/lint-circular-dependencies.py +++ b/test/lint/lint-circular-dependencies.py @@ -11,7 +11,7 @@ import subprocess import sys -EXPECTED_CIRCULAR_DEPENDENCIES = ( +EXPECTED_BITCOIN_CIRCULAR_DEPENDENCIES = ( "chainparamsbase -> util/system -> chainparamsbase", "node/blockstorage -> validation -> node/blockstorage", "node/utxo_snapshot -> validation -> node/utxo_snapshot", @@ -22,7 +22,9 @@ "wallet/wallet -> wallet/walletdb -> wallet/wallet", "kernel/coinstats -> validation -> kernel/coinstats", "kernel/mempool_persist -> validation -> kernel/mempool_persist", - # Dash +) + +EXPECTED_DASH_CIRCULAR_DEPENDENCIES = ( "banman -> common/bloom -> evo/assetlocktx -> llmq/quorumsman -> llmq/blockprocessor -> net -> banman", "coinjoin/client -> coinjoin/util -> wallet/wallet -> psbt -> node/transaction -> net_processing -> coinjoin/walletman -> coinjoin/client", "common/bloom -> evo/assetlocktx -> llmq/commitment -> evo/deterministicmns -> evo/simplifiedmns -> merkleblock -> common/bloom", @@ -51,6 +53,8 @@ "wallet/coinjoin -> wallet/receive -> wallet/coinjoin", ) +EXPECTED_CIRCULAR_DEPENDENCIES = EXPECTED_BITCOIN_CIRCULAR_DEPENDENCIES + EXPECTED_DASH_CIRCULAR_DEPENDENCIES + CODE_DIR = "src" From b0ad4384396e21da4bd53122cc85a4a4fa702f4a Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Tue, 19 Jul 2022 20:56:49 +0100 Subject: [PATCH 2/2] Merge bitcoin/bitcoin#25494: indexes: Stop using node internal types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 7878f97bf15b6e7c9b47d1c0d96419b97e1bdcbd indexes, refactor: Remove CChainState use in index CommitInternal method (Ryan Ofsky) ee3a079fab2c33b4186b62ab822753954a4e545f indexes, refactor: Remove CBlockIndex* uses in index Rewind methods (Ryan Ofsky) dc971be0831959e7ee6a6df9e1aa46091351a8fb indexes, refactor: Remove CBlockIndex* uses in index WriteBlock methods (Ryan Ofsky) bef4e405f3de2718dfee279a9abff4daf016da26 indexes, refactor: Remove CBlockIndex* uses in index Init methods (Ryan Ofsky) addb4f2af183a25ce4a6b6485b5b49575a2ba31b indexes, refactor: Remove CBlockIndex* uses in coinstatsindex LookUpOne function (Ryan Ofsky) 33b4d48cfcdf145f49cb2283ac3e2936a4e23fff indexes, refactor: Pass Chain interface instead of CChainState class to indexes (Ryan Ofsky) a0b5b4ae5a24536d333cbce2ea584f2d935c651f interfaces, refactor: Add more block information to block connected notifications (Ryan Ofsky) Pull request description: Start transitioning index code away from using internal node types like `CBlockIndex` and `CChain` so index code is less coupled to node code and index code will later be able to stop locking cs_main and sync without having to deal with validationinterface race conditions, and so new indexes are easier to write and can run as plugins or separate processes. This PR contains the first 7 commits from https://github.com/bitcoin/bitcoin/pull/24230#issuecomment-1165625977 which have been split off for easier review. Previous review comments can be found in #24230 ACKs for top commit: MarcoFalke: ACK 7878f97bf15b6e7c9b47d1c0d96419b97e1bdcbd though did not review the last commit 🤼 mzumsande: Code Review ACK 7878f97bf15b6e7c9b47d1c0d96419b97e1bdcbd Tree-SHA512: f84ac2eb6dca2c305566ddeb35ea14d0b71c00860c0fd752bbcf1a0188be833d8c2a6ac9d3ef6ab5b46fbd02d7a24cbb8f60cf12464cb8ba208e22287f709989 Co-authored-by: fanquake --- src/Makefile.am | 3 ++ src/core_write.cpp | 2 +- src/index/addressindex.cpp | 43 ++++++++++------- src/index/addressindex.h | 6 +-- src/index/base.cpp | 63 +++++++++++++++++-------- src/index/base.h | 24 +++++++--- src/index/blockfilterindex.cpp | 42 +++++++++-------- src/index/blockfilterindex.h | 12 ++--- src/index/coinstatsindex.cpp | 63 +++++++++++++------------ src/index/coinstatsindex.h | 10 ++-- src/index/spentindex.cpp | 33 ++++++++----- src/index/spentindex.h | 13 ++--- src/index/spentindex_types.h | 4 ++ src/index/timestampindex.cpp | 21 +++++---- src/index/timestampindex.h | 6 +-- src/index/txindex.cpp | 17 ++++--- src/index/txindex.h | 4 +- src/init.cpp | 24 +++++----- src/interfaces/chain.h | 33 +++++++++++-- src/kernel/chain.cpp | 26 ++++++++++ src/kernel/chain.h | 19 ++++++++ src/node/interfaces.cpp | 7 ++- src/test/blockfilter_index_tests.cpp | 11 +++-- src/test/coinstatsindex_tests.cpp | 13 ++--- src/test/txindex_tests.cpp | 5 +- src/wallet/test/fuzz/notifications.cpp | 18 +++++-- src/wallet/wallet.cpp | 25 +++++----- src/wallet/wallet.h | 4 +- test/lint/lint-circular-dependencies.py | 4 ++ 29 files changed, 350 insertions(+), 205 deletions(-) create mode 100644 src/kernel/chain.cpp create mode 100644 src/kernel/chain.h 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