Skip to content

Commit

Permalink
Mint: verify outputs first during mint (#379)
Browse files Browse the repository at this point in the history
* verify outputs first during mint

* verify outputs during melt as well
  • Loading branch information
callebtc authored Dec 3, 2023
1 parent 7d4ed95 commit e6ed8fd
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 9 deletions.
10 changes: 8 additions & 2 deletions cashu/mint/ledger.py
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,8 @@ async def mint(
logger.trace("called mint")
amount_outputs = sum([b.amount for b in B_s])

await self._verify_outputs(B_s)

if settings.lightning:
if not id:
raise NotAllowedError("no id provided.")
Expand All @@ -317,8 +319,6 @@ async def mint(
await self.crud.update_lightning_invoice(id=id, issued=True, db=self.db)
del self.locks[id]

await self._verify_outputs(B_s)

promises = await self._generate_promises(B_s, keyset)
logger.trace("generated promises")
return promises
Expand Down Expand Up @@ -368,6 +368,12 @@ async def melt(
f" {total_provided}, needed: {invoice_amount + reserve_fees_sat}"
)

if outputs:
# verify the outputs. note: we don't verify inputs
# and outputs simultaneously with verify_inputs_and_outputs() as we do
# in split() because we do not expect the amounts to be equal here.
await self._verify_outputs(outputs)

# verify spending inputs and their spending conditions
await self.verify_inputs_and_outputs(proofs)

Expand Down
39 changes: 32 additions & 7 deletions tests/test_mint_operations.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,19 +37,19 @@ async def test_melt(wallet1: Wallet, ledger: Ledger):
invoice = await wallet1.request_mint(64)
pay_if_regtest(invoice.bolt11)
await wallet1.mint(64, id=invoice.id)
invoice = await wallet1.request_mint(64)
pay_if_regtest(invoice.bolt11)
await wallet1.mint(64, id=invoice.id)
invoice2 = await wallet1.request_mint(64)
pay_if_regtest(invoice2.bolt11)
await wallet1.mint(64, id=invoice2.id)
assert wallet1.balance == 128
total_amount, fee_reserve_sat = await wallet1.get_pay_amount_with_fees(
invoice.bolt11
invoice2.bolt11
)
mint_fees = await ledger.get_melt_fees(invoice.bolt11)
assert mint_fees == fee_reserve_sat
melt_fees = await ledger.get_melt_fees(invoice2.bolt11)
assert melt_fees == fee_reserve_sat

keep_proofs, send_proofs = await wallet1.split_to_send(wallet1.proofs, total_amount)

await ledger.melt(send_proofs, invoice.bolt11, outputs=None)
await ledger.melt(send_proofs, invoice2.bolt11, outputs=None)


@pytest.mark.asyncio
Expand Down Expand Up @@ -176,6 +176,31 @@ async def test_mint_with_same_outputs_twice(wallet1: Wallet, ledger: Ledger):
)


@pytest.mark.asyncio
async def test_melt_with_same_outputs_twice(wallet1: Wallet, ledger: Ledger):
invoice = await wallet1.request_mint(130)
pay_if_regtest(invoice.bolt11)
await wallet1.mint(130, id=invoice.id)

output_amounts = [128]
secrets, rs, derivation_paths = await wallet1.generate_n_secrets(
len(output_amounts)
)
outputs, rs = wallet1._construct_outputs(output_amounts, secrets, rs)

# we use the outputs once for minting
invoice2 = await wallet1.request_mint(128)
pay_if_regtest(invoice2.bolt11)
await ledger.mint(outputs, id=invoice2.id)

# use the same outputs for melting
invoice3 = await wallet1.request_mint(128)
await assert_err(
ledger.melt(wallet1.proofs, invoice3.bolt11, outputs=outputs),
"outputs have already been signed before.",
)


@pytest.mark.asyncio
async def test_check_proof_state(wallet1: Wallet, ledger: Ledger):
invoice = await wallet1.request_mint(64)
Expand Down

0 comments on commit e6ed8fd

Please sign in to comment.