Skip to content
Draft
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
161 changes: 144 additions & 17 deletions src/c2pool/main_dash.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@
#include <impl/dash/coin/rpc_conf.hpp> // dash.conf creds resolution (rpcpassword off argv)
#include <impl/dash/coin/node_interface.hpp>
#include <impl/dash/coin/p2p_client.hpp> // dash::coin::p2p::CoinClient — OPT-IN coin-network dial (E1, --coin-p2p-connect)
#include <impl/dash/coin/coin_peer_manager.hpp> // dash::coin::DashCoinPeerManager — DASH-ISOLATED scored/diverse peer discovery (--coin-p2p-discover; network-standalone gate)
#include <impl/dash/coin/chain_seeds.hpp> // dash::coin::dash_dns_seeds / dash_fixed_seeds — DASH mainnet/testnet seed bootstrap
#include <impl/dash/coin/won_block_dispatch.hpp> // dash::coin::broadcast_won_block — S8 dual-path won-block dispatcher (embedded P2P primary + submitblock RPC backup)
#include <impl/dash/coin/node_coin_state.hpp> // dash::coin::NodeCoinState (embedded work bundle)
#include <impl/dash/coin/utxo_lane.hpp> // dash::coin::UtxoLane — embedded UTXO/fee lane (E2b, #738)
Expand Down Expand Up @@ -192,7 +194,7 @@ void print_banner(const char* argv0)
<< " " << argv0 << " --run [--coin-rpc H:P] [--coin-rpc-auth PATH]\n"
<< " [--testnet] [--submit-block HEX | --submit-block-file PATH]\n"
<< " [--listen [HOST:]PORT] [--addnode HOST:PORT]... [--connect HOST:PORT]...\n"
<< " [--stratum [HOST:]PORT] [--coin-p2p-connect HOST:PORT]...\n"
<< " [--stratum [HOST:]PORT] [--coin-p2p-connect HOST:PORT]... [--coin-p2p-discover]\n"
<< " [--web-port PORT] [--web-host ADDR] [--dashboard-dir PATH]\n"
<< " [--embedded-utxo]\n"
<< " [--give-author PCT] [-f|--fee PCT] [--node-owner-address ADDR]\n"
Expand All @@ -211,6 +213,12 @@ void print_banner(const char* argv0)
<< " --coin-p2p-connect HOST:PORT (repeatable) OPT-IN dials the DASH\n"
<< " coin network directly (version/verack + keep-alive + reconnect);\n"
<< " absent => no coin P2P client, dashd-RPC fallback path unchanged.\n"
<< " --coin-p2p-discover arms the DASH-isolated peer manager: seed\n"
<< " (dnsseed.dash.org + fixed) bootstrap, source-scored + group-diverse\n"
<< " (Sybil-capped) peer selection, anchors, and a self-healing dial\n"
<< " rotation INDEPENDENT of the local dashd — the network-standalone\n"
<< " arm (daemonless witness). Any --coin-p2p-connect peer is kept as a\n"
<< " pinned/preferred node alongside the discovered set.\n"
<< " --web-port PORT (alias --http-port, default 8080) serves the FULL\n"
<< " c2pool web dashboard + JSON API on --web-host (default 0.0.0.0)\n"
<< " from --dashboard-dir (default web-static); --web-port 0 disables.\n"
Expand Down Expand Up @@ -378,6 +386,7 @@ int run_node(bool testnet, const std::string& rpc_endpoint,
const std::string& web_host, uint16_t web_port,
const std::string& dashboard_dir,
const std::vector<NetService>& coin_p2p_targets,
bool coin_p2p_discover,
bool embedded_utxo,
double dev_donation, double node_owner_fee,
const std::string& node_owner_address,
Expand Down Expand Up @@ -722,26 +731,135 @@ int run_node(bool testnet, const std::string& rpc_endpoint,
// layers, never conflated. The client rides the SAME ioc as the
// sharechain node and stratum; declared after config/coin_state (both of
// which it borrows), so it is destroyed before them at scope exit.
// DASH-ISOLATED scored/diverse peer discovery (--coin-p2p-discover). Owns
// its own peer table + seed bootstrap + scoring; feeds the single embedded
// connection an INDEPENDENT (non-dashd) diverse peer set. Declared FIRST of
// the three so it is destroyed LAST — the CoinClient's callbacks and the
// refresh timer both capture it by raw pointer, so it must outlive both.
std::unique_ptr<dash::coin::DashCoinPeerManager> coin_peer_mgr;
std::unique_ptr<dash::coin::p2p::CoinClient<dash::Config>> coin_p2p;
if (!coin_p2p_targets.empty()) {
// Refresh timer declared LAST -> destroyed FIRST: it stops (and its lambda
// stops capturing coin_p2p / coin_peer_mgr) before either is torn down.
std::unique_ptr<core::Timer> coin_dial_refresh_timer;
if (!coin_p2p_targets.empty() || coin_p2p_discover) {
config.coin()->m_testnet = testnet;
config.coin()->m_p2p.prefix =
ParseHexBytes(testnet ? "cee2caff" : "bf0c6bbd");
config.coin()->m_p2p.address = coin_p2p_targets.front();
if (!coin_p2p_targets.empty())
config.coin()->m_p2p.address = coin_p2p_targets.front();

coin_p2p = std::make_unique<dash::coin::p2p::CoinClient<dash::Config>>(
&ioc, &coin_state, &config, "COIN-P2P");
coin_p2p->connect(coin_p2p_targets);
std::cout << "[run] coin-network P2P client dialing "
<< coin_p2p_targets.front().to_string()
<< (coin_p2p_targets.size() > 1
? " (+" + std::to_string(coin_p2p_targets.size() - 1)
+ " alternate[s], reconnect rotates)"
: "")
<< " magic=" << (testnet ? "cee2caff" : "bf0c6bbd")
<< " proto=70230 (E1: dial+handshake+keep-alive only;\n"
"[run] ingest legs are later slices — templates still source from\n"
"[run] the dashd-RPC fallback until NodeCoinState is fed)\n";

if (coin_p2p_discover) {
// ── Network-standalone arm: seed-discovered, SCORED, group-diverse
// peer set INDEPENDENT of the local dashd. The pinned local dashd
// (--coin-p2p-connect front target, if any) is registered as the
// PROTECTED/preferred peer that carries the redundant block-relay
// leg; it coexists with — never suppresses — the discovered set.
const uint16_t coin_port = testnet ? 19999 : 9999;
dash::coin::DashPeerManagerConfig pm_cfg;
pm_cfg.valid_ports = { coin_port };
const std::string pm_data_dir = (core::filesystem::config_path()
/ net_subdir / "dash_embedded_peers").string();
coin_peer_mgr = std::make_unique<dash::coin::DashCoinPeerManager>(
ioc, "DASH", pm_data_dir, pm_cfg);

// Pin the local dashd (if supplied) as the protected preferred peer.
std::string pinned_str = "(none — fully daemonless)";
if (!coin_p2p_targets.empty()) {
if (coin_peer_mgr->set_local_node(coin_p2p_targets.front()))
pinned_str = coin_p2p_targets.front().to_string();
}
coin_peer_mgr->set_dns_seeds(dash::coin::dash_dns_seeds(testnet));
coin_peer_mgr->set_fixed_seeds(dash::coin::dash_fixed_seeds(testnet));

// ── ACTIVE daemon-disjointness (connect+discover / oracle-shadow) ──
// When a local dashd RPC is armed, feed its getpeerinfo so the
// manager tracks the daemon's OWN peers (m_coind_peers). That is
// what makes the coind-source -20 penalty AND the daemon-peer
// overlap filter engage — without it those ported mechanisms stay
// dormant (m_coind_peers empty => no peer ever Source::coind) and
// only passive independence holds. Mirrors main_ltc.cpp:5484.
// Absent RPC (fully daemonless) => seeds-only, passive independence.
if (rpc) {
coin_peer_mgr->set_getpeerinfo_fn(
[rp = rpc.get()]() -> std::vector<NetService> {
try { return rp->getpeerinfo(); }
catch (...) { return {}; }
});
std::cout << "[run] daemon-disjointness ACTIVE: dashd getpeerinfo "
"wired -> coind -20 penalty + overlap filter engage\n";
}

coin_peer_mgr->start();

// addr-crawl discoveries feed back into the manager (source-scored
// +50); handshake connect/disconnect drive scoring + anchors.
coin_p2p->set_addr_callback(
[mgr = coin_peer_mgr.get()](const std::vector<NetService>& addrs) {
for (auto& a : addrs) mgr->add_discovered_peer(a);
});
coin_p2p->set_on_peer_connected(
[mgr = coin_peer_mgr.get()](const NetService& s) {
mgr->notify_connected(s.to_string());
});
coin_p2p->set_on_peer_disconnected(
[mgr = coin_peer_mgr.get()](const NetService& s) {
mgr->notify_disconnected(s.to_string());
});

// Pinned keys: passed to get_peers_to_connect() as the "already
// handled" set so the protected pinned node (score 999999, always
// ranked first) is NOT also appended by the scorer — the pinned
// entries are prepended explicitly, so this dedups them out.
std::set<std::string> pinned_keys;
for (auto& t : coin_p2p_targets) pinned_keys.insert(t.to_string());

// Initial dial plan: pinned local dashd first (preferred), then the
// top scored+diverse discovered peers (pinned excluded from that set).
std::vector<NetService> dial;
for (auto& t : coin_p2p_targets) dial.push_back(t);
for (auto& ep : coin_peer_mgr->get_peers_to_connect(pinned_keys))
dial.push_back(ep.to_net_service());
coin_p2p->connect(dial);

// Periodic dial-plan refresh (60s): rebuild from the freshly-scored,
// group-diverse set so newly-discovered high-score peers enter the
// rotation on the next reconnect — the self-healing witness loop.
coin_dial_refresh_timer = std::make_unique<core::Timer>(&ioc, /*repeat=*/true);
coin_dial_refresh_timer->start(60, [cp = coin_p2p.get(),
mgr = coin_peer_mgr.get(),
pinned = coin_p2p_targets,
pinned_keys]() {
std::vector<NetService> refreshed = pinned; // pinned always preferred, first
for (auto& ep : mgr->get_peers_to_connect(pinned_keys))
refreshed.push_back(ep.to_net_service());
cp->update_dial_targets(std::move(refreshed));
});

std::cout << "[run] coin-network P2P DISCOVERY armed (--coin-p2p-discover): "
"DASH-isolated scored/diverse peer set, pinned="
<< pinned_str << " magic=" << (testnet ? "cee2caff" : "bf0c6bbd")
<< " proto=70230 dns_seeds=" << dash::coin::dash_dns_seeds(testnet).size()
<< " fixed_seeds=" << dash::coin::dash_fixed_seeds(testnet).size()
<< " initial_dial=" << dial.size() << " target[s]\n"
"[run] (network-standalone witness: independent peers -> independent\n"
"[run] mempool/relay view; oracle-shadow standalone graduation gate)\n";
} else {
// Legacy single/pinned dial (--coin-p2p-connect only, no discovery).
coin_p2p->connect(coin_p2p_targets);
std::cout << "[run] coin-network P2P client dialing "
<< coin_p2p_targets.front().to_string()
<< (coin_p2p_targets.size() > 1
? " (+" + std::to_string(coin_p2p_targets.size() - 1)
+ " alternate[s], reconnect rotates)"
: "")
<< " magic=" << (testnet ? "cee2caff" : "bf0c6bbd")
<< " proto=70230 (E1: dial+handshake+keep-alive only;\n"
"[run] ingest legs are later slices — templates still source from\n"
"[run] the dashd-RPC fallback until NodeCoinState is fed)\n";
}
}

// ── S8 miner-facing Stratum accept-loop standup (run-path caller) ─────
Expand Down Expand Up @@ -1382,11 +1500,17 @@ int run_node(bool testnet, const std::string& rpc_endpoint,
// Kick the initial sync once the version/verack handshake completes:
// getheaders off our current locator + a mempool prime.
coin_p2p->set_on_handshake_complete(
[cp = coin_p2p.get(), hc = header_chain.get()]() {
[cp = coin_p2p.get(), hc = header_chain.get(),
discover = coin_p2p_discover]() {
LOG_INFO << "[EMB-DASH] handshake complete -> initial sync:"
" getheaders + mempool";
" getheaders + mempool"
<< (discover ? " + getaddr (peer crawl)" : "");
cp->send_getheaders(70230, hc->get_locator(), uint256::ZERO);
cp->send_mempool();
// Peer-crawl: getaddr feeds set_addr_callback -> the isolated
// peer manager (addr-crawl source, +50 scored) so the diverse
// independent peer set grows off live wire discovery.
if (discover) cp->send_getaddr();
});

std::cout << "[run] E2a live-feed wired: header-chain(" << hdr_db
Expand Down Expand Up @@ -1796,6 +1920,7 @@ int main(int argc, char** argv)
std::vector<std::string> addnode_raw; // --addnode HOST:PORT (persistent outbound)
std::vector<std::string> connect_raw; // --connect HOST:PORT (connect-only)
std::vector<std::string> coin_p2p_raw; // --coin-p2p-connect HOST:PORT (repeatable; E1 opt-in coin-network dial)
bool coin_p2p_discover = false; // --coin-p2p-discover: DASH-isolated scored/diverse peer discovery (network-standalone arm; independent of local dashd)
bool no_p2p_relay = false; // --no-p2p-relay: suppress the embedded P2P-relay won-block arm (A/B isolation; RPC backup stays live)
std::string stratum_host = "0.0.0.0"; // --stratum [HOST:]PORT bind interface (default all)
uint16_t stratum_port = 0; // 0 disables the Stratum accept-loop; --stratum sets it
Expand Down Expand Up @@ -1840,6 +1965,8 @@ int main(int argc, char** argv)
connect_raw.emplace_back(argv[++i]);
else if (std::strcmp(argv[i], "--coin-p2p-connect") == 0 && i + 1 < argc)
coin_p2p_raw.emplace_back(argv[++i]);
else if (std::strcmp(argv[i], "--coin-p2p-discover") == 0)
coin_p2p_discover = true;
else if (std::strcmp(argv[i], "--no-p2p-relay") == 0)
no_p2p_relay = true;
else if (std::strcmp(argv[i], "--embedded-utxo") == 0)
Expand Down Expand Up @@ -1942,7 +2069,7 @@ int main(int argc, char** argv)
}
return run_node(testnet, rpc_endpoint, rpc_conf_path, submit_hex, peer,
stratum_host, stratum_port, web_host, web_port,
dashboard_dir, coin_p2p_targets,
dashboard_dir, coin_p2p_targets, coin_p2p_discover,
embedded_utxo, dev_donation, node_owner_fee,
node_owner_address, redistribute_mode, no_p2p_relay);
}
Expand Down
Loading
Loading