diff --git a/src/c2pool/CMakeLists.txt b/src/c2pool/CMakeLists.txt index eb0482075..bc5d8b488 100644 --- a/src/c2pool/CMakeLists.txt +++ b/src/c2pool/CMakeLists.txt @@ -183,14 +183,32 @@ 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 + dgb_stratum # DGB miner-facing IWorkSource (DGBWorkSource) — #82 stratum standup + 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 aa4e1b7be..c40e53667 100644 --- a/src/c2pool/main_dgb.cpp +++ b/src/c2pool/main_dgb.cpp @@ -2,35 +2,58 @@ // // 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 — io_context + graceful +// SIGINT/SIGTERM shutdown, the dgb::Node sharechain peer +// (P2P listener), and (this slice) the miner-facing +// Stratum work source. A won Scrypt block reaches the +// network via the #82 dual-path broadcaster: the +// submitblock-RPC arm (rpc.cpp:387 submit_block_hex, REAL) +// is wired here; the embedded P2P-relay arm +// (m_on_block_found -> reconstruct_won_block -> +// broadcast_won_block, PRs #163/#166/#167/#173/#174/#176/ +// #177/#179) binds in the NEXT stacked slice once that +// reconstructor stack lands on this base. // // 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 + +#include +#include +#include + +#include #include #include +#include #include +#include #include +#include +#include +#include #ifndef C2POOL_VERSION #define C2POOL_VERSION "dev" #endif +namespace io = boost::asio; + namespace { // Live network summary sourced from the oracle-populated CoinParams @@ -49,10 +72,14 @@ 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] [--stratum [H:]P]\n\n" + << "Status: pool/sharechain pillars live (Phase B); run-loop up\n" + << " (--run: io_context + sharechain peer + Stratum standup).\n" + << " --stratum [HOST:]PORT binds a miner-facing TCP listener\n" + << " (e.g. --stratum 5022 or --stratum 127.0.0.1:5022); omit to\n" + << " disable. Embedded P2P won-block relay + external digibyted\n" + << " RPC fallback land in the next slices.\n" << "Network: " << network_summary(p) << "\n"; } @@ -84,12 +111,176 @@ int run_selftest(const core::CoinParams& params) return 0; } +// Run-loop: sharechain peer bring-up + miner-facing Stratum standup. Stands up +// the io_context that every node subsystem hangs off, an explicit graceful +// shutdown driven from boost::asio::signal_set, the dgb::Node sharechain peer +// (P2P listener), and (this slice) the Stratum work source + acceptor so a +// won Scrypt block reaches the network. +// +// 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. +int run_node(const core::CoinParams& params, bool testnet, + const std::string& stratum_addr, uint16_t stratum_port) +{ + 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); + } + + // 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 work source is built. + std::unique_ptr stratum_server; + + bool shutdown_initiated = false; + io::signal_set signals(ioc, SIGINT, SIGTERM); + signals.async_wait( + [&ioc, &stratum_server, &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; + // Stop stratum BEFORE ioc.stop() so the acceptor cancels and live + // miner sessions close cleanly (their pending async ops unwind on + // the io_context). The sharechain peer's sockets close when + // p2p_node destructs at scope exit after ioc.run() returns. + if (stratum_server) + stratum_server->stop(); + ioc.stop(); + }); + + // 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 + + // ── #82 dual-path won-block CLOSER: miner-facing Stratum standup ─────── + // + // Stand the miner path up so a Scrypt block found by a connected miner + // reaches the network. The embedded coin side is MVP-unwired this slice + // (empty HeaderChain + Mempool): the DGBWorkSource 4a skeleton returns + // default work and mining_submit low-diff-rejects before the broadcaster + // (compute_share_difficulty's 0.0 sentinel), so NO garbage block is ever + // emitted — the standup proves the StratumServer<->IWorkSource wiring + // end-to-end. Real work-gen / Scrypt share-validation land in 4b/4c. + // This mirrors btc::stratum standing its skeleton wiring up first. + c2pool::dgb::HeaderChain header_chain; // §7b chain, MVP-unwired (empty) + dgb::coin::Mempool mempool; // unwired (no UTXO/template feed yet) + + // submitblock-RPC arm of the #82 dual-path broadcaster (rpc.cpp:387 + // submit_block_hex — REAL, not a stub). CoinNode(nullptr embedded, + // nullptr rpc) => has_rpc()==false => submit_block_hex returns false + // LOUDLY (the #163 seam guard: no silent drop). Point a real NodeRPC at + // external digibyted here to light this arm up. + dgb::coin::CoinNode coin_node(/*embedded=*/nullptr, /*rpc=*/nullptr); + + auto stratum_submit_fn = + [&coin_node](const std::vector& block_bytes, + uint32_t height) -> bool { + const std::string block_hex = HexStr(block_bytes); + std::cout << "[DGB-STRATUM-BLOCK] won block height=" << height + << " bytes=" << block_bytes.size() + << " — dispatching via submitblock-RPC arm" << std::endl; + // P2P-relay arm (m_on_block_found -> reconstruct_won_block -> + // broadcast_won_block) binds in the NEXT slice once the + // reconstructor stack (#163/#166/#167/#177) lands on this base. + const bool ok = + coin_node.submit_block_hex(block_hex, /*ignore_failure=*/false); + if (!ok) + std::cout << "[DGB-STRATUM-BLOCK] submitblock arm reached NO sink " + "(no embedded backend / no digibyted RPC wired yet) " + "— P2P-relay arm pending reconstructor stack" + << std::endl; + return ok; + }; + + // DGBWorkSource holds non-owning refs to chain + mempool; both outlive it + // (declared just above, same scope). The StratumServer co-owns the work + // source via shared_ptr. + auto work_source = std::make_shared( + header_chain, mempool, testnet, std::move(stratum_submit_fn)); + + if (stratum_port != 0) { + stratum_server = std::make_unique( + ioc, stratum_addr, stratum_port, work_source); + if (stratum_server->start()) { + std::cout << "[DGB] stratum listening on " << stratum_addr << ":" + << stratum_port + << " (work source: DGBWorkSource 4a skeleton — Scrypt-only;" + << " work-gen/share-validation land in 4b/4c)" << std::endl; + } else { + std::cout << "[DGB] stratum FAILED to bind " << stratum_addr << ":" + << stratum_port << " — stratum disabled" << std::endl; + stratum_server.reset(); + } + } else { + std::cout << "[DGB] stratum disabled (no --stratum flag)" << std::endl; + } + + std::cout << "[DGB] run-loop up: " << network_summary(params) << "\n"; + std::cout << "[DGB] io_context running. Ctrl-C to stop." << std::endl; + + ioc.run(); + + // Tear the acceptor + sessions down while the work source and the coin + // objects it references (header_chain / mempool / coin_node) are still + // alive — explicit reset keeps destruction order safe (stratum_server was + // declared first, so it would otherwise outlive them). + stratum_server.reset(); + + 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; + std::string stratum_addr = "0.0.0.0"; // bind all interfaces by default + uint16_t stratum_port = 0; // 0 disables stratum; --stratum sets it for (int i = 1; i < argc; ++i) { if (std::strcmp(argv[i], "--version") == 0) { std::cout << "c2pool-dgb " << C2POOL_VERSION << "\n"; @@ -97,6 +288,18 @@ 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; + if (std::strcmp(argv[i], "--stratum") == 0 && i + 1 < argc) { + // --stratum [HOST:]PORT — bind a stratum TCP listener for miners. + const std::string ep = argv[++i]; + const auto colon = ep.find(':'); + if (colon == std::string::npos) { + stratum_port = static_cast(std::stoi(ep)); + } else { + stratum_addr = ep.substr(0, colon); + stratum_port = static_cast(std::stoi(ep.substr(colon + 1))); + } + } } const core::CoinParams params = dgb::make_coin_params(/*testnet=*/false); @@ -105,8 +308,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 (io_context + sharechain peer + stratum). + if (want_run) + return run_node(params, /*testnet=*/false, stratum_addr, stratum_port); + + // --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); } diff --git a/src/impl/dgb/CMakeLists.txt b/src/impl/dgb/CMakeLists.txt index e284a09ba..f87fb9785 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,33 @@ # 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), dgb_stratum +# (miner-facing IWorkSource) 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/-ldgb_stratum 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(stratum) # dgb_stratum: IWorkSource (#82 miner-facing path) +# 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()