light_registry/
lib.rs

1#![allow(clippy::too_many_arguments)]
2use account_compression::{
3    utils::constants::CPI_AUTHORITY_PDA_SEED, AddressMerkleTreeConfig, AddressQueueConfig,
4    NullifierQueueConfig, StateMerkleTreeConfig,
5};
6use anchor_lang::prelude::*;
7use light_merkle_tree_metadata::merkle_tree::MerkleTreeMetadata;
8
9pub mod account_compression_cpi;
10pub mod errors;
11pub use account_compression_cpi::{
12    batch_append::*, batch_nullify::*, batch_update_address_tree::*,
13    initialize_batched_address_tree::*, initialize_batched_state_tree::*,
14    initialize_tree_and_queue::*, migrate_state::*, nullify::*, register_program::*,
15    rollover_batched_address_tree::*, rollover_batched_state_tree::*, rollover_state_tree::*,
16    update_address_tree::*,
17};
18pub use protocol_config::{initialize::*, update::*};
19
20pub use crate::epoch::{finalize_registration::*, register_epoch::*, report_work::*};
21pub mod constants;
22pub mod epoch;
23pub mod protocol_config;
24pub mod selection;
25pub mod utils;
26use account_compression::MigrateLeafParams;
27use anchor_lang::solana_program::pubkey::Pubkey;
28use errors::RegistryError;
29use light_batched_merkle_tree::{
30    initialize_address_tree::InitAddressTreeAccountsInstructionData,
31    initialize_state_tree::InitStateTreeAccountsInstructionData,
32    merkle_tree::BatchedMerkleTreeAccount, queue::BatchedQueueAccount,
33};
34use protocol_config::state::ProtocolConfig;
35pub use selection::forester::*;
36#[cfg(not(target_os = "solana"))]
37pub mod sdk;
38
39#[cfg(not(feature = "no-entrypoint"))]
40solana_security_txt::security_txt! {
41    name: "light-registry",
42    project_url: "lightprotocol.com",
43    contacts: "email:security@lightprotocol.com",
44    policy: "https://github.com/Lightprotocol/light-protocol/blob/main/SECURITY.md",
45    source_code: "https://github.com/Lightprotocol/light-protocol"
46}
47
48declare_id!("Lighton6oQpVkeewmo2mcPTQQp7kYHr4fWpAgJyEmDX");
49
50#[program]
51pub mod light_registry {
52
53    use constants::DEFAULT_WORK_V1;
54
55    use super::*;
56
57    /// Initializes the protocol config pda. Can only be called once by the
58    /// program account keypair.
59    pub fn initialize_protocol_config(
60        ctx: Context<InitializeProtocolConfig>,
61        bump: u8,
62        protocol_config: ProtocolConfig,
63    ) -> Result<()> {
64        ctx.accounts.protocol_config_pda.authority = ctx.accounts.authority.key();
65        ctx.accounts.protocol_config_pda.bump = bump;
66        check_protocol_config(protocol_config)?;
67        ctx.accounts.protocol_config_pda.config = protocol_config;
68        Ok(())
69    }
70
71    pub fn update_protocol_config(
72        ctx: Context<UpdateProtocolConfig>,
73        protocol_config: Option<ProtocolConfig>,
74    ) -> Result<()> {
75        if let Some(new_authority) = ctx.accounts.new_authority.as_ref() {
76            ctx.accounts.protocol_config_pda.authority = new_authority.key();
77        }
78        if let Some(protocol_config) = protocol_config {
79            if protocol_config.genesis_slot != ctx.accounts.protocol_config_pda.config.genesis_slot
80            {
81                msg!("Genesis slot cannot be changed.");
82                return err!(RegistryError::InvalidConfigUpdate);
83            }
84            if protocol_config.active_phase_length
85                != ctx.accounts.protocol_config_pda.config.active_phase_length
86            {
87                msg!(
88                    "Active phase length must not be changed, otherwise epochs will repeat {} {}.",
89                    protocol_config.active_phase_length,
90                    ctx.accounts.protocol_config_pda.config.active_phase_length
91                );
92                return err!(RegistryError::InvalidConfigUpdate);
93            }
94            check_protocol_config(protocol_config)?;
95            ctx.accounts.protocol_config_pda.config = protocol_config;
96        }
97        Ok(())
98    }
99
100    pub fn register_system_program(ctx: Context<RegisterProgram>, bump: u8) -> Result<()> {
101        let bump = &[bump];
102        let seeds = [CPI_AUTHORITY_PDA_SEED, bump];
103        let signer_seeds = &[&seeds[..]];
104
105        let accounts = account_compression::cpi::accounts::RegisterProgramToGroup {
106            authority: ctx.accounts.cpi_authority.to_account_info(),
107            program_to_be_registered: ctx.accounts.program_to_be_registered.to_account_info(),
108            system_program: ctx.accounts.system_program.to_account_info(),
109            registered_program_pda: ctx.accounts.registered_program_pda.to_account_info(),
110            group_authority_pda: ctx.accounts.group_pda.to_account_info(),
111        };
112
113        let cpi_ctx = CpiContext::new_with_signer(
114            ctx.accounts.account_compression_program.to_account_info(),
115            accounts,
116            signer_seeds,
117        );
118
119        account_compression::cpi::register_program_to_group(cpi_ctx)
120    }
121
122    pub fn deregister_system_program(ctx: Context<DeregisterProgram>, bump: u8) -> Result<()> {
123        let bump = &[bump];
124        let seeds = [CPI_AUTHORITY_PDA_SEED, bump];
125        let signer_seeds = &[&seeds[..]];
126
127        let accounts = account_compression::cpi::accounts::DeregisterProgram {
128            authority: ctx.accounts.cpi_authority.to_account_info(),
129            registered_program_pda: ctx.accounts.registered_program_pda.to_account_info(),
130            group_authority_pda: ctx.accounts.group_pda.to_account_info(),
131            close_recipient: ctx.accounts.authority.to_account_info(),
132        };
133
134        let cpi_ctx = CpiContext::new_with_signer(
135            ctx.accounts.account_compression_program.to_account_info(),
136            accounts,
137            signer_seeds,
138        );
139
140        account_compression::cpi::deregister_program(cpi_ctx)
141    }
142
143    pub fn register_forester(
144        ctx: Context<RegisterForester>,
145        _bump: u8,
146        authority: Pubkey,
147        config: ForesterConfig,
148        weight: Option<u64>,
149    ) -> Result<()> {
150        ctx.accounts.forester_pda.authority = authority;
151        ctx.accounts.forester_pda.config = config;
152
153        if let Some(weight) = weight {
154            ctx.accounts.forester_pda.active_weight = weight;
155        }
156        Ok(())
157    }
158
159    pub fn update_forester_pda(
160        ctx: Context<UpdateForesterPda>,
161        config: Option<ForesterConfig>,
162    ) -> Result<()> {
163        if let Some(authority) = ctx.accounts.new_authority.as_ref() {
164            ctx.accounts.forester_pda.authority = authority.key();
165        }
166        if let Some(config) = config {
167            ctx.accounts.forester_pda.config = config;
168        }
169        Ok(())
170    }
171
172    pub fn update_forester_pda_weight(
173        ctx: Context<UpdateForesterPdaWeight>,
174        new_weight: u64,
175    ) -> Result<()> {
176        ctx.accounts.forester_pda.active_weight = new_weight;
177        Ok(())
178    }
179
180    /// Registers the forester for the epoch.
181    /// 1. Only the forester can register herself for the epoch.
182    /// 2. Protocol config is copied.
183    /// 3. Epoch account is created if needed.
184    pub fn register_forester_epoch<'info>(
185        ctx: Context<'_, '_, '_, 'info, RegisterForesterEpoch<'info>>,
186        epoch: u64,
187    ) -> Result<()> {
188        // Only init if not initialized
189        if ctx.accounts.epoch_pda.registered_weight == 0 {
190            (*ctx.accounts.epoch_pda).clone_from(&EpochPda {
191                epoch,
192                protocol_config: ctx.accounts.protocol_config.config,
193                total_work: 0,
194                registered_weight: 0,
195            });
196        }
197        let current_solana_slot = anchor_lang::solana_program::clock::Clock::get()?.slot;
198        // Init epoch account if not initialized
199        let current_epoch = ctx
200            .accounts
201            .epoch_pda
202            .protocol_config
203            .get_latest_register_epoch(current_solana_slot)?;
204
205        if current_epoch != epoch {
206            return err!(RegistryError::InvalidEpoch);
207        }
208        // check that epoch is in registration phase is in process_register_for_epoch
209        process_register_for_epoch(
210            &ctx.accounts.authority.key(),
211            &mut ctx.accounts.forester_pda,
212            &mut ctx.accounts.forester_epoch_pda,
213            &mut ctx.accounts.epoch_pda,
214            current_solana_slot,
215        )?;
216        Ok(())
217    }
218
219    /// This transaction can be included as additional instruction in the first
220    /// work instructions during the active phase.
221    /// Registration Period must be over.
222    pub fn finalize_registration<'info>(
223        ctx: Context<'_, '_, '_, 'info, FinalizeRegistration<'info>>,
224    ) -> Result<()> {
225        let current_solana_slot = anchor_lang::solana_program::clock::Clock::get()?.slot;
226        let current_active_epoch = ctx
227            .accounts
228            .epoch_pda
229            .protocol_config
230            .get_current_active_epoch(current_solana_slot)?;
231        if current_active_epoch != ctx.accounts.epoch_pda.epoch {
232            return err!(RegistryError::InvalidEpoch);
233        }
234        ctx.accounts.forester_epoch_pda.total_epoch_weight =
235            Some(ctx.accounts.epoch_pda.registered_weight);
236        ctx.accounts.forester_epoch_pda.finalize_counter += 1;
237        // Check limit for finalize counter to throw if exceeded
238        // Is a safeguard so that noone can block parallelism.
239        // This instruction can be passed with nullify instructions, to prevent
240        // read locking the epoch account for more than X transactions limit
241        // the number of syncs without failing the tx to X
242        if ctx.accounts.forester_epoch_pda.finalize_counter
243            > ctx
244                .accounts
245                .forester_epoch_pda
246                .protocol_config
247                .finalize_counter_limit
248        {
249            return err!(RegistryError::FinalizeCounterExceeded);
250        }
251
252        Ok(())
253    }
254
255    pub fn report_work<'info>(ctx: Context<'_, '_, '_, 'info, ReportWork<'info>>) -> Result<()> {
256        let current_solana_slot = anchor_lang::solana_program::clock::Clock::get()?.slot;
257        ctx.accounts
258            .epoch_pda
259            .protocol_config
260            .is_report_work_phase(current_solana_slot, ctx.accounts.epoch_pda.epoch)?;
261        if ctx.accounts.epoch_pda.epoch != ctx.accounts.forester_epoch_pda.epoch {
262            return err!(RegistryError::InvalidEpoch);
263        }
264        if ctx.accounts.forester_epoch_pda.has_reported_work {
265            return err!(RegistryError::ForesterAlreadyReportedWork);
266        }
267        ctx.accounts.epoch_pda.total_work += ctx.accounts.forester_epoch_pda.work_counter;
268        ctx.accounts.forester_epoch_pda.has_reported_work = true;
269        Ok(())
270    }
271
272    pub fn initialize_address_merkle_tree(
273        ctx: Context<InitializeMerkleTreeAndQueue>,
274        bump: u8,
275        program_owner: Option<Pubkey>,
276        forester: Option<Pubkey>,
277        merkle_tree_config: AddressMerkleTreeConfig,
278        queue_config: AddressQueueConfig,
279    ) -> Result<()> {
280        // The network fee must be either zero or the same as the protocol config.
281        // Only trees with a network fee will be serviced by light foresters.
282        if let Some(network_fee) = merkle_tree_config.network_fee {
283            if network_fee != ctx.accounts.protocol_config_pda.config.network_fee {
284                return err!(RegistryError::InvalidNetworkFee);
285            }
286            if forester.is_some() {
287                msg!("Forester pubkey must not be defined for trees serviced by light foresters.");
288                return err!(RegistryError::ForesterDefined);
289            }
290        } else if forester.is_none() {
291            msg!("Forester pubkey required for trees without a network fee.");
292            msg!("Trees without a network fee will not be serviced by light foresters.");
293            return err!(RegistryError::ForesterUndefined);
294        }
295        // Unused parameter
296        if queue_config.network_fee.is_some() {
297            return err!(RegistryError::InvalidNetworkFee);
298        }
299        process_initialize_address_merkle_tree(
300            ctx,
301            bump,
302            0,
303            program_owner,
304            forester,
305            merkle_tree_config,
306            queue_config,
307        )
308    }
309
310    pub fn initialize_state_merkle_tree(
311        ctx: Context<InitializeMerkleTreeAndQueue>,
312        bump: u8,
313        program_owner: Option<Pubkey>,
314        forester: Option<Pubkey>,
315        merkle_tree_config: StateMerkleTreeConfig,
316        queue_config: NullifierQueueConfig,
317    ) -> Result<()> {
318        // The network fee must be either zero or the same as the protocol config.
319        // Only trees with a network fee will be serviced by light foresters.
320        if let Some(network_fee) = merkle_tree_config.network_fee {
321            if network_fee != ctx.accounts.protocol_config_pda.config.network_fee {
322                return err!(RegistryError::InvalidNetworkFee);
323            }
324        } else if forester.is_none() {
325            msg!("Forester pubkey required for trees without a network fee.");
326            msg!("Trees without a network fee will not be serviced by light foresters.");
327            return err!(RegistryError::ForesterUndefined);
328        }
329
330        // Unused parameter
331        if queue_config.network_fee.is_some() {
332            return err!(RegistryError::InvalidNetworkFee);
333        }
334        check_cpi_context(
335            ctx.accounts
336                .cpi_context_account
337                .as_ref()
338                .unwrap()
339                .to_account_info(),
340            &ctx.accounts.protocol_config_pda.config,
341        )?;
342        process_initialize_state_merkle_tree(
343            &ctx,
344            bump,
345            0,
346            program_owner,
347            forester,
348            merkle_tree_config,
349            queue_config,
350        )?;
351
352        process_initialize_cpi_context(
353            bump,
354            ctx.accounts.authority.to_account_info(),
355            ctx.accounts
356                .cpi_context_account
357                .as_ref()
358                .unwrap()
359                .to_account_info(),
360            ctx.accounts.merkle_tree.to_account_info(),
361            ctx.accounts
362                .light_system_program
363                .as_ref()
364                .unwrap()
365                .to_account_info(),
366        )
367    }
368
369    pub fn nullify<'info>(
370        ctx: Context<'_, '_, '_, 'info, NullifyLeaves<'info>>,
371        bump: u8,
372        change_log_indices: Vec<u64>,
373        leaves_queue_indices: Vec<u16>,
374        indices: Vec<u64>,
375        proofs: Vec<Vec<[u8; 32]>>,
376    ) -> Result<()> {
377        let metadata = ctx.accounts.merkle_tree.load()?.metadata;
378        check_forester(
379            &metadata,
380            ctx.accounts.authority.key(),
381            ctx.accounts.nullifier_queue.key(),
382            &mut ctx.accounts.registered_forester_pda,
383            DEFAULT_WORK_V1,
384        )?;
385
386        process_nullify(
387            &ctx,
388            bump,
389            change_log_indices,
390            leaves_queue_indices,
391            indices,
392            proofs,
393        )
394    }
395
396    #[allow(clippy::too_many_arguments)]
397    pub fn update_address_merkle_tree(
398        ctx: Context<UpdateAddressMerkleTree>,
399        bump: u8,
400        changelog_index: u16,
401        indexed_changelog_index: u16,
402        value: u16,
403        low_address_index: u64,
404        low_address_value: [u8; 32],
405        low_address_next_index: u64,
406        low_address_next_value: [u8; 32],
407        low_address_proof: [[u8; 32]; 16],
408    ) -> Result<()> {
409        let metadata = ctx.accounts.merkle_tree.load()?.metadata;
410
411        check_forester(
412            &metadata,
413            ctx.accounts.authority.key(),
414            ctx.accounts.queue.key(),
415            &mut ctx.accounts.registered_forester_pda,
416            DEFAULT_WORK_V1,
417        )?;
418        process_update_address_merkle_tree(
419            &ctx,
420            bump,
421            changelog_index,
422            indexed_changelog_index,
423            value,
424            low_address_index,
425            low_address_value,
426            low_address_next_index,
427            low_address_next_value,
428            low_address_proof,
429        )
430    }
431
432    pub fn rollover_address_merkle_tree_and_queue<'info>(
433        ctx: Context<'_, '_, '_, 'info, RolloverAddressMerkleTreeAndQueue<'info>>,
434        bump: u8,
435    ) -> Result<()> {
436        let metadata = ctx.accounts.old_merkle_tree.load()?.metadata;
437        check_forester(
438            &metadata,
439            ctx.accounts.authority.key(),
440            ctx.accounts.old_queue.key(),
441            &mut ctx.accounts.registered_forester_pda,
442            DEFAULT_WORK_V1,
443        )?;
444
445        process_rollover_address_merkle_tree_and_queue(&ctx, bump)
446    }
447
448    pub fn rollover_state_merkle_tree_and_queue<'info>(
449        ctx: Context<'_, '_, '_, 'info, RolloverStateMerkleTreeAndQueue<'info>>,
450        bump: u8,
451    ) -> Result<()> {
452        let metadata = ctx.accounts.old_merkle_tree.load()?.metadata;
453        check_forester(
454            &metadata,
455            ctx.accounts.authority.key(),
456            ctx.accounts.old_queue.key(),
457            &mut ctx.accounts.registered_forester_pda,
458            DEFAULT_WORK_V1,
459        )?;
460
461        check_cpi_context(
462            ctx.accounts.cpi_context_account.to_account_info(),
463            &ctx.accounts.protocol_config_pda.config,
464        )?;
465        process_rollover_state_merkle_tree_and_queue(&ctx, bump)?;
466        process_initialize_cpi_context(
467            bump,
468            ctx.accounts.authority.to_account_info(),
469            ctx.accounts.cpi_context_account.to_account_info(),
470            ctx.accounts.new_merkle_tree.to_account_info(),
471            ctx.accounts.light_system_program.to_account_info(),
472        )
473    }
474
475    pub fn initialize_batched_state_merkle_tree<'info>(
476        ctx: Context<'_, '_, '_, 'info, InitializeBatchedStateMerkleTreeAndQueue<'info>>,
477        bump: u8,
478        params: Vec<u8>,
479    ) -> Result<()> {
480        let params = InitStateTreeAccountsInstructionData::try_from_slice(&params)?;
481        if let Some(network_fee) = params.network_fee {
482            if network_fee != ctx.accounts.protocol_config_pda.config.network_fee {
483                return err!(RegistryError::InvalidNetworkFee);
484            }
485            if params.forester.is_some() {
486                msg!("Forester pubkey must not be defined for trees serviced by light foresters.");
487                return err!(RegistryError::ForesterDefined);
488            }
489        } else if params.forester.is_none() {
490            msg!("Forester pubkey required for trees without a network fee.");
491            msg!("Trees without a network fee will not be serviced by light foresters.");
492            return err!(RegistryError::ForesterUndefined);
493        }
494        check_cpi_context(
495            ctx.accounts.cpi_context_account.to_account_info(),
496            &ctx.accounts.protocol_config_pda.config,
497        )?;
498
499        process_initialize_batched_state_merkle_tree(&ctx, bump, params.try_to_vec().unwrap())?;
500
501        process_initialize_cpi_context(
502            bump,
503            ctx.accounts.authority.to_account_info(),
504            ctx.accounts.cpi_context_account.to_account_info(),
505            ctx.accounts.merkle_tree.to_account_info(),
506            ctx.accounts.light_system_program.to_account_info(),
507        )
508    }
509
510    pub fn batch_nullify<'info>(
511        ctx: Context<'_, '_, '_, 'info, BatchNullify<'info>>,
512        bump: u8,
513        data: Vec<u8>,
514    ) -> Result<()> {
515        let merkle_tree =
516            BatchedMerkleTreeAccount::state_from_account_info(&ctx.accounts.merkle_tree)
517                .map_err(ProgramError::from)?;
518        check_forester(
519            &merkle_tree.metadata,
520            ctx.accounts.authority.key(),
521            ctx.accounts.merkle_tree.key(),
522            &mut ctx.accounts.registered_forester_pda,
523            // Reward for performed work is input queue batch size.
524            merkle_tree.queue_batches.batch_size,
525        )?;
526
527        process_batch_nullify(&ctx, bump, data)
528    }
529
530    pub fn batch_append<'info>(
531        ctx: Context<'_, '_, '_, 'info, BatchAppend<'info>>,
532        bump: u8,
533        data: Vec<u8>,
534    ) -> Result<()> {
535        let queue_account =
536            BatchedQueueAccount::output_from_account_info(&ctx.accounts.output_queue)
537                .map_err(ProgramError::from)?;
538        let merkle_tree =
539            BatchedMerkleTreeAccount::state_from_account_info(&ctx.accounts.merkle_tree)
540                .map_err(ProgramError::from)?;
541        // Eligibility is checked for the Merkle tree,
542        // so that the same forester is eligible to
543        // batch append and batch nullify of the same tree.
544        check_forester(
545            &merkle_tree.metadata,
546            ctx.accounts.authority.key(),
547            ctx.accounts.merkle_tree.key(),
548            &mut ctx.accounts.registered_forester_pda,
549            // Reward for performed work is output queue batch size.
550            queue_account.batch_metadata.batch_size,
551        )?;
552        process_batch_append(&ctx, bump, data)
553    }
554
555    pub fn initialize_batched_address_merkle_tree(
556        ctx: Context<InitializeBatchedAddressTree>,
557        bump: u8,
558        params: Vec<u8>,
559    ) -> Result<()> {
560        let params = InitAddressTreeAccountsInstructionData::try_from_slice(&params)?;
561        if let Some(network_fee) = params.network_fee {
562            if network_fee != ctx.accounts.protocol_config_pda.config.network_fee {
563                return err!(RegistryError::InvalidNetworkFee);
564            }
565            if params.forester.is_some() {
566                msg!("Forester pubkey must not be defined for trees serviced by light foresters.");
567                return err!(RegistryError::ForesterDefined);
568            }
569        } else if params.forester.is_none() {
570            msg!("Forester pubkey required for trees without a network fee.");
571            msg!("Trees without a network fee will not be serviced by light foresters.");
572            return err!(RegistryError::ForesterUndefined);
573        }
574        process_initialize_batched_address_merkle_tree(&ctx, bump, params.try_to_vec()?)
575    }
576
577    pub fn batch_update_address_tree<'info>(
578        ctx: Context<'_, '_, '_, 'info, BatchUpdateAddressTree<'info>>,
579        bump: u8,
580        data: Vec<u8>,
581    ) -> Result<()> {
582        let account =
583            BatchedMerkleTreeAccount::address_from_account_info(&ctx.accounts.merkle_tree)
584                .map_err(ProgramError::from)?;
585        check_forester(
586            &account.metadata,
587            ctx.accounts.authority.key(),
588            ctx.accounts.merkle_tree.key(),
589            &mut ctx.accounts.registered_forester_pda,
590            account.queue_batches.batch_size,
591        )?;
592
593        process_batch_update_address_tree(&ctx, bump, data)
594    }
595
596    pub fn rollover_batched_address_merkle_tree<'info>(
597        ctx: Context<'_, '_, '_, 'info, RolloverBatchedAddressMerkleTree<'info>>,
598        bump: u8,
599    ) -> Result<()> {
600        let account = BatchedMerkleTreeAccount::address_from_account_info(
601            &ctx.accounts.old_address_merkle_tree,
602        )
603        .map_err(ProgramError::from)?;
604        check_forester(
605            &account.metadata,
606            ctx.accounts.authority.key(),
607            ctx.accounts.old_address_merkle_tree.key(),
608            &mut ctx.accounts.registered_forester_pda,
609            DEFAULT_WORK_V1,
610        )?;
611        process_rollover_batched_address_merkle_tree(&ctx, bump)
612    }
613
614    pub fn rollover_batched_state_merkle_tree<'info>(
615        ctx: Context<'_, '_, '_, 'info, RolloverBatchedStateMerkleTree<'info>>,
616        bump: u8,
617    ) -> Result<()> {
618        let account =
619            BatchedMerkleTreeAccount::state_from_account_info(&ctx.accounts.old_state_merkle_tree)
620                .map_err(ProgramError::from)?;
621        check_forester(
622            &account.metadata,
623            ctx.accounts.authority.key(),
624            ctx.accounts.old_state_merkle_tree.key(),
625            &mut ctx.accounts.registered_forester_pda,
626            DEFAULT_WORK_V1,
627        )?;
628        check_cpi_context(
629            ctx.accounts.cpi_context_account.to_account_info(),
630            &ctx.accounts.protocol_config_pda.config,
631        )?;
632
633        process_rollover_batched_state_merkle_tree(&ctx, bump)?;
634
635        process_initialize_cpi_context(
636            bump,
637            ctx.accounts.authority.to_account_info(),
638            ctx.accounts.cpi_context_account.to_account_info(),
639            ctx.accounts.new_state_merkle_tree.to_account_info(),
640            ctx.accounts.light_system_program.to_account_info(),
641        )
642    }
643
644    pub fn migrate_state<'info>(
645        ctx: Context<'_, '_, '_, 'info, MigrateState<'info>>,
646        bump: u8,
647        inputs: MigrateLeafParams,
648    ) -> Result<()> {
649        check_forester(
650            &ctx.accounts.merkle_tree.load()?.metadata,
651            ctx.accounts.authority.key(),
652            ctx.accounts.merkle_tree.key(),
653            &mut Some(ctx.accounts.registered_forester_pda.clone()),
654            DEFAULT_WORK_V1,
655        )?;
656        process_migrate_state(&ctx, bump, inputs)
657    }
658}
659
660/// if registered_forester_pda is not None check forester eligibility and network_fee is not 0
661/// if metadata.forester == authority can forest
662/// else throw error
663pub fn check_forester(
664    metadata: &MerkleTreeMetadata,
665    authority: Pubkey,
666    queue: Pubkey,
667    registered_forester_pda: &mut Option<Account<'_, ForesterEpochPda>>,
668    num_work_items: u64,
669) -> Result<()> {
670    if let Some(forester_pda) = registered_forester_pda.as_mut() {
671        // Checks forester:
672        // - signer
673        // - eligibility
674        // - increments work counter
675        ForesterEpochPda::check_forester_in_program(
676            forester_pda,
677            &authority,
678            &queue,
679            num_work_items,
680        )?;
681        if metadata.rollover_metadata.network_fee == 0 {
682            return err!(RegistryError::InvalidNetworkFee);
683        }
684        Ok(())
685    } else if metadata.access_metadata.forester == authority {
686        Ok(())
687    } else {
688        err!(RegistryError::InvalidSigner)
689    }
690}