Skip to content

Commit 6d6850d

Browse files
committed
dgb: M3 §7b demote nBits-exact retarget gate to V36 no-op
The ingest path no longer re-derives the required next-work and rejects on an nBits-exact mismatch. V36's defining constraint is p2pool-merged-v36 compatibility, and that reference never recomputes parent difficulty -- it trusts the parent header's declared nBits and checks PoW against it. The two daemon-independent CheckProofOfWork halves (pow_limit floor + scrypt(header) <= target) remain and ARE the correct, complete V36 parent-difficulty validation. Re-derivation is also structurally impossible in a Scrypt-only walk: DigiByte's live retarget is MultiShield V4, whose averaging window is GLOBAL across all 5 algos with per-algo adjust + MedianTimePast deltas + /4 damping -- reconstructing it needs full multi-algo header tracking == V37 (5-algo validation). digishield_next_target()/next_retarget_window() are retained as test scaffolding and a reference for the V37 embedded-daemon port; the ingest path deliberately no longer calls them. Test IngestGateEnforcesDigiShieldNextTarget -> IngestRetargetGateDemotedTo- NoOpForV36: a target != the single-algo DigiShield reference is now ACCEPTED; the reference value stays computable as scaffolding; PoW + ceiling halves still reject. header_chain_test 21/21 PASS. (cherry picked from commit 62398af)
1 parent b217a60 commit 6d6850d

2 files changed

Lines changed: 56 additions & 35 deletions

File tree

src/impl/dgb/coin/header_chain.hpp

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -232,19 +232,26 @@ class HeaderChain {
232232
if (h.pow_hash > h.target)
233233
return IngestResult::REJECTED;
234234

235-
// Consensus retarget gate: the declared target must equal the
236-
// DigiShield next-target computed over the Scrypt-only window
237-
// ending at the CURRENT tip (assembled BEFORE h is appended).
238-
// expected == 0 means no Scrypt ancestor is in range yet
239-
// (genesis/bootstrap) or the gate is unconfigured -- no retarget
240-
// constraint is enforceable, so the header passes on its
241-
// structural (non-zero) target alone. nBits-style exact match
242-
// mirrors Bitcoin/DigiByte consensus: the header carries the
243-
// required next-work value, not merely a target it satisfies.
244-
const RetargetWindow rw = next_retarget_window(m_retarget_window);
245-
const u256 expected = digishield_next_target(rw, m_ds_params);
246-
if (!expected.is_zero() && !(h.target == expected))
247-
return IngestResult::REJECTED;
235+
// Parent-difficulty retarget gate: DEMOTED to a no-op for V36
236+
// (integrator decision 2026-06-18, operator FYI'd). V36's defining
237+
// constraint is p2pool-merged-v36 compatibility, and that reference
238+
// NEVER re-derives parent difficulty -- it trusts the parent
239+
// header's declared nBits and checks PoW against it. The two
240+
// daemon-independent CheckProofOfWork halves above (pow_limit floor +
241+
// scrypt(header) <= target) ARE the correct, complete V36
242+
// parent-difficulty validation.
243+
//
244+
// Re-derivation is also structurally impossible here: DigiByte's
245+
// live retarget is MultiShield V4, whose averaging window is GLOBAL
246+
// across all 5 algos (Scrypt/SHA256d/Skein/Qubit/Odocrypt) with
247+
// per-algo adjust + MedianTimePast deltas + /4 damping. A Scrypt-only
248+
// header walk cannot reconstruct that window without full multi-algo
249+
// header tracking == V37 (5-algo validation) by definition.
250+
// digishield_next_target() / next_retarget_window() are retained as
251+
// test scaffolding and a reference for the V37 embedded-daemon port;
252+
// the ingest path deliberately does NOT call them here (an nBits-exact
253+
// gate would only deepen the wrong single-algo retarget model). See
254+
// V37 backlog: full V4 MultiShield recompute.
248255

249256
m_chain.push_back(h);
250257
m_cumulative_work += work_from_target(h.target); // credited

src/impl/dgb/test/header_chain_test.cpp

Lines changed: 36 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -206,40 +206,54 @@ TEST(DigiShieldRetarget, ConsumesLiveScryptOnlyWindow)
206206
}
207207

