#[cfg(not(target_os = "solana"))]
use {
crate::{
encryption::{
elgamal::{ElGamalCiphertext, ElGamalKeypair, ElGamalPubkey},
pedersen::PedersenOpening,
},
errors::ProofError,
sigma_proofs::ctxt_ctxt_equality_proof::CiphertextCiphertextEqualityProof,
transcript::TranscriptProtocol,
},
merlin::Transcript,
std::convert::TryInto,
};
use {
crate::{
instruction::{ProofType, ZkProofData},
zk_token_elgamal::pod,
},
bytemuck::{Pod, Zeroable},
};
#[derive(Clone, Copy, Pod, Zeroable)]
#[repr(C)]
pub struct CiphertextCiphertextEqualityProofData {
pub context: CiphertextCiphertextEqualityProofContext,
pub proof: pod::CiphertextCiphertextEqualityProof,
}
#[derive(Clone, Copy, Pod, Zeroable)]
#[repr(C)]
pub struct CiphertextCiphertextEqualityProofContext {
pub source_pubkey: pod::ElGamalPubkey, pub destination_pubkey: pod::ElGamalPubkey, pub source_ciphertext: pod::ElGamalCiphertext, pub destination_ciphertext: pod::ElGamalCiphertext, }
#[cfg(not(target_os = "solana"))]
impl CiphertextCiphertextEqualityProofData {
pub fn new(
source_keypair: &ElGamalKeypair,
destination_pubkey: &ElGamalPubkey,
source_ciphertext: &ElGamalCiphertext,
destination_ciphertext: &ElGamalCiphertext,
destination_opening: &PedersenOpening,
amount: u64,
) -> Result<Self, ProofError> {
let pod_source_pubkey = pod::ElGamalPubkey(source_keypair.public.to_bytes());
let pod_destination_pubkey = pod::ElGamalPubkey(destination_pubkey.to_bytes());
let pod_source_ciphertext = pod::ElGamalCiphertext(source_ciphertext.to_bytes());
let pod_destination_ciphertext = pod::ElGamalCiphertext(destination_ciphertext.to_bytes());
let context = CiphertextCiphertextEqualityProofContext {
source_pubkey: pod_source_pubkey,
destination_pubkey: pod_destination_pubkey,
source_ciphertext: pod_source_ciphertext,
destination_ciphertext: pod_destination_ciphertext,
};
let mut transcript = CiphertextCiphertextEqualityProof::transcript_new(
&pod_source_pubkey,
&pod_destination_pubkey,
&pod_source_ciphertext,
&pod_destination_ciphertext,
);
let proof = CiphertextCiphertextEqualityProof::new(
source_keypair,
destination_pubkey,
source_ciphertext,
destination_opening,
amount,
&mut transcript,
)
.into();
Ok(Self { context, proof })
}
}
impl ZkProofData<CiphertextCiphertextEqualityProofContext>
for CiphertextCiphertextEqualityProofData
{
const PROOF_TYPE: ProofType = ProofType::CiphertextCiphertextEquality;
fn context_data(&self) -> &CiphertextCiphertextEqualityProofContext {
&self.context
}
#[cfg(not(target_os = "solana"))]
fn verify_proof(&self) -> Result<(), ProofError> {
let mut transcript = CiphertextCiphertextEqualityProof::transcript_new(
&self.context.source_pubkey,
&self.context.destination_pubkey,
&self.context.source_ciphertext,
&self.context.destination_ciphertext,
);
let source_pubkey = self.context.source_pubkey.try_into()?;
let destination_pubkey = self.context.destination_pubkey.try_into()?;
let source_ciphertext = self.context.source_ciphertext.try_into()?;
let destination_ciphertext = self.context.destination_ciphertext.try_into()?;
let proof: CiphertextCiphertextEqualityProof = self.proof.try_into()?;
proof
.verify(
&source_pubkey,
&destination_pubkey,
&source_ciphertext,
&destination_ciphertext,
&mut transcript,
)
.map_err(|e| e.into())
}
}
#[allow(non_snake_case)]
#[cfg(not(target_os = "solana"))]
impl CiphertextCiphertextEqualityProof {
fn transcript_new(
source_pubkey: &pod::ElGamalPubkey,
destination_pubkey: &pod::ElGamalPubkey,
source_ciphertext: &pod::ElGamalCiphertext,
destination_ciphertext: &pod::ElGamalCiphertext,
) -> Transcript {
let mut transcript = Transcript::new(b"CiphertextCiphertextEqualityProof");
transcript.append_pubkey(b"pubkey-source", source_pubkey);
transcript.append_pubkey(b"pubkey-dest", destination_pubkey);
transcript.append_ciphertext(b"ciphertext-source", source_ciphertext);
transcript.append_ciphertext(b"ciphertext-dest", destination_ciphertext);
transcript
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_ciphertext_ciphertext_instruction_correctness() {
let source_keypair = ElGamalKeypair::new_rand();
let destination_keypair = ElGamalKeypair::new_rand();
let amount: u64 = 0;
let source_ciphertext = source_keypair.public.encrypt(amount);
let destination_opening = PedersenOpening::new_rand();
let destination_ciphertext = destination_keypair
.public
.encrypt_with(amount, &destination_opening);
let proof_data = CiphertextCiphertextEqualityProofData::new(
&source_keypair,
&destination_keypair.public,
&source_ciphertext,
&destination_ciphertext,
&destination_opening,
amount,
)
.unwrap();
assert!(proof_data.verify_proof().is_ok());
let amount: u64 = 55;
let source_ciphertext = source_keypair.public.encrypt(amount);
let destination_opening = PedersenOpening::new_rand();
let destination_ciphertext = destination_keypair
.public
.encrypt_with(amount, &destination_opening);
let proof_data = CiphertextCiphertextEqualityProofData::new(
&source_keypair,
&destination_keypair.public,
&source_ciphertext,
&destination_ciphertext,
&destination_opening,
amount,
)
.unwrap();
assert!(proof_data.verify_proof().is_ok());
let amount = u64::max_value();
let source_ciphertext = source_keypair.public.encrypt(amount);
let destination_opening = PedersenOpening::new_rand();
let destination_ciphertext = destination_keypair
.public
.encrypt_with(amount, &destination_opening);
let proof_data = CiphertextCiphertextEqualityProofData::new(
&source_keypair,
&destination_keypair.public,
&source_ciphertext,
&destination_ciphertext,
&destination_opening,
amount,
)
.unwrap();
assert!(proof_data.verify_proof().is_ok());
}
}