import hashlib import os from typing import Optional from loguru import logger from ..core.base import Token, TokenV3, TokenV4 from ..core.db import Database from ..core.helpers import sum_proofs from ..core.migrations import migrate_databases from ..core.settings import settings from ..wallet import migrations from ..wallet.crud import get_keysets from ..wallet.wallet import Wallet async def migrate_wallet_db(db: Database): await migrate_databases(db, migrations) async def init_wallet(wallet: Wallet, load_proofs: bool = True): """Performs migrations and loads proofs from db.""" await wallet._migrate_database() await wallet._init_private_key() if load_proofs: await wallet.load_proofs(reload=True) async def list_mints(wallet: Wallet): await wallet.load_proofs() balances = await wallet.balance_per_minturl() mints = list(balances.keys()) if wallet.url not in mints: mints.append(wallet.url) return mints async def redeem_TokenV3(wallet: Wallet, token: TokenV3) -> Wallet: """ Helper function to iterate thruogh a token with multiple mints and redeem them from these mints one keyset at a time. """ if not token.unit: # load unit from wallet keyset db keysets = await get_keysets(id=token.token[0].proofs[0].id, db=wallet.db) if keysets: token.unit = keysets[0].unit.name for t in token.token: assert t.mint, Exception("redeem_TokenV3: multimint redeem without URL") mint_wallet = await Wallet.with_db( t.mint, os.path.join(settings.cashu_dir, wallet.name), unit=token.unit or wallet.unit.name, auth_db=wallet.auth_db.db_location if wallet.auth_db else None, auth_keyset_id=wallet.auth_keyset_id, ) keyset_ids = mint_wallet._get_proofs_keyset_ids(t.proofs) logger.trace(f"Keysets in tokens: {' '.join(set(keyset_ids))}") await mint_wallet.load_mint() proofs_to_keep, _ = await mint_wallet.redeem(t.proofs) print(f"Received {mint_wallet.unit.str(sum_proofs(proofs_to_keep))}") # return the last mint_wallet return mint_wallet async def redeem_TokenV4(wallet: Wallet, token: TokenV4) -> Wallet: """ Redeem a token with a single mint. """ await wallet.load_mint() proofs_to_keep, _ = await wallet.redeem(token.proofs) print(f"Received {wallet.unit.str(sum_proofs(proofs_to_keep))}") return wallet async def redeem_universal(wallet: Wallet, token: Token) -> Wallet: if isinstance(token, TokenV3): return await redeem_TokenV3(wallet, token) if isinstance(token, TokenV4): return await redeem_TokenV4(wallet, token) raise Exception("Invalid token type") def deserialize_token_from_string(token: str) -> Token: # deserialize token if token.startswith("cashuA"): tokenV3Obj = TokenV3.deserialize(token) try: return TokenV4.from_tokenv3(tokenV3Obj) except ValueError as e: logger.debug(f"Could not convert TokenV3 to TokenV4: {e}") return tokenV3Obj if token.startswith("cashuB"): tokenObj = TokenV4.deserialize(token) return tokenObj raise Exception("Invalid token") async def receive( wallet: Wallet, token: Token, ) -> Wallet: await wallet.load_proofs() mint_wallet = await redeem_universal(wallet, token) # reload main wallet so the balance updates await wallet.load_proofs(reload=True) return mint_wallet async def send( wallet: Wallet, *, amount: int, lock: str, legacy: bool, offline: bool = False, include_dleq: bool = False, include_fees: bool = False, memo: Optional[str] = None, force_swap: bool = False, ): """ Prints token to send to stdout. """ secret_lock = None if lock: assert len(lock) > 21, Exception( "Error: lock has to be at least 22 characters long." ) if not lock.startswith("P2PK:"): raise Exception("Error: lock has to start with P2PK:") # we add a time lock to the P2PK lock by appending the current unix time + 14 days else: logger.debug(f"Locking token to: {lock}") logger.debug( f"Adding a time lock of {settings.locktime_delta_seconds} seconds." ) secret_lock = await wallet.create_p2pk_lock( lock.split(":")[1], locktime_seconds=settings.locktime_delta_seconds, sig_all=False, n_sigs=1, ) logger.debug(f"Secret lock: {secret_lock}") await wallet.load_proofs() await wallet.load_mint() if secret_lock or force_swap: _, send_proofs = await wallet.swap_to_send( wallet.proofs, amount, set_reserved=False, # we set reserved later secret_lock=secret_lock, ) else: send_proofs, fees = await wallet.select_to_send( wallet.proofs, amount, set_reserved=False, # we set reserved later offline=offline, include_fees=include_fees, ) token = await wallet.serialize_proofs( send_proofs, include_dleq=include_dleq, legacy=legacy, memo=memo ) print(token) await wallet.set_reserved(send_proofs, reserved=True) return wallet.available_balance, token def check_payment_preimage( payment_hash: str, preimage: str, ) -> bool: return ( bytes.fromhex(payment_hash) == hashlib.sha256(bytes.fromhex(preimage)).digest() )