Skip to content

Commit cbeaddf

Browse files
committed
dgb: SSOT for lookbehind chain-walk window clamp (fenced KAT)
Lift the open-coded actual = min(lookbehind, height) clamp and the (actual<=0) empty-walk guard -- duplicated across share_tracker.hpp get_average_stale_prop / get_stale_counts / get_desired_version_counts / get_desired_version_weights -- into coin/chain_walk_window.hpp as chain_walk_window_count() / chain_walk_window_active(). Oracle: p2pool-dgb-scrypt forest.py Tracker.get_chain (walk at most n parents, stop at chain end) under the main.py call-site min(lookbehind, height) clamp. get_desired_version_weights rides this window into the check()-phase 60% work-weighted v36 switch gate. FENCED, additive: header-only (<algorithm>/<cstdint>), share_tracker.hpp NOT rewired -- byte-identity delegation is the follow-on. KAT 5/5 incl a non-circular dense-matrix proof that the SSOT equals the verbatim inline clamp+guard. Wired into the dir CMakeLists.txt and both build.yml --target allowlists.
1 parent 0915d1b commit cbeaddf

4 files changed

Lines changed: 178 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 nmc_mempool_name_test nmc_block_broadcast_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 dgb_aux_doge_dc_proof_test dgb_aux_doge_bind_parsers_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 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_naughty_propagation_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_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_chain_walk_window_test dgb_naughty_propagation_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 nmc_mempool_name_test nmc_block_broadcast_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 dgb_aux_doge_dc_proof_test dgb_aux_doge_bind_parsers_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 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_naughty_propagation_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_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_chain_walk_window_test dgb_naughty_propagation_test test_coin_broadcaster test_multiaddress_pplns test_pplns_stress \
220220
v37_test \
221221
-j$(nproc)
222222
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
#pragma once
2+
3+
// SSOT for the DGB LOOKBEHIND chain-walk WINDOW clamp -- the pure integer guard
4+
// every backward sharechain scan applies before calling forest.get_chain(). The
5+
// same three-line pattern is open-coded in four share_tracker.hpp accessors:
6+
//
7+
// auto height = chain.get_height(share_hash);
8+
// auto actual = std::min(static_cast<int32_t>(lookbehind), height); // clamp
9+
// if (actual <= 0) return <empty>; // guard
10+
// auto view = chain.get_chain(share_hash, actual); // walk
11+
//
12+
// get_average_stale_prop (share_tracker.hpp:2050)
13+
// get_stale_counts (share_tracker.hpp:2072)
14+
// get_desired_version_counts (share_tracker.hpp:2114)
15+
// get_desired_version_weights (share_tracker.hpp:2149)
16+
//
17+
// The clamp is the canonical p2pool windowing idiom: the caller asks for a
18+
// `lookbehind` of N shares, but a head only `height` shares deep can yield at
19+
// most `height` ancestors, so the realized window is min(lookbehind, height).
20+
// A window of zero (genesis / shallower-than-1) must short-circuit to the empty
21+
// result BEFORE the walk, exactly as the oracle does -- forest.get_chain over a
22+
// zero count yields nothing and the per-share denominators (stale_count + actual)
23+
// / weight maps degenerate.
24+
//
25+
// Oracle: p2pool-dgb-scrypt main.py status loop, which clamps every diagnostic
26+
// lookbehind at the call site --
27+
// get_average_stale_prop(tracker, best, min(720, tracker.get_height(best)))
28+
// get_desired_version_counts(tracker, best, min(self.tracker.get_height(best), ...))
29+
// -- and util/forest.py Tracker.get_chain(item_hash, n), which walks at most
30+
// `n` parents and stops at the chain end. The clamp + (actual<=0) guard are the
31+
// c2pool restatement of that min() and the implicit empty-walk.
32+
//
33+
// Two consensus-bearing consumers ride this window:
34+
// * get_desired_version_weights feeds the check()-phase 60% work-weighted v36
35+
// switch gate (share_check step 2) -- a drifted window would re-scope the
36+
// accept gate and break the p2pool-dgb-scrypt crossing-soak invariant.
37+
// * get_average_stale_prop / get_stale_counts feed efficiency diagnostics
38+
// (pool_efficiency.hpp) -- diagnostic, but pinned so a silent floor drift is
39+
// caught at compile-test time.
40+
//
41+
// Per-coin isolation: dgb/ only. Header-only, additive, free functions over the
42+
// already-resolved (height, lookbehind) pair -- the get_chain skip-list walk
43+
// stays in the forest. This slice does NOT rewire share_tracker.hpp; the four
44+
// call sites keep their inline std::min/(actual<=0) guards. That byte-identity
45+
// delegation is the follow-on (mirrors #414 redistribute rewire). The lifted
46+
// body is a verbatim copy of the inline clamp (same int32_t height, same
47+
// std::min, same <=0 comparison), so the follow-on is provably value-identical.
48+
// Consensus-neutral: pure arithmetic, no value semantics changed.
49+
50+
#include <algorithm>
51+
#include <cstdint>
52+
53+
namespace dgb {
54+
55+
// Realized lookbehind window: the number of ancestors a backward get_chain walk
56+
// from a head `height` shares deep will yield when asked for `lookbehind`.
57+
// p2pool/c2pool: actual = min(lookbehind, height)
58+
// `lookbehind` is taken as int32_t here; the two uint64_t call sites
59+
// (get_average_stale_prop / get_stale_counts) apply static_cast<int32_t> at the
60+
// call exactly as today -- the cast stays at the call, so delegation is byte-id.
61+
inline int32_t chain_walk_window_count(int32_t height, int32_t lookbehind)
62+
{
63+
return std::min(lookbehind, height);
64+
}
65+
66+
// Walk activation: the four accessors short-circuit to the empty result when the
67+
// realized window is non-positive (genesis / shallower-than-one). Returns true
68+
// when the get_chain walk should actually run.
69+
// c2pool: if (actual <= 0) return <empty>; -> runs iff actual > 0
70+
inline bool chain_walk_window_active(int32_t actual)
71+
{
72+
return actual > 0;
73+
}
74+
75+
} // namespace dgb

