Problem
requestBlock(hash) picks a single peer — the first one advertising the hash, or (with the forks anyPeer extension) the first free peer — sends one getblock, and resolves null on noblock/timeout. It never tries the next peer.
In a mixed room this is a coin flip per block. Example observed on fork·watch: two peers connected — a store-less forks tab (serves nothing, instant noblock) and a health Blocks tab with ~560 cached blocks. When the fetcher happens to ask the leech first, the request falls back to the explorer even though the other peer holds the block. Result: 'mesh: 2 peers · 0 blocks pulled' during a backfill that should have been largely peer-served.
Health's own catch-up _sync has the same single-shot pick per wanted block.
Proposed fix
Iterate candidates instead of picking one:
- Try peers that advertise the hash first (their
have map).
- Then, when
anyPeer is set, try remaining free peers — adverts only cover the newest 100 blocks, but peers serve their whole cache.
- Move on to the next candidate on
noblock (cheap round-trip) or a short per-peer timeout; keep the overall deadline so the caller's fallback (esplora) isn't delayed much in the worst case.
Skip peers with an in-flight inboundReq as today. No wire-protocol change — purely client-side retry ordering.
Where
Canonical peer-source.js lives here (health); bitcoin-kernel/forks vendors it (with the anyPeer extension) — fix should land here and sync forks-ward, same flow as the mesh-hello/hardening changes.
Problem
requestBlock(hash)picks a single peer — the first one advertising the hash, or (with the forksanyPeerextension) the first free peer — sends onegetblock, and resolvesnullonnoblock/timeout. It never tries the next peer.In a mixed room this is a coin flip per block. Example observed on fork·watch: two peers connected — a store-less forks tab (serves nothing, instant
noblock) and a health Blocks tab with ~560 cached blocks. When the fetcher happens to ask the leech first, the request falls back to the explorer even though the other peer holds the block. Result: 'mesh: 2 peers · 0 blocks pulled' during a backfill that should have been largely peer-served.Health's own catch-up
_synchas the same single-shot pick per wanted block.Proposed fix
Iterate candidates instead of picking one:
havemap).anyPeeris set, try remaining free peers — adverts only cover the newest 100 blocks, but peers serve their whole cache.noblock(cheap round-trip) or a short per-peer timeout; keep the overall deadline so the caller's fallback (esplora) isn't delayed much in the worst case.Skip peers with an in-flight
inboundReqas today. No wire-protocol change — purely client-side retry ordering.Where
Canonical
peer-source.jslives here (health); bitcoin-kernel/forks vendors it (with theanyPeerextension) — fix should land here and sync forks-ward, same flow as the mesh-hello/hardening changes.