From 30871784e4b5a99a8509b7c926829274b6d8f1c1 Mon Sep 17 00:00:00 2001 From: Sam Kim Date: Sun, 12 Dec 2021 10:23:10 -0500 Subject: [PATCH] incorporate validity proof into transfer proof --- zk-token-sdk/src/instruction/transfer.rs | 52 ++++++++++++++++---- zk-token-sdk/src/transcript.rs | 17 +++++-- zk-token-sdk/src/validity_proof/mod.rs | 6 +-- zk-token-sdk/src/zk_token_elgamal/convert.rs | 15 ++++++ zk-token-sdk/src/zk_token_elgamal/pod.rs | 13 ++++- 5 files changed, 82 insertions(+), 21 deletions(-) diff --git a/zk-token-sdk/src/instruction/transfer.rs b/zk-token-sdk/src/instruction/transfer.rs index 1f2d3cc59..62033d870 100644 --- a/zk-token-sdk/src/instruction/transfer.rs +++ b/zk-token-sdk/src/instruction/transfer.rs @@ -15,6 +15,7 @@ use { instruction::{Role, Verifiable}, range_proof::RangeProof, transcript::TranscriptProtocol, + validity_proof::ValidityProof, }, curve25519_dalek::scalar::Scalar, merlin::Transcript, @@ -117,8 +118,7 @@ impl TransferData { &dest_pk, &auditor_pk, (amount_lo as u64, amount_hi as u64), - &open_lo, - &open_hi, + (&open_lo, &open_hi), new_spendable_balance, &new_spendable_ct, ); @@ -205,6 +205,9 @@ pub struct TransferProof { /// Associated equality proof pub equality_proof: pod::EqualityProof, + /// Associated ciphertext validity proof + pub validity_proof: pod::ValidityProof, + // Associated range proof pub range_proof: pod::RangeProof128, } @@ -220,11 +223,10 @@ impl TransferProof { #[allow(clippy::many_single_char_names)] pub fn new( source_keypair: &ElGamalKeypair, - _dest_pk: &ElGamalPubkey, - _auditor_pk: &ElGamalPubkey, + dest_pk: &ElGamalPubkey, + auditor_pk: &ElGamalPubkey, transfer_amt: (u64, u64), - lo_open: &PedersenOpening, - hi_open: &PedersenOpening, + openings: (&PedersenOpening, &PedersenOpening), source_new_balance: u64, source_new_balance_ct: &ElGamalCiphertext, ) -> Self { @@ -260,19 +262,27 @@ impl TransferProof { &mut transcript, ); - // TODO: Add ct validity proof + // generate ciphertext validity proof + let validity_proof = ValidityProof::new( + &dest_pk, + &auditor_pk, + transfer_amt, + openings, + &mut transcript, + ); // generate the range proof let range_proof = RangeProof::create( vec![source_new_balance, transfer_amt.0, transfer_amt.1], vec![64, 32, 32], - vec![&source_open, lo_open, hi_open], + vec![&source_open, openings.0, openings.1], &mut transcript, ); Self { source_commitment: source_commitment.into(), equality_proof: equality_proof.try_into().expect("equality proof"), + validity_proof: validity_proof.try_into().expect("validity proof"), range_proof: range_proof.try_into().expect("range proof"), } } @@ -280,8 +290,8 @@ impl TransferProof { pub fn verify( self, amount_comms: &TransferCommitments, - _decryption_handles_lo: &TransferDecryptHandles, - _decryption_handles_hi: &TransferDecryptHandles, + decryption_handles_lo: &TransferDecryptHandles, + decryption_handles_hi: &TransferDecryptHandles, new_spendable_ct: &pod::ElGamalCiphertext, transfer_public_keys: &TransferPubkeys, ) -> Result<(), ProofError> { @@ -289,6 +299,7 @@ impl TransferProof { let commitment: PedersenCommitment = self.source_commitment.try_into()?; let equality_proof: EqualityProof = self.equality_proof.try_into()?; + let validity_proof: ValidityProof = self.validity_proof.try_into()?; let range_proof: RangeProof = self.range_proof.try_into()?; // add a domain separator to record the start of the protocol @@ -314,7 +325,28 @@ impl TransferProof { // TODO: we can also consider verifying equality and range proof in a batch equality_proof.verify(&source_pk, &new_spendable_ct, &commitment, &mut transcript)?; + // TODO: record destination and auditor public keys to transcript + let dest_elgamal_pubkey: ElGamalPubkey = transfer_public_keys.dest_pk.try_into()?; + let auditor_elgamal_pubkey: ElGamalPubkey = transfer_public_keys.auditor_pk.try_into()?; + + let amount_comm_lo: PedersenCommitment = amount_comms.lo.try_into()?; + let amount_comm_hi: PedersenCommitment = amount_comms.hi.try_into()?; + + let handle_lo_dest: PedersenDecryptHandle = decryption_handles_lo.dest.try_into()?; + let handle_hi_dest: PedersenDecryptHandle = decryption_handles_hi.dest.try_into()?; + + let handle_lo_auditor: PedersenDecryptHandle = decryption_handles_lo.auditor.try_into()?; + let handle_hi_auditor: PedersenDecryptHandle = decryption_handles_hi.auditor.try_into()?; + // TODO: validity proof + validity_proof.verify( + &dest_elgamal_pubkey, + &auditor_elgamal_pubkey, + (&amount_comm_lo, &amount_comm_hi), + (&handle_lo_dest, &handle_hi_dest), + (&handle_lo_auditor, &handle_hi_auditor), + &mut transcript, + )?; // verify range proof range_proof.verify( diff --git a/zk-token-sdk/src/transcript.rs b/zk-token-sdk/src/transcript.rs index b631708d9..836ef2635 100644 --- a/zk-token-sdk/src/transcript.rs +++ b/zk-token-sdk/src/transcript.rs @@ -6,11 +6,11 @@ use { pub trait TranscriptProtocol { /// Append a domain separator for an `n`-bit rangeproof for ElGamalKeypair - /// ciphertext using a decryption key // TODO: remove? + /// ciphertext using a decryption key fn rangeproof_from_key_domain_sep(&mut self, n: u64); /// Append a domain separator for an `n`-bit rangeproof for ElGamalKeypair - /// ciphertext using an opening // TODO: remove? + /// ciphertext using an opening fn rangeproof_from_opening_domain_sep(&mut self, n: u64); /// Append a domain separator for a length-`n` inner product proof. @@ -19,6 +19,9 @@ pub trait TranscriptProtocol { /// Append a domain separator for close account proof. fn close_account_proof_domain_sep(&mut self); + /// Append a domain separator for update account public key proof. + fn update_account_public_key_proof_domain_sep(&mut self); + /// Append a domain separator for withdraw proof. fn withdraw_proof_domain_sep(&mut self); @@ -60,15 +63,19 @@ impl TranscriptProtocol for Transcript { } fn close_account_proof_domain_sep(&mut self) { - self.append_message(b"dom_sep", b"CloseAccountProof"); + self.append_message(b"dom-sep", b"CloseAccountProof"); + } + + fn update_account_public_key_proof_domain_sep(&mut self) { + self.append_message(b"dom-sep", b"UpdateAccountPublicKeyProof"); } fn withdraw_proof_domain_sep(&mut self) { - self.append_message(b"dom_sep", b"WithdrawProof"); + self.append_message(b"dom-sep", b"WithdrawProof"); } fn transfer_proof_domain_sep(&mut self) { - self.append_message(b"dom_sep", b"TransferProof"); + self.append_message(b"dom-sep", b"TransferProof"); } fn append_scalar(&mut self, label: &'static [u8], scalar: &Scalar) { diff --git a/zk-token-sdk/src/validity_proof/mod.rs b/zk-token-sdk/src/validity_proof/mod.rs index a05af1885..ccaa6c9c9 100644 --- a/zk-token-sdk/src/validity_proof/mod.rs +++ b/zk-token-sdk/src/validity_proof/mod.rs @@ -1,7 +1,7 @@ #[cfg(not(target_arch = "bpf"))] use { crate::encryption::{ - elgamal::{ElGamalCiphertext, ElGamalKeypair, ElGamalPubkey}, + elgamal::{ElGamalKeypair, ElGamalPubkey}, pedersen::{PedersenBase, PedersenCommitment, PedersenDecryptHandle, PedersenOpening}, }, curve25519_dalek::traits::MultiscalarMul, @@ -62,8 +62,6 @@ impl ValidityProof { let c = transcript.challenge_scalar(b"c"); transcript.challenge_scalar(b"w"); - println!("prover: {:?}", t); - // aggregate lo and hi messages and openings let x = Scalar::from(messages.0) + t * Scalar::from(messages.1); let r = openings.0.get_scalar() + t * openings.1.get_scalar(); @@ -104,8 +102,6 @@ impl ValidityProof { let w = transcript.challenge_scalar(b"w"); let ww = w * w; - println!("verifier: {:?}", t); - // check the required algebraic conditions let Y_0 = self.Y_0.decompress().ok_or(ProofError::VerificationError)?; let Y_1 = self.Y_1.decompress().ok_or(ProofError::VerificationError)?; diff --git a/zk-token-sdk/src/zk_token_elgamal/convert.rs b/zk-token-sdk/src/zk_token_elgamal/convert.rs index 440bd1ab7..319365696 100644 --- a/zk-token-sdk/src/zk_token_elgamal/convert.rs +++ b/zk-token-sdk/src/zk_token_elgamal/convert.rs @@ -23,6 +23,7 @@ mod target_arch { equality_proof::EqualityProof, errors::ProofError, range_proof::RangeProof, + validity_proof::ValidityProof, }, curve25519_dalek::{ristretto::CompressedRistretto, scalar::Scalar}, std::convert::TryFrom, @@ -155,6 +156,20 @@ mod target_arch { } } + impl From for pod::ValidityProof { + fn from(proof: ValidityProof) -> Self { + Self(proof.to_bytes()) + } + } + + impl TryFrom for ValidityProof { + type Error = ProofError; + + fn try_from(pod: pod::ValidityProof) -> Result { + Self::from_bytes(&pod.0) + } + } + impl TryFrom for pod::RangeProof64 { type Error = ProofError; diff --git a/zk-token-sdk/src/zk_token_elgamal/pod.rs b/zk-token-sdk/src/zk_token_elgamal/pod.rs index 5a558cae2..88f2e351d 100644 --- a/zk-token-sdk/src/zk_token_elgamal/pod.rs +++ b/zk-token-sdk/src/zk_token_elgamal/pod.rs @@ -54,11 +54,22 @@ impl fmt::Debug for PedersenDecryptHandle { #[repr(transparent)] pub struct EqualityProof(pub [u8; 192]); -// `PodRangeProof64` is a Pod and Zeroable. +// `EqualityProof` is a Pod and Zeroable. // Add the marker traits manually because `bytemuck` only adds them for some `u8` arrays unsafe impl Zeroable for EqualityProof {} unsafe impl Pod for EqualityProof {} +/// Serialization of validity proofs +#[derive(Clone, Copy)] +#[repr(transparent)] +pub struct ValidityProof(pub [u8; 160]); + +// `ValidityProof` is a Pod and Zeroable. +// Add the marker traits manually because `bytemuck` only adds them for some `u8` arrays +unsafe impl Zeroable for ValidityProof {} +unsafe impl Pod for ValidityProof {} + + /// Serialization of range proofs for 64-bit numbers (for `Withdraw` instruction) #[derive(Clone, Copy)] #[repr(transparent)]