NUT-00: Notation, Utilization, and Terminology¶
mandatory
This document details the notation and models used throughout the specification and lays the groundwork for understanding the basic cryptography used in the Cashu protocol.
- Sending user:
Alice
- Receiving user:
Carol
- Mint:
Bob
Blind Diffie-Hellmann key exchange (BDHKE)¶
Variables¶
G
elliptic curve generator point
Bob (mint)¶
k
private key of mint (one for each amount)K
public key of mintQ
promise (blinded signature)
Alice (user)¶
x
random string (secret message), corresponds to pointY
on curver
private key (blinding factor)T
blinded messageZ
proof (unblinded signature)
hash_to_curve(x: bytes) -> curve point Y
¶
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 keyDOMAIN_SEPARATOR
constant byte stringb"Secp256k1_HashToCurve_Cashu_"
x
message to hashcounter
uint32 counter(byte order little endian) incremented from 0 until a point is found that lies on the curve
Protocol¶
- Mint
Bob
publishes public keyK = kG
Alice
picks secretx
and computesY = hash_to_curve(x)
Alice
sends toBob
:B_ = Y + rG
withr
being a random blinding factor (blinding)Bob
sends back toAlice
blinded key:C_ = kB_
(these two steps are the DH key exchange) (signing)Alice
can calculate the unblinded key asC_ - rK = kY + krG - krG = kY = C
(unblinding)- Alice can take the pair
(x, C)
as a token and can send it toCarol
. Carol
can send(x, C)
toBob
who then checks thatk*hash_to_curve(x) == C
(verification), and if so treats it as a valid spend of a token, addingx
to the list of spent secrets.
0.1 - Models¶
BlindedMessage
¶
An encrypted ("blinded") secret and an amount is sent from Alice
to Bob
for minting tokens or for swapping tokens. A BlindedMessage
is also called an output
.
{
"amount": int,
"id": hex_str,
"B_": hex_str
}
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
¶
A BlindSignature
is sent from Bob
to Alice
after minting tokens or after swapping tokens. A BlindSignature
is also called a promise
.
{
"amount": int,
"id": hex_str,
"C_": hex_str
}
amount
is the value of the blinded token, id
is the keyset id of the mint keys that signed the token, and C_
is the blinded signature on the secret message B_
sent in the previous step.
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. Serialized Proofs
can also be sent from Alice
to Carol
. Upon receiving the token, Carol
deserializes it and requests a swap from Bob
to receive new Proofs
.
{
"amount": int,
"id": hex_str,
"secret": 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 of the mint public keys that signed the token (hex string).
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:
{
"detail": "oops",
"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¶
Serialization of tokens¶
Tokens can be serialized to send them between users Alice
and Carol
. Serialized tokens have a Cashu token prefix, a versioning flag, and the token. Optionally, a URI prefix for making tokens clickable on the web.
We use the following format for token serialization:
cashu[version][token]
cashu
is the Cashu token prefix. [version]
is a single base64_urlsafe
character to denote the token format version.
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
cashu:cashuAeyJwcm9vZn...
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
.
Format¶
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 -
). base64_urlsafe
strings may have padding characters (usually =
) at the end which can be omitted. Clients need to be able to decode both cases.
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
{
"token": [
{
"mint": str,
"proofs": Proofs
},
...
],
"unit": str <optional>,
"memo": str <optional>
}
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 for supported units), and memo
is an optional text memo from the sender.
Example¶
Below is a TokenV3 JSON before base64_urlsafe
serialization.
{
"token": [
{
"mint": "https://8333.space:3338",
"proofs": [
{
"amount": 2,
"id": "009a1f293253e41e",
"secret": "407915bc212be61a77e3e6d2aeb4c727980bda51cd06a6afc29e2861768a7837",
"C": "02bc9097997d81afb2cc7346b5e4345a9346bd2a506eb7958598a72f0cf85163ea"
},
{
"amount": 8,
"id": "009a1f293253e41e",
"secret": "fe15109314e61d7756b0f8ee0f23a624acaa3f4e042f61433c728c7057b931be",
"C": "029e8e5050b890a7d6c0968db16bc1d5d5fa040ea1de284f6ec69d61299f671059"
}
]
}
],
"unit": "sat",
"memo": "Thank you."
}
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 -
). base64_urlsafe
strings may have padding characters (usually =
) at the end which can be omitted. Clients need to be able to decode both cases.
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.
{
"m": str, // mint URL
"u": str, // unit
"d": str <optional>, // memo
"t": [
{
"i": bytes, // keyset ID
"p": [ // proofs with this keyset ID
{
"a": int, // amount
"s": str, // secret
"c": bytes, // signature
"d": { <optional> // DLEQ proof
"e": bytes,
"s": bytes,
"r": bytes
},
"w": str <optional> // witness
},
...
]
},
...
],
}
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 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.
Example¶
Below is a TokenV4 JSON before CBOR and base64_urlsafe
serialization.
{
"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. 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