src/impl/dgb/test/CMakeLists.txt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -807,6 +807,20 @@ if (BUILD_TESTING AND GTest_FOUND)
807807
c2pool_payout c2pool_merged_mining c2pool_hashrate c2pool_storage
808808
dgb_coin pool sharechain)
809809
gtest_add_tests(dgb_auto_ratchet_tail_guard_test "" AUTO)
810+
# dgb_chain_walk_window_test: FENCED, additive KAT pinning the LOOKBEHIND
811+
# chain-walk WINDOW clamp in coin/chain_walk_window.hpp vs the
812+
# p2pool-dgb-scrypt oracle idiom actual = min(lookbehind, height) with the
813+
# (actual<=0) empty-walk guard, open-coded today in share_tracker.hpp
814+
# get_average_stale_prop / get_stale_counts / get_desired_version_counts /
815+
# get_desired_version_weights (forest.py get_chain under the main.py call-site
816+
# clamp). Pure header (<algorithm>/<cstdint>) -> links only GTest.
817+
# share_tracker.hpp NOT yet rewired (delegation is the byte-identity
818+
# follow-on). MUST also be in the build.yml --target allowlist
819+
# (#143 NOT_BUILT trap).
820+
add_executable(dgb_chain_walk_window_test chain_walk_window_test.cpp)
821+
target_link_libraries(dgb_chain_walk_window_test PRIVATE
822+
GTest::gtest_main GTest::gtest)
823+
gtest_add_tests(dgb_chain_walk_window_test "" AUTO)
810824

