Only say replay protected when a signature was actually checked - #28
Merged
Merged
Conversation
kwsantiago
force-pushed
the
verified-optin-count
branch
2 times, most recently
from
September 5, 2026 20:59
34871fa to
5cc25df
Compare
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
force-pushed
the
verified-optin-count
branch
2 times, most recently
from
September 6, 2026 00:59
7e05bf0 to
aac341e
Compare
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
force-pushed
the
verified-optin-count
branch
from
September 6, 2026 01:15
aac341e to
b0e0187
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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.