1#![cfg(not(target_os = "solana"))]
2use std::collections::HashMap;
3
4use anchor_lang::{AnchorSerialize, InstructionData, ToAccountMetas};
5use solana_sdk::{
6 instruction::{AccountMeta, Instruction},
7 pubkey::Pubkey,
8};
9
10use super::compressed_account::{
11 CompressedAccount, MerkleContext, PackedCompressedAccountWithMerkleContext, PackedMerkleContext,
12};
13use crate::{
14 invoke::{processor::CompressedProof, sol_compression::SOL_POOL_PDA_SEED},
15 utils::{get_cpi_authority_pda, get_registered_program_pda},
16 InstructionDataInvoke, NewAddressParams, NewAddressParamsPacked,
17 OutputCompressedAccountWithPackedContext,
18};
19
20pub fn get_sol_pool_pda() -> Pubkey {
21 Pubkey::find_program_address(&[SOL_POOL_PDA_SEED], &crate::ID).0
22}
23
24#[allow(clippy::too_many_arguments)]
25pub fn create_invoke_instruction(
26 fee_payer: &Pubkey,
27 payer: &Pubkey,
28 input_compressed_accounts: &[CompressedAccount],
29 output_compressed_accounts: &[CompressedAccount],
30 merkle_context: &[MerkleContext],
31 output_compressed_account_merkle_tree_pubkeys: &[Pubkey],
32 input_root_indices: &[u16],
33 new_address_params: &[NewAddressParams],
34 proof: Option<CompressedProof>,
35 compress_or_decompress_lamports: Option<u64>,
36 is_compress: bool,
37 decompression_recipient: Option<Pubkey>,
38 sort: bool,
39) -> Instruction {
40 let (remaining_accounts, mut inputs_struct) =
41 create_invoke_instruction_data_and_remaining_accounts(
42 new_address_params,
43 merkle_context,
44 input_compressed_accounts,
45 input_root_indices,
46 output_compressed_account_merkle_tree_pubkeys,
47 output_compressed_accounts,
48 proof,
49 compress_or_decompress_lamports,
50 is_compress,
51 );
52 if sort {
53 inputs_struct
54 .output_compressed_accounts
55 .sort_by(|a, b| a.merkle_tree_index.cmp(&b.merkle_tree_index));
56 }
57 let mut inputs = Vec::new();
58
59 InstructionDataInvoke::serialize(&inputs_struct, &mut inputs).unwrap();
60
61 let instruction_data = crate::instruction::Invoke { inputs };
62
63 let sol_pool_pda = compress_or_decompress_lamports.map(|_| get_sol_pool_pda());
64
65 let accounts = crate::accounts::InvokeInstruction {
66 fee_payer: *fee_payer,
67 authority: *payer,
68 registered_program_pda: get_registered_program_pda(&crate::ID),
69 noop_program: Pubkey::new_from_array(account_compression::utils::constants::NOOP_PUBKEY),
70 account_compression_program: account_compression::ID,
71 account_compression_authority: get_cpi_authority_pda(&crate::ID),
72 sol_pool_pda,
73 decompression_recipient,
74 system_program: solana_sdk::system_program::ID,
75 };
76 Instruction {
77 program_id: crate::ID,
78 accounts: [accounts.to_account_metas(Some(true)), remaining_accounts].concat(),
79 data: instruction_data.data(),
80 }
81}
82
83#[allow(clippy::too_many_arguments)]
84pub fn create_invoke_instruction_data_and_remaining_accounts(
85 new_address_params: &[NewAddressParams],
86 merkle_context: &[MerkleContext],
87 input_compressed_accounts: &[CompressedAccount],
88 input_root_indices: &[u16],
89 output_compressed_account_merkle_tree_pubkeys: &[Pubkey],
90 output_compressed_accounts: &[CompressedAccount],
91 proof: Option<CompressedProof>,
92 compress_or_decompress_lamports: Option<u64>,
93 is_compress: bool,
94) -> (Vec<AccountMeta>, InstructionDataInvoke) {
95 let mut remaining_accounts = HashMap::<Pubkey, usize>::new();
96 let mut _input_compressed_accounts: Vec<PackedCompressedAccountWithMerkleContext> =
97 Vec::<PackedCompressedAccountWithMerkleContext>::new();
98 let mut index = 0;
99 let mut new_address_params_packed = new_address_params
100 .iter()
101 .map(|x| NewAddressParamsPacked {
102 seed: x.seed,
103 address_merkle_tree_root_index: x.address_merkle_tree_root_index,
104 address_merkle_tree_account_index: 0, address_queue_account_index: 0, })
107 .collect::<Vec<NewAddressParamsPacked>>();
108 for (i, context) in merkle_context.iter().enumerate() {
109 match remaining_accounts.get(&context.merkle_tree_pubkey) {
110 Some(_) => {}
111 None => {
112 remaining_accounts.insert(context.merkle_tree_pubkey, index);
113 index += 1;
114 }
115 };
116 _input_compressed_accounts.push(PackedCompressedAccountWithMerkleContext {
117 compressed_account: input_compressed_accounts[i].clone(),
118 merkle_context: PackedMerkleContext {
119 merkle_tree_pubkey_index: *remaining_accounts
120 .get(&context.merkle_tree_pubkey)
121 .unwrap() as u8,
122 nullifier_queue_pubkey_index: 0,
123 leaf_index: context.leaf_index,
124 queue_index: None,
125 },
126 read_only: false,
127 root_index: input_root_indices[i],
128 });
129 }
130
131 for (i, context) in merkle_context.iter().enumerate() {
132 match remaining_accounts.get(&context.nullifier_queue_pubkey) {
133 Some(_) => {}
134 None => {
135 remaining_accounts.insert(context.nullifier_queue_pubkey, index);
136 index += 1;
137 }
138 };
139 _input_compressed_accounts[i]
140 .merkle_context
141 .nullifier_queue_pubkey_index = *remaining_accounts
142 .get(&context.nullifier_queue_pubkey)
143 .unwrap() as u8;
144 }
145
146 let mut output_compressed_accounts_with_context: Vec<OutputCompressedAccountWithPackedContext> =
147 Vec::<OutputCompressedAccountWithPackedContext>::new();
148
149 for (i, mt) in output_compressed_account_merkle_tree_pubkeys
150 .iter()
151 .enumerate()
152 {
153 match remaining_accounts.get(mt) {
154 Some(_) => {}
155 None => {
156 remaining_accounts.insert(*mt, index);
157 index += 1;
158 }
159 };
160
161 output_compressed_accounts_with_context.push(OutputCompressedAccountWithPackedContext {
162 compressed_account: output_compressed_accounts[i].clone(),
163 merkle_tree_index: *remaining_accounts.get(mt).unwrap() as u8,
164 });
165 }
166
167 for (i, params) in new_address_params.iter().enumerate() {
168 match remaining_accounts.get(¶ms.address_merkle_tree_pubkey) {
169 Some(_) => {}
170 None => {
171 remaining_accounts.insert(params.address_merkle_tree_pubkey, index);
172 index += 1;
173 }
174 };
175 new_address_params_packed[i].address_merkle_tree_account_index = *remaining_accounts
176 .get(¶ms.address_merkle_tree_pubkey)
177 .unwrap()
178 as u8;
179 }
180
181 for (i, params) in new_address_params.iter().enumerate() {
182 match remaining_accounts.get(¶ms.address_queue_pubkey) {
183 Some(_) => {}
184 None => {
185 remaining_accounts.insert(params.address_queue_pubkey, index);
186 index += 1;
187 }
188 };
189 new_address_params_packed[i].address_queue_account_index = *remaining_accounts
190 .get(¶ms.address_queue_pubkey)
191 .unwrap() as u8;
192 }
193 let mut remaining_accounts = remaining_accounts
194 .iter()
195 .map(|(k, i)| (AccountMeta::new(*k, false), *i))
196 .collect::<Vec<(AccountMeta, usize)>>();
197 remaining_accounts.sort_by(|a, b| a.1.cmp(&b.1));
199 let remaining_accounts = remaining_accounts
200 .iter()
201 .map(|(k, _)| k.clone())
202 .collect::<Vec<AccountMeta>>();
203
204 let inputs_struct = InstructionDataInvoke {
205 relay_fee: None,
206 input_compressed_accounts_with_merkle_context: _input_compressed_accounts,
207 output_compressed_accounts: output_compressed_accounts_with_context,
208 proof,
209 new_address_params: new_address_params_packed,
210 compress_or_decompress_lamports,
211 is_compress,
212 };
213 (remaining_accounts, inputs_struct)
214}
215
216#[cfg(test)]
217mod test {
218 use anchor_lang::AnchorDeserialize;
219 use solana_sdk::{signature::Keypair, signer::Signer};
220
221 use super::*;
222
223 #[test]
224 fn test_create_execute_compressed_transaction() {
225 let payer = Keypair::new().pubkey();
226 let recipient = Keypair::new().pubkey();
227 let input_compressed_accounts = vec![
228 CompressedAccount {
229 lamports: 100,
230 owner: payer,
231 address: None,
232 data: None,
233 },
234 CompressedAccount {
235 lamports: 100,
236 owner: payer,
237 address: None,
238 data: None,
239 },
240 ];
241 let output_compressed_accounts = vec![
242 CompressedAccount {
243 lamports: 50,
244 owner: payer,
245 address: None,
246 data: None,
247 },
248 CompressedAccount {
249 lamports: 150,
250 owner: recipient,
251 address: None,
252 data: None,
253 },
254 ];
255 let merkle_tree_indices = vec![0, 2];
256 let merkle_tree_pubkey = Keypair::new().pubkey();
257 let merkle_tree_pubkey_1 = Keypair::new().pubkey();
258
259 let nullifier_array_pubkey = Keypair::new().pubkey();
260 let input_merkle_context = vec![
261 MerkleContext {
262 merkle_tree_pubkey,
263 nullifier_queue_pubkey: nullifier_array_pubkey,
264 leaf_index: 0,
265 queue_index: None,
266 },
267 MerkleContext {
268 merkle_tree_pubkey,
269 nullifier_queue_pubkey: nullifier_array_pubkey,
270 leaf_index: 1,
271 queue_index: None,
272 },
273 ];
274
275 let output_compressed_account_merkle_tree_pubkeys =
276 vec![merkle_tree_pubkey, merkle_tree_pubkey_1];
277 let input_root_indices = vec![0, 1];
278 let proof = CompressedProof {
279 a: [0u8; 32],
280 b: [1u8; 64],
281 c: [0u8; 32],
282 };
283 let instruction = create_invoke_instruction(
284 &payer,
285 &payer,
286 &input_compressed_accounts.clone(),
287 &output_compressed_accounts.clone(),
288 &input_merkle_context,
289 &output_compressed_account_merkle_tree_pubkeys,
290 &input_root_indices.clone(),
291 Vec::<NewAddressParams>::new().as_slice(),
292 Some(proof.clone()),
293 Some(100),
294 true,
295 None,
296 true,
297 );
298 assert_eq!(instruction.program_id, crate::ID);
299
300 let deserialized_instruction_data: InstructionDataInvoke =
301 InstructionDataInvoke::deserialize(&mut instruction.data[12..].as_ref()).unwrap();
302 deserialized_instruction_data
303 .input_compressed_accounts_with_merkle_context
304 .iter()
305 .enumerate()
306 .for_each(|(i, compressed_account_with_context)| {
307 assert_eq!(
308 input_compressed_accounts[i],
309 compressed_account_with_context.compressed_account
310 );
311 });
312 deserialized_instruction_data
313 .output_compressed_accounts
314 .iter()
315 .enumerate()
316 .for_each(|(i, compressed_account)| {
317 assert_eq!(
318 OutputCompressedAccountWithPackedContext {
319 compressed_account: output_compressed_accounts[i].clone(),
320 merkle_tree_index: merkle_tree_indices[i] as u8
321 },
322 *compressed_account
323 );
324 });
325 assert_eq!(
326 deserialized_instruction_data
327 .input_compressed_accounts_with_merkle_context
328 .len(),
329 2
330 );
331 assert_eq!(
332 deserialized_instruction_data
333 .output_compressed_accounts
334 .len(),
335 2
336 );
337 assert_eq!(
338 deserialized_instruction_data.proof.clone().unwrap().a,
339 proof.a
340 );
341 assert_eq!(
342 deserialized_instruction_data.proof.clone().unwrap().b,
343 proof.b
344 );
345 assert_eq!(
346 deserialized_instruction_data.proof.clone().unwrap().c,
347 proof.c
348 );
349 assert_eq!(
350 deserialized_instruction_data
351 .compress_or_decompress_lamports
352 .unwrap(),
353 100
354 );
355 assert_eq!(deserialized_instruction_data.is_compress, true);
356 let ref_account_meta = AccountMeta::new(payer, true);
357 assert_eq!(instruction.accounts[0], ref_account_meta);
358 assert_eq!(
359 deserialized_instruction_data.input_compressed_accounts_with_merkle_context[0]
360 .merkle_context
361 .nullifier_queue_pubkey_index,
362 1
363 );
364 assert_eq!(
365 deserialized_instruction_data.input_compressed_accounts_with_merkle_context[1]
366 .merkle_context
367 .nullifier_queue_pubkey_index,
368 1
369 );
370 assert_eq!(
371 instruction.accounts[9 + deserialized_instruction_data
372 .input_compressed_accounts_with_merkle_context[0]
373 .merkle_context
374 .merkle_tree_pubkey_index as usize],
375 AccountMeta::new(merkle_tree_pubkey, false)
376 );
377 assert_eq!(
378 instruction.accounts[9 + deserialized_instruction_data
379 .input_compressed_accounts_with_merkle_context[1]
380 .merkle_context
381 .merkle_tree_pubkey_index as usize],
382 AccountMeta::new(merkle_tree_pubkey, false)
383 );
384 assert_eq!(
385 instruction.accounts[9 + deserialized_instruction_data
386 .input_compressed_accounts_with_merkle_context[0]
387 .merkle_context
388 .nullifier_queue_pubkey_index as usize],
389 AccountMeta::new(nullifier_array_pubkey, false)
390 );
391 assert_eq!(
392 instruction.accounts[9 + deserialized_instruction_data
393 .input_compressed_accounts_with_merkle_context[1]
394 .merkle_context
395 .nullifier_queue_pubkey_index as usize],
396 AccountMeta::new(nullifier_array_pubkey, false)
397 );
398 assert_eq!(
399 instruction.accounts[9 + deserialized_instruction_data.output_compressed_accounts[0]
400 .merkle_tree_index as usize],
401 AccountMeta::new(merkle_tree_pubkey, false)
402 );
403 assert_eq!(
404 instruction.accounts[9 + deserialized_instruction_data.output_compressed_accounts[1]
405 .merkle_tree_index as usize],
406 AccountMeta::new(merkle_tree_pubkey_1, false)
407 );
408 }
409}