Skip to content

Commit

Permalink
Merge bitcoin/bitcoin#25494: indexes: Stop using node internal types
Browse files Browse the repository at this point in the history
7878f97 indexes, refactor: Remove CChainState use in index CommitInternal method (Ryan Ofsky)
ee3a079 indexes, refactor: Remove CBlockIndex* uses in index Rewind methods (Ryan Ofsky)
dc971be indexes, refactor: Remove CBlockIndex* uses in index WriteBlock methods (Ryan Ofsky)
bef4e40 indexes, refactor: Remove CBlockIndex* uses in index Init methods (Ryan Ofsky)
addb4f2 indexes, refactor: Remove CBlockIndex* uses in coinstatsindex LookUpOne function (Ryan Ofsky)
33b4d48 indexes, refactor: Pass Chain interface instead of CChainState class to indexes (Ryan Ofsky)
a0b5b4a 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 bitcoin/bitcoin#24230 (comment) which have been split off for easier review. Previous review comments can be found in #24230

ACKs for top commit:
  MarcoFalke:
    ACK 7878f97 though did not review the last commit 🤼
  mzumsande:
    Code Review ACK 7878f97

Tree-SHA512: f84ac2eb6dca2c305566ddeb35ea14d0b71c00860c0fd752bbcf1a0188be833d8c2a6ac9d3ef6ab5b46fbd02d7a24cbb8f60cf12464cb8ba208e22287f709989
  • Loading branch information
fanquake committed Jul 19, 2022
2 parents 6900162 + 7878f97 commit 92c8e18
Show file tree
Hide file tree
Showing 21 changed files with 273 additions and 142 deletions.
3 changes: 3 additions & 0 deletions src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ BITCOIN_CORE_H = \
interfaces/ipc.h \
interfaces/node.h \
interfaces/wallet.h \
kernel/chain.h \
kernel/chainstatemanager_opts.h \
kernel/checks.h \
kernel/coinstats.h \
Expand Down Expand Up @@ -365,6 +366,7 @@ libbitcoin_node_a_SOURCES = \
index/coinstatsindex.cpp \
index/txindex.cpp \
init.cpp \
kernel/chain.cpp \
kernel/checks.cpp \
kernel/coinstats.cpp \
kernel/context.cpp \
Expand Down Expand Up @@ -882,6 +884,7 @@ libbitcoinkernel_la_SOURCES = \
flatfile.cpp \
fs.cpp \
hash.cpp \
kernel/chain.cpp \
kernel/checks.cpp \
kernel/coinstats.cpp \
kernel/context.cpp \
Expand Down
63 changes: 44 additions & 19 deletions src/index/base.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@

#include <chainparams.h>
#include <index/base.h>
#include <interfaces/chain.h>
#include <kernel/chain.h>
#include <node/blockstorage.h>
#include <node/context.h>
#include <node/interface_ui.h>
#include <shutdown.h>
#include <tinyformat.h>
Expand All @@ -31,6 +34,15 @@ static void FatalError(const char* fmt, const Args&... args)
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)
{}
Expand All @@ -49,6 +61,9 @@ void BaseIndex::DB::WriteBestBlock(CDBBatch& batch, const CBlockLocator& locator
batch.Write(DB_BEST_BLOCK, locator);
}

BaseIndex::BaseIndex(std::unique_ptr<interfaces::Chain> chain)
: m_chain{std::move(chain)} {}

BaseIndex::~BaseIndex()
{
Interrupt();
Expand Down Expand Up @@ -175,12 +190,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 = &block;
}
if (!WriteBlock(block, pindex)) {
if (!CustomAppend(block_info)) {
FatalError("%s: Failed to write block %s to index database",
__func__, pindex->GetBlockHash().ToString());
return;
Expand All @@ -197,22 +215,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;
}

Expand All @@ -221,6 +237,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.
Expand Down Expand Up @@ -268,8 +288,8 @@ void BaseIndex::BlockConnected(const std::shared_ptr<const CBlock>& block, const
return;
}
}

if (WriteBlock(*block, pindex)) {
interfaces::BlockInfo block_info = kernel::MakeBlockInfo(pindex, block.get());
if (CustomAppend(block_info)) {
SetBestBlockIndex(pindex);
} else {
FatalError("%s: Failed to write block %s to index",
Expand Down Expand Up @@ -346,13 +366,18 @@ void BaseIndex::Interrupt()
m_interrupt();
}

bool BaseIndex::Start(CChainState& 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;
}

Expand Down
24 changes: 17 additions & 7 deletions src/index/base.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,16 @@
#define BITCOIN_INDEX_BASE_H

#include <dbwrapper.h>
#include <interfaces/chain.h>
#include <threadinterrupt.h>
#include <validationinterface.h>

class CBlock;
class CBlockIndex;
class CChainState;
namespace interfaces {
class Chain;
} // namespace interfaces

struct IndexSummary {
std::string name;
Expand Down Expand Up @@ -59,6 +63,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
Expand All @@ -76,30 +83,32 @@ 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<interfaces::Chain> m_chain;
CChainState* m_chainstate{nullptr};

void BlockConnected(const std::shared_ptr<const CBlock>& block, const CBlockIndex* pindex) override;

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<interfaces::BlockKey>& 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;

Expand All @@ -110,6 +119,7 @@ class BaseIndex : public CValidationInterface
void SetBestBlockIndex(const CBlockIndex* block);

public:
BaseIndex(std::unique_ptr<interfaces::Chain> chain);
/// Destructor interrupts sync thread if running and blocks until it exits.
virtual ~BaseIndex();

Expand All @@ -124,7 +134,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(CChainState& active_chainstate);
[[nodiscard]] bool Start();

/// Stops the instance from staying in sync with blockchain updates.
void Stop();
Expand Down
42 changes: 22 additions & 20 deletions src/index/blockfilterindex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <index/blockfilterindex.h>
#include <node/blockstorage.h>
#include <util/system.h>
#include <validation.h>

using node::UndoReadFromDisk;

Expand Down Expand Up @@ -94,9 +95,9 @@ struct DBHashKey {

static std::map<BlockFilterType, BlockFilterIndex> g_filter_indexes;

BlockFilterIndex::BlockFilterIndex(BlockFilterType filter_type,
BlockFilterIndex::BlockFilterIndex(std::unique_ptr<interfaces::Chain> 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");
Expand All @@ -109,7 +110,7 @@ BlockFilterIndex::BlockFilterIndex(BlockFilterType filter_type,
m_filter_fileseq = std::make_unique<FlatFileSeq>(std::move(path), "fltr", FLTR_FILE_CHUNK_SIZE);
}

bool BlockFilterIndex::Init()
bool BlockFilterIndex::CustomInit(const std::optional<interfaces::BlockKey>& block)
{
if (!m_db->Read(DB_FILTER_POS, m_next_filter_pos)) {
// Check that the cause of the read failure is that the key does not exist. Any other errors
Expand All @@ -124,10 +125,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;

Expand All @@ -141,7 +142,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
Expand Down Expand Up @@ -214,22 +215,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<uint256, DBVal> 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());
Expand All @@ -238,18 +242,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<uint256, DBVal> 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;
}

Expand Down Expand Up @@ -283,17 +287,15 @@ static bool CopyHeightIndexToHashIndex(CDBIterator& db_it, CDBBatch& batch,
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<CDBIterator> 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;
}

Expand All @@ -303,7 +305,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)
Expand Down Expand Up @@ -467,12 +469,12 @@ void ForEachBlockFilterIndex(std::function<void (BlockFilterIndex&)> fn)
for (auto& entry : g_filter_indexes) fn(entry.second);
}

bool InitBlockFilterIndex(BlockFilterType filter_type,
bool InitBlockFilterIndex(std::function<std::unique_ptr<interfaces::Chain>()> 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;
}
Expand Down
Loading

0 comments on commit 92c8e18

Please sign in to comment.