solana_zk_elgamal_proof_program/
lib.rs1#![forbid(unsafe_code)]
2
3use {
4 bytemuck::Pod,
5 solana_log_collector::ic_msg,
6 solana_program_runtime::{declare_process_instruction, invoke_context::InvokeContext},
7 solana_sdk::{instruction::InstructionError, system_program},
8 solana_zk_sdk::zk_elgamal_proof_program::{
9 id,
10 instruction::ProofInstruction,
11 proof_data::*,
12 state::{ProofContextState, ProofContextStateMeta},
13 },
14 std::result::Result,
15};
16
17pub const CLOSE_CONTEXT_STATE_COMPUTE_UNITS: u64 = 3_300;
18pub const VERIFY_ZERO_CIPHERTEXT_COMPUTE_UNITS: u64 = 6_000;
19pub const VERIFY_CIPHERTEXT_CIPHERTEXT_EQUALITY_COMPUTE_UNITS: u64 = 8_000;
20pub const VERIFY_CIPHERTEXT_COMMITMENT_EQUALITY_COMPUTE_UNITS: u64 = 6_400;
21pub const VERIFY_PUBKEY_VALIDITY_COMPUTE_UNITS: u64 = 2_600;
22pub const VERIFY_PERCENTAGE_WITH_CAP_COMPUTE_UNITS: u64 = 6_500;
23pub const VERIFY_BATCHED_RANGE_PROOF_U64_COMPUTE_UNITS: u64 = 111_000;
24pub const VERIFY_BATCHED_RANGE_PROOF_U128_COMPUTE_UNITS: u64 = 200_000;
25pub const VERIFY_BATCHED_RANGE_PROOF_U256_COMPUTE_UNITS: u64 = 368_000;
26pub const VERIFY_GROUPED_CIPHERTEXT_2_HANDLES_VALIDITY_COMPUTE_UNITS: u64 = 6_400;
27pub const VERIFY_BATCHED_GROUPED_CIPHERTEXT_2_HANDLES_VALIDITY_COMPUTE_UNITS: u64 = 13_000;
28pub const VERIFY_GROUPED_CIPHERTEXT_3_HANDLES_VALIDITY_COMPUTE_UNITS: u64 = 8_100;
29pub const VERIFY_BATCHED_GROUPED_CIPHERTEXT_3_HANDLES_VALIDITY_COMPUTE_UNITS: u64 = 16_400;
30
31const INSTRUCTION_DATA_LENGTH_WITH_PROOF_ACCOUNT: usize = 5;
32
33fn process_verify_proof<T, U>(invoke_context: &mut InvokeContext) -> Result<(), InstructionError>
34where
35 T: Pod + ZkProofData<U>,
36 U: Pod,
37{
38 let transaction_context = &invoke_context.transaction_context;
39 let instruction_context = transaction_context.get_current_instruction_context()?;
40 let instruction_data = instruction_context.get_instruction_data();
41
42 let mut accessed_accounts = 0_u16;
44
45 let context_data = if instruction_data.len() == INSTRUCTION_DATA_LENGTH_WITH_PROOF_ACCOUNT {
47 let proof_data_account = instruction_context
48 .try_borrow_instruction_account(transaction_context, accessed_accounts)?;
49 accessed_accounts = accessed_accounts.checked_add(1).unwrap();
50
51 let proof_data_offset = u32::from_le_bytes(
52 instruction_data[1..INSTRUCTION_DATA_LENGTH_WITH_PROOF_ACCOUNT]
54 .try_into()
55 .map_err(|_| InstructionError::InvalidInstructionData)?,
56 );
57 let proof_data_start: usize = proof_data_offset
58 .try_into()
59 .map_err(|_| InstructionError::InvalidInstructionData)?;
60 let proof_data_end = proof_data_start
61 .checked_add(std::mem::size_of::<T>())
62 .ok_or(InstructionError::InvalidInstructionData)?;
63 let proof_data_raw = proof_data_account
64 .get_data()
65 .get(proof_data_start..proof_data_end)
66 .ok_or(InstructionError::InvalidAccountData)?;
67
68 let proof_data = bytemuck::try_from_bytes::<T>(proof_data_raw).map_err(|_| {
69 ic_msg!(invoke_context, "invalid proof data");
70 InstructionError::InvalidInstructionData
71 })?;
72 proof_data.verify_proof().map_err(|err| {
73 ic_msg!(invoke_context, "proof verification failed: {:?}", err);
74 InstructionError::InvalidInstructionData
75 })?;
76
77 *proof_data.context_data()
78 } else {
79 let proof_data =
80 ProofInstruction::proof_data::<T, U>(instruction_data).ok_or_else(|| {
81 ic_msg!(invoke_context, "invalid proof data");
82 InstructionError::InvalidInstructionData
83 })?;
84 proof_data.verify_proof().map_err(|err| {
85 ic_msg!(invoke_context, "proof_verification failed: {:?}", err);
86 InstructionError::InvalidInstructionData
87 })?;
88
89 *proof_data.context_data()
90 };
91
92 if instruction_context.get_number_of_instruction_accounts() > accessed_accounts {
94 let context_state_authority = *instruction_context
95 .try_borrow_instruction_account(
96 transaction_context,
97 accessed_accounts.checked_add(1).unwrap(),
98 )?
99 .get_key();
100
101 let mut proof_context_account = instruction_context
102 .try_borrow_instruction_account(transaction_context, accessed_accounts)?;
103
104 if *proof_context_account.get_owner() != id() {
105 return Err(InstructionError::InvalidAccountOwner);
106 }
107
108 let proof_context_state_meta =
109 ProofContextStateMeta::try_from_bytes(proof_context_account.get_data())?;
110
111 if proof_context_state_meta.proof_type != ProofType::Uninitialized.into() {
112 return Err(InstructionError::AccountAlreadyInitialized);
113 }
114
115 let context_state_data =
116 ProofContextState::encode(&context_state_authority, T::PROOF_TYPE, &context_data);
117
118 if proof_context_account.get_data().len() != context_state_data.len() {
119 return Err(InstructionError::InvalidAccountData);
120 }
121
122 proof_context_account.set_data_from_slice(&context_state_data)?;
123 }
124
125 Ok(())
126}
127
128fn process_close_proof_context(invoke_context: &mut InvokeContext) -> Result<(), InstructionError> {
129 let transaction_context = &invoke_context.transaction_context;
130 let instruction_context = transaction_context.get_current_instruction_context()?;
131
132 let owner_pubkey = {
133 let owner_account =
134 instruction_context.try_borrow_instruction_account(transaction_context, 2)?;
135
136 if !owner_account.is_signer() {
137 return Err(InstructionError::MissingRequiredSignature);
138 }
139 *owner_account.get_key()
140 }; let proof_context_account_pubkey = *instruction_context
143 .try_borrow_instruction_account(transaction_context, 0)?
144 .get_key();
145 let destination_account_pubkey = *instruction_context
146 .try_borrow_instruction_account(transaction_context, 1)?
147 .get_key();
148 if proof_context_account_pubkey == destination_account_pubkey {
149 return Err(InstructionError::InvalidInstructionData);
150 }
151
152 let mut proof_context_account =
153 instruction_context.try_borrow_instruction_account(transaction_context, 0)?;
154 let proof_context_state_meta =
155 ProofContextStateMeta::try_from_bytes(proof_context_account.get_data())?;
156 let expected_owner_pubkey = proof_context_state_meta.context_state_authority;
157
158 if owner_pubkey != expected_owner_pubkey {
159 return Err(InstructionError::InvalidAccountOwner);
160 }
161
162 let mut destination_account =
163 instruction_context.try_borrow_instruction_account(transaction_context, 1)?;
164 destination_account.checked_add_lamports(proof_context_account.get_lamports())?;
165 proof_context_account.set_lamports(0)?;
166 proof_context_account.set_data_length(0)?;
167 proof_context_account.set_owner(system_program::id().as_ref())?;
168
169 Ok(())
170}
171
172declare_process_instruction!(Entrypoint, 0, |invoke_context| {
173 let transaction_context = &invoke_context.transaction_context;
174 let instruction_context = transaction_context.get_current_instruction_context()?;
175 let instruction_data = instruction_context.get_instruction_data();
176 let instruction = ProofInstruction::instruction_type(instruction_data)
177 .ok_or(InstructionError::InvalidInstructionData)?;
178
179 match instruction {
180 ProofInstruction::CloseContextState => {
181 invoke_context
182 .consume_checked(CLOSE_CONTEXT_STATE_COMPUTE_UNITS)
183 .map_err(|_| InstructionError::ComputationalBudgetExceeded)?;
184 ic_msg!(invoke_context, "CloseContextState");
185 process_close_proof_context(invoke_context)
186 }
187 ProofInstruction::VerifyZeroCiphertext => {
188 invoke_context
189 .consume_checked(VERIFY_ZERO_CIPHERTEXT_COMPUTE_UNITS)
190 .map_err(|_| InstructionError::ComputationalBudgetExceeded)?;
191 ic_msg!(invoke_context, "VerifyZeroCiphertext");
192 process_verify_proof::<ZeroCiphertextProofData, ZeroCiphertextProofContext>(
193 invoke_context,
194 )
195 }
196 ProofInstruction::VerifyCiphertextCiphertextEquality => {
197 invoke_context
198 .consume_checked(VERIFY_CIPHERTEXT_CIPHERTEXT_EQUALITY_COMPUTE_UNITS)
199 .map_err(|_| InstructionError::ComputationalBudgetExceeded)?;
200 ic_msg!(invoke_context, "VerifyCiphertextCiphertextEquality");
201 process_verify_proof::<
202 CiphertextCiphertextEqualityProofData,
203 CiphertextCiphertextEqualityProofContext,
204 >(invoke_context)
205 }
206 ProofInstruction::VerifyCiphertextCommitmentEquality => {
207 invoke_context
208 .consume_checked(VERIFY_CIPHERTEXT_COMMITMENT_EQUALITY_COMPUTE_UNITS)
209 .map_err(|_| InstructionError::ComputationalBudgetExceeded)?;
210 ic_msg!(invoke_context, "VerifyCiphertextCommitmentEquality");
211 process_verify_proof::<
212 CiphertextCommitmentEqualityProofData,
213 CiphertextCommitmentEqualityProofContext,
214 >(invoke_context)
215 }
216 ProofInstruction::VerifyPubkeyValidity => {
217 invoke_context
218 .consume_checked(VERIFY_PUBKEY_VALIDITY_COMPUTE_UNITS)
219 .map_err(|_| InstructionError::ComputationalBudgetExceeded)?;
220 ic_msg!(invoke_context, "VerifyPubkeyValidity");
221 process_verify_proof::<PubkeyValidityProofData, PubkeyValidityProofContext>(
222 invoke_context,
223 )
224 }
225 ProofInstruction::VerifyPercentageWithCap => {
226 invoke_context
227 .consume_checked(VERIFY_PERCENTAGE_WITH_CAP_COMPUTE_UNITS)
228 .map_err(|_| InstructionError::ComputationalBudgetExceeded)?;
229 ic_msg!(invoke_context, "VerifyPercentageWithCap");
230 process_verify_proof::<PercentageWithCapProofData, PercentageWithCapProofContext>(
231 invoke_context,
232 )
233 }
234 ProofInstruction::VerifyBatchedRangeProofU64 => {
235 invoke_context
236 .consume_checked(VERIFY_BATCHED_RANGE_PROOF_U64_COMPUTE_UNITS)
237 .map_err(|_| InstructionError::ComputationalBudgetExceeded)?;
238 ic_msg!(invoke_context, "VerifyBatchedRangeProofU64");
239 process_verify_proof::<BatchedRangeProofU64Data, BatchedRangeProofContext>(
240 invoke_context,
241 )
242 }
243 ProofInstruction::VerifyBatchedRangeProofU128 => {
244 invoke_context
245 .consume_checked(VERIFY_BATCHED_RANGE_PROOF_U128_COMPUTE_UNITS)
246 .map_err(|_| InstructionError::ComputationalBudgetExceeded)?;
247 ic_msg!(invoke_context, "VerifyBatchedRangeProofU128");
248 process_verify_proof::<BatchedRangeProofU128Data, BatchedRangeProofContext>(
249 invoke_context,
250 )
251 }
252 ProofInstruction::VerifyBatchedRangeProofU256 => {
253 invoke_context
254 .consume_checked(VERIFY_BATCHED_RANGE_PROOF_U256_COMPUTE_UNITS)
255 .map_err(|_| InstructionError::ComputationalBudgetExceeded)?;
256 ic_msg!(invoke_context, "VerifyBatchedRangeProofU256");
257 process_verify_proof::<BatchedRangeProofU256Data, BatchedRangeProofContext>(
258 invoke_context,
259 )
260 }
261 ProofInstruction::VerifyGroupedCiphertext2HandlesValidity => {
262 invoke_context
263 .consume_checked(VERIFY_GROUPED_CIPHERTEXT_2_HANDLES_VALIDITY_COMPUTE_UNITS)
264 .map_err(|_| InstructionError::ComputationalBudgetExceeded)?;
265 ic_msg!(invoke_context, "VerifyGroupedCiphertext2HandlesValidity");
266 process_verify_proof::<
267 GroupedCiphertext2HandlesValidityProofData,
268 GroupedCiphertext2HandlesValidityProofContext,
269 >(invoke_context)
270 }
271 ProofInstruction::VerifyBatchedGroupedCiphertext2HandlesValidity => {
272 invoke_context
273 .consume_checked(VERIFY_BATCHED_GROUPED_CIPHERTEXT_2_HANDLES_VALIDITY_COMPUTE_UNITS)
274 .map_err(|_| InstructionError::ComputationalBudgetExceeded)?;
275 ic_msg!(
276 invoke_context,
277 "VerifyBatchedGroupedCiphertext2HandlesValidity"
278 );
279 process_verify_proof::<
280 BatchedGroupedCiphertext2HandlesValidityProofData,
281 BatchedGroupedCiphertext2HandlesValidityProofContext,
282 >(invoke_context)
283 }
284 ProofInstruction::VerifyGroupedCiphertext3HandlesValidity => {
285 invoke_context
286 .consume_checked(VERIFY_GROUPED_CIPHERTEXT_3_HANDLES_VALIDITY_COMPUTE_UNITS)
287 .map_err(|_| InstructionError::ComputationalBudgetExceeded)?;
288 ic_msg!(invoke_context, "VerifyGroupedCiphertext3HandlesValidity");
289 process_verify_proof::<
290 GroupedCiphertext3HandlesValidityProofData,
291 GroupedCiphertext3HandlesValidityProofContext,
292 >(invoke_context)
293 }
294 ProofInstruction::VerifyBatchedGroupedCiphertext3HandlesValidity => {
295 invoke_context
296 .consume_checked(VERIFY_BATCHED_GROUPED_CIPHERTEXT_3_HANDLES_VALIDITY_COMPUTE_UNITS)
297 .map_err(|_| InstructionError::ComputationalBudgetExceeded)?;
298 ic_msg!(
299 invoke_context,
300 "VerifyBatchedGroupedCiphertext3HandlesValidity"
301 );
302 process_verify_proof::<
303 BatchedGroupedCiphertext3HandlesValidityProofData,
304 BatchedGroupedCiphertext3HandlesValidityProofContext,
305 >(invoke_context)
306 }
307 }
308});