From f67ef1bd63e75824530e9400b6447e87eeb09655 Mon Sep 17 00:00:00 2001 From: frstrtr Date: Wed, 15 Jul 2026 22:20:21 +0000 Subject: [PATCH] =?UTF-8?q?dash(S8):=20main=5Fdash=20--stratum=20run-path?= =?UTF-8?q?=20caller=20=E2=80=94=20StratumServer=20<->=20DASHWorkSource=20?= =?UTF-8?q?standup?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes the S8 "no run-path caller" gap: the DASHWorkSource IWorkSource seam (#706, 4a skeleton + 4b bodies) and the subscribe->notify->submit KATs (#630-634) had no production caller — a real main that constructs the work source and binds a core::StratumServer to it. This is that caller. - main_dash.cpp: --stratum [HOST:]PORT argv (SSOT parse_listen); threaded into run_node; node_coin_state -> DASHWorkSource -> StratumServer standup; won-block dispatch via the EXISTING dashd-RPC submitblock arm (rpc->submit_block_hex) — embedded P2P relay leg stays S8; UNARMED path logs loudly, never a silent drop. Destruction order: stratum_server declared before the work source it references; explicit reset() after ioc.run(). dashd-RPC fallback RETAINED (never removed). - src/c2pool/CMakeLists.txt: link dash_stratum into c2pool-dash (the 4b work_source.cpp bodies) — without it the executable failed to link (undefined DASHWorkSource ctor). Build-glue for the caller. Verified Linux x86_64: c2pool-dash links clean; --run --stratum 127.0.0.1:13333 stands up the acceptor (DASHWorkSource constructed, StratumServer bound, clean stop). subscribe->notify->submit binding is KAT-covered at the work_source seam (#630-634). No other-coin / shared- base edit; per-coin isolation held. --- src/c2pool/CMakeLists.txt | 1 + src/c2pool/main_dash.cpp | 112 +++++++++++++++++++++++++++++++++++++- 2 files changed, 110 insertions(+), 3 deletions(-) diff --git a/src/c2pool/CMakeLists.txt b/src/c2pool/CMakeLists.txt index 844092465..9fdbbc20d 100644 --- a/src/c2pool/CMakeLists.txt +++ b/src/c2pool/CMakeLists.txt @@ -315,6 +315,7 @@ target_link_libraries(c2pool-dash c2pool_payout c2pool_merged_mining c2pool_hashrate c2pool_storage c2pool_difficulty dash_x11 # DASH X11 PoW sph reference (BLAKE->...->ECHO), additive static lib dash_rpc # launcher slice-3: external-dashd-RPC client (submitblock fallback) + dash_stratum # S8: DASHWorkSource IWorkSource bodies (stratum accept-loop caller) btclibs yaml-cpp::yaml-cpp nlohmann_json::nlohmann_json diff --git a/src/c2pool/main_dash.cpp b/src/c2pool/main_dash.cpp index b26980396..85f390683 100644 --- a/src/c2pool/main_dash.cpp +++ b/src/c2pool/main_dash.cpp @@ -65,6 +65,9 @@ #include // dash::make_coin_params (already via top include) #include // uint160 payout pubkey hash +#include // core::StratumServer — miner-facing accept-loop (run-path caller) +#include // dash::stratum::DASHWorkSource — concrete core::stratum::IWorkSource + #include #include @@ -164,11 +167,16 @@ void print_banner(const char* argv0) << " " << argv0 << " --run [--coin-rpc H:P] [--coin-rpc-auth PATH]\n" << " [--testnet] [--submit-block HEX | --submit-block-file PATH]\n" << " [--listen [HOST:]PORT] [--addnode HOST:PORT]... [--connect HOST:PORT]...\n" + << " [--stratum [HOST:]PORT]\n" << " " << argv0 << " --mine-block [--coin-rpc H:P] [--coin-rpc-auth PATH]\n" << " [--testnet] [--payout-pubkey-hash HEX] [--max-nonce N]\n\n" << "Status: consensus layer live (X11 PoW, subsidy, oracle CoinParams).\n" << " --run stands up the run-loop and ARMS the external-dashd\n" << " submitblock fallback (creds from dash.conf, never on argv).\n" + << " --stratum [HOST:]PORT binds the miner-facing Stratum accept-\n" + << " loop (DASHWorkSource seam; e.g. --stratum 3333 or\n" + << " --stratum 127.0.0.1:3333); omit to disable. Won X11 blocks\n" + << " dispatch via the retained dashd-RPC submitblock arm.\n" << " --submit-block[-file] drives ONE real submitblock then exits\n" << " (the won-block-reaches-network leg); embedded P2P relay = S8.\n" << "Consensus: X11 PoW + block identity; 2.5 min spacing; 5 DASH post-V20\n" @@ -311,7 +319,8 @@ int run_selftest() // the gate. int run_node(bool testnet, const std::string& rpc_endpoint, const std::string& rpc_conf_path, const std::string& submit_hex, - const PeeringConfig& peer) + const PeeringConfig& peer, + const std::string& stratum_host, uint16_t stratum_port) { namespace io = boost::asio; @@ -327,9 +336,19 @@ int run_node(bool testnet, const std::string& rpc_endpoint, conf.port = testnet ? 19998 : 9998; // dashd default RPC ports io::io_context ioc; + + // Miner-facing Stratum acceptor handle, declared BEFORE the signal_set so + // the shutdown callback can stop it (cancel acceptor + close sessions) + // ahead of ioc.stop(). Populated below once the DASHWorkSource is built. + std::unique_ptr stratum_server; + io::signal_set signals(ioc, SIGINT, SIGTERM); - signals.async_wait([&ioc](const boost::system::error_code&, int) { + signals.async_wait([&ioc, &stratum_server](const boost::system::error_code&, int) { std::cout << "[run] shutdown signal -- stopping run-loop\n"; + // Stop stratum BEFORE ioc.stop() so the acceptor cancels and live miner + // sessions close cleanly (their pending async ops unwind on the loop). + if (stratum_server) + stratum_server->stop(); ioc.stop(); }); @@ -406,9 +425,84 @@ int run_node(bool testnet, const std::string& rpc_endpoint, // reception — version handshake + the #646 min-proto gate — is live now // via node.cpp (#656/#657). + // ── S8 miner-facing Stratum accept-loop standup (run-path caller) ───── + // This is the production caller the DASHWorkSource 4a/4b skeleton (#706) + // and the subscribe->notify->submit KATs (#630-634) were built for: a real + // main constructs the work source and binds a core::StratumServer to it. + // + // node_coin_state MUST outlive work_source and stratum_server (DASHWorkSource + // holds a non-owning const ref to it). It is declared here, in run_node's + // scope, BEFORE work_source; the explicit stratum_server.reset() after + // ioc.run() tears the acceptor down while node_coin_state is still alive. + // Default (unpopulated) bundle: populated()==false, so get_work() takes the + // retained dashd-fallback arm -- correct + documented for the 4a standup. + dash::coin::NodeCoinState node_coin_state; + + // REQUIRED always-reachable dashd-RPC fallback arm -- the safety + + // [GBT-XCHECK] cross-check path, NEVER removed (operator standing rule). For + // this skeleton standup it returns the documented set-gap default; wiring it + // from NodeRPC getblocktemplate -> DashWorkData is a deferred sub-slice. + std::function dashd_fallback = + []() -> dash::coin::DashWorkData { return dash::coin::DashWorkData{}; }; + + // Won-block dispatch: mirrors the DGB stratum_submit_fn. On a won X11 block, + // HexStr the bytes, log a [DASH-STRATUM-BLOCK] line, and dispatch via the + // EXISTING external-dashd submitblock-RPC arm (rpc->submit_block_hex). If the + // RPC arm is UNARMED (no dash.conf creds), no sink is reached (the embedded + // P2P relay arm is S8) -- log loudly and return false; never a silent drop. + dash::stratum::DASHWorkSource::SubmitBlockFn stratum_submit_fn = + [&rpc](const std::vector& block_bytes, + uint32_t height) -> bool { + const std::string block_hex = HexStr(block_bytes); + std::cout << "[DASH-STRATUM-BLOCK] won block height=" << height + << " bytes=" << block_bytes.size() + << " -- dispatching via submitblock-RPC arm\n"; + if (!rpc) { + std::cout << "[DASH-STRATUM-BLOCK] submit arm UNARMED -- reached NO " + "sink (no dashd RPC creds; embedded P2P relay is S8) -- " + "won block NOT broadcast\n"; + return false; + } + const bool ok = rpc->submit_block_hex(block_hex, /*ignore_failure=*/false); + if (!ok) + std::cout << "[DASH-STRATUM-BLOCK] dashd submitblock REJECTED the " + "won block\n"; + return ok; + }; + + // DASHWorkSource holds a non-owning ref to node_coin_state (declared above, + // same scope). The StratumServer co-owns the work source via shared_ptr. + auto work_source = std::make_shared( + node_coin_state, std::move(dashd_fallback), std::move(stratum_submit_fn), + core::stratum::StratumConfig{}); + + if (stratum_port != 0) { + stratum_server = std::make_unique( + ioc, stratum_host, stratum_port, work_source); + if (stratum_server->start()) { + std::cout << "[run] stratum listening on " << stratum_host << ":" + << stratum_port + << " (work source: DASHWorkSource 4a/4b skeleton -- X11;" + << " get_work routes to the dashd-RPC fallback until the" + << " node-held coin-state is seeded)\n"; + } else { + std::cout << "[run] stratum FAILED to bind " << stratum_host << ":" + << stratum_port << " -- stratum disabled\n"; + stratum_server.reset(); + } + } else { + std::cout << "[run] stratum disabled (no --stratum flag)\n"; + } + std::cout << "[run] run-loop up (Ctrl-C to stop); won blocks relay via the\n" "[run] dashd-RPC submitblock fallback + the embedded sharechain P2P leg.\n"; ioc.run(); + + // Tear the acceptor + sessions down while the work source and node_coin_state + // it references are still alive -- explicit reset keeps destruction order safe + // (stratum_server was declared before them, so it would otherwise outlive them). + stratum_server.reset(); + std::cout << "[run] run-loop stopped cleanly\n"; return 0; } @@ -552,6 +646,8 @@ int main(int argc, char** argv) std::string listen_raw; // --listen [HOST:]PORT (sharechain bind) std::vector addnode_raw; // --addnode HOST:PORT (persistent outbound) std::vector connect_raw; // --connect HOST:PORT (connect-only) + std::string stratum_host = "0.0.0.0"; // --stratum [HOST:]PORT bind interface (default all) + uint16_t stratum_port = 0; // 0 disables the Stratum accept-loop; --stratum sets it for (int i = 1; i < argc; ++i) { if (std::strcmp(argv[i], "--version") == 0) { std::cout << "c2pool-dash " << C2POOL_VERSION << "\n"; @@ -581,6 +677,15 @@ int main(int argc, char** argv) addnode_raw.emplace_back(argv[++i]); else if (std::strcmp(argv[i], "--connect") == 0 && i + 1 < argc) connect_raw.emplace_back(argv[++i]); + else if (std::strcmp(argv[i], "--stratum") == 0 && i + 1 < argc) { + // --stratum [HOST:]PORT -- bind a Stratum TCP listener for miners. + // Bare PORT keeps the default 0.0.0.0 bind host (parse_listen SSOT). + if (!parse_listen(argv[++i], stratum_host, stratum_port)) { + std::cout << "[run] --stratum malformed (want [HOST:]PORT): " + << argv[i] << "\n"; + return 2; + } + } // --selftest is the default; accepted explicitly for symmetry. } @@ -627,7 +732,8 @@ int main(int argc, char** argv) } peer.listen_set = true; } - return run_node(testnet, rpc_endpoint, rpc_conf_path, submit_hex, peer); + return run_node(testnet, rpc_endpoint, rpc_conf_path, submit_hex, peer, + stratum_host, stratum_port); } return run_selftest(); } \ No newline at end of file