From cdabc86ba9d45d96e93ee902938bba898fe28946 Mon Sep 17 00:00:00 2001 From: callebtc <93376500+callebtc@users.noreply.github.com> Date: Wed, 14 Dec 2022 23:27:40 +0100 Subject: [PATCH] defer unpending --- cashu/mint/ledger.py | 97 +++++++++++++++++++++++++------------------- tests/test_wallet.py | 2 +- 2 files changed, 57 insertions(+), 42 deletions(-) diff --git a/cashu/mint/ledger.py b/cashu/mint/ledger.py index 316a831..b322383 100644 --- a/cashu/mint/ledger.py +++ b/cashu/mint/ledger.py @@ -110,9 +110,11 @@ class Ledger: return not proof.secret in self.proofs_used def _verify_secret_criteria(self, proof: Proof): - """Verifies that a secret is present""" + """Verifies that a secret is present and is not too long (DOS prevention).""" if proof.secret is None or proof.secret == "": raise Exception("no secret in proof.") + if len(proof.secret) > 64: + raise Exception("secret too long.") return True def _verify_proof(self, proof: Proof): @@ -269,9 +271,6 @@ class Ledger: for p in proofs: await self.crud.invalidate_proof(proof=p, db=self.db) - # delete proofs from pending list - await self._unset_proofs_pending(proofs) - async def _set_proofs_pending(self, proofs: List[Proof]): """ If none of the proofs is in the pending table (_validate_proofs_pending), adds proofs to @@ -280,7 +279,10 @@ class Ledger: # first we check whether these proofs are pending aready await self._validate_proofs_pending(proofs) for p in proofs: - await self.crud.set_proof_pending(proof=p, db=self.db) + try: + await self.crud.set_proof_pending(proof=p, db=self.db) + except: + raise Exception("proofs already pending.") async def _unset_proofs_pending(self, proofs: List[Proof]): """Deletes proofs from pending table.""" @@ -347,24 +349,31 @@ class Ledger: # validate and set proofs as pending await self._set_proofs_pending(proofs) - # Verify proofs - if not all([self._verify_proof(p) for p in proofs]): - raise Exception("could not verify proofs.") + try: + # Verify proofs + if not all([self._verify_proof(p) for p in proofs]): + raise Exception("could not verify 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) - assert total_provided >= amount + fees_msat / 1000, Exception( - "provided proofs not enough for Lightning payment." - ) + 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) + assert total_provided >= amount + fees_msat / 1000, Exception( + "provided proofs not enough for Lightning payment." + ) + + if LIGHTNING: + status, preimage = await self._pay_lightning_invoice(invoice, fees_msat) + else: + status, preimage = True, "preimage" + if status == True: + await self._invalidate_proofs(proofs) + except Exception as e: + raise e + finally: + # delete proofs from pending list + await self._unset_proofs_pending(proofs) - if LIGHTNING: - status, preimage = await self._pay_lightning_invoice(invoice, fees_msat) - else: - status, preimage = True, "preimage" - if status == True: - await self._invalidate_proofs(proofs) return status, preimage async def check_spendable(self, proofs: List[Proof]): @@ -399,27 +408,33 @@ class Ledger: total = sum_proofs(proofs) - # verify that amount is kosher - self._verify_split_amount(amount) - # verify overspending attempt - if amount > total: - raise Exception("split amount is higher than the total sum.") + try: + # verify that amount is kosher + self._verify_split_amount(amount) + # verify overspending attempt + if amount > total: + raise Exception("split amount is higher than the total sum.") - # Verify scripts - if not all([self._verify_script(i, p) for i, p in enumerate(proofs)]): - raise Exception("script verification failed.") - # Verify secret criteria - if not all([self._verify_secret_criteria(p) for p in proofs]): - raise Exception("secrets do not match criteria.") - # verify that only unique proofs and outputs were used - if not self._verify_no_duplicates(proofs, outputs): - raise Exception("duplicate proofs or promises.") - # verify that outputs have the correct amount - if not self._verify_outputs(total, amount, outputs): - raise Exception("split of promises is not as expected.") - # Verify proofs - if not all([self._verify_proof(p) for p in proofs]): - raise Exception("could not verify proofs.") + # Verify scripts + if not all([self._verify_script(i, p) for i, p in enumerate(proofs)]): + raise Exception("script verification failed.") + # Verify secret criteria + if not all([self._verify_secret_criteria(p) for p in proofs]): + raise Exception("secrets do not match criteria.") + # verify that only unique proofs and outputs were used + if not self._verify_no_duplicates(proofs, outputs): + raise Exception("duplicate proofs or promises.") + # verify that outputs have the correct amount + if not self._verify_outputs(total, amount, outputs): + raise Exception("split of promises is not as expected.") + # Verify proofs + if not all([self._verify_proof(p) for p in proofs]): + raise Exception("could not verify proofs.") + except Exception as e: + raise e + finally: + # delete proofs from pending list + await self._unset_proofs_pending(proofs) # Mark proofs as used and prepare new promises await self._invalidate_proofs(proofs) diff --git a/tests/test_wallet.py b/tests/test_wallet.py index 08ca8aa..f06e530 100644 --- a/tests/test_wallet.py +++ b/tests/test_wallet.py @@ -137,7 +137,7 @@ async def test_duplicate_proofs_double_spent(wallet1: Wallet): doublespend = await wallet1.mint(64) await assert_err( wallet1.split(wallet1.proofs + doublespend, 20), - "Mint Error: duplicate proofs or promises.", + "Mint Error: proofs already pending.", ) assert wallet1.balance == 64 assert wallet1.available_balance == 64