light_system_program/invoke/
verify_state_proof.rs1use crate::{
2 sdk::{accounts::InvokeAccounts, compressed_account::PackedCompressedAccountWithMerkleContext},
3 NewAddressParamsPacked,
4};
5use account_compression::{
6 utils::check_discrimininator::check_discriminator, AddressMerkleTreeAccount,
7 StateMerkleTreeAccount,
8};
9use anchor_lang::{prelude::*, Bumps};
10use light_concurrent_merkle_tree::zero_copy::ConcurrentMerkleTreeZeroCopy;
11use light_hasher::Poseidon;
12use light_indexed_merkle_tree::zero_copy::IndexedMerkleTreeZeroCopy;
13use light_macros::heap_neutral;
14use light_utils::hash_to_bn254_field_size_be;
15use light_verifier::{
16 verify_create_addresses_and_merkle_proof_zkp, verify_create_addresses_zkp,
17 verify_merkle_proof_zkp, CompressedProof,
18};
19use std::mem;
20
21#[inline(never)]
22#[heap_neutral]
23pub fn fetch_input_compressed_account_roots<
24 'a,
25 'b,
26 'c: 'info,
27 'info,
28 A: InvokeAccounts<'info> + Bumps,
29>(
30 input_compressed_accounts_with_merkle_context: &'a [PackedCompressedAccountWithMerkleContext],
31 ctx: &'a Context<'a, 'b, 'c, 'info, A>,
32 roots: &'a mut [[u8; 32]],
33) -> Result<()> {
34 for (i, input_compressed_account_with_context) in input_compressed_accounts_with_merkle_context
35 .iter()
36 .enumerate()
37 {
38 let merkle_tree = &ctx.remaining_accounts[input_compressed_account_with_context
39 .merkle_context
40 .merkle_tree_pubkey_index as usize];
41 let merkle_tree = merkle_tree.try_borrow_data()?;
42 check_discriminator::<StateMerkleTreeAccount>(&merkle_tree)?;
43 let merkle_tree = ConcurrentMerkleTreeZeroCopy::<Poseidon, 26>::from_bytes_zero_copy(
44 &merkle_tree[8 + mem::size_of::<StateMerkleTreeAccount>()..],
45 )
46 .map_err(ProgramError::from)?;
47 let fetched_roots = &merkle_tree.roots;
48
49 roots[i] = fetched_roots[input_compressed_account_with_context.root_index as usize];
50 }
51 Ok(())
52}
53
54#[inline(never)]
55#[heap_neutral]
56pub fn fetch_roots_address_merkle_tree<
57 'a,
58 'b,
59 'c: 'info,
60 'info,
61 A: InvokeAccounts<'info> + Bumps,
62>(
63 new_address_params: &'a [NewAddressParamsPacked],
64 ctx: &'a Context<'a, 'b, 'c, 'info, A>,
65 roots: &'a mut [[u8; 32]],
66) -> Result<()> {
67 for (i, new_address_param) in new_address_params.iter().enumerate() {
68 let merkle_tree = ctx.remaining_accounts
69 [new_address_param.address_merkle_tree_account_index as usize]
70 .to_account_info();
71 let merkle_tree = merkle_tree.try_borrow_data()?;
72 check_discriminator::<AddressMerkleTreeAccount>(&merkle_tree)?;
73 let merkle_tree =
74 IndexedMerkleTreeZeroCopy::<Poseidon, usize, 26, 16>::from_bytes_zero_copy(
75 &merkle_tree[8 + mem::size_of::<AddressMerkleTreeAccount>()..],
76 )
77 .map_err(ProgramError::from)?;
78 let fetched_roots = &merkle_tree.roots;
79
80 roots[i] = fetched_roots[new_address_param.address_merkle_tree_root_index as usize];
81 }
82 Ok(())
83}
84
85#[inline(never)]
89#[heap_neutral]
90#[allow(unused_mut)]
91pub fn hash_input_compressed_accounts<'a, 'b, 'c: 'info, 'info>(
92 remaining_accounts: &'a [AccountInfo<'info>],
93 input_compressed_accounts_with_merkle_context: &'a [PackedCompressedAccountWithMerkleContext],
94 leaves: &'a mut [[u8; 32]],
95 addresses: &'a mut [Option<[u8; 32]>],
96 hashed_pubkeys: &'a mut Vec<(Pubkey, [u8; 32])>,
97) -> Result<()> {
98 let mut owner_pubkey = input_compressed_accounts_with_merkle_context[0]
99 .compressed_account
100 .owner;
101 let mut hashed_owner = hash_to_bn254_field_size_be(&owner_pubkey.to_bytes())
102 .unwrap()
103 .0;
104 hashed_pubkeys.push((owner_pubkey, hashed_owner));
105 #[allow(unused)]
106 let mut current_hashed_mt = [0u8; 32];
107
108 let mut current_mt_index: i16 = -1;
109 for (j, input_compressed_account_with_context) in input_compressed_accounts_with_merkle_context
110 .iter()
111 .enumerate()
112 {
113 match &input_compressed_account_with_context
115 .compressed_account
116 .address
117 {
118 Some(address) => addresses[j] = Some(*address),
119 None => {}
120 };
121 if input_compressed_account_with_context
122 .merkle_context
123 .queue_index
124 .is_some()
125 {
126 unimplemented!("Queue index is not supported.");
127 }
128
129 #[allow(clippy::comparison_chain)]
130 if current_mt_index
131 != input_compressed_account_with_context
132 .merkle_context
133 .merkle_tree_pubkey_index as i16
134 {
135 current_mt_index = input_compressed_account_with_context
136 .merkle_context
137 .merkle_tree_pubkey_index as i16;
138 let merkle_tree_pubkey = remaining_accounts[input_compressed_account_with_context
139 .merkle_context
140 .merkle_tree_pubkey_index
141 as usize]
142 .key();
143 current_hashed_mt = match hashed_pubkeys.iter().find(|x| x.0 == merkle_tree_pubkey) {
144 Some(hashed_merkle_tree_pubkey) => hashed_merkle_tree_pubkey.1,
145 None => {
146 let hashed_merkle_tree_pubkey =
147 hash_to_bn254_field_size_be(&merkle_tree_pubkey.to_bytes())
148 .unwrap()
149 .0;
150 hashed_pubkeys.push((merkle_tree_pubkey, hashed_merkle_tree_pubkey));
151 hashed_merkle_tree_pubkey
152 }
153 };
154 }
155 if owner_pubkey
158 != input_compressed_account_with_context
159 .compressed_account
160 .owner
161 {
162 owner_pubkey = input_compressed_account_with_context
163 .compressed_account
164 .owner;
165 hashed_owner = match hashed_pubkeys.iter().find(|x| {
166 x.0 == input_compressed_account_with_context
167 .compressed_account
168 .owner
169 }) {
170 Some(hashed_owner) => hashed_owner.1,
171 None => {
172 let hashed_owner = hash_to_bn254_field_size_be(
173 &input_compressed_account_with_context
174 .compressed_account
175 .owner
176 .to_bytes(),
177 )
178 .unwrap()
179 .0;
180 hashed_pubkeys.push((
181 input_compressed_account_with_context
182 .compressed_account
183 .owner,
184 hashed_owner,
185 ));
186 hashed_owner
187 }
188 };
189 }
190 leaves[j] = input_compressed_account_with_context
191 .compressed_account
192 .hash_with_hashed_values::<Poseidon>(
193 &hashed_owner,
194 ¤t_hashed_mt,
195 &input_compressed_account_with_context
196 .merkle_context
197 .leaf_index,
198 )?;
199 }
200 Ok(())
201}
202
203#[heap_neutral]
204pub fn verify_state_proof(
205 roots: &[[u8; 32]],
206 leaves: &[[u8; 32]],
207 address_roots: &[[u8; 32]],
208 addresses: &[[u8; 32]],
209 compressed_proof: &CompressedProof,
210) -> anchor_lang::Result<()> {
211 if !addresses.is_empty() && !leaves.is_empty() {
212 verify_create_addresses_and_merkle_proof_zkp(
213 roots,
214 leaves,
215 address_roots,
216 addresses,
217 compressed_proof,
218 )
219 .map_err(ProgramError::from)?;
220 } else if !addresses.is_empty() {
221 verify_create_addresses_zkp(address_roots, addresses, compressed_proof)
222 .map_err(ProgramError::from)?;
223 } else {
224 verify_merkle_proof_zkp(roots, leaves, compressed_proof).map_err(ProgramError::from)?;
225 }
226 Ok(())
227}