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 @@ -84,7 +84,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 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_dc_proof_test dgb_aux_doge_bind_parsers_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_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 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_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 v37_test \
-j$(nproc)

- name: Run tests
Expand Down Expand Up @@ -216,7 +216,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 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_dc_proof_test dgb_aux_doge_bind_parsers_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_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 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_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 test_coin_broadcaster test_multiaddress_pplns test_pplns_stress \
v37_test \
-j$(nproc)

Expand Down
83 changes: 83 additions & 0 deletions src/impl/dgb/coin/desired_version_tally.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
#pragma once

// SSOT for the DGB DESIRED-VERSION TALLY -- the pure accumulation core of
// ShareTracker::get_desired_version_weights() (and its flat-count diagnostic
// sibling get_desired_version_counts()). Walking back over a clamped
// [.., CHAIN_LENGTH] window of shares, each share's m_desired_version is
// bucketed and either its work is summed (the consensus map) or its occurrence
// counted (the diagnostic map). The work-weighted map is the CONSENSUS input to
// the V36 60%-by-work switch rule (share_check step 2) and the #288 AutoRatchet
// activation tail guard; the flat-count map is diagnostics/KAT only and is
// NEVER the gate.
//
// Oracle: p2pool-dgb-scrypt data.py:918-922 get_desired_version_counts:
// def get_desired_version_counts(tracker, best_share_hash, dist):
// res = {}
// for share in tracker.get_chain(best_share_hash, dist):
// res[share.desired_version] = res.get(share.desired_version, 0) \
// + bitcoin_data.target_to_average_attempts(share.target)
// return res
//
// NOTE the oracle's get_desired_version_counts is ALREADY work-weighted -- each
// share contributes target_to_average_attempts(share.target), NOT 1. c2pool
// splits this into two accessors: get_desired_version_weights (the true oracle
// match -- weight = ShareIndex::work = chain::target_to_average_attempts(
// chain::bits_to_target(m_bits)), the CONSENSUS gate input) and
// get_desired_version_counts (a flat occurrence count, diagnostics-only, never
// the gate -- see the share_tracker.hpp comment, #406 and #288). This header
// captures ONLY the per-version accumulation over already-resolved
// (desired_version, work) pairs as free functions; the chain-walk and the
// lookbehind clamp stay inside ShareTracker (the clamp is the separate
// chain_walk_window SSOT). share_tracker.hpp is NOT rewired this slice
// (byte-identity delegation is the follow-on).
//
// Per-coin isolation: dgb/ only. Header-only, additive, consensus-neutral (pure
// std::map accumulation, no value semantics changed). MUST appear in BOTH this
// dir CMakeLists.txt AND the build.yml --target allowlist, or it becomes a #143
// NOT_BUILT sentinel.

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

#include <cstdint>
#include <map>
#include <vector>

