From 792327f5359357ddf5fea4a7a19f465f645698fc Mon Sep 17 00:00:00 2001 From: frstrtr Date: Fri, 19 Jun 2026 01:52:44 +0000 Subject: [PATCH 1/3] =?UTF-8?q?dgb(#82):=20run-loop=20spine=20=E2=80=94=20?= =?UTF-8?q?io=5Fcontext=20+=20graceful=20signal=5Fset=20shutdown=20(--run)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Stand up the run-loop spine in main_dgb.cpp behind a new --run flag: a boost::asio::io_context plus an explicit graceful shutdown driven from signal_set(SIGINT, SIGTERM), mirroring main_btc.cpp s teardown contract. This is the lifecycle every node subsystem (sharechain peer dgb::Node, embedded digibyted P2P, Stratum acceptor) and the #82 won-block dispatch handler hangs off. Standing the spine up first keeps the next increments (node/Config construction over LevelDB, P2P listen, Stratum, and the m_on_block_found -> reconstruct_won_block -> broadcast_won_block binding landed across #163/#166/#167/#173/#174/#176/#177/#179) build-verifiable rather than landing a large unverified port in one step. --selftest / bare invocation unchanged (live ShareTracker::score() path). External digibyted RPC fallback posture preserved. Build: c2pool-dgb links EXIT=0 (-DCOIN_DGB=ON). Smoke: --selftest OK (block_period=75s SSOT); --run spine up, SIGINT -> clean shutdown exit 0. --- src/c2pool/main_dgb.cpp | 97 +++++++++++++++++++++++++++++++++-------- 1 file changed, 80 insertions(+), 17 deletions(-) diff --git a/src/c2pool/main_dgb.cpp b/src/c2pool/main_dgb.cpp index aa4e1b7be..9d2afa357 100644 --- a/src/c2pool/main_dgb.cpp +++ b/src/c2pool/main_dgb.cpp @@ -2,26 +2,32 @@ // // Wires the real dgb sharechain/pool TU (pool pillars + score path, ported from // LTC under impl/dgb/ across PRs #112/#113/#115/#121/#129/#131/#132/#134) into -// the c2pool-dgb executable. This is the exe-wire slice: it replaces the -// slice-#4 skeleton entry (dgb::run_skeleton/network_summary, removed when #134 -// dropped the real node.cpp in) and drives the LIVE chain-score path at -// startup, so the coin smoke gate exercises share_tracker::score() rather than -// merely linking it. node.cpp (the concrete dgb::NodeImpl) is compiled into the -// target so the real node TU links; its full run-loop (NodeBridge over the -// embedded digibyted P2P + Stratum) is a later Phase B slice — dgb::NodeImpl is -// abstract (ICommunicator::handle is supplied by the NodeBridge wrapper), so a -// bare node is not stood up here. The score path lives on dgb::ShareTracker, -// which we drive directly (header-only, no network / no LevelDB). +// the c2pool-dgb executable. Two entry paths: +// +// --selftest / bare : drive the LIVE dgb::ShareTracker::score() path so the +// coin smoke gate exercises real consensus code, then exit. +// --run : stand up the run-loop SPINE (this slice) — io_context + +// graceful SIGINT/SIGTERM shutdown. The node/stratum/P2P +// subsystems and the won-block dispatch binding +// (m_on_block_found -> reconstruct_won_block -> +// broadcast_won_block, #82 connecting tissue landed across +// PRs #163/#166/#167/#173/#174/#176/#177/#179) bind onto +// this io_context in the NEXT stacked slice. Standing the +// spine up first gives those subsystems the lifecycle they +// hang off and keeps each increment build-verifiable. // // V36 scope: Scrypt blocks validated; the other 4 DGB algos (SHA256d, Skein, // Qubit, Odocrypt) are accept-by-continuity / ignored — full 5-algo support is // V37. Conformance oracle: frstrtr/p2pool-dgb-scrypt (DGB-Scrypt standalone // parent; merged-v36 byte-compat WAIVED for DGB per operator 2026-06-17). // CoinParams are oracle-sourced via dgb::make_coin_params (no hardcoded bytes). -// Mirrors src/c2pool/main_btc.cpp's target shape. +// External digibyted RPC stays as a fallback alongside the embedded path. +// Mirrors src/c2pool/main_btc.cpp s target shape. #include +#include + #include #include #include @@ -31,6 +37,8 @@ #define C2POOL_VERSION "dev" #endif +namespace io = boost::asio; + namespace { // Live network summary sourced from the oracle-populated CoinParams @@ -49,10 +57,11 @@ void print_banner(const char* argv0, const core::CoinParams& p) { std::cout << "c2pool-dgb " << C2POOL_VERSION << " — DigiByte Scrypt-only (V36)\n\n" - << "Usage: " << argv0 << " [--version] [--help] [--selftest]\n\n" - << "Status: pool/sharechain pillars live (Phase B). The embedded-daemon\n" - << " run-loop (digibyted P2P + Stratum) lands in a later slice;\n" - << " external digibyted RPC stays as a fallback.\n" + << "Usage: " << argv0 << " [--version] [--help] [--selftest] [--run]\n\n" + << "Status: pool/sharechain pillars live (Phase B); run-loop spine up\n" + << " (--run: io_context + graceful shutdown). Node/stratum/P2P +\n" + << " won-block dispatch binding land in the next slice; external\n" + << " digibyted RPC stays as a fallback.\n" << "Network: " << network_summary(p) << "\n"; } @@ -84,12 +93,61 @@ int run_selftest(const core::CoinParams& params) return 0; } +// Run-loop SPINE. Stands up the io_context that every node subsystem +// (sharechain peer dgb::Node, embedded digibyted P2P, Stratum acceptor) and +// the #82 won-block dispatch handler will hang off, plus an explicit graceful +// shutdown driven from boost::asio::signal_set. +// +// Why signal_set and not std::signal: std::signal handlers run in the +// async-signal-only delivery context; io_context::stop is thread-safe but not +// documented signal-safe. signal_set delivers SIGINT/SIGTERM as an ordinary +// async callback on the io_context thread, so the shutdown path can do real +// work (stop the stratum acceptor, close sessions) before ioc.stop() drains +// the rest — mirrors main_btc.cpp s teardown contract. +// +// SEAM (next stacked slice): construct dgb::Config + dgb::Node(&ioc, &config), +// listen on the sharechain P2P port, stand up the Stratum work source, and +// bind make_on_block_found(reconstruct_won_block, p2p_sink) into +// m_on_block_found so a won share reaches the network (closes #82). Those +// subsystems open LevelDB + bind sockets, so they land as their own +// build-verified increment rather than inline here. +int run_node(const core::CoinParams& params) +{ + io::io_context ioc; + + bool shutdown_initiated = false; + io::signal_set signals(ioc, SIGINT, SIGTERM); + signals.async_wait( + [&ioc, &shutdown_initiated](const boost::system::error_code& ec, int signo) { + if (ec) return; + if (shutdown_initiated) return; + shutdown_initiated = true; + + std::cout << "[DGB] received signal " << signo + << " — initiating graceful shutdown" << std::endl; + // Next slice: stop stratum acceptor + close sessions here BEFORE + // ioc.stop(), so their pending async ops cancel cleanly. + ioc.stop(); + }); + + std::cout << "[DGB] run-loop spine up: " << network_summary(params) << "\n"; + std::cout << "[DGB] io_context running. Ctrl-C to stop. " + << "(node/stratum/P2P + won-block dispatch bind in the next slice)" + << std::endl; + + ioc.run(); + + std::cout << "[DGB] io_context stopped — clean exit" << std::endl; + return 0; +} + } // namespace int main(int argc, char** argv) { bool want_help = false; bool want_selftest = false; + bool want_run = false; for (int i = 1; i < argc; ++i) { if (std::strcmp(argv[i], "--version") == 0) { std::cout << "c2pool-dgb " << C2POOL_VERSION << "\n"; @@ -97,6 +155,7 @@ int main(int argc, char** argv) } if (std::strcmp(argv[i], "--help") == 0) want_help = true; if (std::strcmp(argv[i], "--selftest") == 0) want_selftest = true; + if (std::strcmp(argv[i], "--run") == 0) want_run = true; } const core::CoinParams params = dgb::make_coin_params(/*testnet=*/false); @@ -105,8 +164,12 @@ int main(int argc, char** argv) if (want_help) return 0; - // --selftest, or a bare invocation (no run-loop yet): drive the live score - // path so the binary exercises real consensus code, then exit cleanly. + // --run: stand up the run-loop spine (io_context + graceful shutdown). + if (want_run) + return run_node(params); + + // --selftest, or a bare invocation: drive the live score path so the + // binary exercises real consensus code, then exit cleanly. (void)want_selftest; return run_selftest(params); } From 648ac2f5e88c2fed3979e30ae0f0909f172d1624 Mon Sep 17 00:00:00 2001 From: frstrtr Date: Fri, 19 Jun 2026 03:02:30 +0000 Subject: [PATCH 2/3] dgb(#82): construct dgb::Node sharechain peer + bind P2P listener (--run) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Second stacked run-loop slice. main_dgb.cpp --run now constructs the real dgb::Config + dgb::Node (pool::NodeBridge) on the spine io_context, opens the sharechain LevelDB, and binds the P2P listener on PoolConfig::P2P_PORT (5024) — the sharechain peer is live, not just the io_context spine. Config is set directly from the p2pool-dgb-scrypt oracle constants (PREFIX 1c0553f2..., IDENTIFIER 4b62545b...), skipping Config::init() yaml load, the same contract main_btc.cpp uses. DEFAULT_BOOTSTRAP_HOSTS is empty until DGB p2pool nodes come online, so there are no outbound seeds to dial this slice; the node listens and waits for inbound peers. The per-coin net dir is digibyte/ (bucket-1 isolation). Constructing the node pulls BaseNode::connected (boost::log) and the NodeImpl TU, so the c2pool-dgb exe must link the real dgb pool runtime, not just the header-only score path. Wire the exe to link the dgb OBJECT lib + dgb_coin + core/pool/sharechain/storage base, mirroring the c2pool-btc link set; c2pool_merged_mining is pulled for symbol resolution of the core web_server tangle only (same as c2pool-btc) and does NOT enable -DAUX_DOGE — DGB default build stays standalone. Only the c2pool-dgb target stanza changes; per-coin isolation holds. Build EXIT=0. Smoke: --run binds 0.0.0.0:5024 (ss-confirmed), SIGINT -> graceful shutdown -> clean exit 0, LevelDB closed cleanly; --selftest still drives the live score() path; --version OK. SEAM (next slice): stand up the Stratum work source + bind make_on_block_found(reconstruct_won_block, p2p_sink) into m_on_block_found to close the #82 embedded P2P relay path (submitblock RPC fallback at rpc.cpp:387 is already real, not a stub). --- src/c2pool/CMakeLists.txt | 29 +++++++++++--- src/c2pool/main_dgb.cpp | 79 +++++++++++++++++++++++++++++++-------- 2 files changed, 86 insertions(+), 22 deletions(-) diff --git a/src/c2pool/CMakeLists.txt b/src/c2pool/CMakeLists.txt index eb0482075..bf1c6d32b 100644 --- a/src/c2pool/CMakeLists.txt +++ b/src/c2pool/CMakeLists.txt @@ -183,14 +183,31 @@ target_link_libraries(c2pool-btc # json/Boost. Per-coin isolation held. The embedded run-loop (NodeBridge over # digibyted P2P + Stratum) and its pool-runtime link land in a later Phase B # slice. -add_executable(c2pool-dgb - main_dgb.cpp - ${CMAKE_SOURCE_DIR}/src/core/uint256.cpp -) +# RUN-LOOP slice (#82): main_dgb.cpp --run now constructs dgb::Config + the +# dgb::Node sharechain peer (pool::NodeBridge) and binds its P2P +# listener, so the exe must link the real dgb pool runtime — not just the +# header-only score path. We link the `dgb` OBJECT lib (node.cpp + protocol +# handlers) and the shared core/pool/sharechain/storage base it sits on, +# mirroring the c2pool-btc link set. c2pool_merged_mining is pulled for SYMBOL +# RESOLUTION of the core web_server/stratum tangle ONLY (same reason c2pool-btc +# links it) — it does NOT enable -DAUX_DOGE; DGB default build stays standalone +# (no DOGE aux). core provides core/uint256.cpp, so the explicit source is +# dropped to avoid a duplicate-symbol link. Per-coin isolation holds: only the +# c2pool-dgb target stanza changes. +add_executable(c2pool-dgb main_dgb.cpp) target_compile_definitions(c2pool-dgb PRIVATE C2POOL_VERSION="${C2POOL_GIT_VERSION}") target_link_libraries(c2pool-dgb - btclibs # STATIC util lib: HexStr/HexDigit (strencodings) for base_uint hex - pure utility, no pool runtime - yaml-cpp::yaml-cpp # config_{coin,pool}.hpp -> core/netaddress.hpp -> + c2pool_payout + c2pool_merged_mining # symbol resolution for core web_server tangle (NOT -DAUX_DOGE) + c2pool_hashrate + core + dgb # DGB pool-layer OBJECT lib: node.cpp NodeImpl + protocol handlers + dgb_coin # DGB coin-layer: embedded P2P + mempool + transaction ctors + pool + sharechain + c2pool_storage + btclibs + yaml-cpp::yaml-cpp nlohmann_json::nlohmann_json ${Boost_LIBRARIES} ) diff --git a/src/c2pool/main_dgb.cpp b/src/c2pool/main_dgb.cpp index 9d2afa357..62da62cac 100644 --- a/src/c2pool/main_dgb.cpp +++ b/src/c2pool/main_dgb.cpp @@ -26,12 +26,17 @@ #include +#include +#include + #include #include #include +#include #include #include +#include #ifndef C2POOL_VERSION #define C2POOL_VERSION "dev" @@ -93,28 +98,54 @@ int run_selftest(const core::CoinParams& params) return 0; } -// Run-loop SPINE. Stands up the io_context that every node subsystem -// (sharechain peer dgb::Node, embedded digibyted P2P, Stratum acceptor) and -// the #82 won-block dispatch handler will hang off, plus an explicit graceful -// shutdown driven from boost::asio::signal_set. +// Run-loop SPINE + sharechain peer bring-up. Stands up the io_context that +// every node subsystem hangs off, an explicit graceful shutdown driven from +// boost::asio::signal_set, and (this slice) constructs the dgb::Config + +// dgb::Node sharechain peer and binds its P2P listener. // // Why signal_set and not std::signal: std::signal handlers run in the // async-signal-only delivery context; io_context::stop is thread-safe but not // documented signal-safe. signal_set delivers SIGINT/SIGTERM as an ordinary // async callback on the io_context thread, so the shutdown path can do real // work (stop the stratum acceptor, close sessions) before ioc.stop() drains -// the rest — mirrors main_btc.cpp s teardown contract. +// the rest — mirrors main_btc.cpp's teardown contract. // -// SEAM (next stacked slice): construct dgb::Config + dgb::Node(&ioc, &config), -// listen on the sharechain P2P port, stand up the Stratum work source, and -// bind make_on_block_found(reconstruct_won_block, p2p_sink) into -// m_on_block_found so a won share reaches the network (closes #82). Those -// subsystems open LevelDB + bind sockets, so they land as their own -// build-verified increment rather than inline here. -int run_node(const core::CoinParams& params) +// SEAM (next stacked slice): stand up the Stratum work source and bind +// make_on_block_found(reconstruct_won_block, p2p_sink) into m_on_block_found +// so a won share reaches the network (closes #82's embedded P2P relay path); +// the submitblock RPC fallback (rpc.cpp:387, already real — NOT a stub) is the +// second arm of the dual-path broadcaster gate. +int run_node(const core::CoinParams& params, bool testnet) { io::io_context ioc; + // Per-coin config root: ~/.c2pool// (sharechain LevelDB + addrs.json + // open underneath). Bucket-1 isolation primitive: DGB never shares LTC's + // net dir — keep the subdir per-coin in v36 AND v37. + const std::string net_subdir = testnet ? "digibyte_testnet" : "digibyte"; + const std::filesystem::path net_dir = + core::filesystem::config_path() / net_subdir; + std::error_code mkdir_ec; + std::filesystem::create_directories(net_dir, mkdir_ec); // best effort + + // dgb::Config = core::Config. Skip Config::init() + // (it would load pool.yaml + coin.yaml from disk); set the sharechain + // identity directly from the oracle-sourced constants instead — the same + // contract main_btc.cpp uses for its net smoke. prefix/identifier come from + // the p2pool-dgb-scrypt oracle (PREFIX 1c0553f2…, IDENTIFIER 4b62545b…). + dgb::Config config(net_subdir); + config.pool()->m_prefix = ParseHexBytes(dgb::PoolConfig::DEFAULT_PREFIX_HEX); + config.m_testnet = testnet; + // DEFAULT_BOOTSTRAP_HOSTS is empty until DGB p2pool nodes come online, so + // there are no outbound seeds to dial this slice — the node binds its + // listener and waits for inbound sharechain peers. + for (const auto& host : dgb::PoolConfig::DEFAULT_BOOTSTRAP_HOSTS) { + const std::string addr = host.find(':') == std::string::npos + ? host + ":" + std::to_string(dgb::PoolConfig::P2P_PORT) + : host; + config.pool()->m_bootstrap_addrs.emplace_back(addr); + } + bool shutdown_initiated = false; io::signal_set signals(ioc, SIGINT, SIGTERM); signals.async_wait( @@ -126,13 +157,29 @@ int run_node(const core::CoinParams& params) std::cout << "[DGB] received signal " << signo << " — initiating graceful shutdown" << std::endl; // Next slice: stop stratum acceptor + close sessions here BEFORE - // ioc.stop(), so their pending async ops cancel cleanly. + // ioc.stop(), so their pending async ops cancel cleanly. The + // sharechain peer's sockets close when p2p_node destructs at scope + // exit after ioc.run() returns. ioc.stop(); }); - std::cout << "[DGB] run-loop spine up: " << network_summary(params) << "\n"; + // Sharechain peer node: pool::NodeBridge. The + // NodeImpl ctor opens ~/.c2pool//sharechain_leveldb and seeds the addr + // store from m_bootstrap_addrs, so config must be populated BEFORE + // construction (above). + dgb::Node p2p_node(&ioc, &config); + p2p_node.set_target_outbound_peers(4); + p2p_node.core::Server::listen(dgb::PoolConfig::P2P_PORT); + std::cout << "[DGB] sharechain peer listening on port " + << dgb::PoolConfig::P2P_PORT + << " — proto adv=" << dgb::PoolConfig::ADVERTISED_PROTOCOL_VERSION + << " min=" << dgb::PoolConfig::MINIMUM_PROTOCOL_VERSION + << " prefix=" << dgb::PoolConfig::DEFAULT_PREFIX_HEX << std::endl; + p2p_node.start_outbound_connections(); // no-op until seed hosts exist + + std::cout << "[DGB] run-loop up: " << network_summary(params) << "\n"; std::cout << "[DGB] io_context running. Ctrl-C to stop. " - << "(node/stratum/P2P + won-block dispatch bind in the next slice)" + << "(Stratum work source + won-block dispatch bind in the next slice)" << std::endl; ioc.run(); @@ -166,7 +213,7 @@ int main(int argc, char** argv) // --run: stand up the run-loop spine (io_context + graceful shutdown). if (want_run) - return run_node(params); + return run_node(params, /*testnet=*/false); // --selftest, or a bare invocation: drive the live score path so the // binary exercises real consensus code, then exit cleanly. From 55dbd05f7dfb305b97b2ed7060bf34b43f4341e8 Mon Sep 17 00:00:00 2001 From: frstrtr Date: Fri, 19 Jun 2026 10:37:35 +0000 Subject: [PATCH 3/3] dgb(#82): define dgb/dgb_coin libs unconditionally so coin-matrix links c2pool-dgb The run-loop slice switched c2pool-dgb to link the dgb and dgb_coin OBJECT libs, but both were defined only inside if(COIN_DGB). The coin-matrix CI job configures without -DCOIN_DGB, so the targets were undefined and CMake emitted raw -ldgb/-ldgb_coin, failing the link (cannot find -ldgb). Mirror btc/ltc, which define their per-coin libs unconditionally; keep only the test subtree behind COIN_DGB (build.yml sets the flag and builds those targets). Verified: fresh configure with COIN_DGB=OFF builds + links c2pool-dgb and --help smokes clean (EXIT=0). --- src/impl/dgb/CMakeLists.txt | 49 +++++++++++++++++++------------------ 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/src/impl/dgb/CMakeLists.txt b/src/impl/dgb/CMakeLists.txt index e284a09ba..507e9d9bf 100644 --- a/src/impl/dgb/CMakeLists.txt +++ b/src/impl/dgb/CMakeLists.txt @@ -1,5 +1,4 @@ -# c2pool-dgb coin module (V36). Built when -DCOIN_DGB=ON. -# Mirrors src/impl/btc/CMakeLists.txt; emits the c2pool-dgb binary. +# c2pool-dgb coin module (V36). Mirrors src/impl/btc/CMakeLists.txt. # Per-coin binary scheme: project_v36_per_coin_binary_v37_unified_process. # Impl plan: c2pool-dgb-embedded-impl-plan.md (frstrtr/the docs/v36, 8ef8d2d). # @@ -11,30 +10,32 @@ # in this module yet: parked behind the Phase 5.8 settle (ltc-doge owns the # shared DOGE-aux surface). Do not add AUX_DOGE plumbing here at M2. # -# TODO(M3): add_library(impl_dgb ...) sources as an OBJECT lib (ci-steward -# PR-CMake-object-library convention; impl_dgb = 1 of 5 per-coin OBJECT libs), -# link bitcoin_family/Scrypt shared base, wire embedded DigiByte Core daemon -# slice under daemon/. Top-level add_subdirectory(src/impl/dgb) registration -# follows the OBJECT-lib convention PR; held ~2-3 heartbeats, not raced here. +# LIB GATING (mirrors btc/ltc): the dgb_coin (coin-layer) and dgb (pool-layer) +# OBJECT libs are defined UNCONDITIONALLY — the c2pool-dgb executable links +# them, and the coin-matrix CI builds that exe WITHOUT -DCOIN_DGB, so the +# targets MUST exist regardless of the flag (gating them behind COIN_DGB makes +# the linker fall back to raw -ldgb/-ldgb_coin and fail). Only the test subtree +# stays behind COIN_DGB; build.yml sets -DCOIN_DGB=ON and builds those targets. +add_subdirectory(coin) +# add_subdirectory(daemon) + +# dgb -- DGB Scrypt pool-layer OBJECT lib. Mirrors the ltc/btc OBJECT lib: +# compiles the real NodeImpl translation unit (node.cpp + protocol handlers) +# so share_tracker.hpp / share_check.hpp are pulled into a compiled TU. Links +# the shared core / pool / sharechain base plus dgb_coin (embedded P2P+mempool) +# and c2pool_storage (LevelDB sharechain). +add_library(dgb OBJECT + config_coin.hpp config_coin.cpp config_pool.hpp config_pool.cpp + node.hpp node.cpp + peer.hpp + protocol_actual.cpp protocol_legacy.cpp + messages.hpp + share_types.hpp share.hpp +) +target_link_libraries(dgb core pool sharechain dgb_coin btclibs c2pool_storage ${SECP256K1_LIBRARIES}) + if(COIN_DGB) message(STATUS "c2pool: DGB Scrypt-only coin module enabled") - add_subdirectory(coin) add_subdirectory(stratum) # dgb_stratum: IWorkSource (#82 miner-facing path) - # add_subdirectory(daemon) add_subdirectory(test) - - # dgb -- DGB Scrypt pool-layer OBJECT lib. Mirrors the ltc OBJECT lib - # (src/impl/ltc/CMakeLists.txt): compiles the real NodeImpl translation unit - # (node.cpp + protocol handlers) so share_tracker.hpp / share_check.hpp are - # pulled into a compiled TU. Links the shared core / pool / sharechain base - # plus dgb_coin (embedded P2P+mempool) and c2pool_storage (LevelDB sharechain). - add_library(dgb OBJECT - config_coin.hpp config_coin.cpp config_pool.hpp config_pool.cpp - node.hpp node.cpp - peer.hpp - protocol_actual.cpp protocol_legacy.cpp - messages.hpp - share_types.hpp share.hpp - ) - target_link_libraries(dgb core pool sharechain dgb_coin btclibs c2pool_storage ${SECP256K1_LIBRARIES}) endif()