Skip to content

Verify the wallet's own signatures before building on a stored spend - #467

Open
peachbits wants to merge 6 commits into
masterfrom
matthew/verify-spend-signatures
Open

peachbits wants to merge 6 commits into
masterfrom
matthew/verify-spend-signatures

Conversation

@peachbits

Copy link
Copy Markdown
Contributor

Requested by Matthew

CHANGELOG

Does this branch warrant an entry to the CHANGELOG?

  • Yes
  • No

Dependencies

none

Description

Asana task

RBF acceleration rebuilds a replacement from the stored inputs and outputs of the transaction being replaced. This makes the engine check that a stored unconfirmed spend carries the wallet's own signatures, and that the stored data matches the signed hex, before it builds on that record.

  • verifyInputSignature (keymanager): loads the transaction into a PSBT the way makeTx builds one, lifts the signature and pubkey out of the finalized scriptSig or witness, and validates with Psbt.validateSignaturesOfInput, the same call signTx runs on every input it signs. SIGHASH_ALL only; p2pkh, p2wpkh and p2sh-p2wpkh inputs.
  • verifyOwnInputSignatures (engine): applies it to every wallet-owned input, requires the stored inputs and outputs to be exactly the ones in the hex with each index listed once, and reports each input's proven value.
  • accelerate(): requires a valid result, takes output scripts, input values and the replaced fee from the verified data, and returns spendTargets naming the replacement's recipients.
  • Sync: a first-seen unconfirmed spend from the wallet whose signatures do not verify is not recorded. One the verifier cannot judge (no hex, an unsupported coin or script) is stored as before. Confirmed transactions are left alone.

Coins whose signing digest the PSBT validator does not reproduce (Bitcoin Gold, Groestlcoin, SmartCash) are reported as unverifiable. None of them enable RBF, so accelerate() is unaffected for them.

Testing

  • test/common/utxobased/engine/verifyOwnInputSignatures.spec.ts covers bip44, bip49 and bip84 spends, the records the engine stores for its own sends (including a max spend and a replacement that adds an input), and the rejection cases.
  • On an iOS simulator build with a testnet wallet: a pending send survives a wallet resync and Accelerate is still offered for it.

The GUI change that displays spendTargets is a separate PR in edge-react-gui.

@cursor

cursor Bot commented Nov 12, 2026

Copy link
Copy Markdown

Bugbot is paused — on-demand spend limit reached

Bugbot uses usage-based billing for this team and has hit its on-demand spend limit.

A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue.

@j0ntz j0ntz left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed at 87178aa (high): nothing blocking. The inline comments carry two warnings and three suggestions.

Nit (commit-subject-50): 76f393b subject is 68 chars. Suggest Verify own input signatures before accelerating (47).

Rejected in curation (3)
  • src/common/utxobased/engine/util/verifyOwnInputSignatures.ts:98: txid check returns invalid for coins whose txid hash differs from double-SHA256. Rejected: parsed.getId(coinInfo.txHashFunction) uses the coin's own hash function (Groestlcoin included).
  • src/common/utxobased/engine/UtxoEngine.ts:198: accelerate() has no try/catch around the verifier. Rejected: a throw fails closed (no replacement is built) and surfaces to the GUI as an error, which is the safe outcome.
  • src/common/utxobased/engine/UtxoEngine.ts:193: accelerate is now refused for transactions the verifier cannot check (eCash Schnorr, records stored before upgrade). Rejected: this is the PR's stated intent.

The engine stores transactions as reported during sync, and RBF
acceleration rebuilds a replacement from the stored inputs and outputs of
the transaction being replaced. It now first checks that the stored
record is a transaction this wallet signed.

`verifyInputSignature` loads the transaction into a PSBT the way `makeTx`
builds one, lifts the (signature, pubkey) pair out of the finalized
scriptSig or witness into `partialSig`, and lets
`Psbt.validateSignaturesOfInput`, the same call `signTx` runs on every
input it signs, recompute the digest and verify it against the expected
key. It accepts SIGHASH_ALL only, and handles p2pkh, p2wpkh and
p2sh-p2wpkh inputs. `verifyOwnInputSignatures` applies it to every input
the wallet owns, and also requires the stored inputs and outputs to be
exactly the ones in the hex, each index listed once.

`accelerate()` refuses any transaction that does not verify. The sync
path does not record an unconfirmed transaction that spends from the
wallet when its signatures do not verify; it keeps ones it cannot judge,
so coins with scripts the verifier does not handle behave as before, and
confirmed transactions are left to the chain.

`accelerate()` also returns `spendTargets` naming every output that is
not the wallet's own, so the confirmation can show where the funds go.
@peachbits
peachbits force-pushed the matthew/verify-spend-signatures branch from 87178aa to 61b995b Compare November 13, 2026 23:18
@cursor

cursor Bot commented Nov 13, 2026

Copy link
Copy Markdown

Bugbot is paused — on-demand spend limit reached

Bugbot uses usage-based billing for this team and has hit its on-demand spend limit.

A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue.

@peachbits
peachbits force-pushed the matthew/verify-spend-signatures branch from 44d14bf to b4c53cd Compare November 13, 2026 23:39
@peachbits

Copy link
Copy Markdown
Contributor Author

All five inline comments are addressed in fixups. The commit subject is now Verify own input signatures before accelerating.

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.

2 participants