Page MenuHomePhabricator

No OneTemporary

diff --git a/src/index/coinstatsindex.cpp b/src/index/coinstatsindex.cpp
index 063e3dce0..b6b264a4c 100644
--- a/src/index/coinstatsindex.cpp
+++ b/src/index/coinstatsindex.cpp
@@ -1,555 +1,557 @@
// Copyright (c) 2020-2021 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 <index/coinstatsindex.h>
#include <chainparams.h>
#include <coins.h>
#include <common/args.h>
#include <consensus/amount.h>
#include <crypto/muhash.h>
#include <logging.h>
#include <node/blockstorage.h>
#include <primitives/blockhash.h>
#include <serialize.h>
#include <txdb.h>
#include <undo.h>
#include <util/check.h>
#include <validation.h>
using kernel::CCoinsStats;
using kernel::GetBogoSize;
using kernel::TxOutSer;
static constexpr uint8_t DB_BLOCK_HASH{'s'};
static constexpr uint8_t DB_BLOCK_HEIGHT{'t'};
static constexpr uint8_t DB_MUHASH{'M'};
namespace {
struct DBVal {
uint256 muhash;
uint64_t transaction_output_count;
uint64_t bogo_size;
Amount total_amount;
Amount total_subsidy;
Amount total_unspendable_amount;
Amount total_prevout_spent_amount;
Amount total_new_outputs_ex_coinbase_amount;
Amount total_coinbase_amount;
Amount total_unspendables_genesis_block;
Amount total_unspendables_bip30;
Amount total_unspendables_scripts;
Amount total_unspendables_unclaimed_rewards;
SERIALIZE_METHODS(DBVal, obj) {
READWRITE(obj.muhash);
READWRITE(obj.transaction_output_count);
READWRITE(obj.bogo_size);
READWRITE(obj.total_amount);
READWRITE(obj.total_subsidy);
READWRITE(obj.total_unspendable_amount);
READWRITE(obj.total_prevout_spent_amount);
READWRITE(obj.total_new_outputs_ex_coinbase_amount);
READWRITE(obj.total_coinbase_amount);
READWRITE(obj.total_unspendables_genesis_block);
READWRITE(obj.total_unspendables_bip30);
READWRITE(obj.total_unspendables_scripts);
READWRITE(obj.total_unspendables_unclaimed_rewards);
}
};
struct DBHeightKey {
int height;
explicit DBHeightKey(int height_in) : height(height_in) {}
template <typename Stream> void Serialize(Stream &s) const {
ser_writedata8(s, DB_BLOCK_HEIGHT);
ser_writedata32be(s, height);
}
template <typename Stream> void Unserialize(Stream &s) {
const uint8_t prefix{ser_readdata8(s)};
if (prefix != DB_BLOCK_HEIGHT) {
throw std::ios_base::failure(
"Invalid format for coinstatsindex DB height key");
}
height = ser_readdata32be(s);
}
};
struct DBHashKey {
BlockHash block_hash;
explicit DBHashKey(const BlockHash &hash_in) : block_hash(hash_in) {}
SERIALIZE_METHODS(DBHashKey, obj) {
uint8_t prefix{DB_BLOCK_HASH};
READWRITE(prefix);
if (prefix != DB_BLOCK_HASH) {
throw std::ios_base::failure(
"Invalid format for coinstatsindex DB hash key");
}
READWRITE(obj.block_hash);
}
};
}; // namespace
std::unique_ptr<CoinStatsIndex> g_coin_stats_index;
CoinStatsIndex::CoinStatsIndex(std::unique_ptr<interfaces::Chain> chain,
size_t n_cache_size, bool f_memory, bool f_wipe)
: BaseIndex(std::move(chain), "coinstatsindex") {
fs::path path{gArgs.GetDataDirNet() / "indexes" / "coinstats"};
fs::create_directories(path);
m_db = std::make_unique<CoinStatsIndex::DB>(path / "db", n_cache_size,
f_memory, f_wipe);
}
bool CoinStatsIndex::WriteBlock(const CBlock &block,
const CBlockIndex *pindex) {
CBlockUndo block_undo;
const Amount block_subsidy{
GetBlockSubsidy(pindex->nHeight, Params().GetConsensus())};
m_total_subsidy += block_subsidy;
// Ignore genesis block
if (pindex->nHeight > 0) {
if (!m_chainstate->m_blockman.UndoReadFromDisk(block_undo, *pindex)) {
return false;
}
std::pair<BlockHash, DBVal> read_out;
if (!m_db->Read(DBHeightKey(pindex->nHeight - 1), read_out)) {
return false;
}
BlockHash expected_block_hash{pindex->pprev->GetBlockHash()};
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());
if (!m_db->Read(DBHashKey(expected_block_hash), read_out)) {
return error("%s: previous block header not found; expected %s",
__func__, expected_block_hash.ToString());
}
}
// TODO: Deduplicate BIP30 related code
bool is_bip30_block{
(pindex->nHeight == 91722 &&
pindex->GetBlockHash() ==
BlockHash{uint256S("0x00000000000271a2dc26e7667f8419f2e15416dc"
"6955e5a6c6cdf3f2574dd08e")}) ||
(pindex->nHeight == 91812 &&
pindex->GetBlockHash() ==
BlockHash{uint256S("0x00000000000af0aed4792b1acee3d966af36cf5d"
"ef14935db8de83d6f9306f2f")})};
// 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)};
// Skip duplicate txid coinbase transactions (BIP30).
if (is_bip30_block && tx->IsCoinBase()) {
m_total_unspendable_amount += block_subsidy;
m_total_unspendables_bip30 += block_subsidy;
continue;
}
for (uint32_t j = 0; j < tx->vout.size(); ++j) {
const CTxOut &out{tx->vout[j]};
Coin coin{out, static_cast<uint32_t>(pindex->nHeight),
tx->IsCoinBase()};
COutPoint outpoint{tx->GetId(), j};
// Skip unspendable coins
if (coin.GetTxOut().scriptPubKey.IsUnspendable()) {
m_total_unspendable_amount += coin.GetTxOut().nValue;
m_total_unspendables_scripts += coin.GetTxOut().nValue;
continue;
}
m_muhash.Insert(MakeUCharSpan(TxOutSer(outpoint, coin)));
if (tx->IsCoinBase()) {
m_total_coinbase_amount += coin.GetTxOut().nValue;
} else {
m_total_new_outputs_ex_coinbase_amount +=
coin.GetTxOut().nValue;
}
++m_transaction_output_count;
m_total_amount += coin.GetTxOut().nValue;
m_bogo_size += GetBogoSize(coin.GetTxOut().scriptPubKey);
}
// The coinbase tx has no undo data since no former output is spent
if (!tx->IsCoinBase()) {
const auto &tx_undo{block_undo.vtxundo.at(i - 1)};
for (size_t j = 0; j < tx_undo.vprevout.size(); ++j) {
Coin coin{tx_undo.vprevout[j]};
COutPoint outpoint{tx->vin[j].prevout.GetTxId(),
tx->vin[j].prevout.GetN()};
m_muhash.Remove(MakeUCharSpan(TxOutSer(outpoint, coin)));
m_total_prevout_spent_amount += coin.GetTxOut().nValue;
--m_transaction_output_count;
m_total_amount -= coin.GetTxOut().nValue;
m_bogo_size -= GetBogoSize(coin.GetTxOut().scriptPubKey);
}
}
}
} else {
// genesis block
m_total_unspendable_amount += block_subsidy;
m_total_unspendables_genesis_block += block_subsidy;
}
// If spent prevouts + block subsidy are still a higher amount than
// new outputs + coinbase + current unspendable amount this means
// the miner did not claim the full block reward. Unclaimed block
// rewards are also unspendable.
const Amount unclaimed_rewards{
(m_total_prevout_spent_amount + m_total_subsidy) -
(m_total_new_outputs_ex_coinbase_amount + m_total_coinbase_amount +
m_total_unspendable_amount)};
m_total_unspendable_amount += unclaimed_rewards;
m_total_unspendables_unclaimed_rewards += unclaimed_rewards;
std::pair<BlockHash, DBVal> value;
value.first = pindex->GetBlockHash();
value.second.transaction_output_count = m_transaction_output_count;
value.second.bogo_size = m_bogo_size;
value.second.total_amount = m_total_amount;
value.second.total_subsidy = m_total_subsidy;
value.second.total_unspendable_amount = m_total_unspendable_amount;
value.second.total_prevout_spent_amount = m_total_prevout_spent_amount;
value.second.total_new_outputs_ex_coinbase_amount =
m_total_new_outputs_ex_coinbase_amount;
value.second.total_coinbase_amount = m_total_coinbase_amount;
value.second.total_unspendables_genesis_block =
m_total_unspendables_genesis_block;
value.second.total_unspendables_bip30 = m_total_unspendables_bip30;
value.second.total_unspendables_scripts = m_total_unspendables_scripts;
value.second.total_unspendables_unclaimed_rewards =
m_total_unspendables_unclaimed_rewards;
uint256 out;
m_muhash.Finalize(out);
value.second.muhash = out;
// 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);
}
static bool CopyHeightIndexToHashIndex(CDBIterator &db_it, CDBBatch &batch,
const std::string &index_name,
int start_height, int stop_height) {
DBHeightKey key{start_height};
db_it.Seek(key);
for (int height = start_height; height <= stop_height; ++height) {
if (!db_it.GetKey(key) || key.height != height) {
return error("%s: unexpected key in %s: expected (%c, %d)",
__func__, index_name, DB_BLOCK_HEIGHT, height);
}
std::pair<BlockHash, DBVal> value;
if (!db_it.GetValue(value)) {
return error("%s: unable to read value in %s at key (%c, %d)",
__func__, index_name, DB_BLOCK_HEIGHT, height);
}
batch.Write(DBHashKey(value.first), std::move(value.second));
db_it.Next();
}
return true;
}
bool CoinStatsIndex::Rewind(const CBlockIndex *current_tip,
const CBlockIndex *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 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)) {
return false;
}
if (!m_db->WriteBatch(batch)) {
return false;
}
{
LOCK(cs_main);
const CBlockIndex *iter_tip{m_chainstate->m_blockman.LookupBlockIndex(
current_tip->GetBlockHash())};
do {
CBlock block;
if (!m_chainstate->m_blockman.ReadBlockFromDisk(block, *iter_tip)) {
return error("%s: Failed to read block %s from disk", __func__,
iter_tip->GetBlockHash().ToString());
}
ReverseBlock(block, iter_tip);
iter_tip = iter_tip->GetAncestor(iter_tip->nHeight - 1);
} while (new_tip != iter_tip);
}
return BaseIndex::Rewind(current_tip, new_tip);
}
-static bool LookUpOne(const CDBWrapper &db, const CBlockIndex *block_index,
+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<BlockHash, DBVal> 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<CCoinsStats>
CoinStatsIndex::LookUpStats(const CBlockIndex *block_index) const {
CCoinsStats stats{Assert(block_index)->nHeight,
block_index->GetBlockHash()};
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;
}
stats.hashSerialized = entry.muhash;
stats.nTransactionOutputs = entry.transaction_output_count;
stats.nBogoSize = entry.bogo_size;
stats.nTotalAmount = entry.total_amount;
stats.total_subsidy = entry.total_subsidy;
stats.total_unspendable_amount = entry.total_unspendable_amount;
stats.total_prevout_spent_amount = entry.total_prevout_spent_amount;
stats.total_new_outputs_ex_coinbase_amount =
entry.total_new_outputs_ex_coinbase_amount;
stats.total_coinbase_amount = entry.total_coinbase_amount;
stats.total_unspendables_genesis_block =
entry.total_unspendables_genesis_block;
stats.total_unspendables_bip30 = entry.total_unspendables_bip30;
stats.total_unspendables_scripts = entry.total_unspendables_scripts;
stats.total_unspendables_unclaimed_rewards =
entry.total_unspendables_unclaimed_rewards;
return stats;
}
bool CoinStatsIndex::Init() {
if (!m_db->Read(DB_MUHASH, m_muhash)) {
// Check that the cause of the read failure is that the key does not
// exist. Any other errors indicate database corruption or a disk
// failure, and starting the index would cause further corruption.
if (m_db->Exists(DB_MUHASH)) {
return error(
"%s: Cannot read current %s state; index may be corrupted",
__func__, GetName());
}
}
if (!BaseIndex::Init()) {
return false;
}
const CBlockIndex *pindex{CurrentIndex()};
if (pindex) {
DBVal entry;
- if (!LookUpOne(*m_db, pindex, entry)) {
+ if (!LookUpOne(*m_db, {pindex->GetBlockHash(), pindex->nHeight},
+ entry)) {
return error(
"%s: Cannot read current %s state; index may be corrupted",
__func__, GetName());
}
uint256 out;
m_muhash.Finalize(out);
if (entry.muhash != out) {
return error(
"%s: Cannot read current %s state; index may be corrupted",
__func__, GetName());
}
m_transaction_output_count = entry.transaction_output_count;
m_bogo_size = entry.bogo_size;
m_total_amount = entry.total_amount;
m_total_subsidy = entry.total_subsidy;
m_total_unspendable_amount = entry.total_unspendable_amount;
m_total_prevout_spent_amount = entry.total_prevout_spent_amount;
m_total_new_outputs_ex_coinbase_amount =
entry.total_new_outputs_ex_coinbase_amount;
m_total_coinbase_amount = entry.total_coinbase_amount;
m_total_unspendables_genesis_block =
entry.total_unspendables_genesis_block;
m_total_unspendables_bip30 = entry.total_unspendables_bip30;
m_total_unspendables_scripts = entry.total_unspendables_scripts;
m_total_unspendables_unclaimed_rewards =
entry.total_unspendables_unclaimed_rewards;
}
return true;
}
bool CoinStatsIndex::CommitInternal(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);
}
// Reverse a single block as part of a reorg
bool CoinStatsIndex::ReverseBlock(const CBlock &block,
const CBlockIndex *pindex) {
CBlockUndo block_undo;
std::pair<BlockHash, DBVal> read_out;
const Amount block_subsidy{
GetBlockSubsidy(pindex->nHeight, Params().GetConsensus())};
m_total_subsidy -= block_subsidy;
// Ignore genesis block
if (pindex->nHeight > 0) {
if (!m_chainstate->m_blockman.UndoReadFromDisk(block_undo, *pindex)) {
return false;
}
if (!m_db->Read(DBHeightKey(pindex->nHeight - 1), read_out)) {
return false;
}
BlockHash expected_block_hash{pindex->pprev->GetBlockHash()};
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());
if (!m_db->Read(DBHashKey(expected_block_hash), read_out)) {
return error("%s: previous block header not found; expected %s",
__func__, expected_block_hash.ToString());
}
}
}
// Remove the new UTXOs that were created from the block
for (size_t i = 0; i < block.vtx.size(); ++i) {
const auto &tx{block.vtx.at(i)};
for (uint32_t j = 0; j < tx->vout.size(); ++j) {
const CTxOut &out{tx->vout[j]};
COutPoint outpoint{tx->GetId(), j};
Coin coin{out, static_cast<uint32_t>(pindex->nHeight),
tx->IsCoinBase()};
// Skip unspendable coins
if (coin.GetTxOut().scriptPubKey.IsUnspendable()) {
m_total_unspendable_amount -= coin.GetTxOut().nValue;
m_total_unspendables_scripts -= coin.GetTxOut().nValue;
continue;
}
m_muhash.Remove(MakeUCharSpan(TxOutSer(outpoint, coin)));
if (tx->IsCoinBase()) {
m_total_coinbase_amount -= coin.GetTxOut().nValue;
} else {
m_total_new_outputs_ex_coinbase_amount -=
coin.GetTxOut().nValue;
}
--m_transaction_output_count;
m_total_amount -= coin.GetTxOut().nValue;
m_bogo_size -= GetBogoSize(coin.GetTxOut().scriptPubKey);
}
// The coinbase tx has no undo data since no former output is spent
if (!tx->IsCoinBase()) {
const auto &tx_undo{block_undo.vtxundo.at(i - 1)};
for (size_t j = 0; j < tx_undo.vprevout.size(); ++j) {
Coin coin{tx_undo.vprevout[j]};
COutPoint outpoint{tx->vin[j].prevout.GetTxId(),
tx->vin[j].prevout.GetN()};
m_muhash.Insert(MakeUCharSpan(TxOutSer(outpoint, coin)));
m_total_prevout_spent_amount -= coin.GetTxOut().nValue;
m_transaction_output_count++;
m_total_amount += coin.GetTxOut().nValue;
m_bogo_size += GetBogoSize(coin.GetTxOut().scriptPubKey);
}
}
}
const Amount unclaimed_rewards{
(m_total_new_outputs_ex_coinbase_amount + m_total_coinbase_amount +
m_total_unspendable_amount) -
(m_total_prevout_spent_amount + m_total_subsidy)};
m_total_unspendable_amount -= unclaimed_rewards;
m_total_unspendables_unclaimed_rewards -= unclaimed_rewards;
// Check that the rolled back internal values are consistent with the DB
// read out
uint256 out;
m_muhash.Finalize(out);
Assert(read_out.second.muhash == out);
Assert(m_transaction_output_count ==
read_out.second.transaction_output_count);
Assert(m_total_amount == read_out.second.total_amount);
Assert(m_bogo_size == read_out.second.bogo_size);
Assert(m_total_subsidy == read_out.second.total_subsidy);
Assert(m_total_unspendable_amount ==
read_out.second.total_unspendable_amount);
Assert(m_total_prevout_spent_amount ==
read_out.second.total_prevout_spent_amount);
Assert(m_total_new_outputs_ex_coinbase_amount ==
read_out.second.total_new_outputs_ex_coinbase_amount);
Assert(m_total_coinbase_amount == read_out.second.total_coinbase_amount);
Assert(m_total_unspendables_genesis_block ==
read_out.second.total_unspendables_genesis_block);
Assert(m_total_unspendables_bip30 ==
read_out.second.total_unspendables_bip30);
Assert(m_total_unspendables_scripts ==
read_out.second.total_unspendables_scripts);
Assert(m_total_unspendables_unclaimed_rewards ==
read_out.second.total_unspendables_unclaimed_rewards);
return true;
}
diff --git a/src/interfaces/chain.h b/src/interfaces/chain.h
index 360dc550f..0c90a0bf5 100644
--- a/src/interfaces/chain.h
+++ b/src/interfaces/chain.h
@@ -1,349 +1,355 @@
// Copyright (c) 2018-2019 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_INTERFACES_CHAIN_H
#define BITCOIN_INTERFACES_CHAIN_H
+#include <primitives/blockhash.h>
#include <primitives/transaction.h>
#include <primitives/txid.h>
#include <util/settings.h> // For util::SettingsValue
#include <cstddef>
#include <cstdint>
#include <functional>
#include <memory>
#include <optional>
#include <string>
#include <vector>
class ArgsManager;
class CBlock;
class CChainParams;
class Coin;
class Config;
class CRPCCommand;
class CScheduler;
class TxValidationState;
enum class MemPoolRemovalReason;
-struct BlockHash;
struct bilingual_str;
struct CBlockLocator;
namespace node {
struct NodeContext;
} // namespace node
namespace Consensus {
struct Params;
}
namespace interfaces {
class Handler;
class Wallet;
+//! Hash/height pair to help track and identify blocks.
+struct BlockKey {
+ BlockHash hash;
+ int height = -1;
+};
+
//! Helper for findBlock to selectively return pieces of block data.
class FoundBlock {
public:
FoundBlock &hash(BlockHash &hash) {
m_hash = &hash;
return *this;
}
FoundBlock &height(int &height) {
m_height = &height;
return *this;
}
FoundBlock &time(int64_t &time) {
m_time = &time;
return *this;
}
FoundBlock &maxTime(int64_t &max_time) {
m_max_time = &max_time;
return *this;
}
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 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
//! (for example due to pruning), the CBlock variable will be set to null.
FoundBlock &data(CBlock &data) {
m_data = &data;
return *this;
}
BlockHash *m_hash = nullptr;
int *m_height = nullptr;
int64_t *m_time = nullptr;
int64_t *m_max_time = nullptr;
int64_t *m_mtp_time = nullptr;
bool *m_in_active_chain = nullptr;
const FoundBlock *m_next_block = nullptr;
CBlock *m_data = nullptr;
};
//! 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.
//!
//! TODO: Current chain methods are too low level, exposing too much of the
//! internal workings of the bitcoin node, and not being very convenient to use.
//! Chain methods should be cleaned up and simplified over time. Examples:
//!
//! * The initMessages() and showProgress() methods which the wallet uses to
//! send
//! notifications to the GUI should go away when GUI and wallet can directly
//! communicate with each other without going through the node
//! (https://github.com/bitcoin/bitcoin/pull/15288#discussion_r253321096).
//!
//! * The handleRpc, registerRpcs, rpcEnableDeprecated methods and other RPC
//! methods can go away if wallets listen for HTTP requests on their own
//! ports instead of registering to handle requests on the node HTTP port.
//!
//! * Move fee estimation queries to an asynchronous interface and let the
//! wallet cache it, fee estimation being driven by node mempool, wallet
//! should be the consumer.
//!
//! * `guessVerificationProgress` and similar methods can go away if rescan
//! logic moves out of the wallet, and the wallet just requests scans from the
//! node (https://github.com/bitcoin/bitcoin/issues/11756)
class Chain {
public:
virtual ~Chain() {}
//! Get current chain height, not including genesis block (returns 0 if
//! chain only contains genesis block, std::nullopt if chain does not
//! contain any blocks)
virtual std::optional<int> getHeight() = 0;
//! Get block hash. Height must be valid or this function will abort.
virtual BlockHash getBlockHash(int height) = 0;
//! Check that the block is available on disk (i.e. has not been
//! pruned), and contains transactions.
virtual bool haveBlockOnDisk(int height) = 0;
//! Get locator for the current chain tip.
virtual CBlockLocator getTipLocator() = 0;
//! Return height of the highest block on chain in common with the locator,
//! which will either be the original block used to create the locator,
//! or one of its ancestors.
virtual std::optional<int>
findLocatorFork(const CBlockLocator &locator) = 0;
//! Return whether node has the block and optionally return block metadata
//! or contents.
virtual bool findBlock(const BlockHash &hash,
const FoundBlock &block = {}) = 0;
//! Find first block in the chain with timestamp >= the given time
//! and height >= than the given height, return false if there is no block
//! with a high enough timestamp and height. Optionally return block
//! information.
virtual bool
findFirstBlockWithTimeAndHeight(int64_t min_time, int min_height,
const FoundBlock &block = {}) = 0;
//! Find ancestor of block at specified height and optionally return
//! ancestor information.
virtual bool findAncestorByHeight(const BlockHash &block_hash,
int ancestor_height,
const FoundBlock &ancestor_out = {}) = 0;
//! Return whether block descends from a specified ancestor, and
//! optionally return ancestor information.
virtual bool findAncestorByHash(const BlockHash &block_hash,
const BlockHash &ancestor_hash,
const FoundBlock &ancestor_out = {}) = 0;
//! Find most recent common ancestor between two blocks and optionally
//! return block information.
virtual bool findCommonAncestor(const BlockHash &block_hash1,
const BlockHash &block_hash2,
const FoundBlock &ancestor_out = {},
const FoundBlock &block1_out = {},
const FoundBlock &block2_out = {}) = 0;
//! Look up unspent output information. Returns coins in the mempool and in
//! the current chain UTXO set. Iterates through all the keys in the map and
//! populates the values.
virtual void findCoins(std::map<COutPoint, Coin> &coins) = 0;
//! Estimate fraction of total transactions verified if blocks up to
//! the specified block hash are verified.
virtual double guessVerificationProgress(const BlockHash &block_hash) = 0;
//! Return true if data is available for all blocks in the specified range
//! of blocks. This checks all blocks that are ancestors of block_hash in
//! the height range from min_height to max_height, inclusive.
virtual bool hasBlocks(const BlockHash &block_hash, int min_height = 0,
std::optional<int> max_height = {}) = 0;
//! Transaction is added to memory pool, if the transaction fee is below the
//! amount specified by max_tx_fee, and broadcast to all peers if relay is
//! set to true. Return false if the transaction could not be added due to
//! the fee or for another reason.
virtual bool broadcastTransaction(const Config &config,
const CTransactionRef &tx,
const Amount &max_tx_fee, bool relay,
std::string &err_string) = 0;
//! Estimate fee
virtual CFeeRate estimateFee() const = 0;
//! Relay current minimum fee (from -minrelaytxfee settings).
virtual CFeeRate relayMinFee() = 0;
//! Relay dust fee setting (-dustrelayfee), reflecting lowest rate it's
//! economical to spend.
virtual CFeeRate relayDustFee() = 0;
//! Check if any block has been pruned.
virtual bool havePruned() = 0;
//! Check if the node is ready to broadcast transactions.
virtual bool isReadyToBroadcast() = 0;
//! Check if in IBD.
virtual bool isInitialBlockDownload() = 0;
//! Check if shutdown requested.
virtual bool shutdownRequested() = 0;
//! Send init message.
virtual void initMessage(const std::string &message) = 0;
//! Send init warning.
virtual void initWarning(const bilingual_str &message) = 0;
//! Send init error.
virtual void initError(const bilingual_str &message) = 0;
//! Send progress indicator.
virtual void showProgress(const std::string &title, int progress,
bool resume_possible) = 0;
//! Chain notifications.
class Notifications {
public:
virtual ~Notifications() {}
virtual void transactionAddedToMempool(const CTransactionRef &tx,
uint64_t mempool_sequence) {}
virtual void transactionRemovedFromMempool(const CTransactionRef &ptx,
MemPoolRemovalReason reason,
uint64_t mempool_sequence) {}
virtual void blockConnected(const CBlock &block, int height) {}
virtual void blockDisconnected(const CBlock &block, int height) {}
virtual void updatedBlockTip() {}
virtual void chainStateFlushed(const CBlockLocator &locator) {}
};
//! Register handler for notifications.
virtual std::unique_ptr<Handler>
handleNotifications(std::shared_ptr<Notifications> notifications) = 0;
//! Wait for pending notifications to be processed unless block hash points
//! to the current chain tip.
virtual void waitForNotificationsIfTipChanged(const BlockHash &old_tip) = 0;
//! Register handler for RPC. Command is not copied, so reference
//! needs to remain valid until Handler is disconnected.
virtual std::unique_ptr<Handler> handleRpc(const CRPCCommand &command) = 0;
//! Check if deprecated RPC is enabled.
virtual bool rpcEnableDeprecated(const std::string &method) = 0;
//! Run function after given number of seconds. Cancel any previous calls
//! with same name.
virtual void rpcRunLater(const std::string &name, std::function<void()> fn,
int64_t seconds) = 0;
//! Current RPC serialization flags.
virtual int rpcSerializationFlags() = 0;
//! Get settings value.
virtual util::SettingsValue getSetting(const std::string &arg) = 0;
//! Get list of settings values.
virtual std::vector<util::SettingsValue>
getSettingsList(const std::string &arg) = 0;
//! Return <datadir>/settings.json setting value.
virtual util::SettingsValue getRwSetting(const std::string &name) = 0;
//! Write a setting to <datadir>/settings.json. Optionally just update the
//! setting in memory and do not write the file.
virtual bool updateRwSetting(const std::string &name,
const util::SettingsValue &value,
bool write = true) = 0;
//! Synchronously send transactionAddedToMempool notifications about all
//! current mempool transactions to the specified handler and return after
//! the last one is sent. These notifications aren't coordinated with async
//! notifications sent by handleNotifications, so out of date async
//! notifications from handleNotifications can arrive during and after
//! synchronous notifications from requestMempoolTransactions. Clients need
//! to be prepared to handle this by ignoring notifications about unknown
//! removed transactions and already added new transactions.
virtual void requestMempoolTransactions(Notifications &notifications) = 0;
//! 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; }
//! This Chain's parameters
virtual const CChainParams &params() const = 0;
};
//! Interface to let node manage chain clients (wallets, or maybe tools for
//! monitoring and analysis in the future).
class ChainClient {
public:
virtual ~ChainClient() {}
//! Register rpcs.
virtual void registerRpcs() = 0;
//! Check for errors before loading.
virtual bool verify() = 0;
//! Load saved state.
virtual bool load() = 0;
//! Start client execution and provide a scheduler.
virtual void start(CScheduler &scheduler) = 0;
//! Save state to disk.
virtual void flush() = 0;
//! Shut down client.
virtual void stop() = 0;
//! Set mock time.
virtual void setMockTime(int64_t time) = 0;
};
//! Return implementation of Chain interface.
std::unique_ptr<Chain> MakeChain(node::NodeContext &node,
const CChainParams &params);
} // namespace interfaces
#endif // BITCOIN_INTERFACES_CHAIN_H

File Metadata

Mime Type
text/x-diff
Expires
Wed, May 21, 17:57 (1 h, 42 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5865638
Default Alt Text
(35 KB)

Event Timeline