Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 11 additions & 11 deletions 00.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ Deterministically maps a message to a public key point on the secp256k1 curve, u
- `Y` derived public key
- `DOMAIN_SEPARATOR` constant byte string `b"Secp256k1_HashToCurve_Cashu_"`
- `x` message to hash
- `counter` uint32 counter(byte order little endian) incremented from 0 until a point is found that lies on the curve
- `counter` uint32 counter (byte order little endian) incremented from 0 until a point is found that lies on the curve

## Protocol

Expand All @@ -56,7 +56,7 @@ These specifications use elliptic curve public key cryptography over the field a

### Types

Values in these specification are one of:
Values in these specifications are one of:

- Scalars: integers modulo the curve order `n`
- Curve points: elements of the elliptic curve group (including the identity point at infinity)
Expand All @@ -65,8 +65,8 @@ Values in these specification are one of:
### Byte Operations And Encodings

- Concatenation `||` appends one byte sequence to another.
- All operations such as point addition, scalar multiplication, hashing, are performed on the underlying mathematical values, not on their serialised byte encodings.
- When a curve point is serialised as `hex_str`, it is the hex encoding of the SEC1 compressed public key bytes, unless a NUT specifies a different encoding.
- All operations such as point addition, scalar multiplication, hashing, are performed on the underlying mathematical values, not on their serialized byte encodings.
- When a curve point is serialized as `hex_str`, it is the hex encoding of the SEC1 compressed public key bytes, unless a NUT specifies a different encoding.
- Integer byte order and fixed widths are defined by each NUT where relevant, if a NUT is silent, it must not be assumed.

### Hashing
Expand All @@ -78,7 +78,7 @@ Values in these specification are one of:

### `BlindedMessage`

An encrypted ("blinded") secret and an amount is sent from `Alice` to `Bob` for [minting tokens][04] or for [swapping tokens][03]. A `BlindedMessage` is also called an _output_.
An encrypted ("blinded") secret and an amount are sent from `Alice` to `Bob` for [minting tokens][04] or for [swapping tokens][03]. A `BlindedMessage` is also called an _output_.

```json
{
Expand Down Expand Up @@ -113,11 +113,11 @@ A `Proof` is also called an _input_ and is generated by `Alice` from a `BlindSig
"amount": int,
"id": hex_str,
"secret": str,
"C": hex_str,
"C": hex_str
}
```

`amount` is the amount of the `Proof`, `secret` is the secret message and is a utf-8 encoded string (the use of a 64 character hex string generated from 32 random bytes is recommended to prevent fingerprinting), `C` is the unblinded signature on `secret` (hex string), `id` is the [keyset id][02] of the mint public keys that signed the token (hex string).
`amount` is the amount of the `Proof`, `secret` is the secret message and is a UTF-8-encoded string (the use of a 64 character hex string generated from 32 random bytes is recommended to prevent fingerprinting), `C` is the unblinded signature on `secret` (hex string), `id` is the [keyset id][02] of the mint public keys that signed the token (hex string).

## 0.2 - Protocol

Expand Down Expand Up @@ -274,7 +274,7 @@ For readability, the structure of a TokenV4 object is shown below in its equival
]
},
...
],
]
}
```

Expand Down Expand Up @@ -349,11 +349,11 @@ cashuBo2F0gqJhaUgA_9SLj17PgGFwgaNhYQFhc3hAYWNjMTI0MzVlN2I4NDg0YzNjZjE4NTAxNDkyMT

#### Binary Token

Token can be transmitted in a binary format when applicable (for example when transmitting via NFC).
For this the serialised token is prepended with a prefix and a version byte.
Tokens can be transmitted in a binary format when applicable (for example when transmitting via NFC).
For this the serialized token is prepended with a prefix and a version byte.

```
utf8("craw") || utf8(<token_version>) || <serialised_token>
utf8("craw") || utf8(<token_version>) || <serialized_token>
```

- Binary Encoding V4: `utf8("craw") || utf8("B") || cbor(token_v4_object)`
Expand Down
6 changes: 3 additions & 3 deletions 01.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Note that a mint can support multiple keysets at the same time but will only res

## Supported Currency Units

A mint may support any currency unit(s) they can mint ([NUT-04][04]) and melt([NUT-05][05]), either directly or indirectly, including:
A mint may support any currency unit(s) they can mint ([NUT-04][04]) and melt ([NUT-05][05]), either directly or indirectly, including:

- `btc`: Bitcoin (Minor Unit: 8) - though use of the `sat` unit is generally preferred
- `sat`: Bitcoin's Minor Unit (ie: `100,000,000 sat = 1 bitcoin`)
Expand All @@ -39,7 +39,7 @@ A mint may support any currency unit(s) they can mint ([NUT-04][04]) and melt([N

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]).

Keys in Keysets are maps of the form `{<amount_1> : <mint_pubkey_1>, <amount_2> : <mint_pubkey_2>, ...}` for each `<amount_i>` of the amounts the mint `Bob` supports and the corresponding public key `<mint_pubkey_1>`, that is `K_i` (see [NUT-00][00]). The mint **MUST** use the [compressed Secp256k1 public key format](https://learnmeabitcoin.com/technical/public-key#public-key-format) to represent its public keys.
Keys in Keysets are maps of the form `{<amount_1> : <mint_pubkey_1>, <amount_2> : <mint_pubkey_2>, ...}` for each `<amount_i>` of the amounts the mint `Bob` supports and the corresponding public key `<mint_pubkey_i>`, that is `K_i` (see [NUT-00][00]). The mint **MUST** use the [compressed Secp256k1 public key format](https://learnmeabitcoin.com/technical/public-key#public-key-format) to represent its public keys.

## Example

Expand All @@ -65,7 +65,7 @@ Response `GetKeysResponse` of `Bob`:
"unit": <currency_unit_str>,
"active": <bool>,
"input_fee_ppk": <int|null>,
"final_expiry": <unix_timestamp_int|null>
"final_expiry": <unix_timestamp_int|null>,
"keys": {
<amount_int>: <public_key_str>,
...
Expand Down
14 changes: 7 additions & 7 deletions 02.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,13 @@ Wallets **SHOULD** prioritize swaps with `Proofs` from inactive keysets (see [NU

### Fees

Keysets indicate the fee `input_fee_ppk` that is charged when a `Proof` of that keyset is spent as an input to a transaction. The fee is given in parts per thousand (ppk) per input measured in the `unit` of the keyset. The total fee for a transaction is the sum of all fees per input rounded up to the next larger integer (that that can be represented with the keyset).
Keysets indicate the fee `input_fee_ppk` that is charged when a `Proof` of that keyset is spent as an input to a transaction. The fee is given in parts per thousand (ppk) per input measured in the `unit` of the keyset. The total fee for a transaction is the sum of all fees per input rounded up to the next larger integer (that can be represented with the keyset).

As an example, we construct a transaction spending 3 inputs (`Proofs`) from a keyset with unit `sat` and `input_fee_ppk` of `100`. A fee of `100 ppk` means `0.1 sat` per input. The sum of the individual fees are 300 ppk for this transaction. Rounded up to the next smallest denomination, the mint charges `1 sat` in total fees, i.e. `fees = ceil(0.3) == 1`. In this case, the fees for spending 1-10 inputs is 1 sat, 11-20 inputs is 2 sat and so on.
As an example, we construct a transaction spending 3 inputs (`Proofs`) from a keyset with unit `sat` and `input_fee_ppk` of `100`. A fee of `100 ppk` means `0.1 sat` per input. The sum of the individual fees is 300 ppk for this transaction. Rounded up to the next smallest denomination, the mint charges `1 sat` in total fees, i.e. `fees = ceil(0.3) == 1`. In this case, the fees for spending 1-10 inputs is 1 sat, 11-20 inputs is 2 sat and so on.

#### Wallet transaction construction

When constructing a transaction with ecash `inputs` (example: `/v1/swap` or `/v1/melt`), wallets **MUST** add fees to the inputs or, vice versa, subtract from the outputs. The mint checks the following equation:
When constructing a transaction with ecash `inputs` (example: `/v1/swap` or `/v1/melt/{method}`), wallets **MUST** add fees to the inputs or, vice versa, subtract from the outputs. The mint checks the following equation:

```python
sum(inputs) - fees == sum(outputs)
Expand Down Expand Up @@ -109,8 +109,8 @@ def derive_keyset_id_v1(keys: Dict[int, PublicKey]) -> str:
return "00" + hashlib.sha256(pubkeys_concat).hexdigest()[:14]
```

> [!CRITICAL]
> Wallet implementations should reject any attempt at importing new keysets which IDs
> [!CAUTION]
> Wallet implementations should reject any attempt at importing new keysets whose IDs
> collide with any of the previously added keysets.

### Keyset Final Expiry
Expand Down Expand Up @@ -228,7 +228,7 @@ A useful flow is:

- If we don't have any keys from this mint yet, get all keys: `GET /v1/keys` and store them
- Get all keysets with `GET /v1/keysets`
- For all new keyset returned here which we don't have yet, get it using `GET /v1/keys/{keyset_id}` and store it
- For each new keyset returned here which we don't have yet, get it using `GET /v1/keys/{keyset_id}` and store it
- If any of the keysets has changed its `active` flag, update it in the db and use the keyset accordingly

[00]: 00.md
Expand All @@ -244,4 +244,4 @@ A useful flow is:
[10]: 10.md
[11]: 11.md
[12]: 12.md
[TokenV3]: https://github.com/cashuBTC/nuts/blob/main/00.md#023---v3-tokens
[TokenV3]: https://github.com/cashubtc/nuts/blob/main/00.md#v3-tokens
Loading