diff --git a/.github/workflows/prettier.yml b/.github/workflows/prettier.yml new file mode 100644 index 00000000..27656ffd --- /dev/null +++ b/.github/workflows/prettier.yml @@ -0,0 +1,22 @@ +name: prettier + +on: [push, pull_request] + +jobs: + prettier: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: "20" + + - name: Install dependencies + run: npm install -g prettier + + - name: Run Prettier check + run: npx prettier --check . diff --git a/00.md b/00.md index 6c38c531..c4d714bd 100644 --- a/00.md +++ b/00.md @@ -1,5 +1,4 @@ -NUT-00: Notation, Utilization, and Terminology -========================== +# NUT-00: Notation, Utilization, and Terminology `mandatory` @@ -11,8 +10,6 @@ This document details the notation and models used throughout the specification - Receiving user: `Carol` - Mint: `Bob` - - # Blind Diffie-Hellmann key exchange (BDHKE) ## Variables @@ -37,6 +34,7 @@ This document details the notation and models used throughout the specification Deterministically maps a message to a public key point on the secp256k1 curve, utilizing a domain separator to ensure uniqueness. `Y = PublicKey('02' || SHA256(msg_hash || counter))` where `msg_hash` is `SHA256(DOMAIN_SEPARATOR || x)` + - `Y` derived public key - `DOMAIN_SEPARATOR` constant byte string `b"Secp256k1_HashToCurve_Cashu_"` - `x` message to hash @@ -44,13 +42,13 @@ Deterministically maps a message to a public key point on the secp256k1 curve, u ## Protocol -- Mint `Bob` publishes public key `K = kG` +- Mint `Bob` publishes public key `K = kG` - `Alice` picks secret `x` and computes `Y = hash_to_curve(x)` - `Alice` sends to `Bob`: `B_ = Y + rG` with `r` being a random blinding factor (**blinding**) - `Bob` sends back to `Alice` blinded key: `C_ = kB_` (these two steps are the DH key exchange) (**signing**) - `Alice` can calculate the unblinded key as `C_ - rK = kY + krG - krG = kY = C` (**unblinding**) - Alice can take the pair `(x, C)` as a token and can send it to `Carol`. -- `Carol` can send `(x, C)` to `Bob` who then checks that `k*hash_to_curve(x) == C` (**verification**), and if so treats it as a valid spend of a token, adding `x` to the list of spent secrets. +- `Carol` can send `(x, C)` to `Bob` who then checks that `k*hash_to_curve(x) == C` (**verification**), and if so treats it as a valid spend of a token, adding `x` to the list of spent secrets. ## 0.1 - Models @@ -66,7 +64,7 @@ An encrypted ("blinded") secret and an amount is sent from `Alice` to `Bob` for } ``` - `amount` is the value for the requested `BlindSignature`, `id` is the requested keyset ID from which we expect a signature, and `B_` is the blinded secret message generated by `Alice`. An array `[BlindedMessage]` is also referred to as `BlindedMessages`. +`amount` is the value for the requested `BlindSignature`, `id` is the requested keyset ID from which we expect a signature, and `B_` is the blinded secret message generated by `Alice`. An array `[BlindedMessage]` is also referred to as `BlindedMessages`. ### `BlindSignature` @@ -84,11 +82,11 @@ A `BlindSignature` is sent from `Bob` to `Alice` after [minting tokens][04] or a ### `Proof` -A `Proof` is also called an input and is generated by `Alice` from a `BlindSignature` it received. An array `[Proof]` is called `Proofs`. `Alice` sends `Proofs` to `Bob` for [melting tokens][05]. [Serialized](#serialization-of-tokens) `Proofs` can also be sent from `Alice` to `Carol`. Upon receiving the token, `Carol` deserializes it and requests a [swap][03] from `Bob` to receive new `Proofs`. +A `Proof` is also called an input and is generated by `Alice` from a `BlindSignature` it received. An array `[Proof]` is called `Proofs`. `Alice` sends `Proofs` to `Bob` for [melting tokens][05]. [Serialized](#serialization-of-tokens) `Proofs` can also be sent from `Alice` to `Carol`. Upon receiving the token, `Carol` deserializes it and requests a [swap][03] from `Bob` to receive new `Proofs`. ```json { - "amount": int, + "amount": int, "id": hex_str, "secret": str, "C": hex_str, @@ -100,6 +98,7 @@ A `Proof` is also called an input and is generated by `Alice` from a `BlindSigna ## 0.2 - Protocol ### Errors + In case of an error, mints respond with the HTTP status code `400` and include the following data in their response: ```json @@ -108,6 +107,7 @@ In case of an error, mints respond with the HTTP status code `400` and include t "code": 1337 } ``` + Here, `detail` is the error message, and `code` is the error code. Error codes are to be defined in the documents concerning the use of a certain API endpoint. ## 0.3 - Methods @@ -134,9 +134,10 @@ cashu:cashuAeyJwcm9vZn... ### V3 tokens -> *V3 tokens are deprecated and the use of the more space-efficient V4 tokens is encouraged.* +> _V3 tokens are deprecated and the use of the more space-efficient V4 tokens is encouraged._ ##### Version + This token format has the `[version]` value `A`. ##### Format @@ -166,12 +167,12 @@ The deserialized `base64_token_json` is "memo": str } ``` -`mint` is the mint URL. The mint URL must be stripped of any trailing slashes (`/`). `Proofs` is an array of `Proof` objects. The next two elements are only for displaying the receiving user appropriate information: `unit` is the currency unit of the token keysets (see [Keysets][01] for supported units), and `memo` is an optional text memo from the sender. +`mint` is the mint URL. The mint URL must be stripped of any trailing slashes (`/`). `Proofs` is an array of `Proof` objects. The next two elements are only for displaying the receiving user appropriate information: `unit` is the currency unit of the token keysets (see [Keysets][01] for supported units), and `memo` is an optional text memo from the sender. ##### Example -Below is a TokenV3 JSON before `base64_urlsafe` serialization. +Below is a TokenV3 JSON before `base64_urlsafe` serialization. ```json { @@ -200,15 +201,17 @@ Below is a TokenV3 JSON before `base64_urlsafe` serialization. ``` When serialized, this becomes: + ``` cashuAeyJ0b2tlbiI6W3sibWludCI6Imh0dHBzOi8vODMzMy5zcGFjZTozMzM4IiwicHJvb2ZzIjpbeyJhbW91bnQiOjIsImlkIjoiMDA5YTFmMjkzMjUzZTQxZSIsInNlY3JldCI6IjQwNzkxNWJjMjEyYmU2MWE3N2UzZTZkMmFlYjRjNzI3OTgwYmRhNTFjZDA2YTZhZmMyOWUyODYxNzY4YTc4MzciLCJDIjoiMDJiYzkwOTc5OTdkODFhZmIyY2M3MzQ2YjVlNDM0NWE5MzQ2YmQyYTUwNmViNzk1ODU5OGE3MmYwY2Y4NTE2M2VhIn0seyJhbW91bnQiOjgsImlkIjoiMDA5YTFmMjkzMjUzZTQxZSIsInNlY3JldCI6ImZlMTUxMDkzMTRlNjFkNzc1NmIwZjhlZTBmMjNhNjI0YWNhYTNmNGUwNDJmNjE0MzNjNzI4YzcwNTdiOTMxYmUiLCJDIjoiMDI5ZThlNTA1MGI4OTBhN2Q2YzA5NjhkYjE2YmMxZDVkNWZhMDQwZWExZGUyODRmNmVjNjlkNjEyOTlmNjcxMDU5In1dfV0sInVuaXQiOiJzYXQiLCJtZW1vIjoiVGhhbmsgeW91LiJ9 ``` ### V4 tokens -V4 tokens are a space-efficient way of serializing tokens using the CBOR binary format. All field are single characters and hex strings are encoded in binary. V4 tokens can only hold proofs from a single mint. +V4 tokens are a space-efficient way of serializing tokens using the CBOR binary format. All field are single characters and hex strings are encoded in binary. V4 tokens can only hold proofs from a single mint. ##### Version + This token format has the `[version]` value `B`. ##### Format @@ -227,7 +230,7 @@ The deserialized `base64_token_cbor` is a JSON of the same form as a TokenV4 but { "m": str, // mint URL "u": str, // unit - "d": str , // memo + "d": str , // memo "t": [ { "i": bytes, // keyset ID @@ -250,15 +253,16 @@ The deserialized `base64_token_cbor` is a JSON of the same form as a TokenV4 but ], } ``` -`m` is the mint URL. The mint URL must be stripped of any trailing slashes (`/`). `u` is the currency unit of the token keysets (see [Keysets][01] for supported units), and `d` is an optional text memo from the sender. + +`m` is the mint URL. The mint URL must be stripped of any trailing slashes (`/`). `u` is the currency unit of the token keysets (see [Keysets][01] for supported units), and `d` is an optional text memo from the sender. `i` is the keyset ID of the profs in `p`, which is an array of `Proof` objects without the `id` field. We extracted the keyset ID `id` from each proof and grouped all proofs by their keyset ID `i` one level above (in `p`). -Note that all fields of the `bytes` type encode hex strings in the original representation of `Proof`'s. +Note that all fields of the `bytes` type encode hex strings in the original representation of `Proof`'s. ##### Example -Below is a TokenV4 JSON before CBOR and `base64_urlsafe` serialization. +Below is a TokenV4 JSON before CBOR and `base64_urlsafe` serialization. ```json { @@ -302,7 +306,6 @@ We serialize this JSON using CBOR which can be seen [here](https://cbor.nemo157. cashuBo2F0gqJhaUgA_9SLj17PgGFwgaNhYQFhc3hAYWNjMTI0MzVlN2I4NDg0YzNjZjE4NTAxNDkyMThhZjkwZjcxNmE1MmJmNGE1ZWQzNDdlNDhlY2MxM2Y3NzM4OGFjWCECRFODGd5IXVW-07KaZCvuWHk3WrnnpiDhHki6SCQh88-iYWlIAK0mjE0fWCZhcIKjYWECYXN4QDEzMjNkM2Q0NzA3YTU4YWQyZTIzYWRhNGU5ZjFmNDlmNWE1YjRhYzdiNzA4ZWIwZDYxZjczOGY0ODMwN2U4ZWVhY1ghAjRWqhENhLSsdHrr2Cw7AFrKUL9Ffr1XN6RBT6w659lNo2FhAWFzeEA1NmJjYmNiYjdjYzY0MDZiM2ZhNWQ1N2QyMTc0ZjRlZmY4YjQ0MDJiMTc2OTI2ZDNhNTdkM2MzZGNiYjU5ZDU3YWNYIQJzEpxXGeWZN5qXSmJjY8MzxWyvwObQGr5G1YCCgHicY2FtdWh0dHA6Ly9sb2NhbGhvc3Q6MzMzOGF1Y3NhdA ``` - [00]: 00.md [01]: 01.md [02]: 02.md diff --git a/01.md b/01.md index 364f5cbe..526831dc 100644 --- a/01.md +++ b/01.md @@ -1,7 +1,6 @@ -NUT-01: Mint public key exchange -========================== +# NUT-01: Mint public key exchange -`mandatory` +`mandatory` --- @@ -9,13 +8,12 @@ This document outlines the exchange of the public keys of the mint `Bob` with th ## Description -Wallet user `Alice` receives public keys from mint `Bob` via `GET /v1/keys`. The set of all public keys for a set of amounts is called a *keyset*. +Wallet user `Alice` receives public keys from mint `Bob` via `GET /v1/keys`. The set of all public keys for a set of amounts is called a _keyset_. The mint responds only with its `active` keysets. Keyset are `active` if the mint will sign promises with it. The mint will accept tokens from inactive keysets as inputs but will not sign with them for new outputs. The `active` keysets can change over time, for example due to key rotation. A list of all keysets, active and inactive, can be requested separately (see [NUT-02][02]). Note that a mint can support multiple keysets at the same time but will only respond with the active keysets on the endpoint `GET /v1/keys`. A wallet can ask for the keys of a specific (active or inactive) keyset via the endpoint `GET /v1/keys/{keyset_id}` (see [NUT-02][02]). - ## Keyset generation Keysets are generated by the mint. The mint is free to use any key generation method they like. Each keyset is identified by its keyset `id` which can be computed by anyone from its public keys (see [NUT-02][02]). @@ -54,6 +52,7 @@ Response `GetKeysResponse` of `Bob`: ``` ## Example response + ```json { "keysets": [ diff --git a/02.md b/02.md index b4673078..64d726a1 100644 --- a/02.md +++ b/02.md @@ -49,10 +49,10 @@ def fees(inputs: List[Proof]) -> int: sum_fees += keysets[proof.id].input_fee_ppk return (sum_fees + 999) // 1000 ``` -Here, the `//` operator in `(sum_fees + 999) // 1000` denotes an integer division operator (aka floor division operator) that rounds down `sum_fees + 999` to the next lower integer. Alternatively, we could round up the sum using a floating point division with `ceil(sum_fees / 1000)` although it is not recommended to do so due to the non-deterministic behavior of floating point division. -Notice that since transactions can spend inputs from different keysets, the sum considers the fee for each `Proof` indexed by the keyset ID individually. +Here, the `//` operator in `(sum_fees + 999) // 1000` denotes an integer division operator (aka floor division operator) that rounds down `sum_fees + 999` to the next lower integer. Alternatively, we could round up the sum using a floating point division with `ceil(sum_fees / 1000)` although it is not recommended to do so due to the non-deterministic behavior of floating point division. +Notice that since transactions can spend inputs from different keysets, the sum considers the fee for each `Proof` indexed by the keyset ID individually. ### Deriving the keyset ID @@ -140,7 +140,6 @@ Here, `id` is the keyset ID, `unit` is the unit string (e.g. "sat") of the keyse } ``` - ## Requesting public keys for a specific keyset To receive the public keys of a specific keyset, a wallet can call the `GET /v1/keys/{keyset_id}` endpoint where `keyset_id` is the keyset ID. diff --git a/03.md b/03.md index c4b14778..946e33e6 100644 --- a/03.md +++ b/03.md @@ -1,5 +1,4 @@ -NUT-03: Swap tokens -========================== +# NUT-03: Swap tokens `mandatory` @@ -7,7 +6,7 @@ NUT-03: Swap tokens The swap operation is the most important component of the Cashu system. A swap operation consists of multiple inputs (`Proofs`) and outputs (`BlindedMessages`). Mints verify and invalidate the inputs and issue new promises (`BlindSignatures`). These are then used by the wallet to generate new `Proofs` (see [NUT-00][00]). -The swap operation can serve multiple use cases. The first use case is that `Alice` can use it to split her tokens to a target amount she needs to send to `Carol`, if she does not have the necessary amounts to compose the target amount in her wallet already. The second one is that `Carols`'s wallet can use it to receive tokens from `Alice` by sending them as inputs to the mint and receive new outputs in return. +The swap operation can serve multiple use cases. The first use case is that `Alice` can use it to split her tokens to a target amount she needs to send to `Carol`, if she does not have the necessary amounts to compose the target amount in her wallet already. The second one is that `Carols`'s wallet can use it to receive tokens from `Alice` by sending them as inputs to the mint and receive new outputs in return. ## Swap to send @@ -17,7 +16,6 @@ To make this more clear, we present an example of a typical case of sending toke Note: In order to preserve privacy around the amount that a client might want to send to another user and keep the rest as change, the client **SHOULD** ensure that the list requested outputs is ordered by amount in ascending order. As an example of what to avoid, a request for outputs expressed like so: `[16, 8, 2, 64, 8]` might imply the client is preparing a payment for 26 sat; the client should instead order the list like so: `[2, 8, 8, 16, 64]` to mitigate this privacy leak to the mint. - ## Swap to receive Another useful case for the swap operation follows up the example above where `Alice` has swapped her `Proofs` ready to be sent to `Carol`. `Carol` can receive these `Proofs` using the same operation by using them as inputs to invalidate them and request new outputs from `Bob`. Only if `Carol` has redeemed new outputs, `Alice` can't double-spend the `Proofs` anymore and the transaction is settled. To continue our example, `Carol` requests a swap with input `Proofs` worth `[32, 8]` to receive new outputs (of an arbitrary distribution) with the same total amount. @@ -44,7 +42,7 @@ With curl: ```bash curl -X POST https://mint.host:3338/v1/swap -d \ { - "inputs": + "inputs": [ { "amount": 2, @@ -59,7 +57,7 @@ curl -X POST https://mint.host:3338/v1/swap -d \ "outputs": [ { - "amount": 2, + "amount": 2, "id": "009a1f293253e41e", "B_": "02634a2c2b34bec9e8a4aba4361f6bf202d7fa2365379b0840afe249a7a9d71239" }, @@ -70,7 +68,7 @@ curl -X POST https://mint.host:3338/v1/swap -d \ } ``` -If successful, `Bob` will respond with a `PostSwapResponse` +If successful, `Bob` will respond with a `PostSwapResponse` ```json { diff --git a/04.md b/04.md index 892eeec4..edaf9044 100644 --- a/04.md +++ b/04.md @@ -1,19 +1,18 @@ -NUT-04: Mint tokens -========================== +# NUT-04: Mint tokens `mandatory` --- -Minting tokens is a two-step process: requesting a mint quote and minting new tokens. Here, we describe both steps. +Minting tokens is a two-step process: requesting a mint quote and minting new tokens. Here, we describe both steps. In the first request the wallet asks the mint for a quote for a specific `amount` and `unit` to mint, and the payment `method` to pay. The mint responds with a quote that includes a `quote` id and a payment `request`. The user pays the `request` and, if successful, requests minting of new tokens with the mint in a second request. The wallet includes the `quote` id and new `outputs` in the second request. -We limit this document to mint quotes of `unit="sat"` and `method="bolt11"` which requests a bolt11 Lightning invoice (typically generated by the mint to add Bitcoin to its reserves) to mint ecash denominated in Satoshis. +We limit this document to mint quotes of `unit="sat"` and `method="bolt11"` which requests a bolt11 Lightning invoice (typically generated by the mint to add Bitcoin to its reserves) to mint ecash denominated in Satoshis. # Mint quote -To request a mint quote, the wallet of `Alice` makes a `POST /v1/mint/quote/{method}` request where `method` is the payment method requested (here `bolt11`). +To request a mint quote, the wallet of `Alice` makes a `POST /v1/mint/quote/{method}` request where `method` is the payment method requested (here `bolt11`). ```http POST https://mint.host:3338/v1/mint/quote/bolt11 @@ -27,9 +26,10 @@ The wallet of `Alice` includes the following `PostMintQuoteBolt11Request` data i "unit": } ``` - with the requested `amount` and the `unit`. - - The mint `Bob` then responds with a `PostMintQuoteBolt11Response`: + +with the requested `amount` and the `unit`. + +The mint `Bob` then responds with a `PostMintQuoteBolt11Response`: ```json { @@ -40,14 +40,14 @@ The wallet of `Alice` includes the following `PostMintQuoteBolt11Request` data i } ``` -Where `quote` is the quote ID and `request` is the payment request to fulfill. `expiry` is the Unix timestamp until which the mint quote is valid. +Where `quote` is the quote ID and `request` is the payment request to fulfill. `expiry` is the Unix timestamp until which the mint quote is valid. `state` is an enum string field with possible values `"UNPAID"`, `"PAID"`, `"ISSUED"`: + - `"UNPAID"` means that the quote's request has not been paid yet. - `"PAID"` means that the request has been paid. - `"ISSUED"` means that the quote has already been issued. - Note: `quote` is a **unique and random** id generated by the mint to internally look up the payment state. `quote` **MUST** remain a secret between user and mint and **MUST NOT** be derivable from the payment request. A third party who knows the `quote` ID can front-run and steal the tokens that this operation mints. ## Example @@ -103,9 +103,10 @@ The wallet `Alice` includes the following `PostMintBolt11Request` data in its re "outputs": } ``` - with the `quote` being the quote ID from the previous step and `outputs` being `BlindedMessages` (see [NUT-00][00]) that the wallet requests signatures on whose sum is `amount` as requested in the quote. - - The mint `Bob` then responds with a `PostMintBolt11Response`: + +with the `quote` being the quote ID from the previous step and `outputs` being `BlindedMessages` (see [NUT-00][00]) that the wallet requests signatures on whose sum is `amount` as requested in the quote. + +The mint `Bob` then responds with a `PostMintBolt11Response`: ```json { @@ -138,7 +139,7 @@ curl -X POST https://mint.host:3338/v1/mint/bolt11 -H "Content-Type: application }' ``` -Response of `Bob`: +Response of `Bob`: ```json { @@ -166,22 +167,24 @@ Upon receiving the `BlindSignatures` from the mint `Bob`, the wallet of `Alice` ```json [ { - "id": "009a1f293253e41e", - "amount": 2, - "secret": "407915bc212be61a77e3e6d2aeb4c727980bda51cd06a6afc29e2861768a7837", - "C": "02bc9097997d81afb2cc7346b5e4345a9346bd2a506eb7958598a72f0cf85163ea" + "id": "009a1f293253e41e", + "amount": 2, + "secret": "407915bc212be61a77e3e6d2aeb4c727980bda51cd06a6afc29e2861768a7837", + "C": "02bc9097997d81afb2cc7346b5e4345a9346bd2a506eb7958598a72f0cf85163ea" }, { - "id": "009a1f293253e41e", - "amount": 8, - "secret": "fe15109314e61d7756b0f8ee0f23a624acaa3f4e042f61433c728c7057b931be", - "C": "029e8e5050b890a7d6c0968db16bc1d5d5fa040ea1de284f6ec69d61299f671059" + "id": "009a1f293253e41e", + "amount": 8, + "secret": "fe15109314e61d7756b0f8ee0f23a624acaa3f4e042f61433c728c7057b931be", + "C": "029e8e5050b890a7d6c0968db16bc1d5d5fa040ea1de284f6ec69d61299f671059" } ] ``` ## Settings -The settings for this nut indicate the supported method-unit pairs for minting and whether minting is disabled or not. They are part of the info response of the mint ([NUT-06][06]) which in this case reads + +The settings for this nut indicate the supported method-unit pairs for minting and whether minting is disabled or not. They are part of the info response of the mint ([NUT-06][06]) which in this case reads + ```json { "4": { @@ -197,6 +200,7 @@ The settings for this nut indicate the supported method-unit pairs for minting a `MintMethodSetting` indicates supported `method` and `unit` pairs and additional settings of the mint. `disabled` indicates whether this minting is disabled. `MintMethodSetting` is of the form: + ```json { "method": , @@ -205,6 +209,7 @@ The settings for this nut indicate the supported method-unit pairs for minting a "max_amount": } ``` + `min_amount` and `max_amount` indicate the minimum and maximum amount for an operation of this method-unit pair. Example `MintMethodSetting`: @@ -214,7 +219,7 @@ Example `MintMethodSetting`: "method": "bolt11", "unit": "sat", "min_amount": 0, - "max_amount": 10000 + "max_amount": 10000 } ``` diff --git a/05.md b/05.md index a14befc4..be55a414 100644 --- a/05.md +++ b/05.md @@ -1,22 +1,20 @@ -NUT-05: Melting tokens -========================== +# NUT-05: Melting tokens `mandatory` --- -Melting tokens is the opposite of minting tokens (see [NUT-04][04]). Like minting tokens, melting is a two-step process: requesting a melt quote and melting tokens. Here, we describe both steps. +Melting tokens is the opposite of minting tokens (see [NUT-04][04]). Like minting tokens, melting is a two-step process: requesting a melt quote and melting tokens. Here, we describe both steps. In the first request the wallet asks the mint for a quote for a `request` it wants paid by the mint and the `unit` the wallet would like to spend as inputs. The mint responds with a quote that includes a `quote` id and an `amount` the mint demands in the requested unit. For the method `bolt11`, the mint includes a `fee_reserve` field indicating the reserve fee for a Lightning payment. In the second request, the wallet includes the `quote` id and provides `inputs` that sum up to `amount+fee_reserve` in the first response. For the method `bolt11`, the wallet can also include `outputs` in order for the mint to return overpaid Lightning fees (see [NUT-08][08]). The mint responds with a payment status `paid` and a `proof` of payment. If the request included `outputs`, the mint may respond with `change` for the overpaid fees (see [NUT-08][08]). -We limit this document to mint quotes of `unit="sat"` and `method="bolt11"` which requests a bolt11 Lightning payment (typically paid by the mint from its Bitcoin reserves) using ecash denominated in Satoshis. - +We limit this document to mint quotes of `unit="sat"` and `method="bolt11"` which requests a bolt11 Lightning payment (typically paid by the mint from its Bitcoin reserves) using ecash denominated in Satoshis. # Melt quote -To request a melt quote, the wallet of `Alice` makes a `POST /v1/melt/quote/{method}` request where `method` is the payment method requested (here `bolt11`). +To request a melt quote, the wallet of `Alice` makes a `POST /v1/melt/quote/{method}` request where `method` is the payment method requested (here `bolt11`). ```http POST https://mint.host:3338/v1/melt/quote/bolt11 @@ -33,7 +31,7 @@ The wallet `Alice` includes the following `PostMeltQuoteBolt11Request` data in i Here, `request` is the bolt11 Lightning invoice to be paid and `unit` is the unit the wallet would like to pay with. - The mint `Bob` then responds with a `PostMeltQuoteBolt11Response`: +The mint `Bob` then responds with a `PostMeltQuoteBolt11Response`: ```json { @@ -45,9 +43,11 @@ Here, `request` is the bolt11 Lightning invoice to be paid and `unit` is the uni "payment_preimage": } ``` -Where `quote` is the quote ID, `amount` the amount that needs to be provided, and `fee_reserve` the additional fee reserve that is required. The mint expects `Alice` to include `Proofs` of *at least* `total_amount = amount + fee_reserve`. `expiry` is the Unix timestamp until which the melt quote is valid. `payment_preimage` is the bolt11 payment preimage in case of a successful payment. + +Where `quote` is the quote ID, `amount` the amount that needs to be provided, and `fee_reserve` the additional fee reserve that is required. The mint expects `Alice` to include `Proofs` of _at least_ `total_amount = amount + fee_reserve`. `expiry` is the Unix timestamp until which the melt quote is valid. `payment_preimage` is the bolt11 payment preimage in case of a successful payment. `state` is an enum string field with possible values `"UNPAID"`, `"PENDING"`, `"PAID"`: + - `"UNPAID"` means that the request has not been paid yet. - `"PENDING"` means that the request is currently being paid. - `"PAID"` means that the request has been paid successfully. @@ -72,7 +72,7 @@ Response of `Bob`: "amount": 10, "fee_reserve": 2, "state": "UNPAID", - "expiry": 1701704757, + "expiry": 1701704757 } ``` @@ -142,7 +142,7 @@ curl -X POST https://mint.host:3338/v1/melt/bolt11 -d \ }' ``` -Response `PostMeltQuoteBolt11Response` of `Bob`: +Response `PostMeltQuoteBolt11Response` of `Bob`: ```json { @@ -156,7 +156,9 @@ Response `PostMeltQuoteBolt11Response` of `Bob`: ``` ## Settings -The mint's settings for this nut indicate the supported method-unit pairs for melting. They are part of the info response of the mint ([NUT-06][06]) which in this case reads + +The mint's settings for this nut indicate the supported method-unit pairs for melting. They are part of the info response of the mint ([NUT-06][06]) which in this case reads + ```json { "5": { @@ -172,6 +174,7 @@ The mint's settings for this nut indicate the supported method-unit pairs for me `MeltMethodSetting` indicates supported `method` and `unit` pairs and additional settings of the mint. `disabled` indicates whether melting is disabled. `MeltMethodSetting` is of the form: + ```json { "method": , @@ -180,6 +183,7 @@ The mint's settings for this nut indicate the supported method-unit pairs for me "max_amount": } ``` + `min_amount` and `max_amount` indicate the minimum and maximum amount for an operation of this method-unit pair. Example `MeltMethodSetting`: @@ -189,7 +193,7 @@ Example `MeltMethodSetting`: "method": "bolt11", "unit": "sat", "min_amount": 100, - "max_amount": 10000 + "max_amount": 10000 } ``` diff --git a/06.md b/06.md index d48f8727..16ba1d6e 100644 --- a/06.md +++ b/06.md @@ -1,5 +1,4 @@ -NUT-06: Mint information -========================== +# NUT-06: Mint information `mandatory` @@ -81,15 +80,14 @@ With the mint's response being of the form `GetInfoResponse`: } ``` -- `name` is the name of the mint and should be recognizable. +- `name` is the name of the mint and should be recognizable. - `pubkey` is the hex pubkey of the mint. -- `version` is the implementation name and the version of the software running on this mint separated with a slash "/", -- `description` is a short description of the mint that can be shown in the wallet next to the mint's name. +- `version` is the implementation name and the version of the software running on this mint separated with a slash "/", +- `description` is a short description of the mint that can be shown in the wallet next to the mint's name. - `description_long` is a long description that can be shown in an additional field. - `contact` is an array of contact objects to reach the mint operator. A contact object consists of two fields. The `method` field denotes the contact method (like "email"), the `info` field denotes the identifier (like "contact@me.com"). -- `modt` is the message of the day that the wallet must display to the user. It should only be used to display important announcements to users, such as scheduled maintenances. -- `nuts` indicates each NUT specification that the mint supports and its settings. The settings are defined in each NUT separately. - +- `modt` is the message of the day that the wallet must display to the user. It should only be used to display important announcements to users, such as scheduled maintenances. +- `nuts` indicates each NUT specification that the mint supports and its settings. The settings are defined in each NUT separately. With curl: @@ -97,7 +95,6 @@ With curl: curl -X GET https://mint.host:3338/v1/info ``` - [00]: 00.md [01]: 01.md [02]: 02.md diff --git a/07.md b/07.md index 47cc329a..c2ef4440 100644 --- a/07.md +++ b/07.md @@ -1,5 +1,4 @@ -NUT-07: Token state check -========================== +# NUT-07: Token state check `optional` @@ -7,9 +6,10 @@ NUT-07: Token state check With the token state check, wallets can ask the mint whether a specific proof is already spent and whether it is in-flight in a transaction. Wallets can also request the witness data that was used to spend a proof. -### Token states +### Token states A proof can be in one of the following states + - A proof is `UNSPENT` if it has not been spent yet - A proof is `PENDING` if it is being processed in a transaction (in an ongoing payment). A `PENDING` proof cannot be used in another transaction until it is `live` again. - A proof is `SPENT` if it has been redeemed and its secret is in the list of spent secrets of the mint. @@ -20,11 +20,13 @@ A proof can be in one of the following states ## 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 *pending*. She can then, periodically or upon user input, check with the mint if the token is `UNSPENT` or whether it has been redeemed by `Carol` already, i.e., is `SPENT`. If the proof is not spendable anymore (and, thus, has been redeemed by `Carol`), she can safely delete the proof from her database. +#### Example 1: Ecash transaction + +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 `UNSPENT` or whether it has been redeemed by `Carol` already, i.e., is `SPENT`. 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`'s melt operation takes a long time to complete (for example if she requests a very slow Lightning payment) and she closes her wallet in the meantime, the next time she comes online, she can check all proofs marked as *pending* in her database to determine whether the payment is still in flight (mint returns `PENDING`), it has succeeded (mint returns `SPENT`), or it has failed (mint returns `UNSPENT`). +#### Example 2: Lightning payments + +If `Alice`'s melt operation takes a long time to complete (for example if she requests a very slow Lightning payment) and she closes her wallet in the meantime, the next time she comes online, she can check all proofs marked as _pending_ in her database to determine whether the payment is still in flight (mint returns `PENDING`), it has succeeded (mint returns `SPENT`), or it has failed (mint returns `UNSPENT`). ## Example @@ -46,7 +48,7 @@ Where the elements of the array in `Ys` are the hexadecimal representation of th **Response** of `Bob`: -`Bob` will respond with a `PostCheckStateResponse` +`Bob` will respond with a `PostCheckStateResponse` ```json { @@ -61,7 +63,7 @@ Where the elements of the array in `Ys` are the hexadecimal representation of th } ``` -- `Y` corresponds to the `Proof` checked in the request. +- `Y` corresponds to the `Proof` checked in the request. - `state` is an enum string field with possible values `"UNSPENT"`, `"PENDING"`, `"SPENT"` - `witness` is the serialized witness data that was used to spend the `Proof` if the token required it such as in the case of P2PK (see [NUT-11][11]). @@ -91,8 +93,7 @@ curl -X POST https://mint.host:3338/v1/checkstate -H 'Content-Type: application/ } ``` - -Where `Y` belongs to the provided `Proof` to check in the request, `state` indicates its state, and `witness` is the witness data that was potentially provided in a previous spend operation (can be empty). +Where `Y` belongs to the provided `Proof` to check in the request, `state` indicates its state, and `witness` is the witness data that was potentially provided in a previous spend operation (can be empty). [00]: 00.md [01]: 01.md diff --git a/08.md b/08.md index fc3201fc..abb8d0fc 100644 --- a/08.md +++ b/08.md @@ -1,17 +1,16 @@ -NUT-08: Lightning fee return -========================== +# NUT-08: Lightning fee return `optional`, `depends on: NUT-05` --- -This document describes how the overpaid Lightning fees are handled and extends [NUT-05][05] which describes melting tokens (i.e. paying a Lightning invoice). In short, a wallet includes *blank outputs* when paying a Lightning invoice which can be assigned a value by the mint if the user has overpaid Lightning fees. This can be the case due to the unpredictability of Lightning network fees. To solve this issue, we introduce so-called *blank outputs* which are blinded messages with an undetermined value. +This document describes how the overpaid Lightning fees are handled and extends [NUT-05][05] which describes melting tokens (i.e. paying a Lightning invoice). In short, a wallet includes _blank outputs_ when paying a Lightning invoice which can be assigned a value by the mint if the user has overpaid Lightning fees. This can be the case due to the unpredictability of Lightning network fees. To solve this issue, we introduce so-called _blank outputs_ which are blinded messages with an undetermined value. The problem is also described in [this gist](https://gist.github.com/callebtc/a6cc0bd2b6f70e081e478147c40fc578). ## Description -Before requesting a Lightning payment as described in [NUT-05][05], `Alice` produces a number of `BlindedMessage` which are similar to ordinary blinded messages but their value is yet to be determined by the mint `Bob` and are thus called *blank outputs*. The number of necessary blank outputs is `max(ceil(log2(fee_reserve)), 1)` which ensures that there is at least one output if there is any fee. If the `fee_reserve` is `0`, then the number of blank outputs is `0` as well. The blank outputs will contain the overpaid fees that will be returned by the mint to the wallet. +Before requesting a Lightning payment as described in [NUT-05][05], `Alice` produces a number of `BlindedMessage` which are similar to ordinary blinded messages but their value is yet to be determined by the mint `Bob` and are thus called _blank outputs_. The number of necessary blank outputs is `max(ceil(log2(fee_reserve)), 1)` which ensures that there is at least one output if there is any fee. If the `fee_reserve` is `0`, then the number of blank outputs is `0` as well. The blank outputs will contain the overpaid fees that will be returned by the mint to the wallet. This code calculates the number of necessary blank outputs in Python: @@ -28,13 +27,14 @@ def calculate_number_of_blank_outputs(fee_reserve_sat: int) -> int: The wallet wants to pay an invoice with `amount := 100 000 sat` and determines by asking the mint that `fee_reserve` is `1000 sats`. The wallet then provides `101 000 sat` worth of proofs and 10 blank `outputs` to make the payment (since `ceil(log2(1000))=ceil(9.96..)=10`). The mint pays the invoice and determines that the actual fee was `100 sat`, i.e, the overpaid fee to return is `fee_return = 900 sat`. The mint splits the amount `900` into summands of `2^n` which is `4, 128, 256, 512`. The mint inserts these amounts into the blank `outputs` it received form the wallet and generates 4 new promises. The mint then returns these `BlindSignature`s to the wallet together with the successful payment status. ## Wallet flow + The wallet asks the mint for the `fee_reserve` for paying a specific bolt11 invoice of value `amount` by calling `POST /v1/melt/quote` as described in [NUT-05][05]. The wallet then provides a `PostMeltBolt11Request` to `POST /v1/melt/bolt11` that has (1) proofs of the value `amount+fee_reserve`, (2) the bolt11 invoice to be paid, and finally, as a new entry, (3) a field `outputs` that has `n_blank_outputs` blinded messages that are generated before the payment attempt to receive potential overpaid fees back to her. ## Mint flow Here we describe how the mint generates `BlindSignature`s for the overpaid fees. The mint `Bob` returns in `PostMeltQuoteBolt11Response` the field `change` **ONLY IF** `Alice` has previously provided `outputs` for the change **AND** if the Lightning `actual_fees` were smaller than the `fee_reserve`. -If the `overpaid_fees = fee_reserve - actual_fees` is positive, `Bob` decomposes it to values of `2^n` (as in [NUT-00][00]) and then imprints them into the `blank_outputs` provided by `Alice`. +If the `overpaid_fees = fee_reserve - actual_fees` is positive, `Bob` decomposes it to values of `2^n` (as in [NUT-00][00]) and then imprints them into the `blank_outputs` provided by `Alice`. `Bob` then signs these blank outputs (now with the imprinted amounts) and thus generates `BlindSignature`s. `Bob` then returns a payment status to the wallet, and, in addition, all blind signatures it generated for the overpaid fees. @@ -57,6 +57,7 @@ With the data being of the form `PostMeltBolt11Request`: "outputs": <-- New } ``` + where the new `output` field carries the `BlindMessages`. The mint `Bob` then responds with a `PostMeltQuoteBolt11Response`: @@ -72,6 +73,7 @@ The mint `Bob` then responds with a `PostMeltQuoteBolt11Response`: "change": <-- New } ``` + where the new `change` field carries the returned `BlindSignature`s due to overpaid fees. ## Example @@ -120,11 +122,12 @@ If the mint has made a successful payment, it will respond the following. { "id": "009a1f293253e41e", "amount": 2, - "C_": "03c668f551855ddc792e22ea61d32ddfa6a45b1eb659ce66e915bf5127a8657be0", + "C_": "03c668f551855ddc792e22ea61d32ddfa6a45b1eb659ce66e915bf5127a8657be0" } ] } ``` + The field `change` is an array of `BlindSignatures` that account for the overpaid fees. Notice that the amount has been changed by the mint. `Alice` must take these and generate `Proofs` by unblinding them as described in [NUT-00][00] and as she does in [NUT-04][04] when minting new tokens. After generating the `Proofs`, `Alice` stores them in her database. [00]: 00.md diff --git a/09.md b/09.md index f240e888..04c7f4ee 100644 --- a/09.md +++ b/09.md @@ -1,5 +1,4 @@ -NUT-09: Restore signatures -========================== +# NUT-09: Restore signatures `optional` `used in: NUT-13` @@ -23,7 +22,7 @@ With the data being of the form `PostRestoreRequest`: } ``` -**Response** of `Bob`: +**Response** of `Bob`: The mint `Bob` then responds with a `PostRestoreResponse`. @@ -34,7 +33,7 @@ The mint `Bob` then responds with a `PostRestoreResponse`. } ``` -The returned arrays `outputs` and `signatures` are of the same length and for every entry `outputs[i]`, there is a corresponding entry `signatures[i]`. +The returned arrays `outputs` and `signatures` are of the same length and for every entry `outputs[i]`, there is a corresponding entry `signatures[i]`. [00]: 00.md [02]: 02.md diff --git a/10.md b/10.md index 6fecdbd0..99b131e9 100644 --- a/10.md +++ b/10.md @@ -1,11 +1,10 @@ -NUT-10: Spending conditions -========================== +# NUT-10: Spending conditions `optional` --- -An ordinary ecash token is a set of `Proofs` each with a random string `secret`. To spend such a token in a [swap][03] or a [melt][05] operation, wallets include `proofs` in their request each with a unique `secret`. To autorize a transaction, the mint requires that the `secret` has not been seen before. This is the most fundamental spending condition in Cashu, which ensures that a token can't be double-spent. +An ordinary ecash token is a set of `Proofs` each with a random string `secret`. To spend such a token in a [swap][03] or a [melt][05] operation, wallets include `proofs` in their request each with a unique `secret`. To autorize a transaction, the mint requires that the `secret` has not been seen before. This is the most fundamental spending condition in Cashu, which ensures that a token can't be double-spent. In this NUT, we define a well-known format of `secret` that can be used to express more complex spending conditions. These conditions need to be met before the mint authorizes a transaction. Note that the specific type of spending condition is not part of this document but will be explained in other documents. Here, we describe the structure of `secret` which is expressed as a JSON `Secret` with a specific format. @@ -14,6 +13,7 @@ Spending conditions are enforced by the mint which means that, upon encountering Caution: If the mint does not support spending conditions or a specific `kind` of spending condition, proofs may be treated as a regular anyone-can-spend tokens. Applications need to make sure to check whether the mint supports a specific `kind` of spending condition by checking the mint's [info][06] endpoint. ## Basic components + An ecash transaction, i.e., a [swap][03] or a [melt][05] operation, with a spending condition consists of the following components: - Inputs referring to the `Proofs` being spent @@ -33,7 +33,7 @@ The well-known `Secret` stored in `Proof.secret` is a JSON of the format: ```json [ -kind , +kind , { "nonce": , "data": , @@ -46,7 +46,7 @@ kind , - `nonce` is a unique random string - `data` expresses the spending condition specific to each kind - `tags` hold additional data committed to and can be used for feature extensions - + ## Examples Example use cases of this secret format are diff --git a/11.md b/11.md index 63584cb9..f6006664 100644 --- a/11.md +++ b/11.md @@ -1,5 +1,4 @@ -NUT-11: Pay to Public Key (P2PK) -========================== +# NUT-11: Pay to Public Key (P2PK) `optional`, `depends on: NUT-10` @@ -13,9 +12,9 @@ Caution: If the mint does not support this type of spending condition, proofs ma [NUT-10][10] Secret `kind: P2PK` -If for a `Proof`, `Proof.secret` is a `Secret` of kind `P2PK`, the proof must be unlocked by providing a witness `Proof.witness` and one or more valid signatures in the array `Proof.witness.signatures`. +If for a `Proof`, `Proof.secret` is a `Secret` of kind `P2PK`, the proof must be unlocked by providing a witness `Proof.witness` and one or more valid signatures in the array `Proof.witness.signatures`. -In the basic case, when spending a locked token, the mint requires one valid Schnorr signature in `Proof.witness.signatures` on `Proof.secret` by the public key in `Proof.Secret.data`. +In the basic case, when spending a locked token, the mint requires one valid Schnorr signature in `Proof.witness.signatures` on `Proof.secret` by the public key in `Proof.Secret.data`. To give a concrete example of the basic case, to mint a locked token we first create a P2PK `Secret` that reads: @@ -25,17 +24,12 @@ To give a concrete example of the basic case, to mint a locked token we first cr { "nonce": "859d4935c4907062a6297cf4e663e2835d90d97ecdd510745d32f6816323a41f", "data": "0249098aa8b9d2fbec49ff8598feb17b592b986e62319a4fa488a3dc36387157a7", - "tags": [ - [ - "sigflag", - "SIG_INPUTS" - ] - ] + "tags": [["sigflag", "SIG_INPUTS"]] } ] ``` -Here, `Secret.data` is the public key of the recipient of the locked ecash. We serialize this `Secret` to a string in `Proof.secret` and get a blind signature by the mint that is stored in `Proof.C` (see [NUT-03][03]]). +Here, `Secret.data` is the public key of the recipient of the locked ecash. We serialize this `Secret` to a string in `Proof.secret` and get a blind signature by the mint that is stored in `Proof.C` (see [NUT-03][03]]). The recipient who owns the private key of the public key `Secret.data` can spend this proof by providing a signature on the serialized `Proof.secret` string that is then added to `Proof.witness.signatures`: @@ -51,26 +45,27 @@ The recipient who owns the private key of the public key `Secret.data` can spend #### Signature scheme -To spend a token locked with `P2PK`, the spender needs to include signatures in the spent proofs. We use `libsecp256k1`'s serialized 64 byte Schnorr signatures on the SHA256 hash of the message to sign. The message to sign is the field `Proof.secret` in the inputs. If indicated by `Secret.tags.sigflag` in the inputs, outputs might also require signatures on the message `BlindedMessage.B_`. +To spend a token locked with `P2PK`, the spender needs to include signatures in the spent proofs. We use `libsecp256k1`'s serialized 64 byte Schnorr signatures on the SHA256 hash of the message to sign. The message to sign is the field `Proof.secret` in the inputs. If indicated by `Secret.tags.sigflag` in the inputs, outputs might also require signatures on the message `BlindedMessage.B_`. -An ecash spending operation like [swap][03] and [melt][05] can have multiple inputs and outputs. If we have more than one input or output, we provide signatures in each `Proof` and `BlindedMessage` individually. The inputs are the `Proofs` provided in the `inputs` field and the outputs are the `BlindedMessages` in the `outputs` field in the request body (see `PostMeltRequest` in [NUT-05][05] and `PostSwapRequest` in [NUT-03][03]). +An ecash spending operation like [swap][03] and [melt][05] can have multiple inputs and outputs. If we have more than one input or output, we provide signatures in each `Proof` and `BlindedMessage` individually. The inputs are the `Proofs` provided in the `inputs` field and the outputs are the `BlindedMessages` in the `outputs` field in the request body (see `PostMeltRequest` in [NUT-05][05] and `PostSwapRequest` in [NUT-03][03]). ### Tags + More complex spending conditions can be defined in the tags in `Proof.tags`. All tags are optional. Tags are arrays with two or more strings being `["key", "value1", "value2", ...]`. Supported tags are: - `sigflag: ` determines whether outputs have to be signed as well - `n_sigs: ` specifies the minimum number of valid signatures expected -- `pubkeys: ` are additional public keys that can provide signatures (*allows multiple entries*) +- `pubkeys: ` are additional public keys that can provide signatures (_allows multiple entries_) - `locktime: ` is the Unix timestamp of when the lock expires -- `refund: ` are optional refund public keys that can exclusively spend after `locktime` (*allows multiple entries*) +- `refund: ` are optional refund public keys that can exclusively spend after `locktime` (_allows multiple entries_) **Note:** The tag serialization type is `[, , ...]` but some tag values are `int`. Wallets and mints must cast types appropriately for de/serialization. #### Signature flags -Signature flags are defined in the tag `Secret.tags['sigflag']`. Currently, there are two signature flags. +Signature flags are defined in the tag `Secret.tags['sigflag']`. Currently, there are two signature flags. - `SIG_INPUTS` requires valid signatures on all inputs. It is the default signature flag and will be applied even if the `sigflag` tag is absent. - `SIG_ALL` requires valid signatures on all inputs and on all outputs. @@ -82,10 +77,12 @@ The signature flag `SIG_ALL` is enforced if at least one of the `Proofs` have th Signatures must be provided in the field `Proof.witness.signatures` for each `Proof` which is an input. If the signature flag `SIG_ALL` is enforced, signatures must also be provided for every output in its field `BlindedMessage.witness.signatures`. ##### Signed inputs + A `Proof` (an input) with a signature `P2PKWitness.signatures` on `secret` is the JSON (see [NUT-00][00]): + ```json { - "amount": , + "amount": , "secret": , "C": , "id": , @@ -96,6 +93,7 @@ A `Proof` (an input) with a signature `P2PKWitness.signatures` on `secret` is th The `secret` of each input is **signed as a string**. ##### Signed outputs + A `BlindedMessage` (an output) with a signature `P2PKWitness.signatures` on `B_` is the JSON (see [NUT-00][00]): ```json @@ -111,6 +109,7 @@ The `B_` of each output is **signed as bytes** which comes from the original hex ##### Witness format `P2PKWitness` is a serialized JSON string of the form + ```json { "signatures": ]> @@ -120,14 +119,17 @@ The `B_` of each output is **signed as bytes** which comes from the original hex The `signatures` are an array of signatures in hex. ### Multisig + If the tag `n_sigs` is a positive integer, the mint will also consider signatures from public keys specified in the `pubkeys` tag additional to the public key in `Secret.data`. If the number of valid signatures is greater or equal to the number specified in `n_sigs`, the transaction is valid. Expressed as an "n-of-m" scheme, `n = n_sigs` is the number of required signatures and `m = 1 ("data" field) + len(pubkeys tag)` is the number of public keys that could sign. ### Locktime + If the tag `locktime` is the unix time and the mint's local clock is greater than `locktime`, the `Proof` becomes spendable by anyone, except if the following condition is also true. Note: A `Proof` is considered spendable by anyone if it only requires a `secret` and a valid signature `C` to be spent (which is the default case). #### Refund public keys + If the `locktime` is in the past and a tag `refund` is present, the `Proof` is spendable only if a valid signature by one of the the `refund` pubkeys is provided in `Proof.witness.signatures` and, depending on the signature flag, in `BlindedMessage.witness.signatures`. #### Complex Example @@ -141,27 +143,18 @@ This is an example `secret` that locks a `Proof` with a Pay-to-Pubkey (P2PK) con "nonce": "da62796403af76c80cd6ce9153ed3746", "data": "033281c37677ea273eb7183b783067f5244933ef78d8c3f15b1a77cb246099c26e", "tags": [ - [ - "sigflag", - "SIG_ALL" - ], - [ - "n_sigs", - "2" - ], - [ - "locktime", - "1689418329" - ], + ["sigflag", "SIG_ALL"], + ["n_sigs", "2"], + ["locktime", "1689418329"], [ "refund", "033281c37677ea273eb7183b783067f5244933ef78d8c3f15b1a77cb246099c26e" - ], + ], [ "pubkeys", "02698c4e2b5f9534cd0687d87513c759790cf829aa5739184a3e3735471fbda904", "023192200a0cfd3867e48eb63b03ff599c7e46c8f4e41146b2d281173ca6c50c54" - ], + ] ] } ] @@ -170,6 +163,7 @@ This is an example `secret` that locks a `Proof` with a Pay-to-Pubkey (P2PK) con ## Use cases The following use cases are unlocked using P2PK: + - Publicly post locked ecash that can only be redeemed by the intended receiver - Final offline-receiver payments that can't be double-spent when combined with an offline signature check mechanism like DLEQ proofs - Receiver of locked ecash can defer and batch multiple mint round trips for receiving proofs (requires DLEQ) diff --git a/12.md b/12.md index d5775c7b..a78308b8 100644 --- a/12.md +++ b/12.md @@ -1,16 +1,17 @@ -NUT-12: Offline ecash signature validation -========================== +# NUT-12: Offline ecash signature validation `optional` --- -In this document, we present an extension of Cashu's crypto system to allow a user `Alice` to verify the mint `Bob`'s signature using only `Bob`'s public keys. We explain how another user `Carol` who receives ecash from `Alice` can execute the DLEQ proof as well. This is achieved using a Discrete Log Equality (DLEQ) proof. Previously, `Bob`'s signature could only be checked by himself using his own private keys ([NUT-00](00)). +In this document, we present an extension of Cashu's crypto system to allow a user `Alice` to verify the mint `Bob`'s signature using only `Bob`'s public keys. We explain how another user `Carol` who receives ecash from `Alice` can execute the DLEQ proof as well. This is achieved using a Discrete Log Equality (DLEQ) proof. Previously, `Bob`'s signature could only be checked by himself using his own private keys ([NUT-00](00)). # The DLEQ proof -The purpose of this DLEQ is to prove that the mint has used the same private key `a` for creating its public key `A` ([NUT-01](01)) and for signing the BlindedMessage `B'`. `Bob` returns the DLEQ proof additional to the blind signature `C'` for a mint or swap operation. + +The purpose of this DLEQ is to prove that the mint has used the same private key `a` for creating its public key `A` ([NUT-01](01)) and for signing the BlindedMessage `B'`. `Bob` returns the DLEQ proof additional to the blind signature `C'` for a mint or swap operation. The complete DLEQ proof reads + ``` # DLEQ Proof @@ -64,7 +65,7 @@ The mint produces these DLEQ proofs when returning `BlindSignature`'s in the res ``` -`e` and `s` are the DLEQ proof. +`e` and `s` are the DLEQ proof. ### User to user: DLEQ in `Proof` @@ -83,6 +84,7 @@ In order for `Alice` to communicate the DLEQ to another user `Carol`, we extend } } ``` + `e` and `s` are the challenge and response of the DLEQ proof returned by `Bob`, `r` is the blinding factor of `Alice` that was used to generate the `Proof`. `Alice` serializes these proofs like any other in a token (see [NUT-00][00]) to send it to another user `Carol`. ## Alice (minting user) verifies DLEQ proof @@ -96,6 +98,7 @@ e == hash(R1,R2,A,C') # must be True ``` Here, the variables are + - `A` – the public key `Bob` used to sign this Proof - `(e, s)` – the DLEQ proof returned by `Bob` - `B'` – `Alice`'s `BlindedMessage` @@ -103,7 +106,7 @@ Here, the variables are In order to execute the proof, `Alice` needs `e, s` that are returned in the `BlindSignature` by `Bob`. `Alice` further needs `B'` (the `BlindedMessage` `Alice` created and `Bob` signed) and `C'` (the blind signature in the `BlindSignature` response) from `Bob`, and `A` (the public key of `Bob` with which he signed the BlindedMessage). All these values are available to `Alice` during or after calling the mint and swap operations. -If a DLEQ proof is included in the mint's `BlindSignature` response, wallets **MUST** verify the DLEQ proof. +If a DLEQ proof is included in the mint's `BlindSignature` response, wallets **MUST** verify the DLEQ proof. ## Carol (another user) verifies DLEQ proof @@ -125,7 +128,7 @@ B' = Y + r*G R1 = ... (same as Alice) ``` -If a DLEQ proof is included in a received token, wallets **MUST** verify the proof. +If a DLEQ proof is included in a received token, wallets **MUST** verify the proof. [00]: 00.md [01]: 01.md diff --git a/13.md b/13.md index 7e641773..04655442 100644 --- a/13.md +++ b/13.md @@ -1,17 +1,16 @@ -NUT-13: Deterministic Secrets -========================== +# NUT-13: Deterministic Secrets `optional` `depends on: NUT-09` --- -In this document, we describe the process that allows wallets to recover their ecash balance with the help of the mint using a familiar 12 word seed phrase (mnemonic). This allows us to restore the wallet's previous state in case of a device loss or other loss of access to the wallet. The basic idea is that wallets that generate the ecash deterministically can regenerate the same tokens during a recovery process. For this, they ask the mint to reissue previously generated signatures using [NUT-09][09]. +In this document, we describe the process that allows wallets to recover their ecash balance with the help of the mint using a familiar 12 word seed phrase (mnemonic). This allows us to restore the wallet's previous state in case of a device loss or other loss of access to the wallet. The basic idea is that wallets that generate the ecash deterministically can regenerate the same tokens during a recovery process. For this, they ask the mint to reissue previously generated signatures using [NUT-09][09]. ## Deterministic secret derivation An ecash token, or a `Proof`, consists of a `secret` generated by the wallet, and a signature `C` generated by the wallet and the mint in collaboration. Here, we describe how wallets can deterministically generate the `secrets` and blinding factors `r` necessary to generate the signatures `C`. -The wallet generates a `private_key` derived from a 12-word [BIP39](https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki) `mnemonic` seed phrase that the user stores in a secure place. The wallet uses the `private_key`, to derive deterministic values for the `secret` and the blinding factors `r` for every new ecash token that it generates. +The wallet generates a `private_key` derived from a 12-word [BIP39](https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki) `mnemonic` seed phrase that the user stores in a secure place. The wallet uses the `private_key`, to derive deterministic values for the `secret` and the blinding factors `r` for every new ecash token that it generates. In order to do this, the wallet keeps track of a `counter_k` for each `keyset_k` it uses. The index `k` indicates that the wallet needs to keep track of a separate counter for each keyset `k` it uses. Typically, the wallet will need to keep track of multiple keysets for every mint it interacts with. `counter_k` is used to generate a [BIP32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki) derivation path which can then be used to derive `secret` and `r`. @@ -32,7 +31,7 @@ secret_derivation_path = `m/129372'/0'/{keyset_k_int}'/{counter_k}'/0` r_derivation_path = `m/129372'/0'/{keyset_id_k_int}'/{counter_k}'/1` ``` -Here, `{keyset_k_int}` and `{counter_k}` are the only variables that can change. `keyset_id_k_int` is an integer representation (see below) of the keyset ID the token is generated with. This means that the derivation path is unique for each keyset. Note that the coin type is always `0'`, independent of the unit of the ecash. +Here, `{keyset_k_int}` and `{counter_k}` are the only variables that can change. `keyset_id_k_int` is an integer representation (see below) of the keyset ID the token is generated with. This means that the derivation path is unique for each keyset. Note that the coin type is always `0'`, independent of the unit of the ecash. **Note:** For examples, see the [test vectors][tests]. @@ -45,11 +44,13 @@ The wallet starts with `counter_k := 0` upon encountering a new keyset and incre The integer representation `keyset_id_int` of a keyset is calculated from its [hexadecimal ID][02] which has a length of 8 bytes or 16 hex characters. First, we convert the hex string to a big-endian sequence of bytes. This value is then modulo reduced by `2^31 - 1` to arrive at an integer that is a unique identifier `keyset_id_int`. Example in Python: + ```python keyset_id_int = int.from_bytes(bytes.fromhex(keyset_id_hex), "big") % (2**31 - 1) ``` Example in JavaScript: + ```javascript keysetIdInt = BigInt(`0x${keysetIdHex}`) % BigInt(2 ** 31 - 1); ``` @@ -60,12 +61,12 @@ Using deterministic secret derivation, a user's wallet can regenerate the same ` The wallet takes the following steps during recovery: -1) Generate `secret` and `r` from `counter` and `keyset` -2) Generate `BlindedMessage` from `secret` -3) Obtain `BlindSignature` for `secret` from the mint -4) Unblind `BlindSignature` to `C` using `r` -5) Restore `Proof = (secret, C)` -6) Check if `Proof` is already spent +1. Generate `secret` and `r` from `counter` and `keyset` +2. Generate `BlindedMessage` from `secret` +3. Obtain `BlindSignature` for `secret` from the mint +4. Unblind `BlindSignature` to `C` using `r` +5. Restore `Proof = (secret, C)` +6. Check if `Proof` is already spent #### Generate `BlindedMessages` @@ -74,6 +75,7 @@ To generate the `BlindedMessages`, the wallet starts with a `counter := 0` and , ```python secret = bip32.get_privkey_from_path(secret_derivation_path).hex() ``` + The wallet similarly generates a blinding factor `r` from the `r_derivation_path`: ```python @@ -96,14 +98,15 @@ If the wallet used the restore endpoint [NUT-09][09] for regenerating the `Proof ### Restoring batches -Generally, the user won't remember the last state of `counter` when starting the recovery process. Therefore, wallets need to know how far they need to increment the `counter` during the restore process to be confident to have reached the most recent state. +Generally, the user won't remember the last state of `counter` when starting the recovery process. Therefore, wallets need to know how far they need to increment the `counter` during the restore process to be confident to have reached the most recent state. + +In short, following approach is recommended: -In short, following approach is recommended: - Restore `Proofs` in batches of 100 and increment `counter` - Repeat until three consecutive batches are returned empty - Reset `counter` to the value at the last successful restore + 1 -Wallets restore `Proofs` in batches of 100. The wallet starts with a `counter=0` and increments it for every `Proof` it generated during one batch. When the wallet begins restoring the first `Proofs`, it is likely that the first few batches will only contain spent `Proofs`. Eventually, the wallet will reach a `counter` that will result in unspent `Proofs` which it stores in its database. The wallet then continues to restore until *three successive batches are returned empty by the mint*. This is to be confident that the restore process did not miss any `Proofs` that might have been generated with larger gaps in the `counter` by the previous wallet that we are restoring. +Wallets restore `Proofs` in batches of 100. The wallet starts with a `counter=0` and increments it for every `Proof` it generated during one batch. When the wallet begins restoring the first `Proofs`, it is likely that the first few batches will only contain spent `Proofs`. Eventually, the wallet will reach a `counter` that will result in unspent `Proofs` which it stores in its database. The wallet then continues to restore until _three successive batches are returned empty by the mint_. This is to be confident that the restore process did not miss any `Proofs` that might have been generated with larger gaps in the `counter` by the previous wallet that we are restoring. [00]: 00.md [02]: 02.md diff --git a/14.md b/14.md index 9dc4c2f7..f3a07911 100644 --- a/14.md +++ b/14.md @@ -1,5 +1,4 @@ -NUT-14: Hashed Timelock Contracts (HTLCs) -========================== +# NUT-14: Hashed Timelock Contracts (HTLCs) `optional` `depends on: NUT-10` @@ -18,6 +17,7 @@ Caution: If the mint does not support this type of spending condition, proofs ma If for a `Proof`, `Proof.secret` is a `Secret` of kind `HTLC`, the hash of the lock is in `Proof.secret.data`. The preimage for unlocking the HTLC is in the witness `Proof.witness.preimage`. All additional tags from `P2PK` locks are used here as well, allowing us to add a locktime, signature flag, and use multisig (see [NUT-11][11]). Here is a concrete example of a `Secret` of kind `HTLC`: + ```json [ "HTLC", @@ -27,12 +27,9 @@ Here is a concrete example of a `Secret` of kind `HTLC`: "tags": [ [ "pubkeys", - "02698c4e2b5f9534cd0687d87513c759790cf829aa5739184a3e3735471fbda904", + "02698c4e2b5f9534cd0687d87513c759790cf829aa5739184a3e3735471fbda904" ], - [ - "locktime", - "1689418329" - ], + ["locktime", "1689418329"], [ "refund", "033281c37677ea273eb7183b783067f5244933ef78d8c3f15b1a77cb246099c26e" @@ -51,6 +48,7 @@ See [NUT-11][11] for a description of the signature scheme, the additional use o ##### Witness format `HTLCWitness` is a serialized JSON string of the form + ```json { "preimage": , @@ -70,4 +68,4 @@ See [NUT-11][11] for a description of the signature scheme, the additional use o [09]: 09.md [10]: 10.md [11]: 11.md -[12]: 12.md \ No newline at end of file +[12]: 12.md diff --git a/15.md b/15.md index 4b3efa7d..8418312c 100644 --- a/15.md +++ b/15.md @@ -1,5 +1,4 @@ -NUT-15: Partial multi-path payments -========================== +# NUT-15: Partial multi-path payments `optional` `depends on: NUT-05` @@ -42,6 +41,7 @@ Here, `request` is the bolt11 Lightning invoice to be paid, `unit` is the unit t The settings returned in the info endpoint ([NUT-06][06]) indicate that a mint supports this NUT. The mint MUST indicate each `method` and `unit` that supports `mpp`. It can indicate this in an array of objects for multiple `method` and `unit` pairs and the boolean flag `mpp` set to `true`. `MultipathPaymentSetting` is of the form: + ```json { [ @@ -49,7 +49,7 @@ The settings returned in the info endpoint ([NUT-06][06]) indicate that a mint s "method": , "unit": , "mpp": - }, + }, ... ] } @@ -70,7 +70,7 @@ Example `MultipathPaymentSetting`: "method": "bolt11", "unit": "usd", "mpp": true - }, + }, ] } } diff --git a/16.md b/16.md index 589ceeb8..366817c9 100644 --- a/16.md +++ b/16.md @@ -1,11 +1,10 @@ -NUT-16: Animated QR codes -========================== +# NUT-16: Animated QR codes `optional` --- -This document outlines how tokens should be displayed as QR codes for sending them between two wallets. +This document outlines how tokens should be displayed as QR codes for sending them between two wallets. ## Introduction @@ -20,7 +19,8 @@ If the serialized token is not too large (i.e. includes less than or equal to 2 If a token is too large to be displayed as a single QR code, we use animated QR codes are based on the [UR](https://developer.blockchaincommons.com/ur/) protocol. The sender produces an animated QR code from a serialized Cashu token. The receiver scans the animated QR code until the UR decoder is able to decode the token. ## Resources -- Blockchain commons [UR](https://developer.blockchaincommons.com/ur/) + +- Blockchain commons [UR](https://developer.blockchaincommons.com/ur/) - Typescript library [bc-ur](https://github.com/gandlafbtc/bc-ur) -[00]: 00.md \ No newline at end of file +[00]: 00.md diff --git a/17.md b/17.md index 7d61b174..37edaa33 100644 --- a/17.md +++ b/17.md @@ -39,6 +39,7 @@ All requests from the wallet to the mint are of the form of a `WsRequest`: "id": } ``` + `WsRequestMethod` is a enum of strings with the supported commands `"subscribe"` and `"unsubscribe"`: ```ts @@ -70,22 +71,21 @@ Here, `subId` is a unique uuid generated by the wallet and allows the client to enum SubscriptionKind { bolt11_melt_quote = "bolt11_melt_quote", bolt11_mint_quote = "bolt11_mint_quote", - proof_state = "proof_state" + proof_state = "proof_state", } ``` -The `filters` are an array of mint quote IDs ([NUT-04][04]), or melt quote IDs ([NUT-05][05]), or `Y`'s ([NUT-07][07]) of the corresponding object to receive updates from. +The `filters` are an array of mint quote IDs ([NUT-04][04]), or melt quote IDs ([NUT-05][05]), or `Y`'s ([NUT-07][07]) of the corresponding object to receive updates from. As an example, `filters` would be of the following form to subscribe for updates of three different mint quote IDs: + ```json -[ - "20385fc7245...", "d06667cda9b...", "e14d8ca96f..." -] +["20385fc7245...", "d06667cda9b...", "e14d8ca96f..."] ``` Note that `id` and `subId` are unrelated. The `subId` is the ID for each subscription, whereas `id` is part of the JSON-RPC spec and is an integer counter that must be incremented for every request sent over the websocket. -**Important:** If the subscription is accepted by the mint, the mint MUST first respond with the *current* state of the subscribed object and continue sending any further updates to it. +**Important:** If the subscription is accepted by the mint, the mint MUST first respond with the _current_ state of the subscribed object and continue sending any further updates to it. For example, if the wallet subscribes to a `Proof.Y` of a `Proof` that has not been spent yet, the mint will first respond with a `ProofState` with `state == "UNSPENT"`. If the wallet then spends this `Proof`, the mint would send a `ProofState` with `state == "PENDING"` and then one with `state == "SPENT"`. In total, the mint would send three notifications to the wallet. @@ -149,29 +149,64 @@ Here, the `id` corresponds to the `id` in the request (as part of the JSON-RPC s ``` ### Example: `ProofState` subscription + To subscribe to the `ProofState` of a `Proof`, the wallet establishes a websocket connection to `https://mint.com/v1/ws` and sends a `WsRequest` with a `filters` chosen to be the a `Proof.Y` value of the `Proof` (see [NUT-00][00]). Note that `filters` is an array meaning multiple subscriptions of the same `kind` can be made in the same request. Wallet: + ```json -{"jsonrpc": "2.0", "id": 0, "method": "subscribe", "params": {"kind": "proof_state", "filters": ["02e208f9a78cd523444aadf854a4e91281d20f67a923d345239c37f14e137c7c3d"], "subId": "Ua_IYvRHoCoF_wsZFlJ1m4gBDB--O0_6_n0zHg2T"}} +{ + "jsonrpc": "2.0", + "id": 0, + "method": "subscribe", + "params": { + "kind": "proof_state", + "filters": [ + "02e208f9a78cd523444aadf854a4e91281d20f67a923d345239c37f14e137c7c3d" + ], + "subId": "Ua_IYvRHoCoF_wsZFlJ1m4gBDB--O0_6_n0zHg2T" + } +} ``` The mint first responds with a `WsResponse` confirming that the subscription has been added. Mint: + ```json -{"jsonrpc": "2.0", "result": {"status": "OK", "subId": "Ua_IYvRHoCoF_wsZFlJ1m4gBDB--O0_6_n0zHg2T"}, "id": 0} +{ + "jsonrpc": "2.0", + "result": { + "status": "OK", + "subId": "Ua_IYvRHoCoF_wsZFlJ1m4gBDB--O0_6_n0zHg2T" + }, + "id": 0 +} ``` The mint immediately sends the current `ProofState` of the subscription as a `WsNotification`. Mint: + ```json -{"jsonrpc": "2.0", "method": "subscribe", "params": {"subId": "Ua_IYvRHoCoF_wsZFlJ1m4gBDB--O0_6_n0zHg2T", "payload": {"Y": "02e208f9a78cd523444aadf854a4e91281d20f67a923d345239c37f14e137c7c3d", "state": "UNSPENT", "witness": null}}} +{ + "jsonrpc": "2.0", + "method": "subscribe", + "params": { + "subId": "Ua_IYvRHoCoF_wsZFlJ1m4gBDB--O0_6_n0zHg2T", + "payload": { + "Y": "02e208f9a78cd523444aadf854a4e91281d20f67a923d345239c37f14e137c7c3d", + "state": "UNSPENT", + "witness": null + } + } +} ``` + While leaving the websocket connection open, the wallet then spends the ecash. The mint sends `WsNotification` updating the wallet about state changes of the `ProofState` accordingly: Mint: + ```json {"jsonrpc": "2.0", "method": "subscribe", "params": {"subId": "Ua_IYvRHoCoF_wsZFlJ1m4gBDB--O0_6_n0zHg2T", "payload": {"Y": "02e208f9a78cd523444aadf854a4e91281d20f67a923d345239c37f14e137c7c3d", "state": "PENDING"}}} @@ -181,8 +216,14 @@ Mint: The wallet then unsubscribes. Wallet: + ```json -{"jsonrpc": "2.0", "id": 1, "method": "unsubscribe", "params": {"subId": "Ua_IYvRHoCoF_wsZFlJ1m4gBDB--O0_6_n0zHg2T"}} +{ + "jsonrpc": "2.0", + "id": 1, + "method": "unsubscribe", + "params": { "subId": "Ua_IYvRHoCoF_wsZFlJ1m4gBDB--O0_6_n0zHg2T" } +} ``` ## Signaling Support via NUT-06 @@ -207,6 +248,7 @@ Mints signal websocket support via [NUT-06][06] using the following setting: Here, `commands` is an array of the commands that the mint supports. A mint that supports all commands would return `["bolt11_mint_quote", "bolt11_melt_quote", "proof_state"]`. Supported commands are given for each method-unit pair. Example: + ```json "nuts": { "17": { @@ -215,8 +257,8 @@ Example: "method": "bolt11", "unit": "sat", "commands": [ - "bolt11_mint_quote", - "bolt11_melt_quote", + "bolt11_mint_quote", + "bolt11_melt_quote", "proof_state" ] }, @@ -225,7 +267,6 @@ Example: } ``` - [00]: 00.md [01]: 01.md [02]: 02.md diff --git a/README.md b/README.md index 0099500e..dcf98981 100644 --- a/README.md +++ b/README.md @@ -3,57 +3,57 @@ These documents each specify parts of the Cashu protocol. Read the specifications for the legacy API [here](https://github.com/cashubtc/nuts/tree/74f26b81b6617db710fa1081eebc0c7203711213). ## Specifications + Wallets and mints `MUST` implement all mandatory specs and `CAN` implement optional specs. ### Mandatory -| NUT # | Description | -|----------|-----------------------------------| -| [00][00] | Cryptography and Models | -| [01][01] | Mint public keys | -| [02][02] | Keysets and fees | -| [03][03] | Swapping tokens | -| [04][04] | Minting tokens | -| [05][05] | Melting tokens | -| [06][06] | Mint info | +| NUT # | Description | +| -------- | ----------------------- | +| [00][00] | Cryptography and Models | +| [01][01] | Mint public keys | +| [02][02] | Keysets and fees | +| [03][03] | Swapping tokens | +| [04][04] | Minting tokens | +| [05][05] | Melting tokens | +| [06][06] | Mint info | ### Optional -| # | Description | Wallets | Mints | -| --- | --- | --- | --- | -| [07][07] | Token state check | [Nutshell][py], [Moksha][moksha], [Nutstash][ns], [cashu-ts][ts], [cdk-cli] | [Nutshell][py], [Moksha][moksha], [cdk-mintd] | -| [08][08] | Overpaid Lightning fees | [Nutshell][py], [Moksha][moksha], [Nutstash][ns], [cashu-ts][ts], [cdk-cli] | [Nutshell][py], [Moksha][moksha], [cdk-mintd] | -| [09][09] | Signature restore | [Nutshell][py], [cdk-cli], [cashu-ts][ts], [gonuts] | [Nutshell][py], [cdk-mintd] -| [10][10] | Spending conditions | [Nutshell][py], [cdk-cli], [cashu-ts][ts] | [Nutshell][py], [cdk-mintd], [nutmix] | -| [11][11] | Pay-To-Pubkey (P2PK) | [Nutshell][py], [cdk-cli], [cashu-ts][ts] | [Nutshell][py], [cdk-mintd], [nutmix] | -| [12][12] | DLEQ proofs | [Nutshell][py], [cdk-cli] | [Nutshell][py], [cdk-mintd] | -| [13][13] | Deterministic secrets | [Nutshell][py], [Moksha][moksha], [cashu-ts][ts], [cdk-cli], [gonuts] | - | -| [14][14] | Hashed Timelock Contracts (HTLCs) | [Nutshell][py], [cdk-cli] | [Nutshell][py], [cdk-mintd] | -| [15][15] | Partial multi-path payments (MPP) | [Nutshell][py] | [Nutshell][py] | -| [16][16] | Animated QR codes | [Cashu.me][cashume] | - | -| [17][17] | WebSocket subscriptions | [Nutshell][py] | [Nutshell][py] | +| # | Description | Wallets | Mints | +| -------- | --------------------------------- | --------------------------------------------------------------------------- | --------------------------------------------- | +| [07][07] | Token state check | [Nutshell][py], [Moksha][moksha], [Nutstash][ns], [cashu-ts][ts], [cdk-cli] | [Nutshell][py], [Moksha][moksha], [cdk-mintd] | +| [08][08] | Overpaid Lightning fees | [Nutshell][py], [Moksha][moksha], [Nutstash][ns], [cashu-ts][ts], [cdk-cli] | [Nutshell][py], [Moksha][moksha], [cdk-mintd] | +| [09][09] | Signature restore | [Nutshell][py], [cdk-cli], [cashu-ts][ts], [gonuts] | [Nutshell][py], [cdk-mintd] | +| [10][10] | Spending conditions | [Nutshell][py], [cdk-cli], [cashu-ts][ts] | [Nutshell][py], [cdk-mintd], [nutmix] | +| [11][11] | Pay-To-Pubkey (P2PK) | [Nutshell][py], [cdk-cli], [cashu-ts][ts] | [Nutshell][py], [cdk-mintd], [nutmix] | +| [12][12] | DLEQ proofs | [Nutshell][py], [cdk-cli] | [Nutshell][py], [cdk-mintd] | +| [13][13] | Deterministic secrets | [Nutshell][py], [Moksha][moksha], [cashu-ts][ts], [cdk-cli], [gonuts] | - | +| [14][14] | Hashed Timelock Contracts (HTLCs) | [Nutshell][py], [cdk-cli] | [Nutshell][py], [cdk-mintd] | +| [15][15] | Partial multi-path payments (MPP) | [Nutshell][py] | [Nutshell][py] | +| [16][16] | Animated QR codes | [Cashu.me][cashume] | - | +| [17][17] | WebSocket subscriptions | [Nutshell][py] | [Nutshell][py] | #### Wallets: - - [Nutshell][py] - - [cdk-cli][cdk-cli] - - [cashu-ts][ts] - - [eNuts][enuts] - - [Minibits][minibits] - - [Moksha][moksha] - - [Nutstash][ns] - - [Cashu.me][cashume] - - [Gonuts][gonuts] - - [Boardwalk Cash][bwc] +- [Nutshell][py] +- [cdk-cli][cdk-cli] +- [cashu-ts][ts] +- [eNuts][enuts] +- [Minibits][minibits] +- [Moksha][moksha] +- [Nutstash][ns] +- [Cashu.me][cashume] +- [Gonuts][gonuts] +- [Boardwalk Cash][bwc] #### Mints: - - - [Nutshell][py] - - [Gonuts][gonuts] - - [Moksha][moksha] - - [cdk-mintd][cdk-mintd] - - [Nutmix][nutmix] - + +- [Nutshell][py] +- [Gonuts][gonuts] +- [Moksha][moksha] +- [cdk-mintd][cdk-mintd] +- [Nutmix][nutmix] [py]: https://github.com/cashubtc/nutshell [lnbits]: https://github.com/lnbits/cashu @@ -69,7 +69,6 @@ Wallets and mints `MUST` implement all mandatory specs and `CAN` implement optio [gonuts]: https://github.com/elnosh/gonuts [nutmix]: https://github.com/lescuer97/nutmix [bwc]: https://github.com/MakePrisms/boardwalkcash - [00]: 00.md [01]: 01.md [02]: 02.md diff --git a/tests/00-tests.md b/tests/00-tests.md index 45717908..d3837730 100644 --- a/tests/00-tests.md +++ b/tests/00-tests.md @@ -3,6 +3,7 @@ ### Hash-to-curve function The hash to curve function takes a message of any length and outputs a valid point on the secp256k1 curve. Note that unless you are using complex spend conditions ([NUT-10]), standardized secrets (random 32-bytes-long byte arrays) should be used in order to prevent wallet fingerprinting. + ```shell # Test 1 (hex encoded) Message: 0000000000000000000000000000000000000000000000000000000000000000 @@ -19,7 +20,9 @@ Point: 026cdbe15362df59cd1dd3c9c11de8aedac2106eca69236ecd9fbe117af897be4f ``` ### Blinded messages + These are test vectors for the the blinded secret (public key) `B_` Alice sends to the mint Bob given a secret `x` and a random blinding factor `r`. + ```shell # Test 1 x: d341ee4871f1f889041e63cf0d3823c713eea6aff01e80f1719f08f9e5be98f6 # hex encoded byte array @@ -49,7 +52,9 @@ C_: 0398bc70ce8184d27ba89834d19f5199c84443c31131e48d3c1214db24247d005d ``` ## Serialization of TokenV3 + The following are JSON-formatted v3 tokens and their serialized counterparts. + ```json { "token": [ @@ -76,7 +81,8 @@ The following are JSON-formatted v3 tokens and their serialized counterparts. } ``` -Serialized: +Serialized: + ``` cashuAeyJ0b2tlbiI6W3sibWludCI6Imh0dHBzOi8vODMzMy5zcGFjZTozMzM4IiwicHJvb2ZzIjpbeyJhbW91bnQiOjIsImlkIjoiMDA5YTFmMjkzMjUzZTQxZSIsInNlY3JldCI6IjQwNzkxNWJjMjEyYmU2MWE3N2UzZTZkMmFlYjRjNzI3OTgwYmRhNTFjZDA2YTZhZmMyOWUyODYxNzY4YTc4MzciLCJDIjoiMDJiYzkwOTc5OTdkODFhZmIyY2M3MzQ2YjVlNDM0NWE5MzQ2YmQyYTUwNmViNzk1ODU5OGE3MmYwY2Y4NTE2M2VhIn0seyJhbW91bnQiOjgsImlkIjoiMDA5YTFmMjkzMjUzZTQxZSIsInNlY3JldCI6ImZlMTUxMDkzMTRlNjFkNzc1NmIwZjhlZTBmMjNhNjI0YWNhYTNmNGUwNDJmNjE0MzNjNzI4YzcwNTdiOTMxYmUiLCJDIjoiMDI5ZThlNTA1MGI4OTBhN2Q2YzA5NjhkYjE2YmMxZDVkNWZhMDQwZWExZGUyODRmNmVjNjlkNjEyOTlmNjcxMDU5In1dfV0sInVuaXQiOiJzYXQiLCJtZW1vIjoiVGhhbmsgeW91LiJ9 ``` @@ -84,6 +90,7 @@ cashuAeyJ0b2tlbiI6W3sibWludCI6Imh0dHBzOi8vODMzMy5zcGFjZTozMzM4IiwicHJvb2ZzIjpbey ## Deserialization of TokenV3 The following are incorrectly formatted serialized v3 tokens. + ```shell # Incorrect prefix (casshuA) casshuAeyJ0b2tlbiI6W3sibWludCI6Imh0dHBzOi8vODMzMy5zcGFjZTozMzM4IiwicHJvb2ZzIjpbeyJhbW91bnQiOjIsImlkIjoiMDA5YTFmMjkzMjUzZTQxZSIsInNlY3JldCI6IjQwNzkxNWJjMjEyYmU2MWE3N2UzZTZkMmFlYjRjNzI3OTgwYmRhNTFjZDA2YTZhZmMyOWUyODYxNzY4YTc4MzciLCJDIjoiMDJiYzkwOTc5OTdkODFhZmIyY2M3MzQ2YjVlNDM0NWE5MzQ2YmQyYTUwNmViNzk1ODU5OGE3MmYwY2Y4NTE2M2VhIn0seyJhbW91bnQiOjgsImlkIjoiMDA5YTFmMjkzMjUzZTQxZSIsInNlY3JldCI6ImZlMTUxMDkzMTRlNjFkNzc1NmIwZjhlZTBmMjNhNjI0YWNhYTNmNGUwNDJmNjE0MzNjNzI4YzcwNTdiOTMxYmUiLCJDIjoiMDI5ZThlNTA1MGI4OTBhN2Q2YzA5NjhkYjE2YmMxZDVkNWZhMDQwZWExZGUyODRmNmVjNjlkNjEyOTlmNjcxMDU5In1dfV0sInVuaXQiOiJzYXQiLCJtZW1vIjoiVGhhbmsgeW91LiJ9 @@ -93,11 +100,13 @@ eyJ0b2tlbiI6W3sibWludCI6Imh0dHBzOi8vODMzMy5zcGFjZTozMzM4IiwicHJvb2ZzIjpbeyJhbW91 ``` The following is a correctly serialized v3 token. + ```shell cashuAeyJ0b2tlbiI6W3sibWludCI6Imh0dHBzOi8vODMzMy5zcGFjZTozMzM4IiwicHJvb2ZzIjpbeyJhbW91bnQiOjIsImlkIjoiMDA5YTFmMjkzMjUzZTQxZSIsInNlY3JldCI6IjQwNzkxNWJjMjEyYmU2MWE3N2UzZTZkMmFlYjRjNzI3OTgwYmRhNTFjZDA2YTZhZmMyOWUyODYxNzY4YTc4MzciLCJDIjoiMDJiYzkwOTc5OTdkODFhZmIyY2M3MzQ2YjVlNDM0NWE5MzQ2YmQyYTUwNmViNzk1ODU5OGE3MmYwY2Y4NTE2M2VhIn0seyJhbW91bnQiOjgsImlkIjoiMDA5YTFmMjkzMjUzZTQxZSIsInNlY3JldCI6ImZlMTUxMDkzMTRlNjFkNzc1NmIwZjhlZTBmMjNhNjI0YWNhYTNmNGUwNDJmNjE0MzNjNzI4YzcwNTdiOTMxYmUiLCJDIjoiMDI5ZThlNTA1MGI4OTBhN2Q2YzA5NjhkYjE2YmMxZDVkNWZhMDQwZWExZGUyODRmNmVjNjlkNjEyOTlmNjcxMDU5In1dfV0sInVuaXQiOiJzYXQiLCJtZW1vIjoiVGhhbmsgeW91LiJ9 ``` Both of the following v3 tokens are valid, one includes padding characters at the end and the other does not. + ```shell # Clients should be able to deserialize both cashuAeyJ0b2tlbiI6W3sibWludCI6Imh0dHBzOi8vODMzMy5zcGFjZTozMzM4IiwicHJvb2ZzIjpbeyJhbW91bnQiOjIsImlkIjoiMDA5YTFmMjkzMjUzZTQxZSIsInNlY3JldCI6IjQwNzkxNWJjMjEyYmU2MWE3N2UzZTZkMmFlYjRjNzI3OTgwYmRhNTFjZDA2YTZhZmMyOWUyODYxNzY4YTc4MzciLCJDIjoiMDJiYzkwOTc5OTdkODFhZmIyY2M3MzQ2YjVlNDM0NWE5MzQ2YmQyYTUwNmViNzk1ODU5OGE3MmYwY2Y4NTE2M2VhIn0seyJhbW91bnQiOjgsImlkIjoiMDA5YTFmMjkzMjUzZTQxZSIsInNlY3JldCI6ImZlMTUxMDkzMTRlNjFkNzc1NmIwZjhlZTBmMjNhNjI0YWNhYTNmNGUwNDJmNjE0MzNjNzI4YzcwNTdiOTMxYmUiLCJDIjoiMDI5ZThlNTA1MGI4OTBhN2Q2YzA5NjhkYjE2YmMxZDVkNWZhMDQwZWExZGUyODRmNmVjNjlkNjEyOTlmNjcxMDU5In1dfV0sInVuaXQiOiJzYXQiLCJtZW1vIjoiVGhhbmsgeW91IHZlcnkgbXVjaC4ifQ== @@ -105,10 +114,13 @@ cashuAeyJ0b2tlbiI6W3sibWludCI6Imh0dHBzOi8vODMzMy5zcGFjZTozMzM4IiwicHJvb2ZzIjpbey ``` ## Serialization of TokenV4 + The following are JSON-formatted v4 tokens and their serialized counterparts. The `h''` values are `bytes` but displayed as hex strings here. ### Single keyset + Token from a single keyset and including a memo. + ```json { "t": [ @@ -136,7 +148,9 @@ cashuBpGF0gaJhaUgArSaMTR9YJmFwgaNhYQFhc3hAOWE2ZGJiODQ3YmQyMzJiYTc2ZGIwZGYxOTcyMT ``` ### Multiple keysets + The token below includes proofs from two different keysets. + ```json { "t": [ diff --git a/tests/01-tests.md b/tests/01-tests.md index 74ec3248..a46927d7 100644 --- a/tests/01-tests.md +++ b/tests/01-tests.md @@ -3,28 +3,103 @@ The following are incorrect keysets that should be rejected by wallets implementing the NUT-01 specification. Key 1 is missing a byte + ```json { - "1":"03a40f20667ed53513075dc51e715ff2046cad64eb68960632269ba7f0210e38","2":"03fd4ce5a16b65576145949e6f99f445f8249fee17c606b688b504a849cdc452de","4":"02648eccfa4c026960966276fa5a4cae46ce0fd432211a4f449bf84f13aa5f8303","8":"02fdfd6796bfeac490cbee12f778f867f0a2c68f6508d17c649759ea0dc3547528" + "1": "03a40f20667ed53513075dc51e715ff2046cad64eb68960632269ba7f0210e38", + "2": "03fd4ce5a16b65576145949e6f99f445f8249fee17c606b688b504a849cdc452de", + "4": "02648eccfa4c026960966276fa5a4cae46ce0fd432211a4f449bf84f13aa5f8303", + "8": "02fdfd6796bfeac490cbee12f778f867f0a2c68f6508d17c649759ea0dc3547528" } ``` Key 2 is a valid key but is not in the compressed format. + ```json { - "1":"03a40f20667ed53513075dc51e715ff2046cad64eb68960632269ba7f0210e38bc","2":"04fd4ce5a16b65576145949e6f99f445f8249fee17c606b688b504a849cdc452de3625246cb2c27dac965cb7200a5986467eee92eb7d496bbf1453b074e223e481","4":"02648eccfa4c026960966276fa5a4cae46ce0fd432211a4f449bf84f13aa5f8303","8":"02fdfd6796bfeac490cbee12f778f867f0a2c68f6508d17c649759ea0dc3547528" + "1": "03a40f20667ed53513075dc51e715ff2046cad64eb68960632269ba7f0210e38bc", + "2": "04fd4ce5a16b65576145949e6f99f445f8249fee17c606b688b504a849cdc452de3625246cb2c27dac965cb7200a5986467eee92eb7d496bbf1453b074e223e481", + "4": "02648eccfa4c026960966276fa5a4cae46ce0fd432211a4f449bf84f13aa5f8303", + "8": "02fdfd6796bfeac490cbee12f778f867f0a2c68f6508d17c649759ea0dc3547528" } ``` The following are correct keysets that should be accepted by wallets implementing the NUT-01 specification. Note that the second (bigger) keyset's biggest amount is `9223372036854775808`, one unit over what can fit into a 64-bit signed integer (often known as a `Long`). To handle this you'll need to use either an unsigned 64-bit integer or a 128-bit signed integer (sometimes known as a `BigInteger`). + ```json { - "1":"03a40f20667ed53513075dc51e715ff2046cad64eb68960632269ba7f0210e38bc","2":"03fd4ce5a16b65576145949e6f99f445f8249fee17c606b688b504a849cdc452de","4":"02648eccfa4c026960966276fa5a4cae46ce0fd432211a4f449bf84f13aa5f8303","8":"02fdfd6796bfeac490cbee12f778f867f0a2c68f6508d17c649759ea0dc3547528" + "1": "03a40f20667ed53513075dc51e715ff2046cad64eb68960632269ba7f0210e38bc", + "2": "03fd4ce5a16b65576145949e6f99f445f8249fee17c606b688b504a849cdc452de", + "4": "02648eccfa4c026960966276fa5a4cae46ce0fd432211a4f449bf84f13aa5f8303", + "8": "02fdfd6796bfeac490cbee12f778f867f0a2c68f6508d17c649759ea0dc3547528" } ``` ```json { - "1":"03ba786a2c0745f8c30e490288acd7a72dd53d65afd292ddefa326a4a3fa14c566","2":"03361cd8bd1329fea797a6add1cf1990ffcf2270ceb9fc81eeee0e8e9c1bd0cdf5","4":"036e378bcf78738ddf68859293c69778035740e41138ab183c94f8fee7572214c7","8":"03909d73beaf28edfb283dbeb8da321afd40651e8902fcf5454ecc7d69788626c0","16":"028a36f0e6638ea7466665fe174d958212723019ec08f9ce6898d897f88e68aa5d","32":"03a97a40e146adee2687ac60c2ba2586a90f970de92a9d0e6cae5a4b9965f54612","64":"03ce86f0c197aab181ddba0cfc5c5576e11dfd5164d9f3d4a3fc3ffbbf2e069664","128":"0284f2c06d938a6f78794814c687560a0aabab19fe5e6f30ede38e113b132a3cb9","256":"03b99f475b68e5b4c0ba809cdecaae64eade2d9787aa123206f91cd61f76c01459","512":"03d4db82ea19a44d35274de51f78af0a710925fe7d9e03620b84e3e9976e3ac2eb","1024":"031fbd4ba801870871d46cf62228a1b748905ebc07d3b210daf48de229e683f2dc","2048":"0276cedb9a3b160db6a158ad4e468d2437f021293204b3cd4bf6247970d8aff54b","4096":"02fc6b89b403ee9eb8a7ed457cd3973638080d6e04ca8af7307c965c166b555ea2","8192":"0320265583e916d3a305f0d2687fcf2cd4e3cd03a16ea8261fda309c3ec5721e21","16384":"036e41de58fdff3cb1d8d713f48c63bc61fa3b3e1631495a444d178363c0d2ed50","32768":"0365438f613f19696264300b069d1dad93f0c60a37536b72a8ab7c7366a5ee6c04","65536":"02408426cfb6fc86341bac79624ba8708a4376b2d92debdf4134813f866eb57a8d","131072":"031063e9f11c94dc778c473e968966eac0e70b7145213fbaff5f7a007e71c65f41","262144":"02f2a3e808f9cd168ec71b7f328258d0c1dda250659c1aced14c7f5cf05aab4328","524288":"038ac10de9f1ff9395903bb73077e94dbf91e9ef98fd77d9a2debc5f74c575bc86","1048576":"0203eaee4db749b0fc7c49870d082024b2c31d889f9bc3b32473d4f1dfa3625788","2097152":"033cdb9d36e1e82ae652b7b6a08e0204569ec7ff9ebf85d80a02786dc7fe00b04c","4194304":"02c8b73f4e3a470ae05e5f2fe39984d41e9f6ae7be9f3b09c9ac31292e403ac512","8388608":"025bbe0cfce8a1f4fbd7f3a0d4a09cb6badd73ef61829dc827aa8a98c270bc25b0","16777216":"037eec3d1651a30a90182d9287a5c51386fe35d4a96839cf7969c6e2a03db1fc21","33554432":"03280576b81a04e6abd7197f305506476f5751356b7643988495ca5c3e14e5c262","67108864":"03268bfb05be1dbb33ab6e7e00e438373ca2c9b9abc018fdb452d0e1a0935e10d3","134217728":"02573b68784ceba9617bbcc7c9487836d296aa7c628c3199173a841e7a19798020","268435456":"0234076b6e70f7fbf755d2227ecc8d8169d662518ee3a1401f729e2a12ccb2b276","536870912":"03015bd88961e2a466a2163bd4248d1d2b42c7c58a157e594785e7eb34d880efc9","1073741824":"02c9b076d08f9020ebee49ac8ba2610b404d4e553a4f800150ceb539e9421aaeee","2147483648":"034d592f4c366afddc919a509600af81b489a03caf4f7517c2b3f4f2b558f9a41a","4294967296":"037c09ecb66da082981e4cbdb1ac65c0eb631fc75d85bed13efb2c6364148879b5","8589934592":"02b4ebb0dda3b9ad83b39e2e31024b777cc0ac205a96b9a6cfab3edea2912ed1b3","17179869184":"026cc4dacdced45e63f6e4f62edbc5779ccd802e7fabb82d5123db879b636176e9","34359738368":"02b2cee01b7d8e90180254459b8f09bbea9aad34c3a2fd98c85517ecfc9805af75","68719476736":"037a0c0d564540fc574b8bfa0253cca987b75466e44b295ed59f6f8bd41aace754","137438953472":"021df6585cae9b9ca431318a713fd73dbb76b3ef5667957e8633bca8aaa7214fb6","274877906944":"02b8f53dde126f8c85fa5bb6061c0be5aca90984ce9b902966941caf963648d53a","549755813888":"029cc8af2840d59f1d8761779b2496623c82c64be8e15f9ab577c657c6dd453785","1099511627776":"03e446fdb84fad492ff3a25fc1046fb9a93a5b262ebcd0151caa442ea28959a38a","2199023255552":"02d6b25bd4ab599dd0818c55f75702fde603c93f259222001246569018842d3258","4398046511104":"03397b522bb4e156ec3952d3f048e5a986c20a00718e5e52cd5718466bf494156a","8796093022208":"02d1fb9e78262b5d7d74028073075b80bb5ab281edcfc3191061962c1346340f1e","17592186044416":"030d3f2ad7a4ca115712ff7f140434f802b19a4c9b2dd1c76f3e8e80c05c6a9310","35184372088832":"03e325b691f292e1dfb151c3fb7cad440b225795583c32e24e10635a80e4221c06","70368744177664":"03bee8f64d88de3dee21d61f89efa32933da51152ddbd67466bef815e9f93f8fd1","140737488355328":"0327244c9019a4892e1f04ba3bf95fe43b327479e2d57c25979446cc508cd379ed","281474976710656":"02fb58522cd662f2f8b042f8161caae6e45de98283f74d4e99f19b0ea85e08a56d","562949953421312":"02adde4b466a9d7e59386b6a701a39717c53f30c4810613c1b55e6b6da43b7bc9a","1125899906842624":"038eeda11f78ce05c774f30e393cda075192b890d68590813ff46362548528dca9","2251799813685248":"02ec13e0058b196db80f7079d329333b330dc30c000dbdd7397cbbc5a37a664c4f","4503599627370496":"02d2d162db63675bd04f7d56df04508840f41e2ad87312a3c93041b494efe80a73","9007199254740992":"0356969d6aef2bb40121dbd07c68b6102339f4ea8e674a9008bb69506795998f49","18014398509481984":"02f4e667567ebb9f4e6e180a4113bb071c48855f657766bb5e9c776a880335d1d6","36028797018963968":"0385b4fe35e41703d7a657d957c67bb536629de57b7e6ee6fe2130728ef0fc90b0","72057594037927936":"02b2bc1968a6fddbcc78fb9903940524824b5f5bed329c6ad48a19b56068c144fd","144115188075855872":"02e0dbb24f1d288a693e8a49bc14264d1276be16972131520cf9e055ae92fba19a","288230376151711744":"03efe75c106f931a525dc2d653ebedddc413a2c7d8cb9da410893ae7d2fa7d19cc","576460752303423488":"02c7ec2bd9508a7fc03f73c7565dc600b30fd86f3d305f8f139c45c404a52d958a","1152921504606846976":"035a6679c6b25e68ff4e29d1c7ef87f21e0a8fc574f6a08c1aa45ff352c1d59f06","2305843009213693952":"033cdc225962c052d485f7cfbf55a5b2367d200fe1fe4373a347deb4cc99e9a099","4611686018427387904":"024a4b806cf413d14b294719090a9da36ba75209c7657135ad09bc65328fba9e6f","9223372036854775808":"0377a6fe114e291a8d8e991627c38001c8305b23b9e98b1c7b1893f5cd0dda6cad" + "1": "03ba786a2c0745f8c30e490288acd7a72dd53d65afd292ddefa326a4a3fa14c566", + "2": "03361cd8bd1329fea797a6add1cf1990ffcf2270ceb9fc81eeee0e8e9c1bd0cdf5", + "4": "036e378bcf78738ddf68859293c69778035740e41138ab183c94f8fee7572214c7", + "8": "03909d73beaf28edfb283dbeb8da321afd40651e8902fcf5454ecc7d69788626c0", + "16": "028a36f0e6638ea7466665fe174d958212723019ec08f9ce6898d897f88e68aa5d", + "32": "03a97a40e146adee2687ac60c2ba2586a90f970de92a9d0e6cae5a4b9965f54612", + "64": "03ce86f0c197aab181ddba0cfc5c5576e11dfd5164d9f3d4a3fc3ffbbf2e069664", + "128": "0284f2c06d938a6f78794814c687560a0aabab19fe5e6f30ede38e113b132a3cb9", + "256": "03b99f475b68e5b4c0ba809cdecaae64eade2d9787aa123206f91cd61f76c01459", + "512": "03d4db82ea19a44d35274de51f78af0a710925fe7d9e03620b84e3e9976e3ac2eb", + "1024": "031fbd4ba801870871d46cf62228a1b748905ebc07d3b210daf48de229e683f2dc", + "2048": "0276cedb9a3b160db6a158ad4e468d2437f021293204b3cd4bf6247970d8aff54b", + "4096": "02fc6b89b403ee9eb8a7ed457cd3973638080d6e04ca8af7307c965c166b555ea2", + "8192": "0320265583e916d3a305f0d2687fcf2cd4e3cd03a16ea8261fda309c3ec5721e21", + "16384": "036e41de58fdff3cb1d8d713f48c63bc61fa3b3e1631495a444d178363c0d2ed50", + "32768": "0365438f613f19696264300b069d1dad93f0c60a37536b72a8ab7c7366a5ee6c04", + "65536": "02408426cfb6fc86341bac79624ba8708a4376b2d92debdf4134813f866eb57a8d", + "131072": "031063e9f11c94dc778c473e968966eac0e70b7145213fbaff5f7a007e71c65f41", + "262144": "02f2a3e808f9cd168ec71b7f328258d0c1dda250659c1aced14c7f5cf05aab4328", + "524288": "038ac10de9f1ff9395903bb73077e94dbf91e9ef98fd77d9a2debc5f74c575bc86", + "1048576": "0203eaee4db749b0fc7c49870d082024b2c31d889f9bc3b32473d4f1dfa3625788", + "2097152": "033cdb9d36e1e82ae652b7b6a08e0204569ec7ff9ebf85d80a02786dc7fe00b04c", + "4194304": "02c8b73f4e3a470ae05e5f2fe39984d41e9f6ae7be9f3b09c9ac31292e403ac512", + "8388608": "025bbe0cfce8a1f4fbd7f3a0d4a09cb6badd73ef61829dc827aa8a98c270bc25b0", + "16777216": "037eec3d1651a30a90182d9287a5c51386fe35d4a96839cf7969c6e2a03db1fc21", + "33554432": "03280576b81a04e6abd7197f305506476f5751356b7643988495ca5c3e14e5c262", + "67108864": "03268bfb05be1dbb33ab6e7e00e438373ca2c9b9abc018fdb452d0e1a0935e10d3", + "134217728": "02573b68784ceba9617bbcc7c9487836d296aa7c628c3199173a841e7a19798020", + "268435456": "0234076b6e70f7fbf755d2227ecc8d8169d662518ee3a1401f729e2a12ccb2b276", + "536870912": "03015bd88961e2a466a2163bd4248d1d2b42c7c58a157e594785e7eb34d880efc9", + "1073741824": "02c9b076d08f9020ebee49ac8ba2610b404d4e553a4f800150ceb539e9421aaeee", + "2147483648": "034d592f4c366afddc919a509600af81b489a03caf4f7517c2b3f4f2b558f9a41a", + "4294967296": "037c09ecb66da082981e4cbdb1ac65c0eb631fc75d85bed13efb2c6364148879b5", + "8589934592": "02b4ebb0dda3b9ad83b39e2e31024b777cc0ac205a96b9a6cfab3edea2912ed1b3", + "17179869184": "026cc4dacdced45e63f6e4f62edbc5779ccd802e7fabb82d5123db879b636176e9", + "34359738368": "02b2cee01b7d8e90180254459b8f09bbea9aad34c3a2fd98c85517ecfc9805af75", + "68719476736": "037a0c0d564540fc574b8bfa0253cca987b75466e44b295ed59f6f8bd41aace754", + "137438953472": "021df6585cae9b9ca431318a713fd73dbb76b3ef5667957e8633bca8aaa7214fb6", + "274877906944": "02b8f53dde126f8c85fa5bb6061c0be5aca90984ce9b902966941caf963648d53a", + "549755813888": "029cc8af2840d59f1d8761779b2496623c82c64be8e15f9ab577c657c6dd453785", + "1099511627776": "03e446fdb84fad492ff3a25fc1046fb9a93a5b262ebcd0151caa442ea28959a38a", + "2199023255552": "02d6b25bd4ab599dd0818c55f75702fde603c93f259222001246569018842d3258", + "4398046511104": "03397b522bb4e156ec3952d3f048e5a986c20a00718e5e52cd5718466bf494156a", + "8796093022208": "02d1fb9e78262b5d7d74028073075b80bb5ab281edcfc3191061962c1346340f1e", + "17592186044416": "030d3f2ad7a4ca115712ff7f140434f802b19a4c9b2dd1c76f3e8e80c05c6a9310", + "35184372088832": "03e325b691f292e1dfb151c3fb7cad440b225795583c32e24e10635a80e4221c06", + "70368744177664": "03bee8f64d88de3dee21d61f89efa32933da51152ddbd67466bef815e9f93f8fd1", + "140737488355328": "0327244c9019a4892e1f04ba3bf95fe43b327479e2d57c25979446cc508cd379ed", + "281474976710656": "02fb58522cd662f2f8b042f8161caae6e45de98283f74d4e99f19b0ea85e08a56d", + "562949953421312": "02adde4b466a9d7e59386b6a701a39717c53f30c4810613c1b55e6b6da43b7bc9a", + "1125899906842624": "038eeda11f78ce05c774f30e393cda075192b890d68590813ff46362548528dca9", + "2251799813685248": "02ec13e0058b196db80f7079d329333b330dc30c000dbdd7397cbbc5a37a664c4f", + "4503599627370496": "02d2d162db63675bd04f7d56df04508840f41e2ad87312a3c93041b494efe80a73", + "9007199254740992": "0356969d6aef2bb40121dbd07c68b6102339f4ea8e674a9008bb69506795998f49", + "18014398509481984": "02f4e667567ebb9f4e6e180a4113bb071c48855f657766bb5e9c776a880335d1d6", + "36028797018963968": "0385b4fe35e41703d7a657d957c67bb536629de57b7e6ee6fe2130728ef0fc90b0", + "72057594037927936": "02b2bc1968a6fddbcc78fb9903940524824b5f5bed329c6ad48a19b56068c144fd", + "144115188075855872": "02e0dbb24f1d288a693e8a49bc14264d1276be16972131520cf9e055ae92fba19a", + "288230376151711744": "03efe75c106f931a525dc2d653ebedddc413a2c7d8cb9da410893ae7d2fa7d19cc", + "576460752303423488": "02c7ec2bd9508a7fc03f73c7565dc600b30fd86f3d305f8f139c45c404a52d958a", + "1152921504606846976": "035a6679c6b25e68ff4e29d1c7ef87f21e0a8fc574f6a08c1aa45ff352c1d59f06", + "2305843009213693952": "033cdc225962c052d485f7cfbf55a5b2367d200fe1fe4373a347deb4cc99e9a099", + "4611686018427387904": "024a4b806cf413d14b294719090a9da36ba75209c7657135ad09bc65328fba9e6f", + "9223372036854775808": "0377a6fe114e291a8d8e991627c38001c8305b23b9e98b1c7b1893f5cd0dda6cad" } ``` diff --git a/tests/02-tests.md b/tests/02-tests.md index 759d15e2..c5a80804 100644 --- a/tests/02-tests.md +++ b/tests/02-tests.md @@ -2,15 +2,83 @@ The following keysets and corresponding keyset IDs are correct: Keyset id: `00456a94ab4e1c46` + ```json { - "1":"03a40f20667ed53513075dc51e715ff2046cad64eb68960632269ba7f0210e38bc","2":"03fd4ce5a16b65576145949e6f99f445f8249fee17c606b688b504a849cdc452de","4":"02648eccfa4c026960966276fa5a4cae46ce0fd432211a4f449bf84f13aa5f8303","8":"02fdfd6796bfeac490cbee12f778f867f0a2c68f6508d17c649759ea0dc3547528" + "1": "03a40f20667ed53513075dc51e715ff2046cad64eb68960632269ba7f0210e38bc", + "2": "03fd4ce5a16b65576145949e6f99f445f8249fee17c606b688b504a849cdc452de", + "4": "02648eccfa4c026960966276fa5a4cae46ce0fd432211a4f449bf84f13aa5f8303", + "8": "02fdfd6796bfeac490cbee12f778f867f0a2c68f6508d17c649759ea0dc3547528" } ``` Keyset id: `000f01df73ea149a` + ```json { - "1":"03ba786a2c0745f8c30e490288acd7a72dd53d65afd292ddefa326a4a3fa14c566","2":"03361cd8bd1329fea797a6add1cf1990ffcf2270ceb9fc81eeee0e8e9c1bd0cdf5","4":"036e378bcf78738ddf68859293c69778035740e41138ab183c94f8fee7572214c7","8":"03909d73beaf28edfb283dbeb8da321afd40651e8902fcf5454ecc7d69788626c0","16":"028a36f0e6638ea7466665fe174d958212723019ec08f9ce6898d897f88e68aa5d","32":"03a97a40e146adee2687ac60c2ba2586a90f970de92a9d0e6cae5a4b9965f54612","64":"03ce86f0c197aab181ddba0cfc5c5576e11dfd5164d9f3d4a3fc3ffbbf2e069664","128":"0284f2c06d938a6f78794814c687560a0aabab19fe5e6f30ede38e113b132a3cb9","256":"03b99f475b68e5b4c0ba809cdecaae64eade2d9787aa123206f91cd61f76c01459","512":"03d4db82ea19a44d35274de51f78af0a710925fe7d9e03620b84e3e9976e3ac2eb","1024":"031fbd4ba801870871d46cf62228a1b748905ebc07d3b210daf48de229e683f2dc","2048":"0276cedb9a3b160db6a158ad4e468d2437f021293204b3cd4bf6247970d8aff54b","4096":"02fc6b89b403ee9eb8a7ed457cd3973638080d6e04ca8af7307c965c166b555ea2","8192":"0320265583e916d3a305f0d2687fcf2cd4e3cd03a16ea8261fda309c3ec5721e21","16384":"036e41de58fdff3cb1d8d713f48c63bc61fa3b3e1631495a444d178363c0d2ed50","32768":"0365438f613f19696264300b069d1dad93f0c60a37536b72a8ab7c7366a5ee6c04","65536":"02408426cfb6fc86341bac79624ba8708a4376b2d92debdf4134813f866eb57a8d","131072":"031063e9f11c94dc778c473e968966eac0e70b7145213fbaff5f7a007e71c65f41","262144":"02f2a3e808f9cd168ec71b7f328258d0c1dda250659c1aced14c7f5cf05aab4328","524288":"038ac10de9f1ff9395903bb73077e94dbf91e9ef98fd77d9a2debc5f74c575bc86","1048576":"0203eaee4db749b0fc7c49870d082024b2c31d889f9bc3b32473d4f1dfa3625788","2097152":"033cdb9d36e1e82ae652b7b6a08e0204569ec7ff9ebf85d80a02786dc7fe00b04c","4194304":"02c8b73f4e3a470ae05e5f2fe39984d41e9f6ae7be9f3b09c9ac31292e403ac512","8388608":"025bbe0cfce8a1f4fbd7f3a0d4a09cb6badd73ef61829dc827aa8a98c270bc25b0","16777216":"037eec3d1651a30a90182d9287a5c51386fe35d4a96839cf7969c6e2a03db1fc21","33554432":"03280576b81a04e6abd7197f305506476f5751356b7643988495ca5c3e14e5c262","67108864":"03268bfb05be1dbb33ab6e7e00e438373ca2c9b9abc018fdb452d0e1a0935e10d3","134217728":"02573b68784ceba9617bbcc7c9487836d296aa7c628c3199173a841e7a19798020","268435456":"0234076b6e70f7fbf755d2227ecc8d8169d662518ee3a1401f729e2a12ccb2b276","536870912":"03015bd88961e2a466a2163bd4248d1d2b42c7c58a157e594785e7eb34d880efc9","1073741824":"02c9b076d08f9020ebee49ac8ba2610b404d4e553a4f800150ceb539e9421aaeee","2147483648":"034d592f4c366afddc919a509600af81b489a03caf4f7517c2b3f4f2b558f9a41a","4294967296":"037c09ecb66da082981e4cbdb1ac65c0eb631fc75d85bed13efb2c6364148879b5","8589934592":"02b4ebb0dda3b9ad83b39e2e31024b777cc0ac205a96b9a6cfab3edea2912ed1b3","17179869184":"026cc4dacdced45e63f6e4f62edbc5779ccd802e7fabb82d5123db879b636176e9","34359738368":"02b2cee01b7d8e90180254459b8f09bbea9aad34c3a2fd98c85517ecfc9805af75","68719476736":"037a0c0d564540fc574b8bfa0253cca987b75466e44b295ed59f6f8bd41aace754","137438953472":"021df6585cae9b9ca431318a713fd73dbb76b3ef5667957e8633bca8aaa7214fb6","274877906944":"02b8f53dde126f8c85fa5bb6061c0be5aca90984ce9b902966941caf963648d53a","549755813888":"029cc8af2840d59f1d8761779b2496623c82c64be8e15f9ab577c657c6dd453785","1099511627776":"03e446fdb84fad492ff3a25fc1046fb9a93a5b262ebcd0151caa442ea28959a38a","2199023255552":"02d6b25bd4ab599dd0818c55f75702fde603c93f259222001246569018842d3258","4398046511104":"03397b522bb4e156ec3952d3f048e5a986c20a00718e5e52cd5718466bf494156a","8796093022208":"02d1fb9e78262b5d7d74028073075b80bb5ab281edcfc3191061962c1346340f1e","17592186044416":"030d3f2ad7a4ca115712ff7f140434f802b19a4c9b2dd1c76f3e8e80c05c6a9310","35184372088832":"03e325b691f292e1dfb151c3fb7cad440b225795583c32e24e10635a80e4221c06","70368744177664":"03bee8f64d88de3dee21d61f89efa32933da51152ddbd67466bef815e9f93f8fd1","140737488355328":"0327244c9019a4892e1f04ba3bf95fe43b327479e2d57c25979446cc508cd379ed","281474976710656":"02fb58522cd662f2f8b042f8161caae6e45de98283f74d4e99f19b0ea85e08a56d","562949953421312":"02adde4b466a9d7e59386b6a701a39717c53f30c4810613c1b55e6b6da43b7bc9a","1125899906842624":"038eeda11f78ce05c774f30e393cda075192b890d68590813ff46362548528dca9","2251799813685248":"02ec13e0058b196db80f7079d329333b330dc30c000dbdd7397cbbc5a37a664c4f","4503599627370496":"02d2d162db63675bd04f7d56df04508840f41e2ad87312a3c93041b494efe80a73","9007199254740992":"0356969d6aef2bb40121dbd07c68b6102339f4ea8e674a9008bb69506795998f49","18014398509481984":"02f4e667567ebb9f4e6e180a4113bb071c48855f657766bb5e9c776a880335d1d6","36028797018963968":"0385b4fe35e41703d7a657d957c67bb536629de57b7e6ee6fe2130728ef0fc90b0","72057594037927936":"02b2bc1968a6fddbcc78fb9903940524824b5f5bed329c6ad48a19b56068c144fd","144115188075855872":"02e0dbb24f1d288a693e8a49bc14264d1276be16972131520cf9e055ae92fba19a","288230376151711744":"03efe75c106f931a525dc2d653ebedddc413a2c7d8cb9da410893ae7d2fa7d19cc","576460752303423488":"02c7ec2bd9508a7fc03f73c7565dc600b30fd86f3d305f8f139c45c404a52d958a","1152921504606846976":"035a6679c6b25e68ff4e29d1c7ef87f21e0a8fc574f6a08c1aa45ff352c1d59f06","2305843009213693952":"033cdc225962c052d485f7cfbf55a5b2367d200fe1fe4373a347deb4cc99e9a099","4611686018427387904":"024a4b806cf413d14b294719090a9da36ba75209c7657135ad09bc65328fba9e6f","9223372036854775808":"0377a6fe114e291a8d8e991627c38001c8305b23b9e98b1c7b1893f5cd0dda6cad" + "1": "03ba786a2c0745f8c30e490288acd7a72dd53d65afd292ddefa326a4a3fa14c566", + "2": "03361cd8bd1329fea797a6add1cf1990ffcf2270ceb9fc81eeee0e8e9c1bd0cdf5", + "4": "036e378bcf78738ddf68859293c69778035740e41138ab183c94f8fee7572214c7", + "8": "03909d73beaf28edfb283dbeb8da321afd40651e8902fcf5454ecc7d69788626c0", + "16": "028a36f0e6638ea7466665fe174d958212723019ec08f9ce6898d897f88e68aa5d", + "32": "03a97a40e146adee2687ac60c2ba2586a90f970de92a9d0e6cae5a4b9965f54612", + "64": "03ce86f0c197aab181ddba0cfc5c5576e11dfd5164d9f3d4a3fc3ffbbf2e069664", + "128": "0284f2c06d938a6f78794814c687560a0aabab19fe5e6f30ede38e113b132a3cb9", + "256": "03b99f475b68e5b4c0ba809cdecaae64eade2d9787aa123206f91cd61f76c01459", + "512": "03d4db82ea19a44d35274de51f78af0a710925fe7d9e03620b84e3e9976e3ac2eb", + "1024": "031fbd4ba801870871d46cf62228a1b748905ebc07d3b210daf48de229e683f2dc", + "2048": "0276cedb9a3b160db6a158ad4e468d2437f021293204b3cd4bf6247970d8aff54b", + "4096": "02fc6b89b403ee9eb8a7ed457cd3973638080d6e04ca8af7307c965c166b555ea2", + "8192": "0320265583e916d3a305f0d2687fcf2cd4e3cd03a16ea8261fda309c3ec5721e21", + "16384": "036e41de58fdff3cb1d8d713f48c63bc61fa3b3e1631495a444d178363c0d2ed50", + "32768": "0365438f613f19696264300b069d1dad93f0c60a37536b72a8ab7c7366a5ee6c04", + "65536": "02408426cfb6fc86341bac79624ba8708a4376b2d92debdf4134813f866eb57a8d", + "131072": "031063e9f11c94dc778c473e968966eac0e70b7145213fbaff5f7a007e71c65f41", + "262144": "02f2a3e808f9cd168ec71b7f328258d0c1dda250659c1aced14c7f5cf05aab4328", + "524288": "038ac10de9f1ff9395903bb73077e94dbf91e9ef98fd77d9a2debc5f74c575bc86", + "1048576": "0203eaee4db749b0fc7c49870d082024b2c31d889f9bc3b32473d4f1dfa3625788", + "2097152": "033cdb9d36e1e82ae652b7b6a08e0204569ec7ff9ebf85d80a02786dc7fe00b04c", + "4194304": "02c8b73f4e3a470ae05e5f2fe39984d41e9f6ae7be9f3b09c9ac31292e403ac512", + "8388608": "025bbe0cfce8a1f4fbd7f3a0d4a09cb6badd73ef61829dc827aa8a98c270bc25b0", + "16777216": "037eec3d1651a30a90182d9287a5c51386fe35d4a96839cf7969c6e2a03db1fc21", + "33554432": "03280576b81a04e6abd7197f305506476f5751356b7643988495ca5c3e14e5c262", + "67108864": "03268bfb05be1dbb33ab6e7e00e438373ca2c9b9abc018fdb452d0e1a0935e10d3", + "134217728": "02573b68784ceba9617bbcc7c9487836d296aa7c628c3199173a841e7a19798020", + "268435456": "0234076b6e70f7fbf755d2227ecc8d8169d662518ee3a1401f729e2a12ccb2b276", + "536870912": "03015bd88961e2a466a2163bd4248d1d2b42c7c58a157e594785e7eb34d880efc9", + "1073741824": "02c9b076d08f9020ebee49ac8ba2610b404d4e553a4f800150ceb539e9421aaeee", + "2147483648": "034d592f4c366afddc919a509600af81b489a03caf4f7517c2b3f4f2b558f9a41a", + "4294967296": "037c09ecb66da082981e4cbdb1ac65c0eb631fc75d85bed13efb2c6364148879b5", + "8589934592": "02b4ebb0dda3b9ad83b39e2e31024b777cc0ac205a96b9a6cfab3edea2912ed1b3", + "17179869184": "026cc4dacdced45e63f6e4f62edbc5779ccd802e7fabb82d5123db879b636176e9", + "34359738368": "02b2cee01b7d8e90180254459b8f09bbea9aad34c3a2fd98c85517ecfc9805af75", + "68719476736": "037a0c0d564540fc574b8bfa0253cca987b75466e44b295ed59f6f8bd41aace754", + "137438953472": "021df6585cae9b9ca431318a713fd73dbb76b3ef5667957e8633bca8aaa7214fb6", + "274877906944": "02b8f53dde126f8c85fa5bb6061c0be5aca90984ce9b902966941caf963648d53a", + "549755813888": "029cc8af2840d59f1d8761779b2496623c82c64be8e15f9ab577c657c6dd453785", + "1099511627776": "03e446fdb84fad492ff3a25fc1046fb9a93a5b262ebcd0151caa442ea28959a38a", + "2199023255552": "02d6b25bd4ab599dd0818c55f75702fde603c93f259222001246569018842d3258", + "4398046511104": "03397b522bb4e156ec3952d3f048e5a986c20a00718e5e52cd5718466bf494156a", + "8796093022208": "02d1fb9e78262b5d7d74028073075b80bb5ab281edcfc3191061962c1346340f1e", + "17592186044416": "030d3f2ad7a4ca115712ff7f140434f802b19a4c9b2dd1c76f3e8e80c05c6a9310", + "35184372088832": "03e325b691f292e1dfb151c3fb7cad440b225795583c32e24e10635a80e4221c06", + "70368744177664": "03bee8f64d88de3dee21d61f89efa32933da51152ddbd67466bef815e9f93f8fd1", + "140737488355328": "0327244c9019a4892e1f04ba3bf95fe43b327479e2d57c25979446cc508cd379ed", + "281474976710656": "02fb58522cd662f2f8b042f8161caae6e45de98283f74d4e99f19b0ea85e08a56d", + "562949953421312": "02adde4b466a9d7e59386b6a701a39717c53f30c4810613c1b55e6b6da43b7bc9a", + "1125899906842624": "038eeda11f78ce05c774f30e393cda075192b890d68590813ff46362548528dca9", + "2251799813685248": "02ec13e0058b196db80f7079d329333b330dc30c000dbdd7397cbbc5a37a664c4f", + "4503599627370496": "02d2d162db63675bd04f7d56df04508840f41e2ad87312a3c93041b494efe80a73", + "9007199254740992": "0356969d6aef2bb40121dbd07c68b6102339f4ea8e674a9008bb69506795998f49", + "18014398509481984": "02f4e667567ebb9f4e6e180a4113bb071c48855f657766bb5e9c776a880335d1d6", + "36028797018963968": "0385b4fe35e41703d7a657d957c67bb536629de57b7e6ee6fe2130728ef0fc90b0", + "72057594037927936": "02b2bc1968a6fddbcc78fb9903940524824b5f5bed329c6ad48a19b56068c144fd", + "144115188075855872": "02e0dbb24f1d288a693e8a49bc14264d1276be16972131520cf9e055ae92fba19a", + "288230376151711744": "03efe75c106f931a525dc2d653ebedddc413a2c7d8cb9da410893ae7d2fa7d19cc", + "576460752303423488": "02c7ec2bd9508a7fc03f73c7565dc600b30fd86f3d305f8f139c45c404a52d958a", + "1152921504606846976": "035a6679c6b25e68ff4e29d1c7ef87f21e0a8fc574f6a08c1aa45ff352c1d59f06", + "2305843009213693952": "033cdc225962c052d485f7cfbf55a5b2367d200fe1fe4373a347deb4cc99e9a099", + "4611686018427387904": "024a4b806cf413d14b294719090a9da36ba75209c7657135ad09bc65328fba9e6f", + "9223372036854775808": "0377a6fe114e291a8d8e991627c38001c8305b23b9e98b1c7b1893f5cd0dda6cad" } ``` diff --git a/tests/11-test.md b/tests/11-test.md index d43f1961..af2922dc 100644 --- a/tests/11-test.md +++ b/tests/11-test.md @@ -3,29 +3,71 @@ The following is a `Proof` with a valid signature. ```json -{"amount":1,"secret":"[\"P2PK\",{\"nonce\":\"859d4935c4907062a6297cf4e663e2835d90d97ecdd510745d32f6816323a41f\",\"data\":\"0249098aa8b9d2fbec49ff8598feb17b592b986e62319a4fa488a3dc36387157a7\",\"tags\":[[\"sigflag\",\"SIG_INPUTS\"]]}]","C":"02698c4e2b5f9534cd0687d87513c759790cf829aa5739184a3e3735471fbda904","id":"009a1f293253e41e","witness":"{\"signatures\":[\"60f3c9b766770b46caac1d27e1ae6b77c8866ebaeba0b9489fe6a15a837eaa6fcd6eaa825499c72ac342983983fd3ba3a8a41f56677cc99ffd73da68b59e1383\"]}"} +{ + "amount": 1, + "secret": "[\"P2PK\",{\"nonce\":\"859d4935c4907062a6297cf4e663e2835d90d97ecdd510745d32f6816323a41f\",\"data\":\"0249098aa8b9d2fbec49ff8598feb17b592b986e62319a4fa488a3dc36387157a7\",\"tags\":[[\"sigflag\",\"SIG_INPUTS\"]]}]", + "C": "02698c4e2b5f9534cd0687d87513c759790cf829aa5739184a3e3735471fbda904", + "id": "009a1f293253e41e", + "witness": "{\"signatures\":[\"60f3c9b766770b46caac1d27e1ae6b77c8866ebaeba0b9489fe6a15a837eaa6fcd6eaa825499c72ac342983983fd3ba3a8a41f56677cc99ffd73da68b59e1383\"]}" +} ``` The following is a `Proof` with an invalid signature as it is on a different secret. + ```json -{"amount":1,"secret":"[\"P2PK\",{\"nonce\":\"0ed3fcb22c649dd7bbbdcca36e0c52d4f0187dd3b6a19efcc2bfbebb5f85b2a1\",\"data\":\"0249098aa8b9d2fbec49ff8598feb17b592b986e62319a4fa488a3dc36387157a7\",\"tags\":[[\"pubkeys\",\"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798\",\"02142715675faf8da1ecc4d51e0b9e539fa0d52fdd96ed60dbe99adb15d6b05ad9\"],[\"n_sigs\",\"2\"],[\"sigflag\",\"SIG_INPUTS\"]]}]","C":"02698c4e2b5f9534cd0687d87513c759790cf829aa5739184a3e3735471fbda904","id":"009a1f293253e41e","witness":"{\"signatures\":[\"83564aca48c668f50d022a426ce0ed19d3a9bdcffeeaee0dc1e7ea7e98e9eff1840fcc821724f623468c94f72a8b0a7280fa9ef5a54a1b130ef3055217f467b3\"]}"} +{ + "amount": 1, + "secret": "[\"P2PK\",{\"nonce\":\"0ed3fcb22c649dd7bbbdcca36e0c52d4f0187dd3b6a19efcc2bfbebb5f85b2a1\",\"data\":\"0249098aa8b9d2fbec49ff8598feb17b592b986e62319a4fa488a3dc36387157a7\",\"tags\":[[\"pubkeys\",\"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798\",\"02142715675faf8da1ecc4d51e0b9e539fa0d52fdd96ed60dbe99adb15d6b05ad9\"],[\"n_sigs\",\"2\"],[\"sigflag\",\"SIG_INPUTS\"]]}]", + "C": "02698c4e2b5f9534cd0687d87513c759790cf829aa5739184a3e3735471fbda904", + "id": "009a1f293253e41e", + "witness": "{\"signatures\":[\"83564aca48c668f50d022a426ce0ed19d3a9bdcffeeaee0dc1e7ea7e98e9eff1840fcc821724f623468c94f72a8b0a7280fa9ef5a54a1b130ef3055217f467b3\"]}" +} ``` The following is a `Proof` with 2 signatures required to meet the multi-signature spend condition. + ```json -{"amount":1,"secret":"[\"P2PK\",{\"nonce\":\"0ed3fcb22c649dd7bbbdcca36e0c52d4f0187dd3b6a19efcc2bfbebb5f85b2a1\",\"data\":\"0249098aa8b9d2fbec49ff8598feb17b592b986e62319a4fa488a3dc36387157a7\",\"tags\":[[\"pubkeys\",\"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798\",\"02142715675faf8da1ecc4d51e0b9e539fa0d52fdd96ed60dbe99adb15d6b05ad9\"],[\"n_sigs\",\"2\"],[\"sigflag\",\"SIG_INPUTS\"]]}]","C":"02698c4e2b5f9534cd0687d87513c759790cf829aa5739184a3e3735471fbda904","id":"009a1f293253e41e","witness":"{\"signatures\":[\"83564aca48c668f50d022a426ce0ed19d3a9bdcffeeaee0dc1e7ea7e98e9eff1840fcc821724f623468c94f72a8b0a7280fa9ef5a54a1b130ef3055217f467b3\",\"9a72ca2d4d5075be5b511ee48dbc5e45f259bcf4a4e8bf18587f433098a9cd61ff9737dc6e8022de57c76560214c4568377792d4c2c6432886cc7050487a1f22\"]}"} +{ + "amount": 1, + "secret": "[\"P2PK\",{\"nonce\":\"0ed3fcb22c649dd7bbbdcca36e0c52d4f0187dd3b6a19efcc2bfbebb5f85b2a1\",\"data\":\"0249098aa8b9d2fbec49ff8598feb17b592b986e62319a4fa488a3dc36387157a7\",\"tags\":[[\"pubkeys\",\"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798\",\"02142715675faf8da1ecc4d51e0b9e539fa0d52fdd96ed60dbe99adb15d6b05ad9\"],[\"n_sigs\",\"2\"],[\"sigflag\",\"SIG_INPUTS\"]]}]", + "C": "02698c4e2b5f9534cd0687d87513c759790cf829aa5739184a3e3735471fbda904", + "id": "009a1f293253e41e", + "witness": "{\"signatures\":[\"83564aca48c668f50d022a426ce0ed19d3a9bdcffeeaee0dc1e7ea7e98e9eff1840fcc821724f623468c94f72a8b0a7280fa9ef5a54a1b130ef3055217f467b3\",\"9a72ca2d4d5075be5b511ee48dbc5e45f259bcf4a4e8bf18587f433098a9cd61ff9737dc6e8022de57c76560214c4568377792d4c2c6432886cc7050487a1f22\"]}" +} ``` The following is a `Proof` with one one signature failing the multi-signature spend condition. + ```json -{"amount":1,"secret":"[\"P2PK\",{\"nonce\":\"0ed3fcb22c649dd7bbbdcca36e0c52d4f0187dd3b6a19efcc2bfbebb5f85b2a1\",\"data\":\"0249098aa8b9d2fbec49ff8598feb17b592b986e62319a4fa488a3dc36387157a7\",\"tags\":[[\"pubkeys\",\"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798\",\"02142715675faf8da1ecc4d51e0b9e539fa0d52fdd96ed60dbe99adb15d6b05ad9\"],[\"n_sigs\",\"2\"],[\"sigflag\",\"SIG_INPUTS\"]]}]","C":"02698c4e2b5f9534cd0687d87513c759790cf829aa5739184a3e3735471fbda904","id":"009a1f293253e41e","witness":"{\"signatures\":[\"83564aca48c668f50d022a426ce0ed19d3a9bdcffeeaee0dc1e7ea7e98e9eff1840fcc821724f623468c94f72a8b0a7280fa9ef5a54a1b130ef3055217f467b3\"]}"} +{ + "amount": 1, + "secret": "[\"P2PK\",{\"nonce\":\"0ed3fcb22c649dd7bbbdcca36e0c52d4f0187dd3b6a19efcc2bfbebb5f85b2a1\",\"data\":\"0249098aa8b9d2fbec49ff8598feb17b592b986e62319a4fa488a3dc36387157a7\",\"tags\":[[\"pubkeys\",\"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798\",\"02142715675faf8da1ecc4d51e0b9e539fa0d52fdd96ed60dbe99adb15d6b05ad9\"],[\"n_sigs\",\"2\"],[\"sigflag\",\"SIG_INPUTS\"]]}]", + "C": "02698c4e2b5f9534cd0687d87513c759790cf829aa5739184a3e3735471fbda904", + "id": "009a1f293253e41e", + "witness": "{\"signatures\":[\"83564aca48c668f50d022a426ce0ed19d3a9bdcffeeaee0dc1e7ea7e98e9eff1840fcc821724f623468c94f72a8b0a7280fa9ef5a54a1b130ef3055217f467b3\"]}" +} ``` + The following is a `Proof` with a signature from the refund key that is spendable because the locktime is in the past. + ```json -{"amount":1,"id":"009a1f293253e41e","secret":"[\"P2PK\",{\"nonce\":\"902685f492ef3bb2ca35a47ddbba484a3365d143b9776d453947dcbf1ddf9689\",\"data\":\"026f6a2b1d709dbca78124a9f30a742985f7eddd894e72f637f7085bf69b997b9a\",\"tags\":[[\"pubkeys\",\"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798\",\"03142715675faf8da1ecc4d51e0b9e539fa0d52fdd96ed60dbe99adb15d6b05ad9\"],[\"locktime\",\"21\"],[\"n_sigs\",\"2\"],[\"refund\",\"026f6a2b1d709dbca78124a9f30a742985f7eddd894e72f637f7085bf69b997b9a\"],[\"sigflag\",\"SIG_INPUTS\"]]}]","C":"02698c4e2b5f9534cd0687d87513c759790cf829aa5739184a3e3735471fbda904","witness":"{\"signatures\":[\"710507b4bc202355c91ea3c147c0d0189c75e179d995e566336afd759cb342bcad9a593345f559d9b9e108ac2c9b5bd9f0b4b6a295028a98606a0a2e95eb54f7\"]}"} +{ + "amount": 1, + "id": "009a1f293253e41e", + "secret": "[\"P2PK\",{\"nonce\":\"902685f492ef3bb2ca35a47ddbba484a3365d143b9776d453947dcbf1ddf9689\",\"data\":\"026f6a2b1d709dbca78124a9f30a742985f7eddd894e72f637f7085bf69b997b9a\",\"tags\":[[\"pubkeys\",\"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798\",\"03142715675faf8da1ecc4d51e0b9e539fa0d52fdd96ed60dbe99adb15d6b05ad9\"],[\"locktime\",\"21\"],[\"n_sigs\",\"2\"],[\"refund\",\"026f6a2b1d709dbca78124a9f30a742985f7eddd894e72f637f7085bf69b997b9a\"],[\"sigflag\",\"SIG_INPUTS\"]]}]", + "C": "02698c4e2b5f9534cd0687d87513c759790cf829aa5739184a3e3735471fbda904", + "witness": "{\"signatures\":[\"710507b4bc202355c91ea3c147c0d0189c75e179d995e566336afd759cb342bcad9a593345f559d9b9e108ac2c9b5bd9f0b4b6a295028a98606a0a2e95eb54f7\"]}" +} ``` The following is a `Proof` with a signature from the refund key that is **not** spendable because the locktime is in the future. + ```json -{"amount":1,"id":"009a1f293253e41e","secret":"[\"P2PK\",{\"nonce\":\"64c46e5d30df27286166814b71b5d69801704f23a7ad626b05688fbdb48dcc98\",\"data\":\"026f6a2b1d709dbca78124a9f30a742985f7eddd894e72f637f7085bf69b997b9a\",\"tags\":[[\"pubkeys\",\"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798\",\"03142715675faf8da1ecc4d51e0b9e539fa0d52fdd96ed60dbe99adb15d6b05ad9\"],[\"locktime\",\"21\"],[\"n_sigs\",\"2\"],[\"refund\",\"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798\"],[\"sigflag\",\"SIG_INPUTS\"]]}]","C":"02698c4e2b5f9534cd0687d87513c759790cf829aa5739184a3e3735471fbda904","witness":"{\"signatures\":[\"f661d3dc046d636d47cb3d06586da42c498f0300373d1c2a4f417a44252cdf3809bce207c8888f934dba0d2b1671f1b8622d526840f2d5883e571b462630c1ff\"]}"} +{ + "amount": 1, + "id": "009a1f293253e41e", + "secret": "[\"P2PK\",{\"nonce\":\"64c46e5d30df27286166814b71b5d69801704f23a7ad626b05688fbdb48dcc98\",\"data\":\"026f6a2b1d709dbca78124a9f30a742985f7eddd894e72f637f7085bf69b997b9a\",\"tags\":[[\"pubkeys\",\"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798\",\"03142715675faf8da1ecc4d51e0b9e539fa0d52fdd96ed60dbe99adb15d6b05ad9\"],[\"locktime\",\"21\"],[\"n_sigs\",\"2\"],[\"refund\",\"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798\"],[\"sigflag\",\"SIG_INPUTS\"]]}]", + "C": "02698c4e2b5f9534cd0687d87513c759790cf829aa5739184a3e3735471fbda904", + "witness": "{\"signatures\":[\"f661d3dc046d636d47cb3d06586da42c498f0300373d1c2a4f417a44252cdf3809bce207c8888f934dba0d2b1671f1b8622d526840f2d5883e571b462630c1ff\"]}" +} ``` diff --git a/tests/12-tests.md b/tests/12-tests.md index 8ec314c7..a00518d9 100644 --- a/tests/12-tests.md +++ b/tests/12-tests.md @@ -16,6 +16,7 @@ hash(R1, R2, K, C_): "a4dc034b74338c28c6bc3ea49731f2a24440fc7c4affc08b31a93fc9fb ## DLEQ verification on `BlindSignature` The following is a `BlindSignature` with a **valid** DLEQ proof. + ```shell A: "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798" B_: "02a9acc1e48c25eeeb9289b5031cc57da9fe72f3fe2861d264bdc074209b107ba2" diff --git a/tests/13-tests.md b/tests/13-tests.md index 9f35178e..34086884 100644 --- a/tests/13-tests.md +++ b/tests/13-tests.md @@ -5,10 +5,10 @@ The integer representation of a keyset with an ID `009a1f293253e41e` and its corresponding derivation path for a counter of value `{counter}` are ```json -{ - "keyset_id": "009a1f293253e41e", - "keyest_id_int": 864559728, - "derivation_path": "m/129372'/0'/864559728'/{counter}'" +{ + "keyset_id": "009a1f293253e41e", + "keyest_id_int": 864559728, + "derivation_path": "m/129372'/0'/864559728'/{counter}'" } ``` @@ -18,7 +18,7 @@ We derive values starting from the following BIP39 mnemonic. ```json { - "mnemonic": "half depart obvious quality work element tank gorilla view sugar picture humble" + "mnemonic": "half depart obvious quality work element tank gorilla view sugar picture humble" } ``` @@ -26,11 +26,11 @@ The secrets derived for the first five counters from `counter=0` to `counter=4` ```json { - "secret_0": "485875df74771877439ac06339e284c3acfcd9be7abf3bc20b516faeadfe77ae", - "secret_1": "8f2b39e8e594a4056eb1e6dbb4b0c38ef13b1b2c751f64f810ec04ee35b77270", - "secret_2": "bc628c79accd2364fd31511216a0fab62afd4a18ff77a20deded7b858c9860c8", - "secret_3": "59284fd1650ea9fa17db2b3acf59ecd0f2d52ec3261dd4152785813ff27a33bf", - "secret_4": "576c23393a8b31cc8da6688d9c9a96394ec74b40fdaf1f693a6bb84284334ea0" + "secret_0": "485875df74771877439ac06339e284c3acfcd9be7abf3bc20b516faeadfe77ae", + "secret_1": "8f2b39e8e594a4056eb1e6dbb4b0c38ef13b1b2c751f64f810ec04ee35b77270", + "secret_2": "bc628c79accd2364fd31511216a0fab62afd4a18ff77a20deded7b858c9860c8", + "secret_3": "59284fd1650ea9fa17db2b3acf59ecd0f2d52ec3261dd4152785813ff27a33bf", + "secret_4": "576c23393a8b31cc8da6688d9c9a96394ec74b40fdaf1f693a6bb84284334ea0" } ``` @@ -38,11 +38,11 @@ The corresponding blinding factors `r` are ```json { - "r_0": "ad00d431add9c673e843d4c2bf9a778a5f402b985b8da2d5550bf39cda41d679", - "r_1": "967d5232515e10b81ff226ecf5a9e2e2aff92d66ebc3edf0987eb56357fd6248", - "r_2": "b20f47bb6ae083659f3aa986bfa0435c55c6d93f687d51a01f26862d9b9a4899", - "r_3": "fb5fca398eb0b1deb955a2988b5ac77d32956155f1c002a373535211a2dfdc29", - "r_4": "5f09bfbfe27c439a597719321e061e2e40aad4a36768bb2bcc3de547c9644bf9" + "r_0": "ad00d431add9c673e843d4c2bf9a778a5f402b985b8da2d5550bf39cda41d679", + "r_1": "967d5232515e10b81ff226ecf5a9e2e2aff92d66ebc3edf0987eb56357fd6248", + "r_2": "b20f47bb6ae083659f3aa986bfa0435c55c6d93f687d51a01f26862d9b9a4899", + "r_3": "fb5fca398eb0b1deb955a2988b5ac77d32956155f1c002a373535211a2dfdc29", + "r_4": "5f09bfbfe27c439a597719321e061e2e40aad4a36768bb2bcc3de547c9644bf9" } ``` @@ -50,10 +50,10 @@ The corresponding derivation paths are ```json { - "derivation_path_0": "m/129372'/0'/864559728'/0'", - "derivation_path_1": "m/129372'/0'/864559728'/1'", - "derivation_path_2": "m/129372'/0'/864559728'/2'", - "derivation_path_3": "m/129372'/0'/864559728'/3'", - "derivation_path_4": "m/129372'/0'/864559728'/4'" + "derivation_path_0": "m/129372'/0'/864559728'/0'", + "derivation_path_1": "m/129372'/0'/864559728'/1'", + "derivation_path_2": "m/129372'/0'/864559728'/2'", + "derivation_path_3": "m/129372'/0'/864559728'/3'", + "derivation_path_4": "m/129372'/0'/864559728'/4'" } -``` \ No newline at end of file +``` diff --git a/tests/README.md b/tests/README.md index 070b2e39..557ec867 100644 --- a/tests/README.md +++ b/tests/README.md @@ -1,2 +1,3 @@ # Test Vectors + The files in this directory contain test vectors for NUTs that warrant them.