Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ jobs:
core_test sharechain_test share_test btc_share_test \
test_threading test_weights \
test_header_chain test_mempool test_template_builder \
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_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 \
test_multiaddress_pplns test_pplns_stress \
test_hash_link test_decay_pplns \
test_pplns_consensus \
Expand Down Expand Up @@ -252,7 +252,7 @@ jobs:
test_threading test_weights \
test_header_chain test_mempool test_template_builder \
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_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 \
test_hash_link test_decay_pplns \
test_pplns_consensus \
test_v36_script_sorting test_v36_cross_impl_refhash \
Expand Down
21 changes: 20 additions & 1 deletion src/c2pool/main_dash.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
#include <core/netaddress.hpp> // NetService (dashd RPC endpoint)

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

// 1) Pull the template.
std::cout << "[mine] fetching block template (getblocktemplate)...\n";
dash::coin::DashWorkData work = rpc.getwork();
// Work source (S8 embedded_gbt live-wire capstone): PREFER the locally
// assembled embedded template via build_embedded_workdata, fall back to
// dashd getblocktemplate. The dashd arm is RETAINED as fallback + the
// [GBT-XCHECK] cross-check -- never removed.
//
// NOTE: NodeImpl does not yet hold the embedded coin-state (masternode
// list + mempool + header tip) that build_embedded_workdata consumes, so
// emb.has_state stays false here and the selector routes to the dashd
// fallback today. Populating that in-process state is the flagged next
// sub-slice; once it lands, set emb.has_state=true and the embedded arm
// goes live with zero change to this call site.
dash::coin::EmbeddedWorkInputs emb; // has_state=false until node-held coin-state lands
dash::coin::WorkSelection sel =
dash::coin::select_dash_work(emb, [&]{ return rpc.getwork(); });
dash::coin::DashWorkData work = std::move(sel.work);
std::cout << "[mine] work source: "
<< (sel.source == dash::coin::WorkSource::Embedded
? "EMBEDDED (build_embedded_workdata)"
: "dashd getblocktemplate (fallback)") << "\n";
std::cout << "[mine] template: height=" << work.m_height
<< " bits=0x" << std::hex << work.m_bits << std::dec
<< " prev=" << work.m_previous_block.GetHex().substr(0, 16) << "..."
Expand Down
102 changes: 102 additions & 0 deletions src/impl/dash/coin/work_source.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
#pragma once

/// Phase C-TEMPLATE step 5 (capstone): embedded-vs-dashd work-source selector.
///
/// build_embedded_workdata() (embedded_gbt.hpp) is source-complete and pinned
/// by test_dash_embedded_gbt (oracle: frstrtr/p2pool-dash getwork(),
/// older-than-v35 semantics). What was missing is a LIVE consumer: the running
/// node's get_work path must PREFER the locally-assembled embedded template and
/// fall back to dashd getblocktemplate only when the embedded coin-state bundle
/// is unavailable.
///
/// select_dash_work() is that branch point. When the EmbeddedWorkInputs bundle
/// is viable (masternode list + mempool + header-tip params all present) it
/// assembles the template LOCALLY via build_embedded_workdata() — the
/// oracle-parity path. Otherwise it invokes the supplied dashd fallback.
///
/// The external-daemon (dashd RPC) arm is NEVER removed: it is BOTH the fallback
/// AND the [GBT-XCHECK] cross-check. viable()==false must always route there.

#include <impl/dash/coin/embedded_gbt.hpp> // build_embedded_workdata
#include <impl/dash/coin/mn_state_machine.hpp> // MnStateMachine
#include <impl/dash/coin/mempool.hpp> // Mempool
#include <impl/dash/coin/rpc_data.hpp> // DashWorkData

#include <core/uint256.hpp>

#include <ctime>
#include <cstdint>
#include <functional>
#include <utility>

namespace dash {
namespace coin {

/// The inputs build_embedded_workdata() consumes, plus a validity flag. For the
/// embedded path to be taken, has_state must be true AND both pointers non-null
/// (viable()). Until NodeImpl carries embedded coin-state in-process, callers
/// leave has_state=false and the selector routes to the dashd fallback.
struct EmbeddedWorkInputs {
bool has_state{false};
uint32_t prev_height{0};
uint256 prev_hash;
const MnStateMachine* mnstates{nullptr};
const Mempool* mempool{nullptr};
uint32_t bits_for_next{0};
uint32_t mtp_at_tip{0};
uint8_t address_version{0};
uint8_t address_p2sh_version{0};
// Optional seams. 0 => use build_embedded_workdata's own SAFE-ADDITIVE
// defaults (std::time(nullptr) / 0x20000000 BIP9 baseline).
uint32_t curtime{0};
uint32_t version{0};

bool viable() const {
return has_state && mnstates != nullptr && mempool != nullptr;
}
};

enum class WorkSource { Embedded, DashdFallback };

struct WorkSelection {
DashWorkData work;
WorkSource source{WorkSource::DashdFallback};
};

/// Injectable core — the routing decision, testable without a live daemon or a
/// populated MN/mempool harness. Production binds embedded_builder to the real
/// build_embedded_workdata() closure (see the 2-arg overload below).
inline WorkSelection select_dash_work(
const EmbeddedWorkInputs& emb,
const std::function<DashWorkData()>& embedded_builder,
const std::function<DashWorkData()>& dashd_fallback)
{
if (emb.viable())
return WorkSelection{embedded_builder(), WorkSource::Embedded};
return WorkSelection{dashd_fallback(), WorkSource::DashdFallback};
}

/// Production entry point: builds the embedded template from `emb` when viable,
/// else calls `dashd_fallback` (the retained external-daemon getblocktemplate
/// arm). dashd_fallback is REQUIRED — it is the always-reachable safety path.
inline WorkSelection select_dash_work(
const EmbeddedWorkInputs& emb,
const std::function<DashWorkData()>& dashd_fallback)
{
return select_dash_work(
emb,
[&emb]() {
return build_embedded_workdata(
emb.prev_height, emb.prev_hash, *emb.mnstates, *emb.mempool,
emb.bits_for_next, emb.mtp_at_tip,
emb.address_version, emb.address_p2sh_version,
emb.curtime ? emb.curtime
: static_cast<uint32_t>(std::time(nullptr)),
emb.version ? emb.version : 0x20000000u);
},
dashd_fallback);
}

} // namespace coin
} // namespace dash
17 changes: 17 additions & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,23 @@ if (BUILD_TESTING AND GTest_FOUND)
target_link_libraries(test_dash_embedded_gbt PRIVATE c2pool_payout c2pool_hashrate c2pool_merged_mining) # OBJECT-lib SCC direct-naming (#22/#39)
gtest_add_tests(test_dash_embedded_gbt "" AUTO)

# DASH embedded-vs-dashd work-source selector (S8 embedded_gbt live-wire
# capstone): select_dash_work() prefers the locally-assembled embedded
# template (build_embedded_workdata) and falls back to dashd
# getblocktemplate when the coin-state bundle is not viable. Routing +
# retained-fallback KAT; harness-free (embedded builder injected as a stub,
# oracle-parity already pinned by test_dash_embedded_gbt). Same link set as
# the embedded_gbt capstone (work_source.hpp includes embedded_gbt.hpp).
add_executable(test_dash_work_source test_dash_work_source.cpp)
target_link_libraries(test_dash_work_source PRIVATE
GTest::gtest_main GTest::gtest
dash_x11 core
nlohmann_json::nlohmann_json
${Boost_LIBRARIES}
)
target_link_libraries(test_dash_work_source PRIVATE c2pool_payout c2pool_hashrate c2pool_merged_mining) # OBJECT-lib SCC direct-naming (#22/#39)
gtest_add_tests(test_dash_work_source "" AUTO)

# DASH CSimplifiedMNListDiff (mnlistdiff wire, S8.1 leaf): vendored diff
# round-trip + dashcore field-layout pin + apply_diff semantics. Header-only
# over the SimplifiedMNList leaf + coin/transaction; no ltc/pool SCC dep.
Expand Down
131 changes: 131 additions & 0 deletions test/test_dash_work_source.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
///
/// KAT for dash::coin::select_dash_work — the embedded-vs-dashd work-source
/// selector (S8 embedded_gbt live-wire capstone). Proves the routing contract
/// and the RETAINED dashd fallback, without a live daemon or a populated
/// MN/mempool harness (the embedded builder is injected as a stub; its
/// oracle-parity output is already pinned by test_dash_embedded_gbt).
///
/// Contract under test:
/// 1. viable() bundle -> WorkSource::Embedded, embedded builder run,
/// fallback NEVER touched.
/// 2. has_state=false -> WorkSource::DashdFallback, fallback run.
/// 3. viable but null mnstates -> fallback (defensive null-guard).
/// 4. viable but null mempool -> fallback (defensive null-guard).

