From 19de10bfea160b2cdb0e726d4ca1b0bf980df8b7 Mon Sep 17 00:00:00 2001 From: callebtc <93376500+callebtc@users.noreply.github.com> Date: Wed, 10 Apr 2024 12:23:53 +0200 Subject: [PATCH] [Refactor] Mint: remove output.id optional (#504) * remove output.id optional * asserts * wip * wip * working --- README.md | 2 +- cashu/core/base.py | 45 +++---- cashu/core/settings.py | 4 +- cashu/mint/ledger.py | 187 ++++++++++++++++++------------ cashu/mint/router_deprecated.py | 36 +++--- cashu/wallet/wallet_deprecated.py | 21 +++- pyproject.toml | 2 +- setup.py | 2 +- tests/test_mint_init.py | 4 +- tests/test_wallet.py | 2 +- 10 files changed, 180 insertions(+), 125 deletions(-) diff --git a/README.md b/README.md index 9a9a2b0f..73e89b1e 100644 --- a/README.md +++ b/README.md @@ -172,7 +172,7 @@ This command runs the mint on your local computer. Skip this step if you want to ## Docker ``` -docker run -d -p 3338:3338 --name nutshell -e MINT_BACKEND_BOLT11_SAT=FakeWallet -e MINT_LISTEN_HOST=0.0.0.0 -e MINT_LISTEN_PORT=3338 -e MINT_PRIVATE_KEY=TEST_PRIVATE_KEY cashubtc/nutshell:0.15.2 poetry run mint +docker run -d -p 3338:3338 --name nutshell -e MINT_BACKEND_BOLT11_SAT=FakeWallet -e MINT_LISTEN_HOST=0.0.0.0 -e MINT_LISTEN_PORT=3338 -e MINT_PRIVATE_KEY=TEST_PRIVATE_KEY cashubtc/nutshell:0.15.3 poetry run mint ``` ## From this repository diff --git a/cashu/core/base.py b/cashu/core/base.py index f6375f9e..1e198306 100644 --- a/cashu/core/base.py +++ b/cashu/core/base.py @@ -101,12 +101,12 @@ class Proof(BaseModel): time_created: Union[None, str] = "" time_reserved: Union[None, str] = "" derivation_path: Union[None, str] = "" # derivation path of the proof - mint_id: Union[None, str] = ( - None # holds the id of the mint operation that created this proof - ) - melt_id: Union[None, str] = ( - None # holds the id of the melt operation that destroyed this proof - ) + mint_id: Union[ + None, str + ] = None # holds the id of the mint operation that created this proof + melt_id: Union[ + None, str + ] = None # holds the id of the melt operation that destroyed this proof def __init__(self, **data): super().__init__(**data) @@ -161,20 +161,13 @@ def htlcpreimage(self) -> Union[str, None]: return HTLCWitness.from_witness(self.witness).preimage -class Proofs(BaseModel): - # NOTE: not used in Pydantic validation - __root__: List[Proof] - - class BlindedMessage(BaseModel): """ Blinded message or blinded secret or "output" which is to be signed by the mint """ amount: int - id: Optional[ - str - ] # DEPRECATION: Only Optional for backwards compatibility with old clients < 0.15 for deprecated API route. + id: str B_: str # Hex-encoded blinded message witness: Union[str, None] = None # witnesses (used for P2PK with SIG_ALL) @@ -204,11 +197,6 @@ def from_row(cls, row: Row): ) -class BlindedMessages(BaseModel): - # NOTE: not used in Pydantic validation - __root__: List[BlindedMessage] = [] - - # ------- LIGHTNING INVOICE ------- @@ -341,6 +329,19 @@ class GetInfoResponse_deprecated(BaseModel): parameter: Optional[dict] = None +class BlindedMessage_Deprecated(BaseModel): + # Same as BlindedMessage, but without the id field + amount: int + B_: str # Hex-encoded blinded message + id: Optional[str] = None + witness: Union[str, None] = None # witnesses (used for P2PK with SIG_ALL) + + @property + def p2pksigs(self) -> List[str]: + assert self.witness, "Witness missing in output" + return P2PKWitness.from_witness(self.witness).signatures + + # ------- API: KEYS ------- @@ -407,7 +408,7 @@ class GetMintResponse_deprecated(BaseModel): class PostMintRequest_deprecated(BaseModel): - outputs: List[BlindedMessage] = Field( + outputs: List[BlindedMessage_Deprecated] = Field( ..., max_items=settings.mint_max_request_length ) @@ -454,7 +455,7 @@ class PostMeltResponse(BaseModel): class PostMeltRequest_deprecated(BaseModel): proofs: List[Proof] = Field(..., max_items=settings.mint_max_request_length) pr: str = Field(..., max_length=settings.mint_max_request_length) - outputs: Union[List[BlindedMessage], None] = Field( + outputs: Union[List[BlindedMessage_Deprecated], None] = Field( None, max_items=settings.mint_max_request_length ) @@ -483,7 +484,7 @@ class PostSplitResponse(BaseModel): class PostSplitRequest_Deprecated(BaseModel): proofs: List[Proof] = Field(..., max_items=settings.mint_max_request_length) amount: Optional[int] = None - outputs: List[BlindedMessage] = Field( + outputs: List[BlindedMessage_Deprecated] = Field( ..., max_items=settings.mint_max_request_length ) diff --git a/cashu/core/settings.py b/cashu/core/settings.py index 899ab9ae..0cf2e755 100644 --- a/cashu/core/settings.py +++ b/cashu/core/settings.py @@ -8,7 +8,7 @@ env = Env() -VERSION = "0.15.2" +VERSION = "0.15.3" def find_env_file(): @@ -58,7 +58,7 @@ class MintSettings(CashuSettings): mint_database: str = Field(default="data/mint") mint_test_database: str = Field(default="test_data/test_mint") - mint_duplicate_keysets: bool = Field( + mint_duplicate_old_keysets: bool = Field( default=True, title="Duplicate keysets", description=( diff --git a/cashu/mint/ledger.py b/cashu/mint/ledger.py index c4f2ea02..b5b5743f 100644 --- a/cashu/mint/ledger.py +++ b/cashu/mint/ledger.py @@ -34,6 +34,7 @@ from ..core.crypto.secp import PrivateKey, PublicKey from ..core.db import Connection, Database, get_db_connection from ..core.errors import ( + CashuError, KeysetError, KeysetNotFoundError, LightningError, @@ -72,7 +73,8 @@ def __init__( derivation_path="", crud=LedgerCrudSqlite(), ): - assert seed, "seed not set" + if not seed: + raise Exception("seed not set") # decrypt seed if seed_decryption_key is set try: @@ -189,7 +191,8 @@ async def activate_keyset( Returns: MintKeyset: Keyset """ - assert derivation_path, "derivation path not set" + if not derivation_path: + raise Exception("derivation path not set") seed = seed or self.seed tmp_keyset_local = MintKeyset( seed=seed, @@ -230,7 +233,8 @@ async def activate_keyset( # BEGIN BACKWARDS COMPATIBILITY < 0.15.0 # set the deprecated id - assert keyset.public_keys + if not keyset.public_keys: + raise KeysetError("no public keys for this keyset") keyset.duplicate_keyset_id = derive_keyset_id_deprecated(keyset.public_keys) # END BACKWARDS COMPATIBILITY < 0.15.0 @@ -268,17 +272,22 @@ async def init_keysets( logger.info(f"Current keyset: {self.keyset.id}") # check that we have a least one active keyset - assert any([k.active for k in self.keysets.values()]), "No active keyset found." + if not any([k.active for k in self.keysets.values()]): + raise KeysetError("No active keyset found.") # BEGIN BACKWARDS COMPATIBILITY < 0.15.0 # we duplicate new keysets and compute their old keyset id, and # we duplicate old keysets and compute their new keyset id - if ( - duplicate_keysets is None and settings.mint_duplicate_keysets - ) or duplicate_keysets: + if duplicate_keysets is not False and ( + settings.mint_duplicate_old_keysets or duplicate_keysets + ): for _, keyset in copy.copy(self.keysets).items(): + # if keyset.version_tuple >= (0, 15, 3) and not duplicate_keysets: + # # we do not duplicate keysets from version 0.15.3 and above if not forced by duplicate_keysets + # continue keyset_copy = copy.copy(keyset) - assert keyset_copy.public_keys + if not keyset_copy.public_keys: + raise KeysetError("no public keys for this keyset") if keyset.version_tuple >= (0, 15): keyset_copy.id = derive_keyset_id_deprecated( keyset_copy.public_keys @@ -296,7 +305,8 @@ def get_keyset(self, keyset_id: Optional[str] = None) -> Dict[int, str]: if keyset_id and keyset_id not in self.keysets: raise KeysetNotFoundError() keyset = self.keysets[keyset_id] if keyset_id else self.keyset - assert keyset.public_keys, KeysetError("no public keys for this keyset") + if not keyset.public_keys: + raise KeysetError("no public keys for this keyset") return {a: p.serialize().hex() for a, p in keyset.public_keys.items()} async def get_balance(self) -> int: @@ -400,7 +410,8 @@ async def mint_quote(self, quote_request: PostMintQuoteRequest) -> MintQuote: MintQuote: Mint quote object. """ logger.trace("called request_mint") - assert quote_request.amount > 0, "amount must be positive" + if not quote_request.amount > 0: + raise TransactionError("amount must be positive") if settings.mint_max_peg_in and quote_request.amount > settings.mint_max_peg_in: raise NotAllowedError( f"Maximum mint amount is {settings.mint_max_peg_in} sat." @@ -426,9 +437,8 @@ async def mint_quote(self, quote_request: PostMintQuoteRequest) -> MintQuote: f" {invoice_response.checking_id}" ) - assert ( - invoice_response.payment_request and invoice_response.checking_id - ), LightningError("could not fetch bolt11 payment request from backend") + if not (invoice_response.payment_request and invoice_response.checking_id): + raise LightningError("could not fetch bolt11 payment request from backend") # get invoice expiry time invoice_obj = bolt11.decode(invoice_response.payment_request) @@ -478,7 +488,8 @@ async def get_mint_quote(self, quote_id: str) -> MintQuote: unit, method = self._verify_and_get_unit_method(quote.unit, quote.method) if not quote.paid: - assert quote.checking_id, "quote has no checking id" + if not quote.checking_id: + raise CashuError("quote has no checking id") logger.trace(f"Lightning: checking invoice {quote.checking_id}") status: PaymentStatus = await self.backends[method][ unit @@ -518,18 +529,27 @@ async def mint( await self._verify_outputs(outputs) sum_amount_outputs = sum([b.amount for b in outputs]) + output_units = set([k.unit for k in [self.keysets[o.id] for o in outputs]]) + if not len(output_units) == 1: + raise TransactionError("outputs have different units") + output_unit = list(output_units)[0] + self.locks[quote_id] = ( self.locks.get(quote_id) or asyncio.Lock() ) # create a new lock if it doesn't exist async with self.locks[quote_id]: quote = await self.get_mint_quote(quote_id=quote_id) - assert quote.paid, QuoteNotPaidError() - assert not quote.issued, "quote already issued" - assert ( - quote.amount == sum_amount_outputs - ), "amount to mint does not match quote amount" - if quote.expiry: - assert quote.expiry > int(time.time()), "quote expired" + + if not quote.paid: + raise QuoteNotPaidError() + if quote.issued: + raise TransactionError("quote already issued") + if not quote.unit == output_unit.name: + raise TransactionError("quote unit does not match output unit") + if not quote.amount == sum_amount_outputs: + raise TransactionError("amount to mint does not match quote amount") + if quote.expiry and quote.expiry > int(time.time()): + raise TransactionError("quote expired") promises = await self._generate_promises(outputs) logger.trace("generated promises") @@ -571,12 +591,19 @@ async def melt_quote( request=request, db=self.db ) if mint_quote: - assert request == mint_quote.request, "bolt11 requests do not match" - assert mint_quote.unit == melt_quote.unit, "units do not match" - assert mint_quote.method == method.name, "methods do not match" - assert not mint_quote.paid, "mint quote already paid" - assert not mint_quote.issued, "mint quote already issued" - assert mint_quote.checking_id, "mint quote has no checking id" + if not request == mint_quote.request: + raise TransactionError("bolt11 requests do not match") + if not mint_quote.unit == melt_quote.unit: + raise TransactionError("units do not match") + if not mint_quote.method == method.name: + raise TransactionError("methods do not match") + if mint_quote.paid: + raise TransactionError("mint quote already paid") + if mint_quote.issued: + raise TransactionError("mint quote already issued") + if not mint_quote.checking_id: + raise TransactionError("mint quote has no checking id") + payment_quote = PaymentQuoteResponse( checking_id=mint_quote.checking_id, amount=Amount(unit, mint_quote.amount), @@ -589,20 +616,20 @@ async def melt_quote( else: # not internal, get payment quote by backend payment_quote = await self.backends[method][unit].get_payment_quote(request) - assert payment_quote.checking_id, "quote has no checking id" + if not payment_quote.checking_id: + raise TransactionError("quote has no checking id") # make sure the backend returned the amount with a correct unit - assert ( - payment_quote.amount.unit == unit - ), "payment quote amount units do not match" + if not payment_quote.amount.unit == unit: + raise TransactionError("payment quote amount units do not match") # fee from the backend must be in the same unit as the amount - assert ( - payment_quote.fee.unit == unit - ), "payment quote fee units do not match" + if not payment_quote.fee.unit == unit: + raise TransactionError("payment quote fee units do not match") # We assume that the request is a bolt11 invoice, this works since we # support only the bol11 method for now. invoice_obj = bolt11.decode(melt_quote.request) - assert invoice_obj.amount_msat, "invoice has no amount." + if not invoice_obj.amount_msat: + raise TransactionError("invoice has no amount.") # we set the expiry of this quote to the expiry of the bolt11 invoice expiry = None if invoice_obj.expiry is not None: @@ -703,23 +730,28 @@ async def melt_mint_settle_internally(self, melt_quote: MeltQuote) -> MeltQuote: if not mint_quote: return melt_quote # we settle the transaction internally - assert not melt_quote.paid, "melt quote already paid" + if melt_quote.paid: + raise TransactionError("melt quote already paid") # verify amounts from bolt11 invoice bolt11_request = melt_quote.request invoice_obj = bolt11.decode(bolt11_request) - assert invoice_obj.amount_msat, "invoice has no amount." - # invoice_amount_sat = math.ceil(invoice_obj.amount_msat / 1000) - # assert ( - # Amount(Unit[melt_quote.unit], mint_quote.amount).to(Unit.sat).amount - # == invoice_amount_sat - # ), "amounts do not match" - assert mint_quote.amount == melt_quote.amount, "amounts do not match" - assert bolt11_request == mint_quote.request, "bolt11 requests do not match" - assert mint_quote.unit == melt_quote.unit, "units do not match" - assert mint_quote.method == melt_quote.method, "methods do not match" - assert not mint_quote.paid, "mint quote already paid" - assert not mint_quote.issued, "mint quote already issued" + + if not invoice_obj.amount_msat: + raise TransactionError("invoice has no amount.") + if not mint_quote.amount == melt_quote.amount: + raise TransactionError("amounts do not match") + if not bolt11_request == mint_quote.request: + raise TransactionError("bolt11 requests do not match") + if not mint_quote.unit == melt_quote.unit: + raise TransactionError("units do not match") + if not mint_quote.method == melt_quote.method: + raise TransactionError("methods do not match") + if mint_quote.paid: + raise TransactionError("mint quote already paid") + if mint_quote.issued: + raise TransactionError("mint quote already issued") + logger.info( f"Settling bolt11 payment internally: {melt_quote.quote} ->" f" {mint_quote.quote} ({melt_quote.amount} {melt_quote.unit})" @@ -764,25 +796,25 @@ async def melt( melt_quote.unit, melt_quote.method ) - assert not melt_quote.paid, "melt quote already paid" + if melt_quote.paid: + raise TransactionError("melt quote already paid") # make sure that the outputs (for fee return) are in the same unit as the quote if outputs: await self._verify_outputs(outputs, skip_amount_check=True) - assert outputs[0].id, "output id not set" outputs_unit = self.keysets[outputs[0].id].unit - assert melt_quote.unit == outputs_unit.name, ( - f"output unit {outputs_unit.name} does not match quote unit" - f" {melt_quote.unit}" - ) + if not melt_quote.unit == outputs_unit.name: + raise TransactionError( + f"output unit {outputs_unit.name} does not match quote unit {melt_quote.unit}" + ) # verify that the amount of the input proofs is equal to the amount of the quote total_provided = sum_proofs(proofs) total_needed = melt_quote.amount + (melt_quote.fee_reserve or 0) - assert total_provided >= total_needed, ( - f"not enough inputs provided for melt. Provided: {total_provided}, needed:" - f" {total_needed}" - ) + if not total_provided >= total_needed: + raise TransactionError( + f"not enough inputs provided for melt. Provided: {total_provided}, needed: {total_needed}" + ) # verify that the amount of the proofs is not larger than the maximum allowed if settings.mint_max_peg_out and total_provided > settings.mint_max_peg_out: @@ -831,7 +863,6 @@ async def melt( # prepare change to compensate wallet for overpaid fees return_promises: List[BlindedSignature] = [] if outputs: - assert outputs[0].id, "output id not set" return_promises = await self._generate_change_promises( input_amount=total_provided, output_amount=melt_quote.amount, @@ -871,22 +902,21 @@ async def split( Tuple[List[BlindSignature],List[BlindSignature]]: Promises on both sides of the split. """ logger.trace("split called") + # explicitly check that amount of inputs is equal to amount of outputs + # note: we check this again in verify_inputs_and_outputs but only if any + # outputs are provided at all. To make sure of that before calling + # verify_inputs_and_outputs, we check it here. + self._verify_equation_balanced(proofs, outputs) + # verify spending inputs, outputs, and spending conditions + await self.verify_inputs_and_outputs(proofs=proofs, outputs=outputs) await self._set_proofs_pending(proofs) try: - # explicitly check that amount of inputs is equal to amount of outputs - # note: we check this again in verify_inputs_and_outputs but only if any - # outputs are provided at all. To make sure of that before calling - # verify_inputs_and_outputs, we check it here. - self._verify_equation_balanced(proofs, outputs) - # verify spending inputs, outputs, and spending conditions - await self.verify_inputs_and_outputs(proofs=proofs, outputs=outputs) - # Mark proofs as used and prepare new promises async with get_db_connection(self.db) as conn: # we do this in a single db transaction - promises = await self._generate_promises(outputs, keyset, conn) 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}") @@ -949,15 +979,16 @@ async def _generate_promises( ] = [] for output in outputs: B_ = PublicKey(bytes.fromhex(output.B_), raw=True) - assert output.id, "output id not set" keyset = keyset or self.keysets[output.id] - - assert output.id in self.keysets, f"keyset {output.id} not found" - assert output.id in [ + if output.id not in self.keysets: + raise TransactionError(f"keyset {output.id} not found") + if output.id not in [ keyset.id, keyset.duplicate_keyset_id, - ], "keyset id does not match output id" - assert keyset.active, "keyset is not active" + ]: + raise TransactionError("keyset id does not match output id") + if not keyset.active: + raise TransactionError("keyset is not active") keyset_id = output.id logger.trace(f"Generating promise with keyset {keyset_id}.") private_key_amount = keyset.private_keys[output.amount] @@ -995,7 +1026,8 @@ async def _generate_promises( async def load_used_proofs(self) -> None: """Load all used proofs from database.""" - assert settings.mint_cache_secrets, "MINT_CACHE_SECRETS must be set to TRUE" + if not settings.mint_cache_secrets: + raise Exception("MINT_CACHE_SECRETS must be set to TRUE") logger.debug("Loading used proofs into memory") spent_proofs_list = await self.crud.get_spent_proofs(db=self.db) or [] logger.debug(f"Loaded {len(spent_proofs_list)} used proofs") @@ -1082,11 +1114,12 @@ async def _validate_proofs_pending( Raises: Exception: At least one of the proofs is in the pending table. """ - assert ( + if not ( len( await self.crud.get_proofs_pending( Ys=[p.Y for p in proofs], db=self.db, conn=conn ) ) == 0 - ), TransactionError("proofs are pending.") + ): + raise TransactionError("proofs are pending.") diff --git a/cashu/mint/router_deprecated.py b/cashu/mint/router_deprecated.py index 4a970c4f..dde54056 100644 --- a/cashu/mint/router_deprecated.py +++ b/cashu/mint/router_deprecated.py @@ -4,6 +4,7 @@ from loguru import logger from ..core.base import ( + BlindedMessage, BlindedSignature, CheckFeesRequest_deprecated, CheckFeesResponse_deprecated, @@ -177,10 +178,14 @@ async def mint_deprecated( # BEGIN BACKWARDS COMPATIBILITY < 0.15 # Mint expects "id" in outputs to know which keyset to use to sign them. - for output in payload.outputs: - if not output.id: - # use the deprecated version of the current keyset - output.id = ledger.keyset.duplicate_keyset_id + # use the deprecated version of the current keyset + assert ledger.keyset.duplicate_keyset_id + outputs: list[BlindedMessage] = [ + BlindedMessage( + id=o.id or ledger.keyset.duplicate_keyset_id, **o.dict(exclude={"id"}) + ) + for o in payload.outputs + ] # END BACKWARDS COMPATIBILITY < 0.15 # BEGIN: backwards compatibility < 0.12 where we used to lookup payments with payment_hash @@ -189,7 +194,7 @@ async def mint_deprecated( assert hash, "hash must be set." # END: backwards compatibility < 0.12 - promises = await ledger.mint(outputs=payload.outputs, quote_id=hash) + promises = await ledger.mint(outputs=outputs, quote_id=hash) blinded_signatures = PostMintResponse_deprecated(promises=promises) logger.trace(f"< POST /mint: {blinded_signatures}") @@ -221,15 +226,18 @@ async def melt_deprecated( logger.trace(f"> POST /melt: {payload}") # BEGIN BACKWARDS COMPATIBILITY < 0.14: add "id" to outputs if payload.outputs: - for output in payload.outputs: - if not output.id: - output.id = ledger.keyset.id + outputs: list[BlindedMessage] = [ + BlindedMessage(id=o.id or ledger.keyset.id, **o.dict(exclude={"id"})) + for o in payload.outputs + ] + else: + outputs = [] # END BACKWARDS COMPATIBILITY < 0.14 quote = await ledger.melt_quote( PostMeltQuoteRequest(request=payload.pr, unit="sat") ) preimage, change_promises = await ledger.melt( - proofs=payload.proofs, quote=quote.quote, outputs=payload.outputs + proofs=payload.proofs, quote=quote.quote, outputs=outputs ) resp = PostMeltResponse_deprecated( paid=True, preimage=preimage, change=change_promises @@ -290,12 +298,12 @@ async def split_deprecated( logger.trace(f"> POST /split: {payload}") assert payload.outputs, Exception("no outputs provided.") # BEGIN BACKWARDS COMPATIBILITY < 0.14: add "id" to outputs - if payload.outputs: - for output in payload.outputs: - if not output.id: - output.id = ledger.keyset.id + outputs: list[BlindedMessage] = [ + BlindedMessage(id=o.id or ledger.keyset.id, **o.dict(exclude={"id"})) + for o in payload.outputs + ] # END BACKWARDS COMPATIBILITY < 0.14 - promises = await ledger.split(proofs=payload.proofs, outputs=payload.outputs) + promises = await ledger.split(proofs=payload.proofs, outputs=outputs) if payload.amount: # BEGIN backwards compatibility < 0.13 diff --git a/cashu/wallet/wallet_deprecated.py b/cashu/wallet/wallet_deprecated.py index db5e927a..080a9925 100644 --- a/cashu/wallet/wallet_deprecated.py +++ b/cashu/wallet/wallet_deprecated.py @@ -8,6 +8,7 @@ from ..core.base import ( BlindedMessage, + BlindedMessage_Deprecated, BlindedSignature, CheckFeesRequest_deprecated, CheckFeesResponse_deprecated, @@ -271,7 +272,8 @@ async def mint_deprecated( Raises: Exception: If the minting fails """ - outputs_payload = PostMintRequest_deprecated(outputs=outputs) + outputs_deprecated = [BlindedMessage_Deprecated(**o.dict()) for o in outputs] + outputs_payload = PostMintRequest_deprecated(outputs=outputs_deprecated) def _mintrequest_include_fields(outputs: List[BlindedMessage]): """strips away fields from the model that aren't necessary for the /mint""" @@ -307,7 +309,14 @@ async def pay_lightning_deprecated( Accepts proofs and a lightning invoice to pay in exchange. """ logger.warning("Using deprecated API call: POST /melt") - payload = PostMeltRequest_deprecated(proofs=proofs, pr=invoice, outputs=outputs) + outputs_deprecated = ( + [BlindedMessage_Deprecated(**o.dict()) for o in outputs] + if outputs + else None + ) + payload = PostMeltRequest_deprecated( + proofs=proofs, pr=invoice, outputs=outputs_deprecated + ) def _meltrequest_include_fields(proofs: List[Proof]): """strips away fields from the model that aren't necessary for the /melt""" @@ -336,7 +345,10 @@ async def split_deprecated( ) -> List[BlindedSignature]: """Consume proofs and create new promises based on amount split.""" logger.warning("Using deprecated API call: Calling split. POST /split") - split_payload = PostSplitRequest_Deprecated(proofs=proofs, outputs=outputs) + outputs_deprecated = [BlindedMessage_Deprecated(**o.dict()) for o in outputs] + split_payload = PostSplitRequest_Deprecated( + proofs=proofs, outputs=outputs_deprecated + ) # construct payload def _splitrequest_include_fields(proofs: List[Proof]): @@ -403,7 +415,8 @@ async def restore_promises_deprecated( Asks the mint to restore promises corresponding to outputs. """ logger.warning("Using deprecated API call: POST /restore") - payload = PostMintRequest_deprecated(outputs=outputs) + outputs_deprecated = [BlindedMessage_Deprecated(**o.dict()) for o in outputs] + payload = PostMintRequest_deprecated(outputs=outputs_deprecated) resp = await self.httpx.post(join(self.url, "/restore"), json=payload.dict()) self.raise_on_error(resp) response_dict = resp.json() diff --git a/pyproject.toml b/pyproject.toml index 4c02790c..d3e7d289 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "cashu" -version = "0.15.2" +version = "0.15.3" description = "Ecash wallet and mint" authors = ["calle "] license = "MIT" diff --git a/setup.py b/setup.py index 501f1e1f..420b1673 100644 --- a/setup.py +++ b/setup.py @@ -13,7 +13,7 @@ setuptools.setup( name="cashu", - version="0.15.2", + version="0.15.3", description="Ecash wallet and mint", long_description=long_description, long_description_content_type="text/markdown", diff --git a/tests/test_mint_init.py b/tests/test_mint_init.py index 7bc45676..17263164 100644 --- a/tests/test_mint_init.py +++ b/tests/test_mint_init.py @@ -65,7 +65,7 @@ async def test_init_keysets_with_duplicates(ledger: Ledger): @pytest.mark.asyncio async def test_init_keysets_with_duplicates_via_settings(ledger: Ledger): ledger.keysets = {} - settings.mint_duplicate_keysets = True + settings.mint_duplicate_old_keysets = True await ledger.init_keysets() assert len(ledger.keysets) == 2 @@ -80,7 +80,7 @@ async def test_init_keysets_without_duplicates(ledger: Ledger): @pytest.mark.asyncio async def test_init_keysets_without_duplicates_via_settings(ledger: Ledger): ledger.keysets = {} - settings.mint_duplicate_keysets = False + settings.mint_duplicate_old_keysets = False await ledger.init_keysets() assert len(ledger.keysets) == 1 diff --git a/tests/test_wallet.py b/tests/test_wallet.py index 78195f6d..16d59c8d 100644 --- a/tests/test_wallet.py +++ b/tests/test_wallet.py @@ -355,7 +355,7 @@ async def test_duplicate_proofs_double_spent(wallet1: Wallet): doublespend = await wallet1.mint(64, id=invoice.id) await assert_err( wallet1.split(wallet1.proofs + doublespend, 20), - "Mint Error: Failed to set proofs pending.", + "Mint Error: duplicate proofs.", ) assert wallet1.balance == 64 assert wallet1.available_balance == 64