light_system_program/invoke/
append_state.rs1use account_compression::utils::constants::CPI_AUTHORITY_PDA_SEED;
2use anchor_lang::{
3 prelude::*,
4 solana_program::{program::invoke_signed, pubkey::Pubkey},
5 Bumps,
6};
7use light_hasher::Poseidon;
8use light_heap::{bench_sbf_end, bench_sbf_start};
9use light_macros::heap_neutral;
10use light_utils::hash_to_bn254_field_size_be;
11
12use crate::{
13 constants::CPI_AUTHORITY_PDA_BUMP,
14 errors::SystemProgramError,
15 invoke_cpi::verify_signer::check_program_owner_state_merkle_tree,
16 sdk::{
17 accounts::{InvokeAccounts, SignerAccounts},
18 event::MerkleTreeSequenceNumber,
19 },
20 OutputCompressedAccountWithPackedContext,
21};
22
23#[allow(clippy::too_many_arguments)]
24#[heap_neutral]
25pub fn insert_output_compressed_accounts_into_state_merkle_tree<
26 'a,
27 'b,
28 'c: 'info,
29 'info,
30 A: InvokeAccounts<'info> + SignerAccounts<'info> + Bumps,
31>(
32 output_compressed_accounts: &mut [OutputCompressedAccountWithPackedContext],
33 ctx: &'a Context<'a, 'b, 'c, 'info, A>,
34 output_compressed_account_indices: &'a mut [u32],
35 output_compressed_account_hashes: &'a mut [[u8; 32]],
36 compressed_account_addresses: &'a mut Vec<Option<[u8; 32]>>,
37 invoking_program: &Option<Pubkey>,
38 hashed_pubkeys: &'a mut Vec<(Pubkey, [u8; 32])>,
39 sequence_numbers: &'a mut Vec<MerkleTreeSequenceNumber>,
40) -> Result<()> {
41 bench_sbf_start!("cpda_append_data_init");
42 let mut account_infos = vec![
43 ctx.accounts.get_fee_payer().to_account_info(), ctx.accounts
45 .get_account_compression_authority() .to_account_info(),
47 ctx.accounts.get_registered_program_pda().to_account_info(),
48 ctx.accounts.get_system_program().to_account_info(),
49 ];
50 let mut accounts = vec![
51 AccountMeta {
52 pubkey: account_infos[0].key(),
53 is_signer: true,
54 is_writable: true,
55 },
56 AccountMeta {
57 pubkey: account_infos[1].key(),
58 is_signer: true,
59 is_writable: false,
60 },
61 AccountMeta::new_readonly(account_infos[2].key(), false),
62 AccountMeta::new_readonly(account_infos[3].key(), false),
63 ];
64 let instruction_data = create_cpi_accounts_and_instruction_data(
65 output_compressed_accounts,
66 output_compressed_account_indices,
67 output_compressed_account_hashes,
68 compressed_account_addresses,
69 invoking_program,
70 hashed_pubkeys,
71 sequence_numbers,
72 ctx.remaining_accounts,
73 &mut account_infos,
74 &mut accounts,
75 )?;
76
77 let bump = &[CPI_AUTHORITY_PDA_BUMP];
78 let seeds = &[&[CPI_AUTHORITY_PDA_SEED, bump][..]];
79 let instruction = anchor_lang::solana_program::instruction::Instruction {
80 program_id: account_compression::ID,
81 accounts,
82 data: instruction_data,
83 };
84 invoke_signed(&instruction, account_infos.as_slice(), seeds)?;
85 bench_sbf_end!("cpda_append_rest");
86
87 Ok(())
88}
89
90#[allow(clippy::too_many_arguments)]
103pub fn create_cpi_accounts_and_instruction_data<'a>(
104 output_compressed_accounts: &[OutputCompressedAccountWithPackedContext],
105 output_compressed_account_indices: &mut [u32],
106 output_compressed_account_hashes: &mut [[u8; 32]],
107 compressed_account_addresses: &mut Vec<Option<[u8; 32]>>,
108 invoking_program: &Option<Pubkey>,
109 hashed_pubkeys: &mut Vec<(Pubkey, [u8; 32])>,
110 sequence_numbers: &mut Vec<MerkleTreeSequenceNumber>,
111 remaining_accounts: &'a [AccountInfo<'a>],
112 account_infos: &mut Vec<AccountInfo<'a>>,
113 accounts: &mut Vec<AccountMeta>,
114) -> Result<Vec<u8>> {
115 let mut current_index: i16 = -1;
116 let mut num_leaves_in_tree: u32 = 0;
117 let mut mt_next_index = 0;
118 let num_leaves = output_compressed_account_hashes.len();
119 let mut instruction_data = Vec::<u8>::with_capacity(12 + 33 * num_leaves);
120 let mut hashed_merkle_tree = [0u8; 32];
121 let mut index_merkle_tree_account = 0;
122 let number_of_merkle_trees =
123 output_compressed_accounts.last().unwrap().merkle_tree_index as usize + 1;
124 let mut merkle_tree_pubkeys = Vec::<Pubkey>::with_capacity(number_of_merkle_trees);
125
126 instruction_data.extend_from_slice(&[199, 144, 10, 82, 247, 142, 143, 7]);
128 instruction_data.extend_from_slice(&(num_leaves as u32).to_le_bytes());
130
131 for (j, account) in output_compressed_accounts.iter().enumerate() {
132 #[allow(clippy::comparison_chain)]
135 if account.merkle_tree_index as i16 == current_index {
136 } else if account.merkle_tree_index as i16 > current_index {
138 current_index = account.merkle_tree_index.into();
139 let seq;
140 (mt_next_index, _, seq) = check_program_owner_state_merkle_tree(
142 &remaining_accounts[account.merkle_tree_index as usize],
143 invoking_program,
144 )?;
145 let account_info =
146 remaining_accounts[account.merkle_tree_index as usize].to_account_info();
147
148 sequence_numbers.push(MerkleTreeSequenceNumber {
149 pubkey: account_info.key(),
150 seq,
151 });
152 hashed_merkle_tree = match hashed_pubkeys.iter().find(|x| x.0 == account_info.key()) {
153 Some(hashed_merkle_tree) => hashed_merkle_tree.1,
154 None => {
155 hash_to_bn254_field_size_be(&account_info.key().to_bytes())
156 .unwrap()
157 .0
158 }
159 };
160 if merkle_tree_pubkeys.contains(&account_info.key()) {
162 return err!(SystemProgramError::OutputMerkleTreeNotUnique);
163 } else {
164 merkle_tree_pubkeys.push(account_info.key());
165 }
166 accounts.push(AccountMeta {
167 pubkey: account_info.key(),
168 is_signer: false,
169 is_writable: true,
170 });
171 account_infos.push(account_info);
172
173 num_leaves_in_tree = 0;
174 index_merkle_tree_account += 1;
175 } else {
176 return err!(SystemProgramError::OutputMerkleTreeIndicesNotInOrder);
182 }
183
184 if let Some(address) = account.compressed_account.address {
186 if let Some(position) = compressed_account_addresses
187 .iter()
188 .filter(|x| x.is_some())
189 .position(|&x| x.unwrap() == address)
190 {
191 compressed_account_addresses.remove(position);
192 } else {
193 msg!("Address {:?}, is no new address and does not exist in input compressed accounts.", address);
194 msg!(
195 "Remaining compressed_account_addresses: {:?}",
196 compressed_account_addresses
197 );
198 return Err(SystemProgramError::InvalidAddress.into());
199 }
200 }
201
202 output_compressed_account_indices[j] = mt_next_index + num_leaves_in_tree;
203 num_leaves_in_tree += 1;
204 if account.compressed_account.data.is_some() && invoking_program.is_none() {
205 msg!("Invoking program is not provided.");
206 msg!("Only program owned compressed accounts can have data.");
207 return err!(SystemProgramError::InvokingProgramNotProvided);
208 }
209 let hashed_owner = match hashed_pubkeys
210 .iter()
211 .find(|x| x.0 == account.compressed_account.owner)
212 {
213 Some(hashed_owner) => hashed_owner.1,
214 None => {
215 let hashed_owner =
216 hash_to_bn254_field_size_be(&account.compressed_account.owner.to_bytes())
217 .unwrap()
218 .0;
219 hashed_pubkeys.push((account.compressed_account.owner, hashed_owner));
220 hashed_owner
221 }
222 };
223 output_compressed_account_hashes[j] = account
225 .compressed_account
226 .hash_with_hashed_values::<Poseidon>(
227 &hashed_owner,
228 &hashed_merkle_tree,
229 &output_compressed_account_indices[j],
230 )?;
231 instruction_data.extend_from_slice(&[index_merkle_tree_account - 1]);
233 instruction_data.extend_from_slice(&output_compressed_account_hashes[j]);
234 }
235 Ok(instruction_data)
236}
237
238#[test]
239fn test_instruction_data_borsh_compat() {
240 let mut vec = Vec::<u8>::new();
241 vec.extend_from_slice(&2u32.to_le_bytes());
242 vec.push(1);
243 vec.extend_from_slice(&[2u8; 32]);
244 vec.push(3);
245 vec.extend_from_slice(&[4u8; 32]);
246 let refe = vec![(1, [2u8; 32]), (3, [4u8; 32])];
247 let mut serialized = Vec::new();
248 Vec::<(u8, [u8; 32])>::serialize(&refe, &mut serialized).unwrap();
249 assert_eq!(serialized, vec);
250 let res = Vec::<(u8, [u8; 32])>::deserialize(&mut vec.as_slice()).unwrap();
251 assert_eq!(res, vec![(1, [2u8; 32]), (3, [4u8; 32])]);
252}