use anchor_lang::{prelude::*, Bumps};
use light_heap::{bench_sbf_end, bench_sbf_start};
use light_verifier::CompressedProof as CompressedVerifierProof;
use crate::{
errors::CompressedPdaError,
invoke::{
address::{derive_new_addresses, insert_addresses_into_address_merkle_tree_queue},
append_state::insert_output_compressed_accounts_into_state_merkle_tree,
emit_event::emit_state_transition_event,
nullify_state::insert_nullifiers,
sol_compression::compression_lamports,
verify_state_proof::{
fetch_roots, fetch_roots_address_merkle_tree, hash_input_compressed_accounts,
sum_check, verify_state_proof,
},
},
sdk::accounts::{InvokeAccounts, SignerAccounts},
InstructionDataInvoke,
};
#[derive(Debug, Clone, PartialEq, Eq, AnchorSerialize, AnchorDeserialize)]
pub struct CompressedProof {
pub a: [u8; 32],
pub b: [u8; 64],
pub c: [u8; 32],
}
impl Default for CompressedProof {
fn default() -> Self {
Self {
a: [0; 32],
b: [0; 64],
c: [0; 32],
}
}
}
pub fn process<
'a,
'b,
'c: 'info,
'info,
A: InvokeAccounts<'info> + SignerAccounts<'info> + Bumps,
>(
inputs: InstructionDataInvoke,
invoking_program: Option<Pubkey>,
ctx: Context<'a, 'b, 'c, 'info, A>,
) -> Result<()> {
bench_sbf_start!("cpda_sum_check");
sum_check(
&inputs.input_compressed_accounts_with_merkle_context,
&inputs.output_compressed_accounts,
&inputs.relay_fee,
&inputs.compression_lamports,
&inputs.is_compress,
)?;
bench_sbf_end!("cpda_sum_check");
bench_sbf_start!("cpda_process_compression");
if inputs.compression_lamports.is_some() {
compression_lamports(&inputs, &ctx)?;
}
bench_sbf_end!("cpda_process_compression");
let mut input_compressed_account_hashes =
vec![[0u8; 32]; inputs.input_compressed_accounts_with_merkle_context.len()];
let mut input_compressed_account_addresses: Vec<Option<[u8; 32]>> =
vec![None; inputs.input_compressed_accounts_with_merkle_context.len()];
let mut output_leaf_indices = vec![0u32; inputs.output_compressed_accounts.len()];
let mut output_compressed_account_hashes =
vec![[0u8; 32]; inputs.output_compressed_accounts.len()];
let mut hashed_pubkeys =
Vec::<(Pubkey, [u8; 32])>::with_capacity(ctx.remaining_accounts.len() + 1);
if !inputs
.input_compressed_accounts_with_merkle_context
.is_empty()
|| !inputs.new_address_params.is_empty()
{
bench_sbf_start!("cpda_hash_input_compressed_accounts");
if !inputs
.input_compressed_accounts_with_merkle_context
.is_empty()
{
hash_input_compressed_accounts(
ctx.remaining_accounts,
&inputs,
&mut input_compressed_account_hashes,
&mut input_compressed_account_addresses,
&mut hashed_pubkeys,
)?;
}
bench_sbf_end!("cpda_hash_input_compressed_accounts");
let mut new_addresses = vec![[0u8; 32]; inputs.new_address_params.len()];
if !new_addresses.is_empty() {
derive_new_addresses(
&inputs,
&ctx,
&mut input_compressed_account_addresses,
&mut new_addresses,
);
insert_addresses_into_address_merkle_tree_queue(
&ctx,
&new_addresses,
&inputs.new_address_params,
&invoking_program,
)?;
}
bench_sbf_start!("cpda_verify_state_proof");
let mut new_address_roots = vec![[0u8; 32]; inputs.new_address_params.len()];
fetch_roots_address_merkle_tree(&inputs.new_address_params, &ctx, &mut new_address_roots)?;
let mut roots = vec![[0u8; 32]; inputs.input_compressed_accounts_with_merkle_context.len()];
fetch_roots(&inputs, &ctx, &mut roots)?;
let proof = match &inputs.proof {
Some(proof) => proof,
None => return err!(CompressedPdaError::ProofIsNone),
};
let compressed_verifier_proof = CompressedVerifierProof {
a: proof.a,
b: proof.b,
c: proof.c,
};
match verify_state_proof(
&roots,
&input_compressed_account_hashes,
&new_address_roots,
new_addresses.as_slice(),
&compressed_verifier_proof,
) {
Ok(_) => anchor_lang::Result::Ok(()),
Err(e) => {
msg!(
"input_compressed_accounts_with_merkle_context: {:?}",
inputs.input_compressed_accounts_with_merkle_context
);
Err(e)
}
}?;
bench_sbf_end!("cpda_verify_state_proof");
bench_sbf_start!("cpda_nullifiers");
insert_nullifiers(
&inputs,
&ctx,
&input_compressed_account_hashes,
&invoking_program,
)?;
bench_sbf_end!("cpda_nullifiers");
} else if inputs.proof.is_some() {
msg!("Proof is some but no input compressed accounts or new addresses provided.");
return err!(CompressedPdaError::ProofIsSome);
}
bench_sbf_end!("cpda_nullifiers");
const ITER_SIZE: usize = 28;
if !inputs.output_compressed_accounts.is_empty() {
let mut i = 0;
for _ in inputs.output_compressed_accounts.iter().step_by(ITER_SIZE) {
bench_sbf_start!("cpda_append");
insert_output_compressed_accounts_into_state_merkle_tree::<ITER_SIZE, A>(
&inputs,
&ctx,
&mut output_leaf_indices,
&mut output_compressed_account_hashes,
&mut input_compressed_account_addresses,
&mut i,
&invoking_program,
&mut hashed_pubkeys,
)?;
bench_sbf_end!("cpda_append");
}
}
bench_sbf_start!("emit_state_transition_event");
bench_sbf_start!("emit_state_transition_event");
emit_state_transition_event(
inputs,
&ctx,
input_compressed_account_hashes,
output_compressed_account_hashes,
output_leaf_indices,
)?;
bench_sbf_end!("emit_state_transition_event");
Ok(())
}