From 0495051a673b0599385891bb2b5caaf650eab879 Mon Sep 17 00:00:00 2001 From: samkim-crypto Date: Thu, 1 Jun 2023 15:58:36 +0900 Subject: [PATCH] [zk-token-sdk] Add `VerifyGroupedCiphertext2HandlesValidity` and `VerifyBatchedGroupedCiphertext2HandlesValidity` proof instructions (#31816) * add grouped ciphertext validity proof data * add batched grouped ciphertext validity proof data * rename proof contexts and data for consistency * add grouped ciphertext validity proof instructions * Update zk-token-sdk/src/instruction/batched_grouped_ciphertext_validity.rs Co-authored-by: Tyera --------- Co-authored-by: Tyera --- .../tests/process_transaction.rs | 127 ++++++++++- programs/zk-token-proof/src/lib.rs | 23 ++ .../batched_grouped_ciphertext_validity.rs | 206 ++++++++++++++++++ .../grouped_ciphertext_validity.rs | 166 ++++++++++++++ zk-token-sdk/src/instruction/mod.rs | 11 + zk-token-sdk/src/transcript.rs | 15 ++ .../src/zk_token_proof_instruction.rs | 51 +++++ 7 files changed, 598 insertions(+), 1 deletion(-) create mode 100644 zk-token-sdk/src/instruction/batched_grouped_ciphertext_validity.rs create mode 100644 zk-token-sdk/src/instruction/grouped_ciphertext_validity.rs diff --git a/programs/zk-token-proof-tests/tests/process_transaction.rs b/programs/zk-token-proof-tests/tests/process_transaction.rs index 25fd08c025..6b68c1c605 100644 --- a/programs/zk-token-proof-tests/tests/process_transaction.rs +++ b/programs/zk-token-proof-tests/tests/process_transaction.rs @@ -11,6 +11,7 @@ use { solana_zk_token_sdk::{ encryption::{ elgamal::ElGamalKeypair, + grouped_elgamal::GroupedElGamal, pedersen::{Pedersen, PedersenOpening}, }, instruction::*, @@ -21,7 +22,7 @@ use { std::mem::size_of, }; -const VERIFY_INSTRUCTION_TYPES: [ProofInstruction; 11] = [ +const VERIFY_INSTRUCTION_TYPES: [ProofInstruction; 13] = [ ProofInstruction::VerifyZeroBalance, ProofInstruction::VerifyWithdraw, ProofInstruction::VerifyCiphertextCiphertextEquality, @@ -33,6 +34,8 @@ const VERIFY_INSTRUCTION_TYPES: [ProofInstruction; 11] = [ ProofInstruction::VerifyBatchedRangeProofU128, ProofInstruction::VerifyBatchedRangeProofU256, ProofInstruction::VerifyCiphertextCommitmentEquality, + ProofInstruction::VerifyGroupedCiphertext2HandlesValidity, + ProofInstruction::VerifyBatchedGroupedCiphertext2HandlesValidity, ]; #[tokio::test] @@ -572,6 +575,128 @@ async fn test_ciphertext_commitment_equality() { .await; } +#[tokio::test] +async fn test_grouped_ciphertext_2_handles_validity() { + let destination_pubkey = ElGamalKeypair::new_rand().public; + let auditor_pubkey = ElGamalKeypair::new_rand().public; + + let amount: u64 = 55; + let opening = PedersenOpening::new_rand(); + let grouped_ciphertext = + GroupedElGamal::encrypt_with([&destination_pubkey, &auditor_pubkey], amount, &opening); + + let success_proof_data = GroupedCiphertext2HandlesValidityProofData::new( + &destination_pubkey, + &auditor_pubkey, + &grouped_ciphertext, + amount, + &opening, + ) + .unwrap(); + + let incorrect_opening = PedersenOpening::new_rand(); + let fail_proof_data = GroupedCiphertext2HandlesValidityProofData::new( + &destination_pubkey, + &auditor_pubkey, + &grouped_ciphertext, + amount, + &incorrect_opening, + ) + .unwrap(); + + test_verify_proof_without_context( + ProofInstruction::VerifyGroupedCiphertext2HandlesValidity, + &success_proof_data, + &fail_proof_data, + ) + .await; + + test_verify_proof_with_context( + ProofInstruction::VerifyGroupedCiphertext2HandlesValidity, + size_of::>(), + &success_proof_data, + &fail_proof_data, + ) + .await; + + test_close_context_state( + ProofInstruction::VerifyGroupedCiphertext2HandlesValidity, + size_of::>(), + &success_proof_data, + ) + .await; +} + +#[tokio::test] +async fn test_batched_grouped_ciphertext_2_handles_validity() { + let destination_pubkey = ElGamalKeypair::new_rand().public; + let auditor_pubkey = ElGamalKeypair::new_rand().public; + + let amount_lo: u64 = 55; + let amount_hi: u64 = 22; + + let opening_lo = PedersenOpening::new_rand(); + let opening_hi = PedersenOpening::new_rand(); + + let grouped_ciphertext_lo = GroupedElGamal::encrypt_with( + [&destination_pubkey, &auditor_pubkey], + amount_lo, + &opening_lo, + ); + let grouped_ciphertext_hi = GroupedElGamal::encrypt_with( + [&destination_pubkey, &auditor_pubkey], + amount_hi, + &opening_hi, + ); + + let success_proof_data = BatchedGroupedCiphertext2HandlesValidityProofData::new( + &destination_pubkey, + &auditor_pubkey, + &grouped_ciphertext_lo, + &grouped_ciphertext_hi, + amount_lo, + amount_hi, + &opening_lo, + &opening_hi, + ) + .unwrap(); + + let incorrect_opening = PedersenOpening::new_rand(); + let fail_proof_data = BatchedGroupedCiphertext2HandlesValidityProofData::new( + &destination_pubkey, + &auditor_pubkey, + &grouped_ciphertext_lo, + &grouped_ciphertext_hi, + amount_lo, + amount_hi, + &incorrect_opening, + &opening_hi, + ) + .unwrap(); + + test_verify_proof_without_context( + ProofInstruction::VerifyBatchedGroupedCiphertext2HandlesValidity, + &success_proof_data, + &fail_proof_data, + ) + .await; + + test_verify_proof_with_context( + ProofInstruction::VerifyBatchedGroupedCiphertext2HandlesValidity, + size_of::>(), + &success_proof_data, + &fail_proof_data, + ) + .await; + + test_close_context_state( + ProofInstruction::VerifyBatchedGroupedCiphertext2HandlesValidity, + size_of::>(), + &success_proof_data, + ) + .await; +} + async fn test_verify_proof_without_context( proof_instruction: ProofInstruction, success_proof_data: &T, diff --git a/programs/zk-token-proof/src/lib.rs b/programs/zk-token-proof/src/lib.rs index 72c843c6d0..87665cb235 100644 --- a/programs/zk-token-proof/src/lib.rs +++ b/programs/zk-token-proof/src/lib.rs @@ -244,5 +244,28 @@ declare_process_instruction!(process_instruction, 0, |invoke_context| { CiphertextCommitmentEqualityProofContext, >(invoke_context) } + ProofInstruction::VerifyGroupedCiphertext2HandlesValidity => { + invoke_context + .consume_checked(6_440) + .map_err(|_| InstructionError::ComputationalBudgetExceeded)?; + ic_msg!(invoke_context, "VerifyGroupedCiphertext2HandlesValidity"); + process_verify_proof::< + GroupedCiphertext2HandlesValidityProofData, + GroupedCiphertext2HandlesValidityProofContext, + >(invoke_context) + } + ProofInstruction::VerifyBatchedGroupedCiphertext2HandlesValidity => { + invoke_context + .consume_checked(12_575) + .map_err(|_| InstructionError::ComputationalBudgetExceeded)?; + ic_msg!( + invoke_context, + "VerifyBatchedGroupedCiphertext2HandlesValidity" + ); + process_verify_proof::< + BatchedGroupedCiphertext2HandlesValidityProofData, + BatchedGroupedCiphertext2HandlesValidityProofContext, + >(invoke_context) + } } }); diff --git a/zk-token-sdk/src/instruction/batched_grouped_ciphertext_validity.rs b/zk-token-sdk/src/instruction/batched_grouped_ciphertext_validity.rs new file mode 100644 index 0000000000..899707adce --- /dev/null +++ b/zk-token-sdk/src/instruction/batched_grouped_ciphertext_validity.rs @@ -0,0 +1,206 @@ +//! The batched grouped-ciphertext validity proof instruction. +//! +//! A batched grouped-ciphertext validity proof certifies the validity of two grouped ElGamal +//! ciphertext that are encrypted using the same set of ElGamal public keys. A batched +//! grouped-ciphertext validity proof is shorter and more efficient than two individual +//! grouped-ciphertext validity proofs. +//! +//! Currently, the batched grouped-ciphertext validity proof is restricted to ciphertexts with two +//! handles. In accordance with the SPL Token program application, the first decryption handle +//! associated with the proof is referred to as the "destination" handle and the second decryption +//! handle is referred to as the "auditor" handle. Furthermore, the first grouped ciphertext is +//! referred to as the "lo" ciphertext and the second grouped ciphertext is referred to as the "hi" +//! ciphertext. + +#[cfg(not(target_os = "solana"))] +use { + crate::{ + encryption::{ + elgamal::ElGamalPubkey, grouped_elgamal::GroupedElGamalCiphertext, + pedersen::PedersenOpening, + }, + errors::ProofError, + sigma_proofs::validity_proof::AggregatedValidityProof, + transcript::TranscriptProtocol, + }, + merlin::Transcript, +}; +use { + crate::{ + instruction::{ProofType, ZkProofData}, + zk_token_elgamal::pod, + }, + bytemuck::{Pod, Zeroable}, +}; + +/// The instruction data that is needed for the +/// `ProofInstruction::VerifyBatchedGroupedCiphertextValidity` instruction. +/// +/// It includes the cryptographic proof as well as the context data information needed to verify +/// the proof. +#[derive(Clone, Copy, Pod, Zeroable)] +#[repr(C)] +pub struct BatchedGroupedCiphertext2HandlesValidityProofData { + pub context: BatchedGroupedCiphertext2HandlesValidityProofContext, + + pub proof: pod::AggregatedValidityProof, +} + +#[derive(Clone, Copy, Pod, Zeroable)] +#[repr(C)] +pub struct BatchedGroupedCiphertext2HandlesValidityProofContext { + pub destination_pubkey: pod::ElGamalPubkey, // 32 bytes + + pub auditor_pubkey: pod::ElGamalPubkey, // 32 bytes + + pub grouped_ciphertext_lo: pod::GroupedElGamalCiphertext2Handles, // 96 bytes + + pub grouped_ciphertext_hi: pod::GroupedElGamalCiphertext2Handles, // 96 bytes +} + +#[cfg(not(target_os = "solana"))] +impl BatchedGroupedCiphertext2HandlesValidityProofData { + pub fn new( + destination_pubkey: &ElGamalPubkey, + auditor_pubkey: &ElGamalPubkey, + grouped_ciphertext_lo: &GroupedElGamalCiphertext<2>, + grouped_ciphertext_hi: &GroupedElGamalCiphertext<2>, + amount_lo: u64, + amount_hi: u64, + opening_lo: &PedersenOpening, + opening_hi: &PedersenOpening, + ) -> Result { + let pod_destination_pubkey = pod::ElGamalPubkey(destination_pubkey.to_bytes()); + let pod_auditor_pubkey = pod::ElGamalPubkey(auditor_pubkey.to_bytes()); + let pod_grouped_ciphertext_lo = (*grouped_ciphertext_lo).into(); + let pod_grouped_ciphertext_hi = (*grouped_ciphertext_hi).into(); + + let context = BatchedGroupedCiphertext2HandlesValidityProofContext { + destination_pubkey: pod_destination_pubkey, + auditor_pubkey: pod_auditor_pubkey, + grouped_ciphertext_lo: pod_grouped_ciphertext_lo, + grouped_ciphertext_hi: pod_grouped_ciphertext_hi, + }; + + let mut transcript = context.new_transcript(); + + let proof = AggregatedValidityProof::new( + (destination_pubkey, auditor_pubkey), + (amount_lo, amount_hi), + (opening_lo, opening_hi), + &mut transcript, + ) + .into(); + + Ok(Self { context, proof }) + } +} + +impl ZkProofData + for BatchedGroupedCiphertext2HandlesValidityProofData +{ + const PROOF_TYPE: ProofType = ProofType::BatchedGroupedCiphertext2HandlesValidity; + + fn context_data(&self) -> &BatchedGroupedCiphertext2HandlesValidityProofContext { + &self.context + } + + #[cfg(not(target_os = "solana"))] + fn verify_proof(&self) -> Result<(), ProofError> { + let mut transcript = self.context.new_transcript(); + + let destination_pubkey = self.context.destination_pubkey.try_into()?; + let auditor_pubkey = self.context.auditor_pubkey.try_into()?; + let grouped_ciphertext_lo: GroupedElGamalCiphertext<2> = + self.context.grouped_ciphertext_lo.try_into()?; + let grouped_ciphertext_hi: GroupedElGamalCiphertext<2> = + self.context.grouped_ciphertext_hi.try_into()?; + + let destination_handle_lo = grouped_ciphertext_lo.handles.get(0).unwrap(); + let auditor_handle_lo = grouped_ciphertext_lo.handles.get(1).unwrap(); + + let destination_handle_hi = grouped_ciphertext_hi.handles.get(0).unwrap(); + let auditor_handle_hi = grouped_ciphertext_hi.handles.get(1).unwrap(); + + let proof: AggregatedValidityProof = self.proof.try_into()?; + + proof + .verify( + (&destination_pubkey, &auditor_pubkey), + ( + &grouped_ciphertext_lo.commitment, + &grouped_ciphertext_hi.commitment, + ), + (destination_handle_lo, destination_handle_hi), + (auditor_handle_lo, auditor_handle_hi), + &mut transcript, + ) + .map_err(|e| e.into()) + } +} + +#[cfg(not(target_os = "solana"))] +impl BatchedGroupedCiphertext2HandlesValidityProofContext { + fn new_transcript(&self) -> Transcript { + let mut transcript = Transcript::new(b"BatchedGroupedCiphertextValidityProof"); + + transcript.append_pubkey(b"destination-pubkey", &self.destination_pubkey); + transcript.append_pubkey(b"auditor-pubkey", &self.auditor_pubkey); + transcript.append_grouped_ciphertext_2_handles( + b"grouped-ciphertext-lo", + &self.grouped_ciphertext_lo, + ); + transcript.append_grouped_ciphertext_2_handles( + b"grouped-ciphertext-hi", + &self.grouped_ciphertext_hi, + ); + + transcript + } +} + +#[cfg(test)] +mod test { + use { + super::*, + crate::encryption::{elgamal::ElGamalKeypair, grouped_elgamal::GroupedElGamal}, + }; + + #[test] + fn test_ciphertext_validity_proof_instruction_correctness() { + let destination_pubkey = ElGamalKeypair::new_rand().public; + let auditor_pubkey = ElGamalKeypair::new_rand().public; + + let amount_lo: u64 = 11; + let amount_hi: u64 = 22; + + let opening_lo = PedersenOpening::new_rand(); + let opening_hi = PedersenOpening::new_rand(); + + let grouped_ciphertext_lo = GroupedElGamal::encrypt_with( + [&destination_pubkey, &auditor_pubkey], + amount_lo, + &opening_lo, + ); + + let grouped_ciphertext_hi = GroupedElGamal::encrypt_with( + [&destination_pubkey, &auditor_pubkey], + amount_hi, + &opening_hi, + ); + + let proof_data = BatchedGroupedCiphertext2HandlesValidityProofData::new( + &destination_pubkey, + &auditor_pubkey, + &grouped_ciphertext_lo, + &grouped_ciphertext_hi, + amount_lo, + amount_hi, + &opening_lo, + &opening_hi, + ) + .unwrap(); + + assert!(proof_data.verify_proof().is_ok()); + } +} diff --git a/zk-token-sdk/src/instruction/grouped_ciphertext_validity.rs b/zk-token-sdk/src/instruction/grouped_ciphertext_validity.rs new file mode 100644 index 0000000000..4b74e2cc59 --- /dev/null +++ b/zk-token-sdk/src/instruction/grouped_ciphertext_validity.rs @@ -0,0 +1,166 @@ +//! The grouped-ciphertext validity proof instruction. +//! +//! A grouped-ciphertext validity proof certifies that a grouped ElGamal ciphertext is +//! well-defined, i.e. the ciphertext can be decrypted by private keys associated with its +//! decryption handles. To generate the proof, a prover must provide the Pedersen opening +//! associated with the grouped ciphertext's commitment. +//! +//! Currently, the grouped-ciphertext validity proof is restricted to ciphertexts with two handles. +//! In accordance with the SPL Token program application, the first decryption handle associated +//! with the proof is referred to as the "destination" handle and the second decryption handle is +//! referred to as the "auditor" handle. + +#[cfg(not(target_os = "solana"))] +use { + crate::{ + encryption::{ + elgamal::ElGamalPubkey, grouped_elgamal::GroupedElGamalCiphertext, + pedersen::PedersenOpening, + }, + errors::ProofError, + sigma_proofs::validity_proof::ValidityProof, + transcript::TranscriptProtocol, + }, + merlin::Transcript, +}; +use { + crate::{ + instruction::{ProofType, ZkProofData}, + zk_token_elgamal::pod, + }, + bytemuck::{Pod, Zeroable}, +}; + +/// The instruction data that is needed for the `ProofInstruction::VerifyGroupedCiphertextValidity` +/// instruction. +/// +/// It includes the cryptographic proof as well as the context data information needed to verify +/// the proof. +#[derive(Clone, Copy, Pod, Zeroable)] +#[repr(C)] +pub struct GroupedCiphertext2HandlesValidityProofData { + pub context: GroupedCiphertext2HandlesValidityProofContext, + + pub proof: pod::ValidityProof, +} + +#[derive(Clone, Copy, Pod, Zeroable)] +#[repr(C)] +pub struct GroupedCiphertext2HandlesValidityProofContext { + pub destination_pubkey: pod::ElGamalPubkey, // 32 bytes + + pub auditor_pubkey: pod::ElGamalPubkey, // 32 bytes + + pub grouped_ciphertext: pod::GroupedElGamalCiphertext2Handles, // 96 bytes +} + +#[cfg(not(target_os = "solana"))] +impl GroupedCiphertext2HandlesValidityProofData { + pub fn new( + destination_pubkey: &ElGamalPubkey, + auditor_pubkey: &ElGamalPubkey, + grouped_ciphertext: &GroupedElGamalCiphertext<2>, + amount: u64, + opening: &PedersenOpening, + ) -> Result { + let pod_destination_pubkey = pod::ElGamalPubkey(destination_pubkey.to_bytes()); + let pod_auditor_pubkey = pod::ElGamalPubkey(auditor_pubkey.to_bytes()); + let pod_grouped_ciphertext = (*grouped_ciphertext).into(); + + let context = GroupedCiphertext2HandlesValidityProofContext { + destination_pubkey: pod_destination_pubkey, + auditor_pubkey: pod_auditor_pubkey, + grouped_ciphertext: pod_grouped_ciphertext, + }; + + let mut transcript = context.new_transcript(); + + let proof = ValidityProof::new( + (destination_pubkey, auditor_pubkey), + amount, + opening, + &mut transcript, + ) + .into(); + + Ok(Self { context, proof }) + } +} + +impl ZkProofData + for GroupedCiphertext2HandlesValidityProofData +{ + const PROOF_TYPE: ProofType = ProofType::GroupedCiphertext2HandlesValidity; + + fn context_data(&self) -> &GroupedCiphertext2HandlesValidityProofContext { + &self.context + } + + #[cfg(not(target_os = "solana"))] + fn verify_proof(&self) -> Result<(), ProofError> { + let mut transcript = self.context.new_transcript(); + + let destination_pubkey = self.context.destination_pubkey.try_into()?; + let auditor_pubkey = self.context.auditor_pubkey.try_into()?; + let grouped_ciphertext: GroupedElGamalCiphertext<2> = + self.context.grouped_ciphertext.try_into()?; + + let destination_handle = grouped_ciphertext.handles.get(0).unwrap(); + let auditor_handle = grouped_ciphertext.handles.get(1).unwrap(); + + let proof: ValidityProof = self.proof.try_into()?; + + proof + .verify( + &grouped_ciphertext.commitment, + (&destination_pubkey, &auditor_pubkey), + (destination_handle, auditor_handle), + &mut transcript, + ) + .map_err(|e| e.into()) + } +} + +#[cfg(not(target_os = "solana"))] +impl GroupedCiphertext2HandlesValidityProofContext { + fn new_transcript(&self) -> Transcript { + let mut transcript = Transcript::new(b"CiphertextValidityProof"); + + transcript.append_pubkey(b"destination-pubkey", &self.destination_pubkey); + transcript.append_pubkey(b"auditor-pubkey", &self.auditor_pubkey); + transcript + .append_grouped_ciphertext_2_handles(b"grouped-ciphertext", &self.grouped_ciphertext); + + transcript + } +} + +#[cfg(test)] +mod test { + use { + super::*, + crate::encryption::{elgamal::ElGamalKeypair, grouped_elgamal::GroupedElGamal}, + }; + + #[test] + fn test_ciphertext_validity_proof_instruction_correctness() { + let destination_pubkey = ElGamalKeypair::new_rand().public; + let auditor_pubkey = ElGamalKeypair::new_rand().public; + + let amount: u64 = 55; + let opening = PedersenOpening::new_rand(); + let grouped_ciphertext = + GroupedElGamal::encrypt_with([&destination_pubkey, &auditor_pubkey], amount, &opening); + + let proof_data = GroupedCiphertext2HandlesValidityProofData::new( + &destination_pubkey, + &auditor_pubkey, + &grouped_ciphertext, + amount, + &opening, + ) + .unwrap(); + + assert!(proof_data.verify_proof().is_ok()); + } +} diff --git a/zk-token-sdk/src/instruction/mod.rs b/zk-token-sdk/src/instruction/mod.rs index 29ceb1d647..088b196fbc 100644 --- a/zk-token-sdk/src/instruction/mod.rs +++ b/zk-token-sdk/src/instruction/mod.rs @@ -1,6 +1,8 @@ +pub mod batched_grouped_ciphertext_validity; pub mod batched_range_proof; pub mod ciphertext_ciphertext_equality; pub mod ciphertext_commitment_equality; +pub mod grouped_ciphertext_validity; pub mod pubkey_validity; pub mod range_proof; pub mod transfer; @@ -11,6 +13,10 @@ pub mod zero_balance; use crate::errors::ProofError; use num_derive::{FromPrimitive, ToPrimitive}; pub use { + batched_grouped_ciphertext_validity::{ + BatchedGroupedCiphertext2HandlesValidityProofContext, + BatchedGroupedCiphertext2HandlesValidityProofData, + }, batched_range_proof::{ batched_range_proof_u128::BatchedRangeProofU128Data, batched_range_proof_u256::BatchedRangeProofU256Data, @@ -23,6 +29,9 @@ pub use { ciphertext_commitment_equality::{ CiphertextCommitmentEqualityProofContext, CiphertextCommitmentEqualityProofData, }, + grouped_ciphertext_validity::{ + GroupedCiphertext2HandlesValidityProofContext, GroupedCiphertext2HandlesValidityProofData, + }, pubkey_validity::{PubkeyValidityData, PubkeyValidityProofContext}, range_proof::{RangeProofContext, RangeProofU64Data}, transfer::{ @@ -49,6 +58,8 @@ pub enum ProofType { BatchedRangeProofU128, BatchedRangeProofU256, CiphertextCommitmentEquality, + GroupedCiphertext2HandlesValidity, + BatchedGroupedCiphertext2HandlesValidity, } pub trait ZkProofData { diff --git a/zk-token-sdk/src/transcript.rs b/zk-token-sdk/src/transcript.rs index c0ed116166..8a35753c33 100644 --- a/zk-token-sdk/src/transcript.rs +++ b/zk-token-sdk/src/transcript.rs @@ -37,6 +37,13 @@ pub trait TranscriptProtocol { /// Append an ElGamal ciphertext with the given `label`. fn append_ciphertext(&mut self, label: &'static [u8], point: &pod::ElGamalCiphertext); + /// Append a grouped ElGamal ciphertext with the given `label`. + fn append_grouped_ciphertext_2_handles( + &mut self, + label: &'static [u8], + point: &pod::GroupedElGamalCiphertext2Handles, + ); + /// Append a Pedersen commitment with the given `label`. fn append_commitment(&mut self, label: &'static [u8], point: &pod::PedersenCommitment); @@ -137,6 +144,14 @@ impl TranscriptProtocol for Transcript { self.append_message(label, &ciphertext.0); } + fn append_grouped_ciphertext_2_handles( + &mut self, + label: &'static [u8], + grouped_ciphertext: &pod::GroupedElGamalCiphertext2Handles, + ) { + self.append_message(label, &grouped_ciphertext.0); + } + fn append_commitment(&mut self, label: &'static [u8], commitment: &pod::PedersenCommitment) { self.append_message(label, &commitment.0); } diff --git a/zk-token-sdk/src/zk_token_proof_instruction.rs b/zk-token-sdk/src/zk_token_proof_instruction.rs index 69f92546bf..7c97d0a554 100644 --- a/zk-token-sdk/src/zk_token_proof_instruction.rs +++ b/zk-token-sdk/src/zk_token_proof_instruction.rs @@ -276,6 +276,57 @@ pub enum ProofInstruction { /// `CiphertextCommitmentEqualityProofData` /// VerifyCiphertextCommitmentEquality, + + /// Verify a grouped-ciphertext validity proof. + /// + /// A grouped-ciphertext validity proof certifies that a grouped ElGamal ciphertext is + /// well-defined, i.e. the ciphertext can be decrypted by private keys associated with its + /// decryption handles. + /// + /// This instruction can be configured to optionally initialize a proof context state account. + /// If creating a context state account, an account must be pre-allocated to the exact size of + /// `ProofContextState` and assigned to the ZkToken + /// proof program prior to the execution of this instruction. + /// + /// Accounts expected by this instruction: + /// + /// * Creating a proof context account + /// 0. `[writable]` The proof context account + /// 1. `[]` The proof context account owner + /// + /// * Otherwise + /// None + /// + /// Data expected by this instruction: + /// `GroupedCiphertextValidityProofContext` + /// + VerifyGroupedCiphertext2HandlesValidity, + + /// Verify a batched grouped-ciphertext validity proof. + /// + /// A batched grouped-ciphertext validity proof certifies the validity of two grouped ElGamal + /// ciphertext that are encrypted using the same set of ElGamal public keys. A batched + /// grouped-ciphertext validity proof is shorter and more efficient than two individual + /// grouped-ciphertext validity proofs. + /// + /// This instruction can be configured to optionally initialize a proof context state account. + /// If creating a context state account, an account must be pre-allocated to the exact size of + /// `ProofContextState` and assigned to the + /// ZkToken proof program prior to the execution of this instruction. + /// + /// Accounts expected by this instruction: + /// + /// * Creating a proof context account + /// 0. `[writable]` The proof context account + /// 1. `[]` The proof context account owner + /// + /// * Otherwise + /// None + /// + /// Data expected by this instruction: + /// `BatchedGroupedCiphertextValidityProofContext` + /// + VerifyBatchedGroupedCiphertext2HandlesValidity, } /// Pubkeys associated with a context state account to be used as parameters to functions.