Sending user: Alice
Receivung user: Carol
Mint: Bob
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)
- Mint
Bob
publishesK = kG
Alice
picks secretx
and computesY = hash_to_point(x)
Alice
sends toBob
:T = Y + rG
withr
being a random nonceBob
sends back toAlice
blinded key:Q = kT
(these two steps are the DH key exchange)Alice
can 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
. Carol
can send(x, Z)
toBob
who then checks thatk*hash_to_point(x) == Z
, and if so treats it as a valid spend of a token, addingx
to the list of spent secrets.
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.]
Alice
requests the minting of tokens of valueamount : int
viaGET /mint?amount=<amount>
Bob
responds with a JSON{"pr": <payment_request>, "hash": <payment_hash>}
wherepayment_request
is the bolt11 Lightning invoice thatAlice
needs to pay andpayment_hash
is the hash of the invoice necessary for alice to request minting of tokens later.Alice
storespayment_hash
. [NOTE: <payment_hash> does not need to be passed by Bob, can be derived from <payment_request>]Alice
pays bolt11 invoicepayment_request
using a Bitcoin Lightning wallet.
- To request tokens of value
amount : int
,Alice
decomposesamount
into a sum of values of2^n
, e.g.13
isamounts : List[int] = [1, 4, 8]
. This can be easily done by representingamount
as binary and using each binary digit that is1
as part of the sum, e.g.8
would be1101
wich is2^0 + 2^2 + 2^3
. In this example,Alice
will requestN = len(amounts) = 3
tokens. Alice
generates a random secret stringx_i
of128
random bits withi \in [0,..,N-1]
for each of theN
requested tokens and encodes them inbase64
. [TODO: remove index i]Alice
remembersx
for the construction of the proof in Step 5.
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]
Alice
generates a pointY
on the elliptic curve from the secretx
using the deterministic functionY = hash_to_curve(hash(x : string)) : Point
.h = hash(x : string) : string
can be theSHA256
hash function.Y = hash_to_curve(h : string) : Point
verifies thatY
is an element of the elliptic curve.Alice
generates a random noncer : int
that is a private key and computes the public key from it usingr*G
.Alice
generates the blinded messageT = Y + r*G
Alice
remembersr
for the construction of the proof in Step 5.
Alice
constructs JSONMintPayloads = {"blinded_messages" : ["amount" : <amount>, "B_" : <blinded_message>] }
[NOTE: rename "blinded_messages", rename "B_", rename "MintPayloads"]Alice
requests tokens viaPOST /mint?payment_hash=<payment_hash>
with bodyMintPayloads
[NOTE: rename MintPayloads]Alice
receives fromBob
a list of blinded signaturesList[BlindedSignature]
, one for each token, e.g.[{"amount" : <amount>, "C_" : <blinded_signature>}, ...]
[NOTE: rename C_]- If an error occured,
Alice
receives JSON{"error" : <error_reason>}}
[TODO: Specify case of error]
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.
Alice
unblindsblinded_signature
by 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]Alice
constructs spendable token as a tuple(<amount>, Z, s)
and stores it in her database.
Here we describe how Alice
sends tokens to Carol
.
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.
Alice
performs a split on the amounts<total>
and<alice_balance>-<total>
separately as in 2.2 - Request tokens. [TODO: fix reference]Alice
constructs 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>
.Alice
concatenates both sets of blinded messages into the list<blinded_messages>
[TODO: list?]Alice
constructs 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}, ...], "output_data" : ["amount" : <amount>, "B_" : <blinded_message>]}
. The blinded messages in"output_data"
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]
Alice
constructs a JSON out of multiple tokens of the form[{"amount" : <amount>, "secret" : s, "C" : Z}, ...]
and serializes is as a Base64 stringTOKEN
which is then sent toCarol
as a payment of valuesum(<amount_i>)
. [NOTE: rename C, rewrite sum, find consistency in writing labels, values, TOKEN, in code this is calledProof
]Alice
requests new tokens viaPOST /mint
with the JSON as the body of the request.Alice
receives a JSON of the form{"fst" : <signatures_to_keep>}, "snd" : <signatures_to_send>
with both entries being of the typeList[BlindedSignature]
.Alice
constructs proofs<keep_proofs>
and<send_proofs>
from both of these entries like in Step 2.5 [TODO: fix reference].Alice
stores the proofs<keep_proofs>
and<send_proofs>
in her database and flags<send_proofs>
aspending
(for example in a separate column).Alice
may also give the set of<send_proofs>
a unique IDsend_id
so that she can later connect each set of pending tokens with every send attempt.
Here, Alice
serializes the proofs from the set <send_proofs>
for sending to Carol
.
Alice
constructs 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]Alice
sends the resultingTOKEN
as the stringW3siYW1vdW50IjogMiwgInNlY3...
toCarol
.
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.
Carol
constructs 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.Carol
performs the same steps asAlice
when she split the tokens before sending it to her and calls the endpointPOIT /split
with the JSONSplitPayloads
as the body of the request [TODO: rename SplitPayloads?]
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.
Alice
loads all<send_proofs>
withpending=True
from her database and might group them by thesend_id
.Alice
constructs a JSON of the form{"proofs" : [{"amount" : <amount>, "secret" : s, "C" : Z}, ...]}
from these (grouped) tokens. [TODO: this object is called CheckPayload]Alice
sends them to the mintBob
via the endpointPOST /check
with the JSON as the body of the request.Alice
receives 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 isTrue
if the token has not been claimed yet byCarol
andFalse
if it has already been claimed.- If
<spendable>
isFalse
,Alice
removes the proof [NOTE: consistent name?] from her list of spendable proofs.
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 predefined fee to the request to account for the possible Lightning fees which results in a request with tokens with the total amount of <total>
. [NOTE: there is no way to do this dynamically as for now. We simply include a amount-dependent fee with the request and the mint essentially keeps the difference if it can find a cheaper-than-expected route. The mint refuses to pay the invoice if the fees included are not high-enough.]
Alice
wants to pay the bolt11 invoice<invoice>
.Alice
calculates the fees for the Lightning payments upfront with the functionmax(<MIN_FEE>, <invoice_amount> * <PROPORTIONAL_FEE>*)
with<MIN_FEE>
currently being4
Satoshis and<PROPORTIONAL_FEE>
being0.01
(or 1% of<invoice_amount>
).Alice
then adds this fee to<invoice_amount>
and rounds it up to the next higher integer which results in<amount>
.Alice
now 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>
that she can send for making the Lightning payment.Alice
constructs the JSONMeltPayload
of the form{"proofs" : <List[Proof]>, "amount" : <total>, "invoice" : <invoice>}
[NOTE: Maybe use notation List[Proof] everywhere. Used MeltPayload here, maybe define each payload at the beginning of each section.]Alice
requests a payment fromBob
via the endpointPOST /melt
with the JSON as the body of the request.Alice
receives a JSON of the form{"paid" : <status:bool>}
with<status>
beingTrue
if the payment was successful andFalse
otherwise.- If
<status> == True
,Alice
removes<send_proofs>
from her database of spendable tokens [NOTE: called it tokens again]
- Call subsections 1. and 1.2 etc so they can be referenced
- Define objets like
MintPayloads
andSplitPayloads
once when they appear and reuse them. - Clarify whether a
TOKEN
is a single Proof or a list of Proofs