Skip to content

feat(mint): enforce NUT 11014/11015 max inputs and outputs#965

Open
b-l-u-e wants to merge 1 commit intocashubtc:mainfrom
b-l-u-e:feat/nut-11014/11015-max-inputs-max-outputs-limits
Open

feat(mint): enforce NUT 11014/11015 max inputs and outputs#965
b-l-u-e wants to merge 1 commit intocashubtc:mainfrom
b-l-u-e:feat/nut-11014/11015-max-inputs-max-outputs-limits

Conversation

@b-l-u-e
Copy link
Copy Markdown
Contributor

@b-l-u-e b-l-u-e commented Mar 31, 2026

This PR adds Cashu/NUT-aligned errors 11014 (Max inputs exceeded) and 11015 (Max outputs exceeded), configurable via MINT_MAX_INPUTS and MINT_MAX_OUTPUTS (defaults: 1000).

  • Defines TransactionMaxInputsExceededError and TransactionMaxOutputsExceededError in cashu/core/errors.py.
  • Enforces limits in verify_inputs_and_outputs and _verify_outputs, and on restore().
  • Aligns Pydantic request models so proof/input lists use the inputs cap and blinded output lists use the outputs cap.

Tests cover both error paths in tests/mint/test_mint_verification.py.

@codecov
Copy link
Copy Markdown

codecov bot commented Mar 31, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 74.97%. Comparing base (a1ff72c) to head (e41618e).
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #965      +/-   ##
==========================================
+ Coverage   74.90%   74.97%   +0.06%     
==========================================
  Files          99       99              
  Lines       11685    11703      +18     
==========================================
+ Hits         8753     8774      +21     
+ Misses       2932     2929       -3     

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

@b-l-u-e b-l-u-e force-pushed the feat/nut-11014/11015-max-inputs-max-outputs-limits branch from f27814f to e41618e Compare March 31, 2026 12:03
Comment on lines -402 to 407
async with self.db.get_connection(
lock_table="mint_quotes",
lock_select_statement="quote = :quote",
lock_parameters={"quote": quote_id},
) as conn:
async with self.db.get_connection(
lock_table="mint_quotes",
lock_select_statement="quote = :quote",
lock_parameters={"quote": quote_id},
) as conn:
quote = await self.crud.get_mint_quote(
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.

invisible changes?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

invisible changes?
same EOL normalization issue locally CRLF vs LF that causes ^M and makes unrelated changes
i faced in another PR as well
https://github.com/cashubtc/nutshell/pull/901/changes/BASE..04decfccc11f3265c3c66ffa8c11bfa5a8a22b18#r2996024558

Comment on lines -1052 to 1061
try:
Ys = [p.Y for p in proofs]
lock_parameters = {f"y{i}": y for i, y in enumerate(Ys)}
ys_list = ", ".join(f":y{i}" for i in range(len(Ys)))
async with self.db.get_connection(
lock_table="proofs_pending",
lock_select_statement=f"y IN ({ys_list})",
lock_parameters=lock_parameters,
) as conn:
try:
Ys = [p.Y for p in proofs]
lock_parameters = {f"y{i}": y for i, y in enumerate(Ys)}
ys_list = ", ".join(f":y{i}" for i in range(len(Ys)))
async with self.db.get_connection(
lock_table="proofs_pending",
lock_select_statement=f"y IN ({ys_list})",
lock_parameters=lock_parameters,
) as conn:
await self._store_blinded_messages(outputs, keyset=keyset, conn=conn)
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.

invisible changes?

Comment on lines -1052 to +1061
try:
Ys = [p.Y for p in proofs]
lock_parameters = {f"y{i}": y for i, y in enumerate(Ys)}
ys_list = ", ".join(f":y{i}" for i in range(len(Ys)))
async with self.db.get_connection(
lock_table="proofs_pending",
lock_select_statement=f"y IN ({ys_list})",
lock_parameters=lock_parameters,
) as conn:
try:
Ys = [p.Y for p in proofs]
lock_parameters = {f"y{i}": y for i, y in enumerate(Ys)}
ys_list = ", ".join(f":y{i}" for i in range(len(Ys)))
async with self.db.get_connection(
lock_table="proofs_pending",
lock_select_statement=f"y IN ({ys_list})",
lock_parameters=lock_parameters,
) as conn:
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.

I can't see a difference but could you pls revert all unrelated chnages?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

There’s an issue raised for invisible or unnecessarily changes #970
I opened PR for the issue
#974

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.

2 participants