Skip to content

Commit c171a33

Browse files
committed
dgb(Phase-B): SSOT pool efficiency / real-hashrate diagnostics + non-circular KAT
Lift the pool EFFICIENCY / REAL-HASHRATE diagnostics arithmetic out of node.cpp into a header-only SSOT (pool_efficiency.hpp): - compute_stale_prop(orphan, doa, total) = (orphan+doa)/total, 0 on empty - compute_real_pool_hashrate(pool_hs, stale_prop) = pool_hs / (1 - stale_prop) when stale_prop < 0.999 && pool_hs > 0 pool_hs otherwise Mirrors the p2pool-dgb-scrypt oracle main.py status loop (real_att_s = get_pool_attempts_per_second(...) / (1 - stale_prop)), operator-facing behavior pinned by the V36 master-compat invariant. FENCED/additive: node.cpp NOT yet rewired (byte-identity delegation is the follow-on). Pure arithmetic, consensus-neutral. Non-circular KAT (8/8) pins hand-derived oracle values. Registered in test CMakeLists + both build.yml --target allowlists (#143 NOT_BUILT guard).
1 parent da8be53 commit c171a33

4 files changed

Lines changed: 162 additions & 2 deletions

File tree

.github/workflows/build.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ jobs:
8484
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 dgb_gentx_share_path_test dgb_conn_pplns_producer_test dgb_other_tx_resolver_test \
8585
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 \
8686
rpc_request_test softfork_check_test genesis_check_test algo_select_test digishield_walk_test header_chain_test \
87-
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 v37_test \
87+
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_pool_efficiency_test v37_test \
8888
-j$(nproc)
8989
9090
- name: Run tests
@@ -216,7 +216,7 @@ jobs:
216216
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 dgb_gentx_share_path_test dgb_conn_pplns_producer_test dgb_other_tx_resolver_test \
217217
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 \
218218
rpc_request_test softfork_check_test genesis_check_test algo_select_test digishield_walk_test header_chain_test \
219-
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 test_coin_broadcaster test_multiaddress_pplns test_pplns_stress \
219+
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_pool_efficiency_test test_coin_broadcaster test_multiaddress_pplns test_pplns_stress \
220220
v37_test \
221221
-j$(nproc)
222222

src/impl/dgb/pool_efficiency.hpp

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
#pragma once
2+
3+
// SSOT for the DGB pool EFFICIENCY / REAL-HASHRATE diagnostics — the two pure
4+
// arithmetic steps the status loop applies when reporting pool performance:
5+
// (a) the stale proportion over a recent share window, and
6+
// (b) the efficiency-adjusted "real" pool hashrate that scales the raw
7+
// attempts/s up to compensate for stale (orphan + DOA) work.
8+
//
9+
// These are currently OPEN-CODED inline in node.cpp's diagnostics loop (the
10+
// `stale_prop = (orphan + doa) / total_recent` reduction and the
11+
// `real_pool_hs = pool_hs / (1 - stale_prop)` guarded division feeding the
12+
// "Pool: <hashrate> Expected time to block:" line). A silent drift — a flipped
13+
// guard, a wrong denominator, or dropping the div-by-~0 floor — would misreport
14+
// a node's effective hashrate and expected-time-to-block with NO compile error,
15+
// which is operator-facing behavior the V36 master-compat invariant pins to the
16+
// p2pool reference. Lifting the arithmetic to a single header-only SSOT lets a
17+
// KAT pin it exactly against the oracle.
18+
//
19+
// Oracle: p2pool-dgb-scrypt main.py status loop:
20+
// real_att_s = p2pool_data.get_pool_attempts_per_second(...) / (1 - stale_prop)
21+
// with stale_prop derived from the recent orphan/DOA counts over the window,
22+
// and the (1 - stale_prop) division guarded against the empty / ~all-stale
23+
// case so it never divides by ~zero.
24+
//
25+
// Per-coin isolation: dgb/ only. Header-only, additive; this slice does NOT yet
26+
// rewire node.cpp (that is the byte-identity-fenced delegation follow-on) — it
27+
// pins the math as free functions so the KAT exercises them with no NodeImpl /
28+
// ShareTracker standup. Consensus-neutral: pure arithmetic, no value semantics
29+
// changed.
30+
31+
#include <cstdint>
32+
33+
namespace dgb {
34+
35+
// Stale proportion over a recent share window.
36+
// stale_prop = (orphan_count + doa_count) / total_recent
37+
// An empty window (total_recent == 0) reports 0.0 rather than NaN, matching the
38+
// inline reduction in node.cpp's diagnostics loop.
39+
inline double compute_stale_prop(uint64_t orphan_count, uint64_t doa_count,
40+
uint64_t total_recent) {
41+
if (total_recent == 0)
42+
return 0.0;
43+
return static_cast<double>(orphan_count + doa_count) /
44+
static_cast<double>(total_recent);
45+
}
46+
47+
// Efficiency-adjusted "real" pool hashrate.
48+
// real = pool_hs / (1 - stale_prop) when stale_prop < 0.999 AND pool_hs > 0
49+
// real = pool_hs otherwise
50+
// The guard mirrors the oracle: when stale_prop approaches 1 (≥ 0.999) the
51+
// (1 - stale_prop) denominator approaches zero and the adjustment is suppressed
52+
// to avoid a blow-up; with no measured hashrate (pool_hs <= 0) there is nothing
53+
// to scale.
54+
inline double compute_real_pool_hashrate(double pool_hs, double stale_prop) {
55+
if (stale_prop < 0.999 && pool_hs > 0)
56+
return pool_hs / (1.0 - stale_prop);
57+
return pool_hs;
58+
}
59+
60+
} // namespace dgb

src/impl/dgb/test/CMakeLists.txt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -660,6 +660,18 @@ if (BUILD_TESTING AND GTest_FOUND)
660660
dgb_coin pool sharechain)
661661
gtest_add_tests(dgb_think_p6_desired_cutoff_test "" AUTO)
662662

