diff --git a/cashu/core/helpers.py b/cashu/core/helpers.py index cbc1dc5..9b38c71 100644 --- a/cashu/core/helpers.py +++ b/cashu/core/helpers.py @@ -1,9 +1,15 @@ import asyncio from functools import partial, wraps +from typing import List +from cashu.core.base import Proof from cashu.core.settings import LIGHTNING_FEE_PERCENT, LIGHTNING_RESERVE_FEE_MIN +def sum_proofs(proofs: List[Proof]): + return sum([p.amount for p in proofs]) + + def async_wrap(func): @wraps(func) async def run(*args, loop=None, executor=None, **kwargs): diff --git a/cashu/mint/ledger.py b/cashu/mint/ledger.py index 8d3c558..73126c5 100644 --- a/cashu/mint/ledger.py +++ b/cashu/mint/ledger.py @@ -19,7 +19,7 @@ from cashu.core.base import ( ) from cashu.core.crypto import derive_keys, derive_keyset_id, derive_pubkeys from cashu.core.db import Database -from cashu.core.helpers import fee_reserve +from cashu.core.helpers import fee_reserve, sum_proofs from cashu.core.script import verify_script from cashu.core.secp import PublicKey from cashu.core.settings import LIGHTNING, MAX_ORDER @@ -45,6 +45,7 @@ class Ledger: self.db: Database = Database("mint", db) async def load_used_proofs(self): + """Load all used proofs from database.""" self.proofs_used = set(await get_proofs_used(db=self.db)) async def init_keysets(self): @@ -93,12 +94,8 @@ class Ledger: """Checks whether the proof was already spent.""" return not proof.secret in self.proofs_used - def _verify_secret_or_script(self, proof: Proof): - if proof.secret and proof.script: - raise Exception("secret and script present at the same time.") - return True - def _verify_secret_criteria(self, proof: Proof): + """Verifies that a secret is present""" if proof.secret is None or proof.secret == "": raise Exception("no secret in proof.") return True @@ -213,7 +210,7 @@ class Ledger: invoice: Invoice = await get_lightning_invoice(payment_hash, db=self.db) if invoice.issued: raise Exception("tokens already issued for this invoice.") - total_requested = sum([amount for amount in amounts]) + total_requested = sum(amounts) if total_requested > invoice.amount: raise Exception( f"Requested amount too high: {total_requested}. Invoice amount: {invoice.amount}" @@ -287,7 +284,7 @@ class Ledger: if not all([self._verify_proof(p) for p in proofs]): raise Exception("could not verify proofs.") - total_provided = sum([p["amount"] for p in proofs]) + total_provided = sum_proofs(proofs) invoice_obj = bolt11.decode(invoice) amount = math.ceil(invoice_obj.amount_msat / 1000) fees_msat = await self.check_fees(invoice) @@ -319,7 +316,7 @@ class Ledger: self, proofs: List[Proof], amount: int, outputs: List[BlindedMessage] ): """Consumes proofs and prepares new promises based on the amount split.""" - total = sum([p.amount for p in proofs]) + total = sum_proofs(proofs) # verify that amount is kosher self._verify_split_amount(amount) diff --git a/cashu/mint/router.py b/cashu/mint/router.py index ae78859..260d8bd 100644 --- a/cashu/mint/router.py +++ b/cashu/mint/router.py @@ -81,11 +81,17 @@ async def melt(payload: MeltRequest): @router.post("/check") async def check_spendable(payload: CheckRequest): + """Check whether a secret has been spent already or not.""" return await ledger.check_spendable(payload.proofs) @router.post("/checkfees") async def check_fees(payload: CheckFeesRequest): + """ + Responds with the fees necessary to pay a Lightning invoice. + Used by wallets for figuring out the fees they need to supply. + This is can be useful for checking whether an invoice is internal (Cashu-to-Cashu). + """ fees_msat = await ledger.check_fees(payload.pr) return CheckFeesResponse(fee=fees_msat / 1000) diff --git a/cashu/wallet/cli.py b/cashu/wallet/cli.py index 2ce2272..69c0bf3 100755 --- a/cashu/wallet/cli.py +++ b/cashu/wallet/cli.py @@ -18,7 +18,7 @@ from loguru import logger import cashu.core.bolt11 as bolt11 from cashu.core.base import Proof from cashu.core.bolt11 import Invoice, decode -from cashu.core.helpers import fee_reserve +from cashu.core.helpers import fee_reserve, sum_proofs from cashu.core.migrations import migrate_databases from cashu.core.settings import CASHU_DIR, DEBUG, ENV_FILE, LIGHTNING, MINT_URL, VERSION from cashu.wallet import migrations @@ -267,7 +267,7 @@ async def pending(ctx): int(grouped_proofs[0].time_reserved) ).strftime("%Y-%m-%d %H:%M:%S") print( - f"#{i} Amount: {sum([p['amount'] for p in grouped_proofs])} sat Time: {reserved_date} ID: {key}\n" + f"#{i} Amount: {sum_proofs(grouped_proofs)} sat Time: {reserved_date} ID: {key}\n" ) print(f"With secret: {coin}\n\nSecretless: {coin_hidden_secret}\n") print(f"--------------------------\n") diff --git a/cashu/wallet/wallet.py b/cashu/wallet/wallet.py index 4c150e0..79a1488 100644 --- a/cashu/wallet/wallet.py +++ b/cashu/wallet/wallet.py @@ -22,6 +22,7 @@ from cashu.core.base import ( WalletKeyset, ) from cashu.core.db import Database +from cashu.core.helpers import sum_proofs from cashu.core.script import ( step0_carol_checksig_redeemscrip, step0_carol_privkey, @@ -190,7 +191,7 @@ class LedgerAPI: If scnd_secret is provided, the wallet will create blinded secrets with those to attach a predefined spending condition to the tokens they want to send.""" - total = sum([p["amount"] for p in proofs]) + total = sum_proofs(proofs) frst_amt, scnd_amt = total - amount, amount frst_outputs = amount_split(frst_amt) scnd_outputs = amount_split(scnd_amt) @@ -312,12 +313,6 @@ class Wallet(LedgerAPI): for proof in proofs: await store_proof(proof, db=self.db) - @staticmethod - def _sum_proofs(proofs: List[Proof], available_only=False): - if available_only: - return sum([p.amount for p in proofs if not p.reserved]) - return sum([p.amount for p in proofs]) - @staticmethod def _get_proofs_per_keyset(proofs: List[Proof]): return {key: list(group) for key, group in groupby(proofs, lambda p: p.id)} @@ -345,7 +340,7 @@ class Wallet(LedgerAPI): # attach unlock scripts to proofs for p in proofs: p.script = P2SHScript(script=scnd_script, signature=scnd_siganture) - return await self.split(proofs, sum(p["amount"] for p in proofs)) + return await self.split(proofs, sum_proofs(proofs)) async def split( self, @@ -405,7 +400,7 @@ class Wallet(LedgerAPI): if scnd_secret: logger.debug(f"Spending conditions: {scnd_secret}") spendable_proofs = await self._get_spendable_proofs(proofs) - if sum([p.amount for p in spendable_proofs]) < amount: + if sum_proofs(spendable_proofs) < amount: raise Exception("balance too low.") return await self.split( [p for p in spendable_proofs if not p.reserved], amount, scnd_secret @@ -453,11 +448,11 @@ class Wallet(LedgerAPI): @property def balance(self): - return sum(p["amount"] for p in self.proofs) + return sum_proofs(self.proofs) @property def available_balance(self): - return sum(p["amount"] for p in self.proofs if not p.reserved) + return sum_proofs([p for p in self.proofs if not p.reserved]) def status(self): print( @@ -467,8 +462,8 @@ class Wallet(LedgerAPI): def balance_per_keyset(self): return { key: { - "balance": self._sum_proofs(proofs), - "available": self._sum_proofs(proofs, available_only=True), + "balance": sum_proofs(proofs), + "available": sum_proofs([p for p in proofs if not p.reserved]), } for key, proofs in self._get_proofs_per_keyset(self.proofs).items() }