Skip to content

docs: five wallet-layer claims, verified — one of which was understated - #613

Merged
Zyrtnin merged 1 commit into
mainfrom
fix/wallet-layer-claims
Sep 4, 2026
Merged

docs: five wallet-layer claims, verified — one of which was understated#613
Zyrtnin merged 1 commit into
mainfrom
fix/wallet-layer-claims

Conversation

@Zyrtnin

@Zyrtnin Zyrtnin commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Five prose claims in fund-moving wallet code, flagged by a reviewer that explicitly marked them unverified. All five held on checking — but one's secondary assertion was wrong, in the direction of understating the problem, and that turned into #612.

lead verdict
taproot.py "any caller gets the floor" held — and the "latent" half is wrong, see #612
htlc_leg.py "txid_of resolves via the node" held; mechanism backwards, property fine — all three shipped readers derive locally, which serves the property better
erc20_leg.py "checked alongside both parties" held — the call passes claimant only, deliberately, per the reasoning directly above it
htlc_leg.py "funded balance == negotiated amount" held — the check is <, a deliberate lower bound, correctly documented elsewhere
locator.py "pins which network" held, and there is a real gap

Reported, not fixed

Nothing compares locator.chain_id to anything. EthRpc.assert_chain compares the node to the leg's expected chain id, so a leg pointed at the wrong network is caught — but a locator from a different network driven by a correctly-configured leg is not, and claim() against an address with no code on the target chain does not revert.

Adding that check is a behaviour change on fund-moving code and wants its own review, so it is reported rather than slipped into a prose pass.

Bonus, unprompted

EthHtlcLocator.amount_wei carried the identical false == claim, and swap_state.py carried the twin over-broad "unrepresentable for every caller" — which would have contradicted the taproot correction sitting one call away.

Comments and docstrings only; no behaviour changed. Verified independently: full suite green at baseline skip/xfail counts.

🤖 Generated with Claude Code

Assertive text is a claim, and no test evaluates claims — tests pin that a
sentence is emitted, never that it is correct. Five docstrings/comments in the
swap wallet layer had drifted from the code beside them. Each correction says
what is true and what the text used to say, so anyone who relied on the old
wording knows to re-check. NO BEHAVIOUR CHANGES.

taproot.refund_leaf_script — "Any caller that builds a leaf ... gets the floor by
construction" was false: the refusal is scoped to TimeUnit.BLOCKS. BIP68 quantises
time locks to 512 s, so Timelock(0..511, SECONDS) all encode nSequence 0x00400000
(zero time units) — the same no-op relative lock a 0-block CSV is — and are emitted
without complaint. swap_state.NegotiatedTerms carried the identical over-broad claim
("unrepresentable for every caller") over an identically BLOCKS-scoped condition;
corrected too, since leaving one would have contradicted the other.

btc_wallet.htlc_leg — "txid_of resolves ... VIA THE NODE — never a local segwit
parse" had the mechanism exactly backwards, in the BtcFundingReader Protocol, in
its txid_of stub, and in confirmations_of_claim's own docstring, which contradicted
the comment three lines below it. All three shipped readers (mempool.space,
Bitcoin Core, MultiSource) call taproot.btc_txid_from_raw. The safety property is
unchanged and is better served by the local derivation: the gated txid must be that
of the exact bytes p was scraped from, and serialising them asks nobody.

eth_wallet.erc20_leg.claim — "checked alongside both parties" describes round 4's
address list; round 5 removed the refundee on purpose (a claim never touches it, so
refusing there refuses valid work and hands the counterparty a free unilateral veto)
and the call site passes claimant only.

eth_wallet.htlc_leg module docstring — "funded balance == negotiated amount" where
the check is `bal < expected_amount_wei`, a deliberate LOWER bound (an == check is
griefable by a 1-wei force-send). verify_funded documents it correctly at the check;
EthHtlcLocator.amount_wei repeated the same == claim and is corrected with it.

eth_wallet.locator.EthHtlcLocator.chain_id — "the leg refuses a chain_id mismatch
up front" is false; nothing in src/ or scripts/ compares locator.chain_id to
anything. EthRpc.assert_chain compares the NODE to the LEG's expected_chain_id, a
different check. The field is now documented as a record, not a gate.

Two gaps are REPORTED rather than fixed, because both are behaviour changes on
fund-moving code and need their own review: the BLOCKS-only scoping of the zero-
timelock floors, and the absent locator/leg chain-id comparison.

Verified: ruff check + ruff format --check clean; full suite 10985 passed,
197 skipped, 4 xfailed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@Zyrtnin
Zyrtnin merged commit a389393 into main Sep 4, 2026
17 checks passed
@Zyrtnin
Zyrtnin deleted the fix/wallet-layer-claims branch September 4, 2026 08:20
Zyrtnin pushed a commit that referenced this pull request Sep 4, 2026
#613 documented the BLOCKS-only floor and said 'left as-is deliberately -
widening the refusal is a behaviour change that needs its own review'. This
branch IS that review, and the fix: the refusal now keys on the encoded
magnitude, so it is unit-agnostic by construction. Kept the fix.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant