clockwork_network/instructions/
pools_rotate.rs1use {
2 crate::{
3 errors::ClockworkError,
4 state::*
5 },
6 anchor_lang::prelude::*,
7 clockwork_pool::cpi::accounts::PoolRotate
8};
9
10#[derive(Accounts)]
11pub struct PoolsRotate<'info> {
12 #[account(seeds = [SEED_CONFIG], bump)]
13 pub config: Account<'info, Config>,
14
15 #[account(
16 seeds = [
17 SEED_SNAPSHOT_ENTRY,
18 entry.snapshot.as_ref(),
19 entry.id.to_be_bytes().as_ref(),
20 ],
21 bump,
22 has_one = snapshot,
23 has_one = worker,
24 constraint = is_valid_entry(&entry, &rotator, &snapshot).unwrap() @ ClockworkError::InvalidSnapshotEntry,
25 )]
26 pub entry: Account<'info, SnapshotEntry>,
27
28 #[account(seeds = [SEED_NODE, node.id.to_be_bytes().as_ref()], bump, constraint = node.id == entry.id)]
29 pub node: Account<'info, Node>,
30
31 #[account(address = clockwork_pool::ID)]
32 pub pool_program: Program<'info, clockwork_pool::program::ClockworkPool>,
33
34 #[account(seeds = [SEED_CONFIG], bump, seeds::program = clockwork_pool::ID)]
35 pub pool_program_config: Account<'info, clockwork_pool::state::Config>,
36
37 #[account(
38 mut, seeds = [SEED_ROTATOR], bump,
39 constraint = Clock::get().unwrap().slot >= rotator.last_rotation_at.checked_add(config.slots_per_rotation).unwrap()
40 )]
41 pub rotator: Account<'info, Rotator>,
42
43 #[account()]
44 pub signer: Signer<'info>,
45
46 #[account(
47 seeds = [SEED_SNAPSHOT, snapshot.id.to_be_bytes().as_ref()], bump,
48 constraint = snapshot.status == SnapshotStatus::Current @ ClockworkError::SnapshotNotCurrent
49 )]
50 pub snapshot: Account<'info, Snapshot>,
51
52 #[account()]
53 pub worker: SystemAccount<'info>,
54}
55
56pub fn handler<'info>(ctx: Context<'_, '_, '_, 'info, PoolsRotate<'info>>) -> Result<()> {
57 let node = &ctx.accounts.node;
59 let pool_program = &ctx.accounts.pool_program;
60 let pool_program_config = &ctx.accounts.pool_program_config;
61 let rotator = &mut ctx.accounts.rotator;
62 let worker = &ctx.accounts.worker;
63
64 require!(rotator.pool_pubkeys.len() == ctx.remaining_accounts.len(), ClockworkError::InvalidPool);
66
67 let rotator_bump = *ctx.bumps.get("rotator").unwrap();
69 for i in 0..ctx.remaining_accounts.len() {
70 match ctx.remaining_accounts.get(i) {
71 None => return Err(ClockworkError::InvalidPool.into()),
72 Some(pool_acc_info) => {
73
74 require!(pool_acc_info.key().eq(rotator.pool_pubkeys.get(i).unwrap()), ClockworkError::InvalidPool);
76
77 if node.supported_pools.contains(&pool_acc_info.key()) {
79 clockwork_pool::cpi::pool_rotate(
80 CpiContext::new_with_signer(
81 pool_program.to_account_info(),
82 PoolRotate {
83 config: pool_program_config.to_account_info(),
84 pool: pool_acc_info.clone(),
85 pool_authority: rotator.to_account_info(),
86 worker: worker.to_account_info(),
87 },
88 &[&[SEED_ROTATOR, &[rotator_bump]]],
89 ),
90 )?;
91 }
92 }
93 }
94 }
95
96 rotator.hash_nonce()?;
98
99 Ok(())
100}
101
102fn is_valid_entry(
103 entry: &Account<SnapshotEntry>,
104 rotator: &Account<Rotator>,
105 snapshot: &Account<Snapshot>,
106) -> Result<bool> {
107 match rotator.nonce.checked_rem(snapshot.stake_total) {
109 None => Ok(false),
110 Some(sample) => Ok(sample >= entry.stake_offset
111 && sample < entry.stake_offset.checked_add(entry.stake_amount).unwrap()),
112 }
113}