From d4a89ac76f1f52ed91cc4d42b7c2d449b9538999 Mon Sep 17 00:00:00 2001 From: callebtc <93376500+callebtc@users.noreply.github.com> Date: Tue, 11 Oct 2022 00:19:53 +0200 Subject: [PATCH] add tests --- cashu/mint/ledger.py | 12 +---- cashu/wallet/wallet.py | 114 ++++++++++++++++++++++------------------- tests/test_crypto.py | 37 +++++++++++++ 3 files changed, 99 insertions(+), 64 deletions(-) create mode 100644 tests/test_crypto.py diff --git a/cashu/mint/ledger.py b/cashu/mint/ledger.py index 8f6a885..83b6b31 100644 --- a/cashu/mint/ledger.py +++ b/cashu/mint/ledger.py @@ -116,17 +116,7 @@ class Ledger: C = PublicKey(bytes.fromhex(proof.C), raw=True) - # backwards compatibility with old hash_to_curve - logger.debug(f"Client version {context.get('client-version')}") - if self.keysets.keysets.get(proof.id): - logger.debug( - f"Token keyset: {self.keysets.keysets.get(proof.id)}, token version: {self.keysets.keysets[proof.id].version}" - ) - # if not context.get("client-version") or ( - # self.keysets.keysets.get(proof.id) - # and not self.keysets.keysets[proof.id].version - # ): - # return legacy.verify_pre_0_3_3(secret_key, C, proof.secret) + # backwards compatibility with old hash_to_curve < 0.3.3 try: ret = legacy.verify_pre_0_3_3(secret_key, C, proof.secret) if ret: diff --git a/cashu/wallet/wallet.py b/cashu/wallet/wallet.py index 8c95034..02ca6eb 100644 --- a/cashu/wallet/wallet.py +++ b/cashu/wallet/wallet.py @@ -50,28 +50,8 @@ class LedgerAPI: def __init__(self, url): self.url = url - - async def _get_keys(self, url): - resp = requests.get( - url + "/keys", - headers={"Client-version": VERSION}, - ).json() - keys = resp - assert len(keys), Exception("did not receive any keys") - keyset_keys = { - int(amt): PublicKey(bytes.fromhex(val), raw=True) - for amt, val in keys.items() - } - keyset = WalletKeyset(pubkeys=keyset_keys, mint_url=url) - return keyset - - async def _get_keysets(self, url): - keysets = requests.get( - url + "/keysets", - headers={"Client-version": VERSION}, - ).json() - assert len(keysets), Exception("did not receive any keysets") - return keysets + self.s = requests.Session() + self.s.headers.update({"Client-version": VERSION}) @staticmethod def _get_output_split(amount): @@ -100,6 +80,11 @@ class LedgerAPI: proofs.append(proof) return proofs + @staticmethod + def raise_on_error(resp_dict): + if "error" in resp_dict: + raise Exception("Mint Error: {}".format(resp_dict["error"])) + @staticmethod def _generate_secret(randombits=128): """Returns base64 encoded random string.""" @@ -138,12 +123,6 @@ class LedgerAPI: self.keys = keyset.public_keys self.keyset_id = keyset.id - def request_mint(self, amount): - """Requests a mint from the server and returns Lightning invoice.""" - r = requests.get(self.url + "/mint", params={"amount": amount}) - r.raise_for_status() - return r.json() - @staticmethod def _construct_outputs(amounts: List[int], secrets: List[str]): """Takes a list of amounts and secrets and returns outputs. @@ -173,25 +152,55 @@ class LedgerAPI: return [f"{secret}:{self._generate_secret()}" for i in range(n)] return [f"{i}:{secret}" for i in range(n)] + """ + ENDPOINTS + """ + + async def _get_keys(self, url): + resp = self.s.get( + url + "/keys", + ) + resp.raise_for_status() + keys = resp.json() + assert len(keys), Exception("did not receive any keys") + keyset_keys = { + int(amt): PublicKey(bytes.fromhex(val), raw=True) + for amt, val in keys.items() + } + keyset = WalletKeyset(pubkeys=keyset_keys, mint_url=url) + return keyset + + async def _get_keysets(self, url): + resp = self.s.get( + url + "/keysets", + ).json() + resp.raise_for_status() + keysets = resp.json() + assert len(keysets), Exception("did not receive any keysets") + return keysets + + def request_mint(self, amount): + """Requests a mint from the server and returns Lightning invoice.""" + resp = self.s.get(self.url + "/mint", params={"amount": amount}) + resp.raise_for_status() + return_dict = resp.json() + self.raise_on_error(return_dict) + return return_dict + async def mint(self, amounts, payment_hash=None): """Mints new coins and returns a proof of promise.""" secrets = [self._generate_secret() for s in range(len(amounts))] await self._check_used_secrets(secrets) payloads, rs = self._construct_outputs(amounts, secrets) - resp = requests.post( + resp = self.s.post( self.url + "/mint", json=payloads.dict(), params={"payment_hash": payment_hash}, - headers={"Client-version": VERSION}, ) resp.raise_for_status() - try: - promises_list = resp.json() - except: - raise Exception("Unkown mint error.") - if "error" in promises_list: - raise Exception("Error: {}".format(promises_list["error"])) + promises_list = resp.json() + self.raise_on_error(promises_list) promises = [BlindedSignature.from_dict(p) for p in promises_list] return self._construct_proofs(promises, secrets, rs) @@ -239,18 +248,14 @@ class LedgerAPI: "proofs": {i: proofs_include for i in range(len(proofs))}, } - resp = requests.post( + resp = self.s.post( self.url + "/split", json=split_payload.dict(include=_splitrequest_include_fields(proofs)), - headers={"Client-version": VERSION}, ) resp.raise_for_status() - try: - promises_dict = resp.json() - except: - raise Exception("Unkown mint error.") - if "error" in promises_dict: - raise Exception("Mint Error: {}".format(promises_dict["error"])) + promises_dict = resp.json() + self.raise_on_error(promises_dict) + promises_fst = [BlindedSignature.from_dict(p) for p in promises_dict["fst"]] promises_snd = [BlindedSignature.from_dict(p) for p in promises_dict["snd"]] # Construct proofs from promises (i.e., unblind signatures) @@ -264,31 +269,35 @@ class LedgerAPI: return frst_proofs, scnd_proofs async def check_spendable(self, proofs: List[Proof]): + """ + Cheks whether the secrets in proofs are already spent or not and returns a list of booleans. + """ payload = CheckRequest(proofs=proofs) - resp = requests.post( + resp = self.s.post( self.url + "/check", json=payload.dict(), - headers={"Client-version": VERSION}, ) resp.raise_for_status() return_dict = resp.json() - + self.raise_on_error(return_dict) return return_dict async def check_fees(self, payment_request: str): """Checks whether the Lightning payment is internal.""" payload = CheckFeesRequest(pr=payment_request) - resp = requests.post( + resp = self.s.post( self.url + "/checkfees", json=payload.dict(), - headers={"Client-version": VERSION}, ) resp.raise_for_status() - return_dict = resp.json() + self.raise_on_error(return_dict) return return_dict async def pay_lightning(self, proofs: List[Proof], invoice: str): + """ + Accepts proofs and a lightning invoice to pay in exchange. + """ payload = MeltRequest(proofs=proofs, invoice=invoice) def _meltequest_include_fields(proofs): @@ -300,14 +309,13 @@ class LedgerAPI: "proofs": {i: proofs_include for i in range(len(proofs))}, } - resp = requests.post( + resp = self.s.post( self.url + "/melt", json=payload.dict(include=_meltequest_include_fields(proofs)), - headers={"Client-version": VERSION}, ) resp.raise_for_status() - return_dict = resp.json() + self.raise_on_error(return_dict) return return_dict diff --git a/tests/test_crypto.py b/tests/test_crypto.py new file mode 100644 index 0000000..947dc2a --- /dev/null +++ b/tests/test_crypto.py @@ -0,0 +1,37 @@ +import pytest + +from cashu.core.b_dhke import hash_to_curve + + +def test_hash_to_curve(): + result = hash_to_curve( + bytes.fromhex( + "0000000000000000000000000000000000000000000000000000000000000000" + ) + ) + assert ( + result.serialize().hex() + == "0266687aadf862bd776c8fc18b8e9f8e20089714856ee233b3902a591d0d5f2925" + ) + + result = hash_to_curve( + bytes.fromhex( + "0000000000000000000000000000000000000000000000000000000000000001" + ) + ) + assert ( + result.serialize().hex() + == "02ec4916dd28fc4c10d78e287ca5d9cc51ee1ae73cbfde08c6b37324cbfaac8bc5" + ) + + +def test_hash_to_curve_iteration(): + result = hash_to_curve( + bytes.fromhex( + "0000000000000000000000000000000000000000000000000000000000000002" + ) + ) + assert ( + result.serialize().hex() + == "02076c988b353fcbb748178ecb286bc9d0b4acf474d4ba31ba62334e46c97c416a" + )