Skip to content

bch(M5): wire embedded P2P leg in production run() — close won-block P2P degradation - #231

Merged
frstrtr merged 4 commits into
masterfrom
bch/m5-run-p2p-leg
Jun 19, 2026
Merged

bch(M5): wire embedded P2P leg in production run() — close won-block P2P degradation#231
frstrtr merged 4 commits into
masterfrom
bch/m5-run-p2p-leg

Conversation

@frstrtr

@frstrtr frstrtr commented Jun 19, 2026

Copy link
Copy Markdown
Owner

M5 — wire embedded P2P leg in production run()

Closes the won-block P2P-relay degradation flagged after #229. Production run() brought up only the external BCHN-RPC fallback (init_rpc) and never connected the embedded P2P transport, so m_node.p2p() stayed null for the daemon's whole lifetime. Two paths silently degraded:

  • Won-block broadcastbroadcast_won_block's submit_block_p2p* calls no-op while p2p() is null, so every won block fell back to the RPC-only submitblock leg. The BIP152 / P2P block-relay leg only ever fired in the --ibd evidence harness and the regtest broadcaster gate, never in production.
  • Deep-reorg re-request — the BlockConnector re-request sink wired in assemble() had no download window to issue request_block_downloads on, so a reorg deeper than the remembered-block ring could not re-getdata the missing new-branch bodies.

Change

run() now calls a new maybe_start_p2p() after the cold-start anchor pin: it connects the embedded BCHN P2P transport to the configured peer (coin()->m_p2p.address) and binds the HeaderChain back-off locator provider. Gated on a configured peer (port != 0) — with none set the daemon stays strictly RPC-only, preserving the offline / no-peer contract. The external BCHN-RPC fallback is untouched; this only adds the P2P leg. A P2P connect issues no qm/control op, so VM300 bchn-bch stays read-only.

Test

embedded_daemon_assembly_test gains step 7 — the no-peer RPC-only contract (default NetService, port 0): maybe_start_p2p() returns false and node().has_p2p() stays false. Full bch suite green locally: 22/22 pass.

Scope / safety

  • Single-coin, src/impl/bch/ only. src/impl/{btc,ltc,doge,dgb,dash} and bitcoin_family untouched.
  • p2pool-merged-v36 surface: NONE — transport wiring only, no PoW/share/coinbase/PPLNS/WorkData-shape change.
  • GPG-signed, attribution-clean. Merge stays operator-gated.

frstrtr added 3 commits June 19, 2026 18:05
…ocks instead of stranding UTXO at fork

When a reorg new branch has intermediate blocks not in the connectors
bounded remembered ring (headers-first race, or reorg deeper than the
ring retains), recall_block() returned null and the connector logged a
warning and left the UTXO view parked at the fork with no recovery path.

Add an optional set_block_requester() sink: the not-yet-connected
new-branch hashes (fork->tip order) are handed to it for re-getdata
(wire to BlockDownloadWindow::enqueue, which dedupes) so the reorg
completes on their re-delivery. Unset -> existing warn-and-hold
fallback (cold-start / unwired contract preserved, no crash).

Single-coin BCH (src/impl/bch only); zero p2pool-merged-v36 surface
(local block-connect / download plumbing). New bch_reorg_rerequest_test
pins both legs (sink-wired re-request order + unwired warn-and-hold).
Integration half of the reorg re-request recovery (#226 added the
BlockConnector logic; this instantiates + attaches it in the live daemon
graph). assemble() now owns a BlockConnector(m_chain, m_pool), binds its
deep-reorg re-request sink to the P2P block-download window
(request_block_downloads -> m_block_dl: bounded, deduping, reissue-accounted,
no-op offline), and attaches it to full_block so every received block drives
header connect + best-chain-gated UTXO/mempool reconciliation. Connector
declared after m_node so its full_block subscription tears down before the
event source. Adds connector() accessor + has_block_requester() introspector;
assembly test asserts is_attached()+has_block_requester() and that the wiring
is stable across an idempotent re-assemble(). Network-free (sink no-ops until
start_p2p). Single-coin src/impl/bch only; zero p2pool-merged-v36 surface.
…P2P degradation

Production run() brought up only the external BCHN-RPC fallback (init_rpc) and
never connected the embedded P2P transport, so m_node.p2p() stayed null for the
whole lifetime of the daemon. Two paths silently degraded as a result:

  - broadcast_won_block submit_block_p2p* calls no-op while p2p() is null, so
    EVERY won block fell back to the RPC-only submitblock leg (the BIP152 / P2P
    block-relay leg never fired in production, only in the --ibd evidence harness
    and the regtest broadcaster gate).
  - the BlockConnector deep-reorg re-request sink wired in assemble() had no
    download window to issue request_block_downloads on, so a reorg deeper than
    the remembered-block ring could not re-getdata the missing new-branch bodies.

run() now calls maybe_start_p2p() after the cold-start anchor pin: it connects
the embedded BCHN P2P transport to the configured peer (coin m_p2p.address) and
binds the HeaderChain back-off locator provider. Gated on a configured peer
(port != 0): with none set the daemon stays strictly RPC-only, preserving the
offline / no-peer contract. The external BCHN-RPC fallback is untouched — this
only ADDS the P2P leg. A P2P connect issues no qm/control op, so VM300 bchn-bch
stays read-only.

Test: embedded_daemon_assembly_test gains step 7 — the no-peer RPC-only contract
(default NetService, port 0): maybe_start_p2p() returns false and node has_p2p()
stays false, so the daemon never spins up a transport with no peer configured.
Single-coin scope under src/impl/bch/ only; p2pool-merged-v36 surface: NONE
(transport wiring only — no PoW/share/coinbase/PPLNS/WorkData-shape change).
The reorg-rerequest M5 slice added bch_reorg_rerequest_test via add_test()
in src/impl/bch/test/CMakeLists.txt, but the COIN_BCH Linux and ASan+UBSan
build steps enumerate test targets explicitly and the new target was never
added to either --target list. ctest -R ^bch_ then discovers the registered
test, finds no built executable, reports an empty command, and fails the
leg. Clean CI hit this; local trees passed only because the binary already
existed in the build dir. Add bch_reorg_rerequest_test to both target
enumerations.
@frstrtr
frstrtr merged commit f9fe01e into master Jun 19, 2026
19 checks passed
frstrtr added a commit that referenced this pull request Jun 19, 2026
… forced-stall reissue

Closes the end-to-end gap PR #231 (production run() arming the embedded P2P
leg via maybe_start_p2p) left open: its offline step7 test only proves the
no-peer RPC-only contract. This adds a co-located, strictly read-only WITH-peer
mode against the live VM300 bchn-bch peer proving the two positives:

  (a) arm_p2p_no_rpc() = run() minus init_rpc drives the REAL maybe_start_p2p()
      through its configured-peer gate; broadcast_route() reports p2p -> the
      won-block dispatcher SELECTS the embedded P2P relay leg, not the RPC-only
      fallback. Routing asserted via a dry sink-selection read (broadcast_route),
      no block relayed onto mainnet.
  (b) a forced download-window stall (one unservable hash enqueued through
      request_block_downloads, the connector deep-reorg re-request sink) makes
      reissue_count() go nonzero off the LIVE window after BLOCK_DL_TIMEOUT,
      not the synthetic tick the unit test uses.

Live result @VM300 192.168.86.110:8333 (BCHN 29.0.0, start_height 956031,
compact-blocks v1): armed=yes route_p2p=yes reissue 0->1 at t=70s -> ALL PASS.

Fenced src/impl/bch/ + binary entrypoint only; transport/local block-dl wiring,
no PoW/share/coinbase/PPLNS change. p2pool-merged-v36 surface: NONE.
frstrtr added a commit that referenced this pull request Jun 19, 2026
… forced-stall reissue

Closes the end-to-end gap PR #231 (production run() arming the embedded P2P
leg via maybe_start_p2p) left open: its offline step7 test only proves the
no-peer RPC-only contract. This adds a co-located, strictly read-only WITH-peer
mode against the live VM300 bchn-bch peer proving the two positives:

  (a) arm_p2p_no_rpc() = run() minus init_rpc drives the REAL maybe_start_p2p()
      through its configured-peer gate; broadcast_route() reports p2p -> the
      won-block dispatcher SELECTS the embedded P2P relay leg, not the RPC-only
      fallback. Routing asserted via a dry sink-selection read (broadcast_route),
      no block relayed onto mainnet.
  (b) a forced download-window stall (one unservable hash enqueued through
      request_block_downloads, the connector deep-reorg re-request sink) makes
      reissue_count() go nonzero off the LIVE window after BLOCK_DL_TIMEOUT,
      not the synthetic tick the unit test uses.

Live result @VM300 192.168.86.110:8333 (BCHN 29.0.0, start_height 956031,
compact-blocks v1): armed=yes route_p2p=yes reissue 0->1 at t=70s -> ALL PASS.

Fenced src/impl/bch/ + binary entrypoint only; transport/local block-dl wiring,
no PoW/share/coinbase/PPLNS change. p2pool-merged-v36 surface: NONE.
frstrtr added a commit that referenced this pull request Jun 20, 2026
… forced-stall reissue

Closes the end-to-end gap PR #231 (production run() arming the embedded P2P
leg via maybe_start_p2p) left open: its offline step7 test only proves the
no-peer RPC-only contract. This adds a co-located, strictly read-only WITH-peer
mode against the live VM300 bchn-bch peer proving the two positives:

  (a) arm_p2p_no_rpc() = run() minus init_rpc drives the REAL maybe_start_p2p()
      through its configured-peer gate; broadcast_route() reports p2p -> the
      won-block dispatcher SELECTS the embedded P2P relay leg, not the RPC-only
      fallback. Routing asserted via a dry sink-selection read (broadcast_route),
      no block relayed onto mainnet.
  (b) a forced download-window stall (one unservable hash enqueued through
      request_block_downloads, the connector deep-reorg re-request sink) makes
      reissue_count() go nonzero off the LIVE window after BLOCK_DL_TIMEOUT,
      not the synthetic tick the unit test uses.

Live result @VM300 192.168.86.110:8333 (BCHN 29.0.0, start_height 956031,
compact-blocks v1): armed=yes route_p2p=yes reissue 0->1 at t=70s -> ALL PASS.

Fenced src/impl/bch/ + binary entrypoint only; transport/local block-dl wiring,
no PoW/share/coinbase/PPLNS change. p2pool-merged-v36 surface: NONE.
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