feat: add zk-proof certifying that a ciphertext encrypts specified max fee value
This commit is contained in:
parent
beb95c4884
commit
601247d958
|
@ -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());
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,3 +1,4 @@
|
||||||
pub mod equality_proof;
|
pub mod equality_proof;
|
||||||
|
pub mod fee_proof;
|
||||||
pub mod validity_proof;
|
pub mod validity_proof;
|
||||||
pub mod zero_balance_proof;
|
pub mod zero_balance_proof;
|
||||||
|
|
Loading…
Reference in New Issue