Component: web-wallet/src/withdraw.rs (complete_canonical_withdrawal, persist_canonical_wallet), docs/README.md
Severity: Design risk (acknowledged in docs; filed for tracking)
Summary
Canonical close freezes the wallet's known binding-resolution set and chain tip before signing the one-input/one-output key-path close transaction. The docs acknowledge the race:
"A binding first discovered after the freeze blocks completion while Mercury state remains. A payment discovered only after Mercury/lockbox deletion can be unrecoverable."
Concretely:
- Freeze → sign close tx →
CloseArmed → POST /withdraw/complete → Mercury deletes its state.
- A duplicate funding payment to the aggregate address that confirms in the window between freeze and deletion blocks completion (good — fails closed).
- But one that confirms after Mercury/lockbox state is gone has no sweep path: the server share needed for the cooperative key-path spend no longer exists, and the binding is not part of any statechain the wallet tracks as recoverable.
The value is not stolen, but it is stranded on-chain under an aggregate key whose server half was deleted.
Suggested direction
- Delay Mercury state deletion by N blocks after the close tx confirms, keeping a server-side sweep window; and/or
- Have the wallet retain the materials needed for duplicate sweeps after close, and verify whether a post-close sweep is possible without the server (the funding-update leaf spend needs the aggregate update signature the wallet holds).
Found during security review of feature/bip448-web-wallet-mutinynet @ 64d2423.
Component:
web-wallet/src/withdraw.rs(complete_canonical_withdrawal,persist_canonical_wallet),docs/README.mdSeverity: Design risk (acknowledged in docs; filed for tracking)
Summary
Canonical close freezes the wallet's known binding-resolution set and chain tip before signing the one-input/one-output key-path close transaction. The docs acknowledge the race:
Concretely:
CloseArmed→POST /withdraw/complete→ Mercury deletes its state.The value is not stolen, but it is stranded on-chain under an aggregate key whose server half was deleted.
Suggested direction
Found during security review of
feature/bip448-web-wallet-mutinynet@ 64d2423.