diff --git a/cashu/core/models.py b/cashu/core/models.py index a974314..1fb6067 100644 --- a/cashu/core/models.py +++ b/cashu/core/models.py @@ -222,19 +222,19 @@ class PostMeltRequest_deprecated(BaseModel): # ------- API: SPLIT ------- -class PostSplitRequest(BaseModel): +class PostSwapRequest(BaseModel): inputs: List[Proof] = Field(..., max_items=settings.mint_max_request_length) outputs: List[BlindedMessage] = Field( ..., max_items=settings.mint_max_request_length ) -class PostSplitResponse(BaseModel): +class PostSwapResponse(BaseModel): signatures: List[BlindedSignature] # deprecated since 0.13.0 -class PostSplitRequest_Deprecated(BaseModel): +class PostSwapRequest_Deprecated(BaseModel): proofs: List[Proof] = Field(..., max_items=settings.mint_max_request_length) amount: Optional[int] = None outputs: List[BlindedMessage_Deprecated] = Field( @@ -242,11 +242,11 @@ class PostSplitRequest_Deprecated(BaseModel): ) -class PostSplitResponse_Deprecated(BaseModel): +class PostSwapResponse_Deprecated(BaseModel): promises: List[BlindedSignature] = [] -class PostSplitResponse_Very_Deprecated(BaseModel): +class PostSwapResponse_Very_Deprecated(BaseModel): fst: List[BlindedSignature] = [] snd: List[BlindedSignature] = [] deprecated: str = "The amount field is deprecated since 0.13.0" diff --git a/cashu/mint/ledger.py b/cashu/mint/ledger.py index 5ff9b3b..5d11c40 100644 --- a/cashu/mint/ledger.py +++ b/cashu/mint/ledger.py @@ -961,18 +961,18 @@ class Ledger(LedgerVerification, LedgerSpendingConditions, LedgerTasks, LedgerFe return PostMeltQuoteResponse.from_melt_quote(melt_quote) - async def split( + async def swap( self, *, proofs: List[Proof], outputs: List[BlindedMessage], keyset: Optional[MintKeyset] = None, ): - """Consumes proofs and prepares new promises based on the amount split. Used for splitting tokens + """Consumes proofs and prepares new promises based on the amount swap. Used for swapping tokens Before sending or for redeeming tokens for new ones that have been received by another wallet. Args: - proofs (List[Proof]): Proofs to be invalidated for the split. + proofs (List[Proof]): Proofs to be invalidated for the swap. outputs (List[BlindedMessage]): New outputs that should be signed in return. keyset (Optional[MintKeyset], optional): Keyset to use. Uses default keyset if not given. Defaults to None. @@ -980,9 +980,9 @@ class Ledger(LedgerVerification, LedgerSpendingConditions, LedgerTasks, LedgerFe Exception: Validation of proofs or outputs failed Returns: - Tuple[List[BlindSignature],List[BlindSignature]]: Promises on both sides of the split. + List[BlindedSignature]: New promises (signatures) for the outputs. """ - logger.trace("split called") + logger.trace("swap called") # verify spending inputs, outputs, and spending conditions await self.verify_inputs_and_outputs(proofs=proofs, outputs=outputs) await self.db_write._set_proofs_pending(proofs) @@ -991,13 +991,13 @@ class Ledger(LedgerVerification, LedgerSpendingConditions, LedgerTasks, LedgerFe await self._invalidate_proofs(proofs=proofs, conn=conn) promises = await self._generate_promises(outputs, keyset, conn) except Exception as e: - logger.trace(f"split failed: {e}") + logger.trace(f"swap failed: {e}") raise e finally: # delete proofs from pending list await self.db_write._unset_proofs_pending(proofs) - logger.trace("split successful") + logger.trace("swap successful") return promises async def restore( diff --git a/cashu/mint/router.py b/cashu/mint/router.py index 9a3c26d..d45ea69 100644 --- a/cashu/mint/router.py +++ b/cashu/mint/router.py @@ -21,8 +21,8 @@ from ..core.models import ( PostMintResponse, PostRestoreRequest, PostRestoreResponse, - PostSplitRequest, - PostSplitResponse, + PostSwapRequest, + PostSwapResponse, ) from ..core.settings import settings from ..mint.startup import ledger @@ -312,7 +312,7 @@ async def melt(request: Request, payload: PostMeltRequest) -> PostMeltQuoteRespo "/v1/swap", name="Swap tokens", summary="Swap inputs for outputs of the same value", - response_model=PostSplitResponse, + response_model=PostSwapResponse, response_description=( "An array of blinded signatures that can be used to create proofs." ), @@ -320,20 +320,20 @@ async def melt(request: Request, payload: PostMeltRequest) -> PostMeltQuoteRespo @limiter.limit(f"{settings.mint_transaction_rate_limit_per_minute}/minute") async def swap( request: Request, - payload: PostSplitRequest, -) -> PostSplitResponse: + payload: PostSwapRequest, +) -> PostSwapResponse: """ - Requests a set of Proofs to be split into two a new set of BlindedSignatures. + Requests a set of Proofs to be swapped for another set of BlindSignatures. - This endpoint is used by Alice to split a set of proofs before making a payment to Carol. - It is then used by Carol (by setting split=total) to redeem the tokens. + This endpoint can be used by Alice to swap a set of proofs before making a payment to Carol. + It can then used by Carol to redeem the tokens for new proofs. """ logger.trace(f"> POST /v1/swap: {payload}") assert payload.outputs, Exception("no outputs provided.") - signatures = await ledger.split(proofs=payload.inputs, outputs=payload.outputs) + signatures = await ledger.swap(proofs=payload.inputs, outputs=payload.outputs) - return PostSplitResponse(signatures=signatures) + return PostSwapResponse(signatures=signatures) @router.post( diff --git a/cashu/mint/router_deprecated.py b/cashu/mint/router_deprecated.py index 74d87c7..95d6ee2 100644 --- a/cashu/mint/router_deprecated.py +++ b/cashu/mint/router_deprecated.py @@ -22,9 +22,9 @@ from ..core.models import ( PostMintResponse_deprecated, PostRestoreRequest_Deprecated, PostRestoreResponse, - PostSplitRequest_Deprecated, - PostSplitResponse_Deprecated, - PostSplitResponse_Very_Deprecated, + PostSwapRequest_Deprecated, + PostSwapResponse_Deprecated, + PostSwapResponse_Very_Deprecated, ) from ..core.settings import settings from .limit import limiter @@ -270,7 +270,7 @@ async def check_fees( name="Split", summary="Split proofs at a specified amount", # response_model=Union[ - # PostSplitResponse_Very_Deprecated, PostSplitResponse_Deprecated + # PostSwapResponse_Very_Deprecated, PostSwapResponse_Deprecated # ], response_description=( "A list of blinded signatures that can be used to create proofs." @@ -280,8 +280,8 @@ async def check_fees( @limiter.limit(f"{settings.mint_transaction_rate_limit_per_minute}/minute") async def split_deprecated( request: Request, - payload: PostSplitRequest_Deprecated, - # ) -> Union[PostSplitResponse_Very_Deprecated, PostSplitResponse_Deprecated]: + payload: PostSwapRequest_Deprecated, + # ) -> Union[PostSwapResponse_Very_Deprecated, PostSwapResponse_Deprecated]: ): """ Requests a set of Proofs to be split into two a new set of BlindedSignatures. @@ -297,7 +297,7 @@ async def split_deprecated( for o in payload.outputs ] # END BACKWARDS COMPATIBILITY < 0.14 - promises = await ledger.split(proofs=payload.proofs, outputs=outputs) + promises = await ledger.swap(proofs=payload.proofs, outputs=outputs) if payload.amount: # BEGIN backwards compatibility < 0.13 @@ -319,10 +319,10 @@ async def split_deprecated( f" {sum([p.amount for p in frst_promises])} sat and send:" f" {len(scnd_promises)}: {sum([p.amount for p in scnd_promises])} sat" ) - return PostSplitResponse_Very_Deprecated(fst=frst_promises, snd=scnd_promises) + return PostSwapResponse_Very_Deprecated(fst=frst_promises, snd=scnd_promises) # END backwards compatibility < 0.13 else: - return PostSplitResponse_Deprecated(promises=promises) + return PostSwapResponse_Deprecated(promises=promises) @router_deprecated.post( diff --git a/cashu/wallet/api/router.py b/cashu/wallet/api/router.py index 0065a50..05ec5a6 100644 --- a/cashu/wallet/api/router.py +++ b/cashu/wallet/api/router.py @@ -194,7 +194,7 @@ async def swap( if outgoing_wallet.available_balance < total_amount: raise Exception("balance too low") - _, send_proofs = await outgoing_wallet.split_to_send( + _, send_proofs = await outgoing_wallet.swap_to_send( outgoing_wallet.proofs, total_amount, set_reserved=True ) await outgoing_wallet.melt( @@ -433,7 +433,7 @@ async def restore( if to < 0: raise Exception("Counter must be positive") await wallet.load_mint() - await wallet.restore_promises_from_to(0, to) + await wallet.restore_promises_from_to(wallet.keyset_id, 0, to) await wallet.invalidate(wallet.proofs, check_spendable=True) return RestoreResponse(balance=wallet.available_balance) diff --git a/cashu/wallet/cli/cli.py b/cashu/wallet/cli/cli.py index 2a39a41..c9b81cc 100644 --- a/cashu/wallet/cli/cli.py +++ b/cashu/wallet/cli/cli.py @@ -548,6 +548,14 @@ async def balance(ctx: Context, verbose): help="Include fees for receiving token.", type=bool, ) +@click.option( + "--force-swap", + "-s", + default=False, + is_flag=True, + help="Force swap token.", + type=bool, +) @click.pass_context @coro async def send_command( @@ -562,6 +570,7 @@ async def send_command( yes: bool, offline: bool, include_fees: bool, + force_swap: bool, ): wallet: Wallet = ctx.obj["WALLET"] amount = int(amount * 100) if wallet.unit in [Unit.usd, Unit.eur] else int(amount) @@ -575,6 +584,7 @@ async def send_command( include_dleq=dleq, include_fees=include_fees, memo=memo, + force_swap=force_swap, ) else: await send_nostr(wallet, amount=amount, pubkey=nostr, verbose=verbose, yes=yes) diff --git a/cashu/wallet/helpers.py b/cashu/wallet/helpers.py index 834be83..627423d 100644 --- a/cashu/wallet/helpers.py +++ b/cashu/wallet/helpers.py @@ -116,6 +116,7 @@ async def send( include_dleq: bool = False, include_fees: bool = False, memo: Optional[str] = None, + force_swap: bool = False, ): """ Prints token to send to stdout. @@ -144,13 +145,12 @@ async def send( await wallet.load_proofs() await wallet.load_mint() - if secret_lock: - _, send_proofs = await wallet.split_to_send( + 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, - include_fees=include_fees, ) else: send_proofs, fees = await wallet.select_to_send( diff --git a/cashu/wallet/lightning/lightning.py b/cashu/wallet/lightning/lightning.py index be6ef10..56ab06a 100644 --- a/cashu/wallet/lightning/lightning.py +++ b/cashu/wallet/lightning/lightning.py @@ -61,7 +61,7 @@ class LightningWallet(Wallet): if self.available_balance < total_amount: print("Error: Balance too low.") return PaymentResponse(ok=False) - _, send_proofs = await self.split_to_send(self.proofs, total_amount) + _, send_proofs = await self.swap_to_send(self.proofs, total_amount) try: resp = await self.melt(send_proofs, pr, quote.fee_reserve, quote.quote) if resp.change: diff --git a/cashu/wallet/nostr.py b/cashu/wallet/nostr.py index 217ed95..bb989d0 100644 --- a/cashu/wallet/nostr.py +++ b/cashu/wallet/nostr.py @@ -62,7 +62,7 @@ async def send_nostr( pubkey = await nip5_to_pubkey(wallet, pubkey) await wallet.load_mint() await wallet.load_proofs() - _, send_proofs = await wallet.split_to_send( + _, send_proofs = await wallet.swap_to_send( wallet.proofs, amount, set_reserved=True, include_fees=False ) token = await wallet.serialize_proofs(send_proofs, include_dleq=include_dleq) diff --git a/cashu/wallet/proofs.py b/cashu/wallet/proofs.py index ae8b839..c0169e1 100644 --- a/cashu/wallet/proofs.py +++ b/cashu/wallet/proofs.py @@ -148,6 +148,7 @@ class WalletProofs(SupportsDb, SupportsKeysets): try: _ = [bytes.fromhex(p.id) for p in proofs] except ValueError: + logger.debug("Proof with base64 keyset, using legacy token serialization") legacy = True if legacy: diff --git a/cashu/wallet/secrets.py b/cashu/wallet/secrets.py index 3e6e572..ef35f02 100644 --- a/cashu/wallet/secrets.py +++ b/cashu/wallet/secrets.py @@ -105,27 +105,28 @@ class WalletSecrets(SupportsDb, SupportsKeysets): return hashlib.sha256(os.urandom(32)).hexdigest() async def generate_determinstic_secret( - self, counter: int + self, counter: int, keyset_id: Optional[str] = None ) -> Tuple[bytes, bytes, str]: """ Determinstically generates two secrets (one as the secret message, one as the blinding factor). """ assert self.bip32, "BIP32 not initialized yet." + keyset_id = keyset_id or self.keyset_id # integer keyset id modulo max number of bip32 child keys try: - keyest_id_int = int.from_bytes(bytes.fromhex(self.keyset_id), "big") % ( + keyest_id_int = int.from_bytes(bytes.fromhex(keyset_id), "big") % ( 2**31 - 1 ) except ValueError: # BEGIN: BACKWARDS COMPATIBILITY < 0.15.0 keyset id is not hex # calculate an integer keyset id from the base64 encoded keyset id - keyest_id_int = int.from_bytes(base64.b64decode(self.keyset_id), "big") % ( + keyest_id_int = int.from_bytes(base64.b64decode(keyset_id), "big") % ( 2**31 - 1 ) # END: BACKWARDS COMPATIBILITY < 0.15.0 keyset id is not hex - logger.trace(f"keyset id: {self.keyset_id} becomes {keyest_id_int}") + logger.trace(f"keyset id: {keyset_id} becomes {keyest_id_int}") token_derivation_path = f"m/129372'/0'/{keyest_id_int}'/{counter}'" # for secret secret_derivation_path = f"{token_derivation_path}/0" @@ -177,13 +178,14 @@ class WalletSecrets(SupportsDb, SupportsKeysets): return secrets, rs, derivation_paths async def generate_secrets_from_to( - self, from_counter: int, to_counter: int + self, from_counter: int, to_counter: int, keyset_id: Optional[str] = None ) -> Tuple[List[str], List[PrivateKey], List[str]]: """Generates secrets and blinding factors from `from_counter` to `to_counter` Args: from_counter (int): Start counter to_counter (int): End counter + keyset_id (Optional[str], optional): Keyset id. Defaults to None. Returns: Tuple[List[str], List[PrivateKey], List[str]]: Secrets, blinding factors, derivation paths @@ -196,7 +198,8 @@ class WalletSecrets(SupportsDb, SupportsKeysets): ), "from_counter must be smaller than to_counter" secret_counters = [c for c in range(from_counter, to_counter + 1)] secrets_rs_derivationpaths = [ - await self.generate_determinstic_secret(s) for s in secret_counters + await self.generate_determinstic_secret(s, keyset_id) + for s in secret_counters ] # secrets are supplied as str secrets = [s[0].hex() for s in secrets_rs_derivationpaths] diff --git a/cashu/wallet/transactions.py b/cashu/wallet/transactions.py index 94c47c5..6af54dc 100644 --- a/cashu/wallet/transactions.py +++ b/cashu/wallet/transactions.py @@ -36,7 +36,7 @@ class WalletTransactions(SupportsDb, SupportsKeysets): def get_fees_for_proofs_ppk(self, proofs: List[Proof]) -> int: return sum([self.keysets[p.id].input_fee_ppk for p in proofs]) - async def _select_proofs_to_send_( + async def _select_proofs_to_send_legacy( self, proofs: List[Proof], amount_to_send: int, tolerance: int = 0 ) -> List[Proof]: send_proofs: List[Proof] = [] @@ -63,9 +63,6 @@ class WalletTransactions(SupportsDb, SupportsKeysets): # compose the target amount from the remaining_proofs logger.debug(f"sorted_proofs: {[p.amount for p in sorted_proofs]}") for p in sorted_proofs: - # logger.debug(f"send_proofs: {[p.amount for p in send_proofs]}") - # logger.debug(f"target_amount: {target_amount}") - # logger.debug(f"p.amount: {p.amount}") if sum_proofs(send_proofs) + p.amount <= target_amount + tolerance: send_proofs.append(p) target_amount = amount_to_send + self.get_fees_for_proofs(send_proofs) @@ -83,8 +80,19 @@ class WalletTransactions(SupportsDb, SupportsKeysets): proofs: List[Proof], amount_to_send: Union[int, float], *, - include_fees: bool = True, + include_fees: bool = False, ) -> List[Proof]: + """Select proofs to send based on the amount to send and the proofs available. Implements a simple coin selection algorithm. + Can be used for selecting proofs to send an offline transaction. + + Args: + proofs (List[Proof]): List of proofs to select from + amount_to_send (Union[int, float]): Amount to select proofs for + include_fees (bool, optional): Whether to include fees necessary to redeem the tokens in the selection. Defaults to False. + + Returns: + List[Proof]: _description_ + """ # check that enough spendable proofs exist if sum_proofs(proofs) < amount_to_send: return [] @@ -136,7 +144,7 @@ class WalletTransactions(SupportsDb, SupportsKeysets): ) return selected_proofs - async def _select_proofs_to_split( + async def _select_proofs_to_swap( self, proofs: List[Proof], amount_to_send: int ) -> Tuple[List[Proof], int]: """ @@ -147,9 +155,8 @@ class WalletTransactions(SupportsDb, SupportsKeysets): Rules: 1) Proofs that are not marked as reserved - 2) Proofs that have a different keyset than the activated keyset_id of the mint - 3) Include all proofs that have an older keyset than the current keyset of the mint (to get rid of old epochs). - 4) If the target amount is not reached, add proofs of the current keyset until it is. + 2) Include all proofs from inactive keysets (old epochs) to get rid of them + 3) If the target amount is not reached, add proofs of the current keyset until it is. Args: proofs (List[Proof]): List of proofs to select from @@ -163,7 +170,7 @@ class WalletTransactions(SupportsDb, SupportsKeysets): Exception: If the balance is too low to send the amount """ logger.debug( - f"_select_proofs_to_split - amounts we have: {amount_summary(proofs, self.unit)}" + f"_select_proofs_to_swap - amounts we have: {amount_summary(proofs, self.unit)}" ) send_proofs: List[Proof] = [] @@ -171,11 +178,9 @@ class WalletTransactions(SupportsDb, SupportsKeysets): if sum_proofs(proofs) < amount_to_send: raise Exception("balance too low.") - # add all proofs that have an older keyset than the current keyset of the mint - proofs_old_epochs = [ - p for p in proofs if p.id != self.keysets[self.keyset_id].id - ] - send_proofs += proofs_old_epochs + # add all proofs from inactive keysets + proofs_inactive_keysets = [p for p in proofs if not self.keysets[p.id].active] + send_proofs += proofs_inactive_keysets # coinselect based on amount only from the current keyset # start with the proofs with the largest amount and add them until the target amount is reached @@ -193,7 +198,7 @@ class WalletTransactions(SupportsDb, SupportsKeysets): send_proofs.append(proof_to_add) logger.trace( - f"_select_proofs_to_split – selected proof amounts: {[p.amount for p in send_proofs]}" + f"_select_proofs_to_swap – selected proof amounts: {[p.amount for p in send_proofs]}" ) fees = self.get_fees_for_proofs(send_proofs) return send_proofs, fees diff --git a/cashu/wallet/v1_api.py b/cashu/wallet/v1_api.py index 5acb7a9..d5d9ce5 100644 --- a/cashu/wallet/v1_api.py +++ b/cashu/wallet/v1_api.py @@ -39,8 +39,8 @@ from ..core.models import ( PostMintRequest, PostMintResponse, PostRestoreResponse, - PostSplitRequest, - PostSplitResponse, + PostSwapRequest, + PostSwapResponse, ) from ..core.settings import settings from ..tor.tor import TorProxy @@ -465,7 +465,7 @@ class LedgerAPI(LedgerAPIDeprecated, object): ) -> List[BlindedSignature]: """Consume proofs and create new promises based on amount split.""" logger.debug("Calling split. POST /v1/swap") - split_payload = PostSplitRequest(inputs=proofs, outputs=outputs) + split_payload = PostSwapRequest(inputs=proofs, outputs=outputs) # construct payload def _splitrequest_include_fields(proofs: List[Proof]): @@ -494,7 +494,7 @@ class LedgerAPI(LedgerAPIDeprecated, object): # END backwards compatibility < 0.15.0 self.raise_on_error_request(resp) promises_dict = resp.json() - mint_response = PostSplitResponse.parse_obj(promises_dict) + mint_response = PostSwapResponse.parse_obj(promises_dict) promises = [BlindedSignature(**p.dict()) for p in mint_response.signatures] if len(promises) == 0: diff --git a/cashu/wallet/wallet.py b/cashu/wallet/wallet.py index ad0c2a9..e97dc2b 100644 --- a/cashu/wallet/wallet.py +++ b/cashu/wallet/wallet.py @@ -85,7 +85,7 @@ class Wallet( """ keyset_id: str # holds current keyset id - keysets: Dict[str, WalletKeyset] # holds keysets + keysets: Dict[str, WalletKeyset] = {} # holds keysets # mint_keyset_ids: List[str] # holds active keyset ids of the mint unit: Unit mint_info: MintInfo # holds info about mint @@ -178,7 +178,7 @@ class Wallet( logger.debug(f"Mint info: {self.mint_info}") return self.mint_info - async def load_mint_keysets(self): + async def load_mint_keysets(self, force_old_keysets=False): """Loads all keyset of the mint and makes sure we have them all in the database. Then loads all keysets from the database for the active mint and active unit into self.keysets. @@ -237,7 +237,7 @@ class Wallet( ) # BEGIN backwards compatibility: phase out keysets with base64 ID by treating them as inactive - if settings.wallet_inactivate_legacy_keysets: + if settings.wallet_inactivate_legacy_keysets and not force_old_keysets: keysets_in_db = await get_keysets(mint_url=self.url, db=self.db) for keyset in keysets_in_db: if not keyset.active: @@ -306,14 +306,19 @@ class Wallet( logger.debug(f"Activated keyset {self.keyset_id}") - async def load_mint(self, keyset_id: str = "") -> None: + async def load_mint(self, keyset_id: str = "", force_old_keysets=False) -> None: """ Loads the public keys of the mint. Either gets the keys for the specified `keyset_id` or gets the keys of the active keyset from the mint. Gets the active keyset ids of the mint and stores in `self.mint_keyset_ids`. + + Args: + keyset_id (str, optional): Keyset id to load. Defaults to "". + force_old_keysets (bool, optional): If true, old deprecated base64 keysets are not ignored. This is necessary for restoring tokens from old base64 keysets. + Defaults to False. """ logger.trace("Loading mint.") - await self.load_mint_keysets() + await self.load_mint_keysets(force_old_keysets) await self.activate_keyset(keyset_id) try: await self.load_mint_info() @@ -902,6 +907,7 @@ class Wallet( amounts: List[int], secrets: List[str], rs: List[PrivateKey] = [], + keyset_id: Optional[str] = None, ) -> Tuple[List[BlindedMessage], List[PrivateKey]]: """Takes a list of amounts and secrets and returns outputs. Outputs are blinded messages `outputs` and blinding factors `rs` @@ -921,8 +927,8 @@ class Wallet( assert len(amounts) == len( secrets ), f"len(amounts)={len(amounts)} not equal to len(secrets)={len(secrets)}" + keyset_id = keyset_id or self.keyset_id outputs: List[BlindedMessage] = [] - rs_ = [None] * len(amounts) if not rs else rs rs_return: List[PrivateKey] = [] for secret, amount, r in zip(secrets, amounts, rs_): @@ -935,7 +941,7 @@ class Wallet( rs_return.append(r) output = BlindedMessage( - amount=amount, B_=B_.serialize().hex(), id=self.keyset_id + amount=amount, B_=B_.serialize().hex(), id=keyset_id ) outputs.append(output) logger.trace(f"Constructing output: {output}, r: {r.serialize()}") @@ -1049,7 +1055,7 @@ class Wallet( if not offline: logger.debug("Offline coin selection unsuccessful. Splitting proofs.") # we set the proofs as reserved later - _, send_proofs = await self.split_to_send( + _, send_proofs = await self.swap_to_send( proofs, amount, set_reserved=False ) else: @@ -1061,7 +1067,7 @@ class Wallet( await self.set_reserved(send_proofs, reserved=True) return send_proofs, fees - async def split_to_send( + async def swap_to_send( self, proofs: List[Proof], amount: int, @@ -1093,7 +1099,7 @@ class Wallet( raise Exception("balance too low.") # coin selection for swapping - # spendable_proofs, fees = await self._select_proofs_to_split(proofs, amount) + # spendable_proofs, fees = await self._select_proofs_to_swap(proofs, amount) swap_proofs = await self._select_proofs_to_send( proofs, amount, include_fees=True ) @@ -1195,31 +1201,41 @@ class Wallet( to (int, optional): The number of consecutive empty responses to stop restoring. Defaults to 2. batch (int, optional): The number of proofs to restore in one batch. Defaults to 25. """ - stop_counter = 0 + empty_batches = 0 # we get the current secret counter and restore from there on spendable_proofs = [] counter_before = await bump_secret_derivation( db=self.db, keyset_id=keyset_id, by=0 ) if counter_before != 0: - print("Keyset has already been used. Restoring from it's last state.") + print("Keyset has already been used. Restoring from its last state.") i = counter_before - n_last_restored_proofs = 0 - while stop_counter < to: - print(f"Restoring token {i} to {i + batch}...") - restored_proofs = await self.restore_promises_from_to(i, i + batch - 1) + last_restore_count = 0 + while empty_batches < to: + print(f"Restoring counter {i} to {i + batch} for keyset {keyset_id} ...") + ( + next_restored_output_index, + restored_proofs, + ) = await self.restore_promises_from_to(keyset_id, i, i + batch - 1) + last_restore_count += next_restored_output_index + i += batch if len(restored_proofs) == 0: - stop_counter += 1 + empty_batches += 1 + continue spendable_proofs = await self.invalidate( restored_proofs, check_spendable=True ) if len(spendable_proofs): - n_last_restored_proofs = len(spendable_proofs) - print(f"Restored {sum_proofs(restored_proofs)} sat") - i += batch + print( + f"Restored {sum_proofs(spendable_proofs)} sat for keyset {keyset_id}." + ) + else: + logger.debug( + f"None of the {len(restored_proofs)} restored proofs are spendable." + ) # restore the secret counter to its previous value for the last round - revert_counter_by = batch * to + n_last_restored_proofs + revert_counter_by = i - last_restore_count logger.debug(f"Reverting secret counter by {revert_counter_by}") before = await bump_secret_derivation( db=self.db, @@ -1229,8 +1245,8 @@ class Wallet( logger.debug( f"Secret counter reverted from {before} to {before - revert_counter_by}" ) - if n_last_restored_proofs == 0: - print("No tokens restored for keyset.") + if last_restore_count == 0: + print(f"No tokens restored for keyset {keyset_id}.") return async def restore_wallet_from_mnemonic( @@ -1245,14 +1261,14 @@ class Wallet( batch (int, optional): The number of proofs to restore in one batch. Defaults to 25. """ await self._init_private_key(mnemonic) - await self.load_mint() + await self.load_mint(force_old_keysets=False) print("Restoring tokens...") for keyset_id in self.keysets.keys(): await self.restore_tokens_for_keyset(keyset_id, to, batch) async def restore_promises_from_to( - self, from_counter: int, to_counter: int - ) -> List[Proof]: + self, keyset_id: str, from_counter: int, to_counter: int + ) -> Tuple[int, List[Proof]]: """Restores promises from a given range of counters. This is for restoring a wallet from a mnemonic. Args: @@ -1260,18 +1276,20 @@ class Wallet( to_counter (int): Counter for the secret derivation to end at Returns: - List[Proof]: List of restored proofs + Tuple[int, List[Proof]]: Index of the last restored output and list of restored proofs """ # we regenerate the secrets and rs for the given range secrets, rs, derivation_paths = await self.generate_secrets_from_to( - from_counter, to_counter + from_counter, to_counter, keyset_id=keyset_id ) # we don't know the amount but luckily the mint will tell us so we use a dummy amount here amounts_dummy = [1] * len(secrets) # we generate outputs from deterministic secrets and rs - regenerated_outputs, _ = self._construct_outputs(amounts_dummy, secrets, rs) + regenerated_outputs, _ = self._construct_outputs( + amounts_dummy, secrets, rs, keyset_id=keyset_id + ) # we ask the mint to reissue the promises - proofs = await self.restore_promises( + next_restored_output_index, proofs = await self.restore_promises( outputs=regenerated_outputs, secrets=secrets, rs=rs, @@ -1279,9 +1297,9 @@ class Wallet( ) await set_secret_derivation( - db=self.db, keyset_id=self.keyset_id, counter=to_counter + 1 + db=self.db, keyset_id=keyset_id, counter=to_counter + 1 ) - return proofs + return next_restored_output_index, proofs async def restore_promises( self, @@ -1289,7 +1307,7 @@ class Wallet( secrets: List[str], rs: List[PrivateKey], derivation_paths: List[str], - ) -> List[Proof]: + ) -> Tuple[int, List[Proof]]: """Restores proofs from a list of outputs, secrets, rs and derivation paths. Args: @@ -1299,10 +1317,26 @@ class Wallet( derivation_paths (List[str]): Derivation paths used for the secrets necessary to unblind the promises Returns: - List[Proof]: List of restored proofs + Tuple[int, List[Proof]]: Index of the last restored output and list of restored proofs """ # restored_outputs is there so we can match the promises to the secrets and rs restored_outputs, restored_promises = await super().restore_promises(outputs) + # determine the index in `outputs` of the last restored output from restored_outputs[-1].B_ + if not restored_outputs: + next_restored_output_index = 0 + else: + next_restored_output_index = ( + next( + ( + idx + for idx, val in enumerate(outputs) + if val.B_ == restored_outputs[-1].B_ + ), + 0, + ) + + 1 + ) + logger.trace(f"Last restored output index: {next_restored_output_index}") # now we need to filter out the secrets and rs that had a match matching_indices = [ idx @@ -1311,9 +1345,12 @@ class Wallet( ] secrets = [secrets[i] for i in matching_indices] rs = [rs[i] for i in matching_indices] + logger.debug( + f"Restored {len(restored_promises)} promises. Constructing proofs." + ) # now we can construct the proofs with the secrets and rs proofs = await self._construct_proofs( restored_promises, secrets, rs, derivation_paths ) logger.debug(f"Restored {len(restored_promises)} promises") - return proofs + return next_restored_output_index, proofs diff --git a/cashu/wallet/wallet_deprecated.py b/cashu/wallet/wallet_deprecated.py index e5b953a..4efccde 100644 --- a/cashu/wallet/wallet_deprecated.py +++ b/cashu/wallet/wallet_deprecated.py @@ -31,8 +31,8 @@ from ..core.models import ( PostMintRequest_deprecated, PostMintResponse_deprecated, PostRestoreResponse, - PostSplitRequest_Deprecated, - PostSplitResponse_Deprecated, + PostSwapRequest_Deprecated, + PostSwapResponse_Deprecated, ) from ..core.settings import settings from ..tor.tor import TorProxy @@ -348,7 +348,7 @@ class LedgerAPIDeprecated(SupportsHttpxClient, SupportsMintURL): """Consume proofs and create new promises based on amount split.""" logger.warning("Using deprecated API call: Calling split. POST /split") outputs_deprecated = [BlindedMessage_Deprecated(**o.dict()) for o in outputs] - split_payload = PostSplitRequest_Deprecated( + split_payload = PostSwapRequest_Deprecated( proofs=proofs, outputs=outputs_deprecated ) @@ -373,7 +373,7 @@ class LedgerAPIDeprecated(SupportsHttpxClient, SupportsMintURL): ) self.raise_on_error(resp) promises_dict = resp.json() - mint_response = PostSplitResponse_Deprecated.parse_obj(promises_dict) + mint_response = PostSwapResponse_Deprecated.parse_obj(promises_dict) promises = [BlindedSignature(**p.dict()) for p in mint_response.promises] if len(promises) == 0: diff --git a/tests/test_mint_api.py b/tests/test_mint_api.py index e1f69e2..494c27c 100644 --- a/tests/test_mint_api.py +++ b/tests/test_mint_api.py @@ -422,7 +422,7 @@ async def test_melt_external(ledger: Ledger, wallet: Wallet): assert quote.amount == 62 assert quote.fee_reserve == 2 - keep, send = await wallet.split_to_send(wallet.proofs, 64) + keep, send = await wallet.swap_to_send(wallet.proofs, 64) inputs_payload = [p.to_dict() for p in send] # outputs for change diff --git a/tests/test_mint_fees.py b/tests/test_mint_fees.py index f861a12..3c98841 100644 --- a/tests/test_mint_fees.py +++ b/tests/test_mint_fees.py @@ -123,7 +123,7 @@ async def test_split_with_fees(wallet1: Wallet, ledger: Ledger): assert fees == 1 outputs = await wallet1.construct_outputs(amount_split(9)) - promises = await ledger.split(proofs=send_proofs, outputs=outputs) + promises = await ledger.swap(proofs=send_proofs, outputs=outputs) assert len(promises) == len(outputs) assert [p.amount for p in promises] == [p.amount for p in outputs] @@ -141,7 +141,7 @@ async def test_split_with_high_fees(wallet1: Wallet, ledger: Ledger): assert fees == 3 outputs = await wallet1.construct_outputs(amount_split(7)) - promises = await ledger.split(proofs=send_proofs, outputs=outputs) + promises = await ledger.swap(proofs=send_proofs, outputs=outputs) assert len(promises) == len(outputs) assert [p.amount for p in promises] == [p.amount for p in outputs] @@ -161,7 +161,7 @@ async def test_split_not_enough_fees(wallet1: Wallet, ledger: Ledger): outputs = await wallet1.construct_outputs(amount_split(10)) await assert_err( - ledger.split(proofs=send_proofs, outputs=outputs), "are not balanced" + ledger.swap(proofs=send_proofs, outputs=outputs), "are not balanced" ) diff --git a/tests/test_mint_init.py b/tests/test_mint_init.py index a33a6cf..9c9ae7f 100644 --- a/tests/test_mint_init.py +++ b/tests/test_mint_init.py @@ -252,7 +252,7 @@ async def test_startup_regtest_pending_quote_pending(wallet: Wallet, ledger: Led # wallet pays the invoice quote = await wallet.melt_quote(invoice_payment_request) total_amount = quote.amount + quote.fee_reserve - _, send_proofs = await wallet.split_to_send(wallet.proofs, total_amount) + _, send_proofs = await wallet.swap_to_send(wallet.proofs, total_amount) asyncio.create_task( wallet.melt( proofs=send_proofs, @@ -297,7 +297,7 @@ async def test_startup_regtest_pending_quote_success(wallet: Wallet, ledger: Led # wallet pays the invoice quote = await wallet.melt_quote(invoice_payment_request) total_amount = quote.amount + quote.fee_reserve - _, send_proofs = await wallet.split_to_send(wallet.proofs, total_amount) + _, send_proofs = await wallet.swap_to_send(wallet.proofs, total_amount) asyncio.create_task( wallet.melt( proofs=send_proofs, @@ -347,7 +347,7 @@ async def test_startup_regtest_pending_quote_failure(wallet: Wallet, ledger: Led # wallet pays the invoice quote = await wallet.melt_quote(invoice_payment_request) total_amount = quote.amount + quote.fee_reserve - _, send_proofs = await wallet.split_to_send(wallet.proofs, total_amount) + _, send_proofs = await wallet.swap_to_send(wallet.proofs, total_amount) asyncio.create_task( wallet.melt( proofs=send_proofs, diff --git a/tests/test_mint_operations.py b/tests/test_mint_operations.py index 518ae24..acfd913 100644 --- a/tests/test_mint_operations.py +++ b/tests/test_mint_operations.py @@ -60,7 +60,7 @@ async def test_melt_internal(wallet1: Wallet, ledger: Ledger): assert not melt_quote_pre_payment.paid, "melt quote should not be paid" assert melt_quote_pre_payment.state == MeltQuoteState.unpaid - keep_proofs, send_proofs = await wallet1.split_to_send(wallet1.proofs, 64) + keep_proofs, send_proofs = await wallet1.swap_to_send(wallet1.proofs, 64) await ledger.melt(proofs=send_proofs, quote=melt_quote.quote) melt_quote_post_payment = await ledger.get_melt_quote(melt_quote.quote) @@ -85,7 +85,7 @@ async def test_melt_external(wallet1: Wallet, ledger: Ledger): assert mint_quote.state == MeltQuoteState.unpaid.value total_amount = mint_quote.amount + mint_quote.fee_reserve - keep_proofs, send_proofs = await wallet1.split_to_send(wallet1.proofs, total_amount) + keep_proofs, send_proofs = await wallet1.swap_to_send(wallet1.proofs, total_amount) melt_quote = await ledger.melt_quote( PostMeltQuoteRequest(request=invoice_payment_request, unit="sat") ) @@ -169,13 +169,13 @@ async def test_split(wallet1: Wallet, ledger: Ledger): await pay_if_regtest(invoice.bolt11) await wallet1.mint(64, id=invoice.id) - keep_proofs, send_proofs = await wallet1.split_to_send(wallet1.proofs, 10) + keep_proofs, send_proofs = await wallet1.swap_to_send(wallet1.proofs, 10) secrets, rs, derivation_paths = await wallet1.generate_n_secrets(len(send_proofs)) outputs, rs = wallet1._construct_outputs( [p.amount for p in send_proofs], secrets, rs ) - promises = await ledger.split(proofs=send_proofs, outputs=outputs) + promises = await ledger.swap(proofs=send_proofs, outputs=outputs) assert len(promises) == len(outputs) assert [p.amount for p in promises] == [p.amount for p in outputs] @@ -185,9 +185,9 @@ async def test_split_with_no_outputs(wallet1: Wallet, ledger: Ledger): invoice = await wallet1.request_mint(64) await pay_if_regtest(invoice.bolt11) await wallet1.mint(64, id=invoice.id) - _, send_proofs = await wallet1.split_to_send(wallet1.proofs, 10, set_reserved=False) + _, send_proofs = await wallet1.swap_to_send(wallet1.proofs, 10, set_reserved=False) await assert_err( - ledger.split(proofs=send_proofs, outputs=[]), + ledger.swap(proofs=send_proofs, outputs=[]), "no outputs provided", ) @@ -198,7 +198,7 @@ async def test_split_with_input_less_than_outputs(wallet1: Wallet, ledger: Ledge await pay_if_regtest(invoice.bolt11) await wallet1.mint(64, id=invoice.id) - keep_proofs, send_proofs = await wallet1.split_to_send( + keep_proofs, send_proofs = await wallet1.swap_to_send( wallet1.proofs, 10, set_reserved=False ) @@ -213,7 +213,7 @@ async def test_split_with_input_less_than_outputs(wallet1: Wallet, ledger: Ledge ) await assert_err( - ledger.split(proofs=send_proofs, outputs=outputs), + ledger.swap(proofs=send_proofs, outputs=outputs), "are not balanced", ) @@ -237,7 +237,7 @@ async def test_split_with_input_more_than_outputs(wallet1: Wallet, ledger: Ledge outputs, rs = wallet1._construct_outputs(output_amounts, secrets, rs) await assert_err( - ledger.split(proofs=inputs, outputs=outputs), + ledger.swap(proofs=inputs, outputs=outputs), "are not balanced", ) @@ -262,11 +262,11 @@ async def test_split_twice_with_same_outputs(wallet1: Wallet, ledger: Ledger): ) outputs, rs = wallet1._construct_outputs(output_amounts, secrets, rs) - await ledger.split(proofs=inputs1, outputs=outputs) + await ledger.swap(proofs=inputs1, outputs=outputs) # try to spend other proofs with the same outputs again await assert_err( - ledger.split(proofs=inputs2, outputs=outputs), + ledger.swap(proofs=inputs2, outputs=outputs), "outputs have already been signed before.", ) @@ -277,7 +277,7 @@ async def test_split_twice_with_same_outputs(wallet1: Wallet, ledger: Ledger): ) outputs, rs = wallet1._construct_outputs(output_amounts, secrets, rs) - await ledger.split(proofs=inputs2, outputs=outputs) + await ledger.swap(proofs=inputs2, outputs=outputs) @pytest.mark.asyncio @@ -396,7 +396,7 @@ async def test_check_proof_state(wallet1: Wallet, ledger: Ledger): await pay_if_regtest(invoice.bolt11) await wallet1.mint(64, id=invoice.id) - keep_proofs, send_proofs = await wallet1.split_to_send(wallet1.proofs, 10) + keep_proofs, send_proofs = await wallet1.swap_to_send(wallet1.proofs, 10) proof_states = await ledger.db_read.get_proofs_states(Ys=[p.Y for p in send_proofs]) assert all([p.state.value == "UNSPENT" for p in proof_states]) diff --git a/tests/test_mint_regtest.py b/tests/test_mint_regtest.py index 8a934f0..726dfcd 100644 --- a/tests/test_mint_regtest.py +++ b/tests/test_mint_regtest.py @@ -43,7 +43,7 @@ async def test_regtest_pending_quote(wallet: Wallet, ledger: Ledger): # wallet pays the invoice quote = await wallet.melt_quote(invoice_payment_request) total_amount = quote.amount + quote.fee_reserve - _, send_proofs = await wallet.split_to_send(wallet.proofs, total_amount) + _, send_proofs = await wallet.swap_to_send(wallet.proofs, total_amount) asyncio.create_task(ledger.melt(proofs=send_proofs, quote=quote.quote)) # asyncio.create_task( # wallet.melt( diff --git a/tests/test_wallet.py b/tests/test_wallet.py index 7896090..8bb1a95 100644 --- a/tests/test_wallet.py +++ b/tests/test_wallet.py @@ -241,14 +241,14 @@ async def test_split(wallet1: Wallet): @pytest.mark.asyncio -async def test_split_to_send(wallet1: Wallet): +async def test_swap_to_send(wallet1: Wallet): invoice = await wallet1.request_mint(64) await pay_if_regtest(invoice.bolt11) await wallet1.mint(64, id=invoice.id) assert wallet1.balance == 64 # this will select 32 sats and them (nothing to keep) - keep_proofs, send_proofs = await wallet1.split_to_send( + keep_proofs, send_proofs = await wallet1.swap_to_send( wallet1.proofs, 32, set_reserved=True ) assert_amt(send_proofs, 32) @@ -307,7 +307,7 @@ async def test_melt(wallet1: Wallet): assert total_amount == 64 assert quote.fee_reserve == 0 - _, send_proofs = await wallet1.split_to_send(wallet1.proofs, total_amount) + _, send_proofs = await wallet1.swap_to_send(wallet1.proofs, total_amount) melt_response = await wallet1.melt( proofs=send_proofs, @@ -343,12 +343,12 @@ async def test_melt(wallet1: Wallet): @pytest.mark.asyncio -async def test_split_to_send_more_than_balance(wallet1: Wallet): +async def test_swap_to_send_more_than_balance(wallet1: Wallet): invoice = await wallet1.request_mint(64) await pay_if_regtest(invoice.bolt11) await wallet1.mint(64, id=invoice.id) await assert_err( - wallet1.split_to_send(wallet1.proofs, 128, set_reserved=True), + wallet1.swap_to_send(wallet1.proofs, 128, set_reserved=True), "balance too low.", ) assert wallet1.balance == 64 @@ -405,7 +405,7 @@ async def test_send_and_redeem(wallet1: Wallet, wallet2: Wallet): invoice = await wallet1.request_mint(64) await pay_if_regtest(invoice.bolt11) await wallet1.mint(64, id=invoice.id) - _, spendable_proofs = await wallet1.split_to_send( + _, spendable_proofs = await wallet1.swap_to_send( wallet1.proofs, 32, set_reserved=True ) await wallet2.redeem(spendable_proofs) diff --git a/tests/test_wallet_htlc.py b/tests/test_wallet_htlc.py index bda5726..0a36ecd 100644 --- a/tests/test_wallet_htlc.py +++ b/tests/test_wallet_htlc.py @@ -74,7 +74,7 @@ async def test_htlc_split(wallet1: Wallet, wallet2: Wallet): preimage = "00000000000000000000000000000000" preimage_hash = hashlib.sha256(bytes.fromhex(preimage)).hexdigest() secret = await wallet1.create_htlc_lock(preimage=preimage) - _, send_proofs = await wallet1.split_to_send(wallet1.proofs, 8, secret_lock=secret) + _, send_proofs = await wallet1.swap_to_send(wallet1.proofs, 8, secret_lock=secret) for p in send_proofs: assert HTLCSecret.deserialize(p.secret).data == preimage_hash @@ -87,7 +87,7 @@ async def test_htlc_redeem_with_preimage(wallet1: Wallet, wallet2: Wallet): preimage = "00000000000000000000000000000000" # preimage_hash = hashlib.sha256(bytes.fromhex(preimage)).hexdigest() secret = await wallet1.create_htlc_lock(preimage=preimage) - _, send_proofs = await wallet1.split_to_send(wallet1.proofs, 8, secret_lock=secret) + _, send_proofs = await wallet1.swap_to_send(wallet1.proofs, 8, secret_lock=secret) for p in send_proofs: p.witness = HTLCWitness(preimage=preimage).json() await wallet2.redeem(send_proofs) @@ -103,7 +103,7 @@ async def test_htlc_redeem_with_wrong_preimage(wallet1: Wallet, wallet2: Wallet) secret = await wallet1.create_htlc_lock( preimage=preimage[:-5] + "11111" ) # wrong preimage - _, send_proofs = await wallet1.split_to_send(wallet1.proofs, 8, secret_lock=secret) + _, send_proofs = await wallet1.swap_to_send(wallet1.proofs, 8, secret_lock=secret) for p in send_proofs: p.witness = HTLCWitness(preimage=preimage).json() await assert_err( @@ -122,7 +122,7 @@ async def test_htlc_redeem_with_no_signature(wallet1: Wallet, wallet2: Wallet): secret = await wallet1.create_htlc_lock( preimage=preimage, hashlock_pubkey=pubkey_wallet1 ) - _, send_proofs = await wallet1.split_to_send(wallet1.proofs, 8, secret_lock=secret) + _, send_proofs = await wallet1.swap_to_send(wallet1.proofs, 8, secret_lock=secret) for p in send_proofs: p.witness = HTLCWitness(preimage=preimage).json() await assert_err( @@ -142,7 +142,7 @@ async def test_htlc_redeem_with_wrong_signature(wallet1: Wallet, wallet2: Wallet secret = await wallet1.create_htlc_lock( preimage=preimage, hashlock_pubkey=pubkey_wallet1 ) - _, send_proofs = await wallet1.split_to_send(wallet1.proofs, 8, secret_lock=secret) + _, send_proofs = await wallet1.swap_to_send(wallet1.proofs, 8, secret_lock=secret) signatures = await wallet1.sign_p2pk_proofs(send_proofs) for p, s in zip(send_proofs, signatures): p.witness = HTLCWitness( @@ -166,7 +166,7 @@ async def test_htlc_redeem_with_correct_signature(wallet1: Wallet, wallet2: Wall secret = await wallet1.create_htlc_lock( preimage=preimage, hashlock_pubkey=pubkey_wallet1 ) - _, send_proofs = await wallet1.split_to_send(wallet1.proofs, 8, secret_lock=secret) + _, send_proofs = await wallet1.swap_to_send(wallet1.proofs, 8, secret_lock=secret) signatures = await wallet1.sign_p2pk_proofs(send_proofs) for p, s in zip(send_proofs, signatures): @@ -192,7 +192,7 @@ async def test_htlc_redeem_hashlock_wrong_signature_timelock_correct_signature( locktime_seconds=2, locktime_pubkey=pubkey_wallet1, ) - _, send_proofs = await wallet1.split_to_send(wallet1.proofs, 8, secret_lock=secret) + _, send_proofs = await wallet1.swap_to_send(wallet1.proofs, 8, secret_lock=secret) signatures = await wallet1.sign_p2pk_proofs(send_proofs) for p, s in zip(send_proofs, signatures): @@ -226,7 +226,7 @@ async def test_htlc_redeem_hashlock_wrong_signature_timelock_wrong_signature( locktime_seconds=2, locktime_pubkey=pubkey_wallet1, ) - _, send_proofs = await wallet1.split_to_send(wallet1.proofs, 8, secret_lock=secret) + _, send_proofs = await wallet1.swap_to_send(wallet1.proofs, 8, secret_lock=secret) signatures = await wallet1.sign_p2pk_proofs(send_proofs) for p, s in zip(send_proofs, signatures): diff --git a/tests/test_wallet_p2pk.py b/tests/test_wallet_p2pk.py index ac236d5..d3dcde5 100644 --- a/tests/test_wallet_p2pk.py +++ b/tests/test_wallet_p2pk.py @@ -74,7 +74,7 @@ async def test_p2pk(wallet1: Wallet, wallet2: Wallet): pubkey_wallet2 = await wallet2.create_p2pk_pubkey() # p2pk test secret_lock = await wallet1.create_p2pk_lock(pubkey_wallet2) # sender side - _, send_proofs = await wallet1.split_to_send( + _, send_proofs = await wallet1.swap_to_send( wallet1.proofs, 8, secret_lock=secret_lock ) await wallet2.redeem(send_proofs) @@ -100,7 +100,7 @@ async def test_p2pk_sig_all(wallet1: Wallet, wallet2: Wallet): secret_lock = await wallet1.create_p2pk_lock( pubkey_wallet2, sig_all=True ) # sender side - _, send_proofs = await wallet1.split_to_send( + _, send_proofs = await wallet1.swap_to_send( wallet1.proofs, 8, secret_lock=secret_lock ) await wallet2.redeem(send_proofs) @@ -114,7 +114,7 @@ async def test_p2pk_receive_with_wrong_private_key(wallet1: Wallet, wallet2: Wal pubkey_wallet2 = await wallet2.create_p2pk_pubkey() # receiver side # sender side secret_lock = await wallet1.create_p2pk_lock(pubkey_wallet2) # sender side - _, send_proofs = await wallet1.split_to_send( + _, send_proofs = await wallet1.swap_to_send( wallet1.proofs, 8, secret_lock=secret_lock ) # receiver side: wrong private key @@ -137,7 +137,7 @@ async def test_p2pk_short_locktime_receive_with_wrong_private_key( secret_lock = await wallet1.create_p2pk_lock( pubkey_wallet2, locktime_seconds=2 ) # sender side - _, send_proofs = await wallet1.split_to_send( + _, send_proofs = await wallet1.swap_to_send( wallet1.proofs, 8, secret_lock=secret_lock ) # receiver side: wrong private key @@ -167,7 +167,7 @@ async def test_p2pk_locktime_with_refund_pubkey(wallet1: Wallet, wallet2: Wallet locktime_seconds=2, # locktime tags=Tags([["refund", pubkey_wallet2]]), # refund pubkey ) # sender side - _, send_proofs = await wallet1.split_to_send( + _, send_proofs = await wallet1.swap_to_send( wallet1.proofs, 8, secret_lock=secret_lock ) send_proofs_copy = copy.deepcopy(send_proofs) @@ -198,7 +198,7 @@ async def test_p2pk_locktime_with_wrong_refund_pubkey(wallet1: Wallet, wallet2: locktime_seconds=2, # locktime tags=Tags([["refund", garbage_pubkey_2.serialize().hex()]]), # refund pubkey ) # sender side - _, send_proofs = await wallet1.split_to_send( + _, send_proofs = await wallet1.swap_to_send( wallet1.proofs, 8, secret_lock=secret_lock ) send_proofs_copy = copy.deepcopy(send_proofs) @@ -235,7 +235,7 @@ async def test_p2pk_locktime_with_second_refund_pubkey( [["refund", pubkey_wallet2, pubkey_wallet1]] ), # multiple refund pubkeys ) # sender side - _, send_proofs = await wallet1.split_to_send( + _, send_proofs = await wallet1.swap_to_send( wallet1.proofs, 8, secret_lock=secret_lock ) send_proofs_copy = copy.deepcopy(send_proofs) @@ -263,7 +263,7 @@ async def test_p2pk_multisig_2_of_2(wallet1: Wallet, wallet2: Wallet): pubkey_wallet2, tags=Tags([["pubkeys", pubkey_wallet1]]), n_sigs=2 ) - _, send_proofs = await wallet1.split_to_send( + _, send_proofs = await wallet1.swap_to_send( wallet1.proofs, 8, secret_lock=secret_lock ) # add signatures of wallet1 @@ -285,7 +285,7 @@ async def test_p2pk_multisig_duplicate_signature(wallet1: Wallet, wallet2: Walle pubkey_wallet2, tags=Tags([["pubkeys", pubkey_wallet1]]), n_sigs=2 ) - _, send_proofs = await wallet1.split_to_send( + _, send_proofs = await wallet1.swap_to_send( wallet1.proofs, 8, secret_lock=secret_lock ) # add signatures of wallet2 – this is a duplicate signature @@ -308,7 +308,7 @@ async def test_p2pk_multisig_quorum_not_met_1_of_2(wallet1: Wallet, wallet2: Wal secret_lock = await wallet1.create_p2pk_lock( pubkey_wallet2, tags=Tags([["pubkeys", pubkey_wallet1]]), n_sigs=2 ) - _, send_proofs = await wallet1.split_to_send( + _, send_proofs = await wallet1.swap_to_send( wallet1.proofs, 8, secret_lock=secret_lock ) await assert_err( @@ -330,7 +330,7 @@ async def test_p2pk_multisig_quorum_not_met_2_of_3(wallet1: Wallet, wallet2: Wal pubkey_wallet2, tags=Tags([["pubkeys", pubkey_wallet1]]), n_sigs=3 ) - _, send_proofs = await wallet1.split_to_send( + _, send_proofs = await wallet1.swap_to_send( wallet1.proofs, 8, secret_lock=secret_lock ) # add signatures of wallet1 @@ -352,7 +352,7 @@ async def test_p2pk_multisig_with_duplicate_publickey(wallet1: Wallet, wallet2: secret_lock = await wallet1.create_p2pk_lock( pubkey_wallet2, tags=Tags([["pubkeys", pubkey_wallet2]]), n_sigs=2 ) - _, send_proofs = await wallet1.split_to_send( + _, send_proofs = await wallet1.swap_to_send( wallet1.proofs, 8, secret_lock=secret_lock ) await assert_err(wallet2.redeem(send_proofs), "Mint Error: pubkeys must be unique.") @@ -377,7 +377,7 @@ async def test_p2pk_multisig_with_wrong_first_private_key( secret_lock = await wallet1.create_p2pk_lock( pubkey_wallet2, tags=Tags([["pubkeys", wrong_public_key_hex]]), n_sigs=2 ) - _, send_proofs = await wallet1.split_to_send( + _, send_proofs = await wallet1.swap_to_send( wallet1.proofs, 8, secret_lock=secret_lock ) # add signatures of wallet1 diff --git a/tests/test_wallet_regtest.py b/tests/test_wallet_regtest.py index 3e6d31b..526fa11 100644 --- a/tests/test_wallet_regtest.py +++ b/tests/test_wallet_regtest.py @@ -45,7 +45,7 @@ async def test_regtest_pending_quote(wallet: Wallet, ledger: Ledger): # wallet pays the invoice quote = await wallet.melt_quote(invoice_payment_request) total_amount = quote.amount + quote.fee_reserve - _, send_proofs = await wallet.split_to_send(wallet.proofs, total_amount) + _, send_proofs = await wallet.swap_to_send(wallet.proofs, total_amount) asyncio.create_task( wallet.melt( proofs=send_proofs, @@ -85,7 +85,7 @@ async def test_regtest_failed_quote(wallet: Wallet, ledger: Ledger): # wallet pays the invoice quote = await wallet.melt_quote(invoice_payment_request) total_amount = quote.amount + quote.fee_reserve - _, send_proofs = await wallet.split_to_send(wallet.proofs, total_amount) + _, send_proofs = await wallet.swap_to_send(wallet.proofs, total_amount) asyncio.create_task( wallet.melt( proofs=send_proofs, diff --git a/tests/test_wallet_restore.py b/tests/test_wallet_restore.py index 4b92ca7..e245d49 100644 --- a/tests/test_wallet_restore.py +++ b/tests/test_wallet_restore.py @@ -164,7 +164,7 @@ async def test_restore_wallet_after_mint(wallet3: Wallet): await wallet3.load_proofs() wallet3.proofs = [] assert wallet3.balance == 0 - await wallet3.restore_promises_from_to(0, 20) + await wallet3.restore_promises_from_to(wallet3.keyset_id, 0, 20) assert wallet3.balance == 64 # expect that DLEQ proofs are restored @@ -185,7 +185,7 @@ async def test_restore_wallet_with_invalid_mnemonic(wallet3: Wallet): @pytest.mark.asyncio -async def test_restore_wallet_after_split_to_send(wallet3: Wallet): +async def test_restore_wallet_after_swap_to_send(wallet3: Wallet): await wallet3._init_private_key( "half depart obvious quality work element tank gorilla view sugar picture" " humble" @@ -197,7 +197,7 @@ async def test_restore_wallet_after_split_to_send(wallet3: Wallet): await wallet3.mint(64, id=invoice.id) assert wallet3.balance == 64 - _, spendable_proofs = await wallet3.split_to_send( + _, spendable_proofs = await wallet3.swap_to_send( wallet3.proofs, 32, set_reserved=True ) # type: ignore @@ -205,7 +205,7 @@ async def test_restore_wallet_after_split_to_send(wallet3: Wallet): await wallet3.load_proofs() wallet3.proofs = [] assert wallet3.balance == 0 - await wallet3.restore_promises_from_to(0, 100) + await wallet3.restore_promises_from_to(wallet3.keyset_id, 0, 100) assert wallet3.balance == 96 await wallet3.invalidate(wallet3.proofs, check_spendable=True) assert wallet3.balance == 64 @@ -222,7 +222,7 @@ async def test_restore_wallet_after_send_and_receive(wallet3: Wallet, wallet2: W await wallet3.mint(64, id=invoice.id) assert wallet3.balance == 64 - _, spendable_proofs = await wallet3.split_to_send( + _, spendable_proofs = await wallet3.swap_to_send( wallet3.proofs, 32, set_reserved=True ) # type: ignore @@ -232,7 +232,7 @@ async def test_restore_wallet_after_send_and_receive(wallet3: Wallet, wallet2: W await wallet3.load_proofs(reload=True) assert wallet3.proofs == [] assert wallet3.balance == 0 - await wallet3.restore_promises_from_to(0, 100) + await wallet3.restore_promises_from_to(wallet3.keyset_id, 0, 100) assert wallet3.balance == 96 await wallet3.invalidate(wallet3.proofs, check_spendable=True) assert wallet3.balance == 32 @@ -265,7 +265,7 @@ async def test_restore_wallet_after_send_and_self_receive(wallet3: Wallet): await wallet3.mint(64, id=invoice.id) assert wallet3.balance == 64 - _, spendable_proofs = await wallet3.split_to_send( + _, spendable_proofs = await wallet3.swap_to_send( wallet3.proofs, 32, set_reserved=True ) # type: ignore @@ -275,7 +275,7 @@ async def test_restore_wallet_after_send_and_self_receive(wallet3: Wallet): await wallet3.load_proofs(reload=True) assert wallet3.proofs == [] assert wallet3.balance == 0 - await wallet3.restore_promises_from_to(0, 100) + await wallet3.restore_promises_from_to(wallet3.keyset_id, 0, 100) assert wallet3.balance == 128 await wallet3.invalidate(wallet3.proofs, check_spendable=True) assert wallet3.balance == 64 @@ -295,7 +295,7 @@ async def test_restore_wallet_after_send_twice( box.add(wallet3.proofs) assert wallet3.balance == 2 - keep_proofs, spendable_proofs = await wallet3.split_to_send( + keep_proofs, spendable_proofs = await wallet3.swap_to_send( wallet3.proofs, 1, set_reserved=True ) # type: ignore box.add(wallet3.proofs) @@ -309,7 +309,7 @@ async def test_restore_wallet_after_send_twice( await wallet3.load_proofs(reload=True) assert wallet3.proofs == [] assert wallet3.balance == 0 - await wallet3.restore_promises_from_to(0, 10) + await wallet3.restore_promises_from_to(wallet3.keyset_id, 0, 10) box.add(wallet3.proofs) assert wallet3.balance == 4 await wallet3.invalidate(wallet3.proofs, check_spendable=True) @@ -317,7 +317,7 @@ async def test_restore_wallet_after_send_twice( # again - _, spendable_proofs = await wallet3.split_to_send( + _, spendable_proofs = await wallet3.swap_to_send( wallet3.proofs, 1, set_reserved=True ) # type: ignore box.add(wallet3.proofs) @@ -331,7 +331,7 @@ async def test_restore_wallet_after_send_twice( await wallet3.load_proofs(reload=True) assert wallet3.proofs == [] assert wallet3.balance == 0 - await wallet3.restore_promises_from_to(0, 15) + await wallet3.restore_promises_from_to(wallet3.keyset_id, 0, 15) box.add(wallet3.proofs) assert wallet3.balance == 6 await wallet3.invalidate(wallet3.proofs, check_spendable=True) @@ -354,7 +354,7 @@ async def test_restore_wallet_after_send_and_self_receive_nonquadratic_value( box.add(wallet3.proofs) assert wallet3.balance == 64 - keep_proofs, spendable_proofs = await wallet3.split_to_send( + keep_proofs, spendable_proofs = await wallet3.swap_to_send( wallet3.proofs, 10, set_reserved=True ) # type: ignore box.add(wallet3.proofs) @@ -368,7 +368,7 @@ async def test_restore_wallet_after_send_and_self_receive_nonquadratic_value( await wallet3.load_proofs(reload=True) assert wallet3.proofs == [] assert wallet3.balance == 0 - await wallet3.restore_promises_from_to(0, 20) + await wallet3.restore_promises_from_to(wallet3.keyset_id, 0, 20) box.add(wallet3.proofs) assert wallet3.balance == 84 await wallet3.invalidate(wallet3.proofs, check_spendable=True) @@ -376,7 +376,7 @@ async def test_restore_wallet_after_send_and_self_receive_nonquadratic_value( # again - _, spendable_proofs = await wallet3.split_to_send( + _, spendable_proofs = await wallet3.swap_to_send( wallet3.proofs, 12, set_reserved=True ) # type: ignore @@ -388,7 +388,7 @@ async def test_restore_wallet_after_send_and_self_receive_nonquadratic_value( await wallet3.load_proofs(reload=True) assert wallet3.proofs == [] assert wallet3.balance == 0 - await wallet3.restore_promises_from_to(0, 50) + await wallet3.restore_promises_from_to(wallet3.keyset_id, 0, 50) assert wallet3.balance == 108 await wallet3.invalidate(wallet3.proofs, check_spendable=True) assert wallet3.balance == 64 diff --git a/tests/test_wallet_subscription.py b/tests/test_wallet_subscription.py index ce77a16..ac4711f 100644 --- a/tests/test_wallet_subscription.py +++ b/tests/test_wallet_subscription.py @@ -86,7 +86,7 @@ async def test_wallet_subscription_swap(wallet: Wallet): wallet.proofs, callback=callback ) - _ = await wallet.split_to_send(wallet.proofs, 64) + _ = await wallet.swap_to_send(wallet.proofs, 64) wait = 1 await asyncio.sleep(wait)