From f3f0280da210fac00e7806abf401deb6570ae677 Mon Sep 17 00:00:00 2001 From: David Caseria Date: Wed, 30 Oct 2024 11:24:14 -0400 Subject: [PATCH 1/6] Define Keyset ID V2 --- 00.md | 8 ++++---- 02.md | 30 +++++++++++++++++------------- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/00.md b/00.md index c4d714b..19ec571 100644 --- a/00.md +++ b/00.md @@ -268,7 +268,7 @@ Below is a TokenV4 JSON before CBOR and `base64_urlsafe` serialization. { "t": [ { - "i": h'00ffd48b8f5ecf80', + "i": h'01ff', "p": [ { "a": 1, @@ -278,7 +278,7 @@ Below is a TokenV4 JSON before CBOR and `base64_urlsafe` serialization. ], }, { - "i": h'00ad268c4d1f5826', + "i": h'01ad', "p": [ { "a": 2, @@ -300,10 +300,10 @@ Below is a TokenV4 JSON before CBOR and `base64_urlsafe` serialization. The `h''` values are `bytes` but displayed as hex strings here. -We serialize this JSON using CBOR which can be seen [here](https://cbor.nemo157.com/#type=hex&value=a3617482a261694800ffd48b8f5ecf80617081a36161016173784061636331323433356537623834383463336366313835303134393231386166393066373136613532626634613565643334376534386563633133663737333838616358210244538319de485d55bed3b29a642bee5879375ab9e7a620e11e48ba482421f3cfa261694800ad268c4d1f5826617082a3616102617378403133323364336434373037613538616432653233616461346539663166343966356135623461633762373038656230643631663733386634383330376538656561635821023456aa110d84b4ac747aebd82c3b005aca50bf457ebd5737a4414fac3ae7d94da36161016173784035366263626362623763633634303662336661356435376432313734663465666638623434303262313736393236643361353764336333646362623539643537616358210273129c5719e599379a974a626363c333c56cafc0e6d01abe46d5808280789c63616d75687474703a2f2f6c6f63616c686f73743a33333338617563736174). The resulting bytes are then serialized to a string using `base64_urlsafe` and the prefix `cashuB` is added. This leaves us with the following serialized TokenV4: +We serialize this JSON using CBOR which can be seen [here](https://cbor.nemo157.com/#type=hex&value=a3617482a261694201ff617081a36161016173784061636331323433356537623834383463336366313835303134393231386166393066373136613532626634613565643334376534386563633133663737333838616358210244538319de485d55bed3b29a642bee5879375ab9e7a620e11e48ba482421f3cfa261694201ad617082a3616102617378403133323364336434373037613538616432653233616461346539663166343966356135623461633762373038656230643631663733386634383330376538656561635821023456aa110d84b4ac747aebd82c3b005aca50bf457ebd5737a4414fac3ae7d94da36161016173784035366263626362623763633634303662336661356435376432313734663465666638623434303262313736393236643361353764336333646362623539643537616358210273129c5719e599379a974a626363c333c56cafc0e6d01abe46d5808280789c63616d75687474703a2f2f6c6f63616c686f73743a33333338617563736174). The resulting bytes are then serialized to a string using `base64_urlsafe` and the prefix `cashuB` is added. This leaves us with the following serialized TokenV4: ``` -cashuBo2F0gqJhaUgA_9SLj17PgGFwgaNhYQFhc3hAYWNjMTI0MzVlN2I4NDg0YzNjZjE4NTAxNDkyMThhZjkwZjcxNmE1MmJmNGE1ZWQzNDdlNDhlY2MxM2Y3NzM4OGFjWCECRFODGd5IXVW-07KaZCvuWHk3WrnnpiDhHki6SCQh88-iYWlIAK0mjE0fWCZhcIKjYWECYXN4QDEzMjNkM2Q0NzA3YTU4YWQyZTIzYWRhNGU5ZjFmNDlmNWE1YjRhYzdiNzA4ZWIwZDYxZjczOGY0ODMwN2U4ZWVhY1ghAjRWqhENhLSsdHrr2Cw7AFrKUL9Ffr1XN6RBT6w659lNo2FhAWFzeEA1NmJjYmNiYjdjYzY0MDZiM2ZhNWQ1N2QyMTc0ZjRlZmY4YjQ0MDJiMTc2OTI2ZDNhNTdkM2MzZGNiYjU5ZDU3YWNYIQJzEpxXGeWZN5qXSmJjY8MzxWyvwObQGr5G1YCCgHicY2FtdWh0dHA6Ly9sb2NhbGhvc3Q6MzMzOGF1Y3NhdA +cashuBo2F0gqJhaUIB_2FwgaNhYQFhc3hAYWNjMTI0MzVlN2I4NDg0YzNjZjE4NTAxNDkyMThhZjkwZjcxNmE1MmJmNGE1ZWQzNDdlNDhlY2MxM2Y3NzM4OGFjWCECRFODGd5IXVW-07KaZCvuWHk3WrnnpiDhHki6SCQh88-iYWlCAa1hcIKjYWECYXN4QDEzMjNkM2Q0NzA3YTU4YWQyZTIzYWRhNGU5ZjFmNDlmNWE1YjRhYzdiNzA4ZWIwZDYxZjczOGY0ODMwN2U4ZWVhY1ghAjRWqhENhLSsdHrr2Cw7AFrKUL9Ffr1XN6RBT6w659lNo2FhAWFzeEA1NmJjYmNiYjdjYzY0MDZiM2ZhNWQ1N2QyMTc0ZjRlZmY4YjQ0MDJiMTc2OTI2ZDNhNTdkM2MzZGNiYjU5ZDU3YWNYIQJzEpxXGeWZN5qXSmJjY8MzxWyvwObQGr5G1YCCgHicY2FtdWh0dHA6Ly9sb2NhbGhvc3Q6MzMzOGF1Y3NhdA ``` [00]: 00.md diff --git a/02.md b/02.md index 64d726a..36c2d54 100644 --- a/02.md +++ b/02.md @@ -14,10 +14,12 @@ 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 definied in [NUT-00][00], **SHOULD** contain an abbreviated version of the keyset `id` in the `Proof`. The length of the abbreviated `id` **MUST** be at least two bytes (i.e., the version byte and first byte of the hash). However, when the token is serialized, the chosen `id` length **MUST** not be ambiguous within the mint's existing keyset `id`s. A wallet **MUST** resolve the abbreviated keyset `id` to the full `id`. If the abbreviated `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. + ### 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. @@ -65,8 +67,8 @@ The mint and the wallets of its users can derive a keyset ID from the keyset of ``` 1 - sort public keys by their amount in ascending order 2 - concatenate all public keys to one byte array -3 - HASH_SHA256 the concatenated public keys -4 - take the first 14 characters of the hex-encoded hash +3 - add the unit string to the byte array (e.g. "unit:sat") +4 - HASH_SHA256 the concatenated public keys 5 - prefix it with a keyset ID version byte ``` @@ -75,8 +77,9 @@ An example implementation in Python: ```python def derive_keyset_id(keys: Dict[int, PublicKey]) -> str: sorted_keys = dict(sorted(keys.items())) - pubkeys_concat = b"".join([p.serialize() for p in sorted_keys.values()]) - return "00" + hashlib.sha256(pubkeys_concat).hexdigest()[:14] + 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() ``` ## Example: Get mint keysets @@ -119,19 +122,19 @@ 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, "input_fee_ppk": 100 }, { - "id": "0042ade98b2a370a", + "id": "0188432103b12cec6361587d92bdfb798079c58b1c828c561b4daec6f4d465a810", "unit": "sat", "active": False, "input_fee_ppk": 100 }, { - "id": "00c074b96c7e2b0e", + "id": "01d0257bde6ff4cd55e49318a824bbe67e2f9faa248ff108203b5fe46581b14ffc", "unit": "usd", "active": True, "input_fee_ppk": 100 @@ -142,22 +145,23 @@ 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. +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. ### Example 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 +169,7 @@ Response of `Bob` (same as [NUT-01][01]): ```json { "keysets": [{ - "id": "009a1f293253e41e", + "id": "01c9c20fb8b348b389e296227c6cc7a63f77354b7388c720dbba6218f720f9b785", "unit": "sat", "keys": { "1": "02194603ffa36356f4a56b7df9371fc3192472351453ec7398b8da8117e7c3e104", From 61af756b2ad54a3cd1b6c53dfa6cfdbb4d64dff5 Mon Sep 17 00:00:00 2001 From: David Caseria Date: Fri, 1 Nov 2024 15:15:43 -0400 Subject: [PATCH 2/6] Address PR feedback --- 00.md | 8 ++++---- 02.md | 27 +++++++++++++++++++++++---- 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/00.md b/00.md index 19ec571..c4d714b 100644 --- a/00.md +++ b/00.md @@ -268,7 +268,7 @@ Below is a TokenV4 JSON before CBOR and `base64_urlsafe` serialization. { "t": [ { - "i": h'01ff', + "i": h'00ffd48b8f5ecf80', "p": [ { "a": 1, @@ -278,7 +278,7 @@ Below is a TokenV4 JSON before CBOR and `base64_urlsafe` serialization. ], }, { - "i": h'01ad', + "i": h'00ad268c4d1f5826', "p": [ { "a": 2, @@ -300,10 +300,10 @@ Below is a TokenV4 JSON before CBOR and `base64_urlsafe` serialization. The `h''` values are `bytes` but displayed as hex strings here. -We serialize this JSON using CBOR which can be seen [here](https://cbor.nemo157.com/#type=hex&value=a3617482a261694201ff617081a36161016173784061636331323433356537623834383463336366313835303134393231386166393066373136613532626634613565643334376534386563633133663737333838616358210244538319de485d55bed3b29a642bee5879375ab9e7a620e11e48ba482421f3cfa261694201ad617082a3616102617378403133323364336434373037613538616432653233616461346539663166343966356135623461633762373038656230643631663733386634383330376538656561635821023456aa110d84b4ac747aebd82c3b005aca50bf457ebd5737a4414fac3ae7d94da36161016173784035366263626362623763633634303662336661356435376432313734663465666638623434303262313736393236643361353764336333646362623539643537616358210273129c5719e599379a974a626363c333c56cafc0e6d01abe46d5808280789c63616d75687474703a2f2f6c6f63616c686f73743a33333338617563736174). The resulting bytes are then serialized to a string using `base64_urlsafe` and the prefix `cashuB` is added. This leaves us with the following serialized TokenV4: +We serialize this JSON using CBOR which can be seen [here](https://cbor.nemo157.com/#type=hex&value=a3617482a261694800ffd48b8f5ecf80617081a36161016173784061636331323433356537623834383463336366313835303134393231386166393066373136613532626634613565643334376534386563633133663737333838616358210244538319de485d55bed3b29a642bee5879375ab9e7a620e11e48ba482421f3cfa261694800ad268c4d1f5826617082a3616102617378403133323364336434373037613538616432653233616461346539663166343966356135623461633762373038656230643631663733386634383330376538656561635821023456aa110d84b4ac747aebd82c3b005aca50bf457ebd5737a4414fac3ae7d94da36161016173784035366263626362623763633634303662336661356435376432313734663465666638623434303262313736393236643361353764336333646362623539643537616358210273129c5719e599379a974a626363c333c56cafc0e6d01abe46d5808280789c63616d75687474703a2f2f6c6f63616c686f73743a33333338617563736174). The resulting bytes are then serialized to a string using `base64_urlsafe` and the prefix `cashuB` is added. This leaves us with the following serialized TokenV4: ``` -cashuBo2F0gqJhaUIB_2FwgaNhYQFhc3hAYWNjMTI0MzVlN2I4NDg0YzNjZjE4NTAxNDkyMThhZjkwZjcxNmE1MmJmNGE1ZWQzNDdlNDhlY2MxM2Y3NzM4OGFjWCECRFODGd5IXVW-07KaZCvuWHk3WrnnpiDhHki6SCQh88-iYWlCAa1hcIKjYWECYXN4QDEzMjNkM2Q0NzA3YTU4YWQyZTIzYWRhNGU5ZjFmNDlmNWE1YjRhYzdiNzA4ZWIwZDYxZjczOGY0ODMwN2U4ZWVhY1ghAjRWqhENhLSsdHrr2Cw7AFrKUL9Ffr1XN6RBT6w659lNo2FhAWFzeEA1NmJjYmNiYjdjYzY0MDZiM2ZhNWQ1N2QyMTc0ZjRlZmY4YjQ0MDJiMTc2OTI2ZDNhNTdkM2MzZGNiYjU5ZDU3YWNYIQJzEpxXGeWZN5qXSmJjY8MzxWyvwObQGr5G1YCCgHicY2FtdWh0dHA6Ly9sb2NhbGhvc3Q6MzMzOGF1Y3NhdA +cashuBo2F0gqJhaUgA_9SLj17PgGFwgaNhYQFhc3hAYWNjMTI0MzVlN2I4NDg0YzNjZjE4NTAxNDkyMThhZjkwZjcxNmE1MmJmNGE1ZWQzNDdlNDhlY2MxM2Y3NzM4OGFjWCECRFODGd5IXVW-07KaZCvuWHk3WrnnpiDhHki6SCQh88-iYWlIAK0mjE0fWCZhcIKjYWECYXN4QDEzMjNkM2Q0NzA3YTU4YWQyZTIzYWRhNGU5ZjFmNDlmNWE1YjRhYzdiNzA4ZWIwZDYxZjczOGY0ODMwN2U4ZWVhY1ghAjRWqhENhLSsdHrr2Cw7AFrKUL9Ffr1XN6RBT6w659lNo2FhAWFzeEA1NmJjYmNiYjdjYzY0MDZiM2ZhNWQ1N2QyMTc0ZjRlZmY4YjQ0MDJiMTc2OTI2ZDNhNTdkM2MzZGNiYjU5ZDU3YWNYIQJzEpxXGeWZN5qXSmJjY8MzxWyvwObQGr5G1YCCgHicY2FtdWh0dHA6Ly9sb2NhbGhvc3Q6MzMzOGF1Y3NhdA ``` [00]: 00.md diff --git a/02.md b/02.md index 36c2d54..a164fe0 100644 --- a/02.md +++ b/02.md @@ -18,7 +18,7 @@ A keyset `id` is an identifier for a specific keyset. It can be derived by anyon 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 definied in [NUT-00][00], **SHOULD** contain an abbreviated version of the keyset `id` in the `Proof`. The length of the abbreviated `id` **MUST** be at least two bytes (i.e., the version byte and first byte of the hash). However, when the token is serialized, the chosen `id` length **MUST** not be ambiguous within the mint's existing keyset `id`s. A wallet **MUST** resolve the abbreviated keyset `id` to the full `id`. If the abbreviated `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. +To save space, a `Token`, as definied in [NUT-00][00], **SHOULD** contain an abbreviated version of the keyset `id` in the `Proof`. The length of the abbreviated `id` **MUST** be at least eight bytes (i.e., the version byte and first byte of the hash). However, when the token is serialized, the chosen `id` length **MUST** not be ambiguous within the mint's existing keyset `id`s. A wallet **MUST** resolve the abbreviated keyset `id` to the full `id`. If the abbreviated `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. ### Active keysets @@ -58,9 +58,9 @@ 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: @@ -68,7 +68,7 @@ The mint and the wallets of its users can derive a keyset ID from the keyset of 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 public keys +4 - HASH_SHA256 the concatenated byte array 5 - prefix it with a keyset ID version byte ``` @@ -82,6 +82,25 @@ def derive_keyset_id(keys: Dict[int, PublicKey]) -> str: 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 +3 - HASH_SHA256 the concatenated public keys +4 - take the first 14 characters of the hex-encoded hash +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())) + pubkeys_concat = b"".join([p.serialize() for p in sorted_keys.values()]) + return "00" + hashlib.sha256(pubkeys_concat).hexdigest()[:14] +``` + ## Example: Get mint keysets A wallet can ask the mint for a list of all keysets via the `GET /v1/keysets` endpoint. From d3d01d6c5e9ebd55cb615a3208c25ce5a07beb93 Mon Sep 17 00:00:00 2001 From: David Caseria Date: Fri, 1 Nov 2024 15:33:54 -0400 Subject: [PATCH 3/6] Fixups --- 02.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/02.md b/02.md index a164fe0..af386df 100644 --- a/02.md +++ b/02.md @@ -20,6 +20,8 @@ The keyset `id` is in each `Proof` so it can be used by wallets to identify whic To save space, a `Token`, as definied in [NUT-00][00], **SHOULD** contain an abbreviated version of the keyset `id` in the `Proof`. The length of the abbreviated `id` **MUST** be at least eight bytes (i.e., the version byte and first byte of the hash). However, when the token is serialized, the chosen `id` length **MUST** not be ambiguous within the mint's existing keyset `id`s. A wallet **MUST** resolve the abbreviated keyset `id` to the full `id`. If the abbreviated `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. @@ -143,19 +145,19 @@ Here, `id` is the keyset ID, `unit` is the unit string (e.g. "sat") of the keyse { "id": "01c9c20fb8b348b389e296227c6cc7a63f77354b7388c720dbba6218f720f9b785", "unit": "sat", - "active": True, + "active": true, "input_fee_ppk": 100 }, { "id": "0188432103b12cec6361587d92bdfb798079c58b1c828c561b4daec6f4d465a810", "unit": "sat", - "active": False, + "active": false, "input_fee_ppk": 100 }, { "id": "01d0257bde6ff4cd55e49318a824bbe67e2f9faa248ff108203b5fe46581b14ffc", "unit": "usd", - "active": True, + "active": true, "input_fee_ppk": 100 } ] @@ -164,8 +166,7 @@ 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. +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. ### Example From ae8f6d16078b7e09906738d286feba1cea09403b Mon Sep 17 00:00:00 2001 From: David Caseria Date: Thu, 7 Nov 2024 14:41:13 -0500 Subject: [PATCH 4/6] Fix error and typo --- 02.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/02.md b/02.md index af386df..83002ea 100644 --- a/02.md +++ b/02.md @@ -18,7 +18,7 @@ A keyset `id` is an identifier for a specific keyset. It can be derived by anyon 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 definied in [NUT-00][00], **SHOULD** contain an abbreviated version of the keyset `id` in the `Proof`. The length of the abbreviated `id` **MUST** be at least eight bytes (i.e., the version byte and first byte of the hash). However, when the token is serialized, the chosen `id` length **MUST** not be ambiguous within the mint's existing keyset `id`s. A wallet **MUST** resolve the abbreviated keyset `id` to the full `id`. If the abbreviated `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. +To save space, a `Token`, as defined in [NUT-00][00], **SHOULD** contain an abbreviated version of the keyset `id` in the `Proof`. The length of the abbreviated `id` **MUST** be at least eight bytes (i.e., the version byte and the first seven bytes of the hash). However, while unlikely, when the token is serialized, the chosen `id` length **MUST** not be ambiguous within the mint's existing keyset `id`s. A wallet **MUST** resolve the abbreviated keyset `id` to the full `id`. If the abbreviated `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. From abae74b47507289984305cc9cf90e1ebed03889a Mon Sep 17 00:00:00 2001 From: David Caseria Date: Tue, 19 Nov 2024 14:47:46 -0500 Subject: [PATCH 5/6] Clarify keyset ID usage in NUT-13 --- 13.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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: From 4668545cde4a9945c2d26dc8674dff61037b154c Mon Sep 17 00:00:00 2001 From: David Caseria Date: Mon, 25 Nov 2024 11:24:37 -0500 Subject: [PATCH 6/6] Clarify abbreviated keyset id length --- 02.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/02.md b/02.md index 83002ea..b1efa55 100644 --- a/02.md +++ b/02.md @@ -18,7 +18,7 @@ A keyset `id` is an identifier for a specific keyset. It can be derived by anyon 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 length of the abbreviated `id` **MUST** be at least eight bytes (i.e., the version byte and the first seven bytes of the hash). However, while unlikely, when the token is serialized, the chosen `id` length **MUST** not be ambiguous within the mint's existing keyset `id`s. A wallet **MUST** resolve the abbreviated keyset `id` to the full `id`. If the abbreviated `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. +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.