Skip to content

fix: refuse BTC source send when reservation TTL is too short to extend (#315)#328

Open
Khaostica wants to merge 4 commits into
entrius:testfrom
Khaostica:claude/sweet-montalcini-ded877
Open

fix: refuse BTC source send when reservation TTL is too short to extend (#315)#328
Khaostica wants to merge 4 commits into
entrius:testfrom
Khaostica:claude/sweet-montalcini-ded877

Conversation

@Khaostica
Copy link
Copy Markdown

Summary

Add a CLI-side preflight that classifies remaining reservation TTL at send time and refuses to broadcast source funds into a reservation the validator auto-extension flow cannot rescue. Closes #315.

Problem

alw swap now --from btc --to tao could successfully reserve a miner and broadcast the BTC source transaction, then fail to initiate any on-chain swap because the reservation expired before the required BTC confirmations arrived. Per #315, a user sent 0.00018 BTC against a 50-block (~10 min) reservation — BTC needs 2 confirmations (~20 min), so the reservation expired before the tx confirmed and no TAO was delivered.

#316 addressed two of the five proposed mitigations: it dropped BTC min_confirmations from 3 → 2 and added validator-side auto-extension that fires when the source tx becomes visible. But the auto-extension has a hard prerequisite — validators need at least EXTEND_THRESHOLD_BLOCKS (~18 blocks ≈ 3.6 min) of runway to land their propose tx and let its challenge window close (allways/validator/optimistic_extensions.py:97). Below that the propose is mechanically doomed and the reservation expires anyway. The CLI never checked this, so users could broadcast into a reservation no validator could rescue.

What Changed

  • Added classify_send_runway() in allways/chains.py — pure classifier returning ok / extension_required / too_short based on remaining TTL vs EXTEND_THRESHOLD_BLOCKS plus a 5-block (~1 min) propagation buffer.
  • Added preflight_send_runway() in a new allways/cli/preflight.py module (placed outside swap_commands/ so importing it doesn't trigger that package's eager command-registration __init__.py chain, which pulls in bittensor via helpers).
  • Wired the gate into both alw swap now (after the "Send now?" confirmation) and alw swap resume-reservation --send, re-reading current_block at send time so an idle user at the summary panel doesn't bypass the check.
  • Behavior by band:
    • too_short: hard refusal, suggests starting fresh — overrides --yes to avoid scripted callers silently losing funds.
    • extension_required: warns the validator auto-extension will need to kick in, requires explicit re-confirm (default no) in interactive mode, passes through in --yes.
    • ok: silent passthrough (no prompt, no print).

Implements solutions #3 ("prevent auto-send when required confirmations cannot fit inside reservation TTL") and #5 ("surface a hard preflight error instead of a warning") from #315.

Tests

  • tests/test_chains.py::TestClassifySendRunway — 7 cases covering the classifier's three bands, the BTC vs TAO confirmation-window difference, exact-floor boundary, zero remaining, and already-expired (negative remaining).
  • tests/test_preflight_send_runway.py — 7 cases covering the wiring: subtensor re-read at send time, branch routing per band, and the skip_confirm contract (refuses on too_short even in --yes; passes through extension_required in --yes). The OK band asserts both click.confirm and console.print are untouched, so any future regression that adds chatter to the happy-path swap fails the test.

Verification:

pytest -q tests/test_chains.py tests/test_preflight_send_runway.py

Johnb1109 and others added 4 commits May 14, 2026 11:47
…nd (entrius#315)

PR entrius#316 added validator-side auto-extension of reservations on tx
visibility, but the CLI never refused to broadcast when the remaining
TTL was already below the floor required for that extension flow to
fire. Users could (and did, per entrius#315) send BTC into a reservation that
was mechanically doomed to expire before any propose tx could clear
its challenge window — losing funds when the swap never initiated.

Add a preflight check that reads current_block at send-time and
classifies remaining TTL into ok / extension_required / too_short
against EXTEND_THRESHOLD_BLOCKS + a small propagation buffer. The new
swap and resume-reservation flows hard-refuse below the floor and
soft-warn (require explicit confirm) when validators will need to
auto-extend. Implements solutions entrius#3 and entrius#5 from entrius#315.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Originally the gate lived inside swap.py, which transitively imports
bittensor — so unit tests covering the click.confirm wiring couldn't be
written without spinning up the whole CLI stack. Moves the function to
allways/cli/preflight.py (outside swap_commands/ so importing it doesn't
trigger that package's eager command-registration __init__.py) and adds
tests/test_preflight_send_runway.py covering the seven branch
combinations: ok / too_short / extension_required × skip_confirm.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Stronger version of test_ok_runway_passes_silently — the previous
assertion only covered click.confirm. Now also patches the module's
_console and asserts .print was never called, so the test catches any
future regression that adds chatter to the happy-path swap.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CONTRIBUTING.md requires ruff with E/F/I rules and ruff format. The new
allways.cli.preflight import in swap.py and resume.py wasn't in the
right alphabetical slot, and the new preflight module + test file had a
few multi-line statements the formatter wanted collapsed onto one line.
No behavior change — purely cosmetic.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@xiao-xiao-mao xiao-xiao-mao Bot added the bug Something isn't working label May 14, 2026
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.

BTC source swaps can expire after funds are sent because 3-conf wait exceeds reservation TTL

2 participants