Add opt-in bulk block streaming (-bulkblocksync)

A single getblockstrm request makes a peer stream a contiguous range of old
blocks back-to-back as ordinary BLOCK messages, amortizing the per-block
round-trip over the whole range instead of the MAX_BLOCKS_IN_TRANSIT_PER_PEER
window. This targets the bandwidth-delay-product ceiling that dominates IBD
from few/high-latency peers below the checkpoint.

Design (off by default; negotiated via a NODE_BULKBLOCKS service bit; the
default getdata IBD path is untouched when disabled):
- protocol: NODE_BULKBLOCKS service bit + getblockstrm/blockstream messages.
- requester: in SendMessages, after FindNextBlocksToDownload, when the first
  needed block is >= BULK_TIP_MARGIN (5000) below the network tip and the peer
  advertises the bit and we are in IBD, request a contiguous range (<=128
  blocks) instead of per-block getdata; mark the range in-flight.
- server: stream the range (caps 128 blocks / 8 MiB; reads outside cs_main;
  per-peer flood throttle), then a trailing blockstream header with the actual
  count sent. Self-suppresses while the server itself is in IBD.
- received blocks ride the existing BLOCK -> ProcessNewBlock path (fully
  validated; checkpoints below 2.84M still apply); the trailing header
  reconciles partial deliveries and the range is freed on a 90s timeout, so a
  partial/withheld/refused batch falls back to the normal path (no leak, no
  permanent gap, no disconnect). In-flight tracking is by literal hash, so a
  reorg cannot orphan range entries.

Hardened against the issues found in two adversarial review passes (drain vs
timeout, partial reconciliation, ownership-guarded frees, one-shot header,
reorg-proof helpers, cs_main hold). Validated end-to-end between two local
v1.0.3 nodes (128/128 and partial serves; height advanced; no errors).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-29 21:22:54 -05:00
parent 78ea2aac5b
commit 1f2b109d95
5 changed files with 269 additions and 4 deletions

View File

@@ -1448,6 +1448,11 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
MAX_BLOCKS_IN_TRANSIT_PER_PEER = 4096;
LogPrintf("Per-peer max blocks in transit: %d\n", MAX_BLOCKS_IN_TRANSIT_PER_PEER);
// Opt-in bulk block streaming (DragonX). Drives the requester branch in SendMessages and, when
// set, also advertises NODE_BULKBLOCKS below so we serve bulk ranges to peers. OFF by default.
fBulkBlockSync = GetBoolArg("-bulkblocksync", DEFAULT_BULKBLOCKSYNC);
LogPrintf("Bulk block streaming: %s\n", fBulkBlockSync ? "enabled" : "disabled");
fServer = GetBoolArg("-server", false);
//fprintf(stderr,"%s tik6\n", __FUNCTION__);
@@ -2574,6 +2579,9 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
nLocalServices |= NODE_ADDRINDEX;
if ( GetBoolArg("-spentindex", DEFAULT_SPENTINDEX) != 0 )
nLocalServices |= NODE_SPENTINDEX;
// Advertise willingness to SERVE bulk block streams (full nodes only) when opted in.
if ( fBulkBlockSync )
nLocalServices |= NODE_BULKBLOCKS;
fprintf(stderr,"nLocalServices %llx %d, %d\n",(long long)nLocalServices,GetBoolArg("-addressindex", DEFAULT_ADDRESSINDEX),GetBoolArg("-spentindex", DEFAULT_SPENTINDEX));
}
// ********************************************************* Step 10: import blocks