feat: add zk-proof certifying that a ciphertext encrypts specified max fee value

This commit is contained in:
Sam Kim 2021-12-16 11:27:58 -05:00 committed by Michael Vines
parent beb95c4884
commit 601247d958
2 changed files with 158 additions and 0 deletions

View File

@ -0,0 +1,157 @@
#[cfg(not(target_arch = "bpf"))]
use {
crate::encryption::{
elgamal::{ElGamalCiphertext, ElGamalPubkey},
pedersen::{PedersenBase, PedersenOpening},
},
rand::rngs::OsRng,
};
use {
crate::{errors::ProofError, transcript::TranscriptProtocol},
curve25519_dalek::{
ristretto::{CompressedRistretto, RistrettoPoint},
scalar::Scalar,
traits::{IsIdentity, VartimeMultiscalarMul},
},
merlin::Transcript,
};
#[allow(non_snake_case)]
#[derive(Clone)]
pub struct FeeProof {
pub Y_H: CompressedRistretto,
pub Y_P: CompressedRistretto,
pub z: Scalar,
}
#[allow(non_snake_case, dead_code)]
#[cfg(not(target_arch = "bpf"))]
impl FeeProof {
pub fn new(
fee_authority_elgamal_pubkey: &ElGamalPubkey,
fee_ciphertext: &ElGamalCiphertext,
max_fee: u64,
opening: &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 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();
// 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());
// 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();
// 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");
println!("prover c: {:?}", c);
// compute the masked encryption randomness
let z = c * r + y;
// TODO: actual fee calculation
Self { Y_H, Y_P, z }
}
pub fn verify(
self,
fee_authority_elgamal_pubkey: &ElGamalPubkey,
fee_ciphertext: &ElGamalCiphertext,
max_fee: u64,
transcript: &mut Transcript,
) -> Result<(), ProofError> {
// extract the relevant scalar and Ristretto points from the inputs
let G = PedersenBase::default().G;
let H = PedersenBase::default().H;
let P = fee_authority_elgamal_pubkey.get_point();
let m = Scalar::from(max_fee);
let C = fee_ciphertext.message_comm.get_point() - m * G;
let D = fee_ciphertext.decrypt_handle.get_point();
// record public values in transcript
transcript.append_point(b"P", &P.compress());
transcript.append_point(b"C", &C.compress());
transcript.append_point(b"D", &D.compress());
transcript.validate_and_append_point(b"Y_H", &self.Y_H)?;
transcript.validate_and_append_point(b"Y_P", &self.Y_P)?;
// extract challenge scalars
let c = transcript.challenge_scalar(b"c");
let w = transcript.challenge_scalar(b"w");
println!("verifier c: {:?}", c);
// check that the required algebraic condition holds
let Y_H = self.Y_H.decompress().ok_or(ProofError::VerificationError)?;
let Y_P = self.Y_P.decompress().ok_or(ProofError::VerificationError)?;
let check = RistrettoPoint::vartime_multiscalar_mul(
vec![self.z, -c, -Scalar::one(), w * self.z, -w * c, -w],
vec![H, C, Y_H, P, D, Y_P],
);
if check.is_identity() {
Ok(())
} else {
Err(ProofError::VerificationError)
}
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::encryption::elgamal::ElGamalKeypair;
#[test]
fn test_fee_proof() {
// success case
let fee_authority_keypair = ElGamalKeypair::default();
let fee_authority_pubkey = fee_authority_keypair.public;
let max_fee: u64 = 55;
let opening = PedersenOpening::random(&mut OsRng);
let fee_ciphertext = fee_authority_pubkey.encrypt_with(max_fee, &opening);
let mut transcript_prover = Transcript::new(b"Test");
let mut transcript_verifier = Transcript::new(b"Test");
let proof = FeeProof::new(
&fee_authority_pubkey,
&fee_ciphertext,
max_fee,
&opening,
&mut transcript_prover,
);
assert!(proof
.verify(
&fee_authority_pubkey,
&fee_ciphertext,
max_fee,
&mut transcript_verifier
)
.is_ok());
}
}

View File

@ -1,3 +1,4 @@
pub mod equality_proof;
pub mod fee_proof;
pub mod validity_proof;
pub mod zero_balance_proof;