663+
# dgb_pool_efficiency_test: FENCED, additive KAT pinning the pool
664+
# EFFICIENCY / REAL-HASHRATE diagnostics arithmetic in pool_efficiency.hpp vs
665+
# the p2pool main.py status-loop oracle (stale_prop = (orphan+doa)/total;
666+
# real_att_s = pool_hs / (1 - stale_prop) with the near-all-stale guard).
667+
# Pure header (<cstdint> only) -> links only GTest. node.cpp NOT yet rewired
668+
# (delegation is the byte-identity follow-on). MUST also be in the build.yml
669+
# --target allowlist (#143 NOT_BUILT trap).
670+
add_executable(dgb_pool_efficiency_test pool_efficiency_test.cpp)
671+
target_link_libraries(dgb_pool_efficiency_test PRIVATE
672+
GTest::gtest_main GTest::gtest)
673+
gtest_add_tests(dgb_pool_efficiency_test "" AUTO)
674+
663675
# dgb_think_p4_head_keys_test: FENCED, additive KAT pinning the think()
664676
# Phase-4 head-score KEY construction in think_p4_head_keys.hpp vs the
665677
# p2pool data.py think() oracle (selection key (adjusted_work,-reason,-ts)
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
// dgb::compute_stale_prop / compute_real_pool_hashrate — pool efficiency KAT.
2+
//
3+
// FENCED conformance test (no production code touched). Pins the pool
4+
// EFFICIENCY / REAL-HASHRATE diagnostics arithmetic lifted into
5+
// pool_efficiency.hpp against the p2pool-dgb-scrypt oracle main.py status loop:
6+
// real_att_s = get_pool_attempts_per_second(...) / (1 - stale_prop)
7+
// with stale_prop = (orphan + doa) / total_recent over the recent share window.
8+
//
9+
// The expected values here are HAND-DERIVED from the oracle formulas (not
10+
// produced by calling the helper under test), so the test is non-circular: it
11+
// independently recomputes the oracle expression and asserts the helper matches.
12+
//
13+
// MUST appear in BOTH the ctest registration (this dir CMakeLists.txt) AND the
14+
// build.yml --target allowlist, or it becomes a #143-style NOT_BUILT sentinel
15+
// that reds master.
16+
17+
#include <impl/dgb/pool_efficiency.hpp>
18+
19+
#include <gtest/gtest.h>
20+
21+
namespace {
22+
23+
// ---- compute_stale_prop -------------------------------------------------
24+
25+
TEST(DgbPoolEfficiency, StalePropEmptyWindowIsZero) {
26+
// total_recent == 0 -> 0.0, never NaN.
27+
EXPECT_DOUBLE_EQ(dgb::compute_stale_prop(0, 0, 0), 0.0);
28+
// Even with (impossibly) non-zero counts, an empty window short-circuits.
29+
EXPECT_DOUBLE_EQ(dgb::compute_stale_prop(7, 3, 0), 0.0);
30+
}
31+
32+
TEST(DgbPoolEfficiency, StalePropNoStalesIsZero) {
33+
EXPECT_DOUBLE_EQ(dgb::compute_stale_prop(0, 0, 100), 0.0);
34+
}
35+
36+
TEST(DgbPoolEfficiency, StalePropOrphanPlusDoaOverTotal) {
37+
// 3 orphan + 2 doa over 100 recent -> 5/100 = 0.05 (hand-derived).
38+
EXPECT_DOUBLE_EQ(dgb::compute_stale_prop(3, 2, 100), 0.05);
39+
// 1 orphan + 0 doa over 4 -> 0.25.
40+
EXPECT_DOUBLE_EQ(dgb::compute_stale_prop(1, 0, 4), 0.25);
41+
// All stale: 50 over 50 -> 1.0.
42+
EXPECT_DOUBLE_EQ(dgb::compute_stale_prop(30, 20, 50), 1.0);
43+
}
44+
45+
// ---- compute_real_pool_hashrate ----------------------------------------
46+
47+
TEST(DgbPoolEfficiency, RealHashrateZeroStaleIsIdentity) {
48+
// stale_prop == 0 -> divisor is 1 -> unchanged.
49+
EXPECT_DOUBLE_EQ(dgb::compute_real_pool_hashrate(1000.0, 0.0), 1000.0);
50+
}
51+
52+
TEST(DgbPoolEfficiency, RealHashrateScalesByInverseEfficiency) {
53+
// Oracle: pool_hs / (1 - stale_prop). Recompute the oracle expression
54+
// independently (non-circular) and assert the helper equals it.
55+
const double pool_hs = 1000.0;
56+
const double stale_prop = 0.05;
57+
const double oracle = pool_hs / (1.0 - stale_prop); // ~1052.6315789...
58+
EXPECT_DOUBLE_EQ(dgb::compute_real_pool_hashrate(pool_hs, stale_prop), oracle);
59+
// Sanity anchor on the magnitude (hand: 1000/0.95 > 1052, < 1053).
60+
EXPECT_GT(dgb::compute_real_pool_hashrate(pool_hs, stale_prop), 1052.0);
61+
EXPECT_LT(dgb::compute_real_pool_hashrate(pool_hs, stale_prop), 1053.0);
62+
}
63+
64+
TEST(DgbPoolEfficiency, RealHashrateGuardSuppressesNearAllStale) {
65+
// stale_prop == 0.999 is NOT < 0.999 -> guard holds -> identity, no blow-up.
66+
EXPECT_DOUBLE_EQ(dgb::compute_real_pool_hashrate(1000.0, 0.999), 1000.0);
67+
// Above the threshold likewise suppressed.
68+
EXPECT_DOUBLE_EQ(dgb::compute_real_pool_hashrate(1000.0, 1.0), 1000.0);
69+
}
70+
71+
TEST(DgbPoolEfficiency, RealHashrateNoMeasuredWorkIsIdentity) {
72+
// pool_hs <= 0 -> nothing to scale.
73+
EXPECT_DOUBLE_EQ(dgb::compute_real_pool_hashrate(0.0, 0.05), 0.0);
74+
EXPECT_DOUBLE_EQ(dgb::compute_real_pool_hashrate(-5.0, 0.05), -5.0);
75+
}
76+
77+
// ---- end-to-end chain mirroring node.cpp diagnostics --------------------
78+
79+
TEST(DgbPoolEfficiency, ChainStalePropThenRealHashrateMatchesOracle) {
80+
// node.cpp: stale_prop from counts, then real_pool_hs from raw aps.
81+
const double sp = dgb::compute_stale_prop(3, 2, 100); // 0.05
82+
EXPECT_DOUBLE_EQ(sp, 0.05);
83+
const double pool_hs = 2000.0;
84+
const double oracle = pool_hs / (1.0 - sp); // 2000/0.95
85+
EXPECT_DOUBLE_EQ(dgb::compute_real_pool_hashrate(pool_hs, sp), oracle);
86+
}
87+
88+
} // namespace

0 commit comments

Comments
 (0)