light_merkle_tree_program/utils/
accounts.rs

1use std::convert::TryInto;
2
3use anchor_lang::{
4    err,
5    prelude::AccountLoader,
6    solana_program::{
7        account_info::AccountInfo, msg, program::invoke_signed, program_error::ProgramError,
8        pubkey::Pubkey, system_instruction, sysvar::rent::Rent,
9    },
10    Key, Owner, ZeroCopy,
11};
12
13use crate::{errors::ErrorCode, indexed_merkle_tree::IndexedMerkleTree};
14
15#[allow(clippy::too_many_arguments)]
16pub fn create_and_check_pda<'a, 'b>(
17    program_id: &Pubkey,
18    signer_account: &'a AccountInfo<'b>,
19    passed_in_pda: &'a AccountInfo<'b>,
20    system_program: &'a AccountInfo<'b>,
21    rent: &Rent,
22    _instruction_data: &[u8],
23    domain_separation_seed: &[u8],
24    number_storage_bytes: u64,
25    lamports: u64,
26    rent_exempt: bool,
27) -> Result<(), ProgramError> {
28    let derived_pubkey =
29        Pubkey::find_program_address(&[_instruction_data, domain_separation_seed], program_id);
30
31    if derived_pubkey.0 != *passed_in_pda.key {
32        msg!("Passed-in pda pubkey != on-chain derived pda pubkey.");
33        msg!("On-chain derived pda pubkey {:?}", derived_pubkey);
34        msg!("Passed-in pda pubkey {:?}", *passed_in_pda.key);
35        msg!("Instruction data seed  {:?}", _instruction_data);
36        return Err(ProgramError::InvalidInstructionData);
37    }
38
39    let mut account_lamports = lamports;
40    if rent_exempt {
41        account_lamports += rent.minimum_balance(number_storage_bytes.try_into().unwrap());
42    }
43
44    invoke_signed(
45        &system_instruction::create_account(
46            signer_account.key,   // from_pubkey
47            passed_in_pda.key,    // to_pubkey
48            account_lamports,     // lamports
49            number_storage_bytes, // space
50            program_id,           // owner
51        ),
52        &[
53            signer_account.clone(),
54            passed_in_pda.clone(),
55            system_program.clone(),
56        ],
57        &[&[
58            _instruction_data,
59            domain_separation_seed,
60            &[derived_pubkey.1],
61        ]],
62    )?;
63
64    // Check for rent exemption
65    if rent_exempt
66        && !rent.is_exempt(
67            **passed_in_pda.lamports.borrow(),
68            number_storage_bytes.try_into().unwrap(),
69        )
70    {
71        msg!("Account is not rent exempt.");
72        return Err(ProgramError::AccountNotRentExempt);
73    }
74    Ok(())
75}
76
77/// Validates the old Merkle tree account. If valid, it also checks whether
78/// it's the currently newest one, then afterwards marks it as not the newest.
79/// Should be called only when initializing new Merkle trees.
80pub fn deserialize_and_update_old_merkle_tree<T>(
81    account: &AccountInfo,
82    seed: &[u8],
83    program_id: &Pubkey,
84) -> anchor_lang::Result<()>
85where
86    T: IndexedMerkleTree + ZeroCopy + Owner,
87{
88    let loader: AccountLoader<T> = AccountLoader::try_from(account)?;
89    let pubkey = loader.key();
90    let mut merkle_tree = loader.load_mut()?;
91    let index = merkle_tree.get_index();
92
93    let (expected_pubkey, _) =
94        Pubkey::find_program_address(&[seed, index.to_le_bytes().as_ref()], program_id);
95    if pubkey != expected_pubkey {
96        return err!(ErrorCode::InvalidOldMerkleTree);
97    }
98
99    if !merkle_tree.is_newest() {
100        return err!(ErrorCode::NotNewestOldMerkleTree);
101    }
102    merkle_tree.set_newest(false);
103
104    Ok(())
105}