Skip to content
Draft
Show file tree
Hide file tree
Changes from 73 commits
Commits
Show all changes
79 commits
Select commit Hold shift + click to select a range
ac166f3
feat(network): regulate GetBlocks serving work
czarcas7ic Sep 4, 2026
71cadad
docs(changelog): record GetBlocks regulation
czarcas7ic Sep 4, 2026
8ed38ed
fix(network): isolate test tracking and correct lease fixtures
czarcas7ic Sep 4, 2026
491756d
fix(network): retain serving capacity through state completion
czarcas7ic Sep 4, 2026
85401ce
fix(network): retain GetBlocks terminals through queue pressure
czarcas7ic Sep 4, 2026
8b920aa
Merge remote-tracking branch 'origin/main' into adam/getblocks-regula…
czarcas7ic Sep 5, 2026
7d8d386
fix(release): version dependents of GetBlocks API changes
czarcas7ic Sep 5, 2026
4d953aa
fix(network): serialize serving query claims and cancellation
czarcas7ic Sep 5, 2026
23e951c
test(config): normalize the generated fixture ending
czarcas7ic Sep 5, 2026
774cca4
fix(network): require GetBlocks responses to fit any single block
czarcas7ic Sep 5, 2026
c765f49
Merge remote-tracking branch 'origin/main' into adam/getblocks-regula…
czarcas7ic Sep 6, 2026
122c177
fix: size GetBlocks work rates for native sync
czarcas7ic Sep 6, 2026
e9697eb
Merge remote-tracking branch 'origin/main' into adam/getblocks-regula…
czarcas7ic Sep 7, 2026
0be3f2b
fix(network): remove GetBlocks byte-rate policy
czarcas7ic Sep 7, 2026
a9b37a7
docs: align GetBlocks parameter ledger with capacity admission
czarcas7ic Sep 7, 2026
b9d59ac
fix(network): propagate GetBlocks write backpressure through producer…
czarcas7ic Sep 7, 2026
524c347
test(network): verify producer backpressure over QUIC
czarcas7ic Sep 7, 2026
55b87b6
docs: record bounded GetBlocks transport comparison
czarcas7ic Sep 7, 2026
7704547
Merge main and align publishable dependency versions
czarcas7ic Sep 7, 2026
5f5895f
refactor(network): declare request policy over shared work admission
czarcas7ic Sep 7, 2026
0795f81
fix(network): apply QUIC backpressure at GetBlocks admission
czarcas7ic Sep 7, 2026
a2c395b
test(config): store generated config for version 1.4.0-rc1
czarcas7ic Sep 7, 2026
fb49019
docs: keep Git install example on existing stable release
czarcas7ic Sep 7, 2026
eea1430
chore(network): remove obsolete regulation artifacts
czarcas7ic Sep 7, 2026
9c79b29
test(network): remove measurement-only fixture exports
czarcas7ic Sep 7, 2026
238600d
docs(network): explain GetBlocks policy fields and steps
czarcas7ic Sep 7, 2026
9d7bcae
fix(network): remove unused serving test helper
czarcas7ic Sep 7, 2026
b570724
docs(network): explain peer session checks on reconnect
czarcas7ic Sep 7, 2026
1e1fd1c
docs(network): label reconnect example explicitly
czarcas7ic Sep 7, 2026
44f0e7c
refactor(network): keep slot counters test-only
czarcas7ic Sep 7, 2026
7abcfae
test(config): reuse unchanged config fixture for rc1
czarcas7ic Sep 7, 2026
5a3e7ef
docs(network): describe current GetBlocks design directly
czarcas7ic Sep 7, 2026
2e16d3c
docs(network): explain shared request admission in plain words
czarcas7ic Sep 7, 2026
547b860
docs(network): explain backpressure and ownership in plain words
czarcas7ic Sep 7, 2026
fc2fd44
fix(network): finish ordered frames before cancelling a stream
czarcas7ic Sep 7, 2026
422d993
fix(network): publish download grace before pausing admission
czarcas7ic Sep 7, 2026
9ab5537
fix(network): keep serving streams open during local pressure
czarcas7ic Sep 7, 2026
172b0b9
fix(network): discard redundant rejected-peer bookkeeping
czarcas7ic Sep 7, 2026
25347b4
docs(network): record GetBlocks timeout and receive window defaults
czarcas7ic Sep 7, 2026
e7cabd8
fix(network): retain peer serving capacity across reconnects
czarcas7ic Sep 7, 2026
c624660
refactor(network): simplify GetBlocks admission plumbing
czarcas7ic Sep 7, 2026
43c4860
fix(network): keep test-only bound import out of release builds
czarcas7ic Sep 7, 2026
79ff7c5
fix(network): validate the full GetBlocks request range
czarcas7ic Sep 7, 2026
fbf466b
fix(network): enforce declared payload limits before allocation
czarcas7ic Sep 7, 2026
6e2216e
Merge main into GetBlocks regulation
czarcas7ic Sep 8, 2026
de030de
refactor(network): store one serving request per peer
czarcas7ic Sep 8, 2026
1ebe594
refactor(network): derive received block count from bitmap
czarcas7ic Sep 8, 2026
4b02b80
fix(network): backport QUIC loss recovery datagram bound
czarcas7ic Sep 8, 2026
d9ef1df
docs(changelog): record QUIC recovery fix
czarcas7ic Sep 8, 2026
c35d9ca
merge main and QUIC recovery prerequisite into GetBlocks regulation
czarcas7ic Sep 8, 2026
baa0414
fix(network): keep block responses moving while serving waits
czarcas7ic Sep 8, 2026
88df92f
fix(network): reserve response queue slots before encoding
czarcas7ic Sep 8, 2026
53ccc72
chore(deps): audit the pinned QUIC backport
czarcas7ic Sep 8, 2026
a9692e6
merge QUIC dependency audit into GetBlocks regulation
czarcas7ic Sep 8, 2026
5d5c8ab
ci: refresh GetBlocks checks against transport base
czarcas7ic Sep 8, 2026
d2300ab
test: probe default GetBlocks transport windows
czarcas7ic Sep 8, 2026
0a5ea1b
refactor: retain range-read resources in blocking jobs
czarcas7ic Sep 8, 2026
aa1d90a
refactor: prepare paired block-sync transport and request ownership
czarcas7ic Sep 9, 2026
0a6d564
refactor: serve paired block requests directly through owned storage
czarcas7ic Sep 9, 2026
a289325
fix: give slow block downloads realistic request deadlines
czarcas7ic Sep 9, 2026
54d0732
refactor: bound paired sessions through setup and retirement
czarcas7ic Sep 9, 2026
d916e42
test: adapt regulation ownership coverage to paired serving
czarcas7ic Sep 9, 2026
30752aa
test: allow liveness to retire peers before request expiry
czarcas7ic Sep 9, 2026
dd7f0b9
fix: allow slow block-sync data writes to finish
czarcas7ic Sep 9, 2026
ea9f39a
test: preserve serving prefixes under storage and queue pressure
czarcas7ic Sep 9, 2026
11beaec
refactor: activate paired block sync and remove legacy serving
czarcas7ic Sep 9, 2026
c7481f0
docs: record final paired block-sync validation
czarcas7ic Sep 9, 2026
571ce07
Merge main into GetBlocks refactor
czarcas7ic Sep 9, 2026
7cf0d46
docs: include transport prerequisite in main-targeted GetBlocks PR
czarcas7ic Sep 9, 2026
b80ef5f
refactor(network): remove superseded GetBlocks adapters
czarcas7ic Sep 9, 2026
575abf3
Merge remote-tracking branch 'origin/main' into adam/getblocks-two-st…
czarcas7ic Sep 9, 2026
2fd9cac
test(network): consolidate GetBlocks regressions and separate propert…
czarcas7ic Sep 9, 2026
3b8f8ca
docs: record GetBlocks test consolidation checks
czarcas7ic Sep 9, 2026
745561b
fix(network): reuse incoming pair session reservations
czarcas7ic Sep 9, 2026
98d6147
fix(network): keep paired data timeouts stream-local
czarcas7ic Sep 9, 2026
ea5b4cd
docs(network): explain block-sync session bookkeeping
czarcas7ic Sep 9, 2026
8e27724
fix(network): defer paired session capacity races
czarcas7ic Sep 9, 2026
953b85b
fix(network): retain no-progress policy across pair resets
czarcas7ic Sep 9, 2026
3649cbf
docs: remove withdrawn download test requirement
czarcas7ic Sep 9, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 29 additions & 2 deletions .config/nextest.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
[profile.default]
fail-fast = true
status-level = "pass"
default-filter = 'not test(~handler::tests::paired_block_sync::gate::)'

