Skip to main content

Technical Specifications

Let us take a deep dive into some of the concepts involving the cryptography under the hood which enables the confidential transfers.

Concepts​

Schnorr's protocol​

Schnorr's protocol is a zero-knowledge protocol that allows a Prover to prove that he knows the discrete logarithm between two group elements. It is instantiated with groups such as elliptic curve groups where the discrete logarithm problem is believed to be hard.

Given commitments to pairs representing asset-types and amounts, this protocol can be used to prove that two commitments are to the same asset-type. With minor modifications, the protocol can also be used to prove that the commitments are to distinct asset types if necessary.

Bulletproofs​

The range proofs are derived from a zero-knowledge proof scheme called Bulletproofs. This is an instantiation of a special class of range proofs where the sender proves in zero-knowledge (i.e. without revealing any other information about the transaction) that the masked (or committed) amount falls within a certain range. In particular, a sender can use this scheme to prove that the amount he sent is non-negative and does not exceed his balance, which is necessary to prevent double-spending on the chain. This is a transparent scheme, meaning it does not require any preprocessing phase or trusted setup. The security of Bulletproofs relies on the hardness of the discrete logarithm problem in elliptic curves, which is one of the oldest and most battle-tested assumptions in cryptography.

We first describe a straightforward albeit inefficient way to create a range proof. The Prover decomposes the integer representing the amount into its binary representation. He then creates commitments to each of those bits and then sends a proof attesting to the claim that each of these commitments is to a bit and that the amount is the aggregation of these bits. But this proof size is linear in the bit length of the amount, which is quite inefficient in practice. Bulletproofs is an efficient range proof which is logarithmic in the size of the amount. The core of the bulletproof protocol hinges on a technique called the recursive inner product argument.

Pedersen commitments​

The cryptographic commitments are perfectly hiding, which makes them distinct from encryptions. They do not contain any information that can be decrypted by someone with a key. Rather, they serve as a hidden fingerprint for the committed information, similar to how a server can send a hash of a file before sharing the file. The hash is a unique fingerprint that can be measured from the file, but the file cannot be obtained from the hash. Cryptographic commitments can only be β€œopened” or β€œunblinded” given the unique information that was committed and a secret value called the blinding factor. If CC is a commitment to a message mm using blinding factor rr, then CC can be uniquely computed from mm and rr, and the knowledge of rr is proof that CC was a commitment to mm. In Findora’s blinded asset records, the blinding factors are shared with the new asset owner (i.e. transfer recipient) using a method similar to a Diffie-Hellman key exchange. This user derives the blinding factors from blind share and its private key corresponding to public key. The user needs these blinding factors in order to check that the decrypted contents of the record are correct (i.e. as approved by the validators) and will also need to use them to transfer ownership of the asset in a future transaction.

As before, let G\mathbb{G} denote the Ristretto group, i.e. the group of points on the Ristretto curve. Let pp be the order of this group. For independent group generators g1,g2,h∈Gg_1,g_2,h\in \mathbb{G} and a pair (a,b)∈Fp2(a,b)\in \mathbb{F}_p^2 representing the asset type and amount, the commitment to this pair is given by g1ag2bhrg_1^{a}g_2^{b}h^r where rr is a random element in Fp\mathbb{F}_p.

Findora uses the Ristretto group, which is a quotient group built from the elliptic curve group on Curve25519. This group has order 8p8p for the prime: p=2252+27742317777372353535851937790883648493.p =2^{252} +27742317777372353535851937790883648493. The Ristretto quotient group is the unique quotient group of order pp.

Proof Generation​

The XfrProofs structure contains a zero-knowledge proof that the blinded output records are valid with respect to the blinded input records. Since the fees are denominated in the FRA token, it is necessary to prove in zero-knowledge that:

  • for every asset type other than FRA, the sum of the inputs is the same as the sum of the outputs
  • the sum of the inputs corresponding to the FRA asset is the same as the sum of the outputs plus the fees for the transaction.

Note that for some particular asset types, there might be fees, thus for that cases it proves that sum of output amounts for that asset type in the output asset records plus fees equals the sum of input amounts for the same asset type in the input records). To be more precise, if there are n (nβ‰₯1)(n \geq 1) inputs records and m (mβ‰₯1)(m \geq 1) output records,

  • Ξ±i\alpha_i is the amount in the iith input record
  • Ξ²j\beta_j is the amount in the jjth output record
  • Input[Ο„]Input[\tau] the set of input indices with asset type matching Ο„\tau
  • Output[Ο„]Output[\tau] the set of output indices with asset type matching Ο„\tau
  • T\mathcal{T} is the complete set of types other than FRA in output records

Then XfrProofs prove that

for all Ο„βˆˆTβ€…β€Šβ€…β€Šβˆ‘i∈Input[Ο„]Ξ±i=βˆ‘j∈Output[Ο„]Ξ²j\tau \in \mathcal{T} \;\; \sum_{i \in Input[\tau]} \alpha_i = \sum_{j \in Output[\tau]} \beta_j

