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
4 changes: 2 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ jobs:
dgb_gentx_coinbase_test dgb_connection_coinbase_test dgb_pplns_payout_split_test nmc_auxpow_merkle_test nmc_template_builder_test nmc_auxpow_wire_test nmc_reconstruct_won_block_test nmc_mempool_name_test nmc_block_broadcast_test nmc_host_dualpath_test nmc_fallback_path_conformance_test dgb_gentx_share_path_test dgb_conn_pplns_producer_test dgb_other_tx_resolver_test \
dgb_other_tx_assembler_test dgb_reconstruct_won_block_test dgb_reconstruct_closure_test dgb_gentx_unpack_test dgb_work_source_test dgb_template_builder_test dgb_embedded_coin_node_test dgb_embedded_tx_select_test dgb_template_other_txs_test dgb_coinbase_value_parity_test dgb_submit_classify_test dgb_aux_parent_coinbase_parity_test dgb_template_capture_test dgb_aux_doge_db_commitment_bind_test dgb_aux_doge_mm_commitment_test dgb_aux_doge_dc_proof_test dgb_aux_doge_bind_parsers_test dgb_compact_blocks_bip152_parity_test dgb_aux_dual_target_select_test dgb_aux_broadcast_path_election_test \
rpc_request_test softfork_check_test genesis_check_test algo_select_test digishield_walk_test header_chain_test \
dgb_coin_node_seam_test dgb_block_broadcast_test dgb_won_block_dispatch_test dgb_forced_won_share_dualpath_test dgb_scrypt_pow_test dgb_nonce_grinder_test dgb_regrind_block_test dgb_won_block_finalize_test dgb_share_target_genesis_test dgb_share_target_retarget_test dgb_share_bits_oracle_pin_test dgb_pool_msg_wire_test dgb_get_shares_walk_test dgb_download_stops_test dgb_think_p1_walk_bounds_test dgb_think_p1_desired_emit_test dgb_think_p6_desired_cutoff_test dgb_think_p4_head_keys_test dgb_think_p3_best_head_test dgb_g1_oracle_byte_parity_test dgb_think_p2_walk_bounds_test dgb_expected_time_to_block_test dgb_tail_score_endpoints_test dgb_pool_attempts_per_second_test dgb_pool_efficiency_test dgb_think_p5_best_share_punish_test dgb_auto_ratchet_tail_guard_test dgb_binomial_conf_interval_test dgb_desired_version_tally_test dgb_get_height_and_last_endpoints_test dgb_chain_walk_window_test dgb_redistribute_delegate_ghal_test dgb_share_weight_decay_test dgb_naughty_propagation_test dgb_hash_format_parity_test dgb_emergency_decay_saturation_test v37_test \
dgb_coin_node_seam_test dgb_block_broadcast_test dgb_won_block_dispatch_test dgb_forced_won_share_dualpath_test dgb_scrypt_pow_test dgb_nonce_grinder_test dgb_regrind_block_test dgb_won_block_finalize_test dgb_share_target_genesis_test dgb_share_target_retarget_test dgb_share_bits_oracle_pin_test dgb_pool_msg_wire_test dgb_get_shares_walk_test dgb_download_stops_test dgb_think_p1_walk_bounds_test dgb_think_p1_desired_emit_test dgb_think_p6_desired_cutoff_test dgb_think_p4_head_keys_test dgb_think_p3_best_head_test dgb_g1_oracle_byte_parity_test dgb_think_p2_walk_bounds_test dgb_expected_time_to_block_test dgb_tail_score_endpoints_test dgb_pool_attempts_per_second_test dgb_pool_efficiency_test dgb_think_p5_best_share_punish_test dgb_auto_ratchet_tail_guard_test dgb_binomial_conf_interval_test dgb_desired_version_tally_test dgb_min_protocol_ratchet_test dgb_get_height_and_last_endpoints_test dgb_chain_walk_window_test dgb_redistribute_delegate_ghal_test dgb_share_weight_decay_test dgb_naughty_propagation_test dgb_hash_format_parity_test dgb_emergency_decay_saturation_test v37_test \
-j8

