Skip to content

Commit d29493f

Browse files
committed
dgb: pin V36 decayed-PPLNS weights with bit-exact KAT
Add DecayedPPLNSWeightsKAT to dgb_share_test, closing the gap left by the Phase B share-weight/PPLNS conformance audit: share_test.cpp guarded the sharechain-identity and consensus params but nothing pinned the decay constants or the weight recurrence in DensePPLNSWindow::compute_v36_weights(). The KAT pins DECAY_PRECISION=40 / LN2_MICRO=693147 / half_life=max(len/4,1), the full depth-0..7 decay table, the per-step decay factor, and the per-script address/donation/total weights for a hand-built window. Ground truth is re-derived independently with bigint integer math mirroring the exact fixed-point ops (iterative mul128_shift), so a drift in any constant or in the weight math fails loudly. Conforms to the V36 MASTER reference decayed-weight path, not the legacy flat-weight oracle. Appended to the existing dgb_share_test target (already in both build.yml --target allowlists), so no new CI registration is required.
1 parent 345e88a commit d29493f

1 file changed

Lines changed: 48 additions & 0 deletions

File tree

src/impl/dgb/test/share_test.cpp

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include <impl/dgb/config_pool.hpp>
1515
#include <impl/dgb/config_coin.hpp>
1616
#include <impl/dgb/share.hpp>
17+
#include <impl/dgb/share_tracker.hpp> // DensePPLNSWindow — V36 decayed-PPLNS SSOT
1718

1819
// Sharechain-identity: the isolation primitives (PREFIX / IDENTIFIER) are the
1920
// per-coin namespacing boundary — they MUST stay byte-exact to the DGB oracle.
@@ -88,3 +89,50 @@ TEST(DGB_share_test, LoadShareSymbolLinks)
8889
auto fn = static_cast<dgb::ShareType (*)(chain::RawShare&, NetService)>(&dgb::load_share);
8990
EXPECT_NE(fn, nullptr);
9091
}
92+
93+
// ── V36 decayed-PPLNS bit-exact KAT ───────────────────────────────────────
94+
// Pins DensePPLNSWindow's decay constants + weight recurrence to the V36 MASTER
95+
// (frstrtr/p2pool-merged-v36 data.py get_decayed_cumulative_weights) — NOT the
96+
// legacy flat-weight oracle (that path is pre-v36 compat, replaced in the
97+
// transition). 3-bucket: decayed-PPLNS = BUCKET 2 v36-native shared structure
98+
// (DGB == ltc == merged-v36 ref). Ground truth re-derived independently with
99+
// bigint integer math mirroring the exact fixed-point ops (DECAY_PRECISION=40,
100+
// LN2_MICRO=693147, half_life=max(chain_len/4,1), iterative mul128_shift). This
101+
// guard fails loudly if any decay constant or the weight math drifts.
102+
TEST(DGB_share_test, DecayedPPLNSWeightsKAT)
103+
{
104+
using W = dgb::DensePPLNSWindow;
105+
106+
// chain_len = 8 -> half_life = max(8/4,1) = 2. Pin the per-step factor and
107+
// the full depth-0..7 decay table against the independent bigint oracle.
108+
W::init_decay_table(8);
109+
EXPECT_EQ(W::s_decay_per, 718450034647ull);
110+
ASSERT_EQ(W::s_decay_table.size(), 8u);
111+
EXPECT_EQ(W::s_decay_table[0], 1099511627776ull); // == DECAY_SCALE = 2^40
112+
EXPECT_EQ(W::s_decay_table[1], 718450034647ull);
113+
EXPECT_EQ(W::s_decay_table[2], 469454291564ull);
114+
EXPECT_EQ(W::s_decay_table[3], 306753874646ull);
115+
EXPECT_EQ(W::s_decay_table[4], 200441110671ull);
116+
EXPECT_EQ(W::s_decay_table[5], 130973533401ull);
117+
EXPECT_EQ(W::s_decay_table[6], 85581577522ull);
118+
EXPECT_EQ(W::s_decay_table[7], 55921270664ull);
119+
120+
// Hand-built window (depth 0 = newest). att kept small so every product
121+
// stays < 2^64 and the expected weights are exact uint64 literals.
122+
std::vector<unsigned char> A{0xAA}, B{0xBB}, C{0xCC};
123+
dgb::DensePPLNSWindow win;
124+
win.m_entries.push_back({uint288(1000000ull), 0u, A}); // depth 0
125+
win.m_entries.push_back({uint288(1000000ull), 6553u, B}); // depth 1
126+
win.m_entries.push_back({uint288(2000000ull), 0u, A}); // depth 2
127+
win.m_entries.push_back({uint288(500000ull), 65535u, C}); // depth 3 (full donation)
128+
129+
auto w = win.compute_v36_weights();
130+
131+
// Per-script address weight: A = depth0 + depth2, B = depth1, C = 0 (full
132+
// donation consumed the weight). Totals from the same re-derivation.
133+
EXPECT_EQ(w.weights.at(A), 121497433620ull);
134+
EXPECT_EQ(w.weights.at(B), 38540372332ull);
135+
EXPECT_EQ(w.weights.at(C), 0ull);
136+
EXPECT_EQ(w.total_weight, 173461511355ull);
137+
EXPECT_EQ(w.total_donation_weight, 13423705403ull);
138+
}

0 commit comments

Comments
 (0)