Follow-up to #414. PR #500 ships the destination-validation half (responder re-derives the expected sweep destination from the persisted NEW descriptor and refuses to sign if it differs).
The amount-validation half is still open. Responders trust the proposer's witness_utxo.value_sats verbatim. Because the BIP-341 sighash commits to prevout amounts, an understated input value defeats:
- The 1/
MAX_SWEEP_FEE_FRACTION cap in request_descriptor_migration_sweep.
- The absolute fee cap inside
RecoveryTxBuilder.
A proposer who declares 10000 sats when the on-chain UTXO is actually 100000 sats produces a sighash that signs to a fee far above what the caps would have allowed if computed honestly. Result: fee-griefing / value loss to whoever sees the broadcast first.
What it needs
A chain-view check on the responder side. The responder must independently fetch the prevout amount (and ideally script) before signing instead of accepting the proposer's claim.
Options for the chain view (in order of operational complexity):
- esplora/blockstream HTTP client — small, no node required, depends on a public service.
- bitcoind JSON-RPC — host runs a node, lowest trust.
- electrum protocol — middle ground, more reliable than HTTP.
PR #500 leaves a TODO marker at the destination-check site (keep-cli/src/commands/wallet.rs, keep-desktop/src/app/wallet.rs) noting where the amount check should fire.
Acceptance
- Before any PSBT signing on the recovery path, the responder verifies each input's
witness_utxo.value_sats against the live chain view.
- A divergence aborts the signing with a clear error.
- Operator can configure the chain endpoint (
KEEP_CHAIN_URL env var or similar).
- Test against a regtest harness.
Follow-up to #414. PR #500 ships the destination-validation half (responder re-derives the expected sweep destination from the persisted NEW descriptor and refuses to sign if it differs).
The amount-validation half is still open. Responders trust the proposer's
witness_utxo.value_satsverbatim. Because the BIP-341 sighash commits to prevout amounts, an understated input value defeats:MAX_SWEEP_FEE_FRACTIONcap inrequest_descriptor_migration_sweep.RecoveryTxBuilder.A proposer who declares 10000 sats when the on-chain UTXO is actually 100000 sats produces a sighash that signs to a fee far above what the caps would have allowed if computed honestly. Result: fee-griefing / value loss to whoever sees the broadcast first.
What it needs
A chain-view check on the responder side. The responder must independently fetch the prevout amount (and ideally script) before signing instead of accepting the proposer's claim.
Options for the chain view (in order of operational complexity):
PR #500 leaves a TODO marker at the destination-check site (
keep-cli/src/commands/wallet.rs,keep-desktop/src/app/wallet.rs) noting where the amount check should fire.Acceptance
witness_utxo.value_satsagainst the live chain view.KEEP_CHAIN_URLenv var or similar).