66//
77// --selftest / bare : drive the LIVE dgb::ShareTracker::score() path so the
88// coin smoke gate exercises real consensus code, then exit.
9- // --run : stand up the run-loop SPINE (this slice) — io_context +
10- // graceful SIGINT/SIGTERM shutdown. The node/stratum/P2P
11- // subsystems and the won-block dispatch binding
9+ // --run : stand up the run-loop — io_context + graceful
10+ // SIGINT/SIGTERM shutdown, the dgb::Node sharechain peer
11+ // (P2P listener), and (this slice) the miner-facing
12+ // Stratum work source. A won Scrypt block reaches the
13+ // network via the #82 dual-path broadcaster: the
14+ // submitblock-RPC arm (rpc.cpp:387 submit_block_hex, REAL)
15+ // is wired here; the embedded P2P-relay arm
1216// (m_on_block_found -> reconstruct_won_block ->
13- // broadcast_won_block, #82 connecting tissue landed across
14- // PRs #163/#166/#167/#173/#174/#176/#177/#179) bind onto
15- // this io_context in the NEXT stacked slice. Standing the
16- // spine up first gives those subsystems the lifecycle they
17- // hang off and keeps each increment build-verifiable.
17+ // broadcast_won_block, PRs #163/#166/#167/#173/#174/#176/
18+ // #177/#179) binds in the NEXT stacked slice once that
19+ // reconstructor stack lands on this base.
1820//
1921// V36 scope: Scrypt blocks validated; the other 4 DGB algos (SHA256d, Skein,
2022// Qubit, Odocrypt) are accept-by-continuity / ignored — full 5-algo support is
2527// Mirrors src/c2pool/main_btc.cpp s target shape.
2628
2729#include < impl/dgb/node.hpp>
30+ #include < impl/dgb/coin/header_chain.hpp>
31+ #include < impl/dgb/coin/mempool.hpp>
32+ #include < impl/dgb/coin/coin_node.hpp>
33+ #include < impl/dgb/stratum/work_source.hpp>
2834
2935#include < core/filesystem.hpp>
36+ #include < core/stratum_server.hpp>
3037#include < btclibs/util/strencodings.h>
3138
3239#include < boost/asio.hpp>
3542#include < cstring>
3643#include < filesystem>
3744#include < iostream>
45+ #include < memory>
3846#include < string>
3947#include < system_error>
48+ #include < utility>
49+ #include < vector>
4050
4151#ifndef C2POOL_VERSION
4252#define C2POOL_VERSION " dev"
@@ -62,11 +72,14 @@ void print_banner(const char* argv0, const core::CoinParams& p)
6272{
6373 std::cout
6474 << " c2pool-dgb " << C2POOL_VERSION << " — DigiByte Scrypt-only (V36)\n\n "
65- << " Usage: " << argv0 << " [--version] [--help] [--selftest] [--run]\n\n "
66- << " Status: pool/sharechain pillars live (Phase B); run-loop spine up\n "
67- << " (--run: io_context + graceful shutdown). Node/stratum/P2P +\n "
68- << " won-block dispatch binding land in the next slice; external\n "
69- << " digibyted RPC stays as a fallback.\n "
75+ << " Usage: " << argv0
76+ << " [--version] [--help] [--selftest] [--run] [--stratum [H:]P]\n\n "
77+ << " Status: pool/sharechain pillars live (Phase B); run-loop up\n "
78+ << " (--run: io_context + sharechain peer + Stratum standup).\n "
79+ << " --stratum [HOST:]PORT binds a miner-facing TCP listener\n "
80+ << " (e.g. --stratum 5022 or --stratum 127.0.0.1:5022); omit to\n "
81+ << " disable. Embedded P2P won-block relay + external digibyted\n "
82+ << " RPC fallback land in the next slices.\n "
7083 << " Network: " << network_summary (p) << " \n " ;
7184}
7285
@@ -98,24 +111,20 @@ int run_selftest(const core::CoinParams& params)
98111 return 0 ;
99112}
100113
101- // Run-loop SPINE + sharechain peer bring-up. Stands up the io_context that
102- // every node subsystem hangs off, an explicit graceful shutdown driven from
103- // boost::asio::signal_set, and (this slice) constructs the dgb::Config +
104- // dgb::Node sharechain peer and binds its P2P listener.
114+ // Run-loop: sharechain peer bring-up + miner-facing Stratum standup. Stands up
115+ // the io_context that every node subsystem hangs off, an explicit graceful
116+ // shutdown driven from boost::asio::signal_set, the dgb::Node sharechain peer
117+ // (P2P listener), and (this slice) the Stratum work source + acceptor so a
118+ // won Scrypt block reaches the network.
105119//
106120// Why signal_set and not std::signal: std::signal handlers run in the
107121// async-signal-only delivery context; io_context::stop is thread-safe but not
108122// documented signal-safe. signal_set delivers SIGINT/SIGTERM as an ordinary
109123// async callback on the io_context thread, so the shutdown path can do real
110124// work (stop the stratum acceptor, close sessions) before ioc.stop() drains
111125// the rest — mirrors main_btc.cpp's teardown contract.
112- //
113- // SEAM (next stacked slice): stand up the Stratum work source and bind
114- // make_on_block_found(reconstruct_won_block, p2p_sink) into m_on_block_found
115- // so a won share reaches the network (closes #82's embedded P2P relay path);
116- // the submitblock RPC fallback (rpc.cpp:387, already real — NOT a stub) is the
117- // second arm of the dual-path broadcaster gate.
118- int run_node (const core::CoinParams& params, bool testnet)
126+ int run_node (const core::CoinParams& params, bool testnet,
127+ const std::string& stratum_addr, uint16_t stratum_port)
119128{
120129 io::io_context ioc;
121130
@@ -146,20 +155,28 @@ int run_node(const core::CoinParams& params, bool testnet)
146155 config.pool ()->m_bootstrap_addrs .emplace_back (addr);
147156 }
148157
158+ // Stratum acceptor handle, declared BEFORE the signal_set so the shutdown
159+ // callback can stop it (cancel acceptor + close sessions) ahead of
160+ // ioc.stop(). Populated below once the work source is built.
161+ std::unique_ptr<core::StratumServer> stratum_server;
162+
149163 bool shutdown_initiated = false ;
150164 io::signal_set signals (ioc, SIGINT , SIGTERM );
151165 signals.async_wait (
152- [&ioc, &shutdown_initiated](const boost::system::error_code& ec, int signo) {
166+ [&ioc, &stratum_server, &shutdown_initiated]
167+ (const boost::system::error_code& ec, int signo) {
153168 if (ec) return ;
154169 if (shutdown_initiated) return ;
155170 shutdown_initiated = true ;
156171
157172 std::cout << " [DGB] received signal " << signo
158173 << " — initiating graceful shutdown" << std::endl;
159- // Next slice: stop stratum acceptor + close sessions here BEFORE
160- // ioc.stop(), so their pending async ops cancel cleanly. The
161- // sharechain peer's sockets close when p2p_node destructs at scope
162- // exit after ioc.run() returns.
174+ // Stop stratum BEFORE ioc.stop() so the acceptor cancels and live
175+ // miner sessions close cleanly (their pending async ops unwind on
176+ // the io_context). The sharechain peer's sockets close when
177+ // p2p_node destructs at scope exit after ioc.run() returns.
178+ if (stratum_server)
179+ stratum_server->stop ();
163180 ioc.stop ();
164181 });
165182
@@ -177,13 +194,80 @@ int run_node(const core::CoinParams& params, bool testnet)
177194 << " prefix=" << dgb::PoolConfig::DEFAULT_PREFIX_HEX << std::endl;
178195 p2p_node.start_outbound_connections (); // no-op until seed hosts exist
179196
197+ // ── #82 dual-path won-block CLOSER: miner-facing Stratum standup ───────
198+ //
199+ // Stand the miner path up so a Scrypt block found by a connected miner
200+ // reaches the network. The embedded coin side is MVP-unwired this slice
201+ // (empty HeaderChain + Mempool): the DGBWorkSource 4a skeleton returns
202+ // default work and mining_submit low-diff-rejects before the broadcaster
203+ // (compute_share_difficulty's 0.0 sentinel), so NO garbage block is ever
204+ // emitted — the standup proves the StratumServer<->IWorkSource wiring
205+ // end-to-end. Real work-gen / Scrypt share-validation land in 4b/4c.
206+ // This mirrors btc::stratum standing its skeleton wiring up first.
207+ c2pool::dgb::HeaderChain header_chain; // §7b chain, MVP-unwired (empty)
208+ dgb::coin::Mempool mempool; // unwired (no UTXO/template feed yet)
209+
210+ // submitblock-RPC arm of the #82 dual-path broadcaster (rpc.cpp:387
211+ // submit_block_hex — REAL, not a stub). CoinNode(nullptr embedded,
212+ // nullptr rpc) => has_rpc()==false => submit_block_hex returns false
213+ // LOUDLY (the #163 seam guard: no silent drop). Point a real NodeRPC at
214+ // external digibyted here to light this arm up.
215+ dgb::coin::CoinNode coin_node (/* embedded=*/ nullptr , /* rpc=*/ nullptr );
216+
217+ auto stratum_submit_fn =
218+ [&coin_node](const std::vector<unsigned char >& block_bytes,
219+ uint32_t height) -> bool {
220+ const std::string block_hex = HexStr (block_bytes);
221+ std::cout << " [DGB-STRATUM-BLOCK] won block height=" << height
222+ << " bytes=" << block_bytes.size ()
223+ << " — dispatching via submitblock-RPC arm" << std::endl;
224+ // P2P-relay arm (m_on_block_found -> reconstruct_won_block ->
225+ // broadcast_won_block) binds in the NEXT slice once the
226+ // reconstructor stack (#163/#166/#167/#177) lands on this base.
227+ const bool ok =
228+ coin_node.submit_block_hex (block_hex, /* ignore_failure=*/ false );
229+ if (!ok)
230+ std::cout << " [DGB-STRATUM-BLOCK] submitblock arm reached NO sink "
231+ " (no embedded backend / no digibyted RPC wired yet) "
232+ " — P2P-relay arm pending reconstructor stack"
233+ << std::endl;
234+ return ok;
235+ };
236+
237+ // DGBWorkSource holds non-owning refs to chain + mempool; both outlive it
238+ // (declared just above, same scope). The StratumServer co-owns the work
239+ // source via shared_ptr.
240+ auto work_source = std::make_shared<dgb::stratum::DGBWorkSource>(
241+ header_chain, mempool, testnet, std::move (stratum_submit_fn));
242+
243+ if (stratum_port != 0 ) {
244+ stratum_server = std::make_unique<core::StratumServer>(
245+ ioc, stratum_addr, stratum_port, work_source);
246+ if (stratum_server->start ()) {
247+ std::cout << " [DGB] stratum listening on " << stratum_addr << " :"
248+ << stratum_port
249+ << " (work source: DGBWorkSource 4a skeleton — Scrypt-only;"
250+ << " work-gen/share-validation land in 4b/4c)" << std::endl;
251+ } else {
252+ std::cout << " [DGB] stratum FAILED to bind " << stratum_addr << " :"
253+ << stratum_port << " — stratum disabled" << std::endl;
254+ stratum_server.reset ();
255+ }
256+ } else {
257+ std::cout << " [DGB] stratum disabled (no --stratum flag)" << std::endl;
258+ }
259+
180260 std::cout << " [DGB] run-loop up: " << network_summary (params) << " \n " ;
181- std::cout << " [DGB] io_context running. Ctrl-C to stop. "
182- << " (Stratum work source + won-block dispatch bind in the next slice)"
183- << std::endl;
261+ std::cout << " [DGB] io_context running. Ctrl-C to stop." << std::endl;
184262
185263 ioc.run ();
186264
265+ // Tear the acceptor + sessions down while the work source and the coin
266+ // objects it references (header_chain / mempool / coin_node) are still
267+ // alive — explicit reset keeps destruction order safe (stratum_server was
268+ // declared first, so it would otherwise outlive them).
269+ stratum_server.reset ();
270+
187271 std::cout << " [DGB] io_context stopped — clean exit" << std::endl;
188272 return 0 ;
189273}
@@ -195,6 +279,8 @@ int main(int argc, char** argv)
195279 bool want_help = false ;
196280 bool want_selftest = false ;
197281 bool want_run = false ;
282+ std::string stratum_addr = " 0.0.0.0" ; // bind all interfaces by default
283+ uint16_t stratum_port = 0 ; // 0 disables stratum; --stratum sets it
198284 for (int i = 1 ; i < argc; ++i) {
199285 if (std::strcmp (argv[i], " --version" ) == 0 ) {
200286 std::cout << " c2pool-dgb " << C2POOL_VERSION << " \n " ;
@@ -203,6 +289,17 @@ int main(int argc, char** argv)
203289 if (std::strcmp (argv[i], " --help" ) == 0 ) want_help = true ;
204290 if (std::strcmp (argv[i], " --selftest" ) == 0 ) want_selftest = true ;
205291 if (std::strcmp (argv[i], " --run" ) == 0 ) want_run = true ;
292+ if (std::strcmp (argv[i], " --stratum" ) == 0 && i + 1 < argc) {
293+ // --stratum [HOST:]PORT — bind a stratum TCP listener for miners.
294+ const std::string ep = argv[++i];
295+ const auto colon = ep.find (' :' );
296+ if (colon == std::string::npos) {
297+ stratum_port = static_cast <uint16_t >(std::stoi (ep));
298+ } else {
299+ stratum_addr = ep.substr (0 , colon);
300+ stratum_port = static_cast <uint16_t >(std::stoi (ep.substr (colon + 1 )));
301+ }
302+ }
206303 }
207304
208305 const core::CoinParams params = dgb::make_coin_params (/* testnet=*/ false );
@@ -211,9 +308,9 @@ int main(int argc, char** argv)
211308 if (want_help)
212309 return 0 ;
213310
214- // --run: stand up the run-loop spine (io_context + graceful shutdown ).
311+ // --run: stand up the run-loop (io_context + sharechain peer + stratum ).
215312 if (want_run)
216- return run_node (params, /* testnet=*/ false );
313+ return run_node (params, /* testnet=*/ false , stratum_addr, stratum_port );
217314
218315 // --selftest, or a bare invocation: drive the live score path so the
219316 // binary exercises real consensus code, then exit cleanly.
0 commit comments