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 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196
use crate::errors::RegistryError;
use aligned_sized::aligned_sized;
use anchor_lang::prelude::*;
#[aligned_sized(anchor)]
#[derive(Debug)]
#[account]
pub struct ProtocolConfigPda {
pub authority: Pubkey,
pub bump: u8,
pub config: ProtocolConfig,
}
/// Epoch Phases:
/// 1. Registration
/// 2. Active
/// 3. Report Work
/// 4. Post (Epoch has ended, and rewards can be claimed.)
/// - There is always an active phase in progress, registration and report work
/// phases run in parallel to a currently active phase.
#[derive(Debug, Clone, Copy, PartialEq, Eq, AnchorSerialize, AnchorDeserialize)]
pub struct ProtocolConfig {
/// Solana slot when the protocol starts operating.
pub genesis_slot: u64,
/// Minimum weight required for a forester to register to an epoch.
pub min_weight: u64,
/// Light protocol slot length.
pub slot_length: u64,
/// Foresters can register for this phase.
pub registration_phase_length: u64,
/// Foresters can perform work in this phase.
pub active_phase_length: u64,
/// Foresters can report work to receive performance based rewards in this
/// phase.
pub report_work_phase_length: u64,
pub network_fee: u64,
pub cpi_context_size: u64,
pub finalize_counter_limit: u64,
/// Placeholder for future protocol updates.
pub place_holder: Pubkey,
pub place_holder_a: u64,
pub place_holder_b: u64,
pub place_holder_c: u64,
pub place_holder_d: u64,
pub place_holder_e: u64,
pub place_holder_f: u64,
}
impl Default for ProtocolConfig {
fn default() -> Self {
Self {
genesis_slot: 0,
min_weight: 1,
slot_length: 10,
registration_phase_length: 100,
active_phase_length: 1000,
report_work_phase_length: 100,
network_fee: 5000,
cpi_context_size: 20 * 1024 + 8,
finalize_counter_limit: 100,
place_holder: Pubkey::default(),
place_holder_a: 0,
place_holder_b: 0,
place_holder_c: 0,
place_holder_d: 0,
place_holder_e: 0,
place_holder_f: 0,
}
}
}
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub enum EpochState {
Registration,
Active,
ReportWork,
Post,
#[default]
Pre,
}
/// Light Epoch Example:
///
/// Diagram of epochs 0 and 1.
/// Registration 0 starts at genesis slot.
/// |---- Registration 0 ----|------------------ Active 0 ------|---- Report Work 0 ----|---- Post 0 ----
/// |-- Registration 1 --|------------------ Active 1 -----------------
/// (Post epoch does not end unlike the other phases.)
///
/// let genesis = 0;
/// let registration_phase_length = 100;
/// let active_phase_length = 1000;
/// let report_work_phase_length = 100;
/// let slot = 10;
///
/// To get the latest registry epoch:
/// - slot = 0;
/// let current_registry_epoch = (slot - genesis) / active_phase_length;
/// current_registry_epoch = (0 - 0) / 1000 = 0;
/// first active phase starts at genesis + registration_phase_length
/// = 0 + 100 = 100;
///
/// To get the current active epoch:
/// - slot = 100;
/// let current_active_epoch =
/// (slot - genesis - registration_phase_length) / active_phase_length;
/// current_active_epoch = (100 - 0 - 100) / 1000 = 0;
///
/// Epoch 0:
/// - Registration 0: 0 - 100
/// - Active 0: 100 - 1100
/// - Report Work 0: 1100 - 1200
/// - Post 0: 1200 - inf
///
/// Epoch 1:
/// - Registration 1: 1000 - 1100
/// - Active 1: 1100 - 2100
/// - Report Work 1: 2100 - 2200
/// - Post 1: 2200 - inf
///
/// Epoch 2:
/// - Registration 2: 2000 - 2100
/// - Active 2: 2100 - 3100
/// - Report Work 2: 3100 - 3200
/// - Post 2: 3200 - inf
///
impl ProtocolConfig {
/// Current epoch including registration phase.
pub fn get_latest_register_epoch(&self, slot: u64) -> Result<u64> {
let slot = slot
.checked_sub(self.genesis_slot)
.ok_or(RegistryError::GetLatestedRegisterEpochFailed)?;
Ok(slot / self.active_phase_length)
}
pub fn get_current_epoch(&self, slot: u64) -> u64 {
(slot.saturating_sub(self.genesis_slot)) / self.active_phase_length
}
pub fn get_current_active_epoch(&self, slot: u64) -> Result<u64> {
let slot = slot
.checked_sub(self.genesis_slot + self.registration_phase_length)
.ok_or(RegistryError::GetLatestActiveEpochFailed)?;
Ok(slot / self.active_phase_length)
}
pub fn get_latest_register_epoch_progress(&self, slot: u64) -> Result<u64> {
Ok(slot
.checked_sub(self.genesis_slot)
.ok_or(RegistryError::ArithmeticUnderflow)?
% self.active_phase_length)
}
pub fn get_current_active_epoch_progress(&self, slot: u64) -> u64 {
(slot
.checked_sub(self.genesis_slot + self.registration_phase_length)
.unwrap())
% self.active_phase_length
}
/// In the last part of the active phase the registration phase starts.
/// Returns end slot of the registration phase/start slot of the next active phase.
pub fn is_registration_phase(&self, slot: u64) -> Result<u64> {
let latest_register_epoch = self.get_latest_register_epoch(slot)?;
let latest_register_epoch_progress = self.get_latest_register_epoch_progress(slot)?;
if latest_register_epoch_progress >= self.registration_phase_length {
return err!(RegistryError::NotInRegistrationPeriod);
}
Ok((latest_register_epoch) * self.active_phase_length
+ self.genesis_slot
+ self.registration_phase_length)
}
pub fn is_active_phase(&self, slot: u64, epoch: u64) -> Result<()> {
if self.get_current_active_epoch(slot)? != epoch {
return err!(RegistryError::NotInActivePhase);
}
Ok(())
}
pub fn is_report_work_phase(&self, slot: u64, epoch: u64) -> Result<()> {
self.is_active_phase(slot, epoch + 1)?;
let current_epoch_progress = self.get_current_active_epoch_progress(slot);
if current_epoch_progress >= self.report_work_phase_length {
return err!(RegistryError::NotInReportWorkPhase);
}
Ok(())
}
pub fn is_post_epoch(&self, slot: u64, epoch: u64) -> Result<()> {
if self.get_current_active_epoch(slot)? <= epoch {
return err!(RegistryError::InvalidEpoch);
}
Ok(())
}
}