Skip to content

Commit d53b547

Browse files
authored
Merge pull request #643 from frstrtr/dash/357-auto-ratchet-mint-gate
dash(#357): v36-native mint-side accept-floor auto-ratchet (FENCED/UNWIRED)
2 parents e49dbe4 + 37f1690 commit d53b547

4 files changed

Lines changed: 332 additions & 2 deletions

File tree

.github/workflows/build.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ jobs:
102102
core_test sharechain_test share_test btc_share_test \
103103
test_threading test_weights \
104104
test_header_chain test_mempool test_template_builder \
105-
test_doge_chain test_compact_blocks test_dash_x11_kat test_dash_header_chain test_dash_block_replay test_dash_conformance test_dash_subsidy test_dash_mempool test_dash_simplifiedmns test_dash_quorum test_dash_quorum_root test_dash_mn_state test_dash_embedded_gbt test_dash_smldiff test_dash_p2p_messages test_dash_p2p_connection test_dash_p2p_node test_dash_node_interface test_dash_config test_dash_broadcaster test_dash_broadcaster_full test_dash_share_hash_link test_dash_block_relay test_dash_rpc_request test_dash_rpc_conf test_dash_block_producer test_dash_embedded_relay_e2e test_dash_block_relay_plan test_dash_version_activation_latch test_dash_block_relay_binding test_dash_block_relay_dual_arm test_dash_coinbase_parity test_dash_donation_combined test_dash_g3_assembled test_dash_work_target test_dash_work_job_targets test_dash_stratum_binding test_dash_job_notify_roundtrip test_dash_cb_payee test_dash_stratum_notify_roundtrip test_dash_poolnode_messages test_dash_peer test_dash_share_tracker test_dash_node test_dash_share_messages test_dash_stratum_extranonce_split test_dash_stratum_submit_reassembly test_dash_difficulty_parity \
105+
test_doge_chain test_compact_blocks test_dash_x11_kat test_dash_header_chain test_dash_block_replay test_dash_conformance test_dash_subsidy test_dash_mempool test_dash_simplifiedmns test_dash_quorum test_dash_quorum_root test_dash_mn_state test_dash_embedded_gbt test_dash_smldiff test_dash_p2p_messages test_dash_p2p_connection test_dash_p2p_node test_dash_node_interface test_dash_config test_dash_broadcaster test_dash_broadcaster_full test_dash_share_hash_link test_dash_block_relay test_dash_rpc_request test_dash_rpc_conf test_dash_block_producer test_dash_embedded_relay_e2e test_dash_block_relay_plan test_dash_version_activation_latch test_dash_block_relay_binding test_dash_block_relay_dual_arm test_dash_coinbase_parity test_dash_donation_combined test_dash_g3_assembled test_dash_work_target test_dash_work_job_targets test_dash_stratum_binding test_dash_job_notify_roundtrip test_dash_cb_payee test_dash_stratum_notify_roundtrip test_dash_poolnode_messages test_dash_peer test_dash_share_tracker test_dash_node test_dash_share_messages test_dash_stratum_extranonce_split test_dash_stratum_submit_reassembly test_dash_difficulty_parity test_dash_auto_ratchet \
106106
test_multiaddress_pplns test_pplns_stress \
107107
test_hash_link test_decay_pplns \
108108
test_pplns_consensus \
@@ -252,7 +252,7 @@ jobs:
252252
test_threading test_weights \
253253
test_header_chain test_mempool test_template_builder \
254254
test_doge_chain test_compact_blocks test_dash_x11_kat \
255-
test_dash_header_chain test_dash_block_replay test_dash_conformance test_dash_subsidy test_dash_mempool test_dash_simplifiedmns test_dash_quorum test_dash_quorum_root test_dash_mn_state test_dash_embedded_gbt test_dash_smldiff test_dash_p2p_messages test_dash_p2p_connection test_dash_p2p_node test_dash_node_interface test_dash_config test_dash_broadcaster test_dash_broadcaster_full test_dash_share_hash_link test_dash_block_relay test_dash_rpc_request test_dash_rpc_conf test_dash_block_producer test_dash_embedded_relay_e2e test_dash_block_relay_plan test_dash_version_activation_latch test_dash_block_relay_binding test_dash_block_relay_dual_arm test_dash_coinbase_parity test_dash_donation_combined test_dash_g3_assembled test_dash_work_target test_dash_work_job_targets test_dash_stratum_binding test_dash_job_notify_roundtrip test_dash_cb_payee test_dash_stratum_notify_roundtrip test_dash_poolnode_messages test_dash_peer test_dash_share_tracker test_dash_node test_dash_share_messages test_dash_stratum_extranonce_split test_dash_stratum_submit_reassembly test_dash_difficulty_parity \
255+
test_dash_header_chain test_dash_block_replay test_dash_conformance test_dash_subsidy test_dash_mempool test_dash_simplifiedmns test_dash_quorum test_dash_quorum_root test_dash_mn_state test_dash_embedded_gbt test_dash_smldiff test_dash_p2p_messages test_dash_p2p_connection test_dash_p2p_node test_dash_node_interface test_dash_config test_dash_broadcaster test_dash_broadcaster_full test_dash_share_hash_link test_dash_block_relay test_dash_rpc_request test_dash_rpc_conf test_dash_block_producer test_dash_embedded_relay_e2e test_dash_block_relay_plan test_dash_version_activation_latch test_dash_block_relay_binding test_dash_block_relay_dual_arm test_dash_coinbase_parity test_dash_donation_combined test_dash_g3_assembled test_dash_work_target test_dash_work_job_targets test_dash_stratum_binding test_dash_job_notify_roundtrip test_dash_cb_payee test_dash_stratum_notify_roundtrip test_dash_poolnode_messages test_dash_peer test_dash_share_tracker test_dash_node test_dash_share_messages test_dash_stratum_extranonce_split test_dash_stratum_submit_reassembly test_dash_difficulty_parity test_dash_auto_ratchet \
256256
test_hash_link test_decay_pplns \
257257
test_pplns_consensus \
258258
test_v36_script_sorting test_v36_cross_impl_refhash \

src/impl/dash/auto_ratchet.hpp

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
#pragma once
2+
3+
// DASH v36-native MINT-side auto-ratchet of the P2P accept floor.
4+
//
5+
// FENCED / ADDITIVE / UNWIRED. This header carries NO src/ call site — the live
6+
// consumer (node.cpp, analogous to dgb::NodeImpl min-proto-ratchet) rides the S8
7+
// pool-node wire-up and is consensus-bearing, so it is surfaced separately for an
8+
// integrator merge tap. Landing the skeleton here de-risks that wire-up and closes
9+
// the single gap the #357 AutoRatchet mint-gate weighting audit found: DASH already
10+
// defines the WEIGHTED desired-version tally (version_negotiation.hpp
11+
// get_desired_version_weights) and the 60%/95% accept gates consume it, but there
12+
// was NO mint-side consumer that lifts MINIMUM_PROTOCOL_VERSION off the same window.
13+
//
14+
// v36-STANDARDIZATION (operator 3-bucket rule, bucket 2 = v36-NATIVE SHARED
15+
// STRUCTURE): the auto-ratchet is standardized cross-coin toward the v37 shape. It
16+
// mirrors dgb::apply_min_protocol_ratchet_decision byte-for-byte in decision logic;
17+
// only the floor constants differ per coin (the ISOLATION invariant stays per-coin).
18+
//
19+
// ORACLE NOTE (honest, surfaced to integrator): the DASH oracle frstrtr/p2pool-dash
20+
// (older-than-v35) has NO update_min_protocol_version and NO NEW_MINIMUM_PROTOCOL_VERSION
21+
// — MINIMUM_PROTOCOL_VERSION is the static 1700 net constant (dash.py:23). The
22+
// dgb-scrypt oracle DOES ratchet (data.py:715 newminpver = NEW_MINIMUM_PROTOCOL_VERSION,
23+
// 1400 -> 3500). So the DASH ratchet TARGET floor is a c2pool v36-native choice, not
24+
// an oracle-derived number. The decision MATH below is oracle-faithful to the
25+
// dgb-scrypt update_min_protocol_version (floor-divided 95% work-weighted gate); the
26+
// concrete target value is a wire-up-time decision and is intentionally NOT baked in
27+
// here — every function takes current_floor / target_floor as parameters.
28+
29+
#include <core/uint256.hpp> // uint288
30+
31+
#include <cstdint>
32+
#include <map>
33+
#include <vector>
34+
35+
namespace dash {
36+
37+
// A single share's contribution to a desired-version tally: the version it desires
38+
// and its work weight (target_to_average_attempts of its target, == ShareIndex::work,
39+
// the same metric get_desired_version_weights caches -- version_negotiation.hpp D5).
40+
struct VersionWork {
41+
int64_t desired_version;
42+
uint288 work;
43+
};
44+
45+
// WORK-WEIGHTED accumulation: sum each version's work over the window. This is the
46+
// map get_desired_version_weights produces from a real chain window; exposed here so
47+
// the KAT can exercise the exact weighting divergence without a live ShareChain.
48+
inline std::map<uint64_t, uint288>
49+
accumulate_version_weights(const std::vector<VersionWork>& window)
50+
{
51+
std::map<uint64_t, uint288> res;
52+
for (const auto& vw : window)
53+
res[static_cast<uint64_t>(vw.desired_version)] += vw.work;
54+
return res;
55+
}
56+
57+
// FLAT (plain-count) accumulation: one vote per share, work ignored. This is the
58+
// count map the ratchet must NOT consume -- it exists only to PROVE, in the KAT,
59+
// that the weighted decision diverges from the plain-count decision on a crafted
60+
// window (matching the btc/dgb work-weighted-not-flat-count divergence assert).
61+
inline std::map<uint64_t, uint288>
62+
accumulate_version_counts(const std::vector<VersionWork>& window)
63+
{
64+
std::map<uint64_t, uint288> res;
65+
for (const auto& vw : window)
66+
res[static_cast<uint64_t>(vw.desired_version)] += uint288(1);
67+
return res;
68+
}
69+
70+
// Pure ratchet -- mirrors the dgb-scrypt oracle update_min_protocol_version dict
71+
// branch (data.py:715-719):
72+
// minpver = getattr(share.net, 'MINIMUM_PROTOCOL_VERSION', 1700)
73+
// newminpver = getattr(share.net, 'NEW_MINIMUM_PROTOCOL_VERSION', minpver)
74+
// if (counts is not None) and (minpver < newminpver):
75+
// if counts.get(share.VERSION, 0) >= sum(counts.itervalues())*95//100:
76+
// share.net.MINIMUM_PROTOCOL_VERSION = newminpver
77+
//
78+
// counts is the WORK-WEIGHTED map (get_desired_version_weights), keyed by
79+
// desired_version and looked up by the best share's VERSION. Pure: no I/O, inputs
80+
// unmutated; returns the (possibly lifted) floor.
81+
//
82+
// Integer math is the oracle's exactly: ratchet iff
83+
// best_weight >= floor(total_weight * 95 / 100).
84+
// We floor-divide (total*95)/100 rather than the cross-multiplied
85+
// best*100 >= total*95 idiom -- the two differ at the boundary when
86+
// (total*95) % 100 != 0 (floor-div accepts best == floor(...), cross-mult rejects),
87+
// and the oracle floor-divides. uint288 throughout: no IEEE-double, no overflow at
88+
// consensus (2^256-scale) weights.
89+
inline uint32_t ratchet_min_protocol_version(
90+
const std::map<uint64_t, uint288>& version_weights,
91+
int64_t best_version,
92+
uint32_t current_floor,
93+
uint32_t target_floor)
94+
{
95+
if (current_floor >= target_floor) // oracle: minpver < newminpver guard
96+
return current_floor;
97+
98+
uint288 best_weight;
99+
uint288 total_weight;
100+
for (const auto& [ver, w] : version_weights) {
101+
total_weight = total_weight + w;
102+
if (static_cast<int64_t>(ver) == best_version)
103+
best_weight = best_weight + w;
104+
}
105+
// oracle: counts.get(share.VERSION, 0) >= sum(counts.itervalues())*95//100
106+
if (best_weight >= (total_weight * uint32_t(95)) / uint32_t(100))
107+
return target_floor;
108+
return current_floor;
109+
}
110+
111+
// Runtime WIRING decision -- the call-site guard the pure ratchet deliberately omits.
112+
// ratchet_min_protocol_version mirrors ONLY the oracle dict-branch: over an empty or
113+
// partial window it ratchets (0 >= 0), because the oracle's full-window guard lives
114+
// at the CALL SITE (main.py: only update when len(shares) > CHAIN_LENGTH, sampling
115+
// get_desired_version over [nth_parent(prev, CHAIN_LENGTH*9/10), CHAIN_LENGTH/10] --
116+
// the SAME window the 60% version-switch gate reads, version_negotiation.hpp). Without
117+
// this guard a fresh node (parent_height < CHAIN_LENGTH -> partial window) would
118+
// spuriously lift the floor and reject every legitimate peer. This function is that
119+
// guard: no lift until a FULL window exists behind the best share's parent, then
120+
// delegate to the pure ratchet over the window the caller sampled.
121+
inline uint32_t apply_min_protocol_ratchet_decision(
122+
int32_t parent_height,
123+
int32_t chain_length,
124+
const std::map<uint64_t, uint288>& window_weights,
125+
int64_t best_version,
126+
uint32_t current_floor,
127+
uint32_t target_floor)
128+
{
129+
if (current_floor >= target_floor) // oracle guard: minpver < newminpver
130+
return current_floor;
131+
if (parent_height < chain_length) // oracle: len(shares) > CHAIN_LENGTH
132+
return current_floor; // no full window yet -> never lift
133+
return ratchet_min_protocol_version(
134+
window_weights, best_version, current_floor, target_floor);
135+
}
136+
137+
} // namespace dash

test/CMakeLists.txt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -572,6 +572,21 @@ if (BUILD_TESTING AND GTest_FOUND)
572572
target_link_libraries(test_dash_config PRIVATE c2pool_payout c2pool_hashrate c2pool_merged_mining) # OBJECT-lib SCC direct-naming (#22/#39)
573573
gtest_add_tests(test_dash_config "" AUTO)
574574

575+
# DASH v36-native MINT-side accept-floor auto-ratchet (#357 gap-closer, FENCED/
576+
# UNWIRED). Pins the pure dash::ratchet_min_protocol_version + the full-window
577+
# apply_min_protocol_ratchet_decision guard, and the weighted-vs-plain-count
578+
# divergence assert. Header-only over core (uint288); mirrors the test_dash_config
579+
# link set.
580+
add_executable(test_dash_auto_ratchet test_dash_auto_ratchet.cpp)
581+
target_link_libraries(test_dash_auto_ratchet PRIVATE
582+
GTest::gtest_main GTest::gtest
583+
dash_x11 core
584+
nlohmann_json::nlohmann_json
585+
${Boost_LIBRARIES}
586+
)
587+
target_link_libraries(test_dash_auto_ratchet PRIVATE c2pool_payout c2pool_hashrate c2pool_merged_mining) # OBJECT-lib SCC direct-naming (#22/#39)
588+
gtest_add_tests(test_dash_auto_ratchet "" AUTO)
589+
575590
add_executable(test_dash_peer test_dash_peer.cpp)
576591
target_link_libraries(test_dash_peer PRIVATE
577592
GTest::gtest_main GTest::gtest

0 commit comments

Comments
 (0)