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_aG_\text{blind} + aG_\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_aG_\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_sG_\text{blind} + sG_\text{script}$
Equivalent to Cashu's BlindedSignature
.
The Mint generates this algebraic MAC using its secret parameters (sk
) after verifying RandomizedCredentials
(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 = wG_w + x_0U + x_1tU + y_aM_a + y_sM_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_aG_\text{zamount} + M_a$ $C_s = r_aG_\text{zscript} + M_s$ $C_{x_0} = r_aG_{x_0} + U$ $C_{x_1} = r_aG_{x_1} + tU$ -
$C_v = r_aG_\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 RandomizedCredentials
-
$Z = r_aI$ where$r_a$ is the blinding factor fromAmountAttribute
. -
$C_a = r_aG_\text{zamount} + r_aG_\text{blind} + aG_\text{amount}$ to prove$r_a$ is indeed the same as inAmountAttribute
. -
$C_{x_1} = tC_{x_0} + (-tr_a)G_{x_0} + r_aG_{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
During any swap operation, a client has the option to reveal the Coin
's script commitment
However, if the client chooses not to reveal the script, they must instead prove that the script encoded in each of the new ScriptAttribute
s matches the script encoded in the old RandomizedCredential
s. This proof can be accomplished in an all-to-all manner using a batch discrete logarithm equivalence.
RandomizedCoin
s provided and Coin
s:
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 every operation.
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$ with a script hash$s$ . - 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 new
AmountAttribute
andScriptAttribute
pairs that encode the current wallet balance (minus any fees) and, if applicable, the same script hash as in the currentScriptAttribute
(65-66). - Generate
RandomizedCoin
from the oldCoin
(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 providedRandomizedCredential
s are valid and unspent. (75) -
$\pi_\text{script}$ : Ensures all newScriptAttribute
s encode the same script hash$s$ as the oldRandomizedCredential
s. (81).
The client sends:
- (old
RandomizedCoin
s, newAmountAttribute
/ScriptAttribute
pairs) - All proofs.
The Mint then (89-105):
- Acknowledges the balance difference
$\Delta_a$ . - Verifies that it hasn’t seen the
RandomizedCoin
$C_a$ before. - Validates all proofs.
If verification passes, the Mint issues new MAC
s for the new AmountAttribute
and ScriptAttribute
pairs (108). As with the BootstrapRequest
, the Mint also produces
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