1use borsh::{BorshSerialize, BorshDeserialize};
4use solana_program::{pubkey::Pubkey, program_error::ProgramError, msg, account_info::AccountInfo, entrypoint::ProgramResult};
5use crate::{calc::proportional, checks::check_address, error::CommonError, list::List, ID};
6
7
8#[derive(Clone, Copy, Debug, Default, PartialEq, BorshSerialize, BorshDeserialize)]
9pub struct ValidatorRecord {
10 pub validator_account: Pubkey,
12
13 pub active_balance: u64, pub score: u32,
16 pub last_stake_delta_epoch: u64,
17 pub duplication_flag_bump_seed: u8,
18}
19
20impl ValidatorRecord {
21 pub const DISCRIMINATOR: &'static [u8; 8] = b"validatr";
22 pub const DUPLICATE_FLAG_SEED: &'static [u8] = b"unique_validator";
23
24 pub fn find_duplication_flag(state: &Pubkey, validator_account: &Pubkey) -> (Pubkey, u8) {
25 Pubkey::find_program_address(
26 &[
27 &state.to_bytes()[..32],
28 Self::DUPLICATE_FLAG_SEED,
29 &validator_account.to_bytes()[..32],
30 ],
31 &ID,
32 )
33 }
34
35 pub fn with_duplication_flag_seeds<R, F: FnOnce(&[&[u8]]) -> R>(
36 &self,
37 state: &Pubkey,
38 f: F,
39 ) -> R {
40 f(&[
41 &state.to_bytes()[..32],
42 Self::DUPLICATE_FLAG_SEED,
43 &self.validator_account.to_bytes()[..32],
44 &[self.duplication_flag_bump_seed],
45 ])
46 }
47
48 pub fn duplication_flag_address(&self, state: &Pubkey) -> Pubkey {
49 self.with_duplication_flag_seeds(state, |seeds| Pubkey::create_program_address(seeds, &ID))
50 .unwrap()
51 }
52
53 pub fn new(
54 validator_account: Pubkey,
55 score: u32,
56 state: &Pubkey,
57 duplication_flag_address: &Pubkey,
58 ) -> Result<Self, ProgramError> {
59 let (actual_duplication_flag, duplication_flag_bump_seed) =
60 Self::find_duplication_flag(state, &validator_account);
61 if duplication_flag_address != &actual_duplication_flag {
62 msg!(
63 "Duplication flag {} does not match {}",
64 duplication_flag_address,
65 actual_duplication_flag
66 );
67 return Err(ProgramError::InvalidArgument);
68 }
69 Ok(Self {
70 validator_account,
71 active_balance: 0,
72 score,
73 last_stake_delta_epoch: std::u64::MAX, duplication_flag_bump_seed,
75 })
76 }
77}
78
79#[derive(Clone, BorshSerialize, BorshDeserialize, Debug)]
80pub struct ValidatorSystem {
81 pub validator_list: List,
82 pub manager_authority: Pubkey,
83 pub total_validator_score: u32,
84 pub total_active_balance: u64,
86 pub auto_add_validator_enabled: u8,
88}
89
90impl ValidatorSystem {
91 pub fn bytes_for_list(count: u32, additional_record_space: u32) -> u32 {
92 List::bytes_for(
93 ValidatorRecord::default().try_to_vec().unwrap().len() as u32 + additional_record_space,
94 count,
95 )
96 }
97
98 pub fn validator_list_address(&self) -> &Pubkey {
99 &self.validator_list.account
100 }
101
102 pub fn validator_count(&self) -> u32 {
103 self.validator_list.len()
104 }
105
106 pub fn validator_list_capacity(&self, validator_list_len: usize) -> Result<u32, ProgramError> {
107 self.validator_list.capacity(validator_list_len)
108 }
109
110 pub fn validator_record_size(&self) -> u32 {
111 self.validator_list.item_size()
112 }
113
114 pub fn get(
115 &self,
116 validator_list_data: &[u8],
117 index: u32,
118 ) -> Result<ValidatorRecord, ProgramError> {
119 self.validator_list
120 .get(validator_list_data, index, "validator_list")
121 }
122
123 pub fn validator_stake_target(
124 &self,
125 validator: &ValidatorRecord,
126 total_stake_target: u64,
127 ) -> Result<u64, CommonError> {
128 if self.total_validator_score == 0 {
129 return Ok(0);
130 }
131 proportional(
132 total_stake_target,
133 validator.score as u64,
134 self.total_validator_score as u64,
135 )
136 }
137
138 pub fn check_validator_list<'info>(
139 &self,
140 validator_list: &AccountInfo<'info>,
141 ) -> ProgramResult {
142 check_address(
143 validator_list.key,
144 self.validator_list_address(),
145 "validator_list",
146 )?;
147 if &validator_list.data.borrow().as_ref()[0..8] != ValidatorRecord::DISCRIMINATOR {
148 msg!("Wrong validator list account discriminator");
149 return Err(ProgramError::InvalidAccountData);
150 }
151 Ok(())
152 }
153
154 pub fn check_validator_manager_authority(&self, manager_authority: &Pubkey) -> ProgramResult {
155 check_address(
156 manager_authority,
157 &self.manager_authority,
158 "validator_manager_authority",
159 )
160 }
161}