Skip to content

Commit 37515dc

Browse files
authored
dgb: fix silently-dead tx-relay probe in send_shares (#905) (#914)
NodeImpl::send_shares gated its remember_tx/forget_tx relay on a top-level obj->m_new_transaction_hashes that no dgb share type declares -- the field is nested in m_tx_info (dgb::ShareTxInfo). The probe was therefore always false, so DGB never forwarded a referenced new-tx hash to a peer for any share version (v17..v36). Route the collection through a new SSOT, dgb::append_share_tx_refs, which guards on m_tx_info: live for v17/v33 (which carry it), compiled out for v34/v35/v36 (which do not) -- correct-by-construction, never silently dead. Faithful port of the btc fix behind #880. Adds a fails-before KAT (dgb_share_tx_relay_refs_test) registered in the CI --target allowlist: reverting the probe to the top-level member collapses the v17/v33 expectations to zero. Co-authored-by: frstrtr <frstrtr@users.noreply.github.com>
1 parent eb8616b commit 37515dc

5 files changed

Lines changed: 145 additions & 10 deletions

File tree

.github/workflows/build.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ jobs:
156156
dgb_gentx_coinbase_test dgb_connection_coinbase_test dgb_won_block_serialize_test dgb_pplns_payout_split_test nmc_auxpow_merkle_test nmc_template_builder_test nmc_underfill_guard_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 \
157157
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 dgb_aux_doge_submit_test dgb_aux_doge_embed_livewire_test dgb_aux_doge_dc_layout_verifier_test \
158158
rpc_request_test softfork_check_test genesis_check_test algo_select_test digishield_walk_test header_chain_test \
159-
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_target_retarget_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_auto_ratchet_sim_test dgb_binomial_conf_interval_test dgb_desired_version_tally_test dgb_min_protocol_ratchet_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 dgb_emergency_decay_saturation_test dgb_arith256_muldiv_kat_test v37_test \
159+
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_target_retarget_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_auto_ratchet_sim_test dgb_binomial_conf_interval_test dgb_desired_version_tally_test dgb_min_protocol_ratchet_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 dgb_emergency_decay_saturation_test dgb_arith256_muldiv_kat_test dgb_share_tx_relay_refs_test v37_test \
160160
-j8
161161
162162
- name: Run tests
@@ -343,7 +343,7 @@ jobs:
343343
dgb_gentx_coinbase_test dgb_connection_coinbase_test dgb_won_block_serialize_test dgb_pplns_payout_split_test nmc_auxpow_merkle_test nmc_template_builder_test nmc_underfill_guard_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 \
344344
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 dgb_aux_doge_submit_test dgb_aux_doge_embed_livewire_test dgb_aux_doge_dc_layout_verifier_test \
345345
rpc_request_test softfork_check_test genesis_check_test algo_select_test digishield_walk_test header_chain_test \
346-
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_target_retarget_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_auto_ratchet_sim_test dgb_binomial_conf_interval_test dgb_desired_version_tally_test dgb_min_protocol_ratchet_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 dgb_emergency_decay_saturation_test dgb_arith256_muldiv_kat_test test_coin_broadcaster test_multiaddress_pplns test_pplns_stress \
346+
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_target_retarget_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_auto_ratchet_sim_test dgb_binomial_conf_interval_test dgb_desired_version_tally_test dgb_min_protocol_ratchet_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 dgb_emergency_decay_saturation_test dgb_arith256_muldiv_kat_test dgb_share_tx_relay_refs_test test_coin_broadcaster test_multiaddress_pplns test_pplns_stress \
347347
v37_test \
348348
-j8
349349
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// SPDX-License-Identifier: AGPL-3.0-or-later
2+
#pragma once
3+
4+
#include <vector>
5+
6+
#include <core/uint256.hpp>
7+
8+
namespace dgb
9+
{
10+
11+
// SSOT: the new-transaction hashes a share references for peer tx-relay.
12+
//
13+
// DGB shares carry these INSIDE m_tx_info (dgb::ShareTxInfo), and only on the
14+
// pre-segwit variants Share (v17) and NewShare (v33). v34/v35/v36 declare no
15+
// m_tx_info member, so the `requires { obj->m_tx_info; }` probe compiles the
16+
// body out for them: correct-by-construction, never silently dead.
17+
//
18+
// This replaces the defective `requires { obj->m_new_transaction_hashes; }`
19+
// probe in send_shares(), which named a TOP-LEVEL member NO dgb share type
20+
// declares (the field is nested in m_tx_info). That probe was ALWAYS false, so
21+
// the remember_tx/forget_tx relay block never forwarded a single tx hash to a
22+
// peer for ANY share version. Faithful port of the btc fix behind #880
23+
// (src/impl/btc/node.cpp: guard on obj->m_tx_info, iterate the nested member).
24+
template <typename ShareT>
25+
inline void append_share_tx_refs(const ShareT* obj, std::vector<uint256>& out)
26+
{
27+
if constexpr (requires { obj->m_tx_info; })
28+
for (const auto& th : obj->m_tx_info.m_new_transaction_hashes)
29+
out.push_back(th);
30+
}
31+
32+
} // namespace dgb

src/impl/dgb/node.cpp

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <impl/dgb/pool_efficiency.hpp>
1212
#include <impl/dgb/expected_time_to_block.hpp>
1313
#include <impl/dgb/coin/binomial_conf_interval.hpp>
14+
#include <impl/dgb/coin/share_tx_relay_refs.hpp> // SSOT: per-share new-tx-ref probe (#905)
1415

1516
#include <algorithm>
1617
#include <filesystem>
@@ -694,19 +695,23 @@ void NodeImpl::send_shares(peer_ptr peer, const std::vector<uint256>& share_hash
694695
if (shares.empty())
695696
return;
696697

697-
// Collect transactions that the peer doesn't know about
698+
// Collect transactions that the peer doesn't know about. A share's
699+
// referenced new-tx hashes live INSIDE m_tx_info (dgb::ShareTxInfo) on
700+
// v17/v33 and are absent on v34/v35/v36. The pre-fix probe named a
701+
// top-level m_new_transaction_hashes NO dgb share type declares, so it was
702+
// ALWAYS false and this relay block was silently dead for every version.
703+
// append_share_tx_refs is the SSOT that guards on m_tx_info (#905).
698704
std::set<uint256> needed_txs;
699705
for (auto& share : shares)
700706
{
701707
share.invoke([&](auto* obj) {
702-
if constexpr (requires { obj->m_new_transaction_hashes; })
708+
std::vector<uint256> refs;
709+
dgb::append_share_tx_refs(obj, refs);
710+
for (const auto& th : refs)
703711
{
704-
for (const auto& th : obj->m_new_transaction_hashes)
705-
{
706-
if (!peer->m_remote_txs.count(th) &&
707-
!peer->m_remembered_txs.count(th))
708-
needed_txs.insert(th);
709-
}
712+
if (!peer->m_remote_txs.count(th) &&
713+
!peer->m_remembered_txs.count(th))
714+
needed_txs.insert(th);
710715
}
711716
});
712717
}

src/impl/dgb/test/CMakeLists.txt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,22 @@ if (BUILD_TESTING AND GTest_FOUND)
1717
include_directories(${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR})
1818
gtest_add_tests(dgb_share_test "" AUTO)
1919

20+
# --- #905 tx-relay dead-probe regression (port of btc #880) ------------
21+
# Pins coin/share_tx_relay_refs.hpp: append_share_tx_refs, the SSOT
22+
# send_shares() uses to collect a share's referenced new-tx hashes for
23+
# remember_tx relay. Guards against the top-level m_new_transaction_hashes
24+
# probe that matched no dgb share type (always-false -> silently dead relay
25+
# for every version). Links the dgb OBJECT lib like dgb_share_test (pulls
26+
# share.hpp share codec). MUST appear in BOTH this registration AND the
27+
# build.yml --target allowlist (#143 NOT_BUILT trap).
28+
add_executable(dgb_share_tx_relay_refs_test share_tx_relay_refs_test.cpp)
29+
target_link_libraries(dgb_share_tx_relay_refs_test PRIVATE
30+
GTest::gtest_main GTest::gtest
31+
core dgb
32+
c2pool_payout c2pool_merged_mining c2pool_hashrate c2pool_storage
33+
dgb_coin pool sharechain)
34+
gtest_add_tests(dgb_share_tx_relay_refs_test "" AUTO)
35+
2036
# --- #82 broadcaster-gate: faithful as_block FRAMING -----------------------
2137
# Pins coin/block_assembly.hpp (share->block reassembly: merkle_root
2238
# reconstruction from the gentx hash + the share`s merkle_link, then
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
// SPDX-License-Identifier: AGPL-3.0-or-later
2+
// DGB tx-relay dead-probe regression (#905, port of the btc #880 fix).
3+
//
4+
// send_shares() collects the new-tx hashes each share references so it can
5+
// remember_tx-forward them to a peer. The pre-fix probe guarded on a TOP-LEVEL
6+
// m_new_transaction_hashes that NO dgb share type declares (the field is nested
7+
// in m_tx_info), so the collection was ALWAYS empty and DGB never relayed a tx
8+
// byte to a peer for ANY share version. This pins append_share_tx_refs, the
9+
// SSOT send_shares() now uses:
10+
// - v17/v33 (carry m_tx_info) -> the referenced hashes ARE collected
11+
// - v34/v35/v36 (no m_tx_info) -> compiled out, vacuously empty
12+
//
13+
// FAILS-BEFORE: revert append_share_tx_refs to probe obj->m_new_transaction_hashes
14+
// and the v17/v33 expectations below collapse to 0 -- the historical dead path.
15+
16+
#include <gtest/gtest.h>
17+
18+
#include <vector>
19+
20+
#include <core/uint256.hpp>
21+
#include <impl/dgb/share.hpp>
22+
#include <impl/dgb/coin/share_tx_relay_refs.hpp>
23+
24+
namespace {
25+
26+
uint256 mk(const char* hex) { uint256 h; h.SetHex(hex); return h; }
27+
28+
const char* H1 = "1111111111111111111111111111111111111111111111111111111111111111";
29+
const char* H2 = "2222222222222222222222222222222222222222222222222222222222222222";
30+
31+
// v17: m_tx_info present -> referenced hashes collected in order.
32+
TEST(DGB_tx_relay_refs, V17CollectsNestedNewTxHashes)
33+
{
34+
dgb::Share s;
35+
s.m_tx_info.m_new_transaction_hashes = {mk(H1), mk(H2)};
36+
37+
std::vector<uint256> refs;
38+
dgb::append_share_tx_refs(&s, refs);
39+
40+
ASSERT_EQ(refs.size(), 2u); // dead-probe regression makes this 0
41+
EXPECT_EQ(refs[0], mk(H1));
42+
EXPECT_EQ(refs[1], mk(H2));
43+
}
44+
45+
// v33: same nested carrier -> collected.
46+
TEST(DGB_tx_relay_refs, V33CollectsNestedNewTxHashes)
47+
{
48+
dgb::NewShare s;
49+
s.m_tx_info.m_new_transaction_hashes = {mk(H1)};
50+
51+
std::vector<uint256> refs;
52+
dgb::append_share_tx_refs(&s, refs);
53+
54+
ASSERT_EQ(refs.size(), 1u); // dead-probe regression makes this 0
55+
EXPECT_EQ(refs[0], mk(H1));
56+
}
57+
58+
// v34/v35: no m_tx_info member -> probe compiled out, vacuously empty.
59+
TEST(DGB_tx_relay_refs, SegwitVariantsCarryNoRefsByConstruction)
60+
{
61+
dgb::SegwitMiningShare v34;
62+
dgb::PaddingBugfixShare v35;
63+
64+
std::vector<uint256> refs;
65+
dgb::append_share_tx_refs(&v34, refs);
66+
dgb::append_share_tx_refs(&v35, refs);
67+
68+
EXPECT_TRUE(refs.empty());
69+
}
70+
71+
// v36 merged-mining: no m_tx_info member -> vacuously empty.
72+
TEST(DGB_tx_relay_refs, MergedMiningShareCarriesNoRefs)
73+
{
74+
dgb::MergedMiningShare v36;
75+
76+
std::vector<uint256> refs;
77+
dgb::append_share_tx_refs(&v36, refs);
78+
79+
EXPECT_TRUE(refs.empty());
80+
}
81+
82+
} // namespace

0 commit comments

Comments
 (0)