Skip to content

dash(embedded): DASH-isolated CoinPeerManager — network-standalone coin arm#794

Draft
frstrtr wants to merge 2 commits into
masterfrom
coinpeermgr-dash
Draft

dash(embedded): DASH-isolated CoinPeerManager — network-standalone coin arm#794
frstrtr wants to merge 2 commits into
masterfrom
coinpeermgr-dash

Conversation

@frstrtr

@frstrtr frstrtr commented Jul 21, 2026

Copy link
Copy Markdown
Owner

Summary

The DASH embedded coin-daemon arm had no peer-scoring: a single static
--coin-p2p-connect target (usually the local dashd itself), so it could not
reach 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 the
embedded 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.hppdash::coin::DashCoinPeerManager.
Brought over 1:1 (chain-agnostic p2pool broadcaster behaviour):

  • Source scoring — addr-crawl peers +50, daemon-learned (coind) peers
    -20 (the 70-pt swing that pulls the arm off the dashd peer view).
  • Daemon-peer overlap filter — a non-coind peer already in the daemon's
    getpeerinfo set is rejected as a separate entry.
  • Sybil / group diversity/16 (IPv4) /32 (IPv6) groups, per-group caps,
    max-2-per-group at selection.
  • Anchors — persisted last-N good peers, boosted on restart.
  • Protected local node — pinned dashd, score=999999, never pruned.
  • Seed discovery — DNS (dnsseed.dash.org:9999) + fixed seeds + HTTP fallback;
    JSON peer-db with atomic write.

DASH-adapted: dash::coin namespace + renamed types; valid_ports {9999} /
{19999}, magic bf0c6bbd; peer-db …/<net>/dash_embedded_peers; HTTP key dash;
the merged-child is_merged inv branch dropped (DASH is a parent chain).

Coexistence with --coin-p2p-connect

New opt-in --coin-p2p-discover. The single-connection E1 CoinClient is fed
its dial targets from the manager:

  • --coin-p2p-connect front target → set_local_node() = pinned / protected /
    preferred
    , still carries the redundant block-relay leg; coexists with (never
    suppresses) the discovered set.
  • Initial dial = [pinned] ++ get_peers_to_connect() (scored, diverse); a 60s
    refresh keeps the rotation pool fresh; getaddr on handshake + connect/disconnect
    feed scoring/anchors via two new CoinClient seams.
  • --coin-p2p-discover without --coin-p2p-connect = fully daemonless (seeds only).
  • Neither flag → byte-unchanged legacy single-peer dial.

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.hpp is a known #759-class
consolidation follow-up
(unify behind a shared core/ peer-manager once the fence
can be lifted); not resolved here. Multi-simultaneous embedded connections
(as the merged CoinBroadcaster does) 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-shadow and (2) the
prerequisite 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 motivating
provenance audit.

Consensus-neutral / isolation

DASH-only (src/impl/dash/** + main_dash.cpp + one KAT). No shared-core edit, no
other-coin edit, no share/consensus surface.

Verification

  • c2pool-dash builds clean; --selftest unchanged.
  • KAT test/test_dash_coin_peer_manager.cpp (folded into the allowlisted
    test_dash_p2p_node target): daemon-penalty scoring swing, selection ordering,
    /16 Sybil 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.

frstrtr added 2 commits July 21, 2026 10:20
…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.
@frstrtr

frstrtr commented Jul 21, 2026

Copy link
Copy Markdown
Owner Author

CR fixes pushed (e96bfe5) — still DRAFT for re-review

Both required fixes applied, both in the --coin-p2p-discover opt-in path.

CR-1 — daemonless cold-start dial wedge. CoinClient::connect() now 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 --coin-p2p-discover without --coin-p2p-connect, first run + DNS down (empty initial dial), now retries and connects as soon as fixed seeds (t+60s) / HTTP (t+90s) arrive — no restart, no wedge. Extracted arm_reconnect_timer(). Regression pin: DashCoinP2PClient.empty_connect_arms_without_wedging.

CR-2 — dormant overlap filter + coind −20 penalty. Added NodeRPC::getpeerinfo() to the DASH RPC (parses each entry's addr, IPv6-bracket aware) and wired set_getpeerinfo_fn in the discover path when a dashd RPC is armed. m_coind_peers now populates → the −20 penalty and the overlap filter engage (active daemon-disjointness in the connect+discover / oracle-shadow deployment). Absent RPC (fully daemonless) = seeds-only passive independence. Mirrors main_ltc.cpp.

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 + refresh timer that borrow it.

Build clean; test_dash_p2p_node 23/23 (was 22 + the CR-1 pin); --selftest unchanged. Design note updated in frstrtr/the PR #59 (§5a). Ready for re-review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant