Skip to content

Commit 7d2887b

Browse files
committed
dgb: SSOT think() Phase-2 verification-extension walk bounds + non-circular KAT
Lift the inline want/can/get walk-bound arithmetic from share_tracker.hpp think() Phase 2 into a header-only SSOT (think_p2_walk_bounds.hpp), sibling to the Phase-1 think_p1_walk_bounds.hpp landed in #353. Pins the three integer decisions per verified head against the p2pool data.py:2098-2103 oracle: want = max(CHAIN_LENGTH - head_height, 0) can = last_height if unrooted = max(last_height - 1 - CHAIN_LENGTH, 0) otherwise get = min(want, can) FENCED, additive: dgb/ tree-local only, header-only, share_tracker.hpp is NOT yet rewired (byte-identity delegation is the follow-on). Consensus- neutral pure arithmetic. KAT (4/4) hand-derives expectations from the oracle formula, not the subject. Registered in both the dir CMakeLists.txt and both build.yml --target allowlists (avoids #143 NOT_BUILT sentinel).
1 parent ca47115 commit 7d2887b

4 files changed

Lines changed: 177 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 dgb_think_p1_walk_bounds_test dgb_think_p1_desired_emit_test dgb_think_p6_desired_cutoff_test dgb_think_p4_head_keys_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_p2_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 dgb_think_p1_walk_bounds_test dgb_think_p1_desired_emit_test dgb_think_p6_desired_cutoff_test dgb_think_p4_head_keys_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_p2_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
@@ -662,4 +662,17 @@ if (BUILD_TESTING AND GTest_FOUND)
662662
dgb_coin pool sharechain)
663663
gtest_add_tests(dgb_think_p4_head_keys_test "" AUTO)
664664

665+
# dgb_think_p2_walk_bounds_test: FENCED, additive KAT pinning the think()
666+
# Phase-2 verification-extension walk bounds in think_p2_walk_bounds.hpp vs
667+
# the p2pool data.py:2098-2103 oracle (want=max(CL-h,0); can=last_height if
668+
# unrooted else max(last_height-1-CL,0); get=min(want,can)). Pure arithmetic
669+
# -> links only core. MUST also be in build.yml --target allowlist (#143).
670+
add_executable(dgb_think_p2_walk_bounds_test think_p2_walk_bounds_test.cpp)
671+
target_link_libraries(dgb_think_p2_walk_bounds_test PRIVATE
672+
GTest::gtest_main GTest::gtest
673+
core dgb
674+
c2pool_payout c2pool_merged_mining c2pool_hashrate c2pool_storage
675+
dgb_coin pool sharechain)
676+
gtest_add_tests(dgb_think_p2_walk_bounds_test "" AUTO)
677+
665678
endif()
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
// dgb think() Phase-2 verification-extension walk BOUNDS conformance KAT.
2+
//
3+
// FENCED, additive (no production code touched this slice). Pins
4+
// src/impl/dgb/think_p2_walk_bounds.hpp against the p2pool data.py think()
5+
// Phase-2 oracle (data.py:2098-2103) for the three pure decisions per verified
6+
// head:
7+
// want = max(CHAIN_LENGTH - head_height, 0)
8+
// can = last_height if unrooted (no last_last)
9+
// = max(last_height - 1 - CHAIN_LENGTH, 0) otherwise
10+
// get = min(want, can)
11+
//
12+
// Expectations are HAND-DERIVED from the oracle formula, NOT read back from the
13+
// code under test — a conformance KAT that asks its subject for the answer
14+
// passes vacuously. Pure arithmetic: links only core (no chain standup). MUST
15+
// appear in BOTH this dir's CMakeLists.txt AND the build.yml --target allowlist,
16+
// or it becomes a #143 NOT_BUILT sentinel (compiled-out, silently "passing").
17+
18+
#include <impl/dgb/think_p2_walk_bounds.hpp>
19+
20+
#include <gtest/gtest.h>
21+
22+
#include <cstdint>
23+
24+
namespace {
25+
26+
// ---- want = max(CHAIN_LENGTH - head_height, 0): floors at 0 above CL
27+
TEST(ThinkP2WalkBounds, WantFloorsAtZeroAboveChainLength)
28+
{
29+
constexpr int32_t CL = 10;
30+
// head below CL → positive remaining-to-verify count
31+
EXPECT_EQ(dgb::think_p2_walk_bounds(0, 0, false, CL).want, 10); // max(10,0)
32+
EXPECT_EQ(dgb::think_p2_walk_bounds(3, 0, false, CL).want, 7); // max(7,0)
33+
EXPECT_EQ(dgb::think_p2_walk_bounds(9, 0, false, CL).want, 1); // max(1,0)
34+
// head at/above CL → floored to 0
35+
EXPECT_EQ(dgb::think_p2_walk_bounds(10, 0, false, CL).want, 0); // boundary
36+
EXPECT_EQ(dgb::think_p2_walk_bounds(15, 0, false, CL).want, 0); // max(-5,0)
37+
}
38+
39+
// ---- can, UNROOTED head (has_last == false) → full last_height
40+
TEST(ThinkP2WalkBounds, CanUnrootedIsFullLastHeight)
41+
{
42+
constexpr int32_t CL = 10;
43+
EXPECT_EQ(dgb::think_p2_walk_bounds(0, 0, false, CL).can, 0);
44+
EXPECT_EQ(dgb::think_p2_walk_bounds(0, 1, false, CL).can, 1);
45+
EXPECT_EQ(dgb::think_p2_walk_bounds(0, 50, false, CL).can, 50);
46+
// chain_length must not influence the unrooted `can`.
47+
EXPECT_EQ(dgb::think_p2_walk_bounds(0, 7, false, 9999).can, 7);
48+
}
49+
50+
// ---- can, ROOTED head (has_last == true) → max(last_height - 1 - CL, 0)
51+
TEST(ThinkP2WalkBounds, CanRootedAppliesOffsetAndFloor)
52+
{
53+
constexpr int32_t CL = 10;
54+
// last_height - 1 - CL below 0 → floored to 0
55+
EXPECT_EQ(dgb::think_p2_walk_bounds(0, 10, true, CL).can, 0); // max(-1,0)
56+
EXPECT_EQ(dgb::think_p2_walk_bounds(0, 11, true, CL).can, 0); // max(0,0) boundary
57+
// above the offset → exact difference
58+
EXPECT_EQ(dgb::think_p2_walk_bounds(0, 12, true, CL).can, 1); // max(1,0)
59+
EXPECT_EQ(dgb::think_p2_walk_bounds(0, 20, true, CL).can, 9); // max(9,0)
60+
EXPECT_EQ(dgb::think_p2_walk_bounds(0, 100, true, CL).can, 89);
61+
}
62+
63+
// ---- get = min(want, can): each side can dominate
64+
TEST(ThinkP2WalkBounds, GetIsMinOfWantAndCan)
65+
{
66+
constexpr int32_t CL = 10;
67+
// want dominates: head=2 → want=8; unrooted last_height=50 → can=50; get=8
68+
{
69+
auto b = dgb::think_p2_walk_bounds(2, 50, false, CL);
70+
EXPECT_EQ(b.want, 8);
71+
EXPECT_EQ(b.can, 50);
72+
EXPECT_EQ(b.get, 8);
73+
}
74+
// can dominates: head=0 → want=10; rooted last_height=14 → can=max(3,0)=3; get=3
75+
{
76+
auto b = dgb::think_p2_walk_bounds(0, 14, true, CL);
77+
EXPECT_EQ(b.want, 10);
78+
EXPECT_EQ(b.can, 3);
79+
EXPECT_EQ(b.get, 3);
80+
}
81+
// both zero → get=0 (head past CL, rooted tail under offset)
82+
{
83+
auto b = dgb::think_p2_walk_bounds(12, 11, true, CL);
84+
EXPECT_EQ(b.want, 0);
85+
EXPECT_EQ(b.can, 0);
86+
EXPECT_EQ(b.get, 0);
87+
}
88+
// equal want==can → get equals both
89+
{
90+
auto b = dgb::think_p2_walk_bounds(5, 5, false, CL); // want=5, can=5
91+
EXPECT_EQ(b.get, 5);
92+
}
93+
}
94+
95+
} // namespace
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
#pragma once
2+
3+
// SSOT for the think() Phase-2 verification-extension walk BOUNDS — the three
4+
// pure integer decisions that govern, per VERIFIED sharechain head, how many
5+
// shares think() Phase 2 walks backward attempting to extend verification:
6+
// want = how many shares below this head still need verifying,
7+
// can = how many the resolved segment actually permits fetching,
8+
// get = min(want, can) = the bounded walk count handed to get_chain().
9+
//
10+
// This formula is currently OPEN-CODED inline in share_tracker.hpp think()
11+
// Phase 2 (the want/can/get block just before the get_chain(last, to_get)
12+
// walk). A silent drift against the p2pool oracle — or against the sibling
13+
// Phase-1 walk-bounds SSOT (think_p1_walk_bounds.hpp) — would change how far a
14+
// c2pool-dgb node extends verification per think() cycle during sync, with NO
15+
// compile error. Lifting it to a single header-only SSOT lets a KAT pin the
16+
// exact arithmetic (want floor 0, the unrooted-vs-rooted `can` split, the
17+
// -1-CHAIN_LENGTH offset on the rooted branch, get = min).
18+
//
19+
// Oracle: p2pool data.py:2098-2103 OkayTracker.think(), per verified head:
20+
// want = max(self.net.CHAIN_LENGTH - head_height, 0)
21+
// can = max(last_height - 1 - self.net.CHAIN_LENGTH, 0)
22+
// if last_last_hash is not None else last_height
23+
// get = min(want, can)
24+
// (head_height = height of the verified head; last_height/last_last_hash =
25+
// the resolved tail segment's height and its parent, i.e. whether the
26+
// segment is rooted.)
27+
//
28+
// Per-coin isolation: dgb/ only. Header-only, additive; this slice does NOT yet
29+
// rewire share_tracker.hpp (that is the byte-identity-fenced delegation
30+
// follow-on) — it pins the bounds as a free function so the KAT exercises them
31+
// with no ShareTracker/NodeImpl standup. Consensus-neutral: pure arithmetic,
32+
// no value semantics changed. Sibling of think_p1_walk_bounds.hpp.
33+
34+
#include <algorithm>
35+
#include <cstdint>
36+
37+
namespace dgb {
38+
39+
// The three Phase-2 walk-bound integers, returned together so the KAT (and the
40+
// eventual delegation) can pin each independently.
41+
struct ThinkP2WalkBounds {
42+
int32_t want; // shares still needing verification below this head
43+
int32_t can; // shares the resolved segment permits fetching
44+
int32_t get; // min(want, can) — the bounded count handed to get_chain()
45+
};
46+
47+
// Compute the Phase-2 verification-extension walk bounds for one verified head.
48+
// head_height : accumulated height of the verified head.
49+
// last_height : height of the resolved tail segment (`last`).
50+
// has_last : whether the segment is rooted (last_last_hash is non-null).
51+
// false => `can` is the full last_height (unrooted: nothing
52+
// below has been settled away yet).
53+
// chain_length: net.CHAIN_LENGTH.
54+
inline ThinkP2WalkBounds think_p2_walk_bounds(int32_t head_height,
55+
int32_t last_height,
56+
bool has_last,
57+
int32_t chain_length)
58+
{
59+
int32_t want = std::max(chain_length - head_height, 0);
60+
int32_t can = has_last
61+
? std::max(last_height - 1 - chain_length, 0)
62+
: last_height;
63+
int32_t get = std::min(want, can);
64+
return {want, can, get};
65+
}
66+
67+
} // namespace dgb

0 commit comments

Comments
 (0)