From 2ded9c8b5cdebfa80ed75f41cf21a1aa4e02ffc1 Mon Sep 17 00:00:00 2001 From: calle <93376500+callebtc@users.noreply.github.com> Date: Sat, 1 Apr 2023 00:46:56 +0200 Subject: [PATCH] use tokenObj in burn (#156) * use tokenObj in burn * refactor token serialization * add tests * rename --- cashu/core/base.py | 36 +++++++++++++++++++++++++++++++++ cashu/wallet/cli/cli.py | 10 ++++----- cashu/wallet/cli/cli_helpers.py | 4 ++-- cashu/wallet/wallet.py | 32 ++++------------------------- tests/test_core.py | 19 +++++++++++++++++ 5 files changed, 65 insertions(+), 36 deletions(-) diff --git a/cashu/core/base.py b/cashu/core/base.py index 8504826..49ed79f 100644 --- a/cashu/core/base.py +++ b/cashu/core/base.py @@ -1,3 +1,5 @@ +import base64 +import json from sqlite3 import Row from typing import Any, Dict, List, Optional, TypedDict, Union @@ -357,3 +359,37 @@ class TokenV3(BaseModel): if self.memo: return_dict.update(dict(memo=self.memo)) # type: ignore return return_dict + + def get_proofs(self): + return [proof for token in self.token for proof in token.proofs] + + def get_amount(self): + return sum([p.amount for p in self.get_proofs()]) + + def get_keysets(self): + return list(set([p.id for p in self.get_proofs()])) + + @classmethod + def deserialize(cls, tokenv3_serialized: str): + """ + Takes a TokenV3 and serializes it as "cashuA. + """ + prefix = "cashuA" + assert tokenv3_serialized.startswith(prefix), Exception( + f"Token prefix not valid. Expected {prefix}." + ) + token_base64 = tokenv3_serialized[len(prefix) :] + token = json.loads(base64.urlsafe_b64decode(token_base64)) + return cls.parse_obj(token) + + def serialize(self): + """ + Takes a TokenV3 and serializes it as "cashuA. + """ + prefix = "cashuA" + tokenv3_serialized = prefix + # encode the token as a base64 string + tokenv3_serialized += base64.urlsafe_b64encode( + json.dumps(self.to_dict()).encode() + ).decode() + return tokenv3_serialized diff --git a/cashu/wallet/cli/cli.py b/cashu/wallet/cli/cli.py index e7af3c7..ab3095e 100644 --- a/cashu/wallet/cli/cli.py +++ b/cashu/wallet/cli/cli.py @@ -18,7 +18,7 @@ import click from click import Context from loguru import logger -from cashu.core.base import Proof, TokenV1, TokenV2 +from cashu.core.base import Proof, TokenV1, TokenV2, TokenV3 from cashu.core.helpers import sum_proofs from cashu.core.migrations import migrate_databases from cashu.core.settings import settings @@ -342,8 +342,7 @@ async def receive(ctx: Context, token: str, lock: str): # ----- receive token ----- # deserialize token - # dtoken = json.loads(base64.urlsafe_b64decode(token)) - tokenObj = wallet._deserialize_token_V3(token) + tokenObj = TokenV3.deserialize(token) # tokenObj = TokenV2.parse_obj(dtoken) assert len(tokenObj.token), Exception("no proofs in token") @@ -457,9 +456,8 @@ async def burn(ctx: Context, token: str, all: bool, force: bool, delete: str): proofs = [proof for proof in reserved_proofs if proof["send_id"] == delete] else: # check only the specified ones - proofs = [ - Proof(**p) for p in json.loads(base64.urlsafe_b64decode(token))["proofs"] - ] + tokenObj = TokenV3.deserialize(token) + proofs = tokenObj.get_proofs() if delete: await wallet.invalidate(proofs, check_spendable=False) diff --git a/cashu/wallet/cli/cli_helpers.py b/cashu/wallet/cli/cli_helpers.py index 2895273..8c7eb0a 100644 --- a/cashu/wallet/cli/cli_helpers.py +++ b/cashu/wallet/cli/cli_helpers.py @@ -235,7 +235,7 @@ async def serialize_TokenV2_to_TokenV3(wallet: Wallet, tokenv2: TokenV2): tokenv3 = TokenV3(token=[TokenV3Token(proofs=tokenv2.proofs)]) if tokenv2.mints: tokenv3.token[0].mint = tokenv2.mints[0].url - token_serialized = await wallet._serialize_token_V3(tokenv3) + token_serialized = tokenv3.serialize() return token_serialized @@ -248,5 +248,5 @@ async def serialize_TokenV1_to_TokenV3(wallet: Wallet, tokenv1: TokenV1): TokenV3: TokenV3 """ tokenv3 = TokenV3(token=[TokenV3Token(proofs=tokenv1.__root__)]) - token_serialized = await wallet._serialize_token_V3(tokenv3) + token_serialized = tokenv3.serialize() return token_serialized diff --git a/cashu/wallet/wallet.py b/cashu/wallet/wallet.py index 7915796..53131d8 100644 --- a/cashu/wallet/wallet.py +++ b/cashu/wallet/wallet.py @@ -44,7 +44,7 @@ from cashu.core.script import ( step1_carol_create_p2sh_address, step2_carol_sign_tx, ) -from cashu.core.secp import PublicKey +from cashu.core.secp import PrivateKey, PublicKey from cashu.core.settings import settings from cashu.core.split import amount_split from cashu.tor.tor import TorProxy @@ -111,7 +111,7 @@ class LedgerAPI: return def _construct_proofs( - self, promises: List[BlindedSignature], secrets: List[str], rs: List[str] + self, promises: List[BlindedSignature], secrets: List[str], rs: List[PrivateKey] ): """Returns proofs of promise from promises. Wants secrets and blinding factors rs.""" proofs: List[Proof] = [] @@ -199,7 +199,7 @@ class LedgerAPI: secrets ), f"len(amounts)={len(amounts)} not equal to len(secrets)={len(secrets)}" outputs: List[BlindedMessage] = [] - rs = [] + rs: List[PrivateKey] = [] for secret, amount in zip(secrets, amounts): B_, r = b_dhke.step1_alice(secret) rs.append(r) @@ -684,30 +684,6 @@ class Wallet(LedgerAPI): token.token.append(token_proofs) return token - async def _serialize_token_V3(self, token: TokenV3): - """ - Takes a TokenV3 and serializes it as "cashuA. - """ - prefix = "cashuA" - tokenv3_serialized = prefix - # encode the token as a base64 string - tokenv3_serialized += base64.urlsafe_b64encode( - json.dumps(token.to_dict()).encode() - ).decode() - return tokenv3_serialized - - def _deserialize_token_V3(self, tokenv3_serialized: str) -> TokenV3: - """ - Takes a TokenV3 and serializes it as "cashuA. - """ - prefix = "cashuA" - assert tokenv3_serialized.startswith(prefix), Exception( - f"Token prefix not valid. Expected {prefix}." - ) - token_base64 = tokenv3_serialized[len(prefix) :] - token = json.loads(base64.urlsafe_b64decode(token_base64)) - return TokenV3.parse_obj(token) - async def serialize_proofs( self, proofs: List[Proof], include_mints=True, legacy=False ): @@ -728,7 +704,7 @@ class Wallet(LedgerAPI): # V3 tokens token = await self._make_token(proofs, include_mints) - return await self._serialize_token_V3(token) + return token.serialize() async def _make_token_v2(self, proofs: List[Proof], include_mints=True): """ diff --git a/tests/test_core.py b/tests/test_core.py index b50b07c..882cae1 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -1,5 +1,24 @@ +from cashu.core.base import TokenV3 from cashu.core.split import amount_split def test_get_output_split(): assert amount_split(13) == [1, 4, 8] + + +def test_tokenv3_get_amount(): + token_str = "cashuAeyJ0b2tlbiI6IFt7InByb29mcyI6IFt7ImlkIjogIkplaFpMVTZuQ3BSZCIsICJhbW91bnQiOiAyLCAic2VjcmV0IjogIjBFN2lDazRkVmxSZjVQRjFnNFpWMnciLCAiQyI6ICIwM2FiNTgwYWQ5NTc3OGVkNTI5NmY4YmVlNjU1ZGJkN2Q2NDJmNWQzMmRlOGUyNDg0NzdlMGI0ZDZhYTg2M2ZjZDUifSwgeyJpZCI6ICJKZWhaTFU2bkNwUmQiLCAiYW1vdW50IjogOCwgInNlY3JldCI6ICJzNklwZXh3SGNxcXVLZDZYbW9qTDJnIiwgIkMiOiAiMDIyZDAwNGY5ZWMxNmE1OGFkOTAxNGMyNTliNmQ2MTRlZDM2ODgyOWYwMmMzODc3M2M0NzIyMWY0OTYxY2UzZjIzIn1dLCAibWludCI6ICJodHRwOi8vbG9jYWxob3N0OjMzMzgifV19" + token = TokenV3.deserialize(token_str) + assert token.get_amount() == 10 + + +def test_tokenv3_get_proofs(): + token_str = "cashuAeyJ0b2tlbiI6IFt7InByb29mcyI6IFt7ImlkIjogIkplaFpMVTZuQ3BSZCIsICJhbW91bnQiOiAyLCAic2VjcmV0IjogIjBFN2lDazRkVmxSZjVQRjFnNFpWMnciLCAiQyI6ICIwM2FiNTgwYWQ5NTc3OGVkNTI5NmY4YmVlNjU1ZGJkN2Q2NDJmNWQzMmRlOGUyNDg0NzdlMGI0ZDZhYTg2M2ZjZDUifSwgeyJpZCI6ICJKZWhaTFU2bkNwUmQiLCAiYW1vdW50IjogOCwgInNlY3JldCI6ICJzNklwZXh3SGNxcXVLZDZYbW9qTDJnIiwgIkMiOiAiMDIyZDAwNGY5ZWMxNmE1OGFkOTAxNGMyNTliNmQ2MTRlZDM2ODgyOWYwMmMzODc3M2M0NzIyMWY0OTYxY2UzZjIzIn1dLCAibWludCI6ICJodHRwOi8vbG9jYWxob3N0OjMzMzgifV19" + token = TokenV3.deserialize(token_str) + assert len(token.get_proofs()) == 2 + + +def test_tokenv3_deserialize_serialize(): + token_str = "cashuAeyJ0b2tlbiI6IFt7InByb29mcyI6IFt7ImlkIjogIkplaFpMVTZuQ3BSZCIsICJhbW91bnQiOiAyLCAic2VjcmV0IjogIjBFN2lDazRkVmxSZjVQRjFnNFpWMnciLCAiQyI6ICIwM2FiNTgwYWQ5NTc3OGVkNTI5NmY4YmVlNjU1ZGJkN2Q2NDJmNWQzMmRlOGUyNDg0NzdlMGI0ZDZhYTg2M2ZjZDUifSwgeyJpZCI6ICJKZWhaTFU2bkNwUmQiLCAiYW1vdW50IjogOCwgInNlY3JldCI6ICJzNklwZXh3SGNxcXVLZDZYbW9qTDJnIiwgIkMiOiAiMDIyZDAwNGY5ZWMxNmE1OGFkOTAxNGMyNTliNmQ2MTRlZDM2ODgyOWYwMmMzODc3M2M0NzIyMWY0OTYxY2UzZjIzIn1dLCAibWludCI6ICJodHRwOi8vbG9jYWxob3N0OjMzMzgifV19" + token = TokenV3.deserialize(token_str) + assert token.serialize() == token_str