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 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 \
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 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_pool_efficiency_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 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 \
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 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_pool_efficiency_test test_coin_broadcaster test_multiaddress_pplns test_pplns_stress \
v37_test \
-j$(nproc)

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

// SSOT for the DGB pool EFFICIENCY / REAL-HASHRATE diagnostics — the two pure
// arithmetic steps the status loop applies when reporting pool performance:
// (a) the stale proportion over a recent share window, and
// (b) the efficiency-adjusted "real" pool hashrate that scales the raw
// attempts/s up to compensate for stale (orphan + DOA) work.
//
// These are currently OPEN-CODED inline in node.cpp's diagnostics loop (the
// `stale_prop = (orphan + doa) / total_recent` reduction and the
// `real_pool_hs = pool_hs / (1 - stale_prop)` guarded division feeding the
// "Pool: <hashrate> Expected time to block:" line). A silent drift — a flipped
// guard, a wrong denominator, or dropping the div-by-~0 floor — would misreport
// a node's effective hashrate and expected-time-to-block with NO compile error,
// which is operator-facing behavior the V36 master-compat invariant pins to the
// p2pool reference. Lifting the arithmetic to a single header-only SSOT lets a
// KAT pin it exactly against the oracle.
//
// Oracle: p2pool-dgb-scrypt main.py status loop:
// real_att_s = p2pool_data.get_pool_attempts_per_second(...) / (1 - stale_prop)
// with stale_prop derived from the recent orphan/DOA counts over the window,
// and the (1 - stale_prop) division guarded against the empty / ~all-stale
// case so it never divides by ~zero.
//
// Per-coin isolation: dgb/ only. Header-only, additive; this slice does NOT yet
// rewire node.cpp (that is the byte-identity-fenced delegation follow-on) — it
// pins the math as free functions so the KAT exercises them with no NodeImpl /
// ShareTracker standup. Consensus-neutral: pure arithmetic, no value semantics
// changed.

#include <cstdint>

namespace dgb {

// Stale proportion over a recent share window.
// stale_prop = (orphan_count + doa_count) / total_recent
// An empty window (total_recent == 0) reports 0.0 rather than NaN, matching the
// inline reduction in node.cpp's diagnostics loop.
inline double compute_stale_prop(uint64_t orphan_count, uint64_t doa_count,
uint64_t total_recent) {
if (total_recent == 0)
return 0.0;
return static_cast<double>(orphan_count + doa_count) /
static_cast<double>(total_recent);
}

// Efficiency-adjusted "real" pool hashrate.
// real = pool_hs / (1 - stale_prop) when stale_prop < 0.999 AND pool_hs > 0
// real = pool_hs otherwise
// The guard mirrors the oracle: when stale_prop approaches 1 (≥ 0.999) the
// (1 - stale_prop) denominator approaches zero and the adjustment is suppressed
// to avoid a blow-up; with no measured hashrate (pool_hs <= 0) there is nothing
// to scale.
inline double compute_real_pool_hashrate(double pool_hs, double stale_prop) {
if (stale_prop < 0.999 && pool_hs > 0)
return pool_hs / (1.0 - stale_prop);
return pool_hs;
}

} // namespace dgb
11 changes: 11 additions & 0 deletions src/impl/dgb/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -671,6 +671,17 @@ if (BUILD_TESTING AND GTest_FOUND)
target_link_libraries(dgb_expected_time_to_block_test PRIVATE
GTest::gtest_main GTest::gtest)
gtest_add_tests(dgb_expected_time_to_block_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;
# real_att_s = pool_hs / (1 - stale_prop) with the near-all-stale guard).
# Pure header (<cstdint> only) -> links only GTest. node.cpp 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_pool_efficiency_test pool_efficiency_test.cpp)
target_link_libraries(dgb_pool_efficiency_test PRIVATE
GTest::gtest_main GTest::gtest)
gtest_add_tests(dgb_pool_efficiency_test "" AUTO)

# dgb_think_p4_head_keys_test: FENCED, additive KAT pinning the think()
# Phase-4 head-score KEY construction in think_p4_head_keys.hpp vs the
Expand Down
88 changes: 88 additions & 0 deletions src/impl/dgb/test/pool_efficiency_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// dgb::compute_stale_prop / compute_real_pool_hashrate — pool efficiency KAT.
//
// FENCED conformance test (no production code touched). Pins the pool
// EFFICIENCY / REAL-HASHRATE diagnostics arithmetic lifted into
// pool_efficiency.hpp against the p2pool-dgb-scrypt oracle main.py status loop:
// real_att_s = get_pool_attempts_per_second(...) / (1 - stale_prop)
// with stale_prop = (orphan + doa) / total_recent over the recent share window.
//
// The expected values here are HAND-DERIVED from the oracle formulas (not
// produced by calling the helper under test), so the test is non-circular: it
// independently recomputes the oracle expression and asserts the helper matches.
//
// MUST appear in BOTH the ctest registration (this dir CMakeLists.txt) AND the
// build.yml --target allowlist, or it becomes a #143-style NOT_BUILT sentinel
// that reds master.

#include <impl/dgb/pool_efficiency.hpp>

#include <gtest/gtest.h>

namespace {

// ---- compute_stale_prop -------------------------------------------------

TEST(DgbPoolEfficiency, StalePropEmptyWindowIsZero) {
// total_recent == 0 -> 0.0, never NaN.
EXPECT_DOUBLE_EQ(dgb::compute_stale_prop(0, 0, 0), 0.0);
// Even with (impossibly) non-zero counts, an empty window short-circuits.
EXPECT_DOUBLE_EQ(dgb::compute_stale_prop(7, 3, 0), 0.0);
}

TEST(DgbPoolEfficiency, StalePropNoStalesIsZero) {
EXPECT_DOUBLE_EQ(dgb::compute_stale_prop(0, 0, 100), 0.0);
}

TEST(DgbPoolEfficiency, StalePropOrphanPlusDoaOverTotal) {
// 3 orphan + 2 doa over 100 recent -> 5/100 = 0.05 (hand-derived).
EXPECT_DOUBLE_EQ(dgb::compute_stale_prop(3, 2, 100), 0.05);
// 1 orphan + 0 doa over 4 -> 0.25.
EXPECT_DOUBLE_EQ(dgb::compute_stale_prop(1, 0, 4), 0.25);
// All stale: 50 over 50 -> 1.0.
EXPECT_DOUBLE_EQ(dgb::compute_stale_prop(30, 20, 50), 1.0);
}

// ---- compute_real_pool_hashrate ----------------------------------------

TEST(DgbPoolEfficiency, RealHashrateZeroStaleIsIdentity) {
// stale_prop == 0 -> divisor is 1 -> unchanged.
EXPECT_DOUBLE_EQ(dgb::compute_real_pool_hashrate(1000.0, 0.0), 1000.0);
}

TEST(DgbPoolEfficiency, RealHashrateScalesByInverseEfficiency) {
// Oracle: pool_hs / (1 - stale_prop). Recompute the oracle expression
// independently (non-circular) and assert the helper equals it.
const double pool_hs = 1000.0;
const double stale_prop = 0.05;
const double oracle = pool_hs / (1.0 - stale_prop); // ~1052.6315789...
EXPECT_DOUBLE_EQ(dgb::compute_real_pool_hashrate(pool_hs, stale_prop), oracle);
// Sanity anchor on the magnitude (hand: 1000/0.95 > 1052, < 1053).
EXPECT_GT(dgb::compute_real_pool_hashrate(pool_hs, stale_prop), 1052.0);
EXPECT_LT(dgb::compute_real_pool_hashrate(pool_hs, stale_prop), 1053.0);
}

TEST(DgbPoolEfficiency, RealHashrateGuardSuppressesNearAllStale) {
// stale_prop == 0.999 is NOT < 0.999 -> guard holds -> identity, no blow-up.
EXPECT_DOUBLE_EQ(dgb::compute_real_pool_hashrate(1000.0, 0.999), 1000.0);
// Above the threshold likewise suppressed.
EXPECT_DOUBLE_EQ(dgb::compute_real_pool_hashrate(1000.0, 1.0), 1000.0);
}

TEST(DgbPoolEfficiency, RealHashrateNoMeasuredWorkIsIdentity) {
// pool_hs <= 0 -> nothing to scale.
EXPECT_DOUBLE_EQ(dgb::compute_real_pool_hashrate(0.0, 0.05), 0.0);
EXPECT_DOUBLE_EQ(dgb::compute_real_pool_hashrate(-5.0, 0.05), -5.0);
}

// ---- end-to-end chain mirroring node.cpp diagnostics --------------------

TEST(DgbPoolEfficiency, ChainStalePropThenRealHashrateMatchesOracle) {
// node.cpp: stale_prop from counts, then real_pool_hs from raw aps.
const double sp = dgb::compute_stale_prop(3, 2, 100); // 0.05
EXPECT_DOUBLE_EQ(sp, 0.05);
const double pool_hs = 2000.0;
const double oracle = pool_hs / (1.0 - sp); // 2000/0.95
EXPECT_DOUBLE_EQ(dgb::compute_real_pool_hashrate(pool_hs, sp), oracle);
}

} // namespace
Loading