Skip to content

Commit 8b8324d

Browse files
authored
Merge pull request #353 from frstrtr/dgb/think-p1-walk-bounds
dgb: SSOT think() Phase-1 desired-walk bounds + KAT (Phase-B pool/share)
2 parents d1f5053 + a72d596 commit 8b8324d

4 files changed

Lines changed: 139 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 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 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 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 test_coin_broadcaster test_multiaddress_pplns test_pplns_stress \
220220
v37_test \
221221
-j$(nproc)
222222

src/impl/dgb/test/CMakeLists.txt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -605,4 +605,17 @@ if (BUILD_TESTING AND GTest_FOUND)
605605
dgb_coin pool sharechain)
606606
gtest_add_tests(dgb_download_stops_test "" AUTO)
607607

608+
# dgb_think_p1_walk_bounds_test: FENCED, additive KAT pinning the think()
609+
# Phase-1 desired-set walk bounds in think_p1_walk_bounds.hpp vs the p2pool
610+
# data.py think() oracle (walk_count clamp min(5,max(0,h-CL)) / unrooted full
611+
# height; inclusive prune threshold 2*CL+10). Pure arithmetic -> links only
612+
# core. MUST also be in the build.yml --target allowlist (#143 NOT_BUILT trap).
613+
add_executable(dgb_think_p1_walk_bounds_test think_p1_walk_bounds_test.cpp)
614+
target_link_libraries(dgb_think_p1_walk_bounds_test PRIVATE
615+
GTest::gtest_main GTest::gtest
616+
core dgb
617+
c2pool_payout c2pool_merged_mining c2pool_hashrate c2pool_storage
618+
dgb_coin pool sharechain)
619+
gtest_add_tests(dgb_think_p1_walk_bounds_test "" AUTO)
620+
608621
endif()
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// dgb think() Phase-1 desired-set walk BOUNDS conformance KAT.
2+
//
3+
// FENCED, additive (no production code touched this slice). Pins
4+
// src/impl/dgb/think_p1_walk_bounds.hpp against the p2pool data.py think()
5+
// Phase-1 oracle for the two pure decisions per unverified head:
6+
// walk_count = head_height if unrooted (no last)
7+
// = min(5, max(0, head_height - CHAIN_LENGTH)) otherwise
8+
// in_pruning_zone = head_height >= 2*CHAIN_LENGTH + 10 (inclusive)
9+
//
10+
// Expectations are HAND-DERIVED from the oracle formula, NOT read back from the
11+
// code under test — a conformance KAT that asks its subject for the answer
12+
// passes vacuously. Pure arithmetic: links only core (no chain standup). MUST
13+
// appear in BOTH this dir's CMakeLists.txt AND the build.yml --target allowlist,
14+
// or it becomes a #143 NOT_BUILT sentinel (compiled-out, silently "passing").
15+
16+
#include <impl/dgb/think_p1_walk_bounds.hpp>
17+
18+
#include <gtest/gtest.h>
19+
20+
#include <cstdint>
21+
22+
namespace {
23+
24+
// ---- walk_count: unrooted head (has_last == false) → full accumulated height
25+
TEST(ThinkP1WalkBounds, UnrootedWalksFullHeight)
26+
{
27+
EXPECT_EQ(dgb::think_p1_walk_count(0, false, 10), 0);
28+
EXPECT_EQ(dgb::think_p1_walk_count(1, false, 10), 1);
29+
EXPECT_EQ(dgb::think_p1_walk_count(3, false, 10), 3);
30+
EXPECT_EQ(dgb::think_p1_walk_count(100, false, 10), 100);
31+
// chain_length must not influence the unrooted branch.
32+
EXPECT_EQ(dgb::think_p1_walk_count(7, false, 9999), 7);
33+
}
34+
35+
// ---- walk_count: rooted head (has_last == true) → min(5, max(0, h - CL))
36+
TEST(ThinkP1WalkBounds, RootedClampsAtFiveFloorsAtZero)
37+
{
38+
constexpr int32_t CL = 10;
39+
// below/at CHAIN_LENGTH → floored to 0
40+
EXPECT_EQ(dgb::think_p1_walk_count(8, true, CL), 0); // max(0,-2)=0
41+
EXPECT_EQ(dgb::think_p1_walk_count(10, true, CL), 0); // max(0, 0)=0 (boundary)
42+
// between CL and CL+5 → exact difference
43+
EXPECT_EQ(dgb::think_p1_walk_count(11, true, CL), 1); // min(5,1)=1
44+
EXPECT_EQ(dgb::think_p1_walk_count(12, true, CL), 2); // min(5,2)=2
45+
EXPECT_EQ(dgb::think_p1_walk_count(15, true, CL), 5); // min(5,5)=5 (boundary)
46+
// above CL+5 → clamped to 5
47+
EXPECT_EQ(dgb::think_p1_walk_count(16, true, CL), 5); // min(5,6)=5
48+
EXPECT_EQ(dgb::think_p1_walk_count(20, true, CL), 5); // min(5,10)=5
49+
EXPECT_EQ(dgb::think_p1_walk_count(999, true, CL), 5);
50+
}
51+
52+
// ---- pruning zone: inclusive threshold 2*CL + 10
53+
TEST(ThinkP1WalkBounds, PruningZoneInclusiveThreshold)
54+
{
55+
constexpr int32_t CL = 10; // threshold = 2*10 + 10 = 30
56+
EXPECT_FALSE(dgb::think_p1_in_pruning_zone(0, CL));
57+
EXPECT_FALSE(dgb::think_p1_in_pruning_zone(29, CL));
58+
EXPECT_TRUE (dgb::think_p1_in_pruning_zone(30, CL)); // inclusive lower bound
59+
EXPECT_TRUE (dgb::think_p1_in_pruning_zone(31, CL));
60+
EXPECT_TRUE (dgb::think_p1_in_pruning_zone(1000, CL));
61+
62+
constexpr int32_t CL2 = 24; // threshold = 2*24 + 10 = 58
63+
EXPECT_FALSE(dgb::think_p1_in_pruning_zone(57, CL2));
64+
EXPECT_TRUE (dgb::think_p1_in_pruning_zone(58, CL2));
65+
}
66+
67+
} // namespace
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
#pragma once
2+
3+
// SSOT for the think() Phase-1 desired-set walk BOUNDS — the two pure integer
4+
// decisions that govern, per unverified sharechain head, (a) how far back to
5+
// walk attempting verification and (b) whether to skip emitting a parent
6+
// (desired) request because the head already sits in the prune zone.
7+
//
8+
// These two formulas are currently OPEN-CODED 2-3x inline in share_tracker.hpp
9+
// think() Phase 1 (the walk0 branch and the for/else branch each re-derive the
10+
// pruning threshold; the walk-count clamp carries the magic literal 5). A silent
11+
// drift between the duplicated copies — or against the p2pool oracle — would
12+
// change which parents a c2pool-dgb node requests during sync, with NO compile
13+
// error. Lifting them to a single header-only SSOT lets a KAT pin the exact
14+
// arithmetic (clamp at 5, floor at 0, inclusive prune threshold 2*CL+10).
15+
//
16+
// Oracle: p2pool data.py:2077-2108 OkayTracker.think(), per unverified head:
17+
// # walk back at most a few shares trying to verify one
18+
// to_test = self.get_chain(head, min(5, max(0, height - net.CHAIN_LENGTH)))
19+
// ...
20+
// and c2pool's node-local prune-zone guard: a head at/over
21+
// 2*CHAIN_LENGTH+10 has parents that clean_tracker would immediately re-prune,
22+
// so no parent request is emitted for it.
23+
//
24+
// Per-coin isolation: dgb/ only. Header-only, additive; this slice does NOT yet
25+
// rewire share_tracker.hpp (that is the byte-identity-fenced delegation
26+
// follow-on) — it pins the bounds as free functions so the KAT exercises them
27+
// with no ShareTracker/NodeImpl standup. Consensus-neutral: pure arithmetic,
28+
// no value semantics changed.
29+
30+
#include <algorithm>
31+
#include <cstdint>
32+
33+
namespace dgb {
34+
35+
// How far back think() Phase 1 walks from an unverified head attempting to
36+
// verify one share.
37+
// has_last == false (unrooted head, no resolved segment): walk the full
38+
// accumulated height.
39+
// has_last == true: walk at most 5, and only the shares ABOVE CHAIN_LENGTH
40+
// (max(0, height - CHAIN_LENGTH)) — older shares are already settled.
41+
inline int32_t think_p1_walk_count(int32_t head_height, bool has_last,
42+
int32_t chain_length)
43+
{
44+
if (!has_last)
45+
return head_height;
46+
return std::min(5, std::max(0, head_height - chain_length));
47+
}
48+
49+
// Prune-zone guard (inclusive). A head whose accumulated height has reached
50+
// 2*CHAIN_LENGTH+10 will have any requested parent immediately re-pruned by
51+
// clean_tracker, so think() suppresses the parent (desired) request for it.
52+
inline bool think_p1_in_pruning_zone(int32_t head_height, int32_t chain_length)
53+
{
54+
return head_height >= 2 * chain_length + 10;
55+
}
56+
57+
} // namespace dgb

0 commit comments

Comments
 (0)