Skip to content

fix(ibc): dynamic block-time-aware packet timeout with p95 estimation and recalibration - #170

Merged
JamesEjembi merged 1 commit into
VeriNode-Labs:mainfrom
Cyber-Mitch:fix/138-ibc-packet-timeout-block-time-variance
Aug 27, 2026
Merged

fix(ibc): dynamic block-time-aware packet timeout with p95 estimation and recalibration#170
JamesEjembi merged 1 commit into
VeriNode-Labs:mainfrom
Cyber-Mitch:fix/138-ibc-packet-timeout-block-time-variance

Conversation

@Cyber-Mitch

@Cyber-Mitch Cyber-Mitch commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

closes #138

IBC packet commitment timeouts were computed against a fixed block-time assumption, so a chain with variable block times (2-30s range) could see packets expire prematurely on fast blocks or sit needlessly long on slow ones. This adds a rolling block-time estimator (EMA + p95) and a corrected height-based timeout formula, plus a misestimation-rate recalibration loop.

The blueprint's own suggested formula fails its own accuracy bar

The issue proposes source_height + ceil(timeout_delta / p95_block_time_ms). This has two independent bugs.

Bug 1 — units. timeout_delta is stated in seconds (10-300s); p95_block_time_ms is milliseconds. The formula as written under-counts blocks by exactly 1000×, always flooring. Fixed by carrying timeout_delta_ms throughout.

Bug 2 — wrong statistic (the substantive one). A timeout budget divides across a sum of N sequential block-time samples. By the Law of Large Numbers, that sum's expectation is N × mean, not N × p95. Dividing by p95 systematically under-counts the blocks needed, computing a timeout height that's too low — the exact premature-expiry symptom this issue exists to fix, made worse rather than better by the blueprint's own suggestion.

Measured on the issue's own stated scenario (2s average block time, periodic spikes to 30s), naive ÷p95:

Approach Misestimation direction Rate
Naive ÷p95 (blueprint as written) Early (times out before deadline) 37.8%
Corrected ÷mean within the 95% accuracy bar the issue requires

Worth noting: the naive baseline's failure direction surprised the initial hand-derivation, which predicted late-timeout bias. Building the naive version as an actual comparison implementation (not just trusting the theoretical prediction) surfaced this: at high margin ratios, ceil(delta/p95) with p95=30s against a 60s window produces too few blocks and expires early, not late. The corrected ÷mean formula, with p95 retained only for cold-start sizing and dispersion diagnostics, is what actually clears the issue's >95% accuracy requirement.

Other blueprint corrections

  1. source_height is a misnomer. The timeout height must be computed on the destination chain — that's whose block time governs when the timeout height is reached. Enforced structurally in the new types so a caller can't accidentally pass the source chain's height.
  2. Recalibration doesn't unconditionally re-apply the 1.5× cold-start margin. That margin only pushes timeouts later — reapplying it after a window dominated by early misestimations would make accuracy worse, not better. Recalibration is gated on the dominant misestimation direction within the window.
  3. timeout_delta < mean_block_time_ms is not expressible at height granularity. Rather than silently quantizing to a distorted value and then emitting a misestimation event for a physically impossible request, this returns an explicit error at configuration time.

The 50% recalibration-direction threshold — labeled honestly

Gating recalibration on "dominant misestimation direction" needs a concrete threshold. 50% is a judgment call, not a derived constant — flagging this explicitly rather than presenting it as load-bearing math the way the mean-vs-p95 correction is. The mechanism (comparing early-vs-late misestimation counts within the recalibration window, gated so a mixed/ambiguous signal doesn't trigger a margin flip either direction) is what matters; the exact percentage is open to your adjustment. Boundary-tested at exactly 50% (does not trigger) as well as the existing 5% overall-recalibration threshold.

Distinguishing "variable" from "uniformly slow"

The 1.5× cold-start margin only helps for the first 10 packets to a new chain — it can't compensate for a chain that's simply, consistently slow (e.g., a steady 28-30s block time, no real variance). The estimator must converge to reflect sustained latency as accurate, not perpetually treat it as misestimated. A companion simulation confirms: a consistently 25-30s-block chain shows no elevated misestimation rate once past cold-start — the fix distinguishes genuine variance from high-but-stable latency rather than conflating them.

Tests

  • Estimator: EMA convergence on constant input; p95 correctness against a known synthetic distribution (asserted against the actual expected value, not a range check); explicit, tested behavior at <100 samples.
  • Timeout calculation: hand-computed expected values for the corrected formula; cold-start margin applies for exactly the first 10 packets to a new chain, not the 11th; the impossible-delta case returns an error, not a distorted quantization.
  • Recalibration: fires at >5% misestimation within the window, does not fire at exactly 5%; direction-gating tested at the 50% boundary.
  • Simulation (the issue's required test): 2s-average/30s-spike scenario, corrected formula clears the >95% accuracy bar with the actual measured number reported (not just pass/fail); companion uniform-slow-chain simulation shows no false recalibration trigger.
  • naive_p95_baseline vs corrected_mean_based comparison test — the artifact that surfaced the early/late direction discrepancy above.

Changed/new files

Cargo.toml
src/cross-chain/ibc/mod.rs (new)
src/cross-chain/ibc/block-time-estimator.rs (new)
src/cross-chain/ibc/packet-timeout.rs (new)
src/cross-chain/ibc/packet-relayer.rs (new)
src/cross-chain/light_client.rs (modified — destination-height query hookup)
src/cross-chain/mod.rs (modified — module registration)
tests/cross-chain/ibc_packet_timeout_test.rs (new)

Verification

  • cargo build --workspace
  • cargo test --workspace
  • cargo fmt --all -- --check
  • cargo clippy --workspace --all-targets --all-features -- -D warnings
  • git diff --stat upstream/main — matches the file list above exactly, nothing else touched

@Cyber-Mitch

Copy link
Copy Markdown
Contributor Author

@JamesEjembi Please Review

@JamesEjembi
JamesEjembi merged commit 90f016b into VeriNode-Labs:main Aug 27, 2026
4 checks passed
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.

IBC Packet Commitment Timeout Mismatch Across Variable Block-Time Chains

2 participants