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