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 dgb_desired_version_tally_test dgb_get_height_and_last_endpoints_test dgb_naughty_propagation_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 dgb_get_height_and_last_endpoints_test dgb_chain_walk_window_test dgb_naughty_propagation_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 dgb_desired_version_tally_test dgb_get_height_and_last_endpoints_test dgb_naughty_propagation_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 dgb_get_height_and_last_endpoints_test dgb_chain_walk_window_test dgb_naughty_propagation_test test_coin_broadcaster test_multiaddress_pplns test_pplns_stress \
v37_test \
-j$(nproc)

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

// SSOT for the DGB LOOKBEHIND chain-walk WINDOW clamp -- the pure integer guard
// every backward sharechain scan applies before calling forest.get_chain(). The
// same three-line pattern is open-coded in four share_tracker.hpp accessors:
//
// auto height = chain.get_height(share_hash);
// auto actual = std::min(static_cast<int32_t>(lookbehind), height); // clamp
// if (actual <= 0) return <empty>; // guard
// auto view = chain.get_chain(share_hash, actual); // walk
//
// get_average_stale_prop (share_tracker.hpp:2050)
// get_stale_counts (share_tracker.hpp:2072)
// get_desired_version_counts (share_tracker.hpp:2114)
// get_desired_version_weights (share_tracker.hpp:2149)
//
// The clamp is the canonical p2pool windowing idiom: the caller asks for a
// `lookbehind` of N shares, but a head only `height` shares deep can yield at
// most `height` ancestors, so the realized window is min(lookbehind, height).
// A window of zero (genesis / shallower-than-1) must short-circuit to the empty
// result BEFORE the walk, exactly as the oracle does -- forest.get_chain over a
// zero count yields nothing and the per-share denominators (stale_count + actual)
// / weight maps degenerate.
//
// Oracle: p2pool-dgb-scrypt main.py status loop, which clamps every diagnostic
// lookbehind at the call site --
// get_average_stale_prop(tracker, best, min(720, tracker.get_height(best)))
// get_desired_version_counts(tracker, best, min(self.tracker.get_height(best), ...))
// -- and util/forest.py Tracker.get_chain(item_hash, n), which walks at most
// `n` parents and stops at the chain end. The clamp + (actual<=0) guard are the
// c2pool restatement of that min() and the implicit empty-walk.
//
// Two consensus-bearing consumers ride this window:
// * get_desired_version_weights feeds the check()-phase 60% work-weighted v36
// switch gate (share_check step 2) -- a drifted window would re-scope the
// accept gate and break the p2pool-dgb-scrypt crossing-soak invariant.
// * get_average_stale_prop / get_stale_counts feed efficiency diagnostics
// (pool_efficiency.hpp) -- diagnostic, but pinned so a silent floor drift is
// caught at compile-test time.
//
// Per-coin isolation: dgb/ only. Header-only, additive, free functions over the
// already-resolved (height, lookbehind) pair -- the get_chain skip-list walk
// stays in the forest. This slice does NOT rewire share_tracker.hpp; the four
// call sites keep their inline std::min/(actual<=0) guards. That byte-identity
// delegation is the follow-on (mirrors #414 redistribute rewire). The lifted
// body is a verbatim copy of the inline clamp (same int32_t height, same
// std::min, same <=0 comparison), so the follow-on is provably value-identical.
// Consensus-neutral: pure arithmetic, no value semantics changed.

#include <algorithm>
#include <cstdint>

