diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7b6a08a01..588f24b20 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -94,7 +94,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 nmc_host_dualpath_test nmc_fallback_path_conformance_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_mm_commitment_test dgb_aux_doge_dc_proof_test dgb_aux_doge_bind_parsers_test dgb_compact_blocks_bip152_parity_test dgb_aux_dual_target_select_test dgb_aux_broadcast_path_election_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_chain_walk_window_test dgb_redistribute_delegate_ghal_test dgb_share_weight_decay_test dgb_naughty_propagation_test dgb_hash_format_parity_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_share_bits_oracle_pin_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_redistribute_delegate_ghal_test dgb_share_weight_decay_test dgb_naughty_propagation_test dgb_hash_format_parity_test v37_test \ -j8 - name: Run tests @@ -237,7 +237,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 nmc_host_dualpath_test nmc_fallback_path_conformance_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_mm_commitment_test dgb_aux_doge_dc_proof_test dgb_aux_doge_bind_parsers_test dgb_compact_blocks_bip152_parity_test dgb_aux_dual_target_select_test dgb_aux_broadcast_path_election_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_chain_walk_window_test dgb_redistribute_delegate_ghal_test dgb_share_weight_decay_test dgb_naughty_propagation_test dgb_hash_format_parity_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_share_bits_oracle_pin_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_redistribute_delegate_ghal_test dgb_share_weight_decay_test dgb_naughty_propagation_test dgb_hash_format_parity_test test_coin_broadcaster test_multiaddress_pplns test_pplns_stress \ v37_test \ -j8 diff --git a/src/impl/dgb/test/CMakeLists.txt b/src/impl/dgb/test/CMakeLists.txt index dbdcd5137..8cbf590c5 100644 --- a/src/impl/dgb/test/CMakeLists.txt +++ b/src/impl/dgb/test/CMakeLists.txt @@ -665,6 +665,19 @@ if (BUILD_TESTING AND GTest_FOUND) dgb_coin pool sharechain) gtest_add_tests(dgb_share_target_genesis_test "" AUTO) + # dgb_share_bits_oracle_pin_test: FENCED Phase-B KAT. Pins the sharechain + # `bits` (compact FloatingInteger) encoding against LITERAL goldens from the + # oracle reference encoder (data.py from_target_upper_bound), removing the + # SUT-vs-SUT circularity in dgb_share_target_genesis_test. MUST also be in + # the build.yml --target allowlist (#143). + add_executable(dgb_share_bits_oracle_pin_test share_bits_oracle_pin_test.cpp) + target_link_libraries(dgb_share_bits_oracle_pin_test PRIVATE + GTest::gtest_main GTest::gtest + core dgb + c2pool_payout c2pool_merged_mining c2pool_hashrate c2pool_storage + dgb_coin pool sharechain) + gtest_add_tests(dgb_share_bits_oracle_pin_test "" AUTO) + # dgb_g1_oracle_byte_parity_test: FENCED greenlight-gate G1 KAT. Pins the # assembled make_coin_params() net/consensus constants AND bucket-1 # isolation primitives (PREFIX/IDENTIFIER) against oracle-transcribed diff --git a/src/impl/dgb/test/share_bits_oracle_pin_test.cpp b/src/impl/dgb/test/share_bits_oracle_pin_test.cpp new file mode 100644 index 000000000..745720cb9 --- /dev/null +++ b/src/impl/dgb/test/share_bits_oracle_pin_test.cpp @@ -0,0 +1,120 @@ +// dgb sharechain `bits` (compact FloatingInteger) encoding — oracle-LITERAL pin. +// +// FENCED conformance KAT (Phase B, pool/share layer; no production code touched). +// +// Pins the DGB sharechain share-difficulty `bits` field against LITERAL goldens +// produced by the oracle's own reference encoder — frstrtr/p2pool-dgb-scrypt +// bitcoin/data.py FloatingInteger.from_target_upper_bound (data.py:64-70): +// +// n = math.natural_to_string(target) # big-endian, minimal, no +// # leading zero byte +// if n and ord(n[0]) >= 128: n = '\x00' + n # sign-pad high bit +// bits2 = (chr(len(n)) + (n + 3*chr(0))[:3])[::-1] +// bits = pack.IntType(32).unpack(bits2) # => (len(n)<<24)|mant24 +// +// This is the encoder the oracle applies at data.py:169-170 to set BOTH +// share_info['max_bits'] (= from_target_upper_bound(pre_target3)) and the +// per-share difficulty `bits` (= from_target_upper_bound(clip(desired_target, +// (pre_target3//30, pre_target3)))). It is the on-wire encoding of sharechain +// difficulty, so byte-drift here desyncs the whole pool/share layer from the +// oracle network. +// +// WHY a separate slice from dgb_share_target_genesis_test: that KAT asserts +// compute_share_target() == chain::target_to_bits_upper_bound(), +// i.e. SUT-vs-SUT (self-consistent but circular w.r.t. the oracle). This KAT +// pins the ABSOLUTE compact value at each canonical retarget anchor against the +// oracle reference encoder, removing the circularity: if both the SUT encoder +// and compute_share_target() drifted in lockstep, the genesis KAT would still +// pass but THESE literals would fail. +// +// Goldens (independently computed from the oracle algorithm above; MAX_TARGET = +// 2**256//2**20 - 1 = 2^236 - 1 per networks/digibyte.py): +// target = MAX_TARGET (2^236-1) -> bits = 0x1e0fffff +// target = MAX_TARGET / 30 (floor) -> bits = 0x1e008888 +// target = MAX_TARGET / 2 -> bits = 0x1e07ffff +// target = 0 (MIN_TARGET) -> bits = 0x00000000 +// +// 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. + +#include +#include +#include +#include + +#include + +namespace { + +// Oracle-LITERAL goldens — see header block. Computed from +// FloatingInteger.from_target_upper_bound, NOT from the SUT encoder. +constexpr uint32_t ORACLE_BITS_MAX_TARGET = 0x1e0fffffu; // 2^236 - 1 +constexpr uint32_t ORACLE_BITS_MAX_OVER_30 = 0x1e008888u; // genesis bits floor +constexpr uint32_t ORACLE_BITS_MAX_OVER_2 = 0x1e07ffffu; // mid-range +constexpr uint32_t ORACLE_BITS_MIN_TARGET = 0x00000000u; // MIN_TARGET = 0 + +// The genesis branch ignores desired_timestamp; any fixed value is fine. +constexpr uint32_t kTs = 1700000000u; + +uint256 all_ones() { + uint256 t; + t.SetHex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); + return t; +} + +// ---- Layer 1: the standalone compact encoder vs oracle literals ------------- + +TEST(DgbShareBitsOraclePin, MaxTargetEncodesTo1e0fffff) { + const uint256 MAX_TARGET = dgb::PoolConfig::max_target(); + EXPECT_EQ(chain::target_to_bits_upper_bound(MAX_TARGET), ORACLE_BITS_MAX_TARGET); +} + +TEST(DgbShareBitsOraclePin, MaxOver30EncodesTo1e008888) { + const uint256 MAX_TARGET = dgb::PoolConfig::max_target(); + EXPECT_EQ(chain::target_to_bits_upper_bound(MAX_TARGET / 30), ORACLE_BITS_MAX_OVER_30); +} + +TEST(DgbShareBitsOraclePin, MaxOver2EncodesTo1e07ffff) { + const uint256 MAX_TARGET = dgb::PoolConfig::max_target(); + EXPECT_EQ(chain::target_to_bits_upper_bound(MAX_TARGET / 2), ORACLE_BITS_MAX_OVER_2); +} + +TEST(DgbShareBitsOraclePin, MinTargetEncodesToZero) { + EXPECT_EQ(chain::target_to_bits_upper_bound(uint256()), ORACLE_BITS_MIN_TARGET); +} + +// ---- Layer 2: compute_share_target genesis ladder emits the oracle literals -- +// Pins that the genesis retarget path (previous_share unknown -> pre_target3 = +// MAX_TARGET, then bits = from_target_upper_bound(clip(desired, MAX/30..MAX))) +// produces the ABSOLUTE oracle-literal compact bits, not merely a SUT-consistent +// value. This is what the on-wire share carries. + +TEST(DgbShareBitsOraclePin, GenesisMaxBitsAreOracleLiteral) { + dgb::ShareTracker tracker; + const uint256 MAX_TARGET = dgb::PoolConfig::max_target(); + auto st = tracker.compute_share_target(uint256(), kTs, MAX_TARGET); + EXPECT_EQ(st.max_bits, ORACLE_BITS_MAX_TARGET); +} + +TEST(DgbShareBitsOraclePin, GenesisDesiredAboveMaxClampsToOracleMaxBits) { + dgb::ShareTracker tracker; + auto st = tracker.compute_share_target(uint256(), kTs, all_ones()); + EXPECT_EQ(st.bits, ORACLE_BITS_MAX_TARGET); + EXPECT_EQ(st.bits, st.max_bits); +} + +TEST(DgbShareBitsOraclePin, GenesisDesiredBelowFloorClampsToOracleMaxOver30) { + dgb::ShareTracker tracker; + // desired_target = 0 -> clipped up to MAX_TARGET/30 (data.py min clip). + auto st = tracker.compute_share_target(uint256(), kTs, uint256()); + EXPECT_EQ(st.bits, ORACLE_BITS_MAX_OVER_30); +} + +TEST(DgbShareBitsOraclePin, GenesisDesiredInRangeEncodesToOracleMaxOver2) { + dgb::ShareTracker tracker; + const uint256 MAX_TARGET = dgb::PoolConfig::max_target(); + auto st = tracker.compute_share_target(uint256(), kTs, MAX_TARGET / 2); + EXPECT_EQ(st.bits, ORACLE_BITS_MAX_OVER_2); +} + +} // namespace