- name: Run tests
Expand Down Expand Up @@ -241,7 +241,7 @@ jobs:
dgb_gentx_coinbase_test dgb_connection_coinbase_test dgb_pplns_payout_split_test nmc_auxpow_merkle_test nmc_template_builder_test nmc_auxpow_wire_test nmc_reconstruct_won_block_test nmc_mempool_name_test nmc_block_broadcast_test nmc_host_dualpath_test nmc_fallback_path_conformance_test dgb_gentx_share_path_test dgb_conn_pplns_producer_test dgb_other_tx_resolver_test \
dgb_other_tx_assembler_test dgb_reconstruct_won_block_test dgb_reconstruct_closure_test dgb_gentx_unpack_test dgb_work_source_test dgb_template_builder_test dgb_embedded_coin_node_test dgb_embedded_tx_select_test dgb_template_other_txs_test dgb_coinbase_value_parity_test dgb_submit_classify_test dgb_aux_parent_coinbase_parity_test dgb_template_capture_test dgb_aux_doge_db_commitment_bind_test dgb_aux_doge_mm_commitment_test dgb_aux_doge_dc_proof_test dgb_aux_doge_bind_parsers_test dgb_compact_blocks_bip152_parity_test dgb_aux_dual_target_select_test dgb_aux_broadcast_path_election_test \
rpc_request_test softfork_check_test genesis_check_test algo_select_test digishield_walk_test header_chain_test \
dgb_coin_node_seam_test dgb_block_broadcast_test dgb_won_block_dispatch_test dgb_forced_won_share_dualpath_test dgb_scrypt_pow_test dgb_nonce_grinder_test dgb_regrind_block_test dgb_won_block_finalize_test dgb_share_target_genesis_test dgb_share_target_retarget_test dgb_share_bits_oracle_pin_test dgb_pool_msg_wire_test dgb_get_shares_walk_test dgb_download_stops_test dgb_think_p1_walk_bounds_test dgb_think_p1_desired_emit_test dgb_think_p6_desired_cutoff_test dgb_think_p4_head_keys_test dgb_think_p3_best_head_test dgb_g1_oracle_byte_parity_test dgb_think_p2_walk_bounds_test dgb_expected_time_to_block_test dgb_tail_score_endpoints_test dgb_pool_attempts_per_second_test dgb_pool_efficiency_test dgb_think_p5_best_share_punish_test dgb_auto_ratchet_tail_guard_test dgb_binomial_conf_interval_test dgb_desired_version_tally_test dgb_get_height_and_last_endpoints_test dgb_chain_walk_window_test dgb_redistribute_delegate_ghal_test dgb_share_weight_decay_test dgb_naughty_propagation_test dgb_hash_format_parity_test dgb_emergency_decay_saturation_test test_coin_broadcaster test_multiaddress_pplns test_pplns_stress \
dgb_coin_node_seam_test dgb_block_broadcast_test dgb_won_block_dispatch_test dgb_forced_won_share_dualpath_test dgb_scrypt_pow_test dgb_nonce_grinder_test dgb_regrind_block_test dgb_won_block_finalize_test dgb_share_target_genesis_test dgb_share_target_retarget_test dgb_share_bits_oracle_pin_test dgb_pool_msg_wire_test dgb_get_shares_walk_test dgb_download_stops_test dgb_think_p1_walk_bounds_test dgb_think_p1_desired_emit_test dgb_think_p6_desired_cutoff_test dgb_think_p4_head_keys_test dgb_think_p3_best_head_test dgb_g1_oracle_byte_parity_test dgb_think_p2_walk_bounds_test dgb_expected_time_to_block_test dgb_tail_score_endpoints_test dgb_pool_attempts_per_second_test dgb_pool_efficiency_test dgb_think_p5_best_share_punish_test dgb_auto_ratchet_tail_guard_test dgb_binomial_conf_interval_test dgb_desired_version_tally_test dgb_min_protocol_ratchet_test dgb_get_height_and_last_endpoints_test dgb_chain_walk_window_test dgb_redistribute_delegate_ghal_test dgb_share_weight_decay_test dgb_naughty_propagation_test dgb_hash_format_parity_test dgb_emergency_decay_saturation_test test_coin_broadcaster test_multiaddress_pplns test_pplns_stress \
v37_test \
-j8

Expand Down
48 changes: 48 additions & 0 deletions src/impl/dgb/coin/desired_version_tally.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,4 +80,52 @@ accumulate_version_counts(const std::vector<uint64_t>& window)
return counts;
}

// Runtime P2P accept-floor RATCHET -- SSOT for the oracle update_min_protocol_version
// (p2pool-dgb-scrypt data.py:857-863), the runtime sibling of the 60% version-switch
// gate (share_check.hpp step 2). Both read the SAME work-weighted desired-version map
// over the SAME window [CHAIN_LENGTH*9//10, CHAIN_LENGTH//10] behind the best share's
// parent (main.py:216 / data.py:562 call-site); this one lifts the inbound handshake
// floor instead of rejecting a share.
//
// Oracle (data.py:857):
// def update_min_protocol_version(counts, share):
// minpver = getattr(share.net, 'MINIMUM_PROTOCOL_VERSION', 1400)
// newminpver = share.MINIMUM_PROTOCOL_VERSION
// if (counts is not None) and (minpver < newminpver):
// if counts.get(share.VERSION, 0) >= sum(counts.itervalues())*95//100:
// share.net.MINIMUM_PROTOCOL_VERSION = newminpver
//
// `counts` here is get_desired_version_counts -- WORK-WEIGHTED by
// target_to_average_attempts(share.target), keyed by desired_version, looked up by the
// best share's VERSION. c2pool's get_desired_version_weights is the byte-faithful
// match (see accumulate_version_weights above), so this function consumes that map
// directly. Pure: no I/O, inputs unmutated; returns the (possibly lifted) floor.
//
// Integer math is the oracle's exactly: ratchet iff
// best_weight >= floor(total_weight * 95 / 100).
// We floor-divide (total*95)/100 and NOT the cross-multiplied best*100 >= total*95
// idiom -- the two differ at the boundary when (total*95) % 100 != 0 (then floor-div
// accepts best == floor(...), cross-mult rejects it), and the oracle floor-divides.
inline uint32_t ratchet_min_protocol_version(
const std::map<uint64_t, uint288>& version_weights,
int64_t best_version,
uint32_t current_floor,
uint32_t target_floor)
{
if (current_floor >= target_floor) // oracle: minpver < newminpver guard
return current_floor;

uint288 best_weight;
uint288 total_weight;
for (const auto& [ver, w] : version_weights) {
total_weight = total_weight + w;
if (static_cast<int64_t>(ver) == best_version)
best_weight = best_weight + w;
}
// oracle: counts.get(share.VERSION, 0) >= sum(counts.itervalues())*95//100
if (best_weight >= (total_weight * uint32_t(95)) / uint32_t(100))
return target_floor;
return current_floor;
}

} // namespace dgb
14 changes: 14 additions & 0 deletions src/impl/dgb/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -901,6 +901,20 @@ if (BUILD_TESTING AND GTest_FOUND)
core
c2pool_payout c2pool_merged_mining c2pool_hashrate c2pool_storage)
gtest_add_tests(dgb_desired_version_tally_test "" AUTO)
# dgb_min_protocol_ratchet_test: FENCED, additive KAT pinning the runtime
# P2P accept-floor ratchet dgb::ratchet_min_protocol_version (coin/
# desired_version_tally.hpp) vs the p2pool-dgb-scrypt update_min_protocol_version
# oracle (data.py:857-863): best-VERSION work-weight >= floor(total*95/100) lifts
# MINIMUM_PROTOCOL_VERSION 1400->3500. Same work-weighted map / window as the 60%
# switch gate; floor-div boundary case pins the oracle math (not cross-multiply).
# Pure header -> links core only. MUST also be in the build.yml --target allowlist
# (#143 NOT_BUILT trap).
add_executable(dgb_min_protocol_ratchet_test min_protocol_ratchet_test.cpp)
target_link_libraries(dgb_min_protocol_ratchet_test PRIVATE
GTest::gtest_main GTest::gtest
core
c2pool_payout c2pool_merged_mining c2pool_hashrate c2pool_storage)
gtest_add_tests(dgb_min_protocol_ratchet_test "" AUTO)
# dgb_naughty_propagation_test: FENCED, additive KAT pinning the
# NAUGHTY-PROPAGATION ancestor-punishment generation arithmetic in
# coin/naughty_propagation.hpp vs the p2pool-dgb-scrypt data.py:543-549
Expand Down
112 changes: 112 additions & 0 deletions src/impl/dgb/test/min_protocol_ratchet_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
// dgb runtime P2P accept-floor RATCHET KAT.
//
// FENCED, additive (no production code touched -- pins the pure free function
// dgb::ratchet_min_protocol_version in coin/desired_version_tally.hpp). Mirrors
// the p2pool-dgb-scrypt update_min_protocol_version oracle (data.py:857-863):
// def update_min_protocol_version(counts, share):
// minpver = getattr(share.net, "MINIMUM_PROTOCOL_VERSION", 1400)
// newminpver = share.MINIMUM_PROTOCOL_VERSION
// if (counts is not None) and (minpver < newminpver):
// if counts.get(share.VERSION, 0) >= sum(counts.itervalues())*95//100:
// share.net.MINIMUM_PROTOCOL_VERSION = newminpver
//
// counts is the WORK-WEIGHTED get_desired_version_counts map (each share weighted
// by target_to_average_attempts, keyed by desired_version, looked up by the best
// share VERSION) -- c2pool exposes it as get_desired_version_weights. The cold
// floor 1400 / ratchet target 3500 are the oracle p2p.py:153 getattr fallback and
// data.py:81 BaseShare.MINIMUM_PROTOCOL_VERSION (== config_pool.hpp
// MINIMUM_PROTOCOL_VERSION / SHARE_MINIMUM_PROTOCOL_VERSION, step-1 #599).
//
// Every expectation is hand-derived from the oracle formula. MUST appear in BOTH
// this dir CMakeLists.txt AND the build.yml --target allowlist, or it becomes a
// #143 NOT_BUILT sentinel.

