Files
zeekay 84add56ce2 swap: the claim and refund windows must never both be open
87.6% -> 99.6%, tests only -- no source file changed.

The two boundary conditions are the whole safety property of an atomic swap,
and nothing pinned them. Moving swap.go:333 from >= to >, or swap.go:398 from
< to <=, opens both windows at one instant and the same swap is claimed AND
refunded. TestZZExactlyOneOutcomeAtEveryClock sweeps the clock and requires
exactly one to succeed at every tick, so a boundary drifting in either
direction fails rather than merely changing behaviour.

46 mutations applied, 46 caught, restore verified by md5 and by an empty diff.

Three defects found and documented rather than fixed, since the brief was
coverage:

A native donation griefs the next lock. Delivery is measured as
balanceOf(swapAddr) - reserve[native], so LUX force-sent by selfdestruct or
coinbase is indistinguishable from msg.value: one wei makes every correctly
built in-flight native lock fail ErrDeltaMismatch, cheaply and repeatably. The
donation is not stolen, it is absorbed by whichever lock finally reconciles.
The ERC-20 path is immune because it measures a per-call transferFrom delta.

A zero preimage is a legal secret that the relay cannot read: getPreimage
returns bytes32(0), byte-identical to never claimed, so a watchtower polling
for the cross-leg secret stalls on its own leg. The event and the status word
still carry the truth, so it is observability, not funds.

The timeout bounds are unguarded uint64 arithmetic. Above 2^64 - MaxTimeout
both sums wrap and a lock is accepted with an expiry in the past -- born
expired, refundable in its own block, with the counterparty window gone.
Unreachable on any real chain (it needs a block timestamp within ~30 days of
584 billion years), so it is pinned as behaviour, not asserted as a bug.

Two gas notes, changed nothing: the ERC-20 sub-call takes no gas parameter and
returns none, so an arbitrarily expensive transfer runs under a flat fee -- the
one genuinely unbounded path. And lock writes nine slots, seven of them
zero->nonzero, for 60,000 gas, so it subsidises permanent state growth by
roughly 3x at reference prices. Fee ordering is pinned so a repricing surfaces
as a failure rather than silently.
2026-08-28 11:35:22 -07:00
..
…
…
…