Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand Down Expand Up @@ -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 \
Expand Down Expand Up @@ -1284,6 +1286,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 \
Expand Down
25 changes: 17 additions & 8 deletions src/index/addressindex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <tinyformat.h>
#include <undo.h>
#include <util/system.h>
#include <validation.h>

constexpr uint8_t DB_ADDRESSINDEX{'a'};
constexpr uint8_t DB_ADDRESSUNSPENTINDEX{'u'};
Expand Down Expand Up @@ -159,15 +160,19 @@ bool AddressIndex::DB::RewindBatch(const std::vector<CAddressIndexEntry>& addres
return CDBWrapper::WriteBatch(batch);
}

AddressIndex::AddressIndex(size_t n_cache_size, bool f_memory, bool f_wipe) :
AddressIndex::AddressIndex(std::unique_ptr<interfaces::Chain> chain, size_t n_cache_size, bool f_memory, bool f_wipe) :
BaseIndex(std::move(chain)),
m_db(std::make_unique<AddressIndex::DB>(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)
{
assert(block.data);
const CBlockIndex* pindex = WITH_LOCK(cs_main, return m_chainstate->m_blockman.LookupBlockIndex(block.hash));
assert(pindex);
// Skip genesis block (no inputs to index)
if (pindex->nHeight == 0) {
return true;
Expand All @@ -185,13 +190,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());
pindex->GetBlockHash().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();

Expand Down Expand Up @@ -244,7 +249,7 @@ bool AddressIndex::WriteBlock(const CBlock& block, const CBlockIndex* pindex)
}

// 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];
Expand All @@ -267,8 +272,12 @@ bool AddressIndex::WriteBlock(const CBlock& block, const CBlockIndex* pindex)
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_key, const interfaces::BlockKey& new_tip_key)
{
LOCK(cs_main);
const CBlockIndex* current_tip = m_chainstate->m_blockman.LookupBlockIndex(current_tip_key.hash);
const CBlockIndex* new_tip = m_chainstate->m_blockman.LookupBlockIndex(new_tip_key.hash);
assert(current_tip && new_tip);
assert(current_tip->GetAncestor(new_tip->nHeight) == new_tip);

// Rewind the unspent index by processing blocks in reverse
Expand Down Expand Up @@ -388,7 +397,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; }
Expand Down
6 changes: 3 additions & 3 deletions src/index/addressindex.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,18 +62,18 @@ 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;

const char* GetName() const override { return "addressindex"; }

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<interfaces::Chain> chain, size_t n_cache_size, bool f_memory = false, bool f_wipe = false);

/// Destructor
virtual ~AddressIndex() override;
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 @@ 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)
{}
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 @@ -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 = &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 Down Expand Up @@ -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;
}

Expand All @@ -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.
Expand Down Expand Up @@ -271,8 +291,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)) {
// 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
Expand Down Expand Up @@ -377,13 +397,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 @@ -8,13 +8,17 @@
#include <dbwrapper.h>
#include <tinyformat.h>
#include <util/threadinterrupt.h>
#include <interfaces/chain.h>
#include <validationinterface.h>

#include <atomic>

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

struct IndexSummary {
std::string name;
Expand Down Expand Up @@ -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
Expand All @@ -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<interfaces::Chain> m_chain;
CChainState* m_chainstate{nullptr};

void BlockConnected(const std::shared_ptr<const CBlock>& block, const CBlockIndex* pindex) override;
Expand All @@ -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<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 @@ -128,6 +137,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 @@ -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(CChainState& active_chainstate);
[[nodiscard]] bool Start();

/// Stops the instance from staying in sync with blockchain updates.
void Stop();
Expand Down
Loading
Loading