forester_utils/
registry.rs

1use crate::address_merkle_tree_config::{get_address_bundle_config, get_state_bundle_config};
2use crate::create_account_instruction;
3use account_compression::{
4    AddressMerkleTreeConfig, AddressQueueConfig, NullifierQueueConfig, QueueAccount,
5    StateMerkleTreeConfig,
6};
7
8use crate::indexer::{AddressMerkleTreeAccounts, StateMerkleTreeAccounts};
9use light_client::rpc::{RpcConnection, RpcError};
10use light_registry::account_compression_cpi::sdk::{
11    create_rollover_state_merkle_tree_instruction, CreateRolloverMerkleTreeInstructionInputs,
12};
13use light_registry::protocol_config::state::ProtocolConfig;
14use light_registry::sdk::{
15    create_register_forester_instruction, create_update_forester_pda_instruction,
16};
17use light_registry::utils::get_forester_pda;
18use light_registry::{ForesterConfig, ForesterPda};
19use solana_sdk::{
20    instruction::Instruction,
21    pubkey::Pubkey,
22    signature::{Keypair, Signer},
23};
24
25/// Creates and asserts forester account creation.
26pub async fn register_test_forester<R: RpcConnection>(
27    rpc: &mut R,
28    governance_authority: &Keypair,
29    forester_authority: &Pubkey,
30    config: ForesterConfig,
31) -> Result<(), RpcError> {
32    let ix = create_register_forester_instruction(
33        &governance_authority.pubkey(),
34        &governance_authority.pubkey(),
35        forester_authority,
36        config,
37    );
38    rpc.create_and_send_transaction(
39        &[ix],
40        &governance_authority.pubkey(),
41        &[governance_authority],
42    )
43    .await?;
44    assert_registered_forester(
45        rpc,
46        forester_authority,
47        ForesterPda {
48            authority: *forester_authority,
49            config,
50            active_weight: 1,
51            ..Default::default()
52        },
53    )
54    .await
55}
56
57pub async fn update_test_forester<R: RpcConnection>(
58    rpc: &mut R,
59    forester_authority: &Keypair,
60    derivation_key: &Pubkey,
61    new_forester_authority: Option<&Keypair>,
62    config: ForesterConfig,
63) -> Result<(), RpcError> {
64    let mut pre_account_state = rpc
65        .get_anchor_account::<ForesterPda>(&get_forester_pda(derivation_key).0)
66        .await?
67        .unwrap();
68    let (signers, new_forester_authority) = if let Some(new_authority) = new_forester_authority {
69        pre_account_state.authority = new_authority.pubkey();
70
71        (
72            vec![forester_authority, &new_authority],
73            Some(new_authority.pubkey()),
74        )
75    } else {
76        (vec![forester_authority], None)
77    };
78    let ix = create_update_forester_pda_instruction(
79        &forester_authority.pubkey(),
80        derivation_key,
81        new_forester_authority,
82        Some(config),
83    );
84
85    rpc.create_and_send_transaction(&[ix], &forester_authority.pubkey(), &signers)
86        .await?;
87
88    pre_account_state.config = config;
89    assert_registered_forester(rpc, derivation_key, pre_account_state).await
90}
91
92pub async fn assert_registered_forester<R: RpcConnection>(
93    rpc: &mut R,
94    forester: &Pubkey,
95    expected_account: ForesterPda,
96) -> Result<(), RpcError> {
97    let pda = get_forester_pda(forester).0;
98    let account_data = rpc.get_anchor_account::<ForesterPda>(&pda).await?.unwrap();
99    if account_data != expected_account {
100        return Err(RpcError::AssertRpcError(format!(
101            "Expected account data: {:?}, got: {:?}",
102            expected_account, account_data
103        )));
104    }
105    Ok(())
106}
107
108pub struct RentExemption {
109    pub size: usize,
110    pub lamports: u64,
111}
112
113pub async fn get_rent_exemption_for_address_merkle_tree_and_queue<R: RpcConnection>(
114    rpc: &mut R,
115    address_merkle_tree_config: &AddressMerkleTreeConfig,
116    address_queue_config: &AddressQueueConfig,
117) -> (RentExemption, RentExemption) {
118    let queue_size = QueueAccount::size(address_queue_config.capacity as usize).unwrap();
119
120    let queue_rent_exempt_lamports = rpc
121        .get_minimum_balance_for_rent_exemption(queue_size)
122        .await
123        .unwrap();
124    let tree_size = account_compression::state::AddressMerkleTreeAccount::size(
125        address_merkle_tree_config.height as usize,
126        address_merkle_tree_config.changelog_size as usize,
127        address_merkle_tree_config.roots_size as usize,
128        address_merkle_tree_config.canopy_depth as usize,
129        address_merkle_tree_config.address_changelog_size as usize,
130    );
131    let merkle_tree_rent_exempt_lamports = rpc
132        .get_minimum_balance_for_rent_exemption(tree_size)
133        .await
134        .unwrap();
135    (
136        RentExemption {
137            lamports: merkle_tree_rent_exempt_lamports,
138            size: tree_size,
139        },
140        RentExemption {
141            lamports: queue_rent_exempt_lamports,
142            size: queue_size,
143        },
144    )
145}
146
147pub async fn get_rent_exemption_for_state_merkle_tree_and_queue<R: RpcConnection>(
148    rpc: &mut R,
149    merkle_tree_config: &StateMerkleTreeConfig,
150    queue_config: &NullifierQueueConfig,
151) -> (RentExemption, RentExemption) {
152    let queue_size = QueueAccount::size(queue_config.capacity as usize).unwrap();
153
154    let queue_rent_exempt_lamports = rpc
155        .get_minimum_balance_for_rent_exemption(queue_size)
156        .await
157        .unwrap();
158    let tree_size = account_compression::state::StateMerkleTreeAccount::size(
159        merkle_tree_config.height as usize,
160        merkle_tree_config.changelog_size as usize,
161        merkle_tree_config.roots_size as usize,
162        merkle_tree_config.canopy_depth as usize,
163    );
164    let merkle_tree_rent_exempt_lamports = rpc
165        .get_minimum_balance_for_rent_exemption(tree_size)
166        .await
167        .unwrap();
168    (
169        RentExemption {
170            lamports: merkle_tree_rent_exempt_lamports,
171            size: tree_size,
172        },
173        RentExemption {
174            lamports: queue_rent_exempt_lamports,
175            size: queue_size,
176        },
177    )
178}
179
180#[allow(clippy::too_many_arguments)]
181pub async fn create_rollover_address_merkle_tree_instructions<R: RpcConnection>(
182    rpc: &mut R,
183    authority: &Pubkey,
184    new_nullifier_queue_keypair: &Keypair,
185    new_address_merkle_tree_keypair: &Keypair,
186    merkle_tree_pubkey: &Pubkey,
187    nullifier_queue_pubkey: &Pubkey,
188    epoch: u64,
189    is_metadata_forester: bool,
190) -> Vec<Instruction> {
191    let (merkle_tree_config, queue_config) = get_address_bundle_config(
192        rpc,
193        AddressMerkleTreeAccounts {
194            merkle_tree: *merkle_tree_pubkey,
195            queue: *nullifier_queue_pubkey,
196        },
197    )
198    .await;
199    let (merkle_tree_rent_exemption, queue_rent_exemption) =
200        get_rent_exemption_for_address_merkle_tree_and_queue(
201            rpc,
202            &merkle_tree_config,
203            &queue_config,
204        )
205        .await;
206    let create_nullifier_queue_instruction = create_account_instruction(
207        authority,
208        queue_rent_exemption.size,
209        queue_rent_exemption.lamports,
210        &account_compression::ID,
211        Some(new_nullifier_queue_keypair),
212    );
213    let create_state_merkle_tree_instruction = create_account_instruction(
214        authority,
215        merkle_tree_rent_exemption.size,
216        merkle_tree_rent_exemption.lamports,
217        &account_compression::ID,
218        Some(new_address_merkle_tree_keypair),
219    );
220    let instruction = light_registry::account_compression_cpi::sdk::create_rollover_address_merkle_tree_instruction(
221        CreateRolloverMerkleTreeInstructionInputs {
222            authority: *authority,
223            new_queue: new_nullifier_queue_keypair.pubkey(),
224            new_merkle_tree: new_address_merkle_tree_keypair.pubkey(),
225            old_queue: *nullifier_queue_pubkey,
226            old_merkle_tree: *merkle_tree_pubkey,
227            cpi_context_account: None,
228            is_metadata_forester,
229        },epoch
230    );
231    vec![
232        create_nullifier_queue_instruction,
233        create_state_merkle_tree_instruction,
234        instruction,
235    ]
236}
237
238#[allow(clippy::too_many_arguments)]
239pub async fn perform_state_merkle_tree_roll_over<R: RpcConnection>(
240    rpc: &mut R,
241    authority: &Keypair,
242    new_nullifier_queue_keypair: &Keypair,
243    new_state_merkle_tree_keypair: &Keypair,
244    merkle_tree_pubkey: &Pubkey,
245    nullifier_queue_pubkey: &Pubkey,
246    epoch: u64,
247    is_metadata_forester: bool,
248) -> Result<(), RpcError> {
249    let instructions = create_rollover_address_merkle_tree_instructions(
250        rpc,
251        &authority.pubkey(),
252        new_nullifier_queue_keypair,
253        new_state_merkle_tree_keypair,
254        merkle_tree_pubkey,
255        nullifier_queue_pubkey,
256        epoch,
257        is_metadata_forester,
258    )
259    .await;
260    rpc.create_and_send_transaction(
261        &instructions,
262        &authority.pubkey(),
263        &[
264            authority,
265            new_nullifier_queue_keypair,
266            new_state_merkle_tree_keypair,
267        ],
268    )
269    .await?;
270    Ok(())
271}
272#[allow(clippy::too_many_arguments)]
273pub async fn create_rollover_state_merkle_tree_instructions<R: RpcConnection>(
274    rpc: &mut R,
275    authority: &Pubkey,
276    new_nullifier_queue_keypair: &Keypair,
277    new_state_merkle_tree_keypair: &Keypair,
278    new_cpi_context_keypair: &Keypair,
279    merkle_tree_pubkey: &Pubkey,
280    nullifier_queue_pubkey: &Pubkey,
281    epoch: u64,
282    is_metadata_forester: bool,
283) -> Vec<Instruction> {
284    let (merkle_tree_config, queue_config) = get_state_bundle_config(
285        rpc,
286        StateMerkleTreeAccounts {
287            merkle_tree: *merkle_tree_pubkey,
288            nullifier_queue: *nullifier_queue_pubkey,
289            cpi_context: new_cpi_context_keypair.pubkey(),
290        },
291    )
292    .await;
293    let (state_merkle_tree_rent_exemption, queue_rent_exemption) =
294        get_rent_exemption_for_state_merkle_tree_and_queue(rpc, &merkle_tree_config, &queue_config)
295            .await;
296    let create_nullifier_queue_instruction = create_account_instruction(
297        authority,
298        queue_rent_exemption.size,
299        queue_rent_exemption.lamports,
300        &account_compression::ID,
301        Some(new_nullifier_queue_keypair),
302    );
303    let create_state_merkle_tree_instruction = create_account_instruction(
304        authority,
305        state_merkle_tree_rent_exemption.size,
306        state_merkle_tree_rent_exemption.lamports,
307        &account_compression::ID,
308        Some(new_state_merkle_tree_keypair),
309    );
310    let account_size: usize = ProtocolConfig::default().cpi_context_size as usize;
311    let create_cpi_context_account_instruction = create_account_instruction(
312        authority,
313        account_size,
314        rpc.get_minimum_balance_for_rent_exemption(account_size)
315            .await
316            .unwrap(),
317        &light_system_program::ID,
318        Some(new_cpi_context_keypair),
319    );
320    let instruction = create_rollover_state_merkle_tree_instruction(
321        CreateRolloverMerkleTreeInstructionInputs {
322            authority: *authority,
323            new_queue: new_nullifier_queue_keypair.pubkey(),
324            new_merkle_tree: new_state_merkle_tree_keypair.pubkey(),
325            old_queue: *nullifier_queue_pubkey,
326            old_merkle_tree: *merkle_tree_pubkey,
327            cpi_context_account: Some(new_cpi_context_keypair.pubkey()),
328            is_metadata_forester,
329        },
330        epoch,
331    );
332    vec![
333        create_nullifier_queue_instruction,
334        create_state_merkle_tree_instruction,
335        create_cpi_context_account_instruction,
336        instruction,
337    ]
338}