Tighten nut-11 spending conditions #358

Open
robwoodgate wants to merge 5 commits from robwoodgate/nut11-malformed into main
robwoodgate commented 2026-04-01 21:41:48 +00:00 (Migrated from github.com)

This PR removes ambiguity by tightening the spending conditions to specifically reject malformed P2PK secrets.

This PR removes ambiguity by tightening the spending conditions to specifically reject malformed P2PK secrets. - Nutshell: https://github.com/cashubtc/nutshell/pull/969 - CDK: https://github.com/cashubtc/cdk/pull/1880 - Cashu-TS: https://github.com/cashubtc/cashu-ts/pull/578
chatgpt-codex-connector[bot] commented 2026-04-01 21:41:53 +00:00 (Migrated from github.com)

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

You have reached your Codex usage limits for code reviews. You can see your limits in the [Codex usage dashboard](https://chatgpt.com/codex/settings/usage). To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your [settings](https://chatgpt.com/codex/settings/code-review).
KvngMikey (Migrated from github.com) reviewed 2026-04-02 15:02:17 +00:00
@ -82,6 +82,10 @@ Supported tags are:
- `refund: <hex_str>` are additional public keys that can provide signatures after `locktime` (_allows multiple entries_)
- `n_sigs_refund: <int>` specifies the minimum number of [Refund Multisig](#refund-multisig) public keys providing valid signatures after `locktime` expires
KvngMikey (Migrated from github.com) commented 2026-04-02 15:02:17 +00:00

This PR tightens tag-count rules well, but n_sigs and n_sigs_refund values themselves are still underspecified. Three edge cases remain unaddressed:

n_sigs = 0: is this a valid anyone can spend shorthand or a malformed proof?

n_sigs > total available keys: unsatisfiable condition, should the proof be immediately rejected at mint time or only at spend time?

A non-integer string value like "two": the existing spec already says wallets/mints must cast types, but there's no rejection rule here comparable to the new duplicate-tag rule.

i suggest adding a line such as: "If n_sigs or n_sigs_refund is not a positive integer, or exceeds the total number of keys in its pathway, the P2PK secret is malformed and the Proof MUST be rejected as invalid."

This PR tightens tag-count rules well, but n_sigs and n_sigs_refund values themselves are still underspecified. Three edge cases remain unaddressed: `n_sigs = 0`: is this a valid anyone can spend shorthand or a malformed proof? `n_sigs > total available keys`: unsatisfiable condition, should the proof be immediately rejected at mint time or only at spend time? A non-integer string value like `"two"`: the existing spec already says wallets/mints must cast types, but there's no rejection rule here comparable to the new duplicate-tag rule. i suggest adding a line such as: "If `n_sigs` or `n_sigs_refund` is not a positive integer, or exceeds the total number of keys in its pathway, the P2PK secret is malformed and the Proof MUST be rejected as invalid."
KvngMikey (Migrated from github.com) reviewed 2026-04-02 15:05:53 +00:00
KvngMikey (Migrated from github.com) commented 2026-04-02 15:05:53 +00:00

the sentence "A public key may appear in both the Locktime Multisig and Refund Multisig pathways at the same time, but only ONCE in each pathway" could be interpreted as blanket permission for duplicates as long as they span two pathways.

I suggest we reword to something like this:
"Within a single pathway (Locktime or Refund), each key MUST be unique by its x-coordinate. A key that is unique within each pathway MAY appear in both pathways simultaneously."

this makes it constraint first, then the permission, which is the safer reading order for implementers.

the sentence "A public key may appear in both the Locktime Multisig and Refund Multisig pathways at the same time, but only ONCE in each pathway" could be interpreted as blanket permission for duplicates as long as they span two pathways. I suggest we reword to something like this: "Within a single pathway (Locktime or Refund), each key MUST be unique by its x-coordinate. A key that is unique within each pathway MAY appear in both pathways simultaneously." this makes it constraint first, then the permission, which is the safer reading order for implementers.
robwoodgate (Migrated from github.com) reviewed 2026-04-02 15:19:02 +00:00
@ -82,6 +82,10 @@ Supported tags are:
- `refund: <hex_str>` are additional public keys that can provide signatures after `locktime` (_allows multiple entries_)
- `n_sigs_refund: <int>` specifies the minimum number of [Refund Multisig](#refund-multisig) public keys providing valid signatures after `locktime` expires
robwoodgate (Migrated from github.com) commented 2026-04-02 15:19:02 +00:00

n_sigs = 0: is this a valid anyone can spend shorthand or a malformed proof?

Good point. I think malformed.

n_sigs > total available keys: unsatisfiable condition, should the proof be immediately rejected at mint time or only at spend time?

Secret is blinded at mint time, so only rejectable at spend time. This condition is "unspendable" (malformed).

A non-integer string value like "two": the existing spec already says wallets/mints must cast types, but there's no rejection rule here comparable to the new duplicate-tag rule.

Malformed - the spec clearly defines these as "integer"

i suggest adding a line such as: "If n_sigs or n_sigs_refund is not a positive integer, or exceeds the total number of keys in its pathway, the P2PK secret is malformed and the Proof MUST be rejected as invalid."

I like that. Will add.

> `n_sigs = 0`: is this a valid anyone can spend shorthand or a malformed proof? Good point. I think malformed. > `n_sigs > total available keys`: unsatisfiable condition, should the proof be immediately rejected at mint time or only at spend time? Secret is blinded at mint time, so only rejectable at spend time. This condition is "unspendable" (malformed). > A non-integer string value like `"two"`: the existing spec already says wallets/mints must cast types, but there's no rejection rule here comparable to the new duplicate-tag rule. Malformed - the spec clearly defines these as "integer" > i suggest adding a line such as: "If `n_sigs` or `n_sigs_refund` is not a positive integer, or exceeds the total number of keys in its pathway, the P2PK secret is malformed and the Proof MUST be rejected as invalid." I like that. Will add.
robwoodgate (Migrated from github.com) reviewed 2026-04-02 15:36:59 +00:00
robwoodgate (Migrated from github.com) commented 2026-04-02 15:36:59 +00:00

Thanks @KvngMikey - I cleaned it up and went constraint first

Thanks @KvngMikey - I cleaned it up and went constraint first
KvngMikey (Migrated from github.com) approved these changes 2026-04-02 15:40:05 +00:00
KvngMikey (Migrated from github.com) left a comment

lgtm

lgtm
thesimplekid (Migrated from github.com) reviewed 2026-04-02 18:19:02 +00:00
@ -82,6 +82,10 @@ Supported tags are:
- `refund: <hex_str>` are additional public keys that can provide signatures after `locktime` (_allows multiple entries_)
thesimplekid (Migrated from github.com) commented 2026-04-02 18:19:02 +00:00

I think rejected is not clear that it means the proof is unspendable.

I think rejected is not clear that it means the proof is unspendable.
robwoodgate (Migrated from github.com) reviewed 2026-04-02 23:01:10 +00:00
@ -82,6 +82,10 @@ Supported tags are:
- `refund: <hex_str>` are additional public keys that can provide signatures after `locktime` (_allows multiple entries_)
robwoodgate (Migrated from github.com) commented 2026-04-02 23:01:10 +00:00

so if I change to 'be rejected as unspendable`?

Or just 'and the Proof MUST be treated as unspendable`?

so if I change to 'be rejected as unspendable`? Or just 'and the Proof **MUST** be treated as unspendable`?
robwoodgate (Migrated from github.com) reviewed 2026-04-07 14:35:54 +00:00
@ -82,6 +82,10 @@ Supported tags are:
- `refund: <hex_str>` are additional public keys that can provide signatures after `locktime` (_allows multiple entries_)
robwoodgate (Migrated from github.com) commented 2026-04-07 14:35:54 +00:00

changed invalid to unspendable.

changed invalid to unspendable.
robwoodgate commented 2026-04-13 12:28:05 +00:00 (Migrated from github.com)

@thesimplekid @callebtc - we have merged implementations across the core repos.
Can we consider merging, unless any other wording tweaks required?

@thesimplekid @callebtc - we have merged implementations across the core repos. Can we consider merging, unless any other wording tweaks required?
a1denvalu3 (Migrated from github.com) reviewed 2026-04-17 09:55:14 +00:00
@ -82,6 +82,10 @@ Supported tags are:
- `refund: <hex_str>` are additional public keys that can provide signatures after `locktime` (_allows multiple entries_)
- `n_sigs_refund: <int>` specifies the minimum number of [Refund Multisig](#refund-multisig) public keys providing valid signatures after `locktime` expires
Each of the above tags may appear exactly **ONCE** in a P2PK secret. If a tag appears more than once, the P2PK secret is malformed and the Proof **MUST** be rejected as unspendable.
a1denvalu3 (Migrated from github.com) commented 2026-04-17 09:55:15 +00:00

As per discussion, we could enforce split handling on the mint and wallet sides.

  1. The mint could treat malformed NUT-11 as anyone-can-spend
  2. The wallets could reject malformed NUT-11.
As per discussion, we could enforce split handling on the mint and wallet sides. 1. The mint could treat malformed NUT-11 as anyone-can-spend 2. The wallets could reject malformed NUT-11.
robwoodgate (Migrated from github.com) reviewed 2026-04-17 22:42:50 +00:00
@ -82,6 +82,10 @@ Supported tags are:
- `refund: <hex_str>` are additional public keys that can provide signatures after `locktime` (_allows multiple entries_)
- `n_sigs_refund: <int>` specifies the minimum number of [Refund Multisig](#refund-multisig) public keys providing valid signatures after `locktime` expires
Each of the above tags may appear exactly **ONCE** in a P2PK secret. If a tag appears more than once, the P2PK secret is malformed and the Proof **MUST** be rejected as unspendable.
robwoodgate (Migrated from github.com) commented 2026-04-17 22:42:50 +00:00

What change in wording would work for you?

What change in wording would work for you?
This pull request can be merged automatically.
You are not authorized to merge this pull request.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin robwoodgate/nut11-malformed:robwoodgate/nut11-malformed
git switch robwoodgate/nut11-malformed

Merge

Merge the changes and update on Forgejo.

Warning: The "Autodetect manual merge" setting is not enabled for this repository, you will have to mark this pull request as manually merged afterwards.

git switch main
git merge --no-ff robwoodgate/nut11-malformed
git switch robwoodgate/nut11-malformed
git rebase main
git switch main
git merge --ff-only robwoodgate/nut11-malformed
git switch robwoodgate/nut11-malformed
git rebase main
git switch main
git merge --no-ff robwoodgate/nut11-malformed
git switch main
git merge --squash robwoodgate/nut11-malformed
git switch main
git merge --ff-only robwoodgate/nut11-malformed
git switch main
git merge robwoodgate/nut11-malformed
git push origin main
Sign in to join this conversation.
No description provided.