Skip to content

Commit 70e098a

Browse files
committed
kernel: replace BlockTreeEntry by Chain
Back btck_Chain by a CBlockIndex instead of a CChain, so a chain becomes a snapshot identified by its tip block. A chain and the block tree entry at its tip then describe the same thing, making btck_BlockTreeEntry redundant; remove it and fold its accessors into btck_Chain. A null btck_Chain* now denotes the empty chain: all chain accessors are null-safe and chain-returning functions return the empty chain instead of an error, so ARG_NONNULL is dropped from chain parameters. In the C++ wrapper, rename ChainView to Chain and model it as a std::ranges range of its block headers (value_type = BlockHeader): operator[] indexes by height, size() - 1 is the height, and emptiness is observable via operator bool(). Mismatch mirrors std::ranges::mismatch but uses find_fork (O(log n)).
1 parent 6ca607f commit 70e098a

5 files changed

Lines changed: 493 additions & 318 deletions

File tree

src/bitcoin-chainstate.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ class TestValidationInterface : public ValidationInterface
110110
class TestKernelNotifications : public KernelNotifications
111111
{
112112
public:
113-
void BlockTipHandler(SynchronizationState, const BlockTreeEntry, double) override
113+
void BlockTipHandler(SynchronizationState, const Chain, double) override
114114
{
115115
std::cout << "Block tip changed" << std::endl;
116116
}

src/kernel/bitcoinkernel.cpp

Lines changed: 89 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -146,10 +146,10 @@ struct Handle {
146146

147147
} // namespace
148148

149-
struct btck_BlockTreeEntry: Handle<btck_BlockTreeEntry, CBlockIndex> {};
150149
struct btck_Block : Handle<btck_Block, std::shared_ptr<const CBlock>> {};
151150
struct btck_BlockValidationState : Handle<btck_BlockValidationState, BlockValidationState> {};
152151
struct btck_TxValidationState : Handle<btck_TxValidationState, TxValidationState> {};
152+
struct btck_Chain : Handle<btck_Chain, CBlockIndex> {};
153153

154154
namespace {
155155

@@ -303,7 +303,7 @@ class KernelNotifications final : public kernel::Notifications
303303

304304
kernel::InterruptResult blockTip(SynchronizationState state, const CBlockIndex& index, double verification_progress) override
305305
{
306-
if (m_cbs.block_tip) m_cbs.block_tip(m_cbs.user_data, cast_state(state), btck_BlockTreeEntry::ref(&index), verification_progress);
306+
if (m_cbs.block_tip) m_cbs.block_tip(m_cbs.user_data, cast_state(state), btck_Chain::ref(&index), verification_progress);
307307
return {};
308308
}
309309
void headerTip(SynchronizationState state, int64_t height, int64_t timestamp, bool presync) override
@@ -363,7 +363,7 @@ class KernelValidationInterface final : public CValidationInterface
363363
if (m_cbs.pow_valid_block) {
364364
m_cbs.pow_valid_block(m_cbs.user_data,
365365
btck_Block::copy(btck_Block::ref(&block)),
366-
btck_BlockTreeEntry::ref(pindex));
366+
btck_Chain::ref(pindex));
367367
}
368368
}
369369

@@ -372,7 +372,7 @@ class KernelValidationInterface final : public CValidationInterface
372372
if (m_cbs.block_connected) {
373373
m_cbs.block_connected(m_cbs.user_data,
374374
btck_Block::copy(btck_Block::ref(&block)),
375-
btck_BlockTreeEntry::ref(pindex));
375+
btck_Chain::ref(pindex));
376376
}
377377
}
378378

