diff --git a/07.md b/07.md index 5f39785d..fe46d6b8 100644 --- a/07.md +++ b/07.md @@ -1,11 +1,31 @@ -NUT-07: Spendable check +NUT-07: Token state check ========================== -`optional` `author: calle` +`optional` `author: calle` `author: tb` --- -With the spendable check, wallets can ask the mint whether a specific token is already spent. This can be useful for different reasons. For example, when `Alice` prepares a token to be sent to `Carol`, she can mark these tokens in her database as *pending*. She can then, periodically or upon user input, check with the mint, if the token is still spendable or if it has been redeemed by `Carol` already (thus not spendable anymore). Another use case is for wallets that want to delete spent proofs from their database. Before deleting a proof, a wallet can check if the token is still spendable to be sure that they don't delete an unspent token by accident. +With the token state check, wallets can ask the mint whether a specific proof is already spent (`spendable`) and whether it is in-flight in a transaction (`pending`). Both of these attributes are returned as sepearte booleans. + +### Token states + +A wallet can internally represent the combination of these booleans the proof's `state`. A proof can either be `live` (`spendable == true && pending == false`), `burned` (`spendable == false && pending == (true || false)`), or `in-flight` (`spendable == true && pending == true`). + +- A proof is `live` if it has been minted but not spent yet. +- A proof is `burned` if it has been redeemed and its secret is in the list of spent secrets of the mint. +- A proof is `in-flight` if it is being processed in a transaction (in an ongoing payment). An `in-flight` proof cannot be used in another transaction until it is `live` again. + +**Important:** Before deleting spent proofs from their database, wallets **MUST** check if the proof is still `spendable` to make sure that they don't delete an unspent proof by accident. + +**Note:** Mints **MUST** remember which proofs are currently `pending` to avoid reuse of the same token in multiple concurrent transactions. This can be achieved with a mutex whose key is the proof's `secret`. + +## Use cases + +#### Example 1: Ecash transaction +When `Alice` prepares a token to be sent to `Carol`, she can mark these tokens in her database as *db:pending*. She can then, periodically or upon user input, check with the mint if the proof is still spendable (mint returns `True` for `spendable`) or if it has been redeemed by `Carol` already (mint retrns `False` for `spendable`). If the proof is not spendable anymore (and, thus, has been redeemed by `Carol`), she can safely delete the proof from her database. + +#### Example 2: Lightning payments +If `Alice` pays a very slow Lightning invoice (for example a HODL invoice) and closes her wallet in the meantime, the next time she comes online, she can check all proof marked as *db:pending* in her database to determine, whether the payment is still in flight (mint returns `True` for `pending` and `spendable`), it has succeeded (mint returns `False` for `pending` and `spendable`), or it has failed (mint returns `False` for `pending` and `True` for `spendable`). ## Example @@ -15,7 +35,7 @@ With the spendable check, wallets can ask the mint whether a specific token is a POST https://mint.host:3338/check ``` -With the data being of the form `CheckSpendableRequest`: +With the data being of the form `CheckStateRequest`: ```json { @@ -23,7 +43,7 @@ With the data being of the form `CheckSpendableRequest`: } ``` -`Proofs` is a list (array) of `Proof`s (see [NUT-0][00]). `Alice` CAN provide a full `Proof` but MUST provide at least the `secret` (which is the only thing that `Bob` needs to check whether the token has been spent). +`Proofs` is a list (array) of `Proof`s (see [NUT-0][00]). `Alice` CAN provide a full `Proof` but MUST provide at least the `secret` (which is the only thing that `Bob` needs to check whether the proof has been spent). With curl: @@ -43,15 +63,18 @@ curl -X POST https://mint.host:3338/check -d \ ``` **Response** of `Bob`: -`Bob` will respond with a `CheckSpendableResponse` +`Bob` will respond with a `CheckStateResponse` ```json { - "spendable": Array[bool] + "spendable": Array[bool], + "pending": Array[bool] } ``` -Where `[bool]` is an array of booleans indicating whether the provided `Proof` is still spendable. **Important:** The list of booleans MUST be in the same order as the proofs provided by `Alice` in the request. +Where `Array[bool]` is an array of booleans indicating whether the provided `Proof` is still spendable or pending. + +**Important:** The list of booleans **MUST** be in the same order as the proofs provided by `Alice` in the request. [00]: 00.md [01]: 01.md diff --git a/README.md b/README.md index 7ef82d7e..9e0662d3 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Wallets and mints `MUST` implement all mandatory specs and `CAN` implement optio ### Optional | # | Description | Wallets | Mints |--- | --- | --- | --- | -| [07][07] | Token spendable check | [Nutshell][py], [Feni][feni], [Cashu.Me][cashume], [Nutstash][ns] | [Nutshell][py], [Feni][feni], [LNbits] +| [07][07] | Token state check | [Nutshell][py], [Feni][feni], [Cashu.Me][cashume], [Nutstash][ns] | [Nutshell][py], [Feni][feni], [LNbits] | [08][08] | Overpaid Lightning fees | [Nutshell][py], [Feni][feni], [Cashu.Me][cashume], [Nutstash][ns] | [Nutshell][py], [LNbits] | [09][09] | Mint info | - | [Nutshell][py] | TBD | Multimint support | [Nutshell][py], [Cashu.Me][cashume], [Nutstash][ns] | N/A diff --git a/wallet/cashu_wallet_spec.md b/wallet/cashu_wallet_spec.md index ab2b520a..4f6a4650 100644 --- a/wallet/cashu_wallet_spec.md +++ b/wallet/cashu_wallet_spec.md @@ -99,9 +99,9 @@ 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 CheckSpendableRequest*] +- `Alice` constructs a JSON of the form `{"proofs" : [{"amount" : , "secret" : s, "C" : Z}, ...]}` from these (grouped) tokens (`CheckStateRequest`). - `Alice` sends them to the mint `Bob` via the endpoint `POST /check` with the JSON as the body of the request. -- `Alice` receives a JSON of the form `{"spendable" : [true, false, ...]}` where each boolean value corresponds to the proof she sent to the mint before in the same order. The value is `True` if the token has not been claimed yet and `False` if it has already been claimed. +- `Alice` receives a JSON of the form `{"spendable" : [true, false, ...], "pending": [false, false, ....]}` where each boolean value corresponds to the proof she sent to the mint before in the same order. The value is `True` if the token has not been claimed yet and `False` if it has already been claimed. - If `` is `False`, `Alice` removes the proof from her list of spendable proofs. ## 6 - Pay a Lightning invoice