#include <impl/dash/coin/work_source.hpp>

#include <gtest/gtest.h>

using dash::coin::EmbeddedWorkInputs;
using dash::coin::WorkSource;
using dash::coin::WorkSelection;
using dash::coin::select_dash_work;
using dash::coin::DashWorkData;
using dash::coin::MnStateMachine;
using dash::coin::Mempool;

namespace {

// Distinguishable sentinels so we can prove WHICH closure produced the result.
constexpr uint32_t EMB_SENTINEL_HEIGHT = 0xE3BEDDEDu & 0xffffffu; // "embedded"
constexpr uint32_t DASHD_SENTINEL_HEIGHT = 999'999u;

DashWorkData embedded_stub(bool& ran) {
ran = true;
DashWorkData w;
w.m_height = EMB_SENTINEL_HEIGHT;
return w;
}

DashWorkData dashd_stub(bool& ran) {
ran = true;
DashWorkData w;
w.m_height = DASHD_SENTINEL_HEIGHT;
return w;
}

} // namespace

// 1) Viable bundle routes to the EMBEDDED builder; fallback is not invoked.
TEST(DashWorkSource, ViableRoutesEmbedded)
{
MnStateMachine mn;
Mempool mp;
EmbeddedWorkInputs emb;
emb.has_state = true;
emb.mnstates = &mn;
emb.mempool = &mp;
ASSERT_TRUE(emb.viable());

bool emb_ran = false, fb_ran = false;
WorkSelection sel = select_dash_work(
emb,
[&] { return embedded_stub(emb_ran); },
[&] { return dashd_stub(fb_ran); });

EXPECT_EQ(sel.source, WorkSource::Embedded);
EXPECT_TRUE(emb_ran);
EXPECT_FALSE(fb_ran);
EXPECT_EQ(sel.work.m_height, EMB_SENTINEL_HEIGHT);
}

// 2) No embedded state -> the RETAINED dashd getblocktemplate fallback runs.
TEST(DashWorkSource, NoStateRoutesDashdFallback)
{
EmbeddedWorkInputs emb; // has_state defaults false
ASSERT_FALSE(emb.viable());

bool emb_ran = false, fb_ran = false;
WorkSelection sel = select_dash_work(
emb,
[&] { return embedded_stub(emb_ran); },
[&] { return dashd_stub(fb_ran); });

EXPECT_EQ(sel.source, WorkSource::DashdFallback);
EXPECT_FALSE(emb_ran);
EXPECT_TRUE(fb_ran);
EXPECT_EQ(sel.work.m_height, DASHD_SENTINEL_HEIGHT);
}

// 3) has_state true but mnstates null -> not viable -> fallback (null-guard).
TEST(DashWorkSource, NullMnStatesRoutesFallback)
{
Mempool mp;
EmbeddedWorkInputs emb;
emb.has_state = true;
emb.mnstates = nullptr;
emb.mempool = &mp;
EXPECT_FALSE(emb.viable());

bool emb_ran = false, fb_ran = false;
WorkSelection sel = select_dash_work(
emb,
[&] { return embedded_stub(emb_ran); },
[&] { return dashd_stub(fb_ran); });

EXPECT_EQ(sel.source, WorkSource::DashdFallback);
EXPECT_FALSE(emb_ran);
EXPECT_TRUE(fb_ran);
}

// 4) has_state true but mempool null -> not viable -> fallback (null-guard).
TEST(DashWorkSource, NullMempoolRoutesFallback)
{
MnStateMachine mn;
EmbeddedWorkInputs emb;
emb.has_state = true;
emb.mnstates = &mn;
emb.mempool = nullptr;
EXPECT_FALSE(emb.viable());

bool emb_ran = false, fb_ran = false;
WorkSelection sel = select_dash_work(
emb,
[&] { return embedded_stub(emb_ran); },
[&] { return dashd_stub(fb_ran); });

EXPECT_EQ(sel.source, WorkSource::DashdFallback);
EXPECT_FALSE(emb_ran);
EXPECT_TRUE(fb_ran);
}
Loading