solana_zk_token_sdk/instruction/
ciphertext_ciphertext_equality.rs1#[cfg(not(target_os = "solana"))]
12use {
13 crate::{
14 encryption::{
15 elgamal::{ElGamalCiphertext, ElGamalKeypair, ElGamalPubkey},
16 pedersen::PedersenOpening,
17 },
18 errors::{ProofGenerationError, ProofVerificationError},
19 sigma_proofs::ciphertext_ciphertext_equality_proof::CiphertextCiphertextEqualityProof,
20 transcript::TranscriptProtocol,
21 },
22 merlin::Transcript,
23 std::convert::TryInto,
24};
25use {
26 crate::{
27 instruction::{ProofType, ZkProofData},
28 zk_token_elgamal::pod,
29 },
30 bytemuck_derive::{Pod, Zeroable},
31};
32
33#[derive(Clone, Copy, Pod, Zeroable)]
39#[repr(C)]
40pub struct CiphertextCiphertextEqualityProofData {
41 pub context: CiphertextCiphertextEqualityProofContext,
42
43 pub proof: pod::CiphertextCiphertextEqualityProof,
44}
45
46#[derive(Clone, Copy, Pod, Zeroable)]
48#[repr(C)]
49pub struct CiphertextCiphertextEqualityProofContext {
50 pub source_pubkey: pod::ElGamalPubkey, pub destination_pubkey: pod::ElGamalPubkey, pub source_ciphertext: pod::ElGamalCiphertext, pub destination_ciphertext: pod::ElGamalCiphertext, }
58
59#[cfg(not(target_os = "solana"))]
60impl CiphertextCiphertextEqualityProofData {
61 pub fn new(
62 source_keypair: &ElGamalKeypair,
63 destination_pubkey: &ElGamalPubkey,
64 source_ciphertext: &ElGamalCiphertext,
65 destination_ciphertext: &ElGamalCiphertext,
66 destination_opening: &PedersenOpening,
67 amount: u64,
68 ) -> Result<Self, ProofGenerationError> {
69 let pod_source_pubkey = pod::ElGamalPubkey(source_keypair.pubkey().into());
70 let pod_destination_pubkey = pod::ElGamalPubkey(destination_pubkey.into());
71 let pod_source_ciphertext = pod::ElGamalCiphertext(source_ciphertext.to_bytes());
72 let pod_destination_ciphertext = pod::ElGamalCiphertext(destination_ciphertext.to_bytes());
73
74 let context = CiphertextCiphertextEqualityProofContext {
75 source_pubkey: pod_source_pubkey,
76 destination_pubkey: pod_destination_pubkey,
77 source_ciphertext: pod_source_ciphertext,
78 destination_ciphertext: pod_destination_ciphertext,
79 };
80
81 let mut transcript = context.new_transcript();
82
83 let proof = CiphertextCiphertextEqualityProof::new(
84 source_keypair,
85 destination_pubkey,
86 source_ciphertext,
87 destination_opening,
88 amount,
89 &mut transcript,
90 )
91 .into();
92
93 Ok(Self { context, proof })
94 }
95}
96
97impl ZkProofData<CiphertextCiphertextEqualityProofContext>
98 for CiphertextCiphertextEqualityProofData
99{
100 const PROOF_TYPE: ProofType = ProofType::CiphertextCiphertextEquality;
101
102 fn context_data(&self) -> &CiphertextCiphertextEqualityProofContext {
103 &self.context
104 }
105
106 #[cfg(not(target_os = "solana"))]
107 fn verify_proof(&self) -> Result<(), ProofVerificationError> {
108 let mut transcript = self.context.new_transcript();
109
110 let source_pubkey = self.context.source_pubkey.try_into()?;
111 let destination_pubkey = self.context.destination_pubkey.try_into()?;
112 let source_ciphertext = self.context.source_ciphertext.try_into()?;
113 let destination_ciphertext = self.context.destination_ciphertext.try_into()?;
114 let proof: CiphertextCiphertextEqualityProof = self.proof.try_into()?;
115
116 proof
117 .verify(
118 &source_pubkey,
119 &destination_pubkey,
120 &source_ciphertext,
121 &destination_ciphertext,
122 &mut transcript,
123 )
124 .map_err(|e| e.into())
125 }
126}
127
128#[allow(non_snake_case)]
129#[cfg(not(target_os = "solana"))]
130impl CiphertextCiphertextEqualityProofContext {
131 fn new_transcript(&self) -> Transcript {
132 let mut transcript = Transcript::new(b"CiphertextCiphertextEqualityProof");
133
134 transcript.append_pubkey(b"source-pubkey", &self.source_pubkey);
135 transcript.append_pubkey(b"destination-pubkey", &self.destination_pubkey);
136
137 transcript.append_ciphertext(b"source-ciphertext", &self.source_ciphertext);
138 transcript.append_ciphertext(b"destination-ciphertext", &self.destination_ciphertext);
139
140 transcript
141 }
142}
143
144#[cfg(test)]
145mod test {
146 use super::*;
147
148 #[test]
149 fn test_ciphertext_ciphertext_instruction_correctness() {
150 let source_keypair = ElGamalKeypair::new_rand();
151 let destination_keypair = ElGamalKeypair::new_rand();
152
153 let amount: u64 = 0;
154 let source_ciphertext = source_keypair.pubkey().encrypt(amount);
155
156 let destination_opening = PedersenOpening::new_rand();
157 let destination_ciphertext = destination_keypair
158 .pubkey()
159 .encrypt_with(amount, &destination_opening);
160
161 let proof_data = CiphertextCiphertextEqualityProofData::new(
162 &source_keypair,
163 destination_keypair.pubkey(),
164 &source_ciphertext,
165 &destination_ciphertext,
166 &destination_opening,
167 amount,
168 )
169 .unwrap();
170
171 assert!(proof_data.verify_proof().is_ok());
172
173 let amount: u64 = 55;
174 let source_ciphertext = source_keypair.pubkey().encrypt(amount);
175
176 let destination_opening = PedersenOpening::new_rand();
177 let destination_ciphertext = destination_keypair
178 .pubkey()
179 .encrypt_with(amount, &destination_opening);
180
181 let proof_data = CiphertextCiphertextEqualityProofData::new(
182 &source_keypair,
183 destination_keypair.pubkey(),
184 &source_ciphertext,
185 &destination_ciphertext,
186 &destination_opening,
187 amount,
188 )
189 .unwrap();
190
191 assert!(proof_data.verify_proof().is_ok());
192
193 let amount = u64::MAX;
194 let source_ciphertext = source_keypair.pubkey().encrypt(amount);
195
196 let destination_opening = PedersenOpening::new_rand();
197 let destination_ciphertext = destination_keypair
198 .pubkey()
199 .encrypt_with(amount, &destination_opening);
200
201 let proof_data = CiphertextCiphertextEqualityProofData::new(
202 &source_keypair,
203 destination_keypair.pubkey(),
204 &source_ciphertext,
205 &destination_ciphertext,
206 &destination_opening,
207 amount,
208 )
209 .unwrap();
210
211 assert!(proof_data.verify_proof().is_ok());
212 }
213}