From b4d19127a22dd150dd44be0a2cdaa6807df2c8e3 Mon Sep 17 00:00:00 2001 From: frstrtr Date: Thu, 18 Jun 2026 05:55:44 +0000 Subject: [PATCH 1/2] core: add version_gate v36 SSOT; BTC share.hpp delegates MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce core::version_gate as the single source of truth for the V36 share-format / consensus-revision activation boundary (uniform 36 across all coins). Replace the seven scattered "version >= 36" literals in the BTC share serializer with core::version_gate::is_v36_active(version). Scope: owns the v36 share-format gate only. Segwit activation stays per coin PoolConfig (BTC 33, dgb 35, ltc/bch 17) — explicitly out of scope. BTC is the reference adoption; ltc/dgb/bch conform by mechanical site-rewrite (no value change, byte-identical gate). constexpr predicate serves both if-constexpr template gates and runtime checks. c2pool-btc builds green (self-verify). --- src/core/version_gate.hpp | 50 +++++++++++++++++++++++++++++++++++++++ src/impl/btc/share.hpp | 13 +++++----- 2 files changed, 57 insertions(+), 6 deletions(-) create mode 100644 src/core/version_gate.hpp diff --git a/src/core/version_gate.hpp b/src/core/version_gate.hpp new file mode 100644 index 000000000..ad5f12e6d --- /dev/null +++ b/src/core/version_gate.hpp @@ -0,0 +1,50 @@ +#pragma once +// +// version_gate.hpp — Cross-coin c2pool share-version activation gate (SINGLE SOURCE OF TRUTH). +// +// c2pool's sharechain format is versioned. The V36 format revision is a +// CONSENSUS boundary that is IDENTICAL across every coin c2pool mines +// (btc/ltc/bch/dgb): when a share's VERSION >= 36 the wire layout switches to +// the V36 encoding (VarInt subsidy, AbsworkV36Format, merged_addresses, +// merged_coinbase_info + merged_payout_hash, V36HashLinkType extra_data, +// message_data) and the V36 PPLNS / donation semantics engage. The activation +// number 36 carries no per-coin network state — every coin's params set +// current_share_version = 36 — so it belongs here in core/ rather than being +// re-spelled as a bare `>= 36` literal at each call site. +// +// SCOPE — what this gate does and does NOT own: +// OWNS: the V36 share-format / consensus-revision boundary (uniform 36). +// NOT: segwit activation. That version is coin-SPECIFIC +// (BTC 33, dgb 35, ltc/bch 17) and stays in each coin's PoolConfig +// as SEGWIT_ACTIVATION_VERSION, surfaced via the per-coin +// is_segwit_activated() helper. Do not fold segwit into this gate. +// +// Conformance shape (BTC-first, per v36-standardize goal): +// Every coin replaces its scattered `version >= 36` / `>= 36` literals with +// core::version_gate::is_v36_active(version). Both compile-time +// (`if constexpr (is_v36_active(Tmpl::version))`) and runtime +// (`if (is_v36_active(p.share_version))`) call sites are covered because the +// predicate is constexpr. BTC is the reference adoption; ltc/dgb/bch conform +// by mechanical site-rewrite — no value changes, byte-for-byte identical gate. +// +// Provenance: p2pool check() gates the V36 share rev on self.VERSION >= 36 +// (jtoomim/forrestv). The number is shared across the c2pool multi-coin tree. +// + +#include + +namespace core::version_gate +{ + +// V36 share-format / consensus-revision activation version. Uniform cross-coin. +inline constexpr uint64_t V36_ACTIVATION_VERSION = 36; + +// True iff a share of the given VERSION uses the V36 sharechain encoding and +// V36 consensus semantics. constexpr so it serves both `if constexpr` template +// gates and runtime checks. +constexpr bool is_v36_active(uint64_t version) +{ + return version >= V36_ACTIVATION_VERSION; +} + +} // namespace core::version_gate diff --git a/src/impl/btc/share.hpp b/src/impl/btc/share.hpp index 2b4fcb953..c536dcde0 100644 --- a/src/impl/btc/share.hpp +++ b/src/impl/btc/share.hpp @@ -2,6 +2,7 @@ #include "coin/block.hpp" #include "share_types.hpp" +#include // SSOT: core::version_gate::is_v36_active #include #include @@ -159,7 +160,7 @@ struct Formatter ); // Address handling — version-dependent - if constexpr (version >= 36) + if constexpr (core::version_gate::is_v36_active(version)) { READWRITE(obj->m_pubkey_hash); // IntType(160) READWRITE(obj->m_pubkey_type); // IntType(8) @@ -174,7 +175,7 @@ struct Formatter } // Subsidy — V36 uses VarInt, others use fixed uint64 - if constexpr (version >= 36) + if constexpr (core::version_gate::is_v36_active(version)) { READWRITE(VarInt(obj->m_subsidy)); } @@ -195,7 +196,7 @@ struct Formatter } // V36: merged_addresses (after segwit_data, before far_share_hash) - if constexpr (version >= 36) + if constexpr (core::version_gate::is_v36_active(version)) { READWRITE(obj->m_merged_addresses); } @@ -214,7 +215,7 @@ struct Formatter ); // Abswork — V36 uses VarInt-encoded uint64, others use fixed uint128 - if constexpr (version >= 36) + if constexpr (core::version_gate::is_v36_active(version)) { READWRITE(Using(obj->m_abswork)); } @@ -224,7 +225,7 @@ struct Formatter } // V36: merged_coinbase_info + merged_payout_hash (after abswork) - if constexpr (version >= 36) + if constexpr (core::version_gate::is_v36_active(version)) { READWRITE(obj->m_merged_coinbase_info); READWRITE(obj->m_merged_payout_hash); @@ -244,7 +245,7 @@ struct Formatter ); // V36: message_data (at the end) - if constexpr (version >= 36) + if constexpr (core::version_gate::is_v36_active(version)) { READWRITE(obj->m_message_data); } From ebfb960962952cb527956ffe30d38a1c2a52e4bc Mon Sep 17 00:00:00 2001 From: frstrtr Date: Thu, 18 Jun 2026 06:03:49 +0000 Subject: [PATCH 2/2] btc: adopt core::version_gate SSOT in share_check + share_tracker Mechanical follow-up to b4d19127: replace the remaining bare >= 36 share-version gates with core::version_gate::is_v36_active(). 26 sites in share_check.hpp (both if constexpr and runtime) + 1 in share_tracker.hpp; byte-identical gate, no value change. Comments referencing p2pool provenance left intact. Explicit #include added to both headers. --- src/impl/btc/share_check.hpp | 53 +++++++++++++++++----------------- src/impl/btc/share_tracker.hpp | 3 +- 2 files changed, 29 insertions(+), 27 deletions(-) diff --git a/src/impl/btc/share_check.hpp b/src/impl/btc/share_check.hpp index a48a0771a..4e87ba933 100644 --- a/src/impl/btc/share_check.hpp +++ b/src/impl/btc/share_check.hpp @@ -5,6 +5,7 @@ #include "config_pool.hpp" #include "share.hpp" +#include // SSOT: core::version_gate::is_v36_active #include "share_messages.hpp" #include "share_types.hpp" @@ -374,7 +375,7 @@ inline std::pair compute_ref_hash_for_work(const RefHashParam ref_stream << p.share_nonce; - if (p.share_version >= 36) { + if (core::version_gate::is_v36_active(p.share_version)) { // V36: pubkey_hash (uint160) + pubkey_type (uint8) ref_stream << p.pubkey_hash; ref_stream << p.pubkey_type; @@ -399,7 +400,7 @@ inline std::pair compute_ref_hash_for_work(const RefHashParam ref_stream << p.segwit_data; // V36: merged_addresses (after segwit_data, before far_share_hash) - if (p.share_version >= 36) + if (core::version_gate::is_v36_active(p.share_version)) ref_stream << p.merged_addresses; ref_stream << p.far_share_hash; @@ -408,7 +409,7 @@ inline std::pair compute_ref_hash_for_work(const RefHashParam ref_stream << p.timestamp; ref_stream << p.absheight; - if (p.share_version >= 36) { + if (core::version_gate::is_v36_active(p.share_version)) { ::Serialize(ref_stream, Using(p.abswork)); ref_stream << p.merged_coinbase_info; ref_stream << p.merged_payout_hash; @@ -426,10 +427,10 @@ inline std::pair compute_ref_hash_for_work(const RefHashParam { static int rfn_log = 0; static int rfn_v36_log = 0; - bool should_log = (rfn_log < 3) || (p.share_version >= 36 && rfn_v36_log < 5); + bool should_log = (rfn_log < 3) || (core::version_gate::is_v36_active(p.share_version) && rfn_v36_log < 5); if (should_log) { rfn_log++; - if (p.share_version >= 36) rfn_v36_log++; + if (core::version_gate::is_v36_active(p.share_version)) rfn_v36_log++; static const char* HX = "0123456789abcdef"; std::string hex; auto* rd = reinterpret_cast(ref_stream.data()); @@ -543,7 +544,7 @@ uint256 share_init_verify(const ShareT& share, bool check_pow = true) ref_stream << share.m_pubkey_hash; // subsidy: VarInt for V36+, raw uint64_t LE for older - if constexpr (ver >= 36) + if constexpr (core::version_gate::is_v36_active(ver)) ::Serialize(ref_stream, VarInt(share.m_subsidy)); else ref_stream << share.m_subsidy; @@ -578,7 +579,7 @@ uint256 share_init_verify(const ShareT& share, bool check_pow = true) } // merged_addresses (V36+) - if constexpr (ver >= 36) + if constexpr (core::version_gate::is_v36_active(ver)) { if constexpr (requires { share.m_merged_addresses; }) ref_stream << share.m_merged_addresses; @@ -599,7 +600,7 @@ uint256 share_init_verify(const ShareT& share, bool check_pow = true) ref_stream << share.m_absheight; // abswork: AbsworkV36Format for V36+, raw uint128 LE for older - if constexpr (ver >= 36) + if constexpr (core::version_gate::is_v36_active(ver)) { if constexpr (requires { share.m_abswork; }) ::Serialize(ref_stream, Using(share.m_abswork)); @@ -610,7 +611,7 @@ uint256 share_init_verify(const ShareT& share, bool check_pow = true) } // V36+ merged mining commitment fields - if constexpr (ver >= 36) + if constexpr (core::version_gate::is_v36_active(ver)) { if constexpr (requires { share.m_merged_coinbase_info; }) ref_stream << share.m_merged_coinbase_info; @@ -621,7 +622,7 @@ uint256 share_init_verify(const ShareT& share, bool check_pow = true) // V36 ref_type includes message_data as PossiblyNoneType(b'', VarStrType()) // When m_message_data is empty, BaseScript serialises as varint(0) = 0x00. - if constexpr (ver >= 36) + if constexpr (core::version_gate::is_v36_active(ver)) { if constexpr (requires { share.m_message_data; }) ref_stream << share.m_message_data; @@ -935,7 +936,7 @@ uint256 generate_share_transaction(const ShareT& share, TrackerT& tracker, bool // p2pool selects PPLNS formula by runtime AutoRatchet state, not compile-time // share version. When v36_active is true (AutoRatchet ACTIVATED/CONFIRMED), // use v36 PPLNS even for v35 shares. Ref: p2pool data.py:879, work.py:759. - const bool use_v36_pplns = v36_active || (ver >= 36); + const bool use_v36_pplns = v36_active || (core::version_gate::is_v36_active(ver)); const uint64_t subsidy = share.m_subsidy; const uint16_t donation = share.m_donation; @@ -1268,7 +1269,7 @@ uint256 generate_share_transaction(const ShareT& share, TrackerT& tracker, bool else ref_stream << share.m_pubkey_hash; - if constexpr (ver >= 36) + if constexpr (core::version_gate::is_v36_active(ver)) ::Serialize(ref_stream, VarInt(share.m_subsidy)); else ref_stream << share.m_subsidy; @@ -1297,7 +1298,7 @@ uint256 generate_share_transaction(const ShareT& share, TrackerT& tracker, bool } } - if constexpr (ver >= 36) + if constexpr (core::version_gate::is_v36_active(ver)) { if constexpr (requires { share.m_merged_addresses; }) ref_stream << share.m_merged_addresses; @@ -1315,7 +1316,7 @@ uint256 generate_share_transaction(const ShareT& share, TrackerT& tracker, bool ref_stream << share.m_timestamp; ref_stream << share.m_absheight; - if constexpr (ver >= 36) + if constexpr (core::version_gate::is_v36_active(ver)) { if constexpr (requires { share.m_abswork; }) ::Serialize(ref_stream, Using(share.m_abswork)); @@ -1325,7 +1326,7 @@ uint256 generate_share_transaction(const ShareT& share, TrackerT& tracker, bool ref_stream << share.m_abswork; } - if constexpr (ver >= 36) + if constexpr (core::version_gate::is_v36_active(ver)) { if constexpr (requires { share.m_merged_coinbase_info; }) ref_stream << share.m_merged_coinbase_info; @@ -1335,7 +1336,7 @@ uint256 generate_share_transaction(const ShareT& share, TrackerT& tracker, bool } // V36 ref_type includes message_data (must match verify_share) - if constexpr (ver >= 36) + if constexpr (core::version_gate::is_v36_active(ver)) { if constexpr (requires { share.m_message_data; }) ref_stream << share.m_message_data; @@ -1737,7 +1738,7 @@ bool share_check(const ShareT& share, // This ensures V35 shares always verify with V35 PPLNS formula, even after // the AutoRatchet transitions to ACTIVATED. constexpr int64_t share_ver = ShareT::version; - bool v36_active = (share_ver >= 36); + bool v36_active = (core::version_gate::is_v36_active(share_ver)); if (!share.m_prev_hash.IsNull() && tracker.chain.contains(share.m_prev_hash)) { uint256 expected_gentx = generate_share_transaction(share, tracker, false, v36_active); @@ -1861,7 +1862,7 @@ bool share_check(const ShareT& share, // independently compute from the share chain. Without this, a malicious // node could steal all merged chain (DOGE) rewards while appearing honest // on the parent chain (LTC payouts are consensus-enforced via gentx above). - if constexpr (ShareT::version >= 36) + if constexpr (core::version_gate::is_v36_active(ShareT::version)) { if constexpr (requires { share.m_merged_payout_hash; }) { @@ -1889,7 +1890,7 @@ bool share_check(const ShareT& share, // 5. V36+ merged coinbase commitment verification (7-step chain) // Verifies the actual merged coinbase matches canonical PPLNS construction. - if constexpr (ShareT::version >= 36) + if constexpr (core::version_gate::is_v36_active(ShareT::version)) { auto mcv_err = verify_merged_coinbase_commitment(share, tracker); if (!mcv_err.empty()) @@ -1959,7 +1960,7 @@ uint256 verify_share(const ShareT& share, TrackerT& tracker) else ref_stream << share.m_pubkey_hash; - if constexpr (ver >= 36) + if constexpr (core::version_gate::is_v36_active(ver)) ::Serialize(ref_stream, VarInt(share.m_subsidy)); else ref_stream << share.m_subsidy; @@ -1987,7 +1988,7 @@ uint256 verify_share(const ShareT& share, TrackerT& tracker) } } - if constexpr (ver >= 36) + if constexpr (core::version_gate::is_v36_active(ver)) { if constexpr (requires { share.m_merged_addresses; }) ref_stream << share.m_merged_addresses; @@ -2005,7 +2006,7 @@ uint256 verify_share(const ShareT& share, TrackerT& tracker) ref_stream << share.m_timestamp; ref_stream << share.m_absheight; - if constexpr (ver >= 36) + if constexpr (core::version_gate::is_v36_active(ver)) { if constexpr (requires { share.m_abswork; }) ::Serialize(ref_stream, Using(share.m_abswork)); @@ -2015,7 +2016,7 @@ uint256 verify_share(const ShareT& share, TrackerT& tracker) ref_stream << share.m_abswork; } - if constexpr (ver >= 36) + if constexpr (core::version_gate::is_v36_active(ver)) { if constexpr (requires { share.m_merged_coinbase_info; }) ref_stream << share.m_merged_coinbase_info; @@ -2025,7 +2026,7 @@ uint256 verify_share(const ShareT& share, TrackerT& tracker) } // V36 ref_type includes message_data - if constexpr (ver >= 36) + if constexpr (core::version_gate::is_v36_active(ver)) { if constexpr (requires { share.m_message_data; }) ref_stream << share.m_message_data; @@ -2050,7 +2051,7 @@ uint256 verify_share(const ShareT& share, TrackerT& tracker) uint256 gentx_hash = check_hash_link(share.m_hash_link, hash_link_data, gentx_before_refhash); // V36+: Validate message_data (reject shares with invalid encrypted messages) - if constexpr (ver >= 36) + if constexpr (core::version_gate::is_v36_active(ver)) { if constexpr (requires { share.m_message_data; }) { @@ -3252,7 +3253,7 @@ uint256 create_local_share( { static int xcheck_count = 0; if (true) { // Always cross-check (was: xcheck_count < 5) - uint256 verify_hash = generate_share_transaction(*heap_share, tracker, true, (MergedMiningShare::version >= 36)); + uint256 verify_hash = generate_share_transaction(*heap_share, tracker, true, (core::version_gate::is_v36_active(MergedMiningShare::version))); bool xcheck_ok = (verify_hash == gentx_hash_for_header); if (xcheck_ok) { LOG_INFO << "[Pool] Cross-check PASSED"; diff --git a/src/impl/btc/share_tracker.hpp b/src/impl/btc/share_tracker.hpp index 82901f9a6..b6e4edd71 100644 --- a/src/impl/btc/share_tracker.hpp +++ b/src/impl/btc/share_tracker.hpp @@ -19,6 +19,7 @@ inline uint64_t mul128_shift(uint64_t a, uint64_t b, unsigned shift) { #endif #include "share.hpp" +#include // SSOT: core::version_gate::is_v36_active #include "share_check.hpp" #include "config_pool.hpp" @@ -975,7 +976,7 @@ class ShareTracker prev_hash = obj->m_prev_hash; share_ver = obj->version; }); - if (share_ver >= 36) { + if (core::version_gate::is_v36_active(share_ver)) { if (!prev_hash.IsNull() && chain.contains(prev_hash)) { if (!pplns_active) {