bch(M5): wire embedded P2P leg in production run() — close won-block P2P degradation - #231
Merged
Conversation
…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
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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, som_node.p2p()stayed null for the daemon's whole lifetime. Two paths silently degraded:broadcast_won_block'ssubmit_block_p2p*calls no-op whilep2p()is null, so every won block fell back to the RPC-onlysubmitblockleg. The BIP152 / P2P block-relay leg only ever fired in the--ibdevidence harness and the regtest broadcaster gate, never in production.BlockConnectorre-request sink wired inassemble()had no download window to issuerequest_block_downloadson, so a reorg deeper than the remembered-block ring could not re-getdata the missing new-branch bodies.Change
run()now calls a newmaybe_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 noqm/control op, so VM300bchn-bchstays read-only.Test
embedded_daemon_assembly_testgains step 7 — the no-peer RPC-only contract (defaultNetService, port 0):maybe_start_p2p()returns false andnode().has_p2p()stays false. Full bch suite green locally: 22/22 pass.Scope / safety
src/impl/bch/only.src/impl/{btc,ltc,doge,dgb,dash}andbitcoin_familyuntouched.