#include <impl/dgb/coin/desired_version_tally.hpp>

#include <core/uint256.hpp> // uint288

#include <gtest/gtest.h>

namespace {

uint288 u(uint64_t n) { return uint288(n); }

// 2^k as a uint288 -- the scale of a real target_to_average_attempts (ttaa(2^k-1)
// = 2^(256-k)), so the boundary/overflow cases run at consensus magnitudes.
uint288 pow2(unsigned k) {
uint288 v = u(1);
for (unsigned i = 0; i < k; ++i) v = v * uint32_t(2);
return v;
}

constexpr uint32_t COLD = 1400; // oracle p2p.py:153 getattr fallback
constexpr uint32_t TARGET = 3500; // oracle data.py:81 BaseShare.MINIMUM_PROTOCOL_VERSION

} // namespace

// --- guard: floor already at/above target -> no-op (minpver < newminpver false)
TEST(DgbMinProtocolRatchet, AlreadyAtTargetIsNoOp) {
EXPECT_EQ(dgb::ratchet_min_protocol_version({{36, u(100)}}, 36, TARGET, TARGET), TARGET);
EXPECT_EQ(dgb::ratchet_min_protocol_version({{36, u(100)}}, 36, TARGET + 1, TARGET), TARGET + 1);
}

// --- unanimous support -> ratchet cold floor up to target -------------------
TEST(DgbMinProtocolRatchet, UnanimousRatchets) {
EXPECT_EQ(dgb::ratchet_min_protocol_version({{36, u(100)}}, 36, COLD, TARGET), TARGET);
}

