Skip to main content

Invariant

U3

Category

Universal

Priority

Must-have

Violation class

Free Shopping, Asset Theft

What it proves

U2 tests sequential retries. U3 targets a different, harder bug class: a check-then-act idempotency guard that’s perfectly safe when requests arrive one at a time, but has a race window under true concurrency — for example, two requests both passing a “has this been settled yet?” check before either one records that it’s claimed the settlement. Source: USENIX Security ‘26 facilitator study — concurrency races enabling double-settlement (Free Shopping / Asset Theft).

How it works

Ward builds one signed payload, then fires five simultaneous /settle requests for it, released from a single shared gate (util/timing.ts’s dispatchConcurrently) rather than relying on incidental network jitter — so the race is reproducible run to run, not a matter of luck.

Pass condition

The safety-critical assertion is the same as U2’s — at most one distinct on-chain transaction id may result, since identical signed bytes hash to the same txid deterministically:
Ward additionally records (as informative, non-blocking context) whether exactly one HTTP-level success occurred — a facilitator with a correctly implemented concurrency lock should have exactly one request “win” the claim, rather than every request racing algod directly and each echoing success on the same eventual transaction. Both outcomes are reported, but only the distinct-txn count gates pass/fail — see D12. When exactly one distinct transaction results, Ward also calls ChainAdapter.waitForConfirmation() on it and records the real on-chain confirmation as additional evidence.

Next: Allowlist Enforcement