diff --git a/src/impl/btc/auto_ratchet.hpp b/src/impl/btc/auto_ratchet.hpp index fb33ec564..b1bd718eb 100644 --- a/src/impl/btc/auto_ratchet.hpp +++ b/src/impl/btc/auto_ratchet.hpp @@ -166,8 +166,16 @@ class AutoRatchet if (static_cast(ver) >= target_version_) tail_target = tail_target + w; } - // Canonical: counts.get(VERSION,0) < sum(counts)*60//100 - bool tail_ok = !(tail_target * uint32_t(100) < tail_total * uint32_t(SWITCH_THRESHOLD)); + // Canonical FLOOR (p2pool data.py share-acceptance): + // valid iff target >= floor(total * SWITCH_THRESHOLD / 100) + // i.e. counts.get(VERSION,0) >= sum(counts)*60//100. + // The prior cross-multiplied form (target*100 >= total*60) is the + // algebraic CEIL: at a non-integral boundary (total*60 % 100 != 0) + // it latched up to one work-unit LATER than the network oracle. + // Adopting FLOOR removes that c2pool-only late-latch divergence so + // our accept gate is byte-identical to every p2pool peer's. + bool tail_ok = !(tail_target < + (tail_total * uint32_t(SWITCH_THRESHOLD)) / uint32_t(100)); if (!tail_ok) { static int tail_log = 0; diff --git a/src/impl/btc/test/auto_ratchet_sim_test.cpp b/src/impl/btc/test/auto_ratchet_sim_test.cpp index 1d5530206..198d66f2d 100644 --- a/src/impl/btc/test/auto_ratchet_sim_test.cpp +++ b/src/impl/btc/test/auto_ratchet_sim_test.cpp @@ -34,14 +34,15 @@ using btc::RatchetState; namespace { // Verbatim replica of the LIVE inline tail-guard in -// AutoRatchet::get_share_version: tail_ok = !(target*100 < total*SWITCH). +// AutoRatchet::get_share_version: tail_ok = !(target < total*SWITCH/100). // Localises the 60%-by-WORK accept gate so C3 pins it WITHOUT driving the -// consensus path or importing a lifted SSOT. +// consensus path or importing a lifted SSOT. FLOOR form, tracking the SSOT +// step-2 adoption (was the algebraic CEIL target*100 < total*SWITCH). bool inline_tail_ok(const uint288& target, const uint288& total, int thr = AutoRatchet::SWITCH_THRESHOLD) { - return !((target * static_cast(100)) < - (total * static_cast(thr))); + return !(target < (total * static_cast(thr)) + / static_cast(100)); } // 95%-by-COUNT activation predicate (flat head-count), mirroring the VOTING -> @@ -193,3 +194,43 @@ TEST(BTC_AutoRatchetSim, C4_StatePersistsAcrossRestart) EXPECT_EQ(vote, 36); std::remove(path.c_str()); } + +// --------------------------------------------------------------------------- +// SSOT step-2 / #1 standardization target — ORACLE FLOOR vs INLINE CEIL. +// +// p2pool oracle (data.py share-acceptance) valid threshold is the FLOOR form: +// counts.get(VERSION,0) >= sum(counts)*60//100 # floor +// The BTC inline tail-guard (auto_ratchet.hpp, replicated as inline_tail_ok +// above) has ADOPTED that FLOOR form. Previously it was the algebraic CEIL +// target*100 >= total*60 <=> target >= ceil(total*60/100), +// which at a non-integral boundary (total*60 % 100 != 0) latched up to one +// share LATER than the oracle. This KAT now PINS the CONVERGED state: at the +// same non-integral boundary (total=101 => total*60 = 6060, 6060 % 100 != 0) +// floor(6060/100) = 60 (oracle : target=60 PASSES) +// inline (FLOOR) = 60 (adopted : target=60 PASSES) <-- seam CLOSED +// Pre-adoption this line read EXPECT_FALSE on the inline form (CEIL held until +// 61); the SSOT floor adoption (operator-tap gated) flips it to EXPECT_TRUE. +// The integral control (total=100) still shows both forms AGREE on the exact +// boundary, so the only behavioural change is the %100 remainder share, now +// accepted one unit earlier in lock-step with the network. Consensus surface +// equals the p2pool oracle EXACTLY. +// --------------------------------------------------------------------------- +TEST(BTC_AutoRatchetSim, SSOT2_OracleFloorVsInlineCeilBoundary) +{ + // Oracle floor predicate: target >= floor(total*60/100). + auto oracle_floor_ok = [](const uint288& target, const uint288& total) { + return !(target < ((total * static_cast(60)) + / static_cast(100))); + }; + + // Integral-boundary control (total=100): floor==ceil==60, forms AGREE. + EXPECT_EQ(oracle_floor_ok(uint288(60), uint288(100)), + inline_tail_ok (uint288(60), uint288(100))); + + const uint288 total(101); // 101*60 = 6060, %100 = 60 + EXPECT_TRUE (oracle_floor_ok(uint288(60), total)); // oracle: latches at 60 + EXPECT_TRUE (inline_tail_ok (uint288(60), total)); // inline FLOOR: latches at 60 too + // Forms are now identical at every point (boundary and beyond). + EXPECT_TRUE (oracle_floor_ok(uint288(61), total)); + EXPECT_TRUE (inline_tail_ok (uint288(61), total)); +}