Commit message · kdmukaiCompare cosigners before presuming multisig change
A multisig output whose committed script holds this seed's key can
still belong to a different wallet: a 2-of-3 over {p1,p2,p4} when the
inputs spend {p1,p2,p3}. When the psbt supplies global xpubs and fully
annotates the output, _get_cosigners resolves a cosigner list for the
inputs and one for the output, and lists that differ mean the output
pays a quorum other than the one being spent from. That output is now
counted as an external spend. It stays presumed change when the lists
match or when either side's cosigners are unresolved.
This is hygiene, not a security control. The global xpubs are optional
and unauthenticated, so a coordinator that omits them skips the check,
and a mismatch demotes rather than raises because a transfer into a
second multisig this seed belongs to is legitimate. No surveyed
coordinator produces the shape the check fires on: every one except
Bitcoin Core annotates only its own wallet's outputs, and Core, which
annotates any output a descriptor in its wallet file can solve, writes
no global xpubs at all. Settling which wallet a multisig output pays
still needs the user's own descriptor.
The comparison sits after the ownership checks rather than at the
policy shape gate, so a psbt whose output cosigners fail to resolve
still faces every ownership check.
_get_cosigners' sorted() is what makes the two lists comparable: two of
the three multisig fixtures order the same wallet's keys differently on
the input and on its change output. Its docstring now separates what
is claim from what is checked and what the result does and does not
establish, its loop carries the procedure inline, and two early-outs
name the omitted-xpubs and omitted-derivations cases before the loop
(behaviour-neutral: both already raised from inside it). Tests pin the
sort, the different-quorum spend, and the no-xpubs presumed-change
fallback.
100/100 · StrongMessage clarity
✓ Specific, descriptive subject✓ Names a concrete action or component✓ Provides detailed explanatory context✓ Explains rationale or failure mode✓ Mentions testing or verification✓ Names security-relevant behavior explicitly
Why it was queuedsigning boundaryaccess controlseed or entropy pathsigning or wallet pathparser or protocol path
AI analysis · Low 34/100This commit tightens how SeedSigner decides whether a multisig payment back to the user is real 'change' or actually a payment to a different wallet. Before, if an output contained the user's key and looked like the same kind of multisig (for example, 2-of-3), the app assumed it was change. Now, when the PSBT file includes enough extra public-key data, the app compares the list of cosigners on the inputs versus the output. If the cosigner lists differ, the output is treated as an external spend instead of change. The commit itself calls this 'hygiene, not a security control' because the extra data is optional and unauthenticated, so a malicious or uncooperative coordinator can still bypass the check by omitting it.