Skip to content

Test gaps: PSBTParser self-output classification and QR type detection #931

Description

@kdmukai

After reviewing a number of PRs that want to incorporate hypothesis testing (full disclosure: I did not look deeply into any of them), I used Claude to determine what we'd actually gain from this approach.

In the end, it seemed simpler to me to just create new targeted test cases that directly address what "property-based testing" (to the extent I understand it) would be aiming for.

Below is Claude's list of specific test case additions.


Recommended SeedSigner test additions

These all belong in tests/test_psbt_parser.py or tests/test_decodepsbtqr.py
and use the existing pytest harness — no new dependencies.

1. Multisig PSBT, no descriptor registered

Construct a multisig PSBT (any of the three existing fixture types).
Instantiate PSBTParser without loading a descriptor. Assert:

  • num_change_outputs == 0 (no output trusted as self/change)
  • spend_amount == sum of all outputs
  • verify_multisig_output(...) is not called or returns False on every
    output

2. Lying-coordinator (single-sig): forged BIP_DERIVATION on output

For each of the four single-sig types, take a fixture and modify one
output's PSBT_OUT_BIP32_DERIVATION to claim a path that does NOT
derive the output's actual scriptPubKey from the on-device seed.
Assert that output is NOT classified as change/self.

3. Lying-coordinator (multisig): forged BIP_DERIVATION with correct

descriptor loaded
Same construction as #2 but for multisig with the correct descriptor
loaded. Assert verify_multisig_output(...) returns False.

4. Multi-output consolidation across both branches

Construct a PSBT with no external outputs — multiple outputs to the
device's own addresses, mixing the change branch (1/*) and receive
branch (0/*). Assert:

  • spend_amount == 0
  • change_amount == input_amount - fee_amount
  • All outputs classified as self

5. Multisig descriptor that doesn't contain the device's xpub

Load a descriptor whose key set does not include the device's xpub at
all (different signing quorum entirely). Construct any multisig PSBT.
Assert verify_multisig_output(...) returns False on all outputs.

6. Self-output at non-standard derivation path

Construct PSBTs with self-outputs at:

  • A high index (e.g. 0/9999)
  • A non-standard branch (e.g. m/.../2/*)
    Assert correct self-classification (high index) and correct rejection
    or non-classification (non-standard branch — pin whatever the policy is).

7. Regression test for the known duplicate-branch bug

Pin the current behavior of the bug referenced in PR #929 / the
proposal. Include a docstring explaining the bug, expected behavior
once fixed, and link to the issue.

8. QR type-detection boundary cases (DecodeQR)

Feed DecodeQR.add_data() inputs that could be ambiguous between
detected types:

  • A base64 string that is also a valid prefix of another format
  • A UR-prefixed payload with a non-PSBT type
  • Inputs at the boundary between PSBT formats (UR2 vs Specter vs BBQR)
  • An input that could parse as both seed-type and wallet-json-type
    For each, assert the chosen qr_type matches the documented priority
    order. Pins the detection policy against silent regressions.

And then a separate section for embit test gaps:

Recommended embit upstream contributions

The following gaps exist in embit's test_psbt.py negative coverage
and would be appropriate as upstream PRs to
github.com/diybitcoinhardware/embit, not as SeedSigner tests.

Byte-level robustness

  • Length-prefix mismatch: declared length exceeds remaining bytes
  • Length-prefix mismatch: declared length is zero where data is expected
  • Truncated input mid-key, mid-value, mid-section
  • Integer overflow on amount fields (values near 2^64 - 1)

Each should assert the parser raises a specific exception cleanly,
without leaking a generic Python exception or silently truncating.

embit already covers BIP-174 invalid-vector compliance, duplicate keys,
invalid typed keys at all levels, malformed descriptors, and
sortedmulti vs multi key ordering — so those don't need duplication
upstream or in SeedSigner.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions