mirror of
https://github.com/aljazceru/nutshell.git
synced 2026-02-04 00:04:20 +01:00
TokenV4 CBOR serialization (#502)
* WIP: cashuB with CBOR * working * tokenv4 works * fix mypy
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import pytest
|
||||
|
||||
from cashu.core.base import TokenV3
|
||||
from cashu.core.base import TokenV3, TokenV4, Unit
|
||||
from cashu.core.helpers import calculate_number_of_blank_outputs
|
||||
from cashu.core.split import amount_split
|
||||
|
||||
@@ -21,6 +21,20 @@ def test_tokenv3_deserialize_get_attributes():
|
||||
assert len(token.get_proofs()) == 2
|
||||
|
||||
|
||||
def test_tokenv3_deserialize_serialize():
|
||||
token_str = (
|
||||
"cashuAeyJ0b2tlbiI6IFt7InByb29mcyI6IFt7ImlkIjogIkplaFpMVTZuQ3BSZCIsICJh"
|
||||
"bW91bnQiOiAyLCAic2VjcmV0IjogIjBFN2lDazRkVmxSZjVQRjFnNFpWMnci"
|
||||
"LCAiQyI6ICIwM2FiNTgwYWQ5NTc3OGVkNTI5NmY4YmVlNjU1ZGJkN2Q2NDJmNWQzMmRlOG"
|
||||
"UyNDg0NzdlMGI0ZDZhYTg2M2ZjZDUifSwgeyJpZCI6ICJKZWhaTFU2bkNwUmQiLCAiYW"
|
||||
"1vdW50IjogOCwgInNlY3JldCI6ICJzNklwZXh3SGNxcXVLZDZYbW9qTDJnIiwgIkMiOiAiM"
|
||||
"DIyZDAwNGY5ZWMxNmE1OGFkOTAxNGMyNTliNmQ2MTRlZDM2ODgyOWYwMmMzODc3M2M0"
|
||||
"NzIyMWY0OTYxY2UzZjIzIn1dLCAibWludCI6ICJodHRwOi8vbG9jYWxob3N0OjMzMzgifV19"
|
||||
)
|
||||
token = TokenV3.deserialize(token_str)
|
||||
assert token.serialize() == token_str
|
||||
|
||||
|
||||
def test_tokenv3_deserialize_serialize_with_dleq():
|
||||
token_str = (
|
||||
"cashuAeyJ0b2tlbiI6IFt7InByb29mcyI6IFt7ImlkIjogIjFjQ05JQVoyWC93M"
|
||||
@@ -45,20 +59,6 @@ def test_tokenv3_deserialize_serialize_with_dleq():
|
||||
assert token.serialize(include_dleq=True) == token_str
|
||||
|
||||
|
||||
def test_tokenv3_deserialize_serialize():
|
||||
token_str = (
|
||||
"cashuAeyJ0b2tlbiI6IFt7InByb29mcyI6IFt7ImlkIjogIkplaFpMVTZuQ3BSZCIsICJh"
|
||||
"bW91bnQiOiAyLCAic2VjcmV0IjogIjBFN2lDazRkVmxSZjVQRjFnNFpWMnci"
|
||||
"LCAiQyI6ICIwM2FiNTgwYWQ5NTc3OGVkNTI5NmY4YmVlNjU1ZGJkN2Q2NDJmNWQzMmRlOG"
|
||||
"UyNDg0NzdlMGI0ZDZhYTg2M2ZjZDUifSwgeyJpZCI6ICJKZWhaTFU2bkNwUmQiLCAiYW"
|
||||
"1vdW50IjogOCwgInNlY3JldCI6ICJzNklwZXh3SGNxcXVLZDZYbW9qTDJnIiwgIkMiOiAiM"
|
||||
"DIyZDAwNGY5ZWMxNmE1OGFkOTAxNGMyNTliNmQ2MTRlZDM2ODgyOWYwMmMzODc3M2M0"
|
||||
"NzIyMWY0OTYxY2UzZjIzIn1dLCAibWludCI6ICJodHRwOi8vbG9jYWxob3N0OjMzMzgifV19"
|
||||
)
|
||||
token = TokenV3.deserialize(token_str)
|
||||
assert token.serialize() == token_str
|
||||
|
||||
|
||||
def test_tokenv3_deserialize_serialize_no_dleq():
|
||||
token_str = (
|
||||
"cashuAeyJ0b2tlbiI6IFt7InByb29mcyI6IFt7ImlkIjogIjFjQ05JQVoyWC93MSIsICJhb"
|
||||
@@ -107,7 +107,7 @@ def test_tokenv3_deserialize_with_memo():
|
||||
assert token.memo == "Test memo"
|
||||
|
||||
|
||||
def test_serialize_example_token_nut00():
|
||||
def test_tokenv3_serialize_example_token_nut00():
|
||||
token_dict = {
|
||||
"token": [
|
||||
{
|
||||
@@ -144,6 +144,107 @@ def test_serialize_example_token_nut00():
|
||||
)
|
||||
|
||||
|
||||
def test_tokenv4_deserialize_get_attributes():
|
||||
token_str = "cashuBo2F0gaJhaUgArSaMTR9YJmFwgqNhYQJhc3hAMDZlM2UzZjY4NDRiOGZkOGQ3NDMwODY1MjY3MjQ5YWU3NjdhMzg5MDBjODdkNGE0ZDMxOGY4MTJmNzkzN2ZiMmFjWCEDXDG_wzG35Lu4vcAtiycLSQlNqH65afih9N2SrFJn3GCjYWEIYXN4QDBmNTE5YjgwOWZlNmQ5MzZkMjVhYmU1YjhjYTZhMDRlNDc3OTJjOTI0YTkwZWRmYjU1MmM1ZjkzODJkNzFjMDJhY1ghA4CNH8dD8NNt715E37Ar65X6p6uBUoDbe8JipQp81TIgYW11aHR0cDovL2xvY2FsaG9zdDozMzM4YXVjc2F0"
|
||||
token = TokenV4.deserialize(token_str)
|
||||
assert token.mint == "http://localhost:3338"
|
||||
assert token.amounts == [2, 8]
|
||||
assert token.amount == 10
|
||||
assert token.unit == Unit.sat.name
|
||||
assert token.memo is None
|
||||
assert len(token.proofs) == 2
|
||||
|
||||
|
||||
def test_tokenv4_deserialize_serialize():
|
||||
token_str = "cashuBo2F0gaJhaUgArSaMTR9YJmFwgqNhYQJhc3hAMDZlM2UzZjY4NDRiOGZkOGQ3NDMwODY1MjY3MjQ5YWU3NjdhMzg5MDBjODdkNGE0ZDMxOGY4MTJmNzkzN2ZiMmFjWCEDXDG_wzG35Lu4vcAtiycLSQlNqH65afih9N2SrFJn3GCjYWEIYXN4QDBmNTE5YjgwOWZlNmQ5MzZkMjVhYmU1YjhjYTZhMDRlNDc3OTJjOTI0YTkwZWRmYjU1MmM1ZjkzODJkNzFjMDJhY1ghA4CNH8dD8NNt715E37Ar65X6p6uBUoDbe8JipQp81TIgYW11aHR0cDovL2xvY2FsaG9zdDozMzM4YXVjc2F0"
|
||||
token = TokenV4.deserialize(token_str)
|
||||
assert token.serialize() == token_str
|
||||
|
||||
|
||||
def test_tokenv4_deserialize_with_dleq():
|
||||
token_str = "cashuBo2F0gaJhaUgArSaMTR9YJmFwgqRhYQhhc3hAY2I4ZWViZWE3OGRjMTZmMWU4MmY5YTZlOWI4YTU3YTM5ZDM2M2M5MzZkMzBmZTI5YmVlZDI2M2MwOGFkOTY2M2FjWCECRmlA6zYOcRSgigEUDv0BBtC2Ag8x8ZOaZUKo8J2_VWdhZKNhZVggscHmr2oHB_x9Bzhgeg2p9Vbq5Ai23olDz2JbmCRx6dlhc1ggrPmtYrRAgEHnYLIQ83cgyFjAjWNqMeNhUadHMxEm0edhclggQ5c_5bES_NhtzunlDls70fhMDWDgo9DY0kk1GuJGM2ikYWECYXN4QDQxN2E2MjZmNWMyNmVhNjliODM0YTZkZTcxYmZiMGY3ZTQ0NDhlZGFkY2FlNGRmNWVhMzM3NDdmOTVhYjRhYjRhY1ghAwyZ1QstFpNe0sppbduQxiePmGVUUk0mWDj5JAFs74-LYWSjYWVYIPyAzLub_bwc60qFkNfETjig-ESZSR8xdpANy1rHwvHKYXNYIOCInwuipARTL8IFT6NoSJqeeSMjlcbPzL-YSmXjDLIuYXJYIOLk-C0Fhba02B0Ut1BjMQqzxVGaO1NJM9Wi_aDQ37jqYW11aHR0cDovL2xvY2FsaG9zdDozMzM4YXVjc2F0"
|
||||
token = TokenV4.deserialize(token_str)
|
||||
assert token.proofs[0].dleq is not None
|
||||
assert token.proofs[0].dleq.e
|
||||
assert token.proofs[0].dleq.s
|
||||
assert token.proofs[0].dleq.r
|
||||
|
||||
assert token.serialize(include_dleq=True) == token_str
|
||||
|
||||
|
||||
def test_tokenv4_serialize_example_single_keyset_nut00():
|
||||
token_dict = {
|
||||
"t": [
|
||||
{
|
||||
"i": bytes.fromhex("00ad268c4d1f5826"),
|
||||
"p": [
|
||||
{
|
||||
"a": 1,
|
||||
"s": "9a6dbb847bd232ba76db0df197216b29d3b8cc14553cd27827fc1cc942fedb4e",
|
||||
"c": bytes.fromhex(
|
||||
"038618543ffb6b8695df4ad4babcde92a34a96bdcd97dcee0d7ccf98d472126792"
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
"d": "Thank you",
|
||||
"m": "http://localhost:3338",
|
||||
"u": "sat",
|
||||
}
|
||||
tokenObj = TokenV4.parse_obj(token_dict)
|
||||
assert (
|
||||
tokenObj.serialize()
|
||||
== "cashuBpGF0gaJhaUgArSaMTR9YJmFwgaNhYQFhc3hAOWE2ZGJiODQ3YmQyMzJiYTc2ZGIwZGYxOTcyMTZiMjlkM2I4Y2MxNDU1M2NkMjc4MjdmYzFjYzk0MmZlZGI0ZWFjWCEDhhhUP_trhpXfStS6vN6So0qWvc2X3O4NfM-Y1HISZ5JhZGlUaGFuayB5b3VhbXVodHRwOi8vbG9jYWxob3N0OjMzMzhhdWNzYXQ="
|
||||
)
|
||||
|
||||
|
||||
def test_tokenv4_serialize_example_token_nut00():
|
||||
token_dict = {
|
||||
"t": [
|
||||
{
|
||||
"i": bytes.fromhex("00ffd48b8f5ecf80"),
|
||||
"p": [
|
||||
{
|
||||
"a": 1,
|
||||
"s": "acc12435e7b8484c3cf1850149218af90f716a52bf4a5ed347e48ecc13f77388",
|
||||
"c": bytes.fromhex(
|
||||
"0244538319de485d55bed3b29a642bee5879375ab9e7a620e11e48ba482421f3cf"
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"i": bytes.fromhex("00ad268c4d1f5826"),
|
||||
"p": [
|
||||
{
|
||||
"a": 2,
|
||||
"s": "1323d3d4707a58ad2e23ada4e9f1f49f5a5b4ac7b708eb0d61f738f48307e8ee",
|
||||
"c": bytes.fromhex(
|
||||
"023456aa110d84b4ac747aebd82c3b005aca50bf457ebd5737a4414fac3ae7d94d"
|
||||
),
|
||||
},
|
||||
{
|
||||
"a": 1,
|
||||
"s": "56bcbcbb7cc6406b3fa5d57d2174f4eff8b4402b176926d3a57d3c3dcbb59d57",
|
||||
"c": bytes.fromhex(
|
||||
"0273129c5719e599379a974a626363c333c56cafc0e6d01abe46d5808280789c63"
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
"m": "http://localhost:3338",
|
||||
"u": "sat",
|
||||
}
|
||||
tokenObj = TokenV4.parse_obj(token_dict)
|
||||
|
||||
assert (
|
||||
tokenObj.serialize()
|
||||
== "cashuBo2F0gqJhaUgA_9SLj17PgGFwgaNhYQFhc3hAYWNjMTI0MzVlN2I4NDg0YzNjZjE4NTAxNDkyMThhZjkwZjcxNmE1MmJmNGE1ZWQzNDdlNDhlY2MxM2Y3NzM4OGFjWCECRFODGd5IXVW-07KaZCvuWHk3WrnnpiDhHki6SCQh88-iYWlIAK0mjE0fWCZhcIKjYWECYXN4QDEzMjNkM2Q0NzA3YTU4YWQyZTIzYWRhNGU5ZjFmNDlmNWE1YjRhYzdiNzA4ZWIwZDYxZjczOGY0ODMwN2U4ZWVhY1ghAjRWqhENhLSsdHrr2Cw7AFrKUL9Ffr1XN6RBT6w659lNo2FhAWFzeEA1NmJjYmNiYjdjYzY0MDZiM2ZhNWQ1N2QyMTc0ZjRlZmY4YjQ0MDJiMTc2OTI2ZDNhNTdkM2MzZGNiYjU5ZDU3YWNYIQJzEpxXGeWZN5qXSmJjY8MzxWyvwObQGr5G1YCCgHicY2FtdWh0dHA6Ly9sb2NhbGhvc3Q6MzMzOGF1Y3NhdA=="
|
||||
)
|
||||
|
||||
|
||||
def test_calculate_number_of_blank_outputs():
|
||||
# Example from NUT-08 specification.
|
||||
fee_reserve_sat = 1000
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
import asyncio
|
||||
import base64
|
||||
import json
|
||||
from typing import Tuple
|
||||
|
||||
import pytest
|
||||
from click.testing import CliRunner
|
||||
|
||||
from cashu.core.base import TokenV3
|
||||
from cashu.core.base import TokenV4
|
||||
from cashu.core.settings import settings
|
||||
from cashu.wallet.cli.cli import cli
|
||||
from cashu.wallet.wallet import Wallet
|
||||
@@ -378,8 +376,8 @@ def test_send(mint, cli_prefix):
|
||||
assert result.exception is None
|
||||
print("test_send", result.output)
|
||||
token_str = result.output.split("\n")[0]
|
||||
assert "cashuA" in token_str, "output does not have a token"
|
||||
token = TokenV3.deserialize(token_str)
|
||||
assert "cashuB" in token_str, "output does not have a token"
|
||||
token = TokenV4.deserialize(token_str).to_tokenv3()
|
||||
assert token.token[0].proofs[0].dleq is None, "dleq included"
|
||||
|
||||
|
||||
@@ -392,8 +390,8 @@ def test_send_with_dleq(mint, cli_prefix):
|
||||
assert result.exception is None
|
||||
print("test_send_with_dleq", result.output)
|
||||
token_str = result.output.split("\n")[0]
|
||||
assert "cashuA" in token_str, "output does not have a token"
|
||||
token = TokenV3.deserialize(token_str)
|
||||
assert "cashuB" in token_str, "output does not have a token"
|
||||
token = TokenV4.deserialize(token_str).to_tokenv3()
|
||||
assert token.token[0].proofs[0].dleq is not None, "no dleq included"
|
||||
|
||||
|
||||
@@ -406,8 +404,8 @@ def test_send_legacy(mint, cli_prefix):
|
||||
assert result.exception is None
|
||||
print("test_send_legacy", result.output)
|
||||
# this is the legacy token in the output
|
||||
token_str = result.output.split("\n")[4]
|
||||
assert token_str.startswith("eyJwcm9v"), "output is not as expected"
|
||||
token_str = result.output.split("\n")[0]
|
||||
assert token_str.startswith("cashuAey"), "output is not as expected"
|
||||
|
||||
|
||||
def test_send_offline(mint, cli_prefix):
|
||||
@@ -419,7 +417,7 @@ def test_send_offline(mint, cli_prefix):
|
||||
assert result.exception is None
|
||||
print("SEND")
|
||||
print("test_send_without_split", result.output)
|
||||
assert "cashuA" in result.output, "output does not have a token"
|
||||
assert "cashuB" in result.output, "output does not have a token"
|
||||
|
||||
|
||||
def test_send_too_much(mint, cli_prefix):
|
||||
@@ -447,113 +445,6 @@ def test_receive_tokenv3(mint, cli_prefix):
|
||||
print(result.output)
|
||||
|
||||
|
||||
def test_receive_tokenv3_no_mint(mint, cli_prefix):
|
||||
# this test works only if the previous test succeeds because we simulate the case
|
||||
# where the mint URL is not in the token therefore, we need to know the mint keyset
|
||||
# already and have the mint URL in the db
|
||||
runner = CliRunner()
|
||||
token_dict = {
|
||||
"token": [
|
||||
{
|
||||
"proofs": [
|
||||
{
|
||||
"id": "009a1f293253e41e",
|
||||
"amount": 2,
|
||||
"secret": "ea3420987e1ecd71de58e4ff00e8a94d1f1f9333dad98e923e3083d21bf314e2",
|
||||
"C": "0204eb99cf27105b4de4029478376d6f71e9e3d5af1cc28a652c028d1bcd6537cc",
|
||||
},
|
||||
{
|
||||
"id": "009a1f293253e41e",
|
||||
"amount": 8,
|
||||
"secret": "3447975db92f43b269290e05b91805df7aa733f622e55d885a2cab78e02d4a72",
|
||||
"C": "0286c78750d414bc067178cbac0f3551093cea47d213ebf356899c972448ee6255",
|
||||
},
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
token = "cashuA" + base64.b64encode(json.dumps(token_dict).encode()).decode()
|
||||
print("RECEIVE")
|
||||
print(token)
|
||||
result = runner.invoke(
|
||||
cli,
|
||||
[
|
||||
*cli_prefix,
|
||||
"receive",
|
||||
token,
|
||||
],
|
||||
)
|
||||
assert result.exception is None
|
||||
print(result.output)
|
||||
|
||||
|
||||
def test_receive_tokenv2(mint, cli_prefix):
|
||||
runner = CliRunner()
|
||||
token_dict = {
|
||||
"proofs": [
|
||||
{
|
||||
"id": "009a1f293253e41e",
|
||||
"amount": 2,
|
||||
"secret": (
|
||||
"a1efb610726b342aec209375397fee86a0b88732779ce218e99132f9a975db2a"
|
||||
),
|
||||
"C": (
|
||||
"03057e5fe352bac785468ffa51a1ecf0f75af24d2d27ab1fd00164672a417d9523"
|
||||
),
|
||||
},
|
||||
{
|
||||
"id": "009a1f293253e41e",
|
||||
"amount": 8,
|
||||
"secret": (
|
||||
"b065a17938bc79d6224dc381873b8b7f3a46267e8b00d9ce59530354d9d81ae4"
|
||||
),
|
||||
"C": (
|
||||
"021e83773f5eb66f837a5721a067caaa8d7018ef0745b4302f4e2c6cac8806dc69"
|
||||
),
|
||||
},
|
||||
],
|
||||
"mints": [{"url": "http://localhost:3337", "ids": ["009a1f293253e41e"]}],
|
||||
}
|
||||
token = base64.b64encode(json.dumps(token_dict).encode()).decode()
|
||||
result = runner.invoke(
|
||||
cli,
|
||||
[*cli_prefix, "receive", token],
|
||||
)
|
||||
assert result.exception is None
|
||||
print("RECEIVE")
|
||||
print(result.output)
|
||||
|
||||
|
||||
def test_receive_tokenv1(mint, cli_prefix):
|
||||
runner = CliRunner()
|
||||
token_dict = [
|
||||
{
|
||||
"id": "009a1f293253e41e",
|
||||
"amount": 2,
|
||||
"secret": (
|
||||
"bc0360c041117969ef7b8add48d0981c669619aa5743cccce13d4a771c9e164d"
|
||||
),
|
||||
"C": "026fd492f933e9240f36fb2559a7327f47b3441b895a5f8f0b1d6825fee73438f0",
|
||||
},
|
||||
{
|
||||
"id": "009a1f293253e41e",
|
||||
"amount": 8,
|
||||
"secret": (
|
||||
"cf83bd8df35bb104d3818511c1653e9ebeb2b645a36fd071b2229aa2c3044acd"
|
||||
),
|
||||
"C": "0279606f3dfd7784757c6320b17e1bf2211f284318814c12bfaa40680e017abd34",
|
||||
},
|
||||
]
|
||||
token = base64.b64encode(json.dumps(token_dict).encode()).decode()
|
||||
result = runner.invoke(
|
||||
cli,
|
||||
[*cli_prefix, "receive", token],
|
||||
)
|
||||
assert result.exception is None
|
||||
print("RECEIVE")
|
||||
print(result.output)
|
||||
|
||||
|
||||
def test_nostr_send(mint, cli_prefix):
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(
|
||||
|
||||
Reference in New Issue
Block a user