when asset type matches FRAFRA (i.e Ο„=FRA\tau = FRA). Fees must be considered. Hence the equation becomes as follows

βˆ‘i∈Input[FRA]Ξ±i=βˆ‘j∈Output[FRA]Ξ²j+Fees\sum_{i \in Input[FRA]} \alpha_i = \sum_{j \in Output[FRA]} \beta_j + Fees

The randomness in the Pedersen commitments is communicated to the receiver in the form of text encrypted with the receiver's public key. The receiver then decrypts this text using his private key. The security of this scheme hinges on the hardness of the Discrete logarithm problem (DLP). The proof of the amount-sum equality relies on the homomorphic property of Pedersen commitments.

Proving Commitment Equality​

A confidential transaction can - and usually does - have multiple associated Pedersen commitments to (asset type, amount) pairs. To prove the so-called amount-sum equality, it is necessary to verifiably reveal which of the commitments correspond to the same asset type, without actually revealing this asset type. To show that two Pedersen commitments C1=g1a1g2b1hr1C_1 = g_1^{a_1}g_2^{b_1}h^{r_1}, C2=g1a2g2b2hr2C_2 = g_1^{a_2}g_2^{b_2}h^{r_2} are such that a1=a2a_1 = a_2, it suffices to show that the quotient C1β‹…C2βˆ’1C_1\cdot C_2^{-1} is of the form g2xhyg_2^{x}h^y for some x,yx,y known to the Prover. Using an aggregation trick, this proof can be kept constant-sized when the Prover needs to show that the asset type committed in C1C_1 is the same as the asset type committed in C2,⋯ ,CnC_2,\cdots,C_n.

Proving the Amount-Sum Equality​

Given input commitments C1in,⋯ ,CminC_1^{\mathrm{in}},\cdots, C_m^{\mathrm{in}} and output commitments C1out,⋯ ,CnoutC_1^{\mathrm{out}},\cdots, C_n^{\mathrm{out}} verifiably corresponding to the same asset type, a Prover shows that the input and output amounts sum up to the same value by proving in zero-knowledge that the element C~:=(∏i=1mCiin)β‹…(∏j=1nCjout)βˆ’1\widetilde{C}:= (\prod_{i=1}^m C_i^{\mathrm{in}})\cdot (\prod_{j=1}^n C_j^{\mathrm{out}})^{-1} is of the form g1a(mβˆ’n)hrg_1^{a(m-n)}h^{r} (or g1a(mβˆ’n)g2feeshrg_1^{a(m-n)}g_2^{\mathrm{fees}}h^{r} in case the asset_type is FRA) where rr is some integer known to the Prover and aa is the common asset-type committed in the commitments CiinC_i^{\mathrm{in}}, CjoutC_j^{\mathrm{out}}.

To prevent double-spends on the blockchain in tandem with maintaining confidentiality, it is necessary for the sender of a transaction to prove in zero-knowledge that the amounts committed are all non-negative. This requires a zero-knowledge range proof to convince a Verifier that the amounts are non-negative. Findora's confidential transfer is accompanied by proofs that the committed amounts lie in the range [0,264βˆ’1][0, 2^{64}-1]. These proofs are constructed using the Bulletproofs scheme.

Bulletproofs are particularly suited for range proofs on small ranges: the proof for a 6464-bit range is less than 1KB and takes only milliseconds to both create and verify. Bulletproofs have a batching mode where a range proof from mm points is only 64log⁑(m)64\log(m) bytes larger than a range proof for a single point (e.g. a batch proof for 100 points is less than 500 bytes larger). Bulletproofs also have a batch verification mode where the amortized time to verify many range proofs is approximately 0.34 ms per proof.

Range proofs via inner product arguments​

Let Cv=gvhrC_v = g^{v} h^r be a commitment to a value vv. To show that vv lies in the range [0,2nβˆ’1][0,2^{n}-1], it suffices for the Prover to show that he knows a vector aL=(a0,⋯ ,anβˆ’1)\mathbf{a}_L = (a_0,\cdots,a_{n-1}) such that:

  • ⟨aLβ€…β€Š,β€…β€Š2n⟩=v\langle \mathbf{a}_L\;,\; \mathbf{2}^n \rangle = v, which shows that v=βˆ‘i=0naiβ‹…2iv = \sum_{i=0}^n a_i\cdot 2^i
  • aL∘(1nβˆ’aL)=0n\mathbf{a}_L \circ (\mathbf{1}^n - \mathbf{a}_L) = \mathbf{0}^n, which shows that the entries of aL\mathbf{a}_L lie in {0,1}n\{0,1 \}^n.

In other words, this shows that the nn entries of aL\mathbb{a}_L represent the bit decomposition of vv.

For randomly generated challenges y,z∈Fpy, z\in \mathbb{F}_p, it suffices to show that,

