solana_zk_token_proof_program/
lib.rs1#![cfg_attr(
2 not(feature = "agave-unstable-api"),
3 deprecated(
4 since = "3.1.0",
5 note = "This crate has been marked for formal inclusion in the Agave Unstable API. From \
6 v4.0.0 onward, the `agave-unstable-api` crate feature must be specified to \
7 acknowledge use of an interface that may break without warning."
8 )
9)]
10#![forbid(unsafe_code)]
11#![allow(deprecated)]
14
15use {
16 bytemuck::Pod,
17 solana_instruction::{error::InstructionError, TRANSACTION_LEVEL_STACK_HEIGHT},
18 solana_program_runtime::{declare_process_instruction, invoke_context::InvokeContext},
19 solana_sdk_ids::system_program,
20 solana_svm_log_collector::ic_msg,
21 solana_zk_token_sdk::{
22 zk_token_proof_instruction::*,
23 zk_token_proof_program::id,
24 zk_token_proof_state::{ProofContextState, ProofContextStateMeta},
25 },
26 std::result::Result,
27};
28
29pub const CLOSE_CONTEXT_STATE_COMPUTE_UNITS: u64 = 3_300;
30pub const VERIFY_ZERO_BALANCE_COMPUTE_UNITS: u64 = 6_000;
31pub const VERIFY_WITHDRAW_COMPUTE_UNITS: u64 = 110_000;
32pub const VERIFY_CIPHERTEXT_CIPHERTEXT_EQUALITY_COMPUTE_UNITS: u64 = 8_000;
33pub const VERIFY_TRANSFER_COMPUTE_UNITS: u64 = 219_000;
34pub const VERIFY_TRANSFER_WITH_FEE_COMPUTE_UNITS: u64 = 407_000;
35pub const VERIFY_PUBKEY_VALIDITY_COMPUTE_UNITS: u64 = 2_600;
36pub const VERIFY_RANGE_PROOF_U64_COMPUTE_UNITS: u64 = 105_000;
37pub const VERIFY_BATCHED_RANGE_PROOF_U64_COMPUTE_UNITS: u64 = 111_000;
38pub const VERIFY_BATCHED_RANGE_PROOF_U128_COMPUTE_UNITS: u64 = 200_000;
39pub const VERIFY_BATCHED_RANGE_PROOF_U256_COMPUTE_UNITS: u64 = 368_000;
40pub const VERIFY_CIPHERTEXT_COMMITMENT_EQUALITY_COMPUTE_UNITS: u64 = 6_400;
41pub const VERIFY_GROUPED_CIPHERTEXT_2_HANDLES_VALIDITY_COMPUTE_UNITS: u64 = 6_400;
42pub const VERIFY_BATCHED_GROUPED_CIPHERTEXT_2_HANDLES_VALIDITY_COMPUTE_UNITS: u64 = 13_000;
43pub const VERIFY_FEE_SIGMA_COMPUTE_UNITS: u64 = 6_500;
44pub const VERIFY_GROUPED_CIPHERTEXT_3_HANDLES_VALIDITY_COMPUTE_UNITS: u64 = 8_100;
45pub const VERIFY_BATCHED_GROUPED_CIPHERTEXT_3_HANDLES_VALIDITY_COMPUTE_UNITS: u64 = 16_400;
46
47const INSTRUCTION_DATA_LENGTH_WITH_PROOF_ACCOUNT: usize = 5;
48
49fn process_verify_proof<T, U>(invoke_context: &mut InvokeContext) -> Result<(), InstructionError>
50where
51 T: Pod + ZkProofData<U>,
52 U: Pod,
53{
54 let transaction_context = &invoke_context.transaction_context;
55 let instruction_context = transaction_context.get_current_instruction_context()?;
56 let instruction_data = instruction_context.get_instruction_data();
57
58 let mut accessed_accounts = 0_u16;
60
61 let context_data = if instruction_data.len() == INSTRUCTION_DATA_LENGTH_WITH_PROOF_ACCOUNT {
63 let enable_zk_proof_from_account = false;
64 if !enable_zk_proof_from_account {
67 return Err(InstructionError::InvalidInstructionData);
68 }
69
70 let proof_data_account =
71 instruction_context.try_borrow_instruction_account(accessed_accounts)?;
72 accessed_accounts = accessed_accounts.checked_add(1).unwrap();
73
74 let proof_data_offset = u32::from_le_bytes(
75 instruction_data[1..INSTRUCTION_DATA_LENGTH_WITH_PROOF_ACCOUNT]
77 .try_into()
78 .map_err(|_| InstructionError::InvalidInstructionData)?,
79 );
80 let proof_data_start: usize = proof_data_offset
81 .try_into()
82 .map_err(|_| InstructionError::InvalidInstructionData)?;
83 let proof_data_end = proof_data_start
84 .checked_add(std::mem::size_of::<T>())
85 .ok_or(InstructionError::InvalidInstructionData)?;
86 let proof_data_raw = proof_data_account
87 .get_data()
88 .get(proof_data_start..proof_data_end)
89 .ok_or(InstructionError::InvalidAccountData)?;
90
91 let proof_data = bytemuck::try_from_bytes::<T>(proof_data_raw).map_err(|_| {
92 ic_msg!(invoke_context, "invalid proof data");
93 InstructionError::InvalidInstructionData
94 })?;
95 proof_data.verify_proof().map_err(|err| {
96 ic_msg!(invoke_context, "proof verification failed: {:?}", err);
97 InstructionError::InvalidInstructionData
98 })?;
99
100 *proof_data.context_data()
101 } else {
102 let proof_data =
103 ProofInstruction::proof_data::<T, U>(instruction_data).ok_or_else(|| {
104 ic_msg!(invoke_context, "invalid proof data");
105 InstructionError::InvalidInstructionData
106 })?;
107 proof_data.verify_proof().map_err(|err| {
108 ic_msg!(invoke_context, "proof_verification failed: {:?}", err);
109 InstructionError::InvalidInstructionData
110 })?;
111
112 *proof_data.context_data()
113 };
114
115 if instruction_context.get_number_of_instruction_accounts() > accessed_accounts {
117 let context_state_authority = *instruction_context
118 .get_key_of_instruction_account(accessed_accounts.checked_add(1).unwrap())?;
119 let mut proof_context_account =
120 instruction_context.try_borrow_instruction_account(accessed_accounts)?;
121
122 if *proof_context_account.get_owner() != id() {
123 return Err(InstructionError::InvalidAccountOwner);
124 }
125
126 let proof_context_state_meta =
127 ProofContextStateMeta::try_from_bytes(proof_context_account.get_data())?;
128
129 if proof_context_state_meta.proof_type != ProofType::Uninitialized.into() {
130 return Err(InstructionError::AccountAlreadyInitialized);
131 }
132
133 let context_state_data =
134 ProofContextState::encode(&context_state_authority, T::PROOF_TYPE, &context_data);
135
136 if proof_context_account.get_data().len() != context_state_data.len() {
137 return Err(InstructionError::InvalidAccountData);
138 }
139
140 proof_context_account.set_data_from_slice(&context_state_data)?;
141 }
142
143 Ok(())
144}
145
146fn process_close_proof_context(invoke_context: &mut InvokeContext) -> Result<(), InstructionError> {
147 let transaction_context = &invoke_context.transaction_context;
148 let instruction_context = transaction_context.get_current_instruction_context()?;
149
150 let owner_pubkey = {
151 if !instruction_context.is_instruction_account_signer(2)? {
152 return Err(InstructionError::MissingRequiredSignature);
153 }
154
155 *instruction_context.get_key_of_instruction_account(2)?
156 };
157
158 let proof_context_account_pubkey = *instruction_context.get_key_of_instruction_account(0)?;
159 let destination_account_pubkey = *instruction_context.get_key_of_instruction_account(1)?;
160 if proof_context_account_pubkey == destination_account_pubkey {
161 return Err(InstructionError::InvalidInstructionData);
162 }
163
164 let mut proof_context_account = instruction_context.try_borrow_instruction_account(0)?;
165 let proof_context_state_meta =
166 ProofContextStateMeta::try_from_bytes(proof_context_account.get_data())?;
167 let expected_owner_pubkey = proof_context_state_meta.context_state_authority;
168
169 if owner_pubkey != expected_owner_pubkey {
170 return Err(InstructionError::InvalidAccountOwner);
171 }
172
173 let mut destination_account = instruction_context.try_borrow_instruction_account(1)?;
174 destination_account.checked_add_lamports(proof_context_account.get_lamports())?;
175 proof_context_account.set_lamports(0)?;
176 proof_context_account.set_data_length(0)?;
177 proof_context_account.set_owner(system_program::id().as_ref())?;
178
179 Ok(())
180}
181
182declare_process_instruction!(Entrypoint, 0, |invoke_context| {
183 let enable_zk_transfer_with_fee = false;
184
185 let transaction_context = &invoke_context.transaction_context;
186 let instruction_context = transaction_context.get_current_instruction_context()?;
187 let instruction_data = instruction_context.get_instruction_data();
188 let instruction = ProofInstruction::instruction_type(instruction_data)
189 .ok_or(InstructionError::InvalidInstructionData)?;
190
191 if invoke_context.get_stack_height() != TRANSACTION_LEVEL_STACK_HEIGHT
192 && instruction != ProofInstruction::CloseContextState
193 {
194 return Err(InstructionError::UnsupportedProgramId);
196 }
197
198 match instruction {
199 ProofInstruction::CloseContextState => {
200 invoke_context
201 .consume_checked(CLOSE_CONTEXT_STATE_COMPUTE_UNITS)
202 .map_err(|_| InstructionError::ComputationalBudgetExceeded)?;
203 ic_msg!(invoke_context, "CloseContextState");
204 process_close_proof_context(invoke_context)
205 }
206 ProofInstruction::VerifyZeroBalance => {
207 invoke_context
208 .consume_checked(VERIFY_ZERO_BALANCE_COMPUTE_UNITS)
209 .map_err(|_| InstructionError::ComputationalBudgetExceeded)?;
210 ic_msg!(invoke_context, "VerifyZeroBalance");
211 process_verify_proof::<ZeroBalanceProofData, ZeroBalanceProofContext>(invoke_context)
212 }
213 ProofInstruction::VerifyWithdraw => {
214 invoke_context
215 .consume_checked(VERIFY_WITHDRAW_COMPUTE_UNITS)
216 .map_err(|_| InstructionError::ComputationalBudgetExceeded)?;
217 ic_msg!(invoke_context, "VerifyWithdraw");
218 process_verify_proof::<WithdrawData, WithdrawProofContext>(invoke_context)
219 }
220 ProofInstruction::VerifyCiphertextCiphertextEquality => {
221 invoke_context
222 .consume_checked(VERIFY_CIPHERTEXT_CIPHERTEXT_EQUALITY_COMPUTE_UNITS)
223 .map_err(|_| InstructionError::ComputationalBudgetExceeded)?;
224 ic_msg!(invoke_context, "VerifyCiphertextCiphertextEquality");
225 process_verify_proof::<
226 CiphertextCiphertextEqualityProofData,
227 CiphertextCiphertextEqualityProofContext,
228 >(invoke_context)
229 }
230 ProofInstruction::VerifyTransfer => {
231 invoke_context
232 .consume_checked(VERIFY_TRANSFER_COMPUTE_UNITS)
233 .map_err(|_| InstructionError::ComputationalBudgetExceeded)?;
234 ic_msg!(invoke_context, "VerifyTransfer");
235 process_verify_proof::<TransferData, TransferProofContext>(invoke_context)
236 }
237 ProofInstruction::VerifyTransferWithFee => {
238 if !enable_zk_transfer_with_fee {
240 return Err(InstructionError::InvalidInstructionData);
241 }
242
243 invoke_context
244 .consume_checked(VERIFY_TRANSFER_WITH_FEE_COMPUTE_UNITS)
245 .map_err(|_| InstructionError::ComputationalBudgetExceeded)?;
246 ic_msg!(invoke_context, "VerifyTransferWithFee");
247 process_verify_proof::<TransferWithFeeData, TransferWithFeeProofContext>(invoke_context)
248 }
249 ProofInstruction::VerifyPubkeyValidity => {
250 invoke_context
251 .consume_checked(VERIFY_PUBKEY_VALIDITY_COMPUTE_UNITS)
252 .map_err(|_| InstructionError::ComputationalBudgetExceeded)?;
253 ic_msg!(invoke_context, "VerifyPubkeyValidity");
254 process_verify_proof::<PubkeyValidityData, PubkeyValidityProofContext>(invoke_context)
255 }
256 ProofInstruction::VerifyRangeProofU64 => {
257 invoke_context
258 .consume_checked(VERIFY_RANGE_PROOF_U64_COMPUTE_UNITS)
259 .map_err(|_| InstructionError::ComputationalBudgetExceeded)?;
260 ic_msg!(invoke_context, "VerifyRangeProof");
261 process_verify_proof::<RangeProofU64Data, RangeProofContext>(invoke_context)
262 }
263 ProofInstruction::VerifyBatchedRangeProofU64 => {
264 invoke_context
265 .consume_checked(VERIFY_BATCHED_RANGE_PROOF_U64_COMPUTE_UNITS)
266 .map_err(|_| InstructionError::ComputationalBudgetExceeded)?;
267 ic_msg!(invoke_context, "VerifyBatchedRangeProof64");
268 process_verify_proof::<BatchedRangeProofU64Data, BatchedRangeProofContext>(
269 invoke_context,
270 )
271 }
272 ProofInstruction::VerifyBatchedRangeProofU128 => {
273 invoke_context
274 .consume_checked(VERIFY_BATCHED_RANGE_PROOF_U128_COMPUTE_UNITS)
275 .map_err(|_| InstructionError::ComputationalBudgetExceeded)?;
276 ic_msg!(invoke_context, "VerifyBatchedRangeProof128");
277 process_verify_proof::<BatchedRangeProofU128Data, BatchedRangeProofContext>(
278 invoke_context,
279 )
280 }
281 ProofInstruction::VerifyBatchedRangeProofU256 => {
282 if !enable_zk_transfer_with_fee {
284 return Err(InstructionError::InvalidInstructionData);
285 }
286
287 invoke_context
288 .consume_checked(VERIFY_BATCHED_RANGE_PROOF_U256_COMPUTE_UNITS)
289 .map_err(|_| InstructionError::ComputationalBudgetExceeded)?;
290 ic_msg!(invoke_context, "VerifyBatchedRangeProof256");
291 process_verify_proof::<BatchedRangeProofU256Data, BatchedRangeProofContext>(
292 invoke_context,
293 )
294 }
295 ProofInstruction::VerifyCiphertextCommitmentEquality => {
296 invoke_context
297 .consume_checked(VERIFY_CIPHERTEXT_COMMITMENT_EQUALITY_COMPUTE_UNITS)
298 .map_err(|_| InstructionError::ComputationalBudgetExceeded)?;
299 ic_msg!(invoke_context, "VerifyCiphertextCommitmentEquality");
300 process_verify_proof::<
301 CiphertextCommitmentEqualityProofData,
302 CiphertextCommitmentEqualityProofContext,
303 >(invoke_context)
304 }
305 ProofInstruction::VerifyGroupedCiphertext2HandlesValidity => {
306 invoke_context
307 .consume_checked(VERIFY_GROUPED_CIPHERTEXT_2_HANDLES_VALIDITY_COMPUTE_UNITS)
308 .map_err(|_| InstructionError::ComputationalBudgetExceeded)?;
309 ic_msg!(invoke_context, "VerifyGroupedCiphertext2HandlesValidity");
310 process_verify_proof::<
311 GroupedCiphertext2HandlesValidityProofData,
312 GroupedCiphertext2HandlesValidityProofContext,
313 >(invoke_context)
314 }
315 ProofInstruction::VerifyBatchedGroupedCiphertext2HandlesValidity => {
316 invoke_context
317 .consume_checked(VERIFY_BATCHED_GROUPED_CIPHERTEXT_2_HANDLES_VALIDITY_COMPUTE_UNITS)
318 .map_err(|_| InstructionError::ComputationalBudgetExceeded)?;
319 ic_msg!(
320 invoke_context,
321 "VerifyBatchedGroupedCiphertext2HandlesValidity"
322 );
323 process_verify_proof::<
324 BatchedGroupedCiphertext2HandlesValidityProofData,
325 BatchedGroupedCiphertext2HandlesValidityProofContext,
326 >(invoke_context)
327 }
328 ProofInstruction::VerifyFeeSigma => {
329 if !enable_zk_transfer_with_fee {
331 return Err(InstructionError::InvalidInstructionData);
332 }
333
334 invoke_context
335 .consume_checked(VERIFY_FEE_SIGMA_COMPUTE_UNITS)
336 .map_err(|_| InstructionError::ComputationalBudgetExceeded)?;
337 ic_msg!(invoke_context, "VerifyFeeSigma");
338 process_verify_proof::<FeeSigmaProofData, FeeSigmaProofContext>(invoke_context)
339 }
340 ProofInstruction::VerifyGroupedCiphertext3HandlesValidity => {
341 invoke_context
342 .consume_checked(VERIFY_GROUPED_CIPHERTEXT_3_HANDLES_VALIDITY_COMPUTE_UNITS)
343 .map_err(|_| InstructionError::ComputationalBudgetExceeded)?;
344 ic_msg!(invoke_context, "VerifyGroupedCiphertext3HandlesValidity");
345 process_verify_proof::<
346 GroupedCiphertext3HandlesValidityProofData,
347 GroupedCiphertext3HandlesValidityProofContext,
348 >(invoke_context)
349 }
350 ProofInstruction::VerifyBatchedGroupedCiphertext3HandlesValidity => {
351 invoke_context
352 .consume_checked(VERIFY_BATCHED_GROUPED_CIPHERTEXT_3_HANDLES_VALIDITY_COMPUTE_UNITS)
353 .map_err(|_| InstructionError::ComputationalBudgetExceeded)?;
354 ic_msg!(
355 invoke_context,
356 "VerifyBatchedGroupedCiphertext3HandlesValidity"
357 );
358 process_verify_proof::<
359 BatchedGroupedCiphertext3HandlesValidityProofData,
360 BatchedGroupedCiphertext3HandlesValidityProofContext,
361 >(invoke_context)
362 }
363 }
364});