fix: align miner timeout cushion with validator extension runway#356
Merged
Conversation
This was referenced May 20, 2026
added 2 commits
May 20, 2026 18:23
Both the miner-side fulfillment cushion and the user-side reservation post-tx flow gated on deadlines without referencing the validator's extension propose runway. Result: a miner could start fulfilling at remaining=6, and a user could broadcast confirm at remaining=1, both landing inside the window where validators refuse to propose an extension (current + CHALLENGE_WINDOW >= deadline). When dest inclusion dragged or the source tx hadn't confirmed yet, there was no rescue path. Changes: - Pin both cushions to EXTEND_THRESHOLD_BLOCKS (= forward step + challenge window). MINER_TIMEOUT_CUSHION_BLOCKS goes from 5 to 18; USER_POST_TX_CUSHION_BLOCKS is new. - Drop the MINER_TIMEOUT_CUSHION_BLOCKS env override and the load_timeout_cushion_blocks hot-reload helper. The right value is system-determined (validator runway), not operator preference; miners who need a different value edit constants.py directly. - Add safe_reservation_remaining helper used by post-tx and resume to abort with a clear message when runway is inside the cushion. - Remove the env-override tests; replace with constant-based boundary tests in test_fulfillment.py. - Drop MINER_TIMEOUT_CUSHION_BLOCKS from .env.example and README; .env.example keeps a one-line deprecation note.
The note is noise for the 99% of operators who never set the env var in the first place. Anyone still setting it sees no effect and can find the constant in constants.py.
33d8ee4 to
de32a02
Compare
The auto-send paths fire near-instantly after reserve (1-2 blocks elapsed out of 50), so the user-side cushion virtually never gates anything real: - alw swap now --auto: send happens seconds after reserve; cushion never fires - alw swap now (manual): cushion at post-tx is post-facto, funds already sent - resume-reservation --send near deadline: niche edge case - resume-reservation manual: same as post-tx, funds already sent Removes USER_POST_TX_CUSHION_BLOCKS, safe_reservation_remaining helper, the post_tx.py / resume.py wire-ins, and the test file. Keeps the miner-side alignment (the real safety win in the extension-rescue path) and the env-override removal.
LandynDev
approved these changes
May 21, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Aligns the miner-side fulfillment cushion with the validator extension flow and removes the env override that let operators silently misconfigure it.
Today
DEFAULT_MINER_TIMEOUT_CUSHION_BLOCKS = 5, but validators refuse to propose a timeout extension oncecurrent + CHALLENGE_WINDOW_BLOCKS >= deadline(optimistic_extensions.py:202) and won't reliably land one untilcurrent + EXTEND_THRESHOLD_BLOCKSof runway exists (forward.py:293). With cushion=5, a miner could start fulfilling at remaining=6 — inside the validator's no-rescue window. If dest-chain inclusion drags, the extension flow that's supposed to save the swap can't propose, and the miner eats a slash on a swap that was meant to be rescued.What changed
MINER_TIMEOUT_CUSHION_BLOCKSpinned toEXTEND_THRESHOLD_BLOCKS(= forward step + challenge window = 18). The miner now stops starting new fulfillments exactly where the validator can no longer rescue them.load_timeout_cushion_blocks(), the hot-reload helper,osimport infulfillment.py, and theMINER_TIMEOUT_CUSHION_BLOCKSline from.env.exampleandREADME.md. Operators who genuinely need a different value editconstants.py.test_fulfillment.py.Scope notes
Originally the PR also added a user-side cushion in
post-tx/resume-reservation, but on review those gates only ever fired post-facto (funds already sent) or in niche resume-after-long-interruption cases. The auto-send paths inalw swap nowfire seconds after reserve, so a cushion there never gates anything real. Dropped the helper, constant, wire-ins, and tests — the miner-side change is the only one doing real safety work.Test plan
pytest tests/test_fulfillment.py tests/test_probe_pending_reservation.py— 18/18 passMINER_TIMEOUT_CUSHION_BLOCKS == EXTEND_THRESHOLD_BLOCKS == 18DEFAULT_MINER_TIMEOUT_CUSHION_BLOCKS,load_timeout_cushion_blocks, orUSER_POST_TX_CUSHION_BLOCKSanywhereMINER_TIMEOUT_CUSHION_BLOCKSenv var set, confirm it's silently ignored and miner uses 18-block cushion from constants.