Skip to content

fix: BTC swap robustness — extension runway, RBF/canonical-chain, 2 confs#316

Merged
anderdc merged 14 commits into
testfrom
fix/btc-swap-robustness-extensions-and-confs
May 12, 2026
Merged

fix: BTC swap robustness — extension runway, RBF/canonical-chain, 2 confs#316
anderdc merged 14 commits into
testfrom
fix/btc-swap-robustness-extensions-and-confs

Conversation

@LandynDev
Copy link
Copy Markdown
Collaborator

Problem → Solution

Problem 1 — tier-0 extension too short for BTC variance

  • BTC block intervals follow a Poisson process; single blocks regularly take 30+ min (https://en.bitcoin.it/wiki/Confirmation: 99.7% chance of a block within 60 min, not guaranteed)
  • Observed in prod swap . #2: 27-min block → tier-0 expired before first conf arrived → honest miner slashed
  • Tier-0 remaining=1 → 4. Lands at +240 sub-blocks (~48 min runway from current).

Problem 2 — tier-1 deadlocks when conf=1 arrives late

  • Tier-1's confs≥1 gate + 8-block challenge-window guard intersect at zero when BTC polling lags by ≥1 block at the deadline
  • Drop the evidence gate. Tier-1 fires on visibility, same as tier-0. Race disappears; tier-1 becomes a tail-of-distribution safety net.

Problem 3 — VALIDATOR_FORWARD_STEP_BLOCKS_ESTIMATE is 15× more conservative than reality

  • Constant = 20 sub-blocks; measured = ~1.3 sub-blocks/step from production step=N logs
  • Flows into EXTEND_THRESHOLD_BLOCKS = ESTIMATE + CHALLENGE_WINDOW_BLOCKS = 18 (was 28)
  • 20 → 10. Still 7× conservative; trims wasted cycles iterating not-yet-near-deadline swaps.

Problem 4 — RBF-flagged BTC txs are replaceable post-broadcast

  • Without BIP-125 rejection, a miner could broadcast, secure a tier extension, then RBF the outputs back to themselves
  • Reject any vin with sequence < 0xfffffffe in both verify paths (RPC + Esplora)

Problem 5 — Esplora confirmation count doesn't verify canonical chain

  • bitcoind RPC inherently filters orphaned blocks (confirmations → 0)
  • Esplora API computes tip_height − block_height + 1 — doesn't check in_best_chain
  • On API path, query /block/{hash}/status and reject if in_best_chain == false

Problem 6 — 3 BTC confs is conservative inertia, not necessity

  • With RBF rejection + canonical-chain check + ~98-min total budget, 2 confs gives ~10× safety margin over 1 conf
  • Wiki: 2-conf reorg rate ~0.1% historical; 1-conf ~1%
  • min_confirmations: 3 → 2

Anchor fix (commit 0ebace7)

Originally landed with compute_extension_target anchoring on max(current, deadline). Self-review caught that the contract enforces target_block - current_at_exec ≤ MAX_EXTENSION_BLOCKS = 250 (lib.rs:670, :1090), so remaining=4 (240 sub-blocks) anchored on deadline produced target - current ∈ [249, 257] at propose time — only a 2-block fitting window. Now anchored on current_block only; cap-safe at any propose time, with ~44-46 min runway past the existing deadline.

Total budget math

Phase Duration
Initial fulfillment window ~10 min (contract config)
Tier-0 extension ~44-46 min past prior deadline
Tier-1 extension ~44-46 min past prior deadline
Total ~98-102 min

Need 2 BTC confs in ~98 min. Time-to-2nd-block ~ Gamma(2, μ):

  • Nominal pace (μ=10 min): P(miss) ≈ 0.06%
  • Slow pace (μ=13, recent observed): P(miss) ≈ 0.46%
  • Pathological (μ=20): P(miss) ≈ 5.6%

Well under the 1% target across reasonable conditions. Sized using https://en.bitcoin.it/wiki/Confirmation.

Test plan

  • ruff check allways/ neurons/ — clean
  • ruff format --check . — all formatted
  • pytest tests/ — 411/411 passing locally
  • REPL: compute_extension_target('btc', 4, 1000) → 1240, delta 240 ≤ 250 cap
  • After merge: pull on lena, restart aw-vali container, watch next BTC swap for tier-0 propose at target_block ≈ current + 240

@xiao-xiao-mao xiao-xiao-mao Bot added the bug Something isn't working label May 12, 2026
LandynDev added 4 commits May 12, 2026 13:49
BIP-125 only replaces mempool txs; with min_confirmations=2 the tx is
already buried under another block by the time verify returns confirmed,
so RBF is mechanically moot. Canonical-chain check still covers reorg.
Removing avoids false-rejecting modern wallets that flag RBF by default.
No method body reads it anymore — tier-1's confirmation gate and the
remaining = min_confs - observed math were both removed when remaining
collapsed to a fixed 4. Drop it from the four method signatures, the four
forward.py call sites, and the test fixtures so the surface matches the
behaviour.
@anderdc anderdc merged commit d54d143 into test May 12, 2026
3 checks passed
@anderdc anderdc deleted the fix/btc-swap-robustness-extensions-and-confs branch May 12, 2026 21:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants