mirror of
https://github.com/aljazceru/nutshell.git
synced 2026-02-03 15:54:20 +01:00
Fix receive -a to receive all pending tokens (#578)
* catch error if receive -a doesnt work with a certain mint * receive pending tokens from multiple mints * receive pending from all mints and catch exceptions
This commit is contained in:
@@ -36,6 +36,7 @@ from ..cli.cli_helpers import (
|
||||
get_unit_wallet,
|
||||
print_balance,
|
||||
print_mint_balances,
|
||||
receive_all_pending,
|
||||
verify_mint,
|
||||
)
|
||||
from ..helpers import (
|
||||
@@ -601,7 +602,7 @@ async def receive_cli(
|
||||
all: bool,
|
||||
):
|
||||
wallet: Wallet = ctx.obj["WALLET"]
|
||||
|
||||
# receive a specific token
|
||||
if token:
|
||||
token_obj = deserialize_token_from_string(token)
|
||||
# verify that we trust the mint in this tokens
|
||||
@@ -615,30 +616,16 @@ async def receive_cli(
|
||||
await verify_mint(mint_wallet, mint_url)
|
||||
receive_wallet = await receive(mint_wallet, token_obj)
|
||||
ctx.obj["WALLET"] = receive_wallet
|
||||
# receive tokens via nostr
|
||||
elif nostr:
|
||||
await receive_nostr(wallet)
|
||||
# exit on keypress
|
||||
input("Enter any text to exit.")
|
||||
print("Exiting.")
|
||||
os._exit(0)
|
||||
# receive all pending outgoing tokens back to the wallet
|
||||
elif all:
|
||||
reserved_proofs = await get_reserved_proofs(wallet.db)
|
||||
if len(reserved_proofs):
|
||||
for key, value in groupby(reserved_proofs, key=itemgetter("send_id")): # type: ignore
|
||||
proofs = list(value)
|
||||
token = await wallet.serialize_proofs(proofs)
|
||||
token_obj = TokenV4.deserialize(token)
|
||||
# verify that we trust the mint of this token
|
||||
# ask the user if they want to trust the mint
|
||||
mint_url = token_obj.mint
|
||||
mint_wallet = Wallet(
|
||||
mint_url,
|
||||
os.path.join(settings.cashu_dir, wallet.name),
|
||||
unit=token_obj.unit,
|
||||
)
|
||||
await verify_mint(mint_wallet, mint_url)
|
||||
receive_wallet = await receive(wallet, token_obj)
|
||||
ctx.obj["WALLET"] = receive_wallet
|
||||
await receive_all_pending(ctx, wallet)
|
||||
else:
|
||||
print("Error: enter token or use either flag --nostr or --all.")
|
||||
return
|
||||
@@ -767,7 +754,9 @@ async def pending(ctx: Context, legacy, number: int, offset: int):
|
||||
)
|
||||
print(f"Legacy token: {token_legacy}\n")
|
||||
print("--------------------------\n")
|
||||
print("To remove all spent tokens use: cashu burn -a")
|
||||
print("To remove all pending tokens that are already spent use: cashu burn -a")
|
||||
print("To remove a specific pending token use: cashu burn <token>")
|
||||
print("To receive all pending tokens use: cashu receive -a")
|
||||
|
||||
|
||||
@cli.command("lock", help="Generate receiving lock.")
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
import os
|
||||
from itertools import groupby
|
||||
from operator import itemgetter
|
||||
|
||||
import click
|
||||
from click import Context
|
||||
@@ -6,8 +9,15 @@ from loguru import logger
|
||||
|
||||
from ...core.base import Unit
|
||||
from ...core.settings import settings
|
||||
from ...wallet.crud import get_keysets
|
||||
from ...wallet.crud import (
|
||||
get_keysets,
|
||||
get_reserved_proofs,
|
||||
)
|
||||
from ...wallet.wallet import Wallet as Wallet
|
||||
from ..helpers import (
|
||||
deserialize_token_from_string,
|
||||
receive,
|
||||
)
|
||||
|
||||
|
||||
async def print_balance(ctx: Context):
|
||||
@@ -170,3 +180,40 @@ async def verify_mint(mint_wallet: Wallet, url: str):
|
||||
)
|
||||
else:
|
||||
logger.debug(f"We know mint {url} already")
|
||||
|
||||
|
||||
async def receive_all_pending(ctx: Context, wallet: Wallet):
|
||||
reserved_proofs = await get_reserved_proofs(wallet.db)
|
||||
if not len(reserved_proofs):
|
||||
print("No pending proofs to receive.")
|
||||
return
|
||||
for key, value in groupby(reserved_proofs, key=itemgetter("send_id")): # type: ignore
|
||||
mint_url = None
|
||||
token_obj = None
|
||||
try:
|
||||
proofs = list(value)
|
||||
mint_url, unit = await wallet._get_proofs_mint_unit(proofs)
|
||||
mint_wallet = await Wallet.with_db(
|
||||
url=mint_url,
|
||||
db=os.path.join(settings.cashu_dir, wallet.name),
|
||||
name=wallet.name,
|
||||
unit=unit.name,
|
||||
)
|
||||
# verify that we trust the mint of this token
|
||||
# ask the user if they want to trust the mint
|
||||
await verify_mint(mint_wallet, mint_url)
|
||||
|
||||
token = await mint_wallet.serialize_proofs(proofs)
|
||||
token_obj = deserialize_token_from_string(token)
|
||||
mint_url = token_obj.mint
|
||||
receive_wallet = await receive(mint_wallet, token_obj)
|
||||
ctx.obj["WALLET"] = receive_wallet
|
||||
except Exception as e:
|
||||
if mint_url and token_obj:
|
||||
unit = Unit[token_obj.unit]
|
||||
print(
|
||||
f"Could not receive {unit.str(token_obj.amount)} from mint {mint_url}: {str(e)}"
|
||||
)
|
||||
else:
|
||||
print(f"Could not receive token: {str(e)}")
|
||||
continue
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from itertools import groupby
|
||||
from typing import Dict, List, Optional
|
||||
from typing import Dict, List, Optional, Tuple
|
||||
|
||||
from loguru import logger
|
||||
|
||||
@@ -91,6 +91,44 @@ class WalletProofs(SupportsDb, SupportsKeysets):
|
||||
)
|
||||
return mint_urls
|
||||
|
||||
async def _get_proofs_keysets(self, proofs: List[Proof]) -> Dict[str, WalletKeyset]:
|
||||
keyset_ids = self._get_proofs_keyset_ids(proofs)
|
||||
keysets_dict = {}
|
||||
async with self.db.get_connection() as conn:
|
||||
for keyset_id in keyset_ids:
|
||||
keyset = await get_keysets(id=keyset_id, db=self.db, conn=conn)
|
||||
if len(keyset) == 1:
|
||||
keysets_dict[keyset_id] = keyset[0]
|
||||
return keysets_dict
|
||||
|
||||
async def _get_proofs_mint_unit(self, proofs: List[Proof]) -> Tuple[str, Unit]:
|
||||
"""Helper function that extracts the mint URL and unit from a list of proofs. It raises an exception if the proofs are from multiple mints or units.
|
||||
|
||||
Args:
|
||||
proofs (List[Proof]): List of proofs to extract the mint URL and unit from.
|
||||
|
||||
Raises:
|
||||
Exception: If the proofs are from multiple mints or units.
|
||||
Exception: If the proofs are from an unknown mint or keyset.
|
||||
|
||||
Returns:
|
||||
Tuple[str, Unit]: Mint URL and `Unit` of the proofs
|
||||
"""
|
||||
proofs_keysets = await self._get_proofs_keysets(proofs)
|
||||
mint_urls = [k.mint_url for k in proofs_keysets.values()]
|
||||
if not mint_urls:
|
||||
raise Exception("Proofs from unknown mint or keyset.")
|
||||
if len(set(mint_urls)) != 1:
|
||||
raise Exception("Proofs from multiple mints.")
|
||||
mint_url = mint_urls[0]
|
||||
if not mint_url:
|
||||
raise Exception("No mint URL found for keyset")
|
||||
proofs_units = [k.unit for k in proofs_keysets.values()]
|
||||
if len(set(proofs_units)) != 1:
|
||||
raise Exception("Proofs from multiple units.")
|
||||
unit = proofs_units[0]
|
||||
return mint_url, unit
|
||||
|
||||
async def serialize_proofs(
|
||||
self,
|
||||
proofs: List[Proof],
|
||||
@@ -136,6 +174,8 @@ class WalletProofs(SupportsDb, SupportsKeysets):
|
||||
# extract all keysets IDs from proofs
|
||||
keyset_ids = self._get_proofs_keyset_ids(proofs)
|
||||
keysets = {k.id: k for k in self.keysets.values() if k.id in keyset_ids}
|
||||
if not keysets:
|
||||
raise ValueError("No keysets found for proofs")
|
||||
assert (
|
||||
len(set([k.unit for k in keysets.values()])) == 1
|
||||
), "All keysets must have the same unit"
|
||||
@@ -170,7 +210,10 @@ class WalletProofs(SupportsDb, SupportsKeysets):
|
||||
|
||||
# get all keysets from proofs
|
||||
keyset_ids = set(self._get_proofs_keyset_ids(proofs))
|
||||
keysets = [self.keysets[i] for i in keyset_ids]
|
||||
try:
|
||||
keysets = [self.keysets[i] for i in keyset_ids]
|
||||
except KeyError:
|
||||
raise ValueError("Keysets of proofs are not loaded in wallet")
|
||||
# we make sure that all proofs are from keysets of the same mint
|
||||
if len(set([k.mint_url for k in keysets])) > 1:
|
||||
raise ValueError("TokenV4 can only contain proofs from a single mint URL")
|
||||
|
||||
Reference in New Issue
Block a user