z2β‹…βŸ¨aL,2n⟩+zβ‹…βŸ¨aLβˆ’1nβˆ’aR,yn⟩+⟨aL,aR∘yn⟩=z2β‹…vz^2 \cdot \langle \mathbf {a}_L \hspace{3pt} , \hspace{3pt} \mathbf {2}^n \rangle + z \cdot \langle \mathbf {a}_L - \mathbf {1}^n - \mathbf {a}_R \hspace{3pt} , \hspace{3pt} \mathbf {y}^n \rangle + \langle \mathbf {a}_L \hspace{3pt} , \hspace{3pt} \mathbf {a}_R \circ \mathbf {y}^n \rangle = z^2 \cdot v \hspace{20pt}

where aR=1nβˆ’aL\mathbf {a}_R = \mathbf{1}^n - \mathbf{a}_L

Proof Verification​

The verification process actually happens in batches for increasing the efficiency.

// 1. verify signature
for xfr_note in notes {
verify_transfer_multisig(xfr_note).c(d!())?;
}

let bodies = notes.iter().map(|note| &note.body).collect_vec();
batch_verify_xfr_bodies(prng, params, &bodies, policies).c(d!())

The first part of the verification process begins with verifying whether all the multisignatures assocated with the transacton is correct.

let mut bytes = vec![];
xfr_note
.body
.serialize(&mut rmp_serde::Serializer::new(&mut bytes))
.c(d!(ZeiError::SerializationError))?;
let pubkeys = xfr_note
.body
.inputs
.iter()
.map(|input| &input.public_key)
.collect_vec();
xfr_note.multisig.verify(&pubkeys, &bytes)
}

The second part of verifying the XFR Note consists of batch verifying whether the XFR bodies are correctly constructed.

// 1. verify amounts and asset types
batch_verify_xfr_body_asset_records(prng, params, bodies).c(d!())?;

// 2. verify tracing proofs
batch_verify_tracer_tracing_proof(prng, &params.pc_gens, bodies, policies).c(d!())

Verifying the XFR Bodies primarily consists of 2 steps. The first step verifies whether the asset records are correctly constructed.

pub(crate) fn batch_verify_xfr_body_asset_records<R: CryptoRng + RngCore>(
prng: &mut R,
params: &mut PublicParams,
bodies: &[&XfrBody],
) -> Result<()>

This function basically processes the XFR Body and prepares for 3 different kinds of proofs. All of these proofs are performed in a batched manner. First up is the batched range proof for verifying the confidential amounts.

let mut transcripts = vec![Transcript::new(b"Zei Range Proof"); instances.len()];
let proofs: Vec<&RangeProof> =
instances.iter().map(|(_, _, pf)| &pf.range_proof).collect();
let mut commitments = vec![];
for (input, output, proof) in instances {
commitments.push(
extract_value_commitments(input.as_slice(), output.as_slice(), proof)
.c(d!())?,
);
}
let value_commitments = commitments.iter().map(|c| c.as_slice()).collect_vec();
batch_verify_ranges(
prng,
&params.bp_gens,
&params.pc_gens,
proofs.as_slice(),
&mut transcripts,
&value_commitments,
BULLET_PROOF_RANGE,
)
.c(d!(ZeiError::XfrVerifyConfidentialAmountError))

The second part consists of verifying the confidential asset types. This step ultimately comprises of the Chaum Pedersen Equality proof of the commitment to the asset type.

let mut transcript = Transcript::new(b"AssetEquality");
let mut proof_instances = Vec::with_capacity(instances.len());
for (inputs, outputs, proof) in instances {
let instance_commitments: Result<Vec<RistrettoPoint>> = inputs
.iter()
.chain(outputs.iter())
.map(|x| match x.asset_type {
XfrAssetType::Confidential(com) => {
com.decompress().c(d!(ZeiError::ParameterError))
}
XfrAssetType::NonConfidential(asset_type) => {
Ok(pc_gens.commit(asset_type.as_scalar(), Scalar::from_u32(0)))
}
})
.collect();
proof_instances.push((instance_commitments.c(d!())?, *proof));
}
chaum_pedersen_batch_verify_multiple_eq(
&mut transcript,
prng,
&pc_gens,
&proof_instances,
)
.c(d!(ZeiError::XfrVerifyConfidentialAssetError))

The third step consists of verifying the asset mixing proofs. This consists of both confidential asset and non-confidential assets.

fn batch_verify_asset_mix<R: CryptoRng + RngCore>(
prng: &mut R,
params: &mut PublicParams,
bars_instances: &[(&[BlindAssetRecord], &[BlindAssetRecord], &AssetMixProof)],
) -> Result<()> {

For the equality of committed asset-typed and the amount-sum equalities for the asset-types, the Verifier's task boils down to verifying Schnorr proofs of knowledge of discrete logarithms. The proofs are batched so that the communication complexity and the verification time stay constant.

To verify the range proofs, the Verifier performs a sequence of inner product checks. The Verifier uses the same hashing algorithm as the Prover to get the independent group generators in G\mathbb{G}. This makes his runtime O(n)\mathbf{O}(n).

The Verifier's task also includes a sequence of inner product checks.

Β© Findora 2022
|
|

Β