Skip to content

Commit 793668f

Browse files
committed
dgb(g2): SIM KAT C5 — Scrypt-only / 4-algos-by-continuity posture pin
Extends the rig-free AutoRatchet SIM KAT (arming the G2 staged-migration harness) with the C5 row: the V36 SCRYPT-ONLY posture and its load-bearing intersection with the #288 work-weighted accept gate. C5 posture disposition vs coin/dgb_block_algo.hpp SSOT: Scrypt header -> VALIDATE_SCRYPT (the only validated PoW lane); SHA256d/Skein/Qubit/Odo + legacy Groestl -> ACCEPT_BY_CONTINUITY (work-neutral); unknown algo nibbles -> REJECT. C5/work-neutral accept-by-continuity blocks credit ZERO work, so a >=95%- by-headcount V36 majority carried by non-Scrypt algos canNOT outrun the 60%-by-work ratchet accept gate -- the multi-algo analogue of C3_MintCannotOutrunAccept. Only Scrypt votes ever move the gate. Pins the consensus SSOT (dgb_block_algo.hpp, itself guarded vs upstream DigiByte Core by algo_select_test); no consensus surface mutated. FENCED to src/impl/dgb/ test-tree. 9/9 (was 7/7).
1 parent 78356dd commit 793668f

1 file changed

Lines changed: 128 additions & 3 deletions

File tree

src/impl/dgb/test/auto_ratchet_sim_test.cpp

Lines changed: 128 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// dgb_auto_ratchet_sim_test: FENCED, additive rig-free SIM KAT that arms the
22
// DGB G2 staged-migration harness
3-
// (scripts/dgb_g2_ratchet_staged_migration_harness.sh) C2/C3/C4 rows WITHOUT a
4-
// Scrypt rig or a live digibyted parent -- the C++ counterpart of that
3+
// (scripts/dgb_g2_ratchet_staged_migration_harness.sh) C2/C3/C4/C5 rows WITHOUT
4+
// a Scrypt rig or a live digibyted parent -- the C++ counterpart of that
55
// harness's --sim-votes path. It exercises the AutoRatchet staged-gate LOGIC
66
// over hand-derived oracle votes:
77
//
@@ -10,6 +10,9 @@
1010
// outrun it (mint-cannot-outrun-accept) -- the property that keeps a
1111
// minted V36 boundary share from being rejected by every peer
1212
// C4 ratchet state persists across restart (CONFIRMED survives reconstruct)
13+
// C5 SCRYPT-ONLY / 4-algos-by-continuity posture (project_v36_dgb_scrypt_only):
14+
// only Scrypt headers are validated; the other algos accept-by-continuity
15+
// (work-NEUTRAL) and so cannot move the 60%-by-work ratchet accept gate
1316
//
1417
// DGB ORACLE PIN (NOT the LTC v35 transition): DGB conforms to its OWN oracle
1518
// frstrtr/p2pool-dgb-scrypt @22761e7, which mints share VERSION=35 with
@@ -21,12 +24,15 @@
2124
// Consensus surface is NOT mutated. C3 pins the live 60%-by-WORK gate via a
2225
// VERBATIM replica of the inline tail-guard expression in
2326
// AutoRatchet::get_share_version (auto_ratchet.hpp), localising the gate the
24-
// same non-circular way dgb_auto_ratchet_tail_guard_test does. Test-only /
27+
// same non-circular way dgb_auto_ratchet_tail_guard_test does. C5 pins the
28+
// multi-algo disposition against the consensus SSOT coin/dgb_block_algo.hpp
29+
// (itself guarded vs upstream DigiByte Core by algo_select_test). Test-only /
2530
// dgb-tree-local.
2631

2732
#include <gtest/gtest.h>
2833

2934
#include <impl/dgb/auto_ratchet.hpp> // also pulls share_tracker.hpp + config_pool.hpp
35+
#include <impl/dgb/coin/dgb_block_algo.hpp> // dgb_header_disposition, is_scrypt_header
3036
#include <core/uint256.hpp> // uint288
3137

3238
#include <cstdint>
@@ -38,6 +44,10 @@
3844

3945
using dgb::AutoRatchet;
4046
using dgb::RatchetState;
47+
using dgb::coin::DgbAlgo;
48+
using dgb::coin::HeaderDisposition;
49+
using dgb::coin::dgb_header_disposition;
50+
using dgb::coin::is_scrypt_header;
4151

