diff --git a/02.md b/02.md index 64d726a..b1efa55 100644 --- a/02.md +++ b/02.md @@ -14,10 +14,14 @@ A mint can have multiple keysets at the same time. For example, it could have on ### Keyset ID -A keyset `id` is an identifier for a specific keyset. It can be derived by anyone who knows the set of public keys of a mint. Wallets **CAN** compute the keyset `id` for a given keyset by themselves to confirm that the mint is supplying the correct keyset ID (see below). +A keyset `id` is an identifier for a specific keyset. It can be derived by anyone who knows the set of public keys of a mint. Wallets **MAY** compute the keyset `id` for a given keyset by themselves to confirm that the mint is supplying the correct keyset ID (see below). The keyset `id` is in each `Proof` so it can be used by wallets to identify which mint and keyset it was generated from. The keyset field `id` is also present in the `BlindedMessages` sent to the mint and `BlindSignatures` returned from the mint (see [NUT-00][00]). +To save space, a `Token`, as defined in [NUT-00][00], **SHOULD** contain an abbreviated version of the keyset `id` in the `Proof`, (the `s_id`). The length of the `s_id` **MUST** be eight bytes (i.e., the version byte and the first seven bytes of the hash: `id_bytes[:8]` or `id_hex[:16]`). A wallet **MUST** resolve the abbreviated keyset `id` to the full `id`. If the abbreviated `s_id` is ambiguous (i.e., multiple keyset `id`s are resolvable), the wallet **MUST** error. The full keyset `id` is only needed when the wallet interacts with the mint. + +A Wallet **SHOULD** save the full-length keyset `id` with proofs in its database. + ### Active keysets Mints can have multiple keysets at the same time but **MUST** have at least one `active` keyset (see [NUT-01][01]). The `active` property determines whether the mint allows generating new ecash from this keyset. `Proofs` from inactive keysets with `active=false` are still accepted as inputs but new outputs (`BlindedMessages` and `BlindSignatures`) **MUST** be from `active` keysets only. @@ -56,12 +60,32 @@ Notice that since transactions can spend inputs from different keysets, the sum ### Deriving the keyset ID -#### Keyset ID version +#### Keyset ID -Keyset IDs have a version byte (two hexadecimal characters). The currently used version byte is `00`. +Keyset IDs have a version byte (two hexadecimal characters). The currently used version byte is `01`. The mint and the wallets of its users can derive a keyset ID from the keyset of the mint. The keyset ID is a lower-case hex string. To derive the keyset ID of a keyset, execute the following steps: +``` +1 - sort public keys by their amount in ascending order +2 - concatenate all public keys to one byte array +3 - add the unit string to the byte array (e.g. "unit:sat") +4 - HASH_SHA256 the concatenated byte array +5 - prefix it with a keyset ID version byte +``` + +An example implementation in Python: + +```python +def derive_keyset_id(keys: Dict[int, PublicKey]) -> str: + sorted_keys = dict(sorted(keys.items())) + keyset_id_bytes = b"".join([p.serialize() for p in sorted_keys.values()]) + keyset_id_bytes += b"unit:sat" + return "01" + hashlib.sha256(keyset_id_bytes).hexdigest() +``` + +##### Version 0 + ``` 1 - sort public keys by their amount in ascending order 2 - concatenate all public keys to one byte array @@ -119,21 +143,21 @@ Here, `id` is the keyset ID, `unit` is the unit string (e.g. "sat") of the keyse { "keysets": [ { - "id": "009a1f293253e41e", + "id": "01c9c20fb8b348b389e296227c6cc7a63f77354b7388c720dbba6218f720f9b785", "unit": "sat", - "active": True, + "active": true, "input_fee_ppk": 100 }, { - "id": "0042ade98b2a370a", + "id": "0188432103b12cec6361587d92bdfb798079c58b1c828c561b4daec6f4d465a810", "unit": "sat", - "active": False, + "active": false, "input_fee_ppk": 100 }, { - "id": "00c074b96c7e2b0e", + "id": "01d0257bde6ff4cd55e49318a824bbe67e2f9faa248ff108203b5fe46581b14ffc", "unit": "usd", - "active": True, + "active": true, "input_fee_ppk": 100 } ] @@ -148,16 +172,16 @@ To receive the public keys of a specific keyset, a wallet can call the `GET /v1/ Request of `Alice`: -We request the keys for the keyset `009a1f293253e41e`. +We request the keys for the keyset `01c9c20fb8b348b389e296227c6cc7a63f77354b7388c720dbba6218f720f9b785`. ```http -GET https://mint.host:3338/v1/keys/009a1f293253e41e +GET https://mint.host:3338/v1/keys/01c9c20fb8b348b389e296227c6cc7a63f77354b7388c720dbba6218f720f9b785 ``` With curl: ```bash -curl -X GET https://mint.host:3338/v1/keys/009a1f293253e41e +curl -X GET https://mint.host:3338/v1/keys/01c9c20fb8b348b389e296227c6cc7a63f77354b7388c720dbba6218f720f9b785 ``` Response of `Bob` (same as [NUT-01][01]): @@ -165,7 +189,7 @@ Response of `Bob` (same as [NUT-01][01]): ```json { "keysets": [{ - "id": "009a1f293253e41e", + "id": "01c9c20fb8b348b389e296227c6cc7a63f77354b7388c720dbba6218f720f9b785", "unit": "sat", "keys": { "1": "02194603ffa36356f4a56b7df9371fc3192472351453ec7398b8da8117e7c3e104", diff --git a/13.md b/13.md index 8d4f2ae..777879a 100644 --- a/13.md +++ b/13.md @@ -43,7 +43,7 @@ The wallet starts with `counter_k := 0` upon encountering a new keyset and incre #### Keyset ID -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`. +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`. Keyset IDs with version prefix `01` should be shortened to the first 8 bytes before conversion. Example in Python: