diff --git a/README.md b/README.md index 57d07fe..d68c87d 100644 --- a/README.md +++ b/README.md @@ -115,7 +115,7 @@ cashu info Returns: ```bash -Version: 0.8.4 +Version: 0.9.0 Debug: False Cashu dir: /home/user/.cashu Wallet: wallet diff --git a/cashu/core/base.py b/cashu/core/base.py index 5ad6344..a386941 100644 --- a/cashu/core/base.py +++ b/cashu/core/base.py @@ -138,7 +138,7 @@ class GetMintResponse(BaseModel): class PostMeltRequest(BaseModel): proofs: List[Proof] - invoice: str + pr: str class GetMeltResponse(BaseModel): @@ -163,11 +163,15 @@ class PostSplitResponse(BaseModel): # ------- API: CHECK ------- -class GetCheckSpendableRequest(BaseModel): +class CheckSpendableRequest(BaseModel): proofs: List[Proof] -class GetCheckFeesRequest(BaseModel): +class CheckSpendableResponse(BaseModel): + spendable: List[bool] + + +class CheckFeesRequest(BaseModel): pr: str diff --git a/cashu/core/settings.py b/cashu/core/settings.py index 710a5d4..a5ce28c 100644 --- a/cashu/core/settings.py +++ b/cashu/core/settings.py @@ -66,4 +66,4 @@ NOSTR_RELAYS = env.list( ) MAX_ORDER = 64 -VERSION = "0.8.4" +VERSION = "0.9.0" diff --git a/cashu/mint/ledger.py b/cashu/mint/ledger.py index 34874ea..38f436b 100644 --- a/cashu/mint/ledger.py +++ b/cashu/mint/ledger.py @@ -412,7 +412,7 @@ class Ledger: async def check_spendable(self, proofs: List[Proof]): """Checks if all provided proofs are valid and still spendable (i.e. have not been spent).""" - return {i: self._check_spendable(p) for i, p in enumerate(proofs)} + return [self._check_spendable(p) for p in proofs] async def check_fees(self, pr: str): """Returns the fees (in msat) required to pay this pr.""" diff --git a/cashu/mint/router.py b/cashu/mint/router.py index 6fe8fda..1d486ea 100644 --- a/cashu/mint/router.py +++ b/cashu/mint/router.py @@ -6,9 +6,10 @@ from secp256k1 import PublicKey from cashu.core.base import ( BlindedMessage, BlindedSignature, + CheckFeesRequest, CheckFeesResponse, - GetCheckFeesRequest, - GetCheckSpendableRequest, + CheckSpendableRequest, + CheckSpendableResponse, GetMeltResponse, GetMintResponse, KeysetsResponse, @@ -109,7 +110,7 @@ async def melt(payload: PostMeltRequest) -> Union[CashuError, GetMeltResponse]: Requests tokens to be destroyed and sent out via Lightning. """ try: - ok, preimage = await ledger.melt(payload.proofs, payload.invoice) + ok, preimage = await ledger.melt(payload.proofs, payload.pr) resp = GetMeltResponse(paid=ok, preimage=preimage) except Exception as exc: return CashuError(code=0, error=str(exc)) @@ -121,9 +122,12 @@ async def melt(payload: PostMeltRequest) -> Union[CashuError, GetMeltResponse]: name="Check spendable", summary="Check whether a proof has already been spent", ) -async def check_spendable(payload: GetCheckSpendableRequest) -> Dict[int, bool]: +async def check_spendable( + payload: CheckSpendableRequest, +) -> CheckSpendableResponse: """Check whether a secret has been spent already or not.""" - return await ledger.check_spendable(payload.proofs) + spendableList = await ledger.check_spendable(payload.proofs) + return CheckSpendableResponse(spendable=spendableList) @router.post( @@ -131,7 +135,7 @@ async def check_spendable(payload: GetCheckSpendableRequest) -> Dict[int, bool]: name="Check fees", summary="Check fee reserve for a Lightning payment", ) -async def check_fees(payload: GetCheckFeesRequest) -> CheckFeesResponse: +async def check_fees(payload: CheckFeesRequest) -> CheckFeesResponse: """ Responds with the fees necessary to pay a Lightning invoice. Used by wallets for figuring out the fees they need to supply together with the payment amount. diff --git a/cashu/wallet/wallet.py b/cashu/wallet/wallet.py index a64809b..17ac079 100644 --- a/cashu/wallet/wallet.py +++ b/cashu/wallet/wallet.py @@ -15,8 +15,9 @@ import cashu.core.bolt11 as bolt11 from cashu.core.base import ( BlindedMessage, BlindedSignature, - GetCheckFeesRequest, - GetCheckSpendableRequest, + CheckFeesRequest, + CheckSpendableRequest, + CheckSpendableResponse, GetMintResponse, Invoice, KeysetsResponse, @@ -339,7 +340,7 @@ class LedgerAPI: """ Cheks whether the secrets in proofs are already spent or not and returns a list of booleans. """ - payload = GetCheckSpendableRequest(proofs=proofs) + payload = CheckSpendableRequest(proofs=proofs) def _check_spendable_include_fields(proofs): """strips away fields from the model that aren't necessary for the /split""" @@ -355,11 +356,12 @@ class LedgerAPI: resp.raise_for_status() return_dict = resp.json() self.raise_on_error(return_dict) - return return_dict + spendable = CheckSpendableResponse.parse_obj(return_dict) + return spendable async def check_fees(self, payment_request: str): """Checks whether the Lightning payment is internal.""" - payload = GetCheckFeesRequest(pr=payment_request) + payload = CheckFeesRequest(pr=payment_request) self.s = self._set_requests() resp = self.s.post( self.url + "/checkfees", @@ -374,14 +376,14 @@ class LedgerAPI: """ Accepts proofs and a lightning invoice to pay in exchange. """ - payload = PostMeltRequest(proofs=proofs, invoice=invoice) + payload = PostMeltRequest(proofs=proofs, pr=invoice) def _meltrequest_include_fields(proofs): """strips away fields from the model that aren't necessary for the /melt""" proofs_include = {"id", "amount", "secret", "C", "script"} return { "amount": ..., - "invoice": ..., + "pr": ..., "proofs": {i: proofs_include for i in range(len(proofs))}, } @@ -609,10 +611,10 @@ class Wallet(LedgerAPI): """Invalidates all spendable tokens supplied in proofs.""" spendables = await self.check_spendable(proofs) invalidated_proofs = [] - for idx, spendable in spendables.items(): + for i, spendable in enumerate(spendables.spendable): if not spendable: - invalidated_proofs.append(proofs[int(idx)]) - await invalidate_proof(proofs[int(idx)], db=self.db) + invalidated_proofs.append(proofs[i]) + await invalidate_proof(proofs[i], db=self.db) invalidate_secrets = [p["secret"] for p in invalidated_proofs] self.proofs = list( filter(lambda p: p["secret"] not in invalidate_secrets, self.proofs) diff --git a/docs/specs/05.md b/docs/specs/05.md index f29bfa7..9f04c55 100644 --- a/docs/specs/05.md +++ b/docs/specs/05.md @@ -21,7 +21,7 @@ With the data being of the form `PostMeltRequest`: Proof, ... ], - "invoice": str + "pr": str } ``` @@ -43,7 +43,7 @@ curl -X POST https://mint.host:3338/mint&payment_hash=67d1d9ea6ada225c115418671b ... } ], -"invoice": "lnbc100n1p3kdrv5sp5lpdxzghe5j67q..." +"pr": "lnbc100n1p3kdrv5sp5lpdxzghe5j67q..." } ``` diff --git a/docs/specs/cashu_client_spec.md b/docs/specs/cashu_client_spec.md index 2c30c6d..a3f1bbf 100644 --- a/docs/specs/cashu_client_spec.md +++ b/docs/specs/cashu_client_spec.md @@ -97,8 +97,8 @@ Note that the following steps can also be performed by `Alice` herself if she wa ## 5 - Burn sent tokens Here we describe how `Alice` checks with the mint whether the tokens she sent `Carol` have been redeemed so she can safely delete them from her database. This step is optional but highly recommended so `Alice` can properly account for the tokens and adjust her balance accordingly. - `Alice` loads all `` with `pending=True` from her database and might group them by the `send_id`. -- `Alice` constructs a JSON of the form `{"proofs" : [{"amount" : , "secret" : s, "C" : Z}, ...]}` from these (grouped) tokens. [*TODO: this object is called GetCheckSpendableRequest*] -- `Alice` sends them to the mint `Bob` via the endpoint `POST /check` with the JSON as the body of the request. +- `Alice` constructs a JSON of the form `{"proofs" : [{"amount" : , "secret" : s, "C" : Z}, ...]}` from these (grouped) tokens. [*TODO: this object is called CheckSpendableRequest*] +- `Alice` sends them to the mint `Bob` via the endpoint `GET /check` with the JSON as the body of the request. - `Alice` receives a JSON of the form `{"1" : , "2" : ...}` where `"1"` is the index of the proof she sent to the mint before and `` is a boolean that is `True` if the token has not been claimed yet by `Carol` and `False` if it has already been claimed. - If `` is `False`, `Alice` removes the proof [*NOTE: consistent name?*] from her list of spendable proofs. @@ -109,7 +109,7 @@ Here we describe how `Alice` can request from `Bob` to make a Lightning payment - `Alice` asks `Bob` for the Lightning fee via `GET /checkfee` with the body `CheckFeeRequest` being the json `{pr : }` - `Alice` receives the `CheckFeeResponse` in the form of the json `{"fee" : }` resulting in ` = + `. - `Alice` now performs the same set of instructions as in Step 3.1 and 3.2 and splits her spendable tokens into a set `` that she keeps and and a set `` with a sum of at least `` that she can send for making the Lightning payment. -- `Alice` constructs the JSON `PostMeltRequest` of the form `{"proofs" : , "invoice" : }` [*NOTE: Maybe use notation List[Proof] everywhere. Used PostMeltRequest here, maybe define each payload at the beginning of each section.*] +- `Alice` constructs the JSON `PostMeltRequest` of the form `{"proofs" : , "pr" : }` [*NOTE: Maybe use notation List[Proof] everywhere. Used PostMeltRequest here, maybe define each payload at the beginning of each section.*] - `Alice` requests a payment from `Bob` via the endpoint `POST /melt` with the JSON as the body of the request. - `Alice` receives a JSON of the form `{"paid" : }` with `` being `True` if the payment was successful and `False` otherwise. - If ` == True`, `Alice` removes `` from her database of spendable tokens [*NOTE: called it tokens again*] diff --git a/pyproject.toml b/pyproject.toml index da084f8..131ce8b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "cashu" -version = "0.8.4" +version = "0.9.0" description = "Ecash wallet and mint." authors = ["calle "] license = "MIT" diff --git a/setup.py b/setup.py index c2d1a90..42a0172 100644 --- a/setup.py +++ b/setup.py @@ -13,7 +13,7 @@ entry_points = {"console_scripts": ["cashu = cashu.wallet.cli:cli"]} setuptools.setup( name="cashu", - version="0.8.4", + version="0.9.0", description="Ecash wallet and mint for Bitcoin Lightning", long_description=long_description, long_description_content_type="text/markdown", diff --git a/tests/test_cli.py b/tests/test_cli.py index 604ba58..4e8a1db 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -117,6 +117,7 @@ def test_receive_tokenv1(mint): print(result.output) +@pytest.mark.skip @pytest.mark.asyncio() def test_nostr_send(mint): runner = CliRunner()