6565#include < impl/dash/params.hpp> // dash::make_coin_params (already via top include)
6666#include < core/uint256.hpp> // uint160 payout pubkey hash
6767
68+ #include < core/stratum_server.hpp> // core::StratumServer — miner-facing accept-loop (run-path caller)
69+ #include < impl/dash/stratum/work_source.hpp> // dash::stratum::DASHWorkSource — concrete core::stratum::IWorkSource
70+
6871#include < boost/asio.hpp>
6972
7073#include < cstdint>
@@ -164,11 +167,16 @@ void print_banner(const char* argv0)
164167 << " " << argv0 << " --run [--coin-rpc H:P] [--coin-rpc-auth PATH]\n "
165168 << " [--testnet] [--submit-block HEX | --submit-block-file PATH]\n "
166169 << " [--listen [HOST:]PORT] [--addnode HOST:PORT]... [--connect HOST:PORT]...\n "
170+ << " [--stratum [HOST:]PORT]\n "
167171 << " " << argv0 << " --mine-block [--coin-rpc H:P] [--coin-rpc-auth PATH]\n "
168172 << " [--testnet] [--payout-pubkey-hash HEX] [--max-nonce N]\n\n "
169173 << " Status: consensus layer live (X11 PoW, subsidy, oracle CoinParams).\n "
170174 << " --run stands up the run-loop and ARMS the external-dashd\n "
171175 << " submitblock fallback (creds from dash.conf, never on argv).\n "
176+ << " --stratum [HOST:]PORT binds the miner-facing Stratum accept-\n "
177+ << " loop (DASHWorkSource seam; e.g. --stratum 3333 or\n "
178+ << " --stratum 127.0.0.1:3333); omit to disable. Won X11 blocks\n "
179+ << " dispatch via the retained dashd-RPC submitblock arm.\n "
172180 << " --submit-block[-file] drives ONE real submitblock then exits\n "
173181 << " (the won-block-reaches-network leg); embedded P2P relay = S8.\n "
174182 << " Consensus: X11 PoW + block identity; 2.5 min spacing; 5 DASH post-V20\n "
@@ -311,7 +319,8 @@ int run_selftest()
311319// the gate.
312320int run_node (bool testnet, const std::string& rpc_endpoint,
313321 const std::string& rpc_conf_path, const std::string& submit_hex,
314- const PeeringConfig& peer)
322+ const PeeringConfig& peer,
323+ const std::string& stratum_host, uint16_t stratum_port)
315324{
316325 namespace io = boost::asio;
317326
@@ -327,9 +336,19 @@ int run_node(bool testnet, const std::string& rpc_endpoint,
327336 conf.port = testnet ? 19998 : 9998 ; // dashd default RPC ports
328337
329338 io::io_context ioc;
339+
340+ // Miner-facing Stratum acceptor handle, declared BEFORE the signal_set so
341+ // the shutdown callback can stop it (cancel acceptor + close sessions)
342+ // ahead of ioc.stop(). Populated below once the DASHWorkSource is built.
343+ std::unique_ptr<core::StratumServer> stratum_server;
344+
330345 io::signal_set signals (ioc, SIGINT , SIGTERM );
331- signals.async_wait ([&ioc](const boost::system::error_code&, int ) {
346+ signals.async_wait ([&ioc, &stratum_server ](const boost::system::error_code&, int ) {
332347 std::cout << " [run] shutdown signal -- stopping run-loop\n " ;
348+ // Stop stratum BEFORE ioc.stop() so the acceptor cancels and live miner
349+ // sessions close cleanly (their pending async ops unwind on the loop).
350+ if (stratum_server)
351+ stratum_server->stop ();
333352 ioc.stop ();
334353 });
335354
@@ -406,9 +425,84 @@ int run_node(bool testnet, const std::string& rpc_endpoint,
406425 // reception — version handshake + the #646 min-proto gate — is live now
407426 // via node.cpp (#656/#657).
408427
428+ // ── S8 miner-facing Stratum accept-loop standup (run-path caller) ─────
429+ // This is the production caller the DASHWorkSource 4a/4b skeleton (#706)
430+ // and the subscribe->notify->submit KATs (#630-634) were built for: a real
431+ // main constructs the work source and binds a core::StratumServer to it.
432+ //
433+ // node_coin_state MUST outlive work_source and stratum_server (DASHWorkSource
434+ // holds a non-owning const ref to it). It is declared here, in run_node's
435+ // scope, BEFORE work_source; the explicit stratum_server.reset() after
436+ // ioc.run() tears the acceptor down while node_coin_state is still alive.
437+ // Default (unpopulated) bundle: populated()==false, so get_work() takes the
438+ // retained dashd-fallback arm -- correct + documented for the 4a standup.
439+ dash::coin::NodeCoinState node_coin_state;
440+
441+ // REQUIRED always-reachable dashd-RPC fallback arm -- the safety +
442+ // [GBT-XCHECK] cross-check path, NEVER removed (operator standing rule). For
443+ // this skeleton standup it returns the documented set-gap default; wiring it
444+ // from NodeRPC getblocktemplate -> DashWorkData is a deferred sub-slice.
445+ std::function<dash::coin::DashWorkData ()> dashd_fallback =
446+ []() -> dash::coin::DashWorkData { return dash::coin::DashWorkData{}; };
447+
448+ // Won-block dispatch: mirrors the DGB stratum_submit_fn. On a won X11 block,
449+ // HexStr the bytes, log a [DASH-STRATUM-BLOCK] line, and dispatch via the
450+ // EXISTING external-dashd submitblock-RPC arm (rpc->submit_block_hex). If the
451+ // RPC arm is UNARMED (no dash.conf creds), no sink is reached (the embedded
452+ // P2P relay arm is S8) -- log loudly and return false; never a silent drop.
453+ dash::stratum::DASHWorkSource::SubmitBlockFn stratum_submit_fn =
454+ [&rpc](const std::vector<unsigned char >& block_bytes,
455+ uint32_t height) -> bool {
456+ const std::string block_hex = HexStr (block_bytes);
457+ std::cout << " [DASH-STRATUM-BLOCK] won block height=" << height
458+ << " bytes=" << block_bytes.size ()
459+ << " -- dispatching via submitblock-RPC arm\n " ;
460+ if (!rpc) {
461+ std::cout << " [DASH-STRATUM-BLOCK] submit arm UNARMED -- reached NO "
462+ " sink (no dashd RPC creds; embedded P2P relay is S8) -- "
463+ " won block NOT broadcast\n " ;
464+ return false ;
465+ }
466+ const bool ok = rpc->submit_block_hex (block_hex, /* ignore_failure=*/ false );
467+ if (!ok)
468+ std::cout << " [DASH-STRATUM-BLOCK] dashd submitblock REJECTED the "
469+ " won block\n " ;
470+ return ok;
471+ };
472+
473+ // DASHWorkSource holds a non-owning ref to node_coin_state (declared above,
474+ // same scope). The StratumServer co-owns the work source via shared_ptr.
475+ auto work_source = std::make_shared<dash::stratum::DASHWorkSource>(
476+ node_coin_state, std::move (dashd_fallback), std::move (stratum_submit_fn),
477+ core::stratum::StratumConfig{});
478+
479+ if (stratum_port != 0 ) {
480+ stratum_server = std::make_unique<core::StratumServer>(
481+ ioc, stratum_host, stratum_port, work_source);
482+ if (stratum_server->start ()) {
483+ std::cout << " [run] stratum listening on " << stratum_host << " :"
484+ << stratum_port
485+ << " (work source: DASHWorkSource 4a/4b skeleton -- X11;"
486+ << " get_work routes to the dashd-RPC fallback until the"
487+ << " node-held coin-state is seeded)\n " ;
488+ } else {
489+ std::cout << " [run] stratum FAILED to bind " << stratum_host << " :"
490+ << stratum_port << " -- stratum disabled\n " ;
491+ stratum_server.reset ();
492+ }
493+ } else {
494+ std::cout << " [run] stratum disabled (no --stratum flag)\n " ;
495+ }
496+
409497 std::cout << " [run] run-loop up (Ctrl-C to stop); won blocks relay via the\n "
410498 " [run] dashd-RPC submitblock fallback + the embedded sharechain P2P leg.\n " ;
411499 ioc.run ();
500+
501+ // Tear the acceptor + sessions down while the work source and node_coin_state
502+ // it references are still alive -- explicit reset keeps destruction order safe
503+ // (stratum_server was declared before them, so it would otherwise outlive them).
504+ stratum_server.reset ();
505+
412506 std::cout << " [run] run-loop stopped cleanly\n " ;
413507 return 0 ;
414508}
@@ -552,6 +646,8 @@ int main(int argc, char** argv)
552646 std::string listen_raw; // --listen [HOST:]PORT (sharechain bind)
553647 std::vector<std::string> addnode_raw; // --addnode HOST:PORT (persistent outbound)
554648 std::vector<std::string> connect_raw; // --connect HOST:PORT (connect-only)
649+ std::string stratum_host = " 0.0.0.0" ; // --stratum [HOST:]PORT bind interface (default all)
650+ uint16_t stratum_port = 0 ; // 0 disables the Stratum accept-loop; --stratum sets it
555651 for (int i = 1 ; i < argc; ++i) {
556652 if (std::strcmp (argv[i], " --version" ) == 0 ) {
557653 std::cout << " c2pool-dash " << C2POOL_VERSION << " \n " ;
@@ -581,6 +677,15 @@ int main(int argc, char** argv)
581677 addnode_raw.emplace_back (argv[++i]);
582678 else if (std::strcmp (argv[i], " --connect" ) == 0 && i + 1 < argc)
583679 connect_raw.emplace_back (argv[++i]);
680+ else if (std::strcmp (argv[i], " --stratum" ) == 0 && i + 1 < argc) {
681+ // --stratum [HOST:]PORT -- bind a Stratum TCP listener for miners.
682+ // Bare PORT keeps the default 0.0.0.0 bind host (parse_listen SSOT).
683+ if (!parse_listen (argv[++i], stratum_host, stratum_port)) {
684+ std::cout << " [run] --stratum malformed (want [HOST:]PORT): "
685+ << argv[i] << " \n " ;
686+ return 2 ;
687+ }
688+ }
584689 // --selftest is the default; accepted explicitly for symmetry.
585690 }
586691
@@ -627,7 +732,8 @@ int main(int argc, char** argv)
627732 }
628733 peer.listen_set = true ;
629734 }
630- return run_node (testnet, rpc_endpoint, rpc_conf_path, submit_hex, peer);
735+ return run_node (testnet, rpc_endpoint, rpc_conf_path, submit_hex, peer,
736+ stratum_host, stratum_port);
631737 }
632738 return run_selftest ();
633739}
0 commit comments