@@ -381,7 +381,7 @@ class KernelValidationInterface final : public CValidationInterface
381381
if (m_cbs.block_disconnected) {
382382
m_cbs.block_disconnected(m_cbs.user_data,
383383
btck_Block::copy(btck_Block::ref(&block)),
384-
btck_BlockTreeEntry::ref(pindex));
384+
btck_Chain::ref(pindex));
385385
}
386386
}
387387
};
@@ -494,7 +494,6 @@ struct btck_Context : Handle<btck_Context, std::shared_ptr<const Context>> {};
494494
struct btck_ChainParameters : Handle<btck_ChainParameters, CChainParams> {};
495495
struct btck_ChainstateManagerOptions : Handle<btck_ChainstateManagerOptions, ChainstateManagerOptions> {};
496496
struct btck_ChainstateManager : Handle<btck_ChainstateManager, ChainMan> {};
497-
struct btck_Chain : Handle<btck_Chain, CChain> {};
498497
struct btck_BlockSpentOutputs : Handle<btck_BlockSpentOutputs, std::shared_ptr<CBlockUndo>> {};
499498
struct btck_TransactionSpentOutputs : Handle<btck_TransactionSpentOutputs, CTxUndo> {};
500499
struct btck_Coin : Handle<btck_Coin, Coin> {};
@@ -895,23 +894,6 @@ void btck_context_destroy(btck_Context* context)
895894
delete context;
896895
}
897896

898-
const btck_BlockTreeEntry* btck_block_tree_entry_get_previous(const btck_BlockTreeEntry* entry)
899-
{
900-
if (!btck_BlockTreeEntry::get(entry).pprev) {
901-
LogInfo("Genesis block has no previous.");
902-
return nullptr;
903-
}
904-
905-
return btck_BlockTreeEntry::ref(btck_BlockTreeEntry::get(entry).pprev);
906-
}
907-
908-
const btck_BlockTreeEntry* btck_block_tree_entry_get_ancestor(const btck_BlockTreeEntry* block_tree_entry, int32_t height)
909-
{
910-
const auto* ancestor{btck_BlockTreeEntry::get(block_tree_entry).GetAncestor(height)};
911-
assert(ancestor);
912-
return btck_BlockTreeEntry::ref(ancestor);
913-
}
914-
915897
btck_BlockValidationState* btck_block_validation_state_create()
916898
{
917899
return btck_BlockValidationState::create();
@@ -1062,21 +1044,21 @@ btck_ChainstateManager* btck_chainstate_manager_create(
10621044
return btck_ChainstateManager::create(std::move(chainman), opts.m_context);
10631045
}
10641046

1065-
const btck_BlockTreeEntry* btck_chainstate_manager_get_block_tree_entry_by_hash(const btck_ChainstateManager* chainman, const btck_BlockHash* block_hash)
1047+
const btck_Chain* btck_chainstate_manager_find(const btck_ChainstateManager* chainman, const btck_BlockHash* block_hash)
10661048
{
10671049
auto block_index = WITH_LOCK(btck_ChainstateManager::get(chainman).m_chainman->GetMutex(),
10681050
return btck_ChainstateManager::get(chainman).m_chainman->m_blockman.LookupBlockIndex(btck_BlockHash::get(block_hash)));
10691051
if (!block_index) {
10701052
LogDebug(BCLog::KERNEL, "A block with the given hash is not indexed.");
10711053
return nullptr;
10721054
}
1073-
return btck_BlockTreeEntry::ref(block_index);
1055+
return btck_Chain::ref(block_index);
10741056
}
10751057

1076-
const btck_BlockTreeEntry* btck_chainstate_manager_get_best_entry(const btck_ChainstateManager* chainstate_manager)
1058+
const btck_Chain* btck_chainstate_manager_get_best_chain(const btck_ChainstateManager* chainstate_manager)
10771059
{
10781060
auto& chainman = *btck_ChainstateManager::get(chainstate_manager).m_chainman;
1079-
return btck_BlockTreeEntry::ref(WITH_LOCK(chainman.GetMutex(), return chainman.m_best_header));
1061+
return btck_Chain::ref(WITH_LOCK(chainman.GetMutex(), return chainman.m_best_header));
10801062
}
10811063

10821064
void btck_chainstate_manager_destroy(btck_ChainstateManager* chainman)
@@ -1186,36 +1168,20 @@ void btck_block_destroy(btck_Block* block)
11861168
delete block;
11871169
}
11881170

