This commit is contained in:
callebtc
2022-12-25 13:22:53 +01:00
parent 77cbf86870
commit 0e3d5c4fd3
2 changed files with 101 additions and 45 deletions

View File

@@ -40,6 +40,8 @@ from cashu.wallet.crud import (
) )
from cashu.wallet.wallet import Wallet as Wallet from cashu.wallet.wallet import Wallet as Wallet
from .cli_helpers import verify_mints, redeem_multimint
async def init_wallet(wallet: Wallet): async def init_wallet(wallet: Wallet):
"""Performs migrations and loads proofs from db.""" """Performs migrations and loads proofs from db."""
@@ -193,18 +195,28 @@ async def balance(ctx, verbose):
print("") print("")
for k, v in keyset_balances.items(): for k, v in keyset_balances.items():
print( print(
f"Keyset: {k or 'undefined'} Balance: {v['balance']} sat (available: {v['available']} sat)" f"Keyset: {k or 'undefined'} - Balance: {v['available']} sat (with pending: {v['balance']} sat)"
) )
print("") print("")
mint_balances = await wallet.balance_per_minturl() mint_balances = await wallet.balance_per_minturl()
if len(mint_balances) > 1:
# if we have a balance on a non-default mint
show_mints = False
keysets = [k for k, v in wallet.balance_per_keyset().items()]
for k in keysets:
ks = await get_keyset(id=str(k), db=wallet.db)
if ks and ks.mint_url != ctx.obj["HOST"]:
show_mints = True
# or we have a balance on more than one mint
# show balances per mint # show balances per mint
if len(mint_balances) > 1 or show_mints:
print(f"You have balances in {len(mint_balances)} mints:") print(f"You have balances in {len(mint_balances)} mints:")
print("") print("")
for k, v in mint_balances.items(): for k, v in mint_balances.items():
print( print(
f"Mint: {k or 'undefined'} Balance: {v['balance']} sat (available: {v['available']} sat)" f"Mint: {k or 'undefined'} - Balance: {v['available']} sat (with pending: {v['balance']} sat)"
) )
print("") print("")
@@ -271,6 +283,8 @@ async def receive(ctx, token: str, lock: str):
wallet: Wallet = ctx.obj["WALLET"] wallet: Wallet = ctx.obj["WALLET"]
await wallet.load_mint() await wallet.load_mint()
wallet.status() wallet.status()
# extract script and signature from P2SH lock
if lock: if lock:
# load the script and signature of this address from the database # load the script and signature of this address from the database
assert len(lock.split("P2SH:")) == 2, Exception( assert len(lock.split("P2SH:")) == 2, Exception(
@@ -285,56 +299,31 @@ async def receive(ctx, token: str, lock: str):
else: else:
script, signature = None, None script, signature = None, None
# we support old tokens (< 0.7) without mint information and (W3siaWQ...)
# new tokens (>= 0.7) with multiple mint support (eyJ0b2...)
try: try:
# backwards compatibility < 0.7.0: tokens without mint information # backwards compatibility: tokens without mint information
proofs = [Proof(**p) for p in json.loads(base64.urlsafe_b64decode(token))] proofs = [Proof(**p) for p in json.loads(base64.urlsafe_b64decode(token))]
_, _ = await wallet.redeem(proofs, scnd_script=script, scnd_siganture=signature) _, _ = await wallet.redeem(proofs, scnd_script=script, scnd_siganture=signature)
except: except:
# assume token with mint information
dtoken = json.loads(base64.urlsafe_b64decode(token)) dtoken = json.loads(base64.urlsafe_b64decode(token))
assert "tokens" in dtoken, Exception("no proofs in token") assert "tokens" in dtoken, Exception("no proofs in token")
includes_mint_info: bool = "mints" in dtoken and dtoken.get("mints") is not None
# if there is a `mints` field in the token # if there is a `mints` field in the token
# we get the mint information in the token and load the keys of each mint # we check whether the token has mints that we don't know yet
# we then redeem the tokens for each keyset individually # and ask the user if they want to trust the new mitns
if "mints" in dtoken and dtoken.get("mints") is not None: if includes_mint_info:
for mint_id in dtoken.get("mints"): # we ask the user to confirm any new mints the tokens may include
for keyset in set(dtoken["mints"][mint_id]["ks"]): await verify_mints(ctx, dtoken)
mint_url = dtoken["mints"][mint_id]["url"] # proceed with redemption
# init a temporary wallet object await redeem_multimint(ctx, dtoken, script, signature)
keyset_wallet = Wallet( # reload main wallet so the balance updates
mint_url, os.path.join(CASHU_DIR, ctx.obj["WALLET_NAME"])
)
# first we check whether we know this mint already and ask the user
mint_keysets = await get_keyset(id=keyset, db=keyset_wallet.db)
if mint_keysets is None:
click.confirm(
f"Do you want to receive tokens from mint {mint_url}?",
abort=True,
default=True,
)
# make sure that this mint indeed supports this keyset
mint_keysets = await keyset_wallet._get_keysets(mint_url)
if keyset in mint_keysets["keysets"]:
# load the keys
await keyset_wallet.load_mint(keyset_id=keyset)
# redeem proofs of this keyset
redeem_proofs = [
Proof(**p)
for p in dtoken["tokens"]
if Proof(**p).id == keyset
]
_, _ = await keyset_wallet.redeem(
redeem_proofs, scnd_script=script, scnd_siganture=signature
)
keyset_wallet.db.connect()
# reload proofs to update the main wallet's balance
await wallet.load_proofs() await wallet.load_proofs()
else: else:
# no mint information present, assume default mint # no mint information present, use wallet's default mint
proofs = [Proof(**p) for p in dtoken["tokens"]] proofs = [Proof(**p) for p in dtoken["tokens"]]
_, _ = await wallet.redeem( _, _ = await wallet.redeem(
proofs, scnd_script=script, scnd_siganture=signature proofs, scnd_script=script, scnd_siganture=signature

View File

@@ -0,0 +1,67 @@
import click
import os
from cashu.core.settings import CASHU_DIR
from cashu.wallet.crud import get_keyset
from cashu.wallet.wallet import Wallet as Wallet
from cashu.core.base import Proof
async def verify_mints(ctx, dtoken):
trust_token_mints = True
for mint_id in dtoken.get("mints"):
for keyset in set(dtoken["mints"][mint_id]["ks"]):
mint_url = dtoken["mints"][mint_id]["url"]
# init a temporary wallet object
keyset_wallet = Wallet(
mint_url, os.path.join(CASHU_DIR, ctx.obj["WALLET_NAME"])
)
# make sure that this mint indeed supports this keyset
mint_keysets = await keyset_wallet._get_keysets(mint_url)
assert keyset in mint_keysets["keysets"], "mint does not have this keyset."
# we validate the keyset id by fetching the keys from the mint
mint_keyset = await keyset_wallet._get_keyset(mint_url, keyset)
assert keyset == mint_keyset.id, Exception("keyset not valid.")
# we check the db whether we know this keyset already and ask the user
mint_keysets = await get_keyset(id=keyset, db=keyset_wallet.db)
if mint_keysets is None:
# we encountered a new mint and ask for a user confirmation
trust_token_mints = False
print("")
print("Warning: Tokens are from a mint or keyset you don't know yet.")
print("\n")
print(f"Mint URL: {mint_url}")
print(f"Mint keyset: {keyset}")
print("\n")
click.confirm(
f"Do you want to trust this mint and receive the tokens?",
abort=True,
default=True,
)
trust_token_mints = True
assert trust_token_mints, Exception("Aborted!")
async def redeem_multimint(ctx, dtoken, script, signature):
# we get the mint information in the token and load the keys of each mint
# we then redeem the tokens for each keyset individually
for mint_id in dtoken.get("mints"):
for keyset in set(dtoken["mints"][mint_id]["ks"]):
mint_url = dtoken["mints"][mint_id]["url"]
# init a temporary wallet object
keyset_wallet = Wallet(
mint_url, os.path.join(CASHU_DIR, ctx.obj["WALLET_NAME"])
)
# load the keys
await keyset_wallet.load_mint(keyset_id=keyset)
# redeem proofs of this keyset
redeem_proofs = [
Proof(**p) for p in dtoken["tokens"] if Proof(**p).id == keyset
]
_, _ = await keyset_wallet.redeem(
redeem_proofs, scnd_script=script, scnd_siganture=signature
)