namespace dgb {

// Realized lookbehind window: the number of ancestors a backward get_chain walk
// from a head `height` shares deep will yield when asked for `lookbehind`.
// p2pool/c2pool: actual = min(lookbehind, height)
// `lookbehind` is taken as int32_t here; the two uint64_t call sites
// (get_average_stale_prop / get_stale_counts) apply static_cast<int32_t> at the
// call exactly as today -- the cast stays at the call, so delegation is byte-id.
inline int32_t chain_walk_window_count(int32_t height, int32_t lookbehind)
{
return std::min(lookbehind, height);
}

// Walk activation: the four accessors short-circuit to the empty result when the
// realized window is non-positive (genesis / shallower-than-one). Returns true
// when the get_chain walk should actually run.
// c2pool: if (actual <= 0) return <empty>; -> runs iff actual > 0
inline bool chain_walk_window_active(int32_t actual)
{
return actual > 0;
}

} // 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 @@ -822,6 +822,20 @@ if (BUILD_TESTING AND GTest_FOUND)
c2pool_payout c2pool_merged_mining c2pool_hashrate c2pool_storage
dgb_coin pool sharechain)
gtest_add_tests(dgb_auto_ratchet_tail_guard_test "" AUTO)
# dgb_chain_walk_window_test: FENCED, additive KAT pinning the LOOKBEHIND
# chain-walk WINDOW clamp in coin/chain_walk_window.hpp vs the
# p2pool-dgb-scrypt oracle idiom actual = min(lookbehind, height) with the
# (actual<=0) empty-walk guard, open-coded today in share_tracker.hpp
# get_average_stale_prop / get_stale_counts / get_desired_version_counts /
# get_desired_version_weights (forest.py get_chain under the main.py call-site
# clamp). Pure header (<algorithm>/<cstdint>) -> links only GTest.
# 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_chain_walk_window_test chain_walk_window_test.cpp)
target_link_libraries(dgb_chain_walk_window_test PRIVATE
GTest::gtest_main GTest::gtest)
gtest_add_tests(dgb_chain_walk_window_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
87 changes: 87 additions & 0 deletions src/impl/dgb/test/chain_walk_window_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// dgb lookbehind chain-walk WINDOW clamp -- KAT.
//
// FENCED, additive (no production code touched this slice). Pins
// src/impl/dgb/coin/chain_walk_window.hpp against the p2pool-dgb-scrypt oracle
// idiom that governs every backward sharechain accessor:
// actual = min(lookbehind, height); if (actual <= 0) -> empty result
// open-coded today in share_tracker.hpp get_average_stale_prop /
// get_stale_counts / get_desired_version_counts / get_desired_version_weights,
// mirroring p2pool util/forest.py Tracker.get_chain (walk at most n parents,
// stop at chain end) under the main.py call-site min(lookbehind, height) clamp.
//
// Every expectation is hand-derived from the oracle min()/<=0 formula, NOT read
// from the code under test. The final case is non-circular: it re-implements the
// verbatim inline clamp+guard the four share_tracker accessors use today and
// asserts the SSOT is value-identical across a dense matrix -- so the
// byte-identity delegation follow-on is proven safe before it is written.
// share_tracker.hpp is NOT rewired here. MUST appear in BOTH this dir
// CMakeLists.txt AND the build.yml --target allowlist (#143 NOT_BUILT trap).

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

#include <algorithm>
#include <gtest/gtest.h>

using namespace dgb;

// --- realized window = min(lookbehind, height) ------------------------------
TEST(DgbChainWalkWindow, ClampsToHeight) {
// lookbehind below height -> identity (the ask is satisfiable).
EXPECT_EQ(chain_walk_window_count(/*height=*/2880, /*lookbehind=*/720), 720);
EXPECT_EQ(chain_walk_window_count(100, 50), 50);
// lookbehind above height -> clamped to what the chain can yield.
EXPECT_EQ(chain_walk_window_count(/*height=*/30, /*lookbehind=*/720), 30);
EXPECT_EQ(chain_walk_window_count(0, 720), 0);
// exact boundary: equal -> that value.
EXPECT_EQ(chain_walk_window_count(720, 720), 720);
// one-deep chain.
EXPECT_EQ(chain_walk_window_count(1, 720), 1);
}

// --- degenerate / defensive inputs ------------------------------------------
TEST(DgbChainWalkWindow, NonPositiveInputs) {
// genesis head (height 0) -> zero window regardless of lookbehind.
EXPECT_EQ(chain_walk_window_count(0, 1), 0);
// zero lookbehind -> zero window (caller asked for nothing).
EXPECT_EQ(chain_walk_window_count(2880, 0), 0);
// negative height (should never occur, but min() must still pick it).
EXPECT_EQ(chain_walk_window_count(-5, 720), -5);
}

// --- activation guard: actual > 0 -------------------------------------------
TEST(DgbChainWalkWindow, ActivationGuard) {
// share_tracker accessors early-return the empty result when actual <= 0.
EXPECT_FALSE(chain_walk_window_active(0));
EXPECT_FALSE(chain_walk_window_active(-3));
EXPECT_TRUE(chain_walk_window_active(1));
EXPECT_TRUE(chain_walk_window_active(2880));
}

// --- composite: the realized window is walked iff it is positive -------------
TEST(DgbChainWalkWindow, ClampThenActivateComposite) {
// genesis: clamp to 0, guard fails -> no walk.
EXPECT_FALSE(chain_walk_window_active(chain_walk_window_count(0, 720)));
// one-deep: clamp to 1, guard passes -> walk one ancestor.
EXPECT_TRUE(chain_walk_window_active(chain_walk_window_count(1, 720)));
// deep chain, real lookbehind: clamp to 720, walk 720.
EXPECT_TRUE(chain_walk_window_active(chain_walk_window_count(5000, 720)));
}

// --- NON-CIRCULAR: SSOT == verbatim inline clamp+guard over a dense matrix ---
// Re-implements the exact three-line pattern from the four share_tracker.hpp
// accessors WITHOUT calling the header, then proves the SSOT matches. This is
// the safety proof for the byte-identity delegation follow-on.
TEST(DgbChainWalkWindow, DelegationMatchesPreDelegationInline) {
for (int32_t height = -2; height <= 64; ++height) {
for (int32_t lookbehind = 0; lookbehind <= 64; ++lookbehind) {
// verbatim pre-delegation inline (see share_tracker.hpp:2149-2153):
int32_t inline_actual = std::min(lookbehind, height);
bool inline_runs = !(inline_actual <= 0);

EXPECT_EQ(chain_walk_window_count(height, lookbehind), inline_actual)
<< "height=" << height << " lookbehind=" << lookbehind;
EXPECT_EQ(chain_walk_window_active(inline_actual), inline_runs)
<< "actual=" << inline_actual;
}
}
}
Loading