Skip to content

Commit 1a561f9

Browse files
committed
dgb(#82): stand StratumServer up in main_dgb.cpp --run — submitblock-RPC arm wired
Stacks on the run-loop (#182 spine + node-construct) and the DGBWorkSource 4a skeleton (#186): closes the SEAM both left for "stand the Stratum work source up + bind the won-block broadcaster." --run now constructs the miner-facing path on the run-loop io_context: - c2pool::dgb::HeaderChain + dgb::coin::Mempool (MVP-unwired this slice — the 4a work source returns default work; mining_submit low-diff-rejects via the 0.0 compute_share_difficulty sentinel BEFORE the broadcaster, so no garbage block is ever emitted) - dgb::coin::CoinNode(nullptr,nullptr) + a SubmitBlockFn bridging block_bytes -> hex -> submit_block_hex (rpc.cpp:387, REAL not a stub): the submitblock-RPC arm of the #82 dual-path broadcaster. has_rpc()==false => returns false LOUDLY (the #163 seam guard: no silent drop). - dgb::stratum::DGBWorkSource + core::StratumServer, gated on a new --stratum [HOST:]PORT flag (mirrors main_btc.cpp). Acceptor lifecycle is wired into the signal_set handler: stratum stop() runs BEFORE ioc.stop() so live miner sessions close cleanly. CMake: link c2pool-dgb against dgb_stratum. Single-coin (no bitcoin_family / src/core touch); core::StratumServer consumed, not modified. No -DAUX_DOGE. Remaining for the #82 closer (NEXT slice): the embedded P2P-relay arm (m_on_block_found -> reconstruct_won_block -> broadcast_won_block) needs the reconstructor stack (#163/#166/#167/#177), HELD off master. Two-arm independent broadcast evidence captured once both arms are live. Build EXIT=0; --run --stratum 127.0.0.1:5022 smoke: StratumServer started, sharechain peer on 5024, SIGINT -> ordered graceful shutdown (stratum stop then clean exit), EXIT=0.
1 parent 35ebb69 commit 1a561f9

2 files changed

Lines changed: 132 additions & 34 deletions

File tree

src/c2pool/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,7 @@ target_link_libraries(c2pool-dgb
203203
core
204204
dgb # DGB pool-layer OBJECT lib: node.cpp NodeImpl + protocol handlers
205205
dgb_coin # DGB coin-layer: embedded P2P + mempool + transaction ctors
206+
dgb_stratum # DGB miner-facing IWorkSource (DGBWorkSource) — #82 stratum standup
206207
pool
207208
sharechain
208209
c2pool_storage

src/c2pool/main_dgb.cpp

Lines changed: 131 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,17 @@
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
@@ -25,8 +27,13 @@
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>
@@ -35,8 +42,11 @@
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

Comments
 (0)