Skip to content

Commit 60f6a3b

Browse files
authored
Merge pull request #672 from frstrtr/dash/s8-embedded-gbt-worksource
dash(S8): embedded_gbt live-wire selector + retained dashd fallback
2 parents 3a0c50d + 15520dd commit 60f6a3b

5 files changed

Lines changed: 272 additions & 3 deletions

File tree

.github/workflows/build.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ jobs:
102102
core_test sharechain_test share_test btc_share_test \
103103
test_threading test_weights \
104104
test_header_chain test_mempool test_template_builder \
105-
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_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 \
105+
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_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 \
106106
test_multiaddress_pplns test_pplns_stress \
107107
test_hash_link test_decay_pplns \
108108
test_pplns_consensus \
@@ -252,7 +252,7 @@ jobs:
252252
test_threading test_weights \
253253
test_header_chain test_mempool test_template_builder \
254254
test_doge_chain test_compact_blocks test_dash_x11_kat \
255-
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_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 \
255+
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_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 \
256256
test_hash_link test_decay_pplns \
257257
test_pplns_consensus \
258258
test_v36_script_sorting test_v36_cross_impl_refhash \

src/c2pool/main_dash.cpp

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
#include <core/netaddress.hpp> // NetService (dashd RPC endpoint)
5151

5252
#include <impl/dash/coin/rpc.hpp> // dash::coin::NodeRPC — external-dashd submitblock arm (slice 3)
53+
#include <impl/dash/coin/work_source.hpp> // dash::coin::select_dash_work -- embedded-gbt live-wire + dashd fallback (S8 capstone)
5354
#include <impl/dash/coin/rpc_conf.hpp> // dash.conf creds resolution (rpcpassword off argv)
5455
#include <impl/dash/coin/node_interface.hpp>
5556
#include <impl/dash/node.hpp> // dash::Node — sharechain pool-node (NodeBridge<NodeImpl,Legacy,Actual>)
@@ -457,7 +458,25 @@ int run_mine_block(bool testnet, const std::string& rpc_endpoint,
457458

458459
// 1) Pull the template.
459460
std::cout << "[mine] fetching block template (getblocktemplate)...\n";
460-
dash::coin::DashWorkData work = rpc.getwork();
461+
// Work source (S8 embedded_gbt live-wire capstone): PREFER the locally
462+
// assembled embedded template via build_embedded_workdata, fall back to
463+
// dashd getblocktemplate. The dashd arm is RETAINED as fallback + the
464+
// [GBT-XCHECK] cross-check -- never removed.
465+
//
466+
// NOTE: NodeImpl does not yet hold the embedded coin-state (masternode
467+
// list + mempool + header tip) that build_embedded_workdata consumes, so
468+
// emb.has_state stays false here and the selector routes to the dashd
469+
// fallback today. Populating that in-process state is the flagged next
470+
// sub-slice; once it lands, set emb.has_state=true and the embedded arm
471+
// goes live with zero change to this call site.
472+
dash::coin::EmbeddedWorkInputs emb; // has_state=false until node-held coin-state lands
473+
dash::coin::WorkSelection sel =
474+
dash::coin::select_dash_work(emb, [&]{ return rpc.getwork(); });
475+
dash::coin::DashWorkData work = std::move(sel.work);
476+
std::cout << "[mine] work source: "
477+
<< (sel.source == dash::coin::WorkSource::Embedded
478+
? "EMBEDDED (build_embedded_workdata)"
479+
: "dashd getblocktemplate (fallback)") << "\n";
461480
std::cout << "[mine] template: height=" << work.m_height
462481
<< " bits=0x" << std::hex << work.m_bits << std::dec
463482
<< " prev=" << work.m_previous_block.GetHex().substr(0, 16) << "..."

src/impl/dash/coin/work_source.hpp

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
// SPDX-License-Identifier: AGPL-3.0-or-later
2+
#pragma once
3+
4+
/// Phase C-TEMPLATE step 5 (capstone): embedded-vs-dashd work-source selector.
5+
///
6+
/// build_embedded_workdata() (embedded_gbt.hpp) is source-complete and pinned
7+
/// by test_dash_embedded_gbt (oracle: frstrtr/p2pool-dash getwork(),
8+
/// older-than-v35 semantics). What was missing is a LIVE consumer: the running
9+
/// node's get_work path must PREFER the locally-assembled embedded template and
10+
/// fall back to dashd getblocktemplate only when the embedded coin-state bundle
11+
/// is unavailable.
12+
///
13+
/// select_dash_work() is that branch point. When the EmbeddedWorkInputs bundle
14+
/// is viable (masternode list + mempool + header-tip params all present) it
15+
/// assembles the template LOCALLY via build_embedded_workdata() — the
16+
/// oracle-parity path. Otherwise it invokes the supplied dashd fallback.
17+
///
18+
/// The external-daemon (dashd RPC) arm is NEVER removed: it is BOTH the fallback
19+
/// AND the [GBT-XCHECK] cross-check. viable()==false must always route there.
20+
21+
#include <impl/dash/coin/embedded_gbt.hpp> // build_embedded_workdata
22+
#include <impl/dash/coin/mn_state_machine.hpp> // MnStateMachine
23+
#include <impl/dash/coin/mempool.hpp> // Mempool
24+
#include <impl/dash/coin/rpc_data.hpp> // DashWorkData
25+
26+
#include <core/uint256.hpp>
27+
28+
#include <ctime>
29+
#include <cstdint>
30+
#include <functional>
31+
#include <utility>
32+
33+
namespace dash {
34+
namespace coin {
35+
36+
/// The inputs build_embedded_workdata() consumes, plus a validity flag. For the
37+
/// embedded path to be taken, has_state must be true AND both pointers non-null
38+
/// (viable()). Until NodeImpl carries embedded coin-state in-process, callers
39+
/// leave has_state=false and the selector routes to the dashd fallback.
40+
struct EmbeddedWorkInputs {
41+
bool has_state{false};
42+
uint32_t prev_height{0};
43+
uint256 prev_hash;
44+
const MnStateMachine* mnstates{nullptr};
45+
const Mempool* mempool{nullptr};
46+
uint32_t bits_for_next{0};
47+
uint32_t mtp_at_tip{0};
48+
uint8_t address_version{0};
49+
uint8_t address_p2sh_version{0};
50+
// Optional seams. 0 => use build_embedded_workdata's own SAFE-ADDITIVE
51+
// defaults (std::time(nullptr) / 0x20000000 BIP9 baseline).
52+
uint32_t curtime{0};
53+
uint32_t version{0};
54+
55+
bool viable() const {
56+
return has_state && mnstates != nullptr && mempool != nullptr;
57+
}
58+
};
59+
60+
enum class WorkSource { Embedded, DashdFallback };
61+
62+
struct WorkSelection {
63+
DashWorkData work;
64+
WorkSource source{WorkSource::DashdFallback};
65+
};
66+
67+
/// Injectable core — the routing decision, testable without a live daemon or a
68+
/// populated MN/mempool harness. Production binds embedded_builder to the real
69+
/// build_embedded_workdata() closure (see the 2-arg overload below).
70+
inline WorkSelection select_dash_work(
71+
const EmbeddedWorkInputs& emb,
72+
const std::function<DashWorkData()>& embedded_builder,
73+
const std::function<DashWorkData()>& dashd_fallback)
74+
{
75+
if (emb.viable())
76+
return WorkSelection{embedded_builder(), WorkSource::Embedded};
77+
return WorkSelection{dashd_fallback(), WorkSource::DashdFallback};
78+
}
79+
80+
/// Production entry point: builds the embedded template from `emb` when viable,
81+
/// else calls `dashd_fallback` (the retained external-daemon getblocktemplate
82+
/// arm). dashd_fallback is REQUIRED — it is the always-reachable safety path.
83+
inline WorkSelection select_dash_work(
84+
const EmbeddedWorkInputs& emb,
85+
const std::function<DashWorkData()>& dashd_fallback)
86+
{
87+
return select_dash_work(
88+
emb,
89+
[&emb]() {
90+
return build_embedded_workdata(
91+
emb.prev_height, emb.prev_hash, *emb.mnstates, *emb.mempool,
92+
emb.bits_for_next, emb.mtp_at_tip,
93+
emb.address_version, emb.address_p2sh_version,
94+
emb.curtime ? emb.curtime
95+
: static_cast<uint32_t>(std::time(nullptr)),
96+
emb.version ? emb.version : 0x20000000u);
97+
},
98+
dashd_fallback);
99+
}
100+
101+
} // namespace coin
102+
} // namespace dash

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 embedded-vs-dashd work-source selector (S8 embedded_gbt live-wire
508+
# capstone): select_dash_work() prefers the locally-assembled embedded
509+
# template (build_embedded_workdata) and falls back to dashd
510+
# getblocktemplate when the coin-state bundle is not viable. Routing +
511+
# retained-fallback KAT; harness-free (embedded builder injected as a stub,
512+
# oracle-parity already pinned by test_dash_embedded_gbt). Same link set as
513+
# the embedded_gbt capstone (work_source.hpp includes embedded_gbt.hpp).
514+
add_executable(test_dash_work_source test_dash_work_source.cpp)
515+
target_link_libraries(test_dash_work_source 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_work_source PRIVATE c2pool_payout c2pool_hashrate c2pool_merged_mining) # OBJECT-lib SCC direct-naming (#22/#39)
522+
gtest_add_tests(test_dash_work_source "" AUTO)
523+
507524
# DASH CSimplifiedMNListDiff (mnlistdiff wire, S8.1 leaf): vendored diff
508525
# round-trip + dashcore field-layout pin + apply_diff semantics. Header-only
509526
# over the SimplifiedMNList leaf + coin/transaction; no ltc/pool SCC dep.

test/test_dash_work_source.cpp

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
// SPDX-License-Identifier: AGPL-3.0-or-later
2+
///
3+
/// KAT for dash::coin::select_dash_work — the embedded-vs-dashd work-source
4+
/// selector (S8 embedded_gbt live-wire capstone). Proves the routing contract
5+
/// and the RETAINED dashd fallback, without a live daemon or a populated
6+
/// MN/mempool harness (the embedded builder is injected as a stub; its
7+
/// oracle-parity output is already pinned by test_dash_embedded_gbt).
8+
///
9+
/// Contract under test:
10+
/// 1. viable() bundle -> WorkSource::Embedded, embedded builder run,
11+
/// fallback NEVER touched.
12+
/// 2. has_state=false -> WorkSource::DashdFallback, fallback run.
13+
/// 3. viable but null mnstates -> fallback (defensive null-guard).
14+
/// 4. viable but null mempool -> fallback (defensive null-guard).
15+
16+
#include <impl/dash/coin/work_source.hpp>
17+
18+
#include <gtest/gtest.h>
19+
20+
using dash::coin::EmbeddedWorkInputs;
21+
using dash::coin::WorkSource;
22+
using dash::coin::WorkSelection;
23+
using dash::coin::select_dash_work;
24+
using dash::coin::DashWorkData;
25+
using dash::coin::MnStateMachine;
26+
using dash::coin::Mempool;
27+
28+
namespace {
29+
30+
// Distinguishable sentinels so we can prove WHICH closure produced the result.
31+
constexpr uint32_t EMB_SENTINEL_HEIGHT = 0xE3BEDDEDu & 0xffffffu; // "embedded"
32+
constexpr uint32_t DASHD_SENTINEL_HEIGHT = 999'999u;
33+
34+
DashWorkData embedded_stub(bool& ran) {
35+
ran = true;
36+
DashWorkData w;
37+
w.m_height = EMB_SENTINEL_HEIGHT;
38+
return w;
39+
}
40+
41+
DashWorkData dashd_stub(bool& ran) {
42+
ran = true;
43+
DashWorkData w;
44+
w.m_height = DASHD_SENTINEL_HEIGHT;
45+
return w;
46+
}
47+
48+
} // namespace
49+
50+
// 1) Viable bundle routes to the EMBEDDED builder; fallback is not invoked.
51+
TEST(DashWorkSource, ViableRoutesEmbedded)
52+
{
53+
MnStateMachine mn;
54+
Mempool mp;
55+
EmbeddedWorkInputs emb;
56+
emb.has_state = true;
57+
emb.mnstates = &mn;
58+
emb.mempool = &mp;
59+
ASSERT_TRUE(emb.viable());
60+
61+
bool emb_ran = false, fb_ran = false;
62+
WorkSelection sel = select_dash_work(
63+
emb,
64+
[&] { return embedded_stub(emb_ran); },
65+
[&] { return dashd_stub(fb_ran); });
66+
67+
EXPECT_EQ(sel.source, WorkSource::Embedded);
68+
EXPECT_TRUE(emb_ran);
69+
EXPECT_FALSE(fb_ran);
70+
EXPECT_EQ(sel.work.m_height, EMB_SENTINEL_HEIGHT);
71+
}
72+
73+
// 2) No embedded state -> the RETAINED dashd getblocktemplate fallback runs.
74+
TEST(DashWorkSource, NoStateRoutesDashdFallback)
75+
{
76+
EmbeddedWorkInputs emb; // has_state defaults false
77+
ASSERT_FALSE(emb.viable());
78+
79+
bool emb_ran = false, fb_ran = false;
80+
WorkSelection sel = select_dash_work(
81+
emb,
82+
[&] { return embedded_stub(emb_ran); },
83+
[&] { return dashd_stub(fb_ran); });
84+
85+
EXPECT_EQ(sel.source, WorkSource::DashdFallback);
86+
EXPECT_FALSE(emb_ran);
87+
EXPECT_TRUE(fb_ran);
88+
EXPECT_EQ(sel.work.m_height, DASHD_SENTINEL_HEIGHT);
89+
}
90+
91+
// 3) has_state true but mnstates null -> not viable -> fallback (null-guard).
92+
TEST(DashWorkSource, NullMnStatesRoutesFallback)
93+
{
94+
Mempool mp;
95+
EmbeddedWorkInputs emb;
96+
emb.has_state = true;
97+
emb.mnstates = nullptr;
98+
emb.mempool = &mp;
99+
EXPECT_FALSE(emb.viable());
100+
101+
bool emb_ran = false, fb_ran = false;
102+
WorkSelection sel = select_dash_work(
103+
emb,
104+
[&] { return embedded_stub(emb_ran); },
105+
[&] { return dashd_stub(fb_ran); });
106+
107+
EXPECT_EQ(sel.source, WorkSource::DashdFallback);
108+
EXPECT_FALSE(emb_ran);
109+
EXPECT_TRUE(fb_ran);
110+
}
111+
112+
// 4) has_state true but mempool null -> not viable -> fallback (null-guard).
113+
TEST(DashWorkSource, NullMempoolRoutesFallback)
114+
{
115+
MnStateMachine mn;
116+
EmbeddedWorkInputs emb;
117+
emb.has_state = true;
118+
emb.mnstates = &mn;
119+
emb.mempool = nullptr;
120+
EXPECT_FALSE(emb.viable());
121+
122+
bool emb_ran = false, fb_ran = false;
123+
WorkSelection sel = select_dash_work(
124+
emb,
125+
[&] { return embedded_stub(emb_ran); },
126+
[&] { return dashd_stub(fb_ran); });
127+
128+
EXPECT_EQ(sel.source, WorkSource::DashdFallback);
129+
EXPECT_FALSE(emb_ran);
130+
EXPECT_TRUE(fb_ran);
131+
}

0 commit comments

Comments
 (0)