Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 24 additions & 6 deletions src/c2pool/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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<NodeImpl,...>) 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 -> <yaml-cpp/yaml.h>
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}
)
Expand Down
241 changes: 224 additions & 17 deletions src/c2pool/main_dgb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 <impl/dgb/node.hpp>
#include <impl/dgb/coin/header_chain.hpp>
#include <impl/dgb/coin/mempool.hpp>
#include <impl/dgb/coin/coin_node.hpp>
#include <impl/dgb/stratum/work_source.hpp>

#include <core/filesystem.hpp>
#include <core/stratum_server.hpp>
#include <btclibs/util/strencodings.h>

#include <boost/asio.hpp>

#include <cstdint>
#include <cstring>
#include <filesystem>
#include <iostream>
#include <memory>
#include <string>
#include <system_error>
#include <utility>
#include <vector>

#ifndef C2POOL_VERSION
#define C2POOL_VERSION "dev"
#endif

namespace io = boost::asio;

namespace {

// Live network summary sourced from the oracle-populated CoinParams
Expand All @@ -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";
}

Expand Down Expand Up @@ -84,19 +111,195 @@ 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/<net>/ (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<PoolConfig, CoinConfig>. 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<core::StratumServer> 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<NodeImpl, Legacy, Actual>. The
// NodeImpl ctor opens ~/.c2pool/<net>/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<unsigned char>& 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<dgb::stratum::DGBWorkSource>(
header_chain, mempool, testnet, std::move(stratum_submit_fn));

if (stratum_port != 0) {
stratum_server = std::make_unique<core::StratumServer>(
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";
return 0;
}
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<uint16_t>(std::stoi(ep));
} else {
stratum_addr = ep.substr(0, colon);
stratum_port = static_cast<uint16_t>(std::stoi(ep.substr(colon + 1)));
}
}
}

const core::CoinParams params = dgb::make_coin_params(/*testnet=*/false);
Expand All @@ -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);
}
Loading
Loading