A Scalar
is an element of group Scalar
is also commonly referred to as PrivateKey
and in cashu-kvac Scalar
is a wrap-around secp256k1-py's PrivateKey
with some added functionality.
A GroupElement
is a point on the secp256k1 curve (PublicKey
, in cashu-kvac it is indeed a wrap-around secp256k1-py's PublicKey
similarly to Scalar
.
Generators are points on the secp256k1
curve can be used as a basis from which it's possible to compute any other point on the curve through repetitive adding.
The term "generator" here is used loosely. While NUMS points might not necessarily generate the entire group of points on the curve, they can still be considered generators in the sense that they can be used to derive a large number of other points through repeated point addition.
In KVAC, different generators are used for specific purposes. Each generator is derived using NUMS (HashToCurve
) to ensure the discrete logarithm relationship between any pair of them remains unknown:
-
$G_w, G_{w'}, G_{x_0}, G_{x_1}$ : Used for computing the algebraicMAC
(on the mint's side) and later for presenting credentials (on the client's side). -
$G_\text{zmac}, G_\text{zamount}, G_\text{zscript}$ : Used for randomizing theMAC
alongsideAmountAttribute
andScriptAttribute
. -
$G_\text{amount}, G_\text{script}$ : Encode amounts into anAmountAttribute
and scripts into aScriptAttribute
. -
$G_\text{blind}$ : Utilized for blinding terms inAmountAttribute
andScriptAttribute
.
In KVAC, a keyset is a single tuple of six secret values (for all amounts):
-
$y_a$ : Private key for signingAmountAttributes
. -
$y_s$ : Private key for signingScriptAttributes
. -
$w, w', x0, x1$ : additional secret values needed for security hardening of the scheme.
The Mint's "public key" is a tuple
$I = G_\text{zmac} - (x_0G_{x_0} + x_1G_{x_1} + y_aG_\text{zamount} + y_sG_\text{zscript})$ $C_w = wG_w + w'G_{w'}$
A point encoding an amount a
with blindness r_a
.
Composition:
$r_a \leftarrow \text{BIP39}(\text{seed}, \text{"r-amount"}, \text{derivation})$ - secret:
$(a, r_a)$ - public:
$M_a = r_a\cdot G_\text{blind} + a\cdot G_\text{amount}$
Simply a AmountAttribute
encoding
$r_a \leftarrow \text{BIP39}(\text{seed}, \text{"r-amount"}, \text{derivation})$ - secret:
$(r_a)$ - public:
$M_a = r_a\cdot G_\text{blind}$
A point encoding a script hash s
with blindness r_s
.
Composition:
$r_s \leftarrow \text{BIP39}(\text{seed}, \text{"r-script"}, \text{derivation})$ - secret:
$(s, r_s)$ - public:
$M_s = r_s\cdot G_\text{blind} + s\cdot G_\text{script}$
Equivalent to Cashu's BlindedSignature
.
The Mint generates this algebraic MAC using its secret parameters (sk
) after verifying RandomizedCoins
(see section Protocol). This MAC binds the AmountAttribute
and ScriptAttribute
together, ensuring neither can be presented alone.
Here AmountAttribute
(and possibly ScriptAttribute
).
The main advantage of letting the wallet derive
Composition:
-
$t \overset{\$}\leftarrow Z_q$ (Mint) or$t \leftarrow \text{BIP39}(\text{seed}, \text{"t"}, \text{derivation})$ (wallet) -
$M_a$ fromAmountAttribute
-
$M_s$ fromScriptAttribute
or point at infinity if no script. $U = \text{HashToCurve}(t)$ $V = w\cdot G_w + x_0\cdot U + x_1tU + y_a\cdot M_a + y_s\cdot M_s$ - MAC:
$(t, V)$
We consider the MAC
together with AmountAttribute
and ScriptAttribute
to be a Coin
.
Before being sent to the Mint, the coin is "randomized" to break the link to the issuance. In other words, they are blinded a second time with a different generator.
We use the blinding term AmountAttribute
and compute:
-
$U = \text{HashToCurve}(t)$ , where$t$ is theMAC
scalar value $C_a = r_a\cdot G_\text{zamount} + M_a$ $C_s = r_a\cdot G_\text{zscript} + M_s$ $C_{x_0} = r_a\cdot G_{x_0} + U$ $C_{x_1} = r_a\cdot G_{x_1} + t\cdot U$ -
$C_v = r_a\cdot G_\text{zmac} + V$ , where$V$ is theMAC
public point value - RandomizedCoin:
$(C_a, C_s, C_{x_0}, C_{x_1}, C_v)$
Note
Amounts can be tweaked by both the Mint and client to produce new attributes that encode
$M_a' = M_a + \delta_aG_\text{amount}$
Nullifiers are values (or a single value) used to mark credentials as spent, ensuring they cannot be reused. In the Cashu protocol, the nullifier for a coin is typically the Proof
object.
Here, we decide to use RandomizedCoin
as the nullifier. The rationale is rooted in the design of
Proof
-
$w, w'$ were used to construct$C_w$ :
-
$x0, x1, y_a, y_s$ were used to construct$I$ :
- the same secret values were used to construct
$V$ :
This is the equivalent of Cashu's current DLEQ proofs, where the Mint proves to the client they are signing with the same keys as for everybody else (no tagging).
This is to prove that the amount encoded into
The attribute's amount is decomposed into a bit-vector
- The bit decomposition sums up to
a
:
- Knowledge of the discrete logs behind the bit-commitments vector
$B$ :
- discrete logs behind the decomposition are either
$0$ or$1$ in value:
Statement 3 leverages the fact that:
This proof shows that RandomizedCoin
was computed from a Coin
for which a valid MAC
was issued.
The public inputs to this proof are the RandomizedCoin
s
-
$Z = r_a\cdot I$ where$r_a$ is the blinding factor fromAmountAttribute
. -
$C_a = r_a\cdot G_\text{zamount} + r_a\cdot G_\text{blind} + a\cdot G_\text{amount}$ to prove$r_a$ is indeed the same as inAmountAttribute
. -
$C_{x_1} = t\cdot C_{x_0} - t\cdot r_a\cdot G_{x_0} + r_a\cdot G_{x_1}$ , where$t$ is the scalar value in theMAC
.
Statement 1 works because the Mint uses private keys RandomizedCoin
's commitments to re-calculate
This proof shows that the difference in encoded amount between the sum of many RandomizedCoin
s AmountAttribute
s
Where
This statement works because the Mint uses
This section explains how a client/wallet (used interchangeably) interacts with a Mint (capital 'M' to distinguish it from the verb "minting").
To perform any interaction (e.g., mint, swap, or melt) with the Mint, a client needs Coin
s worth RandomizedCoin
s for any other operation (mint/melt/swap).
To handle this, the client makes a special BootstrapRequest
:
- The client requests a
MAC
for anAmountAttribute
$M_a$ that encodes$0$ , optionally including aScriptAttribute
$M_s$ encoding a script hash$s$ . This would be used for things like spending conditions. - The client generates a proof,
$\pi_\text{bootstrap}$ , to show that$M_a$ encodes$0$ (48). - The client sends
$(M_a, M_s, \pi_\text{bootstrap})$ to the Mint.
The Mint processes the BootstrapRequest
as follows:
- It verifies the proof
$\pi_\text{bootstrap}$ (53) - If the proof is valid, it issues a
MAC
$(t, V)$ for$M_a$ (and$M_s$ if provided) (56). - It creates and returns
$\pi_\text{iparams}$ to prove that the private keys it used are not linked to individual users (57).
From the wallet's perspective, this bootstrapping process is only needed once per Mint.
When a client wants to swap Coin
s, they:
- Create request outputs: new
AmountAttribute
andScriptAttribute
pairs that encode respectively the final wallet balance (minus any fees) and, optionally, the hash of the script describing the spending conditions (65-66). - Create request inputs: generate
RandomizedCoin
from the oldCoin
which contains the attribute encoding the current balance and the MAC (the Mint's "attestation"/"signature"). If the client doesn't have any suchCoin
, they have toBootstrap
first. (72).
The client also generates the following ZK-proofs:
-
$\pi_\text{balance}$ : Proves that the balance difference$\Delta_a$ (should equal$0$ or the fees) between old and new wallet balances is valid. Inputs: old and newAmountAttribute
s (78). -
$\pi_\text{range}$ : For each newAmountAttribute
, proves the value is within the range$[0, L-1]$ . (69). -
$\pi_\text{MAC}$ : Proves that the providedRandomizedCoin
s are valid and unspent. (75) -
$\pi_\text{script}$ : Ensures all newScriptAttribute
s encode the same script hash$s$ as the oldRandomizedCoin
s. (81).
Then the client sends:
- old
RandomizedCoin
s defined in (2) as inputs - new (
AmountAttribute
/ScriptAttribute
) pairs' COMMITMENTS (not the secret values) as described in (1) - The script the coins were locked to, if any, together with its script-signature
- All proofs as described above.
The Mint then (89-105):
- Acknowledges the balance difference
$\Delta_a$ between inputs and outputs. - Verifies that it hasn’t seen the
RandomizedCoin
$C_a$ before. - Verifies all proofs.
- If all verifications are successful, the Mint issues new
MAC
s for the outputs commitment pairs (108). As with theBootstrapRequest
, the Mint also produces$\pi_\text{iparams}$ to prove to the wallet its private key usage (109).
Sending coins to another wallet is simpler, as sending wallet only needs to communicate the
Coin
of intended value to the receiving wallet, and the receiving wallet will immediately swap it for a new one encoding the same value.
No extra information is needed, as all proofs and randomization can be computed directly by the receiving wallet.
In Cashu, wallets often overpay during melt operations to ensure successful transactions, accounting for the unpredictability of lightning fees.
To allow the Mint to return the excess coins to the client, the client provides "blank" BlindedMessage
s with no predefined amount. The Mint then assigns a value to these outputs and signs them with its keys.
With KVAC, this process is simplified:
- During a melt operation, the client declares a
$\Delta_a$ between the inputs and outputs that exceeds the peg-out amount (amount in the melt quote). This claim is substantiated by$\pi_\text{balance}$ . - The Mint returns the overpaid amount
$o$ by adjusting the commitment$M_a$ of the newAmountAttribute
. Specifically, it tweaks the commitment as follows:
CashuTranscript
is a wrapper around a MerlinTranscript
, which is used to manage a transcript for cryptographic operations. The MerlinTranscript
itself is a tool for maintaining a running log of messages during interactive or non-interactive cryptographic protocols. It provides a way to securely commit to various inputs and derive challenges deterministically.
-
Domain Separation:
domain_sep
ensures that different contexts or types of operations within the protocol are distinguishable by their unique labels. This prevents potential cross-protocol attacks where inputs in one context might be interpreted as valid in another.
-
Commitments:
- The
append
method commits a group element to the transcript.
- The
-
Challenge Derivation:
- The
get_challenge
method extracts a cryptographic challenge deterministically from the transcript. This challenge is used in proofs, ensuring it depends on all prior transcript data, providing strong security guarantees.
- The
In a zero-knowledge proof (ZKP), the prover aims to convince the verifier of a statement's validity without revealing any secrets. CashuTranscript
plays a crucial role in ensuring the soundness and security of this process.
-
Non-Interactive Proofs:
- Using the Fiat-Shamir heuristic,
CashuTranscript
turns interactive proofs into non-interactive ones by simulating the verifier's role in generating challenges. This makes it possible to create proofs that can be verified later without an interactive session.
- Using the Fiat-Shamir heuristic,
-
Binding:
- The commitments recorded in the transcript bind the prover to specific values. This ensures that the prover cannot alter their proof after seeing the challenge.
-
Challenge Integrity:
- The challenges derived via
CashuTranscript
are deterministic but depend on the entire transcript. This means any tampering with the transcript will produce a different challenge, making it impossible to forge valid proofs.
- The challenges derived via
-
Security Against Replay Attacks:
- Since the transcript includes domain separators and commitments to public and private inputs, reusing a proof in a different context will result in a mismatch in the challenge, invalidating the proof.
The implementation creates and verifies Proofs of Knowledge (PoK) for linear relations using a non-interactive zero-knowledge proof (NIZKP) protocol. This approach makes use of the Fiat-Shamir heuristic, which transforms an interactive proof into a non-interactive one by using a cryptographic hash function.
A linear relation is a mathematical statement of the form:
where:
-
$(s_i)$ are secrets (values the prover knows but does not wish to reveal). -
$(P_i)$ are public points (elements from a group/in most cases the previously mentioned generators). -
$(V)$ is the verification value (public input).
The goal is for the prover to convince the verifier that they know the
In the proving phase, the prover demonstrates knowledge of the secrets
-
Generate Random Terms:
For each secret
$(s_i)$ , the prover generates a random term$(k_i)$ (a private nonce):
-
Compute Commitments:
The prover computes public-nonce commitments
$(R)$ for the linear relation using the$(k_i)$ :
-
Append to Transcript:
The prover serializes the verification value (public input)
$(V)$ and commitments$(R)$ and appends them to the running transcript. -
Compute Challenge:
The prover derives a challenge
$(c)$ from the current state of the transcript:
The challenge is a deterministic random scalar.
-
Compute Responses:
For each secret
$(s_i)$ , the prover computes a response$(z_i)$ :
-
Generate Proof:
The prover packages the responses
$({z_i})$ and the challenge$(c)$ into a proof object:
In the verification phase, the verifier ensures the proof is valid without learning the secrets.
-
Extract Proof Components:
The verifier extracts the responses
$({z_i})$ and challenge$(c)$ from the proof. -
Recompute Commitments:
Using the responses
$({z_i})$ and public points$({P_i})$ given by the proof's statement, the verifier recomputes the commitments:
- If
$(R')$ matches the original commitments, it suggests the prover's responses are consistent with the claimed linear relation.
-
Recompute Challenge:
The verifier computes the challenge from the commitments and the public inputs:
- Validate Proof: The verifier checks if:
If this equality holds, the proof is valid.
- The use of random terms
$(k_i)$ ensures that the proof does not leak information about the secrets$({s_i})$ . - The cryptographic hash function
$(H)$ guarantees that the challenge$(c)$ is unpredictable and tamper-proof. - The recomputation of
$(R')$ in the verification phase confirms the consistency of the prover's claim.
Proof of Correctness:
The responses
Substituting into the recomputed commitments during verification:
Expanding:
Using the linear relation
Thus, the recomputed commitments