namespace dgb {

// One share's contribution to the tally: its desired_version and its work
// (= chain::target_to_average_attempts(chain::bits_to_target(m_bits)), the same
// value ShareIndex::work caches). Mirrors the (dv, idx->work) pair the inline
// loop reads per share in get_desired_version_weights.
struct VersionWork {
uint64_t desired_version;
uint288 work;
};

// Work-weighted version tally -- the CONSENSUS gate input. Mirrors verbatim the
// inline body:
// for (share in window) weights[dv] = weights[dv] + idx->work;
// std::map value-initializes uint288(0) on first touch of a version key, exactly
// matching the inline weights[dv] + idx->work (and the oracle
// res.get(dv, 0) + attempts).
inline std::map<uint64_t, uint288>
accumulate_version_weights(const std::vector<VersionWork>& window)
{
std::map<uint64_t, uint288> weights;
for (const auto& s : window)
weights[s.desired_version] = weights[s.desired_version] + s.work;
return weights;
}

// Flat occurrence count -- DIAGNOSTICS/KAT ONLY, never the consensus gate. Each
// share counts as 1 regardless of its work. Mirrors verbatim the inline body:
// for (share in window) counts[dv]++;
inline std::map<uint64_t, int32_t>
accumulate_version_counts(const std::vector<uint64_t>& window)
{
std::map<uint64_t, int32_t> counts;
for (uint64_t dv : window)
counts[dv]++;
return counts;
}

} // namespace dgb
18 changes: 18 additions & 0 deletions src/impl/dgb/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -748,6 +748,24 @@ if (BUILD_TESTING AND GTest_FOUND)
target_link_libraries(dgb_binomial_conf_interval_test PRIVATE
GTest::gtest_main GTest::gtest)
gtest_add_tests(dgb_binomial_conf_interval_test "" AUTO)
# dgb_desired_version_tally_test: FENCED, additive KAT pinning the
# version->work / version->count accumulation core lifted into
# coin/desired_version_tally.hpp vs the p2pool-dgb-scrypt data.py:918-922
# get_desired_version_counts oracle (res[dv] += target_to_average_attempts(
# share.target)). The work-weighted map is the CONSENSUS 60%-by-work switch
# input (share_check step 2 / #288 AutoRatchet tail guard); the flat-count
# accumulator is diagnostics-only and pinned just to lock the documented
# divergence. The work-equivalence anchor uses chain::target_to_average_attempts
# over 2^k-1 targets (hand-derived ttaa), so links core for uint288 +
# target_utils. share_tracker.hpp NOT yet rewired (delegation is the
# byte-identity follow-on). MUST also be in the build.yml --target allowlist
# (#143 NOT_BUILT trap).
add_executable(dgb_desired_version_tally_test desired_version_tally_test.cpp)
target_link_libraries(dgb_desired_version_tally_test PRIVATE
GTest::gtest_main GTest::gtest
core
c2pool_payout c2pool_merged_mining c2pool_hashrate c2pool_storage)
gtest_add_tests(dgb_desired_version_tally_test "" AUTO)
# dgb_pool_efficiency_test: FENCED, additive KAT pinning the pool
# EFFICIENCY / REAL-HASHRATE diagnostics arithmetic in pool_efficiency.hpp vs
# the p2pool main.py status-loop oracle (stale_prop = (orphan+doa)/total;
Expand Down
116 changes: 116 additions & 0 deletions src/impl/dgb/test/desired_version_tally_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
// dgb desired-version tally -- version->work / version->count accumulation KAT.
//
// FENCED, additive (no production code touched this slice). Pins
// src/impl/dgb/coin/desired_version_tally.hpp against the p2pool-dgb-scrypt
// data.py:918-922 get_desired_version_counts oracle:
// res = {}
// for share in tracker.get_chain(best_share_hash, dist):
// res[share.desired_version] = res.get(share.desired_version, 0) \
// + bitcoin_data.target_to_average_attempts(share.target)
// return res
//
// The oracle weights each share by target_to_average_attempts(share.target);
// c2pool exposes that as get_desired_version_weights (CONSENSUS gate input) with
// per-share weight = ShareIndex::work = chain::target_to_average_attempts(
// chain::bits_to_target(m_bits)). The flat-count accumulator is diagnostics-only
// and has NO oracle equivalent (the oracle has no flat count) -- it is pinned
// here only to lock the documented divergence (each share == 1, work ignored).
//
// Every expectation is hand-derived from the oracle formula. The work-equivalence
// anchor uses targets whose target_to_average_attempts is exactly computable by
// hand: ttaa(t) = 2^256 / (t + 1), so for t = 2^k - 1, t + 1 = 2^k and
// ttaa = 2^(256-k). The chain-walk + lookbehind clamp stay in ShareTracker; this
// header lifts ONLY the per-version map accumulation. share_tracker.hpp is NOT
// rewired (delegation is the byte-identity follow-on). Pure header (uint288) ->
// links core only. 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 <core/target_utils.hpp> // chain::target_to_average_attempts

