Skip to content

Commit ada0260

Browse files
authored
dgb: track absolute block height in HeaderChain (M3 4c prerequisite) (#209)
Add tip_height()/next_block_height()/base_height()/set_base_height() to dgb::HeaderChain. Block height is a pure function of base height + appended header count: index i holds absolute height base+i, no separate counter that could drift from m_chain. DGB counts every block of every algo as one height, so height advances on BOTH VALIDATED_SCRYPT and ACCEPTED_CONTINUITY ingests and only those -- a REJECTED header is checked before push_back and never advances. set_base_height seeds a fast-start checkpoint while the chain is empty (default 0 = genesis). This is the next_h source the embedded TemplateBuilder port needs for the BIP34 height push and the subsidy_func(height) lookup that feeds the embedded coinbasevalue SSOT (#207); external-daemon GBT keeps its own authoritative height field. Fenced to src/impl/dgb/. +4 tests into header_chain_test (no new gtest target), 31/31 pass. Co-authored-by: frstrtr <frstrtr@users.noreply.github.com>
1 parent d283596 commit ada0260

2 files changed

Lines changed: 127 additions & 0 deletions

File tree

src/impl/dgb/coin/header_chain.hpp

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
#include <cstdint>
4545
#include <functional>
4646
#include <limits>
47+
#include <optional>
4748
#include <vector>
4849

4950
#include <impl/dgb/coin/dgb_arith256.hpp>
@@ -321,6 +322,50 @@ class HeaderChain {
321322
uint64_t cumulative_work() const { return m_cumulative_work; }
322323
std::size_t size() const { return m_chain.size(); }
323324

325+
// ── Absolute block-height tracking (M3 §4c prerequisite) ────────────────
326+
// DGB block height counts EVERY block of EVERY algo: one block == one
327+
// height regardless of Scrypt / SHA256d / Skein / Qubit / Odocrypt. The
328+
// Scrypt-only HeaderChain still appends non-Scrypt headers via
329+
// accept-by-continuity (work-neutral but chain-extending), so block height
330+
// advances on BOTH VALIDATED_SCRYPT and ACCEPTED_CONTINUITY ingests and
331+
// ONLY those -- a REJECTED header never push_back()s and never advances
332+
// height (rejection is checked before every push_back above). Height is
333+
// therefore a pure function of the seed height + appended-header count:
334+
// index i holds absolute height (m_base_height + i), with no separate
335+
// mutable counter that could drift from m_chain.
336+
//
337+
// The embedded TemplateBuilder port (mirror of btc build_template) reads
338+
// next_block_height() for the coinbase BIP34 height push and the
339+
// subsidy_func(height) lookup that feeds embedded_coinbase_value (#207).
340+
// External-daemon GBT still carries its own authoritative "height"; this
341+
// accessor is the embedded-path source for the SAME field.
342+
343+
// Absolute height assigned to m_chain[0] (the seed/genesis or checkpoint
344+
// header). Default 0 == sync-from-genesis; set_base_height() seeds a
345+
// fast-start checkpoint before the first append.
346+
uint32_t base_height() const { return m_base_height; }
347+
348+
// Seed the absolute height of the FIRST appended header. Must be called
349+
// while the chain is still empty (a checkpoint fast-start); a no-op call
350+
// order otherwise would silently mis-number live headers.
351+
void set_base_height(uint32_t h) { m_base_height = h; }
352+
353+
// Absolute height of the newest header, or nullopt for an empty chain.
354+
std::optional<uint32_t> tip_height() const
355+
{
356+
if (m_chain.empty())
357+
return std::nullopt;
358+
return static_cast<uint32_t>(m_base_height + (m_chain.size() - 1));
359+
}
360+
361+
// Absolute height of the block the next template builds on top of the tip
362+
// == tip_height()+1, or m_base_height for an empty chain. This is the
363+
// template builder's `next_h` (btc template_builder.hpp: tip.height + 1).
364+
uint32_t next_block_height() const
365+
{
366+
return static_cast<uint32_t>(m_base_height + m_chain.size());
367+
}
368+
324369
// Bitcoin/DigiByte median-time-past span (ContextualCheckBlockHeader uses
325370
// the tip + its 10 nearest ancestors == 11 timestamps).
326371
static constexpr std::size_t MEDIAN_TIME_SPAN = 11;
@@ -357,6 +402,7 @@ class HeaderChain {
357402
}
358403

359404
DigiShieldParams m_ds_params{}; // retarget gate params
405+
uint32_t m_base_height = 0; // abs height of m_chain[0]
360406
std::size_t m_retarget_window = 0; // Scrypt window depth
361407
std::vector<HeaderSample> m_chain; // oldest .. newest
362408
uint64_t m_cumulative_work = 0;

src/impl/dgb/test/header_chain_test.cpp

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -646,3 +646,84 @@ TEST(HeaderChainMtp, ContinuityHeaderItselfBypassesMtp)
646646
IngestResult::ACCEPTED_CONTINUITY);
647647
EXPECT_EQ(hc.size(), 2u);
648648
}
649+
650+
651+
// ---------------------------------------------------------------------------
652+
// Absolute block-height tracking (M3 §4c prerequisite). Height is the
653+
// embedded TemplateBuilder's next_h source (subsidy_func(height) + BIP34
654+
// push). DGB counts every block of every algo, so BOTH Scrypt-validated and
655+
// continuity appends advance height; a rejected header never does.
656+
// ---------------------------------------------------------------------------
657+
658+
TEST(HeaderChainBlockHeight, EmptyChainHasNoTipHeight)
659+
{
660+
HeaderChain hc;
661+
EXPECT_FALSE(hc.tip_height().has_value());
662+
EXPECT_EQ(hc.base_height(), 0u);
663+
EXPECT_EQ(hc.next_block_height(), 0u); // genesis is the next block
664+
}
665+
666+
TEST(HeaderChainBlockHeight, EveryAlgoAdvancesHeight)
667+
{
668+
HeaderChain hc;
669+
// Scrypt block at height 0.
670+
ASSERT_EQ(hc.validate_and_append({SCRYPT, 1000, 100}),
671+
IngestResult::VALIDATED_SCRYPT);
672+
EXPECT_EQ(hc.tip_height().value(), 0u);
673+
EXPECT_EQ(hc.next_block_height(), 1u);
674+
675+
// A continuity (non-Scrypt) block STILL occupies a height -- DGB height
676+
// counts all five algos, not just the Scrypt-validated subset.
677+
ASSERT_EQ(hc.validate_and_append({SHA256D, 1500, 100}),
678+
IngestResult::ACCEPTED_CONTINUITY);
679+
EXPECT_EQ(hc.tip_height().value(), 1u);
680+
EXPECT_EQ(hc.next_block_height(), 2u);
681+
682+
ASSERT_EQ(hc.validate_and_append({SKEIN, 1200, 100}),
683+
IngestResult::ACCEPTED_CONTINUITY);
684+
EXPECT_EQ(hc.tip_height().value(), 2u);
685+
686+
// Another Scrypt block (time strictly above the MTP of {1000,1500,1200}).
687+
ASSERT_EQ(hc.validate_and_append({SCRYPT, 2000, 100}),
688+
IngestResult::VALIDATED_SCRYPT);
689+
EXPECT_EQ(hc.tip_height().value(), 3u);
690+
EXPECT_EQ(hc.next_block_height(), 4u);
691+
EXPECT_EQ(hc.size(), 4u);
692+
}
693+
694+
TEST(HeaderChainBlockHeight, RejectedHeaderNeverAdvancesHeight)
695+
{
696+
HeaderChain hc;
697+
ASSERT_EQ(hc.validate_and_append({SCRYPT, 1000, 100}),
698+
IngestResult::VALIDATED_SCRYPT);
699+
EXPECT_EQ(hc.tip_height().value(), 0u);
700+
701+
// Malformed Scrypt header (zero target) is rejected -- it must not occupy
702+
// a height. tip/next stay pinned exactly where the last accepted block left
703+
// them, so the embedded coinbasevalue can never be computed for a phantom
704+
// block the chain never accepted.
705+
EXPECT_EQ(hc.validate_and_append({SCRYPT, 1100, 0}),
706+
IngestResult::REJECTED);
707+
EXPECT_EQ(hc.tip_height().value(), 0u);
708+
EXPECT_EQ(hc.next_block_height(), 1u);
709+
EXPECT_EQ(hc.size(), 1u);
710+
711+
// The next genuinely-valid block takes the height the reject did NOT.
712+
ASSERT_EQ(hc.validate_and_append({SCRYPT, 2000, 100}),
713+
IngestResult::VALIDATED_SCRYPT);
714+
EXPECT_EQ(hc.tip_height().value(), 1u);
715+
}
716+
717+
TEST(HeaderChainBlockHeight, CheckpointSeedNumbersFirstHeader)
718+
{
719+
// Fast-start: seed the absolute height of m_chain[0] before any append.
720+
HeaderChain hc;
721+
hc.set_base_height(12345);
722+
EXPECT_FALSE(hc.tip_height().has_value());
723+
EXPECT_EQ(hc.next_block_height(), 12345u); // the seed block itself
724+
725+
ASSERT_EQ(hc.validate_and_append({SCRYPT, 1000, 100}),
726+
IngestResult::VALIDATED_SCRYPT);
727+
EXPECT_EQ(hc.tip_height().value(), 12345u);
728+
EXPECT_EQ(hc.next_block_height(), 12346u);
729+
}

0 commit comments

Comments
 (0)