From bc7ac42f2a3c0b0667a14a2b7d7a893c077f8dcb Mon Sep 17 00:00:00 2001 From: Sam Kim Date: Thu, 23 Dec 2021 09:41:57 -0500 Subject: [PATCH] feat: proof generation for max and equality proof --- zk-token-sdk/src/sigma_proofs/fee_proof.rs | 170 +++++++++++++++++---- 1 file changed, 140 insertions(+), 30 deletions(-) diff --git a/zk-token-sdk/src/sigma_proofs/fee_proof.rs b/zk-token-sdk/src/sigma_proofs/fee_proof.rs index c7d18f0ae..2f909a6d7 100644 --- a/zk-token-sdk/src/sigma_proofs/fee_proof.rs +++ b/zk-token-sdk/src/sigma_proofs/fee_proof.rs @@ -2,7 +2,7 @@ use { crate::encryption::{ elgamal::{ElGamalCiphertext, ElGamalPubkey}, - pedersen::{PedersenBase, PedersenOpening}, + pedersen::{PedersenBase, PedersenCommitment, PedersenOpening}, }, rand::rngs::OsRng, }; @@ -11,66 +11,175 @@ use { curve25519_dalek::{ ristretto::{CompressedRistretto, RistrettoPoint}, scalar::Scalar, - traits::{IsIdentity, VartimeMultiscalarMul}, + traits::{IsIdentity, MultiscalarMul, VartimeMultiscalarMul}, }, merlin::Transcript, }; -#[allow(non_snake_case)] #[derive(Clone)] pub struct FeeProof { - pub Y_H: CompressedRistretto, - pub Y_P: CompressedRistretto, - pub z: Scalar, + pub fee_max_proof: FeeMaxProof, + pub fee_equality_proof: FeeEqualityProof, } #[allow(non_snake_case, dead_code)] #[cfg(not(target_arch = "bpf"))] impl FeeProof { pub fn new( - fee_authority_elgamal_pubkey: &ElGamalPubkey, - fee_ciphertext: &ElGamalCiphertext, + amount_fee: u64, max_fee: u64, - opening: &PedersenOpening, + commitment_fee: PedersenCommitment, + opening_fee: PedersenOpening, + commitment_delta_real: PedersenCommitment, + opening_delta_real: PedersenOpening, + commitment_delta_claimed: PedersenCommitment, + opening_delta_claimed: PedersenOpening, transcript: &mut Transcript, ) -> Self { // extract the relevant scalar and Ristretto points from the input let G = PedersenBase::default().G; let H = PedersenBase::default().H; - let P = fee_authority_elgamal_pubkey.get_point(); + let x = Scalar::from(amount_fee); let m = Scalar::from(max_fee); - let C = fee_ciphertext.message_comm.get_point() - m * G; - let D = fee_ciphertext.decrypt_handle.get_point(); - let r = opening.get_scalar(); + + let C_max = commitment_fee.get_point(); + let r_max = opening_fee.get_scalar(); + + let C_delta_real = commitment_fee.get_point(); + let r_delta_real = opening_fee.get_scalar(); + + let C_delta_claimed = commitment_fee.get_point(); + let r_delta_claimed = opening_fee.get_scalar(); // record public values in transcript // // TODO: consider committing to these points outside this method - transcript.append_point(b"P", &P.compress()); - transcript.append_point(b"C", &C.compress()); - transcript.append_point(b"D", &D.compress()); + transcript.append_point(b"C_max", &C_max.compress()); + transcript.append_point(b"C_delta_real", &C_delta_real.compress()); + transcript.append_point(b"C_delta_claimed", &C_delta_claimed.compress()); - // generate a random masking factor that also serve as a nonce - let y = Scalar::random(&mut OsRng); - let Y_H = (y * H).compress(); - let Y_P = (y * P).compress(); + // generate z values depending on whether the fee exceeds max fee or not + // + // TODO: use constant time conditional + if amount_fee < max_fee { + // simulate max proof + let z_max = Scalar::random(&mut OsRng); + let c_max = Scalar::random(&mut OsRng); + let Y_max = + RistrettoPoint::multiscalar_mul(vec![z_max, c_max, c_max * x], vec![H, C_max, G]) + .compress(); - // record Y values in transcript and receive a challenge scalar - transcript.append_point(b"Y_H", &Y_H); - transcript.append_point(b"Y_P", &Y_P); - let c = transcript.challenge_scalar(b"c"); - transcript.challenge_scalar(b"w"); + let fee_max_proof = FeeMaxProof { + Y_max, z_max, + }; - println!("prover c: {:?}", c); + // generate equality proof + let y_x = Scalar::random(&mut OsRng); + let y_delta_real = Scalar::random(&mut OsRng); + let y_delta_claimed = Scalar::random(&mut OsRng); - // compute the masked encryption randomness - let z = c * r + y; + let Y_delta_real = + RistrettoPoint::multiscalar_mul(vec![y_x, y_delta_real], vec![G, H]).compress(); + let Y_delta_claimed = + RistrettoPoint::multiscalar_mul(vec![y_x, y_delta_claimed], vec![G, H]).compress(); - // TODO: actual fee calculation + transcript.append_point(b"Y_max", &Y_max); + transcript.append_point(b"Y_delta_real", &Y_delta_real); + transcript.append_point(b"Y_delta_claimed", &Y_delta_claimed); - Self { Y_H, Y_P, z } + let c = transcript.challenge_scalar(b"c"); + let c_equality = c - c_max; + + let z_x = c_equality * x + y_x; + let z_delta_real = c_equality * x + y_delta_real; + let z_delta_claimed = c_equality * x + y_delta_claimed; + + let fee_equality_proof = FeeEqualityProof { + Y_delta_real, + Y_delta_claimed, + z_x, + z_delta_real, + z_delta_claimed, + }; + + Self { + fee_max_proof, + fee_equality_proof, + } + + } else { + // simulate equality proof + let z_x = Scalar::random(&mut OsRng); + let z_delta_real = Scalar::random(&mut OsRng); + let z_delta_claimed = Scalar::random(&mut OsRng); + let c_equality = Scalar::random(&mut OsRng); + + let Y_delta_real = RistrettoPoint::multiscalar_mul( + vec![z_x, z_delta_real, -c_equality], + vec![G, H, C_delta_real], + ) + .compress(); + + let Y_delta_claimed = RistrettoPoint::multiscalar_mul( + vec![z_x, z_delta_claimed, -c_equality], + vec![G, H, C_delta_claimed], + ) + .compress(); + + let fee_equality_proof = FeeEqualityProof { + Y_delta_real, + Y_delta_claimed, + z_x, + z_delta_real, + z_delta_claimed, + }; + + // generate max proof + let y_max = Scalar::random(&mut OsRng); + let Y_max = (y_max * H).compress(); + + transcript.append_point(b"Y_max", &Y_max); + transcript.append_point(b"Y_delta_real", &Y_delta_real); + transcript.append_point(b"Y_delta_claimed", &Y_delta_claimed); + + let c = transcript.challenge_scalar(b"c"); + let c_max = c - c_equality; + + let z_max = c_max * r_max + y_max; + + let fee_max_proof = FeeMaxProof { + Y_max, z_max, + }; + + Self { + fee_max_proof, + fee_equality_proof, + } + } } +} + +#[allow(non_snake_case)] +#[derive(Clone)] +pub struct FeeMaxProof { + pub Y_max: CompressedRistretto, + pub z_max: Scalar, +} + +#[allow(non_snake_case)] +#[derive(Clone)] +pub struct FeeEqualityProof { + pub Y_delta_real: CompressedRistretto, + pub Y_delta_claimed: CompressedRistretto, + pub z_x: Scalar, + pub z_delta_real: Scalar, + pub z_delta_claimed: Scalar, +} + +#[allow(non_snake_case, dead_code)] +#[cfg(not(target_arch = "bpf"))] +impl FeeProof { pub fn verify( self, @@ -119,6 +228,7 @@ impl FeeProof { } } + #[cfg(test)] mod test { use super::*;