[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 <teulberg@gmail.com> --------- Co-authored-by: Tyera <teulberg@gmail.com>
This commit is contained in:
parent
405db3e436
commit
0495051a67
|
@ -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::<ProofContextState<GroupedCiphertext2HandlesValidityProofContext>>(),
|
||||
&success_proof_data,
|
||||
&fail_proof_data,
|
||||
)
|
||||
.await;
|
||||
|
||||
test_close_context_state(
|
||||
ProofInstruction::VerifyGroupedCiphertext2HandlesValidity,
|
||||
size_of::<ProofContextState<GroupedCiphertext2HandlesValidityProofContext>>(),
|
||||
&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::<ProofContextState<BatchedGroupedCiphertext2HandlesValidityProofContext>>(),
|
||||
&success_proof_data,
|
||||
&fail_proof_data,
|
||||
)
|
||||
.await;
|
||||
|
||||
test_close_context_state(
|
||||
ProofInstruction::VerifyBatchedGroupedCiphertext2HandlesValidity,
|
||||
size_of::<ProofContextState<BatchedGroupedCiphertext2HandlesValidityProofContext>>(),
|
||||
&success_proof_data,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
async fn test_verify_proof_without_context<T, U>(
|
||||
proof_instruction: ProofInstruction,
|
||||
success_proof_data: &T,
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -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<Self, ProofError> {
|
||||
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<BatchedGroupedCiphertext2HandlesValidityProofContext>
|
||||
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());
|
||||
}
|
||||
}
|
|
@ -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<Self, ProofError> {
|
||||
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<GroupedCiphertext2HandlesValidityProofContext>
|
||||
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());
|
||||
}
|
||||
}
|
|
@ -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<T: Pod> {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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<GroupedCiphertextValidityProofContext>` 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<BatchedGroupedCiphertextValidityProofContext>` 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.
|
||||
|
|
Loading…
Reference in New Issue