1#[allow(deprecated)]
2use solana_stake_interface::config::Config as StakeConfig;
3use {
4 crate::stake_utils,
5 agave_feature_set::{FeatureSet, FEATURE_NAMES},
6 agave_votor_messages::consensus_message::BLS_KEYPAIR_DERIVE_SEED,
7 bincode::serialize,
8 log::*,
9 solana_account::{
10 state_traits::StateMut, Account, AccountSharedData, ReadableAccount, WritableAccount,
11 },
12 solana_bls_signatures::{
13 keypair::Keypair as BLSKeypair, pubkey::PubkeyCompressed as BLSPubkeyCompressed,
14 Pubkey as BLSPubkey,
15 },
16 solana_cluster_type::ClusterType,
17 solana_config_interface::state::ConfigKeys,
18 solana_feature_gate_interface::{self as feature, Feature},
19 solana_fee_calculator::FeeRateGovernor,
20 solana_genesis_config::GenesisConfig,
21 solana_keypair::Keypair,
22 solana_native_token::LAMPORTS_PER_SOL,
23 solana_pubkey::Pubkey,
24 solana_rent::Rent,
25 solana_sdk_ids::{stake as stake_program, sysvar},
26 solana_seed_derivable::SeedDerivable,
27 solana_signer::Signer,
28 solana_stake_interface::state::{Authorized, Lockup, Meta, StakeStateV2},
29 solana_system_interface::program as system_program,
30 solana_sysvar::{
31 epoch_rewards::{self, EpochRewards},
32 SysvarSerialize,
33 },
34 solana_vote_interface::state::BLS_PUBLIC_KEY_COMPRESSED_SIZE,
35 solana_vote_program::vote_state,
36 std::borrow::Borrow,
37};
38
39const VALIDATOR_LAMPORTS: u64 = 42;
41
42pub fn bootstrap_validator_stake_lamports() -> u64 {
44 Rent::default().minimum_balance(StakeStateV2::size_of())
45}
46
47pub const fn genesis_sysvar_and_builtin_program_lamports() -> u64 {
49 const NUM_BUILTIN_PROGRAMS: u64 = 6;
50 const NUM_PRECOMPILES: u64 = 2;
51 const STAKE_HISTORY_MIN_BALANCE: u64 = 114_979_200;
52 const CLOCK_SYSVAR_MIN_BALANCE: u64 = 1_169_280;
53 const RENT_SYSVAR_MIN_BALANCE: u64 = 1_009_200;
54 const EPOCH_SCHEDULE_SYSVAR_MIN_BALANCE: u64 = 1_120_560;
55 const RECENT_BLOCKHASHES_SYSVAR_MIN_BALANCE: u64 = 42_706_560;
56
57 STAKE_HISTORY_MIN_BALANCE
58 + CLOCK_SYSVAR_MIN_BALANCE
59 + RENT_SYSVAR_MIN_BALANCE
60 + EPOCH_SCHEDULE_SYSVAR_MIN_BALANCE
61 + RECENT_BLOCKHASHES_SYSVAR_MIN_BALANCE
62 + NUM_BUILTIN_PROGRAMS
63 + NUM_PRECOMPILES
64}
65
66pub struct ValidatorVoteKeypairs {
67 pub node_keypair: Keypair,
68 pub vote_keypair: Keypair,
69 pub stake_keypair: Keypair,
70}
71
72impl ValidatorVoteKeypairs {
73 pub fn new(node_keypair: Keypair, vote_keypair: Keypair, stake_keypair: Keypair) -> Self {
74 Self {
75 node_keypair,
76 vote_keypair,
77 stake_keypair,
78 }
79 }
80
81 pub fn new_rand() -> Self {
82 Self {
83 node_keypair: Keypair::new(),
84 vote_keypair: Keypair::new(),
85 stake_keypair: Keypair::new(),
86 }
87 }
88}
89
90pub struct GenesisConfigInfo {
91 pub genesis_config: GenesisConfig,
92 pub mint_keypair: Keypair,
93 pub voting_keypair: Keypair,
94 pub validator_pubkey: Pubkey,
95}
96
97pub fn create_genesis_config(mint_lamports: u64) -> GenesisConfigInfo {
98 create_genesis_config_with_leader(
103 mint_lamports,
104 &solana_pubkey::new_rand(), 0, )
107}
108
109pub fn create_genesis_config_with_vote_accounts(
110 mint_lamports: u64,
111 voting_keypairs: &[impl Borrow<ValidatorVoteKeypairs>],
112 stakes: Vec<u64>,
113) -> GenesisConfigInfo {
114 create_genesis_config_with_vote_accounts_and_cluster_type(
115 mint_lamports,
116 voting_keypairs,
117 stakes,
118 ClusterType::Development,
119 false,
120 )
121}
122
123pub fn create_genesis_config_with_alpenglow_vote_accounts(
124 mint_lamports: u64,
125 voting_keypairs: &[impl Borrow<ValidatorVoteKeypairs>],
126 stakes: Vec<u64>,
127) -> GenesisConfigInfo {
128 create_genesis_config_with_vote_accounts_and_cluster_type(
129 mint_lamports,
130 voting_keypairs,
131 stakes,
132 ClusterType::Development,
133 true,
134 )
135}
136
137pub fn create_genesis_config_with_vote_accounts_and_cluster_type(
138 mint_lamports: u64,
139 voting_keypairs: &[impl Borrow<ValidatorVoteKeypairs>],
140 stakes: Vec<u64>,
141 cluster_type: ClusterType,
142 is_alpenglow: bool,
143) -> GenesisConfigInfo {
144 assert!(!voting_keypairs.is_empty());
145 assert_eq!(voting_keypairs.len(), stakes.len());
146
147 let mint_keypair = Keypair::new();
148 let voting_keypair = voting_keypairs[0].borrow().vote_keypair.insecure_clone();
149
150 let validator_pubkey = voting_keypairs[0].borrow().node_keypair.pubkey();
151 let validator_bls_pubkey = if is_alpenglow {
152 let bls_keypair = BLSKeypair::derive_from_signer(
153 &voting_keypairs[0].borrow().vote_keypair,
154 BLS_KEYPAIR_DERIVE_SEED,
155 )
156 .unwrap();
157 Some(bls_pubkey_to_compressed_bytes(&bls_keypair.public))
158 } else {
159 None
160 };
161 let genesis_config = create_genesis_config_with_leader_ex(
162 mint_lamports,
163 &mint_keypair.pubkey(),
164 &validator_pubkey,
165 &voting_keypairs[0].borrow().vote_keypair.pubkey(),
166 &voting_keypairs[0].borrow().stake_keypair.pubkey(),
167 validator_bls_pubkey,
168 stakes[0],
169 VALIDATOR_LAMPORTS,
170 FeeRateGovernor::new(0, 0), Rent::free(), cluster_type,
173 vec![],
174 );
175
176 let mut genesis_config_info = GenesisConfigInfo {
177 genesis_config,
178 mint_keypair,
179 voting_keypair,
180 validator_pubkey,
181 };
182
183 for (validator_voting_keypairs, stake) in voting_keypairs[1..].iter().zip(&stakes[1..]) {
184 let node_pubkey = validator_voting_keypairs.borrow().node_keypair.pubkey();
185 let vote_pubkey = validator_voting_keypairs.borrow().vote_keypair.pubkey();
186 let stake_pubkey = validator_voting_keypairs.borrow().stake_keypair.pubkey();
187
188 let node_account = Account::new(VALIDATOR_LAMPORTS, 0, &system_program::id());
190 let bls_pubkey_compressed = if is_alpenglow {
191 let bls_keypair = BLSKeypair::derive_from_signer(
192 &validator_voting_keypairs.borrow().vote_keypair,
193 BLS_KEYPAIR_DERIVE_SEED,
194 )
195 .unwrap();
196 Some(bls_pubkey_to_compressed_bytes(&bls_keypair.public))
197 } else {
198 None
199 };
200 let vote_account = vote_state::create_v4_account_with_authorized(
201 &node_pubkey,
202 &vote_pubkey,
203 &vote_pubkey,
204 bls_pubkey_compressed,
205 0,
206 *stake,
207 );
208 let stake_account = Account::from(stake_utils::create_stake_account(
209 &stake_pubkey,
210 &vote_pubkey,
211 &vote_account,
212 &genesis_config_info.genesis_config.rent,
213 *stake,
214 ));
215
216 let vote_account = Account::from(vote_account);
217
218 genesis_config_info.genesis_config.accounts.extend(vec![
220 (node_pubkey, node_account),
221 (vote_pubkey, vote_account),
222 (stake_pubkey, stake_account),
223 ]);
224 }
225
226 genesis_config_info
227}
228
229pub fn create_genesis_config_with_leader(
230 mint_lamports: u64,
231 validator_pubkey: &Pubkey,
232 validator_stake_lamports: u64,
233) -> GenesisConfigInfo {
234 let mint_keypair = Keypair::from_seed(&[
236 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
237 25, 26, 27, 28, 29, 30, 31,
238 ])
239 .unwrap();
240
241 create_genesis_config_with_leader_with_mint_keypair(
242 mint_keypair,
243 mint_lamports,
244 validator_pubkey,
245 validator_stake_lamports,
246 )
247}
248
249pub fn create_genesis_config_with_leader_with_mint_keypair(
250 mint_keypair: Keypair,
251 mint_lamports: u64,
252 validator_pubkey: &Pubkey,
253 validator_stake_lamports: u64,
254) -> GenesisConfigInfo {
255 let voting_keypair = Keypair::from_seed(&[
257 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54,
258 55, 56, 57, 58, 59, 60, 61, 62, 63,
259 ])
260 .unwrap();
261
262 let genesis_config = create_genesis_config_with_leader_ex(
263 mint_lamports,
264 &mint_keypair.pubkey(),
265 validator_pubkey,
266 &voting_keypair.pubkey(),
267 &Pubkey::new_unique(),
268 None,
269 validator_stake_lamports,
270 VALIDATOR_LAMPORTS,
271 FeeRateGovernor::new(0, 0), Rent::free(), ClusterType::Development,
274 vec![],
275 );
276
277 GenesisConfigInfo {
278 genesis_config,
279 mint_keypair,
280 voting_keypair,
281 validator_pubkey: *validator_pubkey,
282 }
283}
284
285pub fn activate_all_features_alpenglow(genesis_config: &mut GenesisConfig) {
286 do_activate_all_features::<true>(genesis_config);
287}
288
289pub fn activate_all_features(genesis_config: &mut GenesisConfig) {
290 do_activate_all_features::<false>(genesis_config);
291}
292
293fn do_activate_all_features<const IS_ALPENGLOW: bool>(genesis_config: &mut GenesisConfig) {
294 for feature_id in FeatureSet::default().inactive() {
296 if IS_ALPENGLOW || *feature_id != agave_feature_set::alpenglow::id() {
297 activate_feature(genesis_config, *feature_id);
298 }
299 }
300}
301
302pub fn deactivate_features(
303 genesis_config: &mut GenesisConfig,
304 features_to_deactivate: &Vec<Pubkey>,
305) {
306 for deactivate_feature_pk in features_to_deactivate {
308 if FEATURE_NAMES.contains_key(deactivate_feature_pk) {
309 genesis_config.accounts.remove(deactivate_feature_pk);
310 } else {
311 warn!(
312 "Feature {deactivate_feature_pk:?} set for deactivation is not a known Feature \
313 public key"
314 );
315 }
316 }
317}
318
319pub fn activate_feature(genesis_config: &mut GenesisConfig, feature_id: Pubkey) {
320 genesis_config.accounts.insert(
321 feature_id,
322 Account::from(feature::create_account(
323 &Feature {
324 activated_at: Some(0),
325 },
326 std::cmp::max(genesis_config.rent.minimum_balance(Feature::size_of()), 1),
327 )),
328 );
329}
330
331pub fn bls_pubkey_to_compressed_bytes(
332 bls_pubkey: &BLSPubkey,
333) -> [u8; BLS_PUBLIC_KEY_COMPRESSED_SIZE] {
334 let key = BLSPubkeyCompressed::try_from(bls_pubkey).unwrap();
335 bincode::serialize(&key).unwrap().try_into().unwrap()
336}
337
338#[allow(clippy::too_many_arguments)]
339pub fn create_genesis_config_with_leader_ex_no_features(
340 mint_lamports: u64,
341 mint_pubkey: &Pubkey,
342 validator_pubkey: &Pubkey,
343 validator_vote_account_pubkey: &Pubkey,
344 validator_stake_account_pubkey: &Pubkey,
345 validator_bls_pubkey: Option<[u8; BLS_PUBLIC_KEY_COMPRESSED_SIZE]>,
346 validator_stake_lamports: u64,
347 validator_lamports: u64,
348 fee_rate_governor: FeeRateGovernor,
349 rent: Rent,
350 cluster_type: ClusterType,
351 mut initial_accounts: Vec<(Pubkey, AccountSharedData)>,
352) -> GenesisConfig {
353 let validator_vote_account = vote_state::create_v4_account_with_authorized(
354 validator_pubkey,
355 validator_vote_account_pubkey,
356 validator_vote_account_pubkey,
357 validator_bls_pubkey,
358 0,
359 validator_stake_lamports,
360 );
361
362 let validator_stake_account = stake_utils::create_stake_account(
363 validator_stake_account_pubkey,
364 validator_vote_account_pubkey,
365 &validator_vote_account,
366 &rent,
367 validator_stake_lamports,
368 );
369
370 initial_accounts.push((
371 *mint_pubkey,
372 AccountSharedData::new(mint_lamports, 0, &system_program::id()),
373 ));
374 initial_accounts.push((
375 *validator_pubkey,
376 AccountSharedData::new(validator_lamports, 0, &system_program::id()),
377 ));
378 initial_accounts.push((*validator_vote_account_pubkey, validator_vote_account));
379 initial_accounts.push((*validator_stake_account_pubkey, validator_stake_account));
380
381 let native_mint_account = solana_account::AccountSharedData::from(Account {
382 owner: spl_generic_token::token::id(),
383 data: spl_generic_token::token::native_mint::ACCOUNT_DATA.to_vec(),
384 lamports: LAMPORTS_PER_SOL,
385 executable: false,
386 rent_epoch: 1,
387 });
388 initial_accounts.push((
389 spl_generic_token::token::native_mint::id(),
390 native_mint_account,
391 ));
392
393 let mut genesis_config = GenesisConfig {
394 accounts: initial_accounts
395 .iter()
396 .cloned()
397 .map(|(key, account)| (key, Account::from(account)))
398 .collect(),
399 fee_rate_governor,
400 rent,
401 cluster_type,
402 ..GenesisConfig::default()
403 };
404
405 add_genesis_stake_config_account(&mut genesis_config);
406 add_genesis_epoch_rewards_account(&mut genesis_config);
407
408 genesis_config
409}
410
411#[allow(clippy::too_many_arguments)]
412pub fn create_genesis_config_with_leader_ex(
413 mint_lamports: u64,
414 mint_pubkey: &Pubkey,
415 validator_pubkey: &Pubkey,
416 validator_vote_account_pubkey: &Pubkey,
417 validator_stake_account_pubkey: &Pubkey,
418 validator_bls_pubkey: Option<[u8; BLS_PUBLIC_KEY_COMPRESSED_SIZE]>,
419 validator_stake_lamports: u64,
420 validator_lamports: u64,
421 fee_rate_governor: FeeRateGovernor,
422 rent: Rent,
423 cluster_type: ClusterType,
424 initial_accounts: Vec<(Pubkey, AccountSharedData)>,
425) -> GenesisConfig {
426 let mut genesis_config = create_genesis_config_with_leader_ex_no_features(
427 mint_lamports,
428 mint_pubkey,
429 validator_pubkey,
430 validator_vote_account_pubkey,
431 validator_stake_account_pubkey,
432 validator_bls_pubkey,
433 validator_stake_lamports,
434 validator_lamports,
435 fee_rate_governor,
436 rent,
437 cluster_type,
438 initial_accounts,
439 );
440
441 if genesis_config.cluster_type == ClusterType::Development {
442 activate_all_features(&mut genesis_config);
443 }
444
445 genesis_config
446}
447
448#[allow(deprecated)]
449pub fn add_genesis_stake_config_account(genesis_config: &mut GenesisConfig) -> u64 {
450 let mut data = serialize(&ConfigKeys { keys: vec![] }).unwrap();
451 data.extend_from_slice(&serialize(&StakeConfig::default()).unwrap());
452 let lamports = std::cmp::max(genesis_config.rent.minimum_balance(data.len()), 1);
453 let account = AccountSharedData::from(Account {
454 lamports,
455 data,
456 owner: solana_sdk_ids::config::id(),
457 ..Account::default()
458 });
459
460 genesis_config.add_account(solana_stake_interface::config::id(), account);
461
462 lamports
463}
464
465pub fn add_genesis_epoch_rewards_account(genesis_config: &mut GenesisConfig) -> u64 {
466 let data = vec![0; EpochRewards::size_of()];
467 let lamports = std::cmp::max(genesis_config.rent.minimum_balance(data.len()), 1);
468
469 let account = AccountSharedData::create(lamports, data, sysvar::id(), false, u64::MAX);
470
471 genesis_config.add_account(epoch_rewards::id(), account);
472
473 lamports
474}
475
476pub fn create_lockup_stake_account(
478 authorized: &Authorized,
479 lockup: &Lockup,
480 rent: &Rent,
481 lamports: u64,
482) -> AccountSharedData {
483 let mut stake_account =
484 AccountSharedData::new(lamports, StakeStateV2::size_of(), &stake_program::id());
485
486 let rent_exempt_reserve = rent.minimum_balance(stake_account.data().len());
487 assert!(
488 lamports >= rent_exempt_reserve,
489 "lamports: {lamports} is less than rent_exempt_reserve {rent_exempt_reserve}"
490 );
491
492 stake_account
493 .set_state(&StakeStateV2::Initialized(Meta {
494 authorized: *authorized,
495 lockup: *lockup,
496 rent_exempt_reserve,
497 }))
498 .expect("set_state");
499
500 stake_account
501}