Skip to content

Only say replay protected when a signature was actually checked - #28

Merged
kwsantiago merged 3 commits into
masterfrom
verified-optin-count
Sep 6, 2026
Merged

kwsantiago merged 3 commits into
masterfrom
verified-optin-count

Conversation

@kwsantiago

@kwsantiago kwsantiago commented Sep 5, 2026

Copy link
Copy Markdown
Collaborator

The transaction view could report Replay protected for a transaction carrying none: any 64 or 65 byte push decodes as a Schnorr signature whose hash type is its own last byte, so a control block, an uncompressed public key, or a partial signature a stranger added and finalising drops all counted as an opt-in. An opt-in now counts only where a signature verifies against a key this wallet derives, for an input whose spent output matches the script this wallet derives for that node. What could not be checked reads as not checked rather than as protected.

Proven against a Bitcoin Knots regtest node in forged_optin_label.py: the wallet counts no opt-in on a PSBT a stranger added an opted-in signature to, and the transaction it broadcasts is accepted by a node that never scheduled the fork, which is what not being protected means.

Needs privkeyio/drongo#5 and privkeyio/drongo#7, which this branch points at.

@kwsantiago
kwsantiago force-pushed the verified-optin-count branch 2 times, most recently from 34871fa to 5cc25df Compare September 5, 2026 20:59
@kwsantiago kwsantiago changed the title Count an opt-in only where a key this wallet vouches for made it Only say replay protected when a signature was actually checked Sep 5, 2026
Any 64 or 65 byte push decodes as a Schnorr signature whose hash type is its own last byte, so a taproot control block, an uncompressed public key, or a partial signature a stranger added and finalising drops all counted as an opt-in.

An opt-in now counts only where a signature verifies against a key this wallet derives, for an input whose spent output matches the script this wallet derives for that node. Keys are never taken from the PSBT, and a wallet built out of one is refused, since either would be the file agreeing with itself. What could not be checked stays in the denominator, so the claim reads as incomplete rather than as settled.

The work a PSBT can ask for on the drawing thread is bounded by cheap guards: the pushes an input is worth reading, the checks one input and one transaction may ask, and the pushes the liftable warning walks.
@kwsantiago
kwsantiago force-pushed the verified-optin-count branch 2 times, most recently from 7e05bf0 to aac341e Compare September 6, 2026 00:59
The transaction view reports what the signatures actually carry, and separates the two reasons a transaction does not opt in: checked and found to be the old kind, or not checked at all. Where nothing has signed yet it says what the transaction will be, since a declaration is what the file wrote for itself rather than something this checked.

The reading is taken again on every event that can change it, including after finalising, which can drop the signature that opted in. Both labels fail closed: they are written saying nothing was checked before anything that could throw.

The send screen's sentence is worked out apart from the label so every decision can be checked, which is how the transaction view's version was found printing the word null for an ordinary multisig.
@kwsantiago
kwsantiago merged commit 1bae88c into master Sep 6, 2026
@kwsantiago
kwsantiago deleted the verified-optin-count branch September 6, 2026 01:18
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