# --- Platform-specific overrides ---

Expand Down Expand Up @@ -36,7 +37,13 @@ threads-required = "num-test-threads"
# TODO: We need a better test architecture to run all non-stateful
[profile.all-tests]
failure-output = "immediate"
default-filter = "not test(check_no_git_dependencies) and not test(=fully_synced_rpc_z_getsubtreesbyindex_snapshot_test) and not test(=lwd_rpc_test) and not test(=lwd_rpc_send_tx) and not test(=lwd_grpc_wallet) and not test(=lwd_integration) and not test(=lwd_sync_full) and not test(=lwd_sync_update) and not test(=lightwalletd_test_suite) and not test(=rpc_get_block_template) and not test(=rpc_submit_block) and not test(~generate_checkpoints_) and not test(=sync_one_checkpoint_mainnet) and not test(=sync_one_checkpoint_testnet) and not test(=sync_update_mainnet) and not test(=activate_mempool_mainnet) and not test(=restart_stop_at_height) and not test(=sync_large_checkpoints_mempool_mainnet) and not test(=pruned_storage_mode_prunes_during_regtest_sync) and not test(=config_tests) and not test(~zakura::testkit::cluster) and not test(~zakura::testkit::blocksync_fuzz)"
default-filter = "not test(check_no_git_dependencies) and not test(=fully_synced_rpc_z_getsubtreesbyindex_snapshot_test) and not test(=lwd_rpc_test) and not test(=lwd_rpc_send_tx) and not test(=lwd_grpc_wallet) and not test(=lwd_integration) and not test(=lwd_sync_full) and not test(=lwd_sync_update) and not test(=lightwalletd_test_suite) and not test(=rpc_get_block_template) and not test(=rpc_submit_block) and not test(~generate_checkpoints_) and not test(=sync_one_checkpoint_mainnet) and not test(=sync_one_checkpoint_testnet) and not test(=sync_update_mainnet) and not test(=activate_mempool_mainnet) and not test(=restart_stop_at_height) and not test(=sync_large_checkpoints_mempool_mainnet) and not test(=pruned_storage_mode_prunes_during_regtest_sync) and not test(=config_tests) and not test(~zakura::testkit::cluster) and not test(~zakura::testkit::blocksync_fuzz) and not test(~handler::tests::paired_block_sync::gate::)"