4252
namespace {
4353

@@ -79,6 +89,28 @@ bool effective_activation(int votes, int total,
7989
// V36, carrying `w_target` of `w_total` work.
8090
struct Stage { int votes; int total; uint288 w_target; uint288 w_total; };
8191

92+
// One staged-migration block AS SEEN BY THE CROSSING: which algo mined it (DGB
93+
// nVersion algo bits), whether it votes V36, and how much PoW work it carries.
94+
// The G2 work-weighted accept gate credits work ONLY for Scrypt blocks; a
95+
// non-Scrypt block is accept-by-continuity == work-NEUTRAL (coin/header_chain.hpp
96+
// THIRD INVARIANT), so its work NEVER enters the ratchet tally however it votes.
97+
struct AlgoBlock { int32_t n_version; bool votes_v36; uint32_t work; };
98+
99+
// Work this block credits to the V36 accept tally: the Scrypt-only,
100+
// work-neutral-continuity posture localised for the sim. Mirrors
101+
// header_credits_work() forwarding to is_scrypt_header().
102+
uint288 scrypt_v36_work_credit(const AlgoBlock& b)
103+
{
104+
return (is_scrypt_header(b.n_version) && b.votes_v36) ? uint288(b.work)
105+
: uint288(0);
106+
}
107+
// Work this block credits to the gate denominator: again Scrypt-only, since a
108+
// continuity header widens neither numerator nor denominator of the tail guard.
109+
uint288 scrypt_total_work_credit(const AlgoBlock& b)
110+
{
111+
return is_scrypt_header(b.n_version) ? uint288(b.work) : uint288(0);
112+
}
113+
82114
} // namespace
83115

84116
// ---------------------------------------------------------------------------
@@ -226,3 +258,96 @@ TEST(DGB_AutoRatchetSim, C4_FreshNodeMintsBaselineOnBootstrap)
226258
EXPECT_EQ(mint, DGB_BASE_VERSION);
227259
EXPECT_EQ(vote, DGB_TARGET_VERSION);
228260
}
261+
262+
// ---------------------------------------------------------------------------
263+
// C5 — SCRYPT-ONLY POSTURE PIN (project_v36_dgb_scrypt_only). V36 c2pool-dgb
264+
// validates the SCRYPT path ONLY; the V36-deferred algos accept-by-continuity;
265+
// unknown algo bits reject. Mirrors the per-header disposition the harness C5
266+
// row exercises against the consensus SSOT coin/dgb_block_algo.hpp (itself
267+
// guarded vs upstream DigiByte Core block.h by algo_select_test).
268+
// ---------------------------------------------------------------------------
269+
TEST(DGB_AutoRatchetSim, C5_ScryptOnlyPostureDisposition)
270+
{
271+
using dgb::coin::DGB_BLOCK_VERSION_SCRYPT;
272+
using dgb::coin::DGB_BLOCK_VERSION_SHA256D;
273+
using dgb::coin::DGB_BLOCK_VERSION_GROESTL;
274+
using dgb::coin::DGB_BLOCK_VERSION_SKEIN;
275+
using dgb::coin::DGB_BLOCK_VERSION_QUBIT;
276+
using dgb::coin::DGB_BLOCK_VERSION_ODO;
277+
278+
// Scrypt is the all-zero algo codepoint -> the ONLY validated PoW lane.
279+
EXPECT_TRUE(is_scrypt_header(DGB_BLOCK_VERSION_SCRYPT));
280+
EXPECT_EQ(dgb_header_disposition(DGB_BLOCK_VERSION_SCRYPT),
281+
HeaderDisposition::VALIDATE_SCRYPT);
282+
283+
// The four V36-deferred algos (SHA256d, Skein, Qubit, Odocrypt) and legacy
284+
// Groestl accept-by-continuity (work-neutral) -- NOT validate, NOT reject.
285+
for (int32_t v : { DGB_BLOCK_VERSION_SHA256D, DGB_BLOCK_VERSION_SKEIN,
286+
DGB_BLOCK_VERSION_QUBIT, DGB_BLOCK_VERSION_ODO,
287+
DGB_BLOCK_VERSION_GROESTL }) {
288+
EXPECT_FALSE(is_scrypt_header(v));
289+
EXPECT_EQ(dgb_header_disposition(v),
290+
HeaderDisposition::ACCEPT_BY_CONTINUITY) << "version_bits=" << v;
291+
}
292+
293+
// Algo nibbles NOT in the DGB map (1/3/5/7/9/15 << 8) reject. Note 7<<8 is
294+
// unknown: ALGO_ODO==7 in the id space but BLOCK_VERSION_ODO encodes 14<<8.
295+
for (int32_t sel : { 1, 3, 5, 7, 9, 15 }) {
296+
EXPECT_EQ(dgb_header_disposition(sel << 8),
297+
HeaderDisposition::REJECT) << "algo_nibble=" << sel;
298+
}
299+
}
300+
301+
// ---------------------------------------------------------------------------
302+
// C5/work-neutral — the load-bearing G2 intersection of the Scrypt-only
303+
// posture with the #288 accept gate: accept-by-continuity non-Scrypt blocks are
304+
// WORK-NEUTRAL, so they cannot move the 60%-by-work ratchet accept gate however
305+
// they vote. A crossing whose V36 vote MAJORITY (>=95% by head-count) is carried
306+
// by non-Scrypt algos credits ZERO V36 work; the Scrypt work tally still decides
307+
// the gate. This is the multi-algo analogue of C3_MintCannotOutrunAccept and is
308+
// what stops a SHA256d/Skein/Qubit/Odo majority from spoofing a Scrypt-sharechain
309+
// v35 -> v36 activation.
310+
// ---------------------------------------------------------------------------
311+
TEST(DGB_AutoRatchetSim, C5_ContinuityVotesCannotOutrunScryptWorkGate)
312+
{
313+
using dgb::coin::DGB_BLOCK_VERSION_SCRYPT;
314+
using dgb::coin::DGB_BLOCK_VERSION_SHA256D;
315+
using dgb::coin::DGB_BLOCK_VERSION_SKEIN;
316+
using dgb::coin::DGB_BLOCK_VERSION_QUBIT;
317+
using dgb::coin::DGB_BLOCK_VERSION_ODO;
318+
319+
std::vector<AlgoBlock> blocks;
320+
blocks.push_back({ DGB_BLOCK_VERSION_SCRYPT, /*votes_v36*/false, 1000 }); // lone Scrypt V35
321+
const int32_t non_scrypt[4] = { DGB_BLOCK_VERSION_SHA256D, DGB_BLOCK_VERSION_SKEIN,
322+
DGB_BLOCK_VERSION_QUBIT, DGB_BLOCK_VERSION_ODO };
323+
for (int i = 0; i < 19; ++i)
324+
blocks.push_back({ non_scrypt[i % 4], /*votes_v36*/true, 50 }); // non-Scrypt V36 voters
325+
326+
uint288 w_target(0), w_total(0);
327+
int v36 = 0, total = 0;
328+
for (const auto& b : blocks) {
329+
w_target = w_target + scrypt_v36_work_credit(b);
330+
w_total = w_total + scrypt_total_work_credit(b);
331+
if (b.votes_v36) ++v36;
332+
++total;
333+
}
334+
EXPECT_EQ(total, 20);
335+
EXPECT_EQ(v36, 19);
336+
EXPECT_TRUE(count_activates(v36, total)); // 19/20 = 95% -> count gate WOULD fire
337+
EXPECT_TRUE(w_target == uint288(0)); // but every V36 vote is continuity
338+
EXPECT_TRUE(w_total == uint288(1000)); // only the lone Scrypt block credits work
339+
EXPECT_FALSE(inline_tail_ok(w_target, w_total)); // work gate holds
340+
EXPECT_FALSE(effective_activation(v36, total, w_target, w_total)); // crossing blocked
341+
342+
// Flip the lone Scrypt miner to V36: now 100% of the CREDITED (Scrypt) work
343+
// is V36 and the work gate passes -- only Scrypt votes ever move the gate.
344+
blocks[0].votes_v36 = true;
345+
uint288 w_t2(0), w_all2(0);
346+
for (const auto& b : blocks) {
347+
w_t2 = w_t2 + scrypt_v36_work_credit(b);
348+
w_all2 = w_all2 + scrypt_total_work_credit(b);
349+
}
350+
EXPECT_TRUE(w_t2 == uint288(1000));
351+
EXPECT_TRUE(w_all2 == uint288(1000));
352+
EXPECT_TRUE(inline_tail_ok(w_t2, w_all2)); // Scrypt work now 100% V36 -> gate passes
353+
}

0 commit comments

Comments
 (0)