Tests: Regression test for multisig change detection and consolidation handling#933
Tests: Regression test for multisig change detection and consolidation handling#933nkatha23 wants to merge 1 commit into
Conversation
b91a825 to
28c700f
Compare
976f78b to
0e71f30
Compare
Can you provide info about the specific coordinator(s) and specific conditions that would create this type of PSBT? |
We already have multisig psbts with self-receive + change as the only outputs. It would be cleaner to just add a few extra checks once those psbts are created: |
For single sig, we have a close-enough existing test harness that just needs to be extended to cover this scenario. See |
|
As of 0e71f30: NACK on No opinion yet on |
When a coordinator strips witness_script/redeem_script from multisig output scopes, _get_policy() returns a bare policy dict with no m/n/cosigners, which never matches the input policy — so no output is classified as change and all value shows as spend_amount. This was the correct conservative behaviour but had no test pinning it. Also adds a consolidation test: a PSBT where both a change-branch (1/*) and a receive-branch (0/*) output belong to the signing wallet. PSBTParser classifies both as internal regardless of branch index, giving spend_amount == 0. Covers all three multisig types for the no-descriptor case, all seven script types for the consolidation case. Part of SeedSigner#931.
0e71f30 to
ff432aa
Compare
No documented coordinator has been shown to produce a multisig PSBT where the change output is missing witness_script. I looked and couldn't find one. The test isn't grounded in a reported user problem, it's a defensive pin of a security property: the parser must never classify an output as change without cryptographic verification of ownership. The risk being guarded against is a future internal refactor, not a current coordinator. |
The test came directly from item 1 in this issue #931 : "Construct a multisig PSBT. Instantiate PSBTParser without loading a descriptor." Since PSBTParser has no descriptor parameter, I interpreted "no descriptor registered" as the output scope lacking witness_script/redeem_script, the fields _get_policy() needs to reconstruct the multisig policy. Without those fields, the output policy dict never matches the input policy, so no output is classified as change. |
In response to issue #931 , this is what I found:
Reading through PSBTParser._parse_outputs(), I noticed two spending scenarios that weren't covered by the existing test suite:
policy from a bare script hash, so it has no way to verify that the output belongs to the wallet. The safe, correct behaviour is to treat it as an external spend. That path existed in the code but was never exercised by a test, meaning a future refactor could silently break it.
classifies any output as internal when the derived key matches the output scriptPubKey, regardless of branch index. Again, this worked but wasn't tested.
so this PR adds:
Two new test methods inside TestPSBTParser in tests/test_psbt_parser.py:
test_multisig_no_descriptor_all_outputs_are_spend- covers all three multisig script types. Strips witness_script and redeem_script from the output scope and pins that num_change_outputs == 0 with everything treated as spend.
test_consolidation_all_outputs_internal- covers all seven supported script types. Two internal outputs (change-branch + receive-branch) both belonging to the signing wallet, pins spend_amount == 0 and change_amount == input_amount - fee.