# Controlled regulation tests must fail on their first divergent execution.
[[profile.all-tests.overrides]]
filter = 'test(~message_payload_limits_) or test(~inbound_frame_cap_rejects_) or test(~paused_ordered_reads_) or test(~stream_cancel_closes_ordered_worker_) or test(~block_sync::service::tests::) or test(~transport::io::) or test(~reconciliation_rejects_stale_admissions_) or test(~regulation::request::) or test(~serving_regulation::) or test(~block_sync::serving::) or test(~work_queue::request_write::tests::) or test(~handler::ordered_pair::tests::) or test(~service::block_range::tests::) or (test(~handler::tests::paired_block_sync::) and not test(~handler::tests::paired_block_sync::gate::))'
retries = 0
slow-timeout = { period = "60s", terminate-after = 1 }

# The Zakura block-sync tests drive the real reactor through a real-time (wall-clock)
# scenario harness — multi-threaded tokio with real sleeps and timeouts. Under a loaded
Expand All @@ -63,7 +70,13 @@ retries = 2
# no-fail-fast without hiding fast unit failures.
[profile.full-tests]
failure-output = "immediate"
default-filter = "not test(check_no_git_dependencies) and not test(=fully_synced_rpc_z_getsubtreesbyindex_snapshot_test) and not test(=lwd_rpc_test) and not test(=lwd_rpc_send_tx) and not test(=lwd_grpc_wallet) and not test(=lwd_integration) and not test(=lwd_sync_full) and not test(=lwd_sync_update) and not test(=lightwalletd_test_suite) and not test(=rpc_get_block_template) and not test(=rpc_submit_block) and not test(~generate_checkpoints_) and not test(=sync_one_checkpoint_mainnet) and not test(=sync_one_checkpoint_testnet) and not test(=sync_update_mainnet) and not test(=activate_mempool_mainnet) and not test(=restart_stop_at_height) and not test(=sync_large_checkpoints_mempool_mainnet) and not test(=pruned_storage_mode_prunes_during_regtest_sync) and not test(=config_tests) and not test(~zakura::testkit::cluster) and not test(~zakura::testkit::blocksync_fuzz)"
default-filter = "not test(check_no_git_dependencies) and not test(=fully_synced_rpc_z_getsubtreesbyindex_snapshot_test) and not test(=lwd_rpc_test) and not test(=lwd_rpc_send_tx) and not test(=lwd_grpc_wallet) and not test(=lwd_integration) and not test(=lwd_sync_full) and not test(=lwd_sync_update) and not test(=lightwalletd_test_suite) and not test(=rpc_get_block_template) and not test(=rpc_submit_block) and not test(~generate_checkpoints_) and not test(=sync_one_checkpoint_mainnet) and not test(=sync_one_checkpoint_testnet) and not test(=sync_update_mainnet) and not test(=activate_mempool_mainnet) and not test(=restart_stop_at_height) and not test(=sync_large_checkpoints_mempool_mainnet) and not test(=pruned_storage_mode_prunes_during_regtest_sync) and not test(=config_tests) and not test(~zakura::testkit::cluster) and not test(~zakura::testkit::blocksync_fuzz) and not test(~handler::tests::paired_block_sync::gate::)"