811825
# dgb_think_p4_head_keys_test: FENCED, additive KAT pinning the think()
812826
# Phase-4 head-score KEY construction in think_p4_head_keys.hpp vs the
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
// dgb lookbehind chain-walk WINDOW clamp -- KAT.
2+
//
3+
// FENCED, additive (no production code touched this slice). Pins
4+
// src/impl/dgb/coin/chain_walk_window.hpp against the p2pool-dgb-scrypt oracle
5+
// idiom that governs every backward sharechain accessor:
6+
// actual = min(lookbehind, height); if (actual <= 0) -> empty result
7+
// open-coded today in share_tracker.hpp get_average_stale_prop /
8+
// get_stale_counts / get_desired_version_counts / get_desired_version_weights,
9+
// mirroring p2pool util/forest.py Tracker.get_chain (walk at most n parents,
10+
// stop at chain end) under the main.py call-site min(lookbehind, height) clamp.
11+
//
12+
// Every expectation is hand-derived from the oracle min()/<=0 formula, NOT read
13+
// from the code under test. The final case is non-circular: it re-implements the
14+
// verbatim inline clamp+guard the four share_tracker accessors use today and
15+
// asserts the SSOT is value-identical across a dense matrix -- so the
16+
// byte-identity delegation follow-on is proven safe before it is written.
17+
// share_tracker.hpp is NOT rewired here. MUST appear in BOTH this dir
18+
// CMakeLists.txt AND the build.yml --target allowlist (#143 NOT_BUILT trap).
19+
20+
#include <impl/dgb/coin/chain_walk_window.hpp>
21+
22+
#include <algorithm>
23+
#include <gtest/gtest.h>
24+
25+
using namespace dgb;
26+
27+
// --- realized window = min(lookbehind, height) ------------------------------
28+
TEST(DgbChainWalkWindow, ClampsToHeight) {
29+
// lookbehind below height -> identity (the ask is satisfiable).
30+
EXPECT_EQ(chain_walk_window_count(/*height=*/2880, /*lookbehind=*/720), 720);
31+
EXPECT_EQ(chain_walk_window_count(100, 50), 50);
32+
// lookbehind above height -> clamped to what the chain can yield.
33+
EXPECT_EQ(chain_walk_window_count(/*height=*/30, /*lookbehind=*/720), 30);
34+
EXPECT_EQ(chain_walk_window_count(0, 720), 0);
35+
// exact boundary: equal -> that value.
36+
EXPECT_EQ(chain_walk_window_count(720, 720), 720);
37+
// one-deep chain.
38+
EXPECT_EQ(chain_walk_window_count(1, 720), 1);
39+
}
40+
41+
// --- degenerate / defensive inputs ------------------------------------------
42+
TEST(DgbChainWalkWindow, NonPositiveInputs) {
43+
// genesis head (height 0) -> zero window regardless of lookbehind.
44+
EXPECT_EQ(chain_walk_window_count(0, 1), 0);
45+
// zero lookbehind -> zero window (caller asked for nothing).
46+
EXPECT_EQ(chain_walk_window_count(2880, 0), 0);
47+
// negative height (should never occur, but min() must still pick it).
48+
EXPECT_EQ(chain_walk_window_count(-5, 720), -5);
49+
}
50+
51+
// --- activation guard: actual > 0 -------------------------------------------
52+
TEST(DgbChainWalkWindow, ActivationGuard) {
53+
// share_tracker accessors early-return the empty result when actual <= 0.
54+
EXPECT_FALSE(chain_walk_window_active(0));
55+
EXPECT_FALSE(chain_walk_window_active(-3));
56+
EXPECT_TRUE(chain_walk_window_active(1));
57+
EXPECT_TRUE(chain_walk_window_active(2880));
58+
}
59+
60+
// --- composite: the realized window is walked iff it is positive -------------
61+
TEST(DgbChainWalkWindow, ClampThenActivateComposite) {
62+
// genesis: clamp to 0, guard fails -> no walk.
63+
EXPECT_FALSE(chain_walk_window_active(chain_walk_window_count(0, 720)));
64+
// one-deep: clamp to 1, guard passes -> walk one ancestor.
65+
EXPECT_TRUE(chain_walk_window_active(chain_walk_window_count(1, 720)));
66+
// deep chain, real lookbehind: clamp to 720, walk 720.
67+
EXPECT_TRUE(chain_walk_window_active(chain_walk_window_count(5000, 720)));
68+
}
69+
70+
// --- NON-CIRCULAR: SSOT == verbatim inline clamp+guard over a dense matrix ---
71+
// Re-implements the exact three-line pattern from the four share_tracker.hpp
72+
// accessors WITHOUT calling the header, then proves the SSOT matches. This is
73+
// the safety proof for the byte-identity delegation follow-on.
74+
TEST(DgbChainWalkWindow, DelegationMatchesPreDelegationInline) {
75+
for (int32_t height = -2; height <= 64; ++height) {
76+
for (int32_t lookbehind = 0; lookbehind <= 64; ++lookbehind) {
77+
// verbatim pre-delegation inline (see share_tracker.hpp:2149-2153):
78+
int32_t inline_actual = std::min(lookbehind, height);
79+
bool inline_runs = !(inline_actual <= 0);
80+
81+
EXPECT_EQ(chain_walk_window_count(height, lookbehind), inline_actual)
82+
<< "height=" << height << " lookbehind=" << lookbehind;
83+
EXPECT_EQ(chain_walk_window_active(inline_actual), inline_runs)
84+
<< "actual=" << inline_actual;
85+
}
86+
}
87+
}

0 commit comments

Comments
 (0)