1189-
btck_Block* btck_block_read(const btck_ChainstateManager* chainman, const btck_BlockTreeEntry* entry)
1171+
btck_Block* btck_block_read(const btck_ChainstateManager* chainman, const btck_Chain* chain)
11901172
{
1173+
if (!chain) {
1174+
LogDebug(BCLog::KERNEL, "The empty chain has no tip block to read.");
1175+
return nullptr;
1176+
}
11911177
auto block{std::make_shared<CBlock>()};
1192-
if (!btck_ChainstateManager::get(chainman).m_chainman->m_blockman.ReadBlock(*block, btck_BlockTreeEntry::get(entry))) {
1178+
if (!btck_ChainstateManager::get(chainman).m_chainman->m_blockman.ReadBlock(*block, btck_Chain::get(chain))) {
11931179
LogError("Failed to read block.");
11941180
return nullptr;
11951181
}
11961182
return btck_Block::create(block);
11971183
}
11981184

1199-
btck_BlockHeader* btck_block_tree_entry_get_block_header(const btck_BlockTreeEntry* entry)
1200-
{
1201-
return btck_BlockHeader::create(btck_BlockTreeEntry::get(entry).GetBlockHeader());
1202-
}
1203-
1204-
int32_t btck_block_tree_entry_get_height(const btck_BlockTreeEntry* entry)
1205-
{
1206-
return btck_BlockTreeEntry::get(entry).nHeight;
1207-
}
1208-
1209-
const btck_BlockHash* btck_block_tree_entry_get_block_hash(const btck_BlockTreeEntry* entry)
1210-
{
1211-
return btck_BlockHash::ref(btck_BlockTreeEntry::get(entry).phashBlock);
1212-
}
1213-
1214-
int btck_block_tree_entry_equals(const btck_BlockTreeEntry* entry1, const btck_BlockTreeEntry* entry2)
1215-
{
1216-
return &btck_BlockTreeEntry::get(entry1) == &btck_BlockTreeEntry::get(entry2);
1217-
}
1218-
12191185
btck_BlockHash* btck_block_hash_create(const unsigned char block_hash[32])
12201186
{
12211187
return btck_BlockHash::create(std::span<const unsigned char>{block_hash, 32});
@@ -1241,14 +1207,18 @@ void btck_block_hash_destroy(btck_BlockHash* hash)
12411207
delete hash;
12421208
}
12431209

1244-
btck_BlockSpentOutputs* btck_block_spent_outputs_read(const btck_ChainstateManager* chainman, const btck_BlockTreeEntry* entry)
1210+
btck_BlockSpentOutputs* btck_block_spent_outputs_read(const btck_ChainstateManager* chainman, const btck_Chain* chain)
12451211
{
1212+
if (!chain) {
1213+
LogDebug(BCLog::KERNEL, "The empty chain has no tip block to read spent outputs for.");
1214+
return nullptr;
1215+
}
12461216
auto block_undo{std::make_shared<CBlockUndo>()};
1247-
if (btck_BlockTreeEntry::get(entry).nHeight < 1) {
1217+
if (btck_Chain::get(chain).nHeight == 0) {
12481218
LogDebug(BCLog::KERNEL, "The genesis block does not have any spent outputs.");
12491219
return btck_BlockSpentOutputs::create(block_undo);
12501220
}
1251-
if (!btck_ChainstateManager::get(chainman).m_chainman->m_blockman.ReadBlockUndo(*block_undo, btck_BlockTreeEntry::get(entry))) {
1221+
if (!btck_ChainstateManager::get(chainman).m_chainman->m_blockman.ReadBlockUndo(*block_undo, btck_Chain::get(chain))) {
12521222
LogError("Failed to read block spent outputs data.");
12531223
return nullptr;
12541224
}
@@ -1355,25 +1325,83 @@ btck_BlockValidationState* btck_chainstate_manager_process_block_header(
13551325

13561326
const btck_Chain* btck_chainstate_manager_get_active_chain(const btck_ChainstateManager* chainman)
13571327
{
1358-
return btck_Chain::ref(&WITH_LOCK(btck_ChainstateManager::get(chainman).m_chainman->GetMutex(), return btck_ChainstateManager::get(chainman).m_chainman->ActiveChain()));
1328+
return btck_Chain::ref(WITH_LOCK(btck_ChainstateManager::get(chainman).m_chainman->GetMutex(), return btck_ChainstateManager::get(chainman).m_chainman->ActiveChain().Tip()));
13591329
}
13601330

13611331
int32_t btck_chain_get_height(const btck_Chain* chain)
13621332
{
1363-
LOCK(::cs_main);
1364-
return btck_Chain::get(chain).Height();
1333+
// A null chain is the empty chain, which has no blocks and so a height of -1.
1334+
return chain ? btck_Chain::get(chain).nHeight : -1;
13651335
}
13661336

1367-
const btck_BlockTreeEntry* btck_chain_get_by_height(const btck_Chain* chain, int32_t height)
1337+
btck_BlockHeader* btck_chain_get_block_header(const btck_Chain* chain, int32_t height)
13681338
{
1369-
LOCK(::cs_main);
1370-
return btck_BlockTreeEntry::ref(btck_Chain::get(chain)[height]);
1339+
const auto* index{chain ? btck_Chain::get(chain).GetAncestor(height) : nullptr};
1340+
if (!index) {
1341+
LogDebug(BCLog::KERNEL, "The requested height is not in the chain.");
1342+
return nullptr;
1343+
}
1344+
return btck_BlockHeader::create(index->GetBlockHeader());
1345+
}
1346+
1347+
const btck_BlockHash* btck_chain_get_block_hash(const btck_Chain* chain, int32_t height)
1348+
{
1349+
const auto* index{chain ? btck_Chain::get(chain).GetAncestor(height) : nullptr};
1350+
if (!index) {
1351+
LogDebug(BCLog::KERNEL, "The requested height is not in the chain.");
1352+
return nullptr;
1353+
}
1354+
return btck_BlockHash::ref(index->phashBlock);
1355+
}
1356+
1357+
const btck_Chain* btck_chain_get_ancestor(const btck_Chain* chain, int32_t height)
1358+
{
1359+
const auto* ancestor{chain ? btck_Chain::get(chain).GetAncestor(height) : nullptr};
1360+
if (!ancestor) {
1361+
LogDebug(BCLog::KERNEL, "The requested height is not in the chain.");
1362+
return nullptr;
1363+
}
1364+
return btck_Chain::ref(ancestor);
1365+
}
1366+
1367+
const btck_Chain* btck_chain_get_parent(const btck_Chain* chain)
1368+
{
1369+
// The parent of the empty chain, and of the genesis-only chain, is the empty
1370+
// chain.
1371+
const auto* pprev{chain ? btck_Chain::get(chain).pprev : nullptr};
1372+
if (!pprev) {
1373+
return nullptr;
1374+
}
1375+
return btck_Chain::ref(pprev);
1376+
}
1377+
1378+
int btck_chain_starts_with(const btck_Chain* chain, const btck_Chain* prefix)
1379+
{
1380+
// The empty chain is a prefix of every chain; a non-empty prefix cannot be a
1381+
// prefix of the empty chain.
1382+
if (!prefix) return 1;
1383+
if (!chain) return 0;
1384+
const auto& tip{btck_Chain::get(chain)};
1385+
const auto& prefix_tip{btck_Chain::get(prefix)};
1386+
return tip.GetAncestor(prefix_tip.nHeight) == &prefix_tip ? 1 : 0;
1387+
}
1388+
1389+
const btck_Chain* btck_chain_find_fork(const btck_Chain* chain1, const btck_Chain* chain2)
1390+
{
1391+
// The empty chain shares no block with any chain, so the fork is empty.
1392+
if (!chain1 || !chain2) return nullptr;
1393+
const auto* common{LastCommonAncestor(&btck_Chain::get(chain1), &btck_Chain::get(chain2))};
1394+
if (!common) {
1395+
return nullptr;
1396+
}
1397+
return btck_Chain::ref(common);
13711398
}
13721399

1373-
int btck_chain_contains(const btck_Chain* chain, const btck_BlockTreeEntry* entry)
1400+
int btck_chain_equals(const btck_Chain* chain1, const btck_Chain* chain2)
13741401
{
1375-
LOCK(::cs_main);
1376-
return btck_Chain::get(chain).Contains(btck_BlockTreeEntry::get(entry)) ? 1 : 0;
1402+
// Chains are identified by their tip, which a btck_Chain* aliases directly,
1403+
// so pointer equality is chain equality (and the empty chain equals itself).
1404+
return chain1 == chain2 ? 1 : 0;
13771405
}
13781406

13791407
btck_BlockHeader* btck_block_header_create(const void* raw_block_header, size_t raw_block_header_len)

0 commit comments

Comments
 (0)