Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F14864208
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
35 KB
Subscribers
None
View Options
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 ¬ifications) = 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 ¶ms() 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 ¶ms);
} // namespace interfaces
#endif // BITCOIN_INTERFACES_CHAIN_H
File Metadata
Details
Attached
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)
Attached To
rABC Bitcoin ABC
Event Timeline
Log In to Comment