clockwork_network/state/
rotator.rs

1use {
2    super::Snapshot,
3    crate::state::SnapshotEntry,
4    anchor_lang::{prelude::*, AnchorDeserialize},
5    std::{
6        collections::hash_map::DefaultHasher,
7        convert::TryFrom,
8        hash::{Hash, Hasher},
9    },
10};
11
12pub const SEED_ROTATOR: &[u8] = b"rotator";
13
14/**
15 * Rotator
16 */
17
18#[account]
19#[derive(Debug)]
20pub struct Rotator {
21    pub last_rotation_at: u64, // Slot of the last rotation
22    pub nonce: u64,
23    pub pool_pubkeys: Vec<Pubkey>,
24}
25
26impl Rotator {
27    pub fn pubkey() -> Pubkey {
28        Pubkey::find_program_address(&[SEED_ROTATOR], &crate::ID).0
29    }
30}
31
32impl TryFrom<Vec<u8>> for Rotator {
33    type Error = Error;
34    fn try_from(data: Vec<u8>) -> std::result::Result<Self, Self::Error> {
35        Rotator::try_deserialize(&mut data.as_slice())
36    }
37}
38
39/**
40 * RotatorAccount
41 */
42
43pub trait RotatorAccount {
44    fn init(&mut self) -> Result<()>;
45
46    fn is_valid_entry(
47        &mut self,
48        entry: &Account<SnapshotEntry>,
49        snapshot: &Account<Snapshot>,
50    ) -> Result<bool>;
51
52    fn hash_nonce(&mut self) -> Result<()>;
53
54    fn add_pool(&mut self, pool: Pubkey) -> Result<()>;
55}
56
57impl RotatorAccount for Account<'_, Rotator> {
58    fn init(&mut self) -> Result<()> {
59        // Start the nonce on a hash of the rotator's pubkey. This is an arbitrary value.
60        let mut hasher = DefaultHasher::new();
61        self.key().hash(&mut hasher);
62        self.nonce = hasher.finish();
63        self.last_rotation_at = 0;
64        self.pool_pubkeys = vec![];
65        Ok(())
66    }
67
68    fn is_valid_entry(
69        &mut self,
70        entry: &Account<SnapshotEntry>,
71        snapshot: &Account<Snapshot>,
72    ) -> Result<bool> {
73        // Return true if the sample is within the entry's stake range
74        match self.nonce.checked_rem(snapshot.stake_total) {
75            None => Ok(false),
76            Some(sample) => Ok(sample >= entry.stake_offset
77                && sample < entry.stake_offset.checked_add(entry.stake_amount).unwrap()),
78        }
79    }
80
81    fn hash_nonce(&mut self) -> Result<()> {
82        // Hash the nonce
83        let mut hasher = DefaultHasher::new();
84        self.nonce.hash(&mut hasher);
85        self.nonce = hasher.finish();
86
87        // Record the slot value
88        self.last_rotation_at = Clock::get().unwrap().slot;
89        Ok(())
90    }
91
92    fn add_pool(&mut self, pool_pubkey: Pubkey) -> Result<()> {
93        // Push the pubkey into the set of registered pools
94        self.pool_pubkeys.push(pool_pubkey);
95        Ok(())
96    }
97}