22//
33// Wires the real dgb sharechain/pool TU (pool pillars + score path, ported from
44// LTC under impl/dgb/ across PRs #112/#113/#115/#121/#129/#131/#132/#134) into
5- // the c2pool-dgb executable. This is the exe-wire slice: it replaces the
6- // slice-#4 skeleton entry (dgb::run_skeleton/network_summary, removed when #134
7- // dropped the real node.cpp in) and drives the LIVE chain-score path at
8- // startup, so the coin smoke gate exercises share_tracker::score() rather than
9- // merely linking it. node.cpp (the concrete dgb::NodeImpl) is compiled into the
10- // target so the real node TU links; its full run-loop (NodeBridge over the
11- // embedded digibyted P2P + Stratum) is a later Phase B slice — dgb::NodeImpl is
12- // abstract (ICommunicator::handle is supplied by the NodeBridge wrapper), so a
13- // bare node is not stood up here. The score path lives on dgb::ShareTracker,
14- // which we drive directly (header-only, no network / no LevelDB).
5+ // the c2pool-dgb executable. Two entry paths:
6+ //
7+ // --selftest / bare : drive the LIVE dgb::ShareTracker::score() path so the
8+ // 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
12+ // (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.
1518//
1619// V36 scope: Scrypt blocks validated; the other 4 DGB algos (SHA256d, Skein,
1720// Qubit, Odocrypt) are accept-by-continuity / ignored — full 5-algo support is
1821// V37. Conformance oracle: frstrtr/p2pool-dgb-scrypt (DGB-Scrypt standalone
1922// parent; merged-v36 byte-compat WAIVED for DGB per operator 2026-06-17).
2023// CoinParams are oracle-sourced via dgb::make_coin_params (no hardcoded bytes).
21- // Mirrors src/c2pool/main_btc.cpp's target shape.
24+ // External digibyted RPC stays as a fallback alongside the embedded path.
25+ // Mirrors src/c2pool/main_btc.cpp s target shape.
2226
2327#include < impl/dgb/node.hpp>
2428
29+ #include < core/filesystem.hpp>
30+ #include < btclibs/util/strencodings.h>
31+
32+ #include < boost/asio.hpp>
33+
2534#include < cstdint>
2635#include < cstring>
36+ #include < filesystem>
2737#include < iostream>
2838#include < string>
39+ #include < system_error>
2940
3041#ifndef C2POOL_VERSION
3142#define C2POOL_VERSION " dev"
3243#endif
3344
45+ namespace io = boost::asio;
46+
3447namespace {
3548
3649// Live network summary sourced from the oracle-populated CoinParams
@@ -49,10 +62,11 @@ void print_banner(const char* argv0, const core::CoinParams& p)
4962{
5063 std::cout
5164 << " c2pool-dgb " << C2POOL_VERSION << " — DigiByte Scrypt-only (V36)\n\n "
52- << " Usage: " << argv0 << " [--version] [--help] [--selftest]\n\n "
53- << " Status: pool/sharechain pillars live (Phase B). The embedded-daemon\n "
54- << " run-loop (digibyted P2P + Stratum) lands in a later slice;\n "
55- << " external digibyted RPC stays as a fallback.\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 "
5670 << " Network: " << network_summary (p) << " \n " ;
5771}
5872
@@ -84,19 +98,111 @@ int run_selftest(const core::CoinParams& params)
8498 return 0 ;
8599}
86100
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.
105+ //
106+ // Why signal_set and not std::signal: std::signal handlers run in the
107+ // async-signal-only delivery context; io_context::stop is thread-safe but not
108+ // documented signal-safe. signal_set delivers SIGINT/SIGTERM as an ordinary
109+ // async callback on the io_context thread, so the shutdown path can do real
110+ // work (stop the stratum acceptor, close sessions) before ioc.stop() drains
111+ // 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)
119+ {
120+ io::io_context ioc;
121+
122+ // Per-coin config root: ~/.c2pool/<net>/ (sharechain LevelDB + addrs.json
123+ // open underneath). Bucket-1 isolation primitive: DGB never shares LTC's
124+ // net dir — keep the subdir per-coin in v36 AND v37.
125+ const std::string net_subdir = testnet ? " digibyte_testnet" : " digibyte" ;
126+ const std::filesystem::path net_dir =
127+ core::filesystem::config_path () / net_subdir;
128+ std::error_code mkdir_ec;
129+ std::filesystem::create_directories (net_dir, mkdir_ec); // best effort
130+
131+ // dgb::Config = core::Config<PoolConfig, CoinConfig>. Skip Config::init()
132+ // (it would load pool.yaml + coin.yaml from disk); set the sharechain
133+ // identity directly from the oracle-sourced constants instead — the same
134+ // contract main_btc.cpp uses for its net smoke. prefix/identifier come from
135+ // the p2pool-dgb-scrypt oracle (PREFIX 1c0553f2…, IDENTIFIER 4b62545b…).
136+ dgb::Config config (net_subdir);
137+ config.pool ()->m_prefix = ParseHexBytes (dgb::PoolConfig::DEFAULT_PREFIX_HEX );
138+ config.m_testnet = testnet;
139+ // DEFAULT_BOOTSTRAP_HOSTS is empty until DGB p2pool nodes come online, so
140+ // there are no outbound seeds to dial this slice — the node binds its
141+ // listener and waits for inbound sharechain peers.
142+ for (const auto & host : dgb::PoolConfig::DEFAULT_BOOTSTRAP_HOSTS ) {
143+ const std::string addr = host.find (' :' ) == std::string::npos
144+ ? host + " :" + std::to_string (dgb::PoolConfig::P2P_PORT )
145+ : host;
146+ config.pool ()->m_bootstrap_addrs .emplace_back (addr);
147+ }
148+
149+ bool shutdown_initiated = false ;
150+ io::signal_set signals (ioc, SIGINT , SIGTERM );
151+ signals.async_wait (
152+ [&ioc, &shutdown_initiated](const boost::system::error_code& ec, int signo) {
153+ if (ec) return ;
154+ if (shutdown_initiated) return ;
155+ shutdown_initiated = true ;
156+
157+ std::cout << " [DGB] received signal " << signo
158+ << " — 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.
163+ ioc.stop ();
164+ });
165+
166+ // Sharechain peer node: pool::NodeBridge<NodeImpl, Legacy, Actual>. The
167+ // NodeImpl ctor opens ~/.c2pool/<net>/sharechain_leveldb and seeds the addr
168+ // store from m_bootstrap_addrs, so config must be populated BEFORE
169+ // construction (above).
170+ dgb::Node p2p_node (&ioc, &config);
171+ p2p_node.set_target_outbound_peers (4 );
172+ p2p_node.core ::Server::listen (dgb::PoolConfig::P2P_PORT );
173+ std::cout << " [DGB] sharechain peer listening on port "
174+ << dgb::PoolConfig::P2P_PORT
175+ << " — proto adv=" << dgb::PoolConfig::ADVERTISED_PROTOCOL_VERSION
176+ << " min=" << dgb::PoolConfig::MINIMUM_PROTOCOL_VERSION
177+ << " prefix=" << dgb::PoolConfig::DEFAULT_PREFIX_HEX << std::endl;
178+ p2p_node.start_outbound_connections (); // no-op until seed hosts exist
179+
180+ 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;
184+
185+ ioc.run ();
186+
187+ std::cout << " [DGB] io_context stopped — clean exit" << std::endl;
188+ return 0 ;
189+ }
190+
87191} // namespace
88192
89193int main (int argc, char ** argv)
90194{
91195 bool want_help = false ;
92196 bool want_selftest = false ;
197+ bool want_run = false ;
93198 for (int i = 1 ; i < argc; ++i) {
94199 if (std::strcmp (argv[i], " --version" ) == 0 ) {
95200 std::cout << " c2pool-dgb " << C2POOL_VERSION << " \n " ;
96201 return 0 ;
97202 }
98203 if (std::strcmp (argv[i], " --help" ) == 0 ) want_help = true ;
99204 if (std::strcmp (argv[i], " --selftest" ) == 0 ) want_selftest = true ;
205+ if (std::strcmp (argv[i], " --run" ) == 0 ) want_run = true ;
100206 }
101207
102208 const core::CoinParams params = dgb::make_coin_params (/* testnet=*/ false );
@@ -105,8 +211,12 @@ int main(int argc, char** argv)
105211 if (want_help)
106212 return 0 ;
107213
108- // --selftest, or a bare invocation (no run-loop yet): drive the live score
109- // path so the binary exercises real consensus code, then exit cleanly.
214+ // --run: stand up the run-loop spine (io_context + graceful shutdown).
215+ if (want_run)
216+ return run_node (params, /* testnet=*/ false );
217+
218+ // --selftest, or a bare invocation: drive the live score path so the
219+ // binary exercises real consensus code, then exit cleanly.
110220 (void )want_selftest;
111221 return run_selftest (params);
112222}
0 commit comments