use tokenObj in burn (#156)

* use tokenObj in burn

* refactor token serialization

* add tests

* rename
This commit is contained in:
calle
2023-04-01 00:46:56 +02:00
committed by GitHub
parent 73f8c277b9
commit 2ded9c8b5c
5 changed files with 65 additions and 36 deletions

View File

@@ -1,3 +1,5 @@
import base64
import json
from sqlite3 import Row from sqlite3 import Row
from typing import Any, Dict, List, Optional, TypedDict, Union from typing import Any, Dict, List, Optional, TypedDict, Union
@@ -357,3 +359,37 @@ class TokenV3(BaseModel):
if self.memo: if self.memo:
return_dict.update(dict(memo=self.memo)) # type: ignore return_dict.update(dict(memo=self.memo)) # type: ignore
return return_dict 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<json_urlsafe_base64>.
"""
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<json_urlsafe_base64>.
"""
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

View File

@@ -18,7 +18,7 @@ import click
from click import Context from click import Context
from loguru import logger 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.helpers import sum_proofs
from cashu.core.migrations import migrate_databases from cashu.core.migrations import migrate_databases
from cashu.core.settings import settings from cashu.core.settings import settings
@@ -342,8 +342,7 @@ async def receive(ctx: Context, token: str, lock: str):
# ----- receive token ----- # ----- receive token -----
# deserialize token # deserialize token
# dtoken = json.loads(base64.urlsafe_b64decode(token)) tokenObj = TokenV3.deserialize(token)
tokenObj = wallet._deserialize_token_V3(token)
# tokenObj = TokenV2.parse_obj(dtoken) # tokenObj = TokenV2.parse_obj(dtoken)
assert len(tokenObj.token), Exception("no proofs in token") 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] proofs = [proof for proof in reserved_proofs if proof["send_id"] == delete]
else: else:
# check only the specified ones # check only the specified ones
proofs = [ tokenObj = TokenV3.deserialize(token)
Proof(**p) for p in json.loads(base64.urlsafe_b64decode(token))["proofs"] proofs = tokenObj.get_proofs()
]
if delete: if delete:
await wallet.invalidate(proofs, check_spendable=False) await wallet.invalidate(proofs, check_spendable=False)

View File

@@ -235,7 +235,7 @@ async def serialize_TokenV2_to_TokenV3(wallet: Wallet, tokenv2: TokenV2):
tokenv3 = TokenV3(token=[TokenV3Token(proofs=tokenv2.proofs)]) tokenv3 = TokenV3(token=[TokenV3Token(proofs=tokenv2.proofs)])
if tokenv2.mints: if tokenv2.mints:
tokenv3.token[0].mint = tokenv2.mints[0].url tokenv3.token[0].mint = tokenv2.mints[0].url
token_serialized = await wallet._serialize_token_V3(tokenv3) token_serialized = tokenv3.serialize()
return token_serialized return token_serialized
@@ -248,5 +248,5 @@ async def serialize_TokenV1_to_TokenV3(wallet: Wallet, tokenv1: TokenV1):
TokenV3: TokenV3 TokenV3: TokenV3
""" """
tokenv3 = TokenV3(token=[TokenV3Token(proofs=tokenv1.__root__)]) tokenv3 = TokenV3(token=[TokenV3Token(proofs=tokenv1.__root__)])
token_serialized = await wallet._serialize_token_V3(tokenv3) token_serialized = tokenv3.serialize()
return token_serialized return token_serialized

View File

@@ -44,7 +44,7 @@ from cashu.core.script import (
step1_carol_create_p2sh_address, step1_carol_create_p2sh_address,
step2_carol_sign_tx, 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.settings import settings
from cashu.core.split import amount_split from cashu.core.split import amount_split
from cashu.tor.tor import TorProxy from cashu.tor.tor import TorProxy
@@ -111,7 +111,7 @@ class LedgerAPI:
return return
def _construct_proofs( 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.""" """Returns proofs of promise from promises. Wants secrets and blinding factors rs."""
proofs: List[Proof] = [] proofs: List[Proof] = []
@@ -199,7 +199,7 @@ class LedgerAPI:
secrets secrets
), f"len(amounts)={len(amounts)} not equal to len(secrets)={len(secrets)}" ), f"len(amounts)={len(amounts)} not equal to len(secrets)={len(secrets)}"
outputs: List[BlindedMessage] = [] outputs: List[BlindedMessage] = []
rs = [] rs: List[PrivateKey] = []
for secret, amount in zip(secrets, amounts): for secret, amount in zip(secrets, amounts):
B_, r = b_dhke.step1_alice(secret) B_, r = b_dhke.step1_alice(secret)
rs.append(r) rs.append(r)
@@ -684,30 +684,6 @@ class Wallet(LedgerAPI):
token.token.append(token_proofs) token.token.append(token_proofs)
return token return token
async def _serialize_token_V3(self, token: TokenV3):
"""
Takes a TokenV3 and serializes it as "cashuA<json_urlsafe_base64>.
"""
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<json_urlsafe_base64>.
"""
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( async def serialize_proofs(
self, proofs: List[Proof], include_mints=True, legacy=False self, proofs: List[Proof], include_mints=True, legacy=False
): ):
@@ -728,7 +704,7 @@ class Wallet(LedgerAPI):
# V3 tokens # V3 tokens
token = await self._make_token(proofs, include_mints) 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): async def _make_token_v2(self, proofs: List[Proof], include_mints=True):
""" """

View File

@@ -1,5 +1,24 @@
from cashu.core.base import TokenV3
from cashu.core.split import amount_split from cashu.core.split import amount_split
def test_get_output_split(): def test_get_output_split():
assert amount_split(13) == [1, 4, 8] 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