208208
// ---------------------------------------------------------------------------
209-
// Ingest-path retarget gate: validate_and_append must demand the declared
210-
// Scrypt target EQUAL the DigiShield next-target computed over the Scrypt-only
211-
// window ending at the current tip (nBits-style exact consensus match). A
212-
// default-constructed chain leaves the gate unconfigured (target_timespan 0 ->
213-
// expected 0), so every test above runs unconstrained; this one configures it.
209+
// Parent-difficulty retarget gate DEMOTED to a no-op for V36 (integrator
210+
// decision 2026-06-18, operator FYI'd). p2pool-merged-v36 never re-derives
211+
// parent difficulty -- it trusts the declared nBits and checks PoW against it.
212+
// DGB's live retarget is MultiShield V4 (a GLOBAL cross-algo averaging window),
213+
// which a Scrypt-only walk cannot reproduce -> full recompute is V37 5-algo
214+
// scope. So a declared Scrypt target that does NOT equal the single-algo
215+
// DigiShield next-target is NO LONGER rejected by the ingest path; it is
216+
// accepted on the daemon-independent CheckProofOfWork halves alone (pow_limit
217+
// floor + scrypt(header) <= target). digishield_next_target() survives as test
218+
// scaffolding / a reference for the V37 embedded-daemon port.
214219
//
215220
// window depth 1, nominal 80. Window(1) over a lone Scrypt tip has
216221
// actual_timespan 0 (front == back) -> damped = 80 + (0-80)/8 = 70, above the
217-
// floor rail 60 -> next target = avg * 70/80 = avg * 7/8, deterministically.
222+
// floor rail 60 -> reference next target = avg * 70/80 = avg * 7/8.
218223
// ---------------------------------------------------------------------------
219-
TEST(HeaderChainValidate, IngestGateEnforcesDigiShieldNextTarget)
224+
TEST(HeaderChainValidate, IngestRetargetGateDemotedToNoOpForV36)
220225
{
221226
HeaderChain hc(DigiShieldParams{80, 0}, /*retarget_window=*/1);
222227

223-
// Seed: empty window -> gate no-op, any non-zero target seeds the chain.
228+
// Seed a lone Scrypt tip.
224229
ASSERT_EQ(hc.validate_and_append({SCRYPT, 1000, 4096}),
225230
IngestResult::VALIDATED_SCRYPT);
226231

227-
// Required next target = 4096 * 7/8 = 3584. A mismatch is consensus-invalid
228-
// and must REJECT without mutating the chain (size + work unchanged).
229-
const std::size_t size_before = hc.size();
230-
const uint64_t work_before = hc.cumulative_work();
231-
EXPECT_EQ(hc.validate_and_append({SCRYPT, 1080, 4096}),
232-
IngestResult::REJECTED);
233-
EXPECT_EQ(hc.size(), size_before);
234-
EXPECT_EQ(hc.cumulative_work(), work_before);
232+
// The single-algo DigiShield reference next-target over window(1) is
233+
// 4096 * 7/8 = 3584 -- still computable as V37 embedded-port scaffolding,
234+
// even though the ingest path no longer enforces it.
235+
const RetargetWindow rw = hc.next_retarget_window(1);
236+
EXPECT_EQ(digishield_next_target(rw, DigiShieldParams{80, 0}), 3584u);
235237

236-
// The exact DigiShield target is accepted and credits work.
237-
EXPECT_EQ(hc.validate_and_append({SCRYPT, 1080, 3584}),
238+
// A header whose declared target does NOT equal that reference (4096 != 3584)
239+
// is ACCEPTED and credits work: V36 trusts the declared nBits rather than
240+
// re-deriving and demanding an exact match.
241+
const uint64_t work_before = hc.cumulative_work();
242+
EXPECT_EQ(hc.validate_and_append({SCRYPT, 1080, 4096}),
238243
IngestResult::VALIDATED_SCRYPT);
239244
EXPECT_GT(hc.cumulative_work(), work_before);
240245

241-
// Continuity headers bypass the retarget gate (work-neutral, no nBits).
242-
EXPECT_EQ(hc.validate_and_append({SHA256D, 1090, 1}),
246+
// The PoW satisfaction half still fires independent of the demoted gate: a
247+
// header whose scrypt(header) digest exceeds its declared target is rejected
248+
// without mutating the chain.
249+
HeaderSample weak{SCRYPT, 1090, 4096};
250+
weak.pow_hash = 4097;
251+
const std::size_t size_before = hc.size();
252+
EXPECT_EQ(hc.validate_and_append(weak), IngestResult::REJECTED);
253+
EXPECT_EQ(hc.size(), size_before);
254+
255+
// Continuity headers remain work-neutral and bypass every PoW gate.
256+
EXPECT_EQ(hc.validate_and_append({SHA256D, 1100, 1}),
243257
IngestResult::ACCEPTED_CONTINUITY);
244258
}
245259

@@ -261,8 +275,8 @@ TEST(HeaderChainValidate, IngestGateRejectsTargetAbovePowLimit)
261275
IngestResult::VALIDATED_SCRYPT);
262276

263277
// A target ABOVE pow_limit (easier than the network minimum) must REJECT
264-
// without mutating the chain -- and it must do so on the ceiling alone,
265-
// before the retarget-equality gate (which here would demand 4096*7/8=3584).
278+
// without mutating the chain -- the ceiling is the sole rejecter here (the
279+
// single-algo retarget-equality gate is demoted to a no-op in V36).
266280
const std::size_t size_before = hc.size();
267281
const uint64_t work_before = hc.cumulative_work();
268282
EXPECT_EQ(hc.validate_and_append({SCRYPT, 1080, 4097}),

0 commit comments

Comments
 (0)