#include <gtest/gtest.h>

namespace {

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

} // namespace

// --- empty window -> empty maps (the actual<=0 early-return shape) ----------
TEST(DgbDesiredVersionTally, EmptyWindowYieldsEmptyMaps) {
EXPECT_TRUE(dgb::accumulate_version_weights({}).empty());
EXPECT_TRUE(dgb::accumulate_version_counts({}).empty());
}

// --- single share: one bucket, weight == its work, count == 1 ---------------
TEST(DgbDesiredVersionTally, SingleShareSingleBucket) {
auto w = dgb::accumulate_version_weights({{36, u(42)}});
ASSERT_EQ(w.size(), 1u);
EXPECT_EQ(w[36], u(42));

auto c = dgb::accumulate_version_counts({36});
ASSERT_EQ(c.size(), 1u);
EXPECT_EQ(c[36], 1);
}

// --- same version, many shares: weights sum, count increments ---------------
TEST(DgbDesiredVersionTally, SameVersionAccumulates) {
auto w = dgb::accumulate_version_weights({{36, u(10)}, {36, u(20)}, {36, u(30)}});
ASSERT_EQ(w.size(), 1u);
EXPECT_EQ(w[36], u(60)); // 10 + 20 + 30

auto c = dgb::accumulate_version_counts({36, 36, 36});
EXPECT_EQ(c[36], 3);
}

// --- multiple versions are bucketed independently ---------------------------
TEST(DgbDesiredVersionTally, MultipleVersionsBucketed) {
auto w = dgb::accumulate_version_weights(
{{35, u(5)}, {36, u(11)}, {35, u(7)}});
ASSERT_EQ(w.size(), 2u);
EXPECT_EQ(w[35], u(12)); // 5 + 7
EXPECT_EQ(w[36], u(11));

auto c = dgb::accumulate_version_counts({35, 36, 35});
EXPECT_EQ(c[35], 2);
EXPECT_EQ(c[36], 1);
}

// --- NON-CIRCULAR oracle anchor: per-share weight == target_to_average_attempts,
// summed per version, matches data.py:918 res[dv] += ttaa(target). ---------
// t = 2^255-1 -> ttaa = 2 ; t = 2^254-1 -> ttaa = 4 ; t = 2^253-1 -> ttaa = 8
TEST(DgbDesiredVersionTally, WorkWeightedMatchesOracleAttempts) {
uint256 t255, t254, t253;
t255.SetHex("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); // 2^255-1
t254.SetHex("3fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); // 2^254-1
t253.SetHex("1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); // 2^253-1

const uint288 a255 = chain::target_to_average_attempts(t255);
const uint288 a254 = chain::target_to_average_attempts(t254);
const uint288 a253 = chain::target_to_average_attempts(t253);

// Hand-derived ttaa = 2^(256-k). These also non-circularly pin the work def.
ASSERT_EQ(a255, u(2));
ASSERT_EQ(a254, u(4));
ASSERT_EQ(a253, u(8));

// v36 has two shares (work 2 and 4), v35 has one share (work 8).
auto w = dgb::accumulate_version_weights(
{{36, a255}, {35, a253}, {36, a254}});
ASSERT_EQ(w.size(), 2u);
EXPECT_EQ(w[36], u(6)); // 2 + 4
EXPECT_EQ(w[35], u(8)); // 8
}

// --- flat count is NOT work-weighted: pins the documented divergence ---------
TEST(DgbDesiredVersionTally, FlatCountIgnoresWork) {
// Three v36 shares with wildly different work: weights sum the work,
// counts treat each as exactly 1 (diagnostics-only, never the gate).
auto w = dgb::accumulate_version_weights({{36, u(1)}, {36, u(1000)}, {36, u(7)}});
EXPECT_EQ(w[36], u(1008));

auto c = dgb::accumulate_version_counts({36, 36, 36});
EXPECT_EQ(c[36], 3); // not 1008 -- work is ignored
}
Loading