// --- exactly 95% (sum=100, best=95) -> 95 >= 100*95//100=95 -> ratchet -------
TEST(DgbMinProtocolRatchet, Exactly95PercentRatchets) {
auto w = std::map<uint64_t, uint288>{{36, u(95)}, {35, u(5)}};
EXPECT_EQ(dgb::ratchet_min_protocol_version(w, 36, COLD, TARGET), TARGET);
}

// --- just below 95% (sum=100, best=94) -> 94 >= 95 false -> stays cold -------
TEST(DgbMinProtocolRatchet, JustBelow95PercentStaysCold) {
auto w = std::map<uint64_t, uint288>{{36, u(94)}, {35, u(6)}};
EXPECT_EQ(dgb::ratchet_min_protocol_version(w, 36, COLD, TARGET), COLD);
}

// --- floor-div vs cross-multiply BOUNDARY: sum=101, best=95.
// oracle floor: 95 >= floor(101*95/100)=floor(95.95)=95 -> RATCHET.
// cross-mult (best*100 >= sum*95): 9500 >= 9595 false -> would NOT ratchet.
// This case proves the function floor-divides like the oracle, not cross-mults.
TEST(DgbMinProtocolRatchet, FloorDivBoundaryRatchets) {
auto w = std::map<uint64_t, uint288>{{36, u(95)}, {35, u(6)}}; // sum=101
EXPECT_EQ(dgb::ratchet_min_protocol_version(w, 36, COLD, TARGET), TARGET);
}

// --- best version absent from the map -> best_weight 0 -> stays cold ---------
TEST(DgbMinProtocolRatchet, BestVersionAbsentStaysCold) {
EXPECT_EQ(dgb::ratchet_min_protocol_version({{35, u(100)}}, 36, COLD, TARGET), COLD);
}

// --- empty window: 0 >= 0*95//100=0 -> oracle ratchets (counts={} != None).
// The None-guard is the CALL-SITE job (oracle main.py:216 only calls when
// len(shares) > CHAIN_LENGTH); the pure function mirrors the dict-branch.
TEST(DgbMinProtocolRatchet, EmptyWindowMirrorsOracleDictBranch) {
EXPECT_EQ(dgb::ratchet_min_protocol_version({}, 36, COLD, TARGET), TARGET);
}

// --- work-weighted, NOT flat count: version 35 has the most SHARES but version
// 36 holds the dominant WORK. ratchet keys on work (get_desired_version_weights).
// Build the map through accumulate_version_weights to exercise the real pipe.
TEST(DgbMinProtocolRatchet, WorkWeightedNotFlatCount) {
// ten tiny v35 shares (work 1 each = 10) + one heavy v36 share (work 1000).
std::vector<dgb::VersionWork> window;
for (int i = 0; i < 10; ++i) window.push_back({35, u(1)});
window.push_back({36, u(1000)}); // total=1010, best(36)=1000 >= 1010*95//100=959
auto w = dgb::accumulate_version_weights(window);
EXPECT_EQ(dgb::ratchet_min_protocol_version(w, 36, COLD, TARGET), TARGET);
// flat count would give v35 ten shares vs v36 one -> would NOT ratchet on 36.
}

// --- consensus-magnitude weights (2^256-scale), no overflow in total*95 ------
TEST(DgbMinProtocolRatchet, ConsensusMagnitudeNoOverflow) {
// 96 * 2^256 at v36, 4 * 2^256 at v35: total=100*2^256, best=96*2^256 >= 95% -> ratchet.
uint288 unit = pow2(256);
uint288 best = unit; for (int i = 1; i < 96; ++i) best = best + unit; // 96 * 2^256
uint288 rest = unit; for (int i = 1; i < 4; ++i) rest = rest + unit; // 4 * 2^256
auto w = std::map<uint64_t, uint288>{{36, best}, {35, rest}};
EXPECT_EQ(dgb::ratchet_min_protocol_version(w, 36, COLD, TARGET), TARGET);
}
Loading