Skip to content

Commit 5b4e67c

Browse files
authored
Merge pull request #182 from frstrtr/dgb/run-loop-skeleton
dgb(#82): run-loop spine — io_context + graceful signal_set shutdown (--run)
2 parents b98912f + 55dbd05 commit 5b4e67c

3 files changed

Lines changed: 175 additions & 47 deletions

File tree

src/c2pool/CMakeLists.txt

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -183,14 +183,31 @@ target_link_libraries(c2pool-btc
183183
# json/Boost. Per-coin isolation held. The embedded run-loop (NodeBridge over
184184
# digibyted P2P + Stratum) and its pool-runtime link land in a later Phase B
185185
# slice.
186-
add_executable(c2pool-dgb
187-
main_dgb.cpp
188-
${CMAKE_SOURCE_DIR}/src/core/uint256.cpp
189-
)
186+
# RUN-LOOP slice (#82): main_dgb.cpp --run now constructs dgb::Config + the
187+
# dgb::Node sharechain peer (pool::NodeBridge<NodeImpl,...>) and binds its P2P
188+
# listener, so the exe must link the real dgb pool runtime — not just the
189+
# header-only score path. We link the `dgb` OBJECT lib (node.cpp + protocol
190+
# handlers) and the shared core/pool/sharechain/storage base it sits on,
191+
# mirroring the c2pool-btc link set. c2pool_merged_mining is pulled for SYMBOL
192+
# RESOLUTION of the core web_server/stratum tangle ONLY (same reason c2pool-btc
193+
# links it) — it does NOT enable -DAUX_DOGE; DGB default build stays standalone
194+
# (no DOGE aux). core provides core/uint256.cpp, so the explicit source is
195+
# dropped to avoid a duplicate-symbol link. Per-coin isolation holds: only the
196+
# c2pool-dgb target stanza changes.
197+
add_executable(c2pool-dgb main_dgb.cpp)
190198
target_compile_definitions(c2pool-dgb PRIVATE C2POOL_VERSION="${C2POOL_GIT_VERSION}")
191199
target_link_libraries(c2pool-dgb
192-
btclibs # STATIC util lib: HexStr/HexDigit (strencodings) for base_uint hex - pure utility, no pool runtime
193-
yaml-cpp::yaml-cpp # config_{coin,pool}.hpp -> core/netaddress.hpp -> <yaml-cpp/yaml.h>
200+
c2pool_payout
201+
c2pool_merged_mining # symbol resolution for core web_server tangle (NOT -DAUX_DOGE)
202+
c2pool_hashrate
203+
core
204+
dgb # DGB pool-layer OBJECT lib: node.cpp NodeImpl + protocol handlers
205+
dgb_coin # DGB coin-layer: embedded P2P + mempool + transaction ctors
206+
pool
207+
sharechain
208+
c2pool_storage
209+
btclibs
210+
yaml-cpp::yaml-cpp
194211
nlohmann_json::nlohmann_json
195212
${Boost_LIBRARIES}
196213
)

src/c2pool/main_dgb.cpp

Lines changed: 127 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,35 +2,48 @@
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+
3447
namespace {
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

89193
int 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
}

src/impl/dgb/CMakeLists.txt

Lines changed: 25 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
# c2pool-dgb coin module (V36). Built when -DCOIN_DGB=ON.
2-
# Mirrors src/impl/btc/CMakeLists.txt; emits the c2pool-dgb binary.
1+
# c2pool-dgb coin module (V36). Mirrors src/impl/btc/CMakeLists.txt.
32
# Per-coin binary scheme: project_v36_per_coin_binary_v37_unified_process.
43
# Impl plan: c2pool-dgb-embedded-impl-plan.md (frstrtr/the docs/v36, 8ef8d2d).
54
#
@@ -11,30 +10,32 @@
1110
# in this module yet: parked behind the Phase 5.8 settle (ltc-doge owns the
1211
# shared DOGE-aux surface). Do not add AUX_DOGE plumbing here at M2.
1312
#
14-
# TODO(M3): add_library(impl_dgb ...) sources as an OBJECT lib (ci-steward
15-
# PR-CMake-object-library convention; impl_dgb = 1 of 5 per-coin OBJECT libs),
16-
# link bitcoin_family/Scrypt shared base, wire embedded DigiByte Core daemon
17-
# slice under daemon/. Top-level add_subdirectory(src/impl/dgb) registration
18-
# follows the OBJECT-lib convention PR; held ~2-3 heartbeats, not raced here.
13+
# LIB GATING (mirrors btc/ltc): the dgb_coin (coin-layer) and dgb (pool-layer)
14+
# OBJECT libs are defined UNCONDITIONALLY — the c2pool-dgb executable links
15+
# them, and the coin-matrix CI builds that exe WITHOUT -DCOIN_DGB, so the
16+
# targets MUST exist regardless of the flag (gating them behind COIN_DGB makes
17+
# the linker fall back to raw -ldgb/-ldgb_coin and fail). Only the test subtree
18+
# stays behind COIN_DGB; build.yml sets -DCOIN_DGB=ON and builds those targets.
19+
add_subdirectory(coin)
20+
# add_subdirectory(daemon)
21+
22+
# dgb -- DGB Scrypt pool-layer OBJECT lib. Mirrors the ltc/btc OBJECT lib:
23+
# compiles the real NodeImpl translation unit (node.cpp + protocol handlers)
24+
# so share_tracker.hpp / share_check.hpp are pulled into a compiled TU. Links
25+
# the shared core / pool / sharechain base plus dgb_coin (embedded P2P+mempool)
26+
# and c2pool_storage (LevelDB sharechain).
27+
add_library(dgb OBJECT
28+
config_coin.hpp config_coin.cpp config_pool.hpp config_pool.cpp
29+
node.hpp node.cpp
30+
peer.hpp
31+
protocol_actual.cpp protocol_legacy.cpp
32+
messages.hpp
33+
share_types.hpp share.hpp
34+
)
35+
target_link_libraries(dgb core pool sharechain dgb_coin btclibs c2pool_storage ${SECP256K1_LIBRARIES})
36+
1937
if(COIN_DGB)
2038
message(STATUS "c2pool: DGB Scrypt-only coin module enabled")
21-
add_subdirectory(coin)
2239
add_subdirectory(stratum) # dgb_stratum: IWorkSource (#82 miner-facing path)
23-
# add_subdirectory(daemon)
2440
add_subdirectory(test)
25-
26-
# dgb -- DGB Scrypt pool-layer OBJECT lib. Mirrors the ltc OBJECT lib
27-
# (src/impl/ltc/CMakeLists.txt): compiles the real NodeImpl translation unit
28-
# (node.cpp + protocol handlers) so share_tracker.hpp / share_check.hpp are
29-
# pulled into a compiled TU. Links the shared core / pool / sharechain base
30-
# plus dgb_coin (embedded P2P+mempool) and c2pool_storage (LevelDB sharechain).
31-
add_library(dgb OBJECT
32-
config_coin.hpp config_coin.cpp config_pool.hpp config_pool.cpp
33-
node.hpp node.cpp
34-
peer.hpp
35-
protocol_actual.cpp protocol_legacy.cpp
36-
messages.hpp
37-
share_types.hpp share.hpp
38-
)
39-
target_link_libraries(dgb core pool sharechain dgb_coin btclibs c2pool_storage ${SECP256K1_LIBRARIES})
4041
endif()

0 commit comments

Comments
 (0)