light_compressed_pda/invoke/
processor.rs

1use anchor_lang::{prelude::*, Bumps};
2use light_heap::{bench_sbf_end, bench_sbf_start};
3use light_verifier::CompressedProof as CompressedVerifierProof;
4
5use crate::{
6    errors::CompressedPdaError,
7    invoke::{
8        address::{derive_new_addresses, insert_addresses_into_address_merkle_tree_queue},
9        append_state::insert_output_compressed_accounts_into_state_merkle_tree,
10        emit_event::emit_state_transition_event,
11        nullify_state::insert_nullifiers,
12        sol_compression::compression_lamports,
13        verify_state_proof::{
14            fetch_roots, fetch_roots_address_merkle_tree, hash_input_compressed_accounts,
15            sum_check, verify_state_proof,
16        },
17    },
18    sdk::accounts::{InvokeAccounts, SignerAccounts},
19    InstructionDataInvoke,
20};
21
22// TODO: remove once upgraded to anchor 0.30.0 (right now it's required for idl generation)
23#[derive(Debug, Clone, PartialEq, Eq, AnchorSerialize, AnchorDeserialize)]
24pub struct CompressedProof {
25    pub a: [u8; 32],
26    pub b: [u8; 64],
27    pub c: [u8; 32],
28}
29
30impl Default for CompressedProof {
31    fn default() -> Self {
32        Self {
33            a: [0; 32],
34            b: [0; 64],
35            c: [0; 32],
36        }
37    }
38}
39
40pub fn process<
41    'a,
42    'b,
43    'c: 'info,
44    'info,
45    A: InvokeAccounts<'info> + SignerAccounts<'info> + Bumps,
46>(
47    inputs: InstructionDataInvoke,
48    invoking_program: Option<Pubkey>,
49    ctx: Context<'a, 'b, 'c, 'info, A>,
50) -> Result<()> {
51    // sum check ---------------------------------------------------
52    // the sum of in compressed accounts and compressed accounts must be equal minus the relay fee
53    bench_sbf_start!("cpda_sum_check");
54    sum_check(
55        &inputs.input_compressed_accounts_with_merkle_context,
56        &inputs.output_compressed_accounts,
57        &inputs.relay_fee,
58        &inputs.compression_lamports,
59        &inputs.is_compress,
60    )?;
61    bench_sbf_end!("cpda_sum_check");
62    // compression lamports ---------------------------------------------------
63    bench_sbf_start!("cpda_process_compression");
64    if inputs.compression_lamports.is_some() {
65        compression_lamports(&inputs, &ctx)?;
66    }
67    bench_sbf_end!("cpda_process_compression");
68
69    let mut input_compressed_account_hashes =
70        vec![[0u8; 32]; inputs.input_compressed_accounts_with_merkle_context.len()];
71    let mut input_compressed_account_addresses: Vec<Option<[u8; 32]>> =
72        vec![None; inputs.input_compressed_accounts_with_merkle_context.len()];
73
74    let mut output_leaf_indices = vec![0u32; inputs.output_compressed_accounts.len()];
75    let mut output_compressed_account_hashes =
76        vec![[0u8; 32]; inputs.output_compressed_accounts.len()];
77    // Bench vs vector
78    let mut hashed_pubkeys =
79        Vec::<(Pubkey, [u8; 32])>::with_capacity(ctx.remaining_accounts.len() + 1);
80
81    // verify state and or address proof ---------------------------------------------------
82    if !inputs
83        .input_compressed_accounts_with_merkle_context
84        .is_empty()
85        || !inputs.new_address_params.is_empty()
86    {
87        // hash input compressed accounts ---------------------------------------------------
88        bench_sbf_start!("cpda_hash_input_compressed_accounts");
89        if !inputs
90            .input_compressed_accounts_with_merkle_context
91            .is_empty()
92        {
93            hash_input_compressed_accounts(
94                ctx.remaining_accounts,
95                &inputs,
96                &mut input_compressed_account_hashes,
97                &mut input_compressed_account_addresses,
98                &mut hashed_pubkeys,
99            )?;
100        }
101
102        bench_sbf_end!("cpda_hash_input_compressed_accounts");
103        let mut new_addresses = vec![[0u8; 32]; inputs.new_address_params.len()];
104        // insert addresses into address merkle tree queue ---------------------------------------------------
105        if !new_addresses.is_empty() {
106            derive_new_addresses(
107                &inputs,
108                &ctx,
109                &mut input_compressed_account_addresses,
110                &mut new_addresses,
111            );
112            insert_addresses_into_address_merkle_tree_queue(
113                &ctx,
114                &new_addresses,
115                &inputs.new_address_params,
116                &invoking_program,
117            )?;
118        }
119        bench_sbf_start!("cpda_verify_state_proof");
120        let mut new_address_roots = vec![[0u8; 32]; inputs.new_address_params.len()];
121        // TODO: enable once address merkle tree init is debugged
122        fetch_roots_address_merkle_tree(&inputs.new_address_params, &ctx, &mut new_address_roots)?;
123        let mut roots = vec![[0u8; 32]; inputs.input_compressed_accounts_with_merkle_context.len()];
124        fetch_roots(&inputs, &ctx, &mut roots)?;
125        let proof = match &inputs.proof {
126            Some(proof) => proof,
127            None => return err!(CompressedPdaError::ProofIsNone),
128        };
129        let compressed_verifier_proof = CompressedVerifierProof {
130            a: proof.a,
131            b: proof.b,
132            c: proof.c,
133        };
134        match verify_state_proof(
135            &roots,
136            &input_compressed_account_hashes,
137            &new_address_roots,
138            new_addresses.as_slice(),
139            &compressed_verifier_proof,
140        ) {
141            Ok(_) => anchor_lang::Result::Ok(()),
142            Err(e) => {
143                msg!(
144                    "input_compressed_accounts_with_merkle_context: {:?}",
145                    inputs.input_compressed_accounts_with_merkle_context
146                );
147                Err(e)
148            }
149        }?;
150        bench_sbf_end!("cpda_verify_state_proof");
151        // insert nullifies (input compressed account hashes)---------------------------------------------------
152        bench_sbf_start!("cpda_nullifiers");
153        insert_nullifiers(
154            &inputs,
155            &ctx,
156            &input_compressed_account_hashes,
157            &invoking_program,
158        )?;
159        bench_sbf_end!("cpda_nullifiers");
160    } else if inputs.proof.is_some() {
161        msg!("Proof is some but no input compressed accounts or new addresses provided.");
162        return err!(CompressedPdaError::ProofIsSome);
163    }
164    bench_sbf_end!("cpda_nullifiers");
165
166    const ITER_SIZE: usize = 28;
167    // insert leaves (output compressed account hashes) ---------------------------------------------------
168    if !inputs.output_compressed_accounts.is_empty() {
169        let mut i = 0;
170        for _ in inputs.output_compressed_accounts.iter().step_by(ITER_SIZE) {
171            bench_sbf_start!("cpda_append");
172            insert_output_compressed_accounts_into_state_merkle_tree::<ITER_SIZE, A>(
173                &inputs,
174                &ctx,
175                &mut output_leaf_indices,
176                &mut output_compressed_account_hashes,
177                &mut input_compressed_account_addresses,
178                &mut i,
179                &invoking_program,
180                &mut hashed_pubkeys,
181            )?;
182            bench_sbf_end!("cpda_append");
183        }
184    }
185    bench_sbf_start!("emit_state_transition_event");
186
187    // emit state transition event ---------------------------------------------------
188    bench_sbf_start!("emit_state_transition_event");
189    emit_state_transition_event(
190        inputs,
191        &ctx,
192        input_compressed_account_hashes,
193        output_compressed_account_hashes,
194        output_leaf_indices,
195    )?;
196    bench_sbf_end!("emit_state_transition_event");
197
198    Ok(())
199}
200
201// TODO: refactor to compressed_account
202// #[derive(Debug)]
203// #[account]
204// pub struct InstructionDataInvoke2 {
205//     pub proof: Option<CompressedProof>,
206//     pub low_element_indices: Vec<u16>,
207//     pub root_indices: Vec<u16>,
208//     pub relay_fee: Option<u64>,
209//     pub utxos: SerializedUtxos,
210// }
211
212// pub fn into_inputs(
213//     inputs: InstructionDataInvoke2,
214//     accounts: &[Pubkey],
215//     remaining_accounts: &[Pubkey],
216// ) -> Result<InstructionDataInvoke> {
217//     let input_compressed_accounts_with_merkle_context = inputs
218//         .utxos
219//         .input_compressed_accounts_from_serialized_utxos(accounts, remaining_accounts)
220//         .unwrap();
221//     let output_compressed_accounts = inputs
222//         .utxos
223//         .output_compressed_accounts_from_serialized_utxos(accounts)
224//         .unwrap();
225//     Ok(InstructionDataInvoke {
226//         proof: inputs.proof,
227//         low_element_indices: inputs.low_element_indices,
228//         root_indices: inputs.root_indices,
229//         relay_fee: inputs.relay_fee,
230//         input_compressed_accounts_with_merkle_context,
231//         output_compressed_accounts,
232//     })
233// }