Skip to content

fix(mint): distinguish unparsable witness from absent witness - #1130

Open
KvngMikey wants to merge 3 commits into
cashubtc:mainfrom
KvngMikey:fix/unparsable-witness-error
Open

fix(mint): distinguish unparsable witness from absent witness#1130
KvngMikey wants to merge 3 commits into
cashubtc:mainfrom
KvngMikey:fix/unparsable-witness-error

Conversation

@KvngMikey

Copy link
Copy Markdown
Member

Closes #1126

A witness carrying no signatures must keep reporting code 11000 with
"no signatures in proof." — cashu-ts#991 is widening its assertions
around exactly these values (cashubtc#1126, question 1).

Pins both rows of that contract, non-SIG_ALL and SIG_ALL, plus the
{"signatures":[]} witness. That last one reaches the same outcome by a
different route: it goes through the parser rather than returning ahead
of it, which makes it the case that would move if the parse/no-parse
boundary in from_p2pk_witness ever shifted.

Asserts the code explicitly rather than the type alone: TransactionError
is a base class, so pytest.raises on it would still pass if the code
drifted.

No source changes.
Copilot AI lite review requested due to automatic review settings August 23, 2026 17:04
@github-project-automation github-project-automation Bot moved this to Backlog in nutshell Aug 23, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR refines mint-side spending-condition error reporting to distinguish unparsable witnesses from absent witnesses (while keeping TransactionError code 11000), and adds unit tests to pin the client-visible (“wire contract”) behavior discussed in #1126.

Changes:

  • Add a malformed flag to witness parsing helpers and raise a TransactionError("witness could not be parsed.") for unparsable P2PK/HTLC witnesses.
  • Add unit tests that pin absent-witness behavior ("no signatures in proof.", code 11000) and new unparsable-witness behavior ("witness could not be parsed", code 11000) across P2PK, HTLC, and SIG_ALL paths.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

File Description
cashu/mint/conditions.py Tracks malformed witness parsing and raises a specific parse-failure TransactionError.
tests/mint/test_spending_conditions_unit_p2pk.py Adds tests pinning absent/empty-signatures behavior and asserting parse-failure behavior for malformed P2PK witnesses.
tests/mint/test_spending_conditions_unit_htlc.py Adds tests asserting malformed HTLC witness is a parse failure while “missing preimage” remains a distinct error.
tests/mint/test_spending_conditions_unit_sigall.py Adds test pinning SIG_ALL absent-witness behavior (no signatures, code 11000).

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread cashu/mint/conditions.py
@codecov

codecov Bot commented Aug 23, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 74.88%. Comparing base (3282be2) to head (9452a44).
⚠️ Report is 6 commits behind head on main.
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1130      +/-   ##
==========================================
+ Coverage   74.85%   74.88%   +0.03%     
==========================================
  Files         112      112              
  Lines       12589    12598       +9     
==========================================
+ Hits         9423     9434      +11     
+ Misses       3166     3164       -2     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

A witness the mint cannot read was reported as "no signatures in proof.",
the same message as a witness that was never sent. That points a client
author at their signing code when the fault is in their serialization.

Flag the parse failure on WitnessForP2pkOrHtlc and report it where the
verifier decides what to surface, after both the primary and refund paths
have been tried. A malformed witness can never satisfy the primary path
-- P2PK requires a signature and has none, HTLC always carries a preimage
hash while the witness has no preimage -- and a zero-sig refund path
still returns before that point. Every success and failure outcome is
unchanged; only the message improves, and it now outranks the refund
attempt's own "no signatures in proof." complaint.

Raising from the witness constructors instead would be shorter but wrong:
they are evaluated as arguments to
_verify_p2pk_or_htlc_spending_requirements, so they run before the
primary/refund fallback is entered. An expired lock with no refund
pubkeys is spendable without a witness signature, and a proof carrying a
garbage witness on that path spends today -- raising at parse time would
start rejecting it. That is a logic change, not a message change.

The absent-witness code and message are unchanged, pinned by the tests in
the preceding commit, so cashu-ts#991's widened assertions stay valid.
The new message keeps code 11000; no new error class.

Note the two witness types differ in what they can reject: P2PKWitness
requires "signatures", so wrong-shape JSON is a parse failure, while
HTLCWitness declares every field optional, so only unreadable input is.

Closes cashubtc#1126

@a1denvalu3 a1denvalu3 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This works but I think introducing a malformed field that has the only purpose of indicating an error is ugly.

Parse the witness inside the requirements verifier so the constructors
can raise directly, dropping the malformed field. Behaviour unchanged.
@KvngMikey

Copy link
Copy Markdown
Member Author

This works but I think introducing a malformed field that has the only purpose of indicating an error is ugly.

@a1denvalu3, yes ! I moved the witness parsing into the shared verifier. The constructors now either return a valid normalized witness or raise TransactionError("witness could not be parsed."), so there is no longer any error state on the dataclass.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Backlog

Development

Successfully merging this pull request may close these issues.

#1008 changed P2PK witness error codes and messages: intentional?

3 participants