diff --git a/zk-token-sdk/src/instruction/close_account.rs b/zk-token-sdk/src/instruction/close_account.rs index e1dbc4d80..938667f04 100644 --- a/zk-token-sdk/src/instruction/close_account.rs +++ b/zk-token-sdk/src/instruction/close_account.rs @@ -5,22 +5,13 @@ use { #[cfg(not(target_arch = "bpf"))] use { crate::{ - encryption::{ - elgamal::{ElGamalCiphertext, ElGamalKeypair, ElGamalPubkey}, - pedersen::PedersenBase, - }, + encryption::elgamal::{ElGamalCiphertext, ElGamalKeypair, ElGamalPubkey}, errors::ProofError, instruction::Verifiable, sigma_proofs::zero_balance_proof::ZeroBalanceProof, transcript::TranscriptProtocol, }, - curve25519_dalek::{ - ristretto::RistrettoPoint, - scalar::Scalar, - traits::{IsIdentity, MultiscalarMul}, - }, merlin::Transcript, - rand::rngs::OsRng, std::convert::TryInto, }; @@ -84,42 +75,15 @@ impl CloseAccountProof { pub fn new(source_keypair: &ElGamalKeypair, balance: &ElGamalCiphertext) -> Self { let mut transcript = Self::transcript_new(); + // TODO: Add ciphertext to transcript // add a domain separator to record the start of the protocol transcript.close_account_proof_domain_sep(); - - - // extract the relevant scalar and Ristretto points from the input - let P = source_keypair.public.get_point(); - let s = source_keypair.secret.get_scalar(); - - let C = balance.message_comm.get_point(); - let D = balance.decrypt_handle.get_point(); - - // record ElGamal pubkey and ciphertext in the transcript - 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 serves as a nonce - let y = Scalar::random(&mut OsRng); - let Y_P = (y * P).compress(); - let Y_D = (y * D).compress(); - - // record Y in transcript and receive a challenge scalar - transcript.append_point(b"Y_P", &Y_P); - transcript.append_point(b"Y_D", &Y_D); - let c = transcript.challenge_scalar(b"c"); - transcript.challenge_scalar(b"w"); - - // compute the masked secret key - let z = c * s + y; + let proof = ZeroBalanceProof::new(source_keypair, balance, &mut transcript); CloseAccountProof { - Y_P: Y_P.into(), - Y_D: Y_D.into(), - z: z.into(), + proof: proof.into(), } } @@ -133,44 +97,9 @@ impl CloseAccountProof { // add a domain separator to record the start of the protocol transcript.close_account_proof_domain_sep(); - // extract the relevant scalar and Ristretto points from the input - let P = elgamal_pubkey.get_point(); - let C = balance.message_comm.get_point(); - let D = balance.decrypt_handle.get_point(); - - let H = PedersenBase::default().H; - - let Y_P = self.Y_P.into(); - let Y_D = self.Y_D.into(); - let z = self.z.into(); - - // record ElGamal pubkey and ciphertext in the transcript - transcript.validate_and_append_point(b"P", &P.compress())?; - transcript.append_point(b"C", &C.compress()); - transcript.append_point(b"D", &D.compress()); - - // record Y in transcript and receive challenge scalars - transcript.validate_and_append_point(b"Y_P", &Y_P)?; - transcript.append_point(b"Y_D", &Y_D); - - let c = transcript.challenge_scalar(b"c"); - let w = transcript.challenge_scalar(b"w"); // w used for multiscalar multiplication verification - - // decompress R or return verification error - let Y_P = Y_P.decompress().ok_or(ProofError::VerificationError)?; - let Y_D = Y_D.decompress().ok_or(ProofError::VerificationError)?; - - // check the required algebraic relation - let check = RistrettoPoint::multiscalar_mul( - vec![z, -c, -Scalar::one(), w * z, -w * c, -w], - vec![P, H, Y_P, D, C, Y_D], - ); - - if check.is_identity() { - Ok(()) - } else { - Err(ProofError::VerificationError) - } + // verify zero balance proof + let proof: ZeroBalanceProof = self.proof.try_into()?; + proof.verify(elgamal_pubkey, balance, &mut transcript) } } diff --git a/zk-token-sdk/src/instruction/transfer.rs b/zk-token-sdk/src/instruction/transfer.rs index 14bd46149..08a9b4658 100644 --- a/zk-token-sdk/src/instruction/transfer.rs +++ b/zk-token-sdk/src/instruction/transfer.rs @@ -10,10 +10,10 @@ use { elgamal::{ElGamalCiphertext, ElGamalKeypair, ElGamalPubkey, ElGamalSecretKey}, pedersen::{Pedersen, PedersenCommitment, PedersenDecryptHandle, PedersenOpening}, }, - sigma_proofs::{equality_proof::EqualityProof, validity_proof::ValidityProof}, errors::ProofError, instruction::{Role, Verifiable}, range_proof::RangeProof, + sigma_proofs::{equality_proof::EqualityProof, validity_proof::ValidityProof}, transcript::TranscriptProtocol, }, curve25519_dalek::scalar::Scalar, diff --git a/zk-token-sdk/src/instruction/withdraw.rs b/zk-token-sdk/src/instruction/withdraw.rs index bc4b14e37..f62356a0f 100644 --- a/zk-token-sdk/src/instruction/withdraw.rs +++ b/zk-token-sdk/src/instruction/withdraw.rs @@ -9,10 +9,10 @@ use { elgamal::{ElGamalCiphertext, ElGamalKeypair, ElGamalPubkey}, pedersen::{Pedersen, PedersenCommitment, PedersenOpening}, }, - sigma_proofs::equality_proof::EqualityProof, errors::ProofError, instruction::Verifiable, range_proof::RangeProof, + sigma_proofs::equality_proof::EqualityProof, transcript::TranscriptProtocol, }, merlin::Transcript, diff --git a/zk-token-sdk/src/sigma_proofs/zero_balance_proof.rs b/zk-token-sdk/src/sigma_proofs/zero_balance_proof.rs index a02006f84..71680cef9 100644 --- a/zk-token-sdk/src/sigma_proofs/zero_balance_proof.rs +++ b/zk-token-sdk/src/sigma_proofs/zero_balance_proof.rs @@ -34,7 +34,6 @@ impl ZeroBalanceProof { elgamal_ciphertext: &ElGamalCiphertext, transcript: &mut Transcript, ) -> Self { - // extract the relevant scalar and Ristretto points from the input let P = elgamal_keypair.public.get_point(); let s = elgamal_keypair.secret.get_scalar(); @@ -61,11 +60,7 @@ impl ZeroBalanceProof { // compute the masked secret key let z = c * s + y; - Self { - Y_P, - Y_D, - z, - } + Self { Y_P, Y_D, z } } pub fn verify( @@ -128,18 +123,17 @@ impl ZeroBalanceProof { let z = Scalar::from_canonical_bytes(*z).ok_or(ProofError::FormatError)?; - Ok(ZeroBalanceProof { - Y_P, - Y_D, - z, - }) + Ok(ZeroBalanceProof { Y_P, Y_D, z }) } } #[cfg(test)] mod test { use super::*; - use crate::encryption::{elgamal::ElGamalKeypair, pedersen::Pedersen}; + use crate::encryption::{ + elgamal::ElGamalKeypair, + pedersen::{Pedersen, PedersenDecryptHandle, PedersenOpening}, + }; #[test] fn test_zero_balance_proof() { @@ -150,18 +144,34 @@ mod test { // general case: encryption of 0 let elgamal_ciphertext = source_keypair.public.encrypt(0_u64); - let proof = ZeroBalanceProof::new(&source_keypair, &elgamal_ciphertext, &mut transcript_prover); - assert!(proof.verify(&source_keypair.public, &elgamal_ciphertext, &mut transcript_verifier).is_ok()); + let proof = + ZeroBalanceProof::new(&source_keypair, &elgamal_ciphertext, &mut transcript_prover); + assert!(proof + .verify( + &source_keypair.public, + &elgamal_ciphertext, + &mut transcript_verifier + ) + .is_ok()); // general case: encryption of > 0 let elgamal_ciphertext = source_keypair.public.encrypt(1_u64); - let proof = ZeroBalanceProof::new(&source_keypair, &elgamal_ciphertext, &mut transcript_prover); - assert!(proof.verify(&source_keypair.public, &elgamal_ciphertext, &mut transcript_verifier).is_err()); + let proof = + ZeroBalanceProof::new(&source_keypair, &elgamal_ciphertext, &mut transcript_prover); + assert!(proof + .verify( + &source_keypair.public, + &elgamal_ciphertext, + &mut transcript_verifier + ) + .is_err()); // // edge case: all zero ciphertext - such ciphertext should always be a valid encryption of 0 let zeroed_ct = ElGamalCiphertext::default(); let proof = ZeroBalanceProof::new(&source_keypair, &zeroed_ct, &mut transcript_prover); - assert!(proof.verify(&source_keypair.public, &zeroed_ct, &mut transcript_verifier).is_ok()); + assert!(proof + .verify(&source_keypair.public, &zeroed_ct, &mut transcript_verifier) + .is_ok()); // edge cases: only C or D is zero - such ciphertext is always invalid let zeroed_comm = Pedersen::with(0_u64, &PedersenOpening::default()); @@ -172,9 +182,17 @@ mod test { decrypt_handle: handle, }; - let proof = ZeroBalanceProof::new(&source_keypair, &zeroed_comm_ciphertext, &mut transcript_prover); + let proof = ZeroBalanceProof::new( + &source_keypair, + &zeroed_comm_ciphertext, + &mut transcript_prover, + ); assert!(proof - .verify(&source_keypair.public, &zeroed_comm_ciphertext, &mut transcript_verifier) + .verify( + &source_keypair.public, + &zeroed_comm_ciphertext, + &mut transcript_verifier + ) .is_err()); let (zero_comm, _) = Pedersen::new(0_u64); @@ -183,9 +201,17 @@ mod test { decrypt_handle: PedersenDecryptHandle::default(), }; - let proof = ZeroBalanceProof::new(&source_keypair, &zeroed_handle_ciphertext, &mut transcript_prover); + let proof = ZeroBalanceProof::new( + &source_keypair, + &zeroed_handle_ciphertext, + &mut transcript_prover, + ); assert!(proof - .verify(&source_keypair.public, &zeroed_handle_ciphertext, &mut transcript_verifier) + .verify( + &source_keypair.public, + &zeroed_handle_ciphertext, + &mut transcript_verifier + ) .is_err()); } } diff --git a/zk-token-sdk/src/zk_token_elgamal/convert.rs b/zk-token-sdk/src/zk_token_elgamal/convert.rs index 5483d100a..50c44e3a8 100644 --- a/zk-token-sdk/src/zk_token_elgamal/convert.rs +++ b/zk-token-sdk/src/zk_token_elgamal/convert.rs @@ -20,9 +20,12 @@ mod target_arch { elgamal::{ElGamalCiphertext, ElGamalPubkey}, pedersen::{PedersenCommitment, PedersenDecryptHandle}, }, - sigma_proofs::{equality_proof::EqualityProof, validity_proof::ValidityProof, zero_balance_proof::ZeroBalanceProof}, errors::ProofError, range_proof::RangeProof, + sigma_proofs::{ + equality_proof::EqualityProof, validity_proof::ValidityProof, + zero_balance_proof::ZeroBalanceProof, + }, }, curve25519_dalek::{ristretto::CompressedRistretto, scalar::Scalar}, std::convert::TryFrom, diff --git a/zk-token-sdk/src/zk_token_elgamal/pod.rs b/zk-token-sdk/src/zk_token_elgamal/pod.rs index 3514d25d3..5721aa041 100644 --- a/zk-token-sdk/src/zk_token_elgamal/pod.rs +++ b/zk-token-sdk/src/zk_token_elgamal/pod.rs @@ -72,7 +72,7 @@ unsafe impl Pod for ValidityProof {} /// Serialization of zero balance proofs #[derive(Clone, Copy)] #[repr(transparent)] -pub struct ZeroBalanceProof(pub [u8; 160]); +pub struct ZeroBalanceProof(pub [u8; 96]); // `ZeroBalanceProof` is a Pod and Zeroable. // Add the marker traits manually because `bytemuck` only adds them for some `u8` arrays