# Controlled regulation tests must fail on their first divergent execution.
[[profile.full-tests.overrides]]
filter = 'test(~message_payload_limits_) or test(~inbound_frame_cap_rejects_) or test(~paused_ordered_reads_) or test(~stream_cancel_closes_ordered_worker_) or test(~block_sync::service::tests::) or test(~transport::io::) or test(~reconciliation_rejects_stale_admissions_) or test(~regulation::request::) or test(~serving_regulation::) or test(~block_sync::serving::) or test(~work_queue::request_write::tests::) or test(~handler::ordered_pair::tests::) or test(~service::block_range::tests::) or (test(~handler::tests::paired_block_sync::) and not test(~handler::tests::paired_block_sync::gate::))'
retries = 0
slow-timeout = { period = "60s", terminate-after = 1 }

# Retry the real-time Zakura block-sync scenario tests under load (see the note on the
# `all-tests` override above).
Expand Down Expand Up @@ -232,3 +245,17 @@ test-threads = 1
slow-timeout = { period = "5m", terminate-after = 2 }
failure-output = "immediate"
default-filter = 'package(zakura) and test(~zcashd_compat)'

# Also selected by ordinary unit-test profiles; isolate the fixed regression checks.
[profile.blocksync-regression]
default-filter = 'test(~message_payload_limits_) or test(~inbound_frame_cap_rejects_) or test(~paused_ordered_reads_) or test(~stream_cancel_closes_ordered_worker_) or test(~block_sync::service::tests::) or test(~transport::io::) or test(~reconciliation_rejects_stale_admissions_) or test(~regulation::request::) or test(~serving_regulation::) or test(~block_sync::serving::) or test(~work_queue::request_write::tests::) or test(~handler::ordered_pair::tests::) or test(~service::block_range::tests::) or (test(~handler::tests::paired_block_sync::) and not test(~handler::tests::paired_block_sync::gate::))'
retries = 0
slow-timeout = { period = "60s", terminate-after = 1 }

# Run explicitly with --run-ignored=all after building; no automatic retry.
[profile.blocksync-transport-gate]
default-filter = 'test(~handler::tests::paired_block_sync::gate::)'
test-threads = 1
retries = 0
slow-timeout = { period = "90m", terminate-after = 1 }
failure-output = "immediate"
11 changes: 4 additions & 7 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,14 @@ keywords = ["zakura", "zcash"]
rust-version = "1.97"
edition = "2021"

# Backport the loss-probe datagram limit fix to Iroh 0.92's exact QUIC versions.
# Keep the related crates on one revision. Workspace patches are not inherited
# by downstream library users; they must apply the same patch in their workspace.
[patch.crates-io]
iroh-quinn = { git = "https://github.com/zakura-core/iroh-quinn", rev = "1dcc7a43488fecd199d343d47e93e9ed8319fcaa" }
iroh-quinn-proto = { git = "https://github.com/zakura-core/iroh-quinn", rev = "1dcc7a43488fecd199d343d47e93e9ed8319fcaa" }
iroh-quinn-udp = { git = "https://github.com/zakura-core/iroh-quinn", rev = "1dcc7a43488fecd199d343d47e93e9ed8319fcaa" }

[workspace.dependencies]
# Zakura's published binary assets: the reviewed Mainnet historical frontier grid, which is too
# large to carry in git history at the weekly cadence the release state advances.
Expand Down
2 changes: 1 addition & 1 deletion crates/zakura-network/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "zakura-network"
version = "7.1.1-rc1"
version = "8.0.0"
authors.workspace = true
description = "Networking code for the Zakura node. Internal crate, published to support cargo install zakura"
# # Legal
Expand Down
16 changes: 10 additions & 6 deletions crates/zakura-network/src/zakura.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,17 +88,21 @@ pub struct ServicePeerLimits {
pub max_inbound_peers: usize,
/// Maximum outbound peers this service admits.
pub max_outbound_peers: usize,
/// Inbound queue depth reserved for this service.
/// Maximum inbound frames queued on each service stream.
///
/// Reserved for future transport queue wiring; not enforced in this phase.
/// Block sync applies this limit alongside the negotiated transport limit.
/// Its paired request stream always uses a single queue slot.
pub inbound_queue_depth: usize,
/// Outbound queue depth reserved for this service.
/// Maximum outbound frames queued on each service stream.
///
/// Reserved for future transport queue wiring; not enforced in this phase.
/// Block sync applies this limit alongside the transport limit. Its paired
/// request stream always uses a single queue slot.
pub outbound_queue_depth: usize,
/// Maximum service escalations that may be pending admission.
/// Maximum paired service sessions that may be establishing at once.
///
/// Reserved for future lazy service escalation; not enforced in this phase.
/// Paired block sync reserves this allowance until both roles arrive or
/// setup ends. Zero disables new paired sessions. Other services currently
/// use their existing admission policy.
pub max_pending_escalations: usize,
}

Expand Down
12 changes: 9 additions & 3 deletions crates/zakura-network/src/zakura/block_sync/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,13 @@ while anything anchored to the verified tip is pinned until real progress commit
| Candidate selection | first pending in `[servable_low, min(servable_high, floor_high)]`, only if this peer is the preferred floor carrier | first pending in the peer's servable range, only when the floor arm produced nothing |
| cwnd slots | may borrow up to `floor_bypass_slots` (default 2) beyond a saturated cwnd — bypass slots fund the floor **only** | normal cwnd slots only |
| Byte funding | never refused: `reserve_request_budget`'s floor path overdrafts the in-flight budget by at most one request when `try_reserve` fails — reachable even at zero in-flight budget | non-blocking `try_reserve`; refused if the in-flight budget is spent |
| Request deadline | short fixed leash (`floor_rescue_timeout`, default 2 s); on expiry the height is rescued to a faster carrier, the peer is retry-avoided but **not** disconnected | `request_timeout` (default 8 s) + expected transfer time (`estimated_bytes / measured BtlBw`, rate floored at 256 KiB/s) — patient, since it never gates the floor |
| Request deadline | with a fresh delivery-rate sample, `floor_rescue_timeout` (default 2 s) + estimated transfer time; without a sample, use the normal deadline so the cold peer's only probe can finish | `request_timeout` (default 8 s) + estimated transfer time |

Both lanes compute transfer time as `pending_response_bytes / measured_bytes_per_second`,
using at least 256 KiB/s to keep the deadline bounded. An unmeasured peer uses
256 KiB/s. Include this request and earlier unreceived responses in the byte
estimate because they share an ordered data stream. Floor expiry returns the missing height for retry; the separate
block-progress deadline controls session cooldown and repeated-stall disconnects.

**Floor carrier preference:** the floor rides the fastest servable peer. Before taking
floor work, a routine asks the shared registry
Expand Down Expand Up @@ -167,7 +173,7 @@ the worst-case decoded contribution is approximately

1. **Commit-window heights are always fundable**, on both lanes, regardless of the
look-ahead gates — a pinned checkpoint range can always assemble.
2. **Floor grants never size below one byte**, and `take_in_range_budgeted` always
2. **Floor grants never size below one byte**, and `take_for_request` always
takes its first item regardless of the byte cap — so the floor block is taken even
when the in-flight budget is exactly full, reaching the floor reservation path…
3. **…which overdrafts instead of waiting.** When `try_reserve` fails,
Expand Down Expand Up @@ -207,7 +213,7 @@ borrowed a bypass slot.
| `max_inflight_block_bytes` | 6 GiB | outstanding-request wire budget, released at receipt (separate from the resident gate) |
| `max_blocks_per_response` | 1 | count cap per request (effective = min of both sides' advertisements, hard max 128) |
| `floor_bypass_slots` | 2 | extra slots past a saturated cwnd, floor lane only |
| `request_timeout` / `floor_rescue_timeout` | 8 s / 2 s | above-floor base deadline / floor rescue leash |
| `request_timeout` / `floor_rescue_timeout` | 8 s / 2 s | normal base deadline / measured-peer floor-rescue base; both add ordered transfer time |
| `max_submitted_block_applies` | 401 | sequencer submit window (floored at one checkpoint range; no ceiling — which is why the exemption span is a constant) |

## Known limitations and follow-ups
Expand Down
Loading
Loading