jito_restaking_core/
config.rs1use bytemuck::{Pod, Zeroable};
4use jito_bytemuck::{types::PodU64, AccountDeserialize, Discriminator};
5use jito_restaking_sdk::error::RestakingError;
6use shank::ShankAccount;
7use solana_program::{
8 account_info::AccountInfo, clock::DEFAULT_SLOTS_PER_EPOCH, msg, program_error::ProgramError,
9 pubkey::Pubkey,
10};
11
12const RESERVED_SPACE_LEN: usize = 263;
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Pod, Zeroable, AccountDeserialize, ShankAccount)]
17#[repr(C)]
18pub struct Config {
19 pub admin: Pubkey,
21
22 pub vault_program: Pubkey,
24
25 ncn_count: PodU64,
27
28 operator_count: PodU64,
30
31 epoch_length: PodU64,
33
34 pub bump: u8,
36
37 reserved: [u8; 263],
39}
40
41impl Config {
42 pub fn new(admin: Pubkey, vault_program: Pubkey, bump: u8) -> Self {
43 Self {
44 admin,
45 vault_program,
46 epoch_length: PodU64::from(DEFAULT_SLOTS_PER_EPOCH),
47 ncn_count: PodU64::from(0),
48 operator_count: PodU64::from(0),
49 bump,
50 reserved: [0; RESERVED_SPACE_LEN],
51 }
52 }
53
54 pub fn epoch_length(&self) -> u64 {
55 self.epoch_length.into()
56 }
57
58 pub fn ncn_count(&self) -> u64 {
59 self.ncn_count.into()
60 }
61
62 pub fn operator_count(&self) -> u64 {
63 self.operator_count.into()
64 }
65
66 pub fn increment_ncn_count(&mut self) -> Result<(), RestakingError> {
67 let ncn_count = self
68 .ncn_count()
69 .checked_add(1)
70 .ok_or(RestakingError::NcnOverflow)?;
71 self.ncn_count = PodU64::from(ncn_count);
72 Ok(())
73 }
74
75 pub fn increment_operator_count(&mut self) -> Result<(), RestakingError> {
76 let operator_count = self
77 .operator_count()
78 .checked_add(1)
79 .ok_or(RestakingError::OperatorOverflow)?;
80 self.operator_count = PodU64::from(operator_count);
81 Ok(())
82 }
83
84 pub fn seeds() -> Vec<Vec<u8>> {
86 vec![b"config".to_vec()]
87 }
88
89 pub fn find_program_address(program_id: &Pubkey) -> (Pubkey, u8, Vec<Vec<u8>>) {
98 let seeds = Self::seeds();
99 let seeds_iter: Vec<_> = seeds.iter().map(|s| s.as_slice()).collect();
100 let (pda, bump) = Pubkey::find_program_address(&seeds_iter, program_id);
101 (pda, bump, seeds)
102 }
103
104 pub fn load(
114 program_id: &Pubkey,
115 account: &AccountInfo,
116 expect_writable: bool,
117 ) -> Result<(), ProgramError> {
118 if account.owner.ne(program_id) {
119 msg!("Config account has an invalid owner");
120 return Err(ProgramError::InvalidAccountOwner);
121 }
122 if account.data_is_empty() {
123 msg!("Config account data is empty");
124 return Err(ProgramError::InvalidAccountData);
125 }
126 if expect_writable && !account.is_writable {
127 msg!("Config account is not writable");
128 return Err(ProgramError::InvalidAccountData);
129 }
130 if account.data.borrow()[0].ne(&Self::DISCRIMINATOR) {
131 msg!("Config account discriminator is invalid");
132 return Err(ProgramError::InvalidAccountData);
133 }
134 if account.key.ne(&Self::find_program_address(program_id).0) {
135 msg!("Config account is not at the correct PDA");
136 return Err(ProgramError::InvalidAccountData);
137 }
138 Ok(())
139 }
140
141 pub fn set_admin(&mut self, new_admin: Pubkey) {
142 self.admin = new_admin;
143 }
144}
145
146#[cfg(test)]
147mod tests {
148 use super::*;
149
150 #[test]
151 fn test_config_no_padding() {
152 let config_size = std::mem::size_of::<Config>();
153 let sum_of_fields = std::mem::size_of::<Pubkey>() + std::mem::size_of::<Pubkey>() + std::mem::size_of::<PodU64>() + std::mem::size_of::<PodU64>() + std::mem::size_of::<PodU64>() + std::mem::size_of::<u8>() + RESERVED_SPACE_LEN; assert_eq!(config_size, sum_of_fields);
161 }
162}