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 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_efficiency_test dgb_think_p5_best_share_punish_test dgb_auto_ratchet_tail_guard_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 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 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_efficiency_test dgb_think_p5_best_share_punish_test dgb_auto_ratchet_tail_guard_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 test_coin_broadcaster test_multiaddress_pplns test_pplns_stress \
v37_test \
-j$(nproc)

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

// SSOT for the DGB POOL-ATTEMPTS-PER-SECOND estimator -- the pure arithmetic
// core of ShareTracker::get_pool_attempts_per_second(): given the resolved
// near/far endpoints of a sharechain window, compute the pool hashrate estimate
//
// attempts_per_second = attempts / max(1, near_ts - far_ts)
//
// where attempts is the cumulative-work delta over the window (work, or min_work
// when the min-work variant is requested) and the time span is clamped to a
// strictly-positive denominator. This figure feeds the operator-facing "Pool:
// <hashrate>" status line AND the PPLNS / expected-time-to-block windows, so a
// silent drift (flipped sign on the span, dropping the <=0 clamp, a non-integer
// divide) would misreport pool hashrate with NO compile error -- operator-facing
// behavior the V36 master-compat invariant pins to the p2pool reference.
//
// Oracle: p2pool-dgb-scrypt data.py get_pool_attempts_per_second(tracker,
// previous_share_hash, dist, min_work=False, integer=False):
// assert dist >= 2
// near = tracker.items[previous_share_hash]
// far = tracker.items[tracker.get_nth_parent_hash(previous_share_hash, dist-1)]
// attempts = tracker.get_delta(near.hash, far.hash).work # or .min_work
// time = near.timestamp - far.timestamp
// if time <= 0: time = 1
// return attempts//time # the integer=True path c2pool always takes
//
// The chain-walk halves of the oracle -- locating `far` via get_nth_parent_hash
// and summing the work delta via get_delta -- stay inside ShareTracker (they need
// the skip-list / TrackerView). This header captures ONLY the endpoint-resolution
// guards and the final clamp+divide as a free function over already-resolved
// inputs, so a KAT can pin the arithmetic with no NodeImpl / ShareTracker standup.
//
// Per-coin isolation: dgb/ only. Header-only, additive. This slice does NOT yet
// rewire share_tracker.hpp -- that is the byte-identity delegation follow-on.
// The lifted body is a verbatim copy of the inline arithmetic (same int32_t span
// type, same uint288 divide), so the follow-on is provably value-identical.
// Consensus-neutral: pure arithmetic, no value semantics changed.

#include <cstdint>

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

