Validator: reject dest-tx replays via init-time tip snapshot#346
Open
LandynDev wants to merge 2 commits into
Open
Validator: reject dest-tx replays via init-time tip snapshot#346LandynDev wants to merge 2 commits into
LandynDev wants to merge 2 commits into
Conversation
Mirrors the contract's used_from_tx source-side defense on the dest side without a contract upgrade: on first sighting of a non-TAO-dest swap, SwapVerifier snapshots the dest chain's tip. Later, a miner-supplied dest tx whose block predates the snapshot is rejected as a replay. TAO-dest uses swap.initiated_block directly (already on the swap struct). Chain-agnostic — adding a new dest chain only needs get_current_block_height on its provider. Fails open with a one-time warning if the tip RPC errors, preserving liveness over defense on a transient backend issue.
Tighter class docstring, single-line ivar comment, collapsed dual log on snapshot failure, dropped redundant fail-open log at verify time (observe_initiation already logged), shorter base.py + forward.py comments. No behavior change — same tests, same defense.
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
The contract's
used_from_txmapping (lib.rs:95, checked at 841) blocks reuse of a swap's source tx hash but has noused_to_txanalog for the miner's dest tx hash. Combined with the validator'sverify_transactiononly checking existence/recipient/amount/sender (chain_providers/base.py:71), a miner whose previous fulfillment tx happens to pay the same user the same amount can recycle that on-chain tx as fake fulfillment for a new swap — collecting the user's escrowed TAO without sending anything.This PR closes that gap validator-side without a contract upgrade.
Mechanic
SwapVerifier.observe_initiation(swap): on first sighting of a non-TAO-dest swap, snapshot the dest chain's current tip (minus a 1-block grace so a tx mined in the same block we sampled still passes). Idempotent; fails open with a one-timelog_on_changewarning if the tip RPC errors.SwapVerifier.is_dest_tx_fresh(swap, dest_info): rejects any dest tx whose block is< swap.initiated_block(TAO-dest) or< dest_tip_at_init[swap.id](other chains).forward.py: callsobserve_initiation+prune_to_activeonce per forward step. Snapshots are bounded by the active swap set.Chain-agnostic — adding a new dest chain only needs
get_current_block_heighton its provider. Same pattern would catch ETH/SOL/whatever dest-side replays the moment the chain is wired in.Why validator-side, not contract-side
A
used_to_txmapping on the contract (parallel to the existingused_from_tx) would be a strictly stronger defense, but ships only on the next contract deployment. The validator check covers the gap now; the contract mirror can land in a future deploy.Test plan
pytest -q— full suite (484 passed, 16 new intests/test_chain_verification.py).ruff format+ruff checkclean.