dash(embedded): DASH-isolated CoinPeerManager — network-standalone coin arm#794
dash(embedded): DASH-isolated CoinPeerManager — network-standalone coin arm#794frstrtr wants to merge 2 commits into
Conversation
…lone coin arm The DASH embedded coin arm carried no peer-scoring: a single static --coin-p2p-connect target (usually the local dashd itself), so its network view was not independent of that daemon. This lands a DASH-isolated port of the peer-prioritization subsystem (the c2pool port of the Python p2pool broadcaster, live today only on the merged LTC+DOGE path) so the embedded arm reaches the Dash network on its own scored, group-diverse peer set. New src/impl/dash/coin/coin_peer_manager.hpp (dash::coin::DashCoinPeerManager): source scoring (addr-crawl +50 / daemon-learned -20), daemon-peer overlap filter, /16-/32 Sybil group caps, anchors (partition resistance), protected pinned local node, DNS/fixed/HTTP seed discovery, JSON peer-db persistence. Wired via new opt-in --coin-p2p-discover in main_dash.cpp: the single-connection E1 CoinClient dials a scored+diverse rotation set fed by the manager, refreshed every 60s; addr-crawl (getaddr on handshake) and connect/disconnect feed scoring and anchors through two new CoinClient seams. An explicit --coin-p2p-connect peer is registered as the pinned/preferred protected node (redundant block-relay leg) alongside the discovered set. --coin-p2p-discover without --coin-p2p-connect is fully daemonless (seeds only). With neither flag the path is byte-unchanged. Isolation fence: DASH-local self-contained copy, NOT a reuse of the shared merged tree (honours the single-coin fence that caused the original drop). The duplication against src/c2pool/merged/coin_peer_manager.hpp is a known #759-class consolidation follow-up, not resolved here. Consensus-neutral: DASH-only, no shared-core / other-coin / share-consensus surface. Uses real DASH mainnet seeds (dnsseed.dash.org, fixed seeds), port 9999, magic bf0c6bbd. KAT test/test_dash_coin_peer_manager.cpp (folded into the allowlisted test_dash_p2p_node target): source-scoring daemon-penalty swing, selection ordering, /16 Sybil cap, anchor persistence across reload, pinned-node prune survival, canonical seed sanity. Builds clean; 22/22 in the target pass.
…disjointness Review of the CoinPeerManager port (#794) required two fixes, both in the new --coin-p2p-discover opt-in path. CR-1 — daemonless cold-start dial wedge: CoinClient::connect() early-returned on an empty target list without arming the reconnect loop, and update_dial_targets() ignored empty lists. So --coin-p2p-discover WITHOUT --coin-p2p-connect on a first run with DNS unavailable (empty initial dial) armed nothing and wedged until restart. Fix: connect() arms the 30s reconnect loop unconditionally (empty-plan guard on the tick so current()/advance() never throw), and update_dial_targets() kicks an immediate dial on the empty->non-empty transition — so the arm connects as soon as fixed seeds (t+60s) / HTTP seeds (t+90s) arrive. Extracted arm_reconnect_timer() helper. Regression pin added (empty_connect_arms_without_wedging). CR-2 — overlap filter + coind -20 penalty were dormant as wired: main_dash never called set_getpeerinfo_fn, so m_coind_peers stayed empty and no peer was ever Source::coind — the active daemon-disjointness never engaged (only passive independence). Fix: added NodeRPC::getpeerinfo() to the DASH RPC (parses each entry's addr, IPv6-bracket aware) and wired it in the discover path when a dashd RPC is armed, so the -20 penalty + overlap filter engage in the connect+discover / oracle-shadow deployment. Absent RPC (fully daemonless) = seeds-only passive independence. Mirrors main_ltc.cpp getpeerinfo wiring. Nits: pinned target no longer duplicated in the dial list (pinned keys passed as the get_peers_to_connect exclusion set); dead is_daemon_overlap_peer() removed; declaration/destruction order corrected so the manager outlives the client and refresh timer that borrow it. Build clean; full test_dash_p2p_node target 23/23 (was 22 + CR-1 pin); --selftest unchanged.
CR fixes pushed (e96bfe5) — still DRAFT for re-reviewBoth required fixes applied, both in the CR-1 — daemonless cold-start dial wedge. CR-2 — dormant overlap filter + coind −20 penalty. Added Nits: pinned target no longer duplicated in the dial list (pinned keys passed as the Build clean; |
Summary
The DASH embedded coin-daemon arm had no peer-scoring: a single static
--coin-p2p-connecttarget (usually the local dashd itself), so it could notreach the Dash network independently and its network view was not independent
of that daemon. This ports a DASH-isolated copy of the peer-prioritization
subsystem — the c2pool port of the Python p2pool broadcaster, live today only on
the merged LTC+DOGE path (
src/c2pool/merged/coin_peer_manager.hpp) — so theembedded arm reaches Dash on its own scored, group-diverse peer set.
This is the network-standalone gate for a fully daemonless c2pool-dash.
Port scope
New
src/impl/dash/coin/coin_peer_manager.hpp—dash::coin::DashCoinPeerManager.Brought over 1:1 (chain-agnostic p2pool broadcaster behaviour):
+50, daemon-learned (coind) peers-20(the 70-pt swing that pulls the arm off the dashd peer view).coindpeer already in the daemon'sgetpeerinfoset is rejected as a separate entry./16(IPv4)/32(IPv6) groups, per-group caps,max-2-per-group at selection.
score=999999, never pruned.dnsseed.dash.org:9999) + fixed seeds + HTTP fallback;JSON peer-db with atomic write.
DASH-adapted:
dash::coinnamespace + renamed types;valid_ports {9999}/{19999}, magicbf0c6bbd; peer-db…/<net>/dash_embedded_peers; HTTP keydash;the merged-child
is_mergedinv branch dropped (DASH is a parent chain).Coexistence with
--coin-p2p-connectNew opt-in
--coin-p2p-discover. The single-connection E1CoinClientis fedits dial targets from the manager:
--coin-p2p-connectfront target →set_local_node()= pinned / protected /preferred, still carries the redundant block-relay leg; coexists with (never
suppresses) the discovered set.
[pinned] ++ get_peers_to_connect()(scored, diverse); a 60srefresh keeps the rotation pool fresh;
getaddron handshake + connect/disconnectfeed scoring/anchors via two new
CoinClientseams.--coin-p2p-discoverwithout--coin-p2p-connect= fully daemonless (seeds only).Single-connection scope: "diverse peer set" is a scored, group-diverse rotation
pool (round-robin on reconnect), not N simultaneous connections — multi-conn is a
follow-up (see below).
Isolation fence & consolidation follow-up
DASH-local self-contained copy, NOT a reuse of the shared merged tree — honours
the single-coin isolation fence that caused the original drop. The duplication
against
src/c2pool/merged/coin_peer_manager.hppis a known #759-classconsolidation follow-up (unify behind a shared
core/peer-manager once the fencecan be lifted); not resolved here. Multi-simultaneous embedded connections
(as the merged
CoinBroadcasterdoes) is tracked with it.Oracle-shadow linkage
Source-scoring + daemon-peer-overlap avoidance converge the embedded connection on
peers the local dashd is not using → independent mempool/relay/tip view. That
independence is (1) the soundness premise for
--embedded-oracle-shadowand (2) theprerequisite for the network-standalone graduation gate — disabling the external
dashd. Design note lands in
frstrtr/the(
docs/DASH-EMBEDDED-COIN-PEER-MANAGER-PORT.md), companion to the motivatingprovenance audit.
Consensus-neutral / isolation
DASH-only (
src/impl/dash/**+main_dash.cpp+ one KAT). No shared-core edit, noother-coin edit, no share/consensus surface.
Verification
c2pool-dashbuilds clean;--selftestunchanged.test/test_dash_coin_peer_manager.cpp(folded into the allowlistedtest_dash_p2p_nodetarget): daemon-penalty scoring swing, selection ordering,/16Sybil cap, anchor persistence across reload, pinned-node prune survival,canonical seed sanity. 22/22 in the target pass.
DRAFT — Fable review precedes any tap. No merge/deploy.