namespace dgb {

// Resolved endpoints of a get_pool_attempts_per_second window. The caller
// performs the chain walk (dist guard, near lookup, far via get_nth_parent_hash,
// work delta via get_delta) and hands the results here.
struct PoolAttemptsInputs {
int32_t dist; // requested window depth; oracle asserts dist >= 2
bool near_in_chain; // previous_share_hash present in the tracker
bool far_resolved; // get_nth_parent_hash(dist-1) found AND in the tracker
uint288 attempts; // get_delta(near, far).work (or .min_work) -- caller picks
uint32_t near_ts; // near.timestamp
uint32_t far_ts; // far.timestamp
};

// attempts_per_second over the resolved window.
//
// Returns 0 when the window cannot be formed: dist < 2 (oracle assert), the near
// share is absent, or the far endpoint did not resolve. Otherwise the time span
// is near_ts - far_ts clamped up to 1 (matching the oracle `if time <= 0: time =
// 1`), and the result is the integer division attempts / span (the integer=True
// path c2pool always uses). uint288 throughout -- no overflow on the cumulative
// work numerator.
inline uint288 compute_pool_attempts_per_second(const PoolAttemptsInputs& in) {
if (in.dist < 2) return uint288(0);
if (!in.near_in_chain) return uint288(0);
if (!in.far_resolved) return uint288(0);

int32_t time_span = static_cast<int32_t>(in.near_ts) - static_cast<int32_t>(in.far_ts);
if (time_span <= 0) time_span = 1;

return in.attempts / uint288(time_span);
}

} // namespace dgb
12 changes: 12 additions & 0 deletions src/impl/dgb/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -724,6 +724,18 @@ if (BUILD_TESTING AND GTest_FOUND)
core
c2pool_payout c2pool_merged_mining c2pool_hashrate c2pool_storage)
gtest_add_tests(dgb_tail_score_endpoints_test "" AUTO)
# dgb_pool_attempts_per_second_test: FENCED, additive KAT pinning the pure
# arithmetic core of ShareTracker::get_pool_attempts_per_second lifted into
# coin/pool_attempts_per_second.hpp vs the p2pool data.py oracle
# (attempts // max(1, near_ts - far_ts), with the dist>=2 + endpoint guards).
# Links core for uint288. 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_pool_attempts_per_second_test pool_attempts_per_second_test.cpp)
target_link_libraries(dgb_pool_attempts_per_second_test PRIVATE
GTest::gtest_main GTest::gtest
core c2pool_hashrate c2pool_merged_mining c2pool_payout c2pool_storage)
gtest_add_tests(dgb_pool_attempts_per_second_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
107 changes: 107 additions & 0 deletions src/impl/dgb/test/pool_attempts_per_second_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
// dgb::compute_pool_attempts_per_second -- pool attempts/second KAT.
//
// FENCED conformance test (no production code touched). Pins the pure arithmetic
// core of ShareTracker::get_pool_attempts_per_second, lifted into
// pool_attempts_per_second.hpp, against the p2pool-dgb-scrypt oracle data.py
// get_pool_attempts_per_second:
// assert dist >= 2
// attempts = get_delta(near, far).work # or .min_work
// time = near.timestamp - far.timestamp
// if time <= 0: time = 1
// return attempts//time
//
// All expected values are HAND-DERIVED from that oracle expression (not produced
// by calling the helper under test), so the test is non-circular: it
// independently recomputes the oracle result 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/coin/pool_attempts_per_second.hpp>

#include <gtest/gtest.h>

namespace {

using dgb::PoolAttemptsInputs;
using dgb::compute_pool_attempts_per_second;

// Convenience: a fully-resolved window (both endpoints present).
PoolAttemptsInputs window(int32_t dist, const uint288& attempts,
uint32_t near_ts, uint32_t far_ts) {
return PoolAttemptsInputs{dist, /*near_in_chain=*/true, /*far_resolved=*/true,
attempts, near_ts, far_ts};
}

// ---- dist guard (oracle: assert dist >= 2) ------------------------------

TEST(DgbPoolAttempts, DistBelowTwoIsZero) {
// dist 1 / 0 / negative -> no window -> 0, even with attempts set.
EXPECT_EQ(compute_pool_attempts_per_second(window(1, uint288(1000), 100, 90)), uint288(0));
EXPECT_EQ(compute_pool_attempts_per_second(window(0, uint288(1000), 100, 90)), uint288(0));
EXPECT_EQ(compute_pool_attempts_per_second(window(-5, uint288(1000), 100, 90)), uint288(0));
}

TEST(DgbPoolAttempts, DistExactlyTwoIsAllowed) {
// dist == 2 is the minimum valid window; 1000 attempts over 10s -> 100.
EXPECT_EQ(compute_pool_attempts_per_second(window(2, uint288(1000), 100, 90)), uint288(100));
}

// ---- endpoint-resolution guards -----------------------------------------

TEST(DgbPoolAttempts, NearNotInChainIsZero) {
PoolAttemptsInputs in{2, /*near_in_chain=*/false, /*far_resolved=*/true,
uint288(1000), 100, 90};
EXPECT_EQ(compute_pool_attempts_per_second(in), uint288(0));
}

TEST(DgbPoolAttempts, FarUnresolvedIsZero) {
PoolAttemptsInputs in{2, /*near_in_chain=*/true, /*far_resolved=*/false,
uint288(1000), 100, 90};
EXPECT_EQ(compute_pool_attempts_per_second(in), uint288(0));
}

// ---- core divide: attempts / span ---------------------------------------

TEST(DgbPoolAttempts, AttemptsOverSpan) {
// 6000 work over (200-150)=50s -> 120 (hand-derived).
EXPECT_EQ(compute_pool_attempts_per_second(window(5, uint288(6000), 200, 150)), uint288(120));
}

TEST(DgbPoolAttempts, IntegerTruncation) {
// Oracle uses integer division: 1000 // 7 = 142 (142.857... truncated).
EXPECT_EQ(compute_pool_attempts_per_second(window(3, uint288(1000), 107, 100)), uint288(142));
}

// ---- time-span clamp (oracle: if time <= 0: time = 1) -------------------

TEST(DgbPoolAttempts, ZeroSpanClampsToOne) {
// near_ts == far_ts -> span 0 -> clamp to 1 -> attempts/1 = attempts.
EXPECT_EQ(compute_pool_attempts_per_second(window(2, uint288(777), 500, 500)), uint288(777));
}

TEST(DgbPoolAttempts, NegativeSpanClampsToOne) {
// far newer than near (span -10) -> clamp to 1 -> attempts unchanged.
EXPECT_EQ(compute_pool_attempts_per_second(window(2, uint288(555), 90, 100)), uint288(555));
}

// ---- attempts is used verbatim (work vs min_work both flow through) -----

TEST(DgbPoolAttempts, AttemptsValuePassedThroughRegardlessOfSource) {
// The function is agnostic to whether attempts came from delta.work or
// delta.min_work -- the caller selects; pin that the chosen value divides.
EXPECT_EQ(compute_pool_attempts_per_second(window(4, uint288(2000), 220, 200)), uint288(100)); // work-like
EXPECT_EQ(compute_pool_attempts_per_second(window(4, uint288(400), 220, 200)), uint288(20)); // min_work-like
}

// ---- wide numerator: no overflow truncation at 64 bits ------------------

TEST(DgbPoolAttempts, WideAttemptsNoOverflow) {
// attempts = 2^200, span 2 -> 2^199. Far beyond uint64; uint288 holds it.
uint288 big = uint288(1); big <<= 200;
uint288 half = uint288(1); half <<= 199;
EXPECT_EQ(compute_pool_attempts_per_second(window(2, big, 12, 10)), half);
}

} // namespace
Loading