Skip to content

dash(embedded/E1): opt-in coin-network P2P client + outbound dial in run_node#743

Merged
frstrtr merged 1 commit into
masterfrom
feat/dash-coin-p2p-dial
Jul 18, 2026
Merged

dash(embedded/E1): opt-in coin-network P2P client + outbound dial in run_node#743
frstrtr merged 1 commit into
masterfrom
feat/dash-coin-p2p-dial

Conversation

@frstrtr

@frstrtr frstrtr commented Jul 18, 2026

Copy link
Copy Markdown
Owner

SLICE E1 — DASH embedded coin-network dial (issue #738)

First slice of the DASH embedded (daemonless) wiring campaign: make the embedded arm establish a live outbound connection to the Dash coin network so later slices can populate NodeCoinState and build templates without a local dashd. E1 scope = instantiate the coin P2P client + dial + maintain the connection. Ingest / state population / mempool pull / fee pricing are later slices.

Opt-in flag design (hard safety constraint)

A mining hotel runs the retained dashd-RPC fallback path in production right now (NodeCoinState default-unpopulated → get_work() takes the fallback). This change is strictly opt-in:

  • New repeatable flag: --coin-p2p-connect HOST:PORT
  • ABSENT (released/prod path): no coin P2P client is instantiated, run_node behavior is byte-identical to today, fallback arm untouched.
  • PRESENT: a dash::coin::p2p::CoinClient dials the given endpoint(s) on the same ioc as the sharechain node, handshakes, keep-alives, and reconnects.

Even with the flag, NodeCoinState stays unpopulated in E1 (no ingest legs yet), so the fallback still serves templates — no mining-path regression in either mode.

What's wired

  • src/impl/dash/coin/p2p_client.hppCoinClient<Config>: the outbound-dial counterpart of the S8 socket-node skeleton (p2p_node.hpp). Ported 1:1 from the proven per-coin clients (dgb <- btc <- ltc), trimmed to E1: dial (via core::Factory<core::Client>), version/verack handshake (proto 70230, the version the vendored SML/clsig codecs assume), 30s ping keep-alive, idle/handshake-timeout teardown, 30s reconnect rotating the dial plan. Two pure helpers extracted for KATs: HandshakeTracker (version/verack state machine) and DialPlan (round-robin over repeated targets).
  • main_dash.cpp run_node — flag parse/validation + gated client standup. Coin-network wire magic (bf0c6bbd main / cee2caff testnet) sourced into config.coin()->m_p2p.prefix, kept distinct from the sharechain PREFIX. Client declared after config/coin_state so it tears down before them at scope exit.

No-flag-unchanged guarantee

Live-verified: --run --testnet with no --coin-p2p-connect emits zero coin-network client lines; run-loop stands up, fallback arm UNARMED, sharechain listener + stratum exactly as before.

Live-smoke result (E1 gate) — PASS

Against a controlled testnet dashd (192.168.86.52:19999):

[COIN-P2P] dialing 192.168.86.52:19999 (1 target[s] in plan)
[COIN-P2P] connected to 192.168.86.52:19999 — sending version (proto 70230)
[COIN-P2P] peer version: proto=70237 start_height=1517161 services=0x1c05 subver=/Dash Core:22.1.3/
[COIN-P2P] handshake complete with 192.168.86.52:19999 (peer proto=70237 height=1517161)
  • Outbound connection ESTABLISHED, handshake completed against real Dash Core 22.1.3.
  • Connection stayed up 65s with no disconnect (testnet advanced a block mid-run, height 1517161→1517162, confirming a live peer).
  • Reconnect: against a closed port, retry fires at exactly 30s (dialing 00:50:30 → reconnecting 00:51:00).

Tests

  • 11 new KATs (test/test_dash_coin_p2p_client.cpp): handshake state machine (legal progression, verack-before-version tolerance, stray/duplicate rejection, reset), dial-plan rotation, and byte-exact version/verack drive through the real Handler::parse path (peer metadata capture, handshake completion, on_handshake_complete seam, unknown-command tolerance, error-teardown-for-redial).
  • Compiled into the existing allowlisted test_dash_p2p_node target (second TU) — no build.yml/workflow edit. 16/16 pass (5 original skeleton + 11 new).
  • --selftest green; test_dash_p2p_messages (7) and test_dash_p2p_connection (4) green.

E2 seam exposed

The client parses inbound tx/block/headers/inv/clsig/mnlistdiff and fires the existing dash::interfaces::Node events (new_block / new_tx / full_block / new_headers / new_chainlock), plus a set_on_handshake_complete(cb) hook to kick initial sync. In E1 there are no subscribers, so every event is a no-op and NodeCoinState stays unpopulated. E2 attaches its ingest legs (CoinStateMaintainer / HeaderChain / Mempool) to exactly that event surface + the handshake hook — zero change to the client to light it up.

Isolation

Dash-fenced: src/impl/dash + src/c2pool/main_dash.cpp only; no src/core edit. Links the additive dash_block_replay static lib (coin/vendor/blockencodings.cpp) into c2pool-dash and test_dash_p2p_node — the full coin-network Handler the client instantiates ODR-uses CBlockHeaderAndShortTxIDs::FillShortTxIDSelector().

…run_node

Wire the DASH embedded (daemonless) arm to establish a live outbound
connection to the Dash coin network, gated behind a new opt-in flag so
the released binary's run path is byte-identical when the flag is absent.

New src/impl/dash/coin/p2p_client.hpp — dash::coin::p2p::CoinClient<Config>:
the outbound-dial counterpart of the S8 socket-node skeleton. Ported 1:1
from the proven per-coin clients (dgb <- btc <- ltc), trimmed to the E1
scope (dial + version/verack handshake + ping keep-alive + reconnect),
with NO ingest legs. Extracts two pure, KAT-able helpers: HandshakeTracker
(version/verack state machine) and DialPlan (round-robin over repeatable
targets).

main_dash.cpp run_node: new repeatable --coin-p2p-connect HOST:PORT flag.
ABSENT => no client instantiated, NodeCoinState stays default-unpopulated,
get_work() keeps the retained dashd-RPC fallback (the mining-hotel prod
posture) untouched. PRESENT => CoinClient dials on the SAME ioc as the
sharechain node, handshakes (proto 70230, testnet magic cee2caff / mainnet
bf0c6bbd), keep-alives, and reconnects (30s, rotating the dial plan).

E2 seam: the client parses inbound tx/block/headers/inv/clsig/mnlistdiff
and fires the existing dash::interfaces::Node events (new_block/new_tx/
full_block/new_headers/new_chainlock) plus a set_on_handshake_complete
hook. With no subscribers in E1 these are no-ops; later slices attach
CoinStateMaintainer/HeaderChain/Mempool ingest to that event surface.

Verify:
- 11 new KATs in test_dash_coin_p2p_client.cpp, compiled into the existing
  allowlisted test_dash_p2p_node target (no workflow edit); 16/16 pass.
- Live smoke vs a controlled testnet dashd: outbound connect + handshake
  complete (peer Dash Core 22.1.3, proto 70237, height 1517161), stayed up
  65s with no disconnect, reconnect retry fires at exactly 30s on a dead
  endpoint. No-flag run path confirmed unchanged. --selftest green;
  existing dash p2p tests green.

Links dash_block_replay (coin/vendor/blockencodings.cpp) into c2pool-dash
and test_dash_p2p_node: the full coin-network Handler the client
instantiates ODR-uses CBlockHeaderAndShortTxIDs::FillShortTxIDSelector().

Dash-fenced: src/impl/dash + src/c2pool/main_dash.cpp only; no src/core edit.
@frstrtr
frstrtr merged commit dc6b9d5 into master Jul 18, 2026
26 of 27 checks passed
@frstrtr
frstrtr deleted the feat/dash-coin-p2p-dial branch July 18, 2026 23:23
frstrtr pushed a commit that referenced this pull request Jul 19, 2026
…te NodeCoinState

E1 (#743) landed a coin-network P2P client that fires the raw dash::interfaces::
Node wire events; E2b (#745) landed the UTXO/fee lane. Nothing joined them into a
live populate loop. E2a is that join, entirely behind --coin-p2p-connect
(+ --embedded-utxo): with no flag, run_node is byte-unchanged.

What got wired (src/impl/dash/coin/live_feed.hpp + main_dash.cpp):
- new_headers -> HeaderChain::add_headers (X11 + DarkGravityWave validated) --
  the tip authority; HeaderChain::set_on_tip_changed derives a TipAdvance
  (next-work bits, median-time-past, address versions) and fires Node::new_tip
  -> wire_tip_ingest -> CoinStateMaintainer::on_new_tip (tip readiness).
- new_tx -> wire_mempool_ingest -> maintainer.on_mempool_tx.
- full_block -> (X11 hash -> header-chain height) -> Node::block_connected,
  driving BOTH wire_block_connect_ingest (MnStateMachine::apply_block) AND the
  E2b UTXO lane connect_block + fee recompute from one fired event.
- new_block(inv) -> request_block (getdata pull); new_chainlock -> best-CL tracker.
- mn_list_update leg wired for completeness (the payout-bearing DMN set source).
- set_on_handshake_complete kicks initial sync (getheaders + mempool); header
  self-propel requests the next batch off the updated locator.
- UTXO bootstrap request-by-height seam bound to the coin-P2P full-block pull.

BlockType parser (src/impl/dash/coin/block.hpp): closes the S5 header-only
deferral -- BlockType now (de)serializes the standard Bitcoin block body
(CompactSize tx-count + txs), mirroring the DGB sibling minus witness. This lets
a live dashd `block` body deserialize into the tx set the ingest legs consume,
and makes the multi-entry `headers` message round-trip (each entry = 80B header
+ CompactSize(0)). HeaderChain gains median_time_past() (BIP113).

#739 folded in: on a real tip change the header-chain callback bumps
work_generation_ + notify_all() sessions (event-driven stale-work notify).

KATs: block-body tx-set round-trip + 81-byte empty-body pin (test_dash_p2p_
messages); live-feed bridge translation (test_dash_live_feed_bridge in the
test_dash_node_reception_wire target). All affected dash targets green;
--selftest green.
frstrtr pushed a commit that referenced this pull request Jul 19, 2026
…te NodeCoinState

E1 (#743) landed a coin-network P2P client that fires the raw dash::interfaces::
Node wire events; E2b (#745) landed the UTXO/fee lane. Nothing joined them into a
live populate loop. E2a is that join, entirely behind --coin-p2p-connect
(+ --embedded-utxo): with no flag, run_node is byte-unchanged.

What got wired (src/impl/dash/coin/live_feed.hpp + main_dash.cpp):
- new_headers -> HeaderChain::add_headers (X11 + DarkGravityWave validated) --
  the tip authority; HeaderChain::set_on_tip_changed derives a TipAdvance
  (next-work bits, median-time-past, address versions) and fires Node::new_tip
  -> wire_tip_ingest -> CoinStateMaintainer::on_new_tip (tip readiness).
- new_tx -> wire_mempool_ingest -> maintainer.on_mempool_tx.
- full_block -> (X11 hash -> header-chain height) -> Node::block_connected,
  driving BOTH wire_block_connect_ingest (MnStateMachine::apply_block) AND the
  E2b UTXO lane connect_block + fee recompute from one fired event.
- new_block(inv) -> request_block (getdata pull); new_chainlock -> best-CL tracker.
- mn_list_update leg wired for completeness (the payout-bearing DMN set source).
- set_on_handshake_complete kicks initial sync (getheaders + mempool); header
  self-propel requests the next batch off the updated locator.
- UTXO bootstrap request-by-height seam bound to the coin-P2P full-block pull.

BlockType parser (src/impl/dash/coin/block.hpp): closes the S5 header-only
deferral -- BlockType now (de)serializes the standard Bitcoin block body
(CompactSize tx-count + txs), mirroring the DGB sibling minus witness. This lets
a live dashd `block` body deserialize into the tx set the ingest legs consume,
and makes the multi-entry `headers` message round-trip (each entry = 80B header
+ CompactSize(0)). HeaderChain gains median_time_past() (BIP113).

work_generation_ + notify_all() sessions (event-driven stale-work notify).

KATs: block-body tx-set round-trip + 81-byte empty-body pin (test_dash_p2p_
messages); live-feed bridge translation (test_dash_live_feed_bridge in the
test_dash_node_reception_wire target). All affected dash targets green;
--selftest green.
frstrtr added a commit that referenced this pull request Jul 19, 2026
…te NodeCoinState (#747)

E1 (#743) landed a coin-network P2P client that fires the raw dash::interfaces::
Node wire events; E2b (#745) landed the UTXO/fee lane. Nothing joined them into a
live populate loop. E2a is that join, entirely behind --coin-p2p-connect
(+ --embedded-utxo): with no flag, run_node is byte-unchanged.

What got wired (src/impl/dash/coin/live_feed.hpp + main_dash.cpp):
- new_headers -> HeaderChain::add_headers (X11 + DarkGravityWave validated) --
  the tip authority; HeaderChain::set_on_tip_changed derives a TipAdvance
  (next-work bits, median-time-past, address versions) and fires Node::new_tip
  -> wire_tip_ingest -> CoinStateMaintainer::on_new_tip (tip readiness).
- new_tx -> wire_mempool_ingest -> maintainer.on_mempool_tx.
- full_block -> (X11 hash -> header-chain height) -> Node::block_connected,
  driving BOTH wire_block_connect_ingest (MnStateMachine::apply_block) AND the
  E2b UTXO lane connect_block + fee recompute from one fired event.
- new_block(inv) -> request_block (getdata pull); new_chainlock -> best-CL tracker.
- mn_list_update leg wired for completeness (the payout-bearing DMN set source).
- set_on_handshake_complete kicks initial sync (getheaders + mempool); header
  self-propel requests the next batch off the updated locator.
- UTXO bootstrap request-by-height seam bound to the coin-P2P full-block pull.

BlockType parser (src/impl/dash/coin/block.hpp): closes the S5 header-only
deferral -- BlockType now (de)serializes the standard Bitcoin block body
(CompactSize tx-count + txs), mirroring the DGB sibling minus witness. This lets
a live dashd `block` body deserialize into the tx set the ingest legs consume,
and makes the multi-entry `headers` message round-trip (each entry = 80B header
+ CompactSize(0)). HeaderChain gains median_time_past() (BIP113).

work_generation_ + notify_all() sessions (event-driven stale-work notify).

KATs: block-body tx-set round-trip + 81-byte empty-body pin (test_dash_p2p_
messages); live-feed bridge translation (test_dash_live_feed_bridge in the
test_dash_node_reception_wire target). All affected dash targets green;
--selftest green.

Co-authored-by: integrator <integrator@morisguide.com>
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