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
G
elliptic curve generator point
k
private key of mint (one for each amount)K
public key of mintQ
promise (blinded signature)
x
random string (secret message), corresponds to pointY
on curver
private key (blinding factor)T
blinded messageZ
proof (unblinded signature)
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
- 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.
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
.
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.
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).
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.
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]
Wallets serialize tokens in a base64_urlsafe
format (base64 encoding with /
replaced by _
and +
by -
).
cashu[version][base64_token_json]
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.
This token format has the [version]
value A
.
To make Cashu tokens clickable on the web, we use the URI scheme cashu:
. A serialized token with URI tag becomes
cashu:cashuAeyJwcm9vZn...
The deserialized base64_token_json
is
{
"token": [
{
"mint": str,
"proofs": Proofs
},
...
],
"unit": str <optional>,
"memo": str <optional>
}
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 for supported units), and memo
is an optional text memo from the sender.
{
"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