diff --git a/00.md b/00.md index b09067f..93074a0 100644 --- a/00.md +++ b/00.md @@ -122,27 +122,33 @@ We use the following format for token serialization: cashu[version][token] ``` -#### V3 tokens +`cashu` is the Cashu token prefix. `[version]` is a single `base64_urlsafe` character to denote the token format version. -Wallets serialize tokens in a `base64_urlsafe` format (base64 encoding with `/` replaced by `_` and `+` by `-`). +##### URI tags + +To make Cashu tokens clickable on the web, we use the URI scheme `cashu:`. An example of a serialized token with URI tag is ```sh -cashu[version][base64_token_json] +cashu:cashuAeyJwcm9vZn... ``` -`cashu` is the Cashu token prefix. `[version]` is a single `base64_urlsafe` character to denote the token format version (starting with `A` for the present token format). `[base64_token_json]` is the token JSON serialized in `base64_urlsafe`. A `[base64_token_json]` should be cleared of any whitespace before serializing. +### V3 tokens + +> *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`. -##### URI tags +##### Format -To make Cashu tokens clickable on the web, we use the URI scheme `cashu:`. A serialized token with URI tag becomes +V3 tokens are base64-encoded JSON objects. The token format supports tokens from multiple mints. The JSON is serialized with a `base64_urlsafe` (base64 encoding with `/` replaced by `_` and `+` by `-`). ```sh -cashu:cashuAeyJwcm9vZn... +cashuA[base64_token_json] ``` +`[base64_token_json]` is the token JSON serialized in `base64_urlsafe`. `[base64_token_json]` should be cleared of any whitespace before serializing. + ##### Token format The deserialized `base64_token_json` is @@ -163,7 +169,9 @@ The deserialized `base64_token_json` is `mint` is the mint URL, `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 JSON +##### Example + +Below is a TokenV3 JSON before `base64_urlsafe` serialization. ```json { @@ -196,6 +204,101 @@ 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. + +##### Version +This token format has the `[version]` value `B`. + +##### Format + +Wallets serialize tokens in a `base64_urlsafe` format (base64 encoding with `/` replaced by `_` and `+` by `-`). + +```sh +cashuB[base64_token_cbor] +``` + +##### Token format + +The deserialized `base64_token_cbor` is a JSON of the same form as a TokenV4 but with shorter keys and data represented as binary data (`bytes`) instead of hex strings (`hex_str`). Note that we have expanded what is called `Proofs` in TokenV3 (called `p` here with TokenV4) showing that its values are also different from the TokenV3 serialization. + +```json +{ + "m": str, // mint URL + "u": str, // unit + "d": str , // memo + "t": [ + { + "i": bytes, // keyset ID + "p": [ // proofs with this keyset ID + { + "a": int, // amount + "s": str, // secret + "c": bytes, // signature + "d": { // DLEQ proof + "e": bytes, + "s": bytes, + "r": bytes + }, + "w": str // witness + }, + ... + ] + }, + ... + ], +} +``` +Note that all fields of the `bytes` type encode hex strings in the original representation of `Proof`'s. We extracted the keyset ID `i` from each proof and grouped all proofs by their keyset ID `i` one level above (in `p`). + +##### Example + +Below is a TokenV4 JSON before CBOR and `base64_urlsafe` serialization. + +```json +{ + "t": [ + { + "i": h'00ffd48b8f5ecf80', + "p": [ + { + "a": 1, + "s": "acc12435e7b8484c3cf1850149218af90f716a52bf4a5ed347e48ecc13f77388", + "c": h'0244538319de485d55bed3b29a642bee5879375ab9e7a620e11e48ba482421f3cf', + }, + ], + }, + { + "i": h'00ad268c4d1f5826', + "p": [ + { + "a": 2, + "s": "1323d3d4707a58ad2e23ada4e9f1f49f5a5b4ac7b708eb0d61f738f48307e8ee", + "c": h'023456aa110d84b4ac747aebd82c3b005aca50bf457ebd5737a4414fac3ae7d94d', + }, + { + "a": 1, + "s": "56bcbcbb7cc6406b3fa5d57d2174f4eff8b4402b176926d3a57d3c3dcbb59d57", + "c": h'0273129c5719e599379a974a626363c333c56cafc0e6d01abe46d5808280789c63', + }, + ], + }, + ], + "m": "http://localhost:3338", + "u": "sat", +} +``` + +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: + +``` +cashuBo2F0gqJhaUgA_9SLj17PgGFwgaNhYQFhc3hAYWNjMTI0MzVlN2I4NDg0YzNjZjE4NTAxNDkyMThhZjkwZjcxNmE1MmJmNGE1ZWQzNDdlNDhlY2MxM2Y3NzM4OGFjWCECRFODGd5IXVW-07KaZCvuWHk3WrnnpiDhHki6SCQh88-iYWlIAK0mjE0fWCZhcIKjYWECYXN4QDEzMjNkM2Q0NzA3YTU4YWQyZTIzYWRhNGU5ZjFmNDlmNWE1YjRhYzdiNzA4ZWIwZDYxZjczOGY0ODMwN2U4ZWVhY1ghAjRWqhENhLSsdHrr2Cw7AFrKUL9Ffr1XN6RBT6w659lNo2FhAWFzeEA1NmJjYmNiYjdjYzY0MDZiM2ZhNWQ1N2QyMTc0ZjRlZmY4YjQ0MDJiMTc2OTI2ZDNhNTdkM2MzZGNiYjU5ZDU3YWNYIQJzEpxXGeWZN5qXSmJjY8MzxWyvwObQGr5G1YCCgHicY2FtdWh0dHA6Ly9sb2NhbGhvc3Q6MzMzOGF1Y3NhdA== +``` + + [00]: 00.md [01]: 01.md [02]: 02.md diff --git a/tests/00-tests.md b/tests/00-tests.md index 43a3128..496bd2b 100644 --- a/tests/00-tests.md +++ b/tests/00-tests.md @@ -16,7 +16,7 @@ Point: 022e7158e11c9506f1aa4248bf531298daa7febd6194f003edcd9b93ade6253acf # Note that this message will take a few iterations of the loop before finding a valid point Message: 0000000000000000000000000000000000000000000000000000000000000002 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`. @@ -35,6 +35,7 @@ B_: 029bdf2d716ee366eddf599ba252786c1033f47e230248a4612a5670ab931f1763 # hex enc ### Blinded signatures These are test vectors for the blinded key `C_` given the mint's private key `k` and Alice's blinded message containing `B_`. + ```shell # Test 1 mint private key: 0000000000000000000000000000000000000000000000000000000000000001 @@ -45,12 +46,11 @@ C_: 02a9acc1e48c25eeeb9289b5031cc57da9fe72f3fe2861d264bdc074209b107ba2 mint private key: 7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f B_: 02a9acc1e48c25eeeb9289b5031cc57da9fe72f3fe2861d264bdc074209b107ba2 C_: 0398bc70ce8184d27ba89834d19f5199c84443c31131e48d3c1214db24247d005d -```` +``` ## Serialization of TokenV3 The following are JSON-formatted v3 tokens and their serialized counterparts. -```shell -# "Pretty print" JSON format +```json { "token": [ { @@ -74,8 +74,10 @@ The following are JSON-formatted v3 tokens and their serialized counterparts. "unit": "sat", "memo": "Thank you." } +``` -# Serialized token +Serialized: +``` cashuAeyJ0b2tlbiI6W3sibWludCI6Imh0dHBzOi8vODMzMy5zcGFjZTozMzM4IiwicHJvb2ZzIjpbeyJhbW91bnQiOjIsImlkIjoiMDA5YTFmMjkzMjUzZTQxZSIsInNlY3JldCI6IjQwNzkxNWJjMjEyYmU2MWE3N2UzZTZkMmFlYjRjNzI3OTgwYmRhNTFjZDA2YTZhZmMyOWUyODYxNzY4YTc4MzciLCJDIjoiMDJiYzkwOTc5OTdkODFhZmIyY2M3MzQ2YjVlNDM0NWE5MzQ2YmQyYTUwNmViNzk1ODU5OGE3MmYwY2Y4NTE2M2VhIn0seyJhbW91bnQiOjgsImlkIjoiMDA5YTFmMjkzMjUzZTQxZSIsInNlY3JldCI6ImZlMTUxMDkzMTRlNjFkNzc1NmIwZjhlZTBmMjNhNjI0YWNhYTNmNGUwNDJmNjE0MzNjNzI4YzcwNTdiOTMxYmUiLCJDIjoiMDI5ZThlNTA1MGI4OTBhN2Q2YzA5NjhkYjE2YmMxZDVkNWZhMDQwZWExZGUyODRmNmVjNjlkNjEyOTlmNjcxMDU5In1dfV0sInVuaXQiOiJzYXQiLCJtZW1vIjoiVGhhbmsgeW91LiJ9 ``` @@ -95,4 +97,78 @@ The following is a correctly serialized v3 token. cashuAeyJ0b2tlbiI6W3sibWludCI6Imh0dHBzOi8vODMzMy5zcGFjZTozMzM4IiwicHJvb2ZzIjpbeyJhbW91bnQiOjIsImlkIjoiMDA5YTFmMjkzMjUzZTQxZSIsInNlY3JldCI6IjQwNzkxNWJjMjEyYmU2MWE3N2UzZTZkMmFlYjRjNzI3OTgwYmRhNTFjZDA2YTZhZmMyOWUyODYxNzY4YTc4MzciLCJDIjoiMDJiYzkwOTc5OTdkODFhZmIyY2M3MzQ2YjVlNDM0NWE5MzQ2YmQyYTUwNmViNzk1ODU5OGE3MmYwY2Y4NTE2M2VhIn0seyJhbW91bnQiOjgsImlkIjoiMDA5YTFmMjkzMjUzZTQxZSIsInNlY3JldCI6ImZlMTUxMDkzMTRlNjFkNzc1NmIwZjhlZTBmMjNhNjI0YWNhYTNmNGUwNDJmNjE0MzNjNzI4YzcwNTdiOTMxYmUiLCJDIjoiMDI5ZThlNTA1MGI4OTBhN2Q2YzA5NjhkYjE2YmMxZDVkNWZhMDQwZWExZGUyODRmNmVjNjlkNjEyOTlmNjcxMDU5In1dfV0sInVuaXQiOiJzYXQiLCJtZW1vIjoiVGhhbmsgeW91LiJ9 ``` + +## 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": [ + { + "i": h'00ad268c4d1f5826', + "p": [ + { + "a": 1, + "s": "9a6dbb847bd232ba76db0df197216b29d3b8cc14553cd27827fc1cc942fedb4e", + "c": h'038618543ffb6b8695df4ad4babcde92a34a96bdcd97dcee0d7ccf98d472126792', + }, + ], + }, + ], + "d": "Thank you", + "m": "http://localhost:3338", + "u": "sat", +} +``` + +Encoded: + +``` +cashuBpGF0gaJhaUgArSaMTR9YJmFwgaNhYQFhc3hAOWE2ZGJiODQ3YmQyMzJiYTc2ZGIwZGYxOTcyMTZiMjlkM2I4Y2MxNDU1M2NkMjc4MjdmYzFjYzk0MmZlZGI0ZWFjWCEDhhhUP_trhpXfStS6vN6So0qWvc2X3O4NfM-Y1HISZ5JhZGlUaGFuayB5b3VhbXVodHRwOi8vbG9jYWxob3N0OjMzMzhhdWNzYXQ= +``` + +### Multiple keysets +The token below includes proofs from two different keysets. +```json +{ + "t": [ + { + "i": h'00ffd48b8f5ecf80', + "p": [ + { + "a": 1, + "s": "acc12435e7b8484c3cf1850149218af90f716a52bf4a5ed347e48ecc13f77388", + "c": h'0244538319de485d55bed3b29a642bee5879375ab9e7a620e11e48ba482421f3cf', + }, + ], + }, + { + "i": h'00ad268c4d1f5826', + "p": [ + { + "a": 2, + "s": "1323d3d4707a58ad2e23ada4e9f1f49f5a5b4ac7b708eb0d61f738f48307e8ee", + "c": h'023456aa110d84b4ac747aebd82c3b005aca50bf457ebd5737a4414fac3ae7d94d', + }, + { + "a": 1, + "s": "56bcbcbb7cc6406b3fa5d57d2174f4eff8b4402b176926d3a57d3c3dcbb59d57", + "c": h'0273129c5719e599379a974a626363c333c56cafc0e6d01abe46d5808280789c63', + }, + ], + }, + ], + "m": "http://localhost:3338", + "u": "sat", +} +``` + +Serialized: + +``` +cashuBo2F0gqJhaUgA_9SLj17PgGFwgaNhYQFhc3hAYWNjMTI0MzVlN2I4NDg0YzNjZjE4NTAxNDkyMThhZjkwZjcxNmE1MmJmNGE1ZWQzNDdlNDhlY2MxM2Y3NzM4OGFjWCECRFODGd5IXVW-07KaZCvuWHk3WrnnpiDhHki6SCQh88-iYWlIAK0mjE0fWCZhcIKjYWECYXN4QDEzMjNkM2Q0NzA3YTU4YWQyZTIzYWRhNGU5ZjFmNDlmNWE1YjRhYzdiNzA4ZWIwZDYxZjczOGY0ODMwN2U4ZWVhY1ghAjRWqhENhLSsdHrr2Cw7AFrKUL9Ffr1XN6RBT6w659lNo2FhAWFzeEA1NmJjYmNiYjdjYzY0MDZiM2ZhNWQ1N2QyMTc0ZjRlZmY4YjQ0MDJiMTc2OTI2ZDNhNTdkM2MzZGNiYjU5ZDU3YWNYIQJzEpxXGeWZN5qXSmJjY8MzxWyvwObQGr5G1YCCgHicY2FtdWh0dHA6Ly9sb2NhbGhvc3Q6MzMzOGF1Y3NhdA== +``` + [NUT-10]: ../10.md