Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions src/core/web_server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6158,9 +6158,22 @@ nlohmann::json MiningInterface::rest_v36_status()
};

int sample_size = vs.value("overall_total", 0);
int v36_shares = vs.value("overall_v36_shares", 0);
// Crossing counter must reflect the DESIRED-version tally the AutoRatchet
// keys on (get_desired_version_weights), NOT the per-share FORMAT flag.
// p2p-received shares relayed from the V35 seed carry format V35 even while
// their miners are voting V36, so overall_v36_shares (a format-flag count)
// stays 0 straight through a real crossing -- the founding-charter class of
// lie, here in the very fields the operator curls to watch the ratchet.
// Re-source from the desired-version data (the same swap #288 made for the
// ratchet mint gate): the headline percentage is the work-weighted sampling
// signal -- the exact "N% old version" figure the journal logs and the
// integrator reads to fire the crossing-event ping -- and the counts are the
// flat desired-version vote counts over the chain. Both climb with the cross;
// neither is the format-flag stub. (overall_v36_shares stays available on the
// parent version_signaling object for anyone who wants the format breakdown.)
int v36_shares = vs.value("overall_v36_votes", 0);
int v35_shares = std::max(0, sample_size - v36_shares);
double v36_pct = vs.value("overall_v36_share_pct", 0.0);
double v36_pct = vs.value("sampling_signaling", 0.0);

int height = 0;
if (m_sharechain_stats_fn) {
Expand Down
48 changes: 48 additions & 0 deletions test/test_web_honesty_regression.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,54 @@ TEST(WebHonestyRegression, V36StatusActiveLatchTracksLiveShareVersion) {
<< "v36_active must latch true once the live ratchet reaches V36";
}

// (c) v36_status.share_chain.{v35_shares,v36_shares,v36_percentage} must reflect
// the DESIRED-version tally the AutoRatchet keys on (get_desired_version_weights),
// NOT the per-share FORMAT flag. During the graded g2 ratchet the .157 seed relays
// V35-FORMAT shares over p2p even while their miners vote V36, so a format-flag
// count reads 0 v36 straight through a real crossing -- the founding 0-stub class
// of lie, in the exact fields the operator curls to watch the vote climb. This pins
// the #288-style swap: build a sharechain-stats snapshot whose FORMAT is 100% V35
// yet whose DESIRED-version tally is ~33% V36 (one rig flipped), and assert the
// crossing counter tracks the vote, never the frozen format zero.
TEST(WebHonestyRegression, V36StatusShareChainTracksDesiredVersionNotFormatFlag) {
MiningInterface mi(/*testnet=*/true, /*node=*/nullptr, Blockchain::LITECOIN);

// Snapshot mirroring rig #1 (.37) flipped to V36 while .38/.39 stay V35 and the
// seed relays V35-format shares: FORMAT all V35, DESIRED-version ~1/3 V36, and a
// work-weighted sampling signal of ~33.33%.
mi.set_sharechain_stats_fn([]() {
nlohmann::json sc = nlohmann::json::object();
sc["total_shares"] = 400;
sc["chain_height"] = 400;
sc["chain_length"] = 400;
sc["shares_by_version"] = {{"35", 400}}; // FORMAT: 100% V35
sc["shares_by_desired_version"] = {{"35", 268}, {"36", 132}}; // VOTE: 132/400 = 33%
sc["sampling_desired_version"] = {{"35", 2.0}, {"36", 1.0}}; // work-weighted: 33.33%
return sc;
});

nlohmann::json v = mi.rest_v36_status();
ASSERT_TRUE(v.contains("share_chain"));
const auto& scj = v["share_chain"];

// The format-flag count IS zero here -- that is the pre-fix stub value the
// crossing counter used to echo. Pin it on the parent version_signaling object
// (its true source) so a refactor that reverts share_chain to the format tally
// is caught: the bug is real precisely because format says 0 while vote says 33.
nlohmann::json sig = mi.rest_version_signaling();
EXPECT_EQ(sig.value("overall_v36_shares", -1), 0)
<< "guard: this snapshot is the crossing where FORMAT is still 100% V35";

EXPECT_EQ(scj["v36_shares"].get<int>(), 132)
<< "v36_shares must be the desired-version vote count (132), not the "
"format-flag count (0) that lies through the crossing";
EXPECT_EQ(scj["v35_shares"].get<int>(), 268)
<< "v35_shares is the desired-version remainder (400-132), not 400";
EXPECT_NEAR(scj["v36_percentage"].get<double>(), 33.33, 0.01)
<< "v36_percentage must be the work-weighted sampling signal (the journal "
"N% old version complement the integrator reads), never 0";
}

// --- charter #3: crossing-banner coin coverage (de-allowlist, #496) ----------
// The V35->V36 crossing banner must surface on EVERY v36-ratcheting coin, not
// just LTC/DOGE. Before #496 a hardcoded {LITECOIN,DOGECOIN} allowlist gated
Expand Down
Loading