12 KiB
Notation
Sending user: Alice
Receivung user: Carol
Mint: Bob
Bob (mint)
kprivate key of mint (one for each amount)Kpublic key of mintQpromise (blinded signature)
Alice (user)
xrandom string (secret message), corresponds to pointYon curverprivate key (blinding factor)Tblinded messageZproof (unblinded signature)
Blind Diffie-Hellmann key exchange (BDH)
- Mint
BobpublishesK = kG Alicepicks secretxand computesY = hash_to_curve(x)Alicesends toBob:T = Y + rGwithrbeing a random nonceBobsends back toAliceblinded key:Q = kT(these two steps are the DH key exchange)Alicecan calculate the unblinded key asQ - rK = kY + krG - krG = kY = Z- Alice can take the pair
(x, Z)as a token and can send it toCarol. Carolcan send(x, Z)toBobwho then checks thatk*hash_to_curve(x) == Z, and if so treats it as a valid spend of a token, addingxto the list of spent secrets.
Cashu client protocol
1 - Request public keys from mint
Alice receives public keys from mint Bob via GET /keys and stores them in a key-value store like a dictionary. Keys are received as a JSON of the form {<amount_1> : <mint_pubkey_1>, <amount_2> : ...} for each <amount_i> of the amounts the mint Bob supports. [NOTE: mint_pubkey should be consistent with the notation above.]
2 - Mint tokens
Step 1: Alice requests mint
Alicerequests the minting of tokens of valueamount : intviaGET /mint?amount=<amount>Bobresponds with a JSON{"pr": <payment_request>, "hash": <payment_hash>}wherepayment_requestis the bolt11 Lightning invoice thatAliceneeds to pay andpayment_hashis the hash of the invoice necessary for alice to request minting of tokens later.Alicestorespayment_hash. [NOTE: <payment_hash> does not need to be passed by Bob, can be derived from <payment_request>]Alicepays bolt11 invoicepayment_requestusing a Bitcoin Lightning wallet.
Step 2: Request tokens
- To request tokens of value
amount : int,Alicedecomposesamountinto a sum of values of2^n, e.g.13isamounts : List[int] = [1, 4, 8]. This can be easily done by representingamountas binary and using each binary digit that is1as part of the sum, e.g.8would be1101wich is2^0 + 2^2 + 2^3. In this example,Alicewill requestN = len(amounts) = 3tokens. Alicegenerates a random secret stringx_iof128random bits withi \in [0,..,N-1]for each of theNrequested tokens and encodes them inbase64. [TODO: remove index i]Aliceremembersxfor the construction of the proof in Step 5.
Step 3: Generate blinded message
Here we see how Alice generates N blinded messages T_i. The following steps are executed for each of the N tokens that Alice requests. The index i is dropped for simplicity. [TODO: either write everything independent of i or not, don't mix]
Alicegenerates a pointYon the elliptic curve from the secretxusing the deterministic functionY = hash_to_curve(hash(x : string)) : Point.h = hash(x : string) : stringcan be theSHA256hash function.Y = hash_to_curve(h : string) : Pointverifies thatYis an element of the elliptic curve.Alicegenerates a random noncer : intthat is a private key and computes the public key from it usingr*G.Alicegenerates the blinded messageT = Y + r*GAliceremembersrfor the construction of the proof in Step 5.
Step 4: Request tokens
Aliceconstructs JSONMintRequest = {"blinded_messages" : ["amount" : <amount>, "B_" : <blinded_message>] }[NOTE: rename "blinded_messages", rename "B_", rename "MintRequest"]Alicerequests tokens viaPOST /mint?payment_hash=<payment_hash>with bodyMintRequest[NOTE: rename MintRequest]Alicereceives fromBoba list of blinded signaturesList[BlindedSignature], one for each token, e.g.[{"amount" : <amount>, "C_" : <blinded_signature>}, ...][NOTE: rename C_]- If an error occured,
Alicereceives JSON{"error" : <error_reason>}}[TODO: Specify case of error]
Step 5: Construct proofs
Here, Alice construct proofs for each token using the tuple (blinded_signature, r, s). Again, all steps are repeated for each token separately but we show it here for only one token.
Aliceunblindsblinded_signatureby subtractingr*<mint_pubkey>from it. Note that<mint_pubkey>must be according to the<amount>of the token. The result is the proofZ. [Note: in notation, this is Z = Q - r*K]Aliceconstructs spendable token as a tuple(<amount>, Z, s)and stores it in her database.
3 - Send tokens
Here we describe how Alice sends tokens to Carol.
3.1 – Split tokens to desired amount
Alice wants to send tokens of total value <total> to Carol but doesn't necessarily have a set of tokens that sum to <total>. Say Alice has tokens of the amount <alice_balance> which is greater than <total> in here database. Note that <alice_balance> does not need to include all of Alice's tokens but only at least tokens of a total amount of <total>. Therefore, Alice sends tokens of amount <alice_balance> to Bob asks Bob to issue two new sets of tokens of value <total> and <alice_balance>-<total> each.
Aliceperforms a split on the amounts<total>and<alice_balance>-<total>separately as in 2.2 - Request tokens. [TODO: fix reference]Aliceconstructs two new sets of blinded messages like in 2.3 - Generate blind messages [TODO: fix reference], one for each of the two amounts<total>and<alice_balance>-<total>.Aliceconcatenates both sets of blinded messages into the list<blinded_messages>[TODO: list?]Aliceconstructs a JSON out of multiple tokens from her database that sum to<alice_balance>of the form{"amount" : <total>, "proofs" : [{"amount" : <amount>, "secret" : s, "C" : Z}, ...], "outputs" : ["amount" : <amount>, "B_" : <blinded_message>]}. The blinded messages in"outputs"are the list of concatenated blinded message from the previous step. [TODO: refer to this as BlindMessages or something and reuse in Section 4 and 2]
3.2 - Request new tokens for sending
Aliceconstructs a JSON out of multiple tokens of the form[{"amount" : <amount>, "secret" : s, "C" : Z}, ...]and serializes is as a Base64 stringTOKENwhich is then sent toCarolas a payment of valuesum(<amount_i>). [NOTE: rename C, rewrite sum, find consistency in writing labels, values, TOKEN, in code this is calledProof]Alicerequests new tokens viaPOST /mintwith the JSON as the body of the request.Alicereceives a JSON of the form{"fst" : <signatures_to_keep>}, "snd" : <signatures_to_send>with both entries being of the typeList[BlindedSignature].Aliceconstructs proofs<keep_proofs>and<send_proofs>from both of these entries like in Step 2.5 [TODO: fix reference].Alicestores the proofs<keep_proofs>and<send_proofs>in her database and flags<send_proofs>aspending(for example in a separate column).Alicemay also give the set of<send_proofs>a unique IDsend_idso that she can later connect each set of pending tokens with every send attempt.
3.3 - Serialize tokens for sending
Here, Alice serializes the proofs from the set <send_proofs> for sending to Carol.
Aliceconstructs a JSON of the form[{"amount" : <amount>, "secret" : s, "C" : Z}, ...]from<send_proofs>and encodes it as a Base64 string using url-safe Base64 encoder. [NOTE: it probably doesn't need to be url-safe, maybe it shouldn't if this is not widespread or consistent across languages]Alicesends the resultingTOKENas the stringW3siYW1vdW50IjogMiwgInNlY3...toCarol.
4 - Receive new tokens
Here we describe how Carol can redeem new tokens from Bob that she previously received from Alice. Carol receives tokens as a url-safe [NOTE: remove url-safe?] base64-encoded string TOKEN that, when decoded, is a JSON of the form [{"amount" : <amount>, "secret" : s, "C" : Z}, ...]. In the following, we will refer to the tuple (<amount>, Z, s) as a single token. [NOTE: clarify whether a TOKEN is a single token or a list of tokens] To redeem a token, Carol sends it to Bob and receives a one of the same value.
Carol essentially performs the same procedure to receive tokens as Alice did earlier when she prepared her tokens for sending: She sends constructs new blinded messages and sends them together with the tokens she received in order to receive a newly-issued set of tokens which settles the transaction between Alice and Carol.
Note that the following steps can also be performed by Alice herself if she wants to cancel the pending token transfer and claim them for herself.
Carolconstructs a list of<blinded_message>'s each with the same amount as the list list of tokens that she received. This can be done by the same procedure as during the minting of new tokens in Section 2 [TODO: update ref] or during sending in Section 3 [TODO: update ref] since the splitting into amounts is deterministic.Carolperforms the same steps asAlicewhen she split the tokens before sending it to her and calls the endpointPOIT /splitwith the JSONSplitRequestsas the body of the request.
5 - Burn sent tokens
Here we describe how Alice checks with the mint whether the tokens she sent Carol have been redeemed so she can safely delete them from her database. This step is optional but highly recommended so Alice can properly account for the tokens and adjust her balance accordingly.
Aliceloads all<send_proofs>withpending=Truefrom her database and might group them by thesend_id.Aliceconstructs a JSON of the form{"proofs" : [{"amount" : <amount>, "secret" : s, "C" : Z}, ...]}from these (grouped) tokens. [TODO: this object is called CheckRequest]Alicesends them to the mintBobvia the endpointPOST /checkwith the JSON as the body of the request.Alicereceives a JSON of the form{"1" : <spendable : bool>, "2" : ...}where"1"is the index of the proof she sent to the mint before and<spendable>is a boolean that isTrueif the token has not been claimed yet byCarolandFalseif it has already been claimed.- If
<spendable>isFalse,Aliceremoves the proof [NOTE: consistent name?] from her list of spendable proofs.
6 - Pay a Lightning invoice
Here we describe how Alice can request from Bob to make a Lightning payment for her and burn an appropriate amount of tokens in return. Alice wants to pay a bolt11 invoice with the amount <invoice_amount>. She has to add a fee to the request to account for the possible Lightning fees which results in a request with tokens with the total amount of <total>.
Alicewants to pay the bolt11 invoice<invoice>.AliceasksBobfor the Lightning fee viaGET /checkfeewith the bodyCheckFeeRequestbeing the json{pr : <invoice>}Alicereceives theCheckFeeResponsein the form of the json{"fee" : <fee>}resulting in<total> = <invoice_amount> + <fee>.Alicenow performs the same set of instructions as in Step 3.1 and 3.2 and splits her spendable tokens into a set<keep_proofs>that she keeps and and a set<send_proofs>with a sum of at least<total>that she can send for making the Lightning payment.Aliceconstructs the JSONMeltRequestof the form{"proofs" : <List[Proof]>, "invoice" : <invoice>}[NOTE: Maybe use notation List[Proof] everywhere. Used MeltRequest here, maybe define each payload at the beginning of each section.]Alicerequests a payment fromBobvia the endpointPOST /meltwith the JSON as the body of the request.Alicereceives a JSON of the form{"paid" : <status:bool>}with<status>beingTrueif the payment was successful andFalseotherwise.- If
<status> == True,Aliceremoves<send_proofs>from her database of spendable tokens [NOTE: called it tokens again]
Todo:
- Call subsections 1. and 1.2 etc so they can be referenced
- Define objets like
MintRequestandSplitRequestsonce when they appear and reuse them. - Clarify whether a
TOKENis a single Proof or a list of Proofs