@@ -244,3 +244,75 @@ TEST(WebHonestyRegression, StratumSecuritySignalsNotInstrumentedNotFakeGreen) {
244244 EXPECT_FALSE (r.contains (" blacklisted_ips" ));
245245 EXPECT_FALSE (r.contains (" threat_level" ));
246246}
247+
248+ // --- charter #3: ratchet / crossing-state honesty ----------------------------
249+ // The dashboard must tell the truth about the V35->V36 cross. Two founding-class
250+ // lies are pinned here:
251+ // (a) currency_info.share_version was hardcoded 36 -- a node still VOTING
252+ // (producing V35 shares) would report 36, making the bundled sharechain-
253+ // explorer misclassify live V35 share cells as V36 (#491).
254+ // (b) v36_status.auto_ratchet.v36_active was a frozen stub -- it must derive
255+ // from the LIVE ratchet latch (m_cached_share_version) so a node that has
256+ // latched to V36 cannot be shown as still "voting", nor vice-versa (#499).
257+ // Both are the same class of lie as the founding 0-stubs: the dashboard claiming
258+ // one thing while the node is actually doing another, during the very crossing
259+ // the operator is coordinating live on LTC prod.
260+
261+ // (a) currency_info.share_version reflects the LIVE ratchet output, never a
262+ // static 36: a VOTING LTC node still mining V35 must report 35.
263+ TEST (WebHonestyRegression, CurrencyInfoShareVersionIsLiveRatchetNotStatic36) {
264+ MiningInterface mi (/* testnet=*/ true , /* node=*/ nullptr , Blockchain::LITECOIN );
265+
266+ mi.set_cached_share_version (35 ); // node still VOTING -- producing V35 shares
267+ json r = mi.rest_web_currency_info ();
268+ EXPECT_EQ (r[" share_version" ].get <int64_t >(), 35 )
269+ << " share_version must track m_cached_share_version (live ratchet), never "
270+ " a hardcoded 36 -- reporting 36 while VOTING lies about the cross" ;
271+ }
272+
273+ // currency_info.share_version follows the latch forward once the node crosses.
274+ TEST (WebHonestyRegression, CurrencyInfoShareVersionFollowsLatchToV36) {
275+ MiningInterface mi (/* testnet=*/ true , /* node=*/ nullptr , Blockchain::LITECOIN );
276+
277+ mi.set_cached_share_version (36 ); // node has latched to V36
278+ json r = mi.rest_web_currency_info ();
279+ EXPECT_EQ (r[" share_version" ].get <int64_t >(), 36 )
280+ << " share_version must follow the live latch to 36 once crossed" ;
281+ }
282+
283+ // DASH is non-ratcheting on the LTC AutoRatchet path: its share_version stays
284+ // the static protocol v16 even if the cached ratchet value is something else --
285+ // the LTC ratchet cache must never bleed into a Dash dashboard.
286+ TEST (WebHonestyRegression, CurrencyInfoDashShareVersionStaysStatic16) {
287+ MiningInterface mi (/* testnet=*/ true , /* node=*/ nullptr , Blockchain::DASH );
288+
289+ mi.set_cached_share_version (35 ); // would be a lie to surface on Dash
290+ json r = mi.rest_web_currency_info ();
291+ EXPECT_EQ (r[" share_version" ].get <int64_t >(), 16 )
292+ << " Dash share_version is the static protocol v16, not the LTC ratchet cache" ;
293+ }
294+
295+ // (b) v36_status.auto_ratchet.v36_active is derived from the LIVE latch
296+ // (m_cached_share_version >= 36), not a frozen stub. With no sharechain stats
297+ // wired, version_signaling returns {} and the chain-derived branch is skipped --
298+ // isolating the latch derivation, which is exactly what #499 surfaces as ground
299+ // truth so a transient sampling dip cannot misreport the crossing state.
300+ TEST (WebHonestyRegression, V36StatusActiveLatchTracksLiveShareVersion) {
301+ MiningInterface mi (/* testnet=*/ true , /* node=*/ nullptr , Blockchain::LITECOIN );
302+
303+ // Pre-cross: still on V35.
304+ mi.set_cached_share_version (35 );
305+ json voting = mi.rest_v36_status ();
306+ ASSERT_TRUE (voting.contains (" auto_ratchet" ));
307+ EXPECT_EQ (voting[" auto_ratchet" ][" live_share_version" ].get <int64_t >(), 35 );
308+ EXPECT_FALSE (voting[" auto_ratchet" ][" v36_active" ].get <bool >())
309+ << " v36_active must be false while the live latch is still V35" ;
310+
311+ // Post-cross: latched to V36.
312+ mi.set_cached_share_version (36 );
313+ json active = mi.rest_v36_status ();
314+ ASSERT_TRUE (active.contains (" auto_ratchet" ));
315+ EXPECT_EQ (active[" auto_ratchet" ][" live_share_version" ].get <int64_t >(), 36 );
316+ EXPECT_TRUE (active[" auto_ratchet" ][" v36_active" ].get <bool >())
317+ << " v36_active must latch true once the live ratchet reaches V36" ;
318+ }
0 commit comments