Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 20 additions & 22 deletions cashu/mint/ledger.py
Original file line number Diff line number Diff line change
Expand Up @@ -496,8 +496,7 @@ async def mint(
Exception: Validation of outputs failed.
Exception: Quote not paid.
Exception: Quote already issued.
Exception: Quote expired.
Exception: Amount to mint does not match quote amount.
Exception: Amount to mint does not match quote amount.

Returns:
List[BlindedSignature]: Signatures on the outputs.
Expand All @@ -512,19 +511,20 @@ async def mint(
raise TransactionError("Mint quote already pending.")
if quote.issued:
raise QuoteAlreadyIssuedError()
if quote.state != MintQuoteState.paid:
raise QuoteNotPaidError()

previous_state = quote.state
if quote.state != MintQuoteState.paid:
raise QuoteNotPaidError()

# Quote expiry limits when the payment request can be paid. Once payment is
# confirmed, the quote remains mintable until its value has been issued.

previous_state = quote.state
Comment on lines +514 to +520

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 change isn't needed

await self.db_write._set_mint_quote_pending(quote_id=quote_id)
try:
if not quote.unit == output_unit.name:
raise TransactionError("quote unit does not match output unit")
if not quote.amount == sum_amount_outputs:
raise TransactionError("amount to mint does not match quote amount")
if quote.expiry and quote.expiry < int(time.time()):
raise TransactionError("quote expired")
if not self._verify_mint_quote_witness(quote, outputs, signature):
if not self._verify_mint_quote_witness(quote, outputs, signature):
raise QuoteSignatureInvalidError()
await self._store_blinded_messages(outputs, mint_id=quote_id)
promises = await self._sign_blinded_messages(outputs)
Expand Down Expand Up @@ -592,15 +592,17 @@ async def mint_batch(
if units.pop() != output_unit.name:
raise TransactionError("quote unit does not match output unit")

for quote in quotes:
if quote.pending:
raise TransactionError("mint quote already pending")
for quote in quotes:
if quote.pending:
raise TransactionError("mint quote already pending")
if quote.issued:
raise QuoteAlreadyIssuedError()
if quote.state != MintQuoteState.paid:
raise QuoteNotPaidError()

# Check amount balance
if quote.state != MintQuoteState.paid:
raise QuoteNotPaidError()

# Quote expiry limits payment, not issuance of already-paid value.

# Check amount balance
Comment on lines +600 to +605

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 change isn't needed

if payload.quote_amounts:
if len(payload.quote_amounts) != len(quotes):
raise TransactionError("quote_amounts length must match quotes length")
Expand Down Expand Up @@ -642,12 +644,8 @@ async def mint_batch(
# Set all quotes to pending
quotes = await self.db_write._set_mint_quotes_pending(quote_ids=payload.quotes)

try:
for quote in quotes:
if quote.expiry and quote.expiry < int(time.time()):
raise TransactionError("quote expired")

# Store all blinded messages
try:
# Store all blinded messages
await self._store_blinded_messages(
payload.outputs, mint_id=payload.quotes[0]
)
Expand Down
26 changes: 26 additions & 0 deletions tests/mint/test_mint.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,32 @@ async def test_mint(ledger: Ledger):
)


@pytest.mark.asyncio
async def test_mint_paid_quote_after_expiry(ledger: Ledger):
quote = await ledger.mint_quote(PostMintQuoteRequest(amount=8, unit="sat"))
await pay_if_regtest(quote.request)
quote = await ledger.get_mint_quote(quote.quote)
assert quote.paid

quote.expiry = int(time.time()) - 1
await ledger.crud.update_mint_quote(quote=quote, db=ledger.db)

blinded_message, _ = step1_alice("paid_quote_after_expiry")
promises = await ledger.mint(
outputs=[
BlindedMessage(
amount=8,
B_=blinded_message.format().hex(),
id=ledger.keyset.id,
)
],
quote_id=quote.quote,
)

assert len(promises) == 1
assert promises[0].amount == 8


@pytest.mark.asyncio
async def test_mint_invalid_quote(ledger: Ledger):
await assert_err(
Expand Down
42 changes: 42 additions & 0 deletions tests/mint/test_mint_batch.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import asyncio
import os
import time

import pytest
import pytest_asyncio
Expand Down Expand Up @@ -81,6 +82,47 @@ async def test_ledger_mint_batch_success(ledger: Ledger, wallet: Wallet):
assert quote2.issued_time is not None


@pytest.mark.asyncio
async def test_ledger_mint_batch_paid_quotes_after_expiry(
ledger: Ledger, wallet: Wallet
):
await wallet.load_mint()
mint_quote1 = await wallet.request_mint(64)
mint_quote2 = await wallet.request_mint(32)

await pay_if_regtest(mint_quote1.request)
await pay_if_regtest(mint_quote2.request)

quote1 = await ledger.get_mint_quote(mint_quote1.quote)
quote2 = await ledger.get_mint_quote(mint_quote2.quote)
assert quote1.paid
assert quote2.paid

quote1.expiry = int(time.time()) - 1
quote2.expiry = int(time.time()) - 1
await ledger.crud.update_mint_quote(quote=quote1, db=ledger.db)
await ledger.crud.update_mint_quote(quote=quote2, db=ledger.db)

secrets, rs, _ = await wallet.generate_secrets_from_to(10002, 10003)
outputs, _ = wallet._construct_outputs([64, 32], secrets, rs)

assert mint_quote1.privkey
assert mint_quote2.privkey
sig1 = nut20.sign_mint_quote(mint_quote1.quote, outputs, mint_quote1.privkey)
sig2 = nut20.sign_mint_quote(mint_quote2.quote, outputs, mint_quote2.privkey)

promises = await ledger.mint_batch(
PostMintBatchRequest(
quotes=[mint_quote1.quote, mint_quote2.quote],
quote_amounts=[64, 32],
outputs=outputs,
signatures=[sig1, sig2],
)
)

assert [promise.amount for promise in promises] == [64, 32]


@pytest.mark.asyncio
async def test_ledger_mint_batch_wrong_amount(ledger: Ledger, wallet: Wallet):
await wallet.load_mint()
Expand Down
Loading