Skip to content

Commit c31add5

Browse files
authored
dash(underfill): port near-empty-template guard to DASH embedded GBT + KAT (#724)
* dash: port LTC/DOGE block-underfill guard to embedded GBT path Port the 'near-empty template on a non-empty mempool' guard from ltc/doge template_builder.hpp into dash build_embedded_workdata() (the DASH embedded template path), for the mining-hotel deployment. - Same cross-coin pins as LTC/DOGE: UNDERFILL_MIN_FILL_BYTES / UNDERFILL_BACKLOG_SLACK = 50 kB (legacy p2pool near-empty floor). - Trip condition factored into a pure predicate underfill_guard_trips(selected, mempool_bytes, known_fees) so the KAT pins the exact boolean without a log scraper. - selected_bytes accumulated from SelectedTx.base_size (DASH has no witness; base_size IS the wire size the mempool byte cap counts). - Log-only (WARNING, [GBT-EMB] UNDERFILL) exactly like LTC/DOGE: never mutates the template; masternode burn/payee projection and the dashd submitblock fallback path are untouched. - SAFE-ADDITIVE trailing seam bool* underfill_tripped = nullptr (matches the existing curtime/version seam style); every existing caller is byte-for-byte unchanged. - test_dash_underfill_guard.cpp: predicate boundary KATs (empty mempool never trips, fee-unknown backlog never trips, strict slack boundary) + build wiring KATs (fee-known backlog rejected by the stale-input window trips; drained small pool and empty pool do not; DASH payment projection asserted unchanged when tripped; default-seam call identical field-for-field). * ci(build): add test_dash_underfill_guard to build.yml --target allowlists Registered in test/CMakeLists.txt but absent from the Linux x86_64 and ASan --target lists, so it showed ***Not Run and failed those two lanes. Add it next to test_dash_embedded_gbt in both lists, mirroring the test_stratum_hotel_interim fix. --------- Co-authored-by: frstrtr <frstrtr@users.noreply.github.com>
1 parent f14e254 commit c31add5

4 files changed

Lines changed: 419 additions & 3 deletions

File tree

.github/workflows/build.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ jobs:
114114
core_test sharechain_test share_test btc_share_test \
115115
test_threading test_weights \
116116
test_header_chain test_mempool test_template_builder \
117-
test_doge_chain test_compact_blocks test_dash_x11_kat test_dash_header_chain test_dash_block_replay test_dash_conformance test_dash_subsidy test_dash_mempool test_dash_simplifiedmns test_dash_quorum test_dash_quorum_root test_dash_mn_state test_dash_embedded_gbt test_dash_smldiff test_dash_p2p_messages test_dash_p2p_connection test_dash_p2p_node test_dash_node_interface test_dash_config test_dash_broadcaster test_dash_broadcaster_full test_dash_share_hash_link test_dash_block_relay test_dash_rpc_request test_dash_rpc_conf test_dash_block_producer test_dash_embedded_relay_e2e test_dash_block_relay_plan test_dash_version_activation_latch test_dash_block_relay_binding test_dash_block_relay_dual_arm test_dash_coinbase_parity test_dash_coinbase_muldiv test_dash_donation_combined test_dash_g3_assembled test_dash_work_target test_dash_work_job_targets test_dash_stratum_binding test_dash_job_notify_roundtrip test_dash_cb_payee test_dash_stratum_notify_roundtrip test_dash_poolnode_messages test_dash_peer test_dash_share_tracker test_dash_node test_dash_share_messages test_dash_stratum_extranonce_split test_dash_stratum_submit_reassembly test_dash_difficulty_parity test_dash_auto_ratchet test_dash_min_protocol_gate test_dash_work_source test_dash_node_coin_state test_dash_coin_state_maintainer test_dash_node_embedded_wire test_dash_node_reception_wire test_dash_get_work test_dash_stratum_work_source \
117+
test_doge_chain test_compact_blocks test_dash_x11_kat test_dash_header_chain test_dash_block_replay test_dash_conformance test_dash_subsidy test_dash_mempool test_dash_simplifiedmns test_dash_quorum test_dash_quorum_root test_dash_mn_state test_dash_embedded_gbt test_dash_underfill_guard test_dash_smldiff test_dash_p2p_messages test_dash_p2p_connection test_dash_p2p_node test_dash_node_interface test_dash_config test_dash_broadcaster test_dash_broadcaster_full test_dash_share_hash_link test_dash_block_relay test_dash_rpc_request test_dash_rpc_conf test_dash_block_producer test_dash_embedded_relay_e2e test_dash_block_relay_plan test_dash_version_activation_latch test_dash_block_relay_binding test_dash_block_relay_dual_arm test_dash_coinbase_parity test_dash_coinbase_muldiv test_dash_donation_combined test_dash_g3_assembled test_dash_work_target test_dash_work_job_targets test_dash_stratum_binding test_dash_job_notify_roundtrip test_dash_cb_payee test_dash_stratum_notify_roundtrip test_dash_poolnode_messages test_dash_peer test_dash_share_tracker test_dash_node test_dash_share_messages test_dash_stratum_extranonce_split test_dash_stratum_submit_reassembly test_dash_difficulty_parity test_dash_auto_ratchet test_dash_min_protocol_gate test_dash_work_source test_dash_node_coin_state test_dash_coin_state_maintainer test_dash_node_embedded_wire test_dash_node_reception_wire test_dash_get_work test_dash_stratum_work_source \
118118
test_multiaddress_pplns test_pplns_stress \
119119
test_hash_link test_decay_pplns \
120120
test_pplns_consensus \
@@ -272,7 +272,7 @@ jobs:
272272
test_threading test_weights \
273273
test_header_chain test_mempool test_template_builder \
274274
test_doge_chain test_compact_blocks test_dash_x11_kat \
275-
test_dash_header_chain test_dash_block_replay test_dash_conformance test_dash_subsidy test_dash_mempool test_dash_simplifiedmns test_dash_quorum test_dash_quorum_root test_dash_mn_state test_dash_embedded_gbt test_dash_smldiff test_dash_p2p_messages test_dash_p2p_connection test_dash_p2p_node test_dash_node_interface test_dash_config test_dash_broadcaster test_dash_broadcaster_full test_dash_share_hash_link test_dash_block_relay test_dash_rpc_request test_dash_rpc_conf test_dash_block_producer test_dash_embedded_relay_e2e test_dash_block_relay_plan test_dash_version_activation_latch test_dash_block_relay_binding test_dash_block_relay_dual_arm test_dash_coinbase_parity test_dash_coinbase_muldiv test_dash_donation_combined test_dash_g3_assembled test_dash_work_target test_dash_work_job_targets test_dash_stratum_binding test_dash_job_notify_roundtrip test_dash_cb_payee test_dash_stratum_notify_roundtrip test_dash_poolnode_messages test_dash_peer test_dash_share_tracker test_dash_node test_dash_share_messages test_dash_stratum_extranonce_split test_dash_stratum_submit_reassembly test_dash_difficulty_parity test_dash_auto_ratchet test_dash_min_protocol_gate test_dash_work_source test_dash_node_coin_state test_dash_coin_state_maintainer test_dash_node_embedded_wire test_dash_node_reception_wire test_dash_get_work test_dash_stratum_work_source \
275+
test_dash_header_chain test_dash_block_replay test_dash_conformance test_dash_subsidy test_dash_mempool test_dash_simplifiedmns test_dash_quorum test_dash_quorum_root test_dash_mn_state test_dash_embedded_gbt test_dash_underfill_guard test_dash_smldiff test_dash_p2p_messages test_dash_p2p_connection test_dash_p2p_node test_dash_node_interface test_dash_config test_dash_broadcaster test_dash_broadcaster_full test_dash_share_hash_link test_dash_block_relay test_dash_rpc_request test_dash_rpc_conf test_dash_block_producer test_dash_embedded_relay_e2e test_dash_block_relay_plan test_dash_version_activation_latch test_dash_block_relay_binding test_dash_block_relay_dual_arm test_dash_coinbase_parity test_dash_coinbase_muldiv test_dash_donation_combined test_dash_g3_assembled test_dash_work_target test_dash_work_job_targets test_dash_stratum_binding test_dash_job_notify_roundtrip test_dash_cb_payee test_dash_stratum_notify_roundtrip test_dash_poolnode_messages test_dash_peer test_dash_share_tracker test_dash_node test_dash_share_messages test_dash_stratum_extranonce_split test_dash_stratum_submit_reassembly test_dash_difficulty_parity test_dash_auto_ratchet test_dash_min_protocol_gate test_dash_work_source test_dash_node_coin_state test_dash_coin_state_maintainer test_dash_node_embedded_wire test_dash_node_reception_wire test_dash_get_work test_dash_stratum_work_source \
276276
test_hash_link test_decay_pplns \
277277
test_pplns_consensus \
278278
test_v36_script_sorting test_v36_cross_impl_refhash \

src/impl/dash/coin/embedded_gbt.hpp

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,36 @@
5252
namespace dash {
5353
namespace coin {
5454

55+
// ── Underfill guard (v36 cutover deploy path) ───────────────────────────────
56+
// Port of the LTC/DOGE template-builder guard (src/impl/ltc/coin/
57+
// template_builder.hpp, src/impl/doge/coin/template_builder.hpp) to the DASH
58+
// embedded GBT path. Detects the "near-empty template on a non-empty mempool"
59+
// regression: the tx selector returns almost no transactions even though the
60+
// local mempool holds a substantial fee-paying backlog. c2pool-side
61+
// template-fill safety net — NOT the byte-parity KAT axis; thresholds are the
62+
// v36-native shared structure (bucket-2, standardize cross-coin) pinned to
63+
// the legacy p2pool near-empty floor (~50 kB), identical to LTC/DOGE.
64+
// DASH counts base_size bytes (no segwit), so "bytes" here are the same
65+
// serialized-tx bytes the mempool byte cap and dashcore's 2 MB limit count.
66+
inline constexpr uint64_t UNDERFILL_MIN_FILL_BYTES = 50'000ull; // < this = near-empty block
67+
inline constexpr uint64_t UNDERFILL_BACKLOG_SLACK = 50'000ull; // unselected fee-paying material that should have filled it
68+
69+
/// Pure trip predicate — the exact boolean the LTC/DOGE guards evaluate.
70+
/// Factored out so the KAT can pin it without a log scraper:
71+
/// near_empty : template packed fewer bytes than the near-empty floor
72+
/// has_backlog : the mempool holds fee-paying material (known fees > 0)
73+
/// well beyond what was selected (> selected + slack)
74+
/// Genuinely empty (or fee-unknown-only) mempools never trip.
75+
inline bool underfill_guard_trips(uint64_t selected_bytes,
76+
uint64_t mempool_bytes,
77+
uint64_t mempool_known_fees)
78+
{
79+
const bool near_empty = selected_bytes < UNDERFILL_MIN_FILL_BYTES;
80+
const bool has_backlog = mempool_known_fees > 0
81+
&& mempool_bytes > selected_bytes + UNDERFILL_BACKLOG_SLACK;
82+
return near_empty && has_backlog;
83+
}
84+
5585
/// Build the GBT-equivalent fields we currently know how to compute
5686
/// for a block at height (prev_height+1). Returns a partially-filled
5787
/// DashWorkData; missing fields documented above.
@@ -73,7 +103,13 @@ inline DashWorkData build_embedded_workdata(
73103
// unchanged (SAFE-ADDITIVE); the G1 golden KAT pins it, and a real
74104
// BIP9-deployment-aware value can later be threaded in without touching
75105
// the default header projection.
76-
uint32_t version = 0x20000000u)
106+
uint32_t version = 0x20000000u,
107+
// Seam: optional underfill-guard observation point. Defaults to nullptr so
108+
// every existing caller is byte-for-byte unchanged (SAFE-ADDITIVE); the
109+
// guard KAT passes a bool to pin the wiring without a log scraper. The
110+
// guard itself is log-only (WARNING), exactly like LTC/DOGE — it never
111+
// alters the template.
112+
bool* underfill_tripped = nullptr)
77113
{
78114
DashWorkData w;
79115
w.m_height = prev_height + 1;
@@ -104,12 +140,39 @@ inline DashWorkData build_embedded_workdata(
104140
w.m_txs.reserve(selected.size());
105141
w.m_tx_hashes.reserve(selected.size());
106142
w.m_tx_fees.reserve(selected.size());
143+
uint64_t selected_bytes = 0; // wire bytes packed into this template (underfill guard)
107144
for (auto& s : selected) {
145+
selected_bytes += s.base_size;
108146
w.m_txs.emplace_back(s.tx);
109147
w.m_tx_hashes.push_back(dash::coin::dash_txid(s.tx));
110148
w.m_tx_fees.push_back(s.fee);
111149
}
112150

151+
// ── Underfill guard ─────────────────────────────────────────────
152+
// Do not silently treat a near-empty DASH template as healthy when the
153+
// DASH mempool held fee-paying backlog that should have filled it. We
154+
// cannot fabricate transactions, so surface loudly (WARNING) for
155+
// contabo-prod-watch / the operator rather than shipping a false-empty
156+
// block as normal. Genuinely empty mempools never trip. Mirrors the
157+
// LTC/DOGE TemplateBuilder guard; additive only — masternode payment /
158+
// CbTx / superblock projection below is untouched either way.
159+
{
160+
const uint64_t mempool_bytes = static_cast<uint64_t>(mempool.byte_size());
161+
const uint64_t mempool_fees = mempool.total_known_fees();
162+
const bool tripped = underfill_guard_trips(selected_bytes,
163+
mempool_bytes,
164+
mempool_fees);
165+
if (underfill_tripped) *underfill_tripped = tripped;
166+
if (tripped) {
167+
LOG_WARNING << "[GBT-EMB] UNDERFILL: selected "
168+
<< selected.size() << " tx / " << selected_bytes
169+
<< "B into template while mempool holds " << mempool.size()
170+
<< " tx / " << mempool_bytes << "B (" << mempool_fees
171+
<< " sat fees) — near-empty block on a non-empty "
172+
<< "mempool; template-fill regression, gates cutover.";
173+
}
174+
}
175+
113176
// Platform Credit Pool burn (DIP-0027): emit OP_RETURN payment
114177
// FIRST (matches dashcore GetBlockTxOuts ordering at payments.cpp:55).
115178
// Payee uses the "!hex" raw-script convention; OP_RETURN single byte = 0x6a.

test/CMakeLists.txt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -504,6 +504,23 @@ if (BUILD_TESTING AND GTest_FOUND)
504504
target_link_libraries(test_dash_embedded_gbt PRIVATE c2pool_payout c2pool_hashrate c2pool_merged_mining) # OBJECT-lib SCC direct-naming (#22/#39)
505505
gtest_add_tests(test_dash_embedded_gbt "" AUTO)
506506

507+
# DASH underfill guard: port of the LTC/DOGE "near-empty template on a
508+
# non-empty mempool" template-builder guard to the DASH embedded GBT path
509+
# (embedded_gbt.hpp), for the mining-hotel deployment. Predicate KATs at
510+
# the pinned 50 kB floor/slack + build_embedded_workdata wiring via the
511+
# SAFE-ADDITIVE underfill_tripped seam (log-only guard; masternode
512+
# burn/payee projection asserted unchanged). Same link set as the
513+
# embedded_gbt capstone.
514+
add_executable(test_dash_underfill_guard test_dash_underfill_guard.cpp)
515+
target_link_libraries(test_dash_underfill_guard PRIVATE
516+
GTest::gtest_main GTest::gtest
517+
dash_x11 core
518+
nlohmann_json::nlohmann_json
519+
${Boost_LIBRARIES}
520+
)
521+
target_link_libraries(test_dash_underfill_guard PRIVATE c2pool_payout c2pool_hashrate c2pool_merged_mining) # OBJECT-lib SCC direct-naming (#22/#39)
522+
gtest_add_tests(test_dash_underfill_guard "" AUTO)
523+
507524
# DASH embedded-vs-dashd work-source selector (S8 embedded_gbt live-wire
508525
# capstone): select_dash_work() prefers the locally-assembled embedded
509526
# template (build_embedded_workdata) and falls back to dashd

0 commit comments

Comments
 (0)