1#[expect(deprecated)]
2use solana_stake_interface::config::Config as StakeConfig;
3use {
4 crate::{
5 bank::DEFAULT_VAT_TO_BURN_PER_EPOCH,
6 block_component_processor::vote_reward::epoch_inflation_account_state::EpochInflationAccountState,
7 stake_utils,
8 },
9 agave_feature_set::{FEATURE_NAMES, FeatureSet},
10 agave_votor_messages::{
11 self,
12 consensus_message::{BLS_KEYPAIR_DERIVE_SEED, Block},
13 migration::GENESIS_CERTIFICATE_ACCOUNT,
14 wire::{WireBlockCertMessage, WireCertSignature},
15 },
16 bincode::serialize,
17 bitvec::vec::BitVec,
18 log::*,
19 solana_account::{Account, AccountSharedData, ReadableAccount, state_traits::StateMut},
20 solana_bls_signatures::{
21 BLS_SIGNATURE_AFFINE_SIZE, Pubkey as BLSPubkey, Signature as BLSSignature,
22 keypair::Keypair as BLSKeypair, pubkey::PubkeyCompressed as BLSPubkeyCompressed,
23 },
24 solana_clock::Epoch,
25 solana_cluster_type::ClusterType,
26 solana_config_interface::state::ConfigKeys,
27 solana_feature_gate_interface::{self as feature, Feature},
28 solana_fee_calculator::FeeRateGovernor,
29 solana_genesis_config::GenesisConfig,
30 solana_hash::Hash,
31 solana_keypair::Keypair,
32 solana_native_token::LAMPORTS_PER_SOL,
33 solana_pubkey::Pubkey,
34 solana_rent::Rent,
35 solana_sdk_ids::{stake as stake_program, sysvar},
36 solana_seed_derivable::SeedDerivable,
37 solana_signer::Signer,
38 solana_signer_store::encode_base2,
39 solana_stake_interface::state::{Authorized, Lockup, Meta, StakeStateV2},
40 solana_system_interface::program as system_program,
41 solana_sysvar::{
42 SysvarSerialize,
43 epoch_rewards::{self, EpochRewards},
44 },
45 solana_vote_interface::state::{BLS_PUBLIC_KEY_COMPRESSED_SIZE, VoteStateV4},
46 solana_vote_program::vote_state,
47 std::{borrow::Borrow, sync::Arc},
48};
49
50const VALIDATOR_LAMPORTS: u64 = 890_880;
52
53pub fn minimum_vote_account_balance_for_vat(num_epochs: Epoch) -> u64 {
56 DEFAULT_VAT_TO_BURN_PER_EPOCH * num_epochs
57 + Rent::default().minimum_balance(VoteStateV4::size_of())
58}
59
60pub fn minimum_stake_lamports_for_vat(rent: &Rent) -> u64 {
63 rent.minimum_balance(StakeStateV2::size_of()) + 1
64}
65
66pub fn bootstrap_validator_stake_lamports() -> u64 {
67 minimum_stake_lamports_for_vat(&Rent::default())
68}
69
70pub const fn genesis_sysvar_and_builtin_program_lamports() -> u64 {
72 const NUM_BUILTIN_PROGRAMS: u64 = 6;
73 const NUM_PRECOMPILES: u64 = 3;
74 const STAKE_HISTORY_MIN_BALANCE: u64 = 114_979_200;
75 const CLOCK_SYSVAR_MIN_BALANCE: u64 = 1_169_280;
76 const RENT_SYSVAR_MIN_BALANCE: u64 = 1_009_200;
77 const EPOCH_SCHEDULE_SYSVAR_MIN_BALANCE: u64 = 1_120_560;
78 const RECENT_BLOCKHASHES_SYSVAR_MIN_BALANCE: u64 = 42_706_560;
79 const LAST_RESTART_SLOT_SYSVAR_MIN_BALANCE: u64 = 946_560;
80
81 STAKE_HISTORY_MIN_BALANCE
82 + CLOCK_SYSVAR_MIN_BALANCE
83 + RENT_SYSVAR_MIN_BALANCE
84 + EPOCH_SCHEDULE_SYSVAR_MIN_BALANCE
85 + RECENT_BLOCKHASHES_SYSVAR_MIN_BALANCE
86 + LAST_RESTART_SLOT_SYSVAR_MIN_BALANCE
87 + NUM_BUILTIN_PROGRAMS
88 + NUM_PRECOMPILES
89}
90
91#[derive(Debug)]
92pub struct ValidatorVoteKeypairs {
93 pub node_keypair: Keypair,
94 pub vote_keypair: Keypair,
95 pub stake_keypair: Keypair,
96 pub bls_keypair: BLSKeypair,
97}
98
99impl ValidatorVoteKeypairs {
100 pub fn new(node_keypair: Keypair, vote_keypair: Keypair, stake_keypair: Keypair) -> Self {
101 let bls_keypair =
102 BLSKeypair::derive_from_signer(&vote_keypair, BLS_KEYPAIR_DERIVE_SEED).unwrap();
103 Self {
104 node_keypair,
105 vote_keypair,
106 stake_keypair,
107 bls_keypair,
108 }
109 }
110
111 pub fn new_rand() -> Self {
112 let node_keypair = Keypair::new();
113 let vote_keypair = Keypair::new();
114 let stake_keypair = Keypair::new();
115 Self::new(node_keypair, vote_keypair, stake_keypair)
116 }
117}
118
119pub struct GenesisConfigInfo {
120 pub genesis_config: GenesisConfig,
121 pub mint_keypair: Keypair,
122 pub voting_keypair: Keypair,
123 pub validator_pubkey: Pubkey,
124}
125
126pub fn create_genesis_config(mint_lamports: u64) -> GenesisConfigInfo {
127 create_genesis_config_with_leader(
132 mint_lamports,
133 &solana_pubkey::new_rand(), 0, )
136}
137
138pub fn create_genesis_config_with_vote_accounts(
139 mint_lamports: u64,
140 voting_keypairs: &[impl Borrow<ValidatorVoteKeypairs>],
141 stakes: Vec<u64>,
142) -> GenesisConfigInfo {
143 create_genesis_config_with_vote_accounts_and_cluster_type(
144 mint_lamports,
145 voting_keypairs,
146 stakes,
147 ClusterType::Development,
148 &FeatureSet::all_enabled(),
149 false,
150 )
151}
152
153#[cfg(feature = "dev-context-only-utils")]
154pub fn create_genesis_config_with_alpenglow_vote_accounts(
155 mint_lamports: u64,
156 voting_keypairs: &[impl Borrow<ValidatorVoteKeypairs>],
157 stakes: Vec<u64>,
158) -> GenesisConfigInfo {
159 create_genesis_config_with_vote_accounts_and_cluster_type(
160 mint_lamports,
161 voting_keypairs,
162 stakes,
163 ClusterType::Development,
164 &FeatureSet::all_enabled(),
165 true,
166 )
167}
168
169pub fn create_genesis_config_with_vote_accounts_and_cluster_type(
170 mint_lamports: u64,
171 voting_keypairs: &[impl Borrow<ValidatorVoteKeypairs>],
172 stakes: Vec<u64>,
173 cluster_type: ClusterType,
174 feature_set: &FeatureSet,
175 is_alpenglow: bool,
176) -> GenesisConfigInfo {
177 assert!(!voting_keypairs.is_empty());
178 assert_eq!(voting_keypairs.len(), stakes.len());
179
180 let mint_keypair = Keypair::new();
181 let voting_keypair = voting_keypairs[0].borrow().vote_keypair.insecure_clone();
182
183 let validator_pubkey = voting_keypairs[0].borrow().node_keypair.pubkey();
184 let validator_bls_pubkey = Some(
185 voting_keypairs[0]
186 .borrow()
187 .bls_keypair
188 .public
189 .to_bytes_compressed(),
190 );
191 let mut genesis_config = create_genesis_config_with_leader_ex(
192 mint_lamports,
193 &mint_keypair.pubkey(),
194 &validator_pubkey,
195 &voting_keypairs[0].borrow().vote_keypair.pubkey(),
196 &voting_keypairs[0].borrow().stake_keypair.pubkey(),
197 validator_bls_pubkey,
198 stakes[0],
199 VALIDATOR_LAMPORTS,
200 FeeRateGovernor::new(0, 0), Rent::free(), cluster_type,
203 feature_set,
204 vec![],
205 );
206
207 if is_alpenglow {
208 activate_all_features_alpenglow(&mut genesis_config);
209 }
210
211 let mut genesis_config_info = GenesisConfigInfo {
212 genesis_config,
213 mint_keypair,
214 voting_keypair,
215 validator_pubkey,
216 };
217
218 for (validator_voting_keypairs, &stake) in voting_keypairs[1..].iter().zip(&stakes[1..]) {
219 let node_pubkey = validator_voting_keypairs.borrow().node_keypair.pubkey();
220 let vote_pubkey = validator_voting_keypairs.borrow().vote_keypair.pubkey();
221 let stake_pubkey = validator_voting_keypairs.borrow().stake_keypair.pubkey();
222 let bls_pubkey = validator_voting_keypairs
223 .borrow()
224 .bls_keypair
225 .public
226 .to_bytes_compressed();
227
228 let rent = &genesis_config_info.genesis_config.rent;
231 let (vote_account_lamports, stake_lamports) = if stake > 0 {
232 (
233 stake.max(minimum_vote_account_balance_for_vat(100)),
234 stake.max(minimum_stake_lamports_for_vat(rent)),
235 )
236 } else {
237 (
239 rent.minimum_balance(VoteStateV4::size_of()),
240 rent.minimum_balance(StakeStateV2::size_of()),
241 )
242 };
243
244 let accounts = create_validator(
245 rent,
246 node_pubkey,
247 VALIDATOR_LAMPORTS,
248 vote_pubkey,
249 vote_account_lamports,
250 stake_pubkey,
251 stake_lamports,
252 Some(bls_pubkey),
253 )
254 .into_iter()
255 .map(|(pubkey, account)| (pubkey, Account::from(account)));
256 genesis_config_info.genesis_config.accounts.extend(accounts);
257 }
258
259 genesis_config_info
260}
261
262pub fn create_genesis_config_with_leader(
263 mint_lamports: u64,
264 validator_pubkey: &Pubkey,
265 validator_stake_lamports: u64,
266) -> GenesisConfigInfo {
267 let mint_keypair = Keypair::from_seed(&[
269 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,
270 25, 26, 27, 28, 29, 30, 31,
271 ])
272 .unwrap();
273
274 create_genesis_config_with_leader_with_mint_keypair(
275 mint_keypair,
276 mint_lamports,
277 validator_pubkey,
278 validator_stake_lamports,
279 )
280}
281
282pub fn create_genesis_config_with_leader_with_mint_keypair(
283 mint_keypair: Keypair,
284 mint_lamports: u64,
285 validator_pubkey: &Pubkey,
286 validator_stake_lamports: u64,
287) -> GenesisConfigInfo {
288 let voting_keypair = Keypair::from_seed(&[
290 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54,
291 55, 56, 57, 58, 59, 60, 61, 62, 63,
292 ])
293 .unwrap();
294
295 let bls_keypair =
296 BLSKeypair::derive_from_signer(&voting_keypair, BLS_KEYPAIR_DERIVE_SEED).unwrap();
297 let validator_bls_pubkey = Some(bls_keypair.public.to_bytes_compressed());
298
299 let genesis_config = create_genesis_config_with_leader_ex(
300 mint_lamports,
301 &mint_keypair.pubkey(),
302 validator_pubkey,
303 &voting_keypair.pubkey(),
304 &Pubkey::new_unique(),
305 validator_bls_pubkey,
306 validator_stake_lamports,
307 VALIDATOR_LAMPORTS,
308 FeeRateGovernor::new(0, 0), Rent::free(), ClusterType::Development,
311 &FeatureSet::all_enabled(),
312 vec![],
313 );
314
315 GenesisConfigInfo {
316 genesis_config,
317 mint_keypair,
318 voting_keypair,
319 validator_pubkey: *validator_pubkey,
320 }
321}
322
323pub fn activate_all_features_alpenglow(genesis_config: &mut GenesisConfig) {
324 do_activate_all_features::<true>(genesis_config);
325 configure_alpenglow_at_genesis(genesis_config);
326}
327
328pub fn activate_alpenglow_at_genesis(genesis_config: &mut GenesisConfig) {
329 activate_feature(genesis_config, agave_feature_set::alpenglow::id());
330 configure_alpenglow_at_genesis(genesis_config);
331}
332
333fn configure_alpenglow_at_genesis(genesis_config: &mut GenesisConfig) {
334 genesis_config.poh_config.hashes_per_tick = None;
336
337 let cert = WireBlockCertMessage {
340 block: Block {
341 slot: 0,
342 block_id: Hash::default(),
343 },
344 signature: WireCertSignature {
345 signature: BLSSignature([0; BLS_SIGNATURE_AFFINE_SIZE]),
346 bitmap: encode_base2(&BitVec::new()).unwrap(),
347 },
348 };
349 let cert_size = bincode::serialized_size(&cert).unwrap();
350 let lamports = Rent::default().minimum_balance(cert_size as usize);
351 let certificate_account = Account::new_data(lamports, &cert, &system_program::ID).unwrap();
352
353 genesis_config
354 .accounts
355 .insert(*GENESIS_CERTIFICATE_ACCOUNT, certificate_account);
356 EpochInflationAccountState::insert_into_genesis_config(genesis_config);
357}
358
359pub fn activate_all_features(genesis_config: &mut GenesisConfig) {
360 do_activate_all_features::<false>(genesis_config);
361}
362
363fn do_activate_all_features<const IS_ALPENGLOW: bool>(genesis_config: &mut GenesisConfig) {
364 for feature_id in FeatureSet::default().inactive() {
366 if IS_ALPENGLOW || *feature_id != agave_feature_set::alpenglow::id() {
367 activate_feature(genesis_config, *feature_id);
368 }
369 }
370}
371
372pub fn deactivate_features(
373 genesis_config: &mut GenesisConfig,
374 features_to_deactivate: &Vec<Pubkey>,
375) {
376 for deactivate_feature_pk in features_to_deactivate {
378 if FEATURE_NAMES.contains_key(deactivate_feature_pk) {
379 genesis_config.accounts.remove(deactivate_feature_pk);
380 } else {
381 warn!(
382 "Feature {deactivate_feature_pk:?} set for deactivation is not a known Feature \
383 public key"
384 );
385 }
386 }
387}
388
389pub fn activate_feature(genesis_config: &mut GenesisConfig, feature_id: Pubkey) {
390 genesis_config.accounts.insert(
391 feature_id,
392 Account::from(feature::create_account(
393 &Feature {
394 activated_at: Some(0),
395 },
396 std::cmp::max(genesis_config.rent.minimum_balance(Feature::size_of()), 1),
397 )),
398 );
399}
400
401pub fn bls_pubkey_to_compressed_bytes(
402 bls_pubkey: &BLSPubkey,
403) -> [u8; BLS_PUBLIC_KEY_COMPRESSED_SIZE] {
404 let key = BLSPubkeyCompressed::try_from(bls_pubkey).unwrap();
405 bincode::serialize(&key).unwrap().try_into().unwrap()
406}
407
408pub(crate) fn create_validator(
409 rent: &Rent,
410 node_pubkey: Pubkey,
411 node_lamports: u64,
412 vote_pubkey: Pubkey,
413 vote_lamports: u64,
414 stake_pubkey: Pubkey,
415 stake_lamports: u64,
416 bls_pubkey: Option<[u8; BLS_PUBLIC_KEY_COMPRESSED_SIZE]>,
417) -> Vec<(Pubkey, AccountSharedData)> {
418 let vote_account = vote_state::create_v4_account_with_authorized(
419 &node_pubkey,
420 &vote_pubkey,
421 bls_pubkey.unwrap_or([0u8; BLS_PUBLIC_KEY_COMPRESSED_SIZE]),
422 &vote_pubkey,
423 0,
424 &vote_pubkey,
425 0,
426 &node_pubkey,
427 vote_lamports,
428 );
429
430 let stake_account = stake_utils::create_stake_account(
431 &stake_pubkey,
432 &vote_pubkey,
433 &vote_account,
434 rent,
435 stake_lamports,
436 );
437
438 let node_account = AccountSharedData::new(node_lamports, 0, &system_program::id());
439
440 vec![
441 (vote_pubkey, vote_account),
442 (stake_pubkey, stake_account),
443 (node_pubkey, node_account),
444 ]
445}
446
447#[expect(clippy::too_many_arguments)]
448pub fn create_genesis_config_with_leader_ex_no_features(
449 mint_lamports: u64,
450 mint_pubkey: &Pubkey,
451 validator_pubkey: &Pubkey,
452 validator_vote_account_pubkey: &Pubkey,
453 validator_stake_account_pubkey: &Pubkey,
454 validator_bls_pubkey: Option<[u8; BLS_PUBLIC_KEY_COMPRESSED_SIZE]>,
455 validator_stake_lamports: u64,
456 validator_lamports: u64,
457 fee_rate_governor: FeeRateGovernor,
458 rent: Rent,
459 cluster_type: ClusterType,
460 mut initial_accounts: Vec<(Pubkey, AccountSharedData)>,
461) -> GenesisConfig {
462 let (vote_account_lamports, stake_lamports) = if validator_stake_lamports > 0 {
467 (
468 validator_stake_lamports.max(minimum_vote_account_balance_for_vat(100)),
469 validator_stake_lamports.max(minimum_stake_lamports_for_vat(&rent)),
470 )
471 } else {
472 (
474 rent.minimum_balance(VoteStateV4::size_of()),
475 rent.minimum_balance(StakeStateV2::size_of()),
476 )
477 };
478
479 initial_accounts.push((
480 *mint_pubkey,
481 AccountSharedData::new(mint_lamports, 0, &system_program::id()),
482 ));
483 let mut validator_accounts = create_validator(
484 &rent,
485 *validator_pubkey,
486 validator_lamports,
487 *validator_vote_account_pubkey,
488 vote_account_lamports,
489 *validator_stake_account_pubkey,
490 stake_lamports,
491 validator_bls_pubkey,
492 );
493 initial_accounts.append(&mut validator_accounts);
494
495 let native_mint_account = solana_account::AccountSharedData::from(Account {
496 owner: spl_generic_token::token::id(),
497 data: spl_generic_token::token::native_mint::ACCOUNT_DATA.to_vec(),
498 lamports: LAMPORTS_PER_SOL,
499 executable: false,
500 rent_epoch: 1,
501 });
502 initial_accounts.push((
503 spl_generic_token::token::native_mint::id(),
504 native_mint_account,
505 ));
506
507 let mut genesis_config = GenesisConfig {
508 accounts: initial_accounts
509 .iter()
510 .cloned()
511 .map(|(key, account)| (key, Account::from(account)))
512 .collect(),
513 fee_rate_governor,
514 rent,
515 cluster_type,
516 ..GenesisConfig::default()
517 };
518
519 add_genesis_stake_config_account(&mut genesis_config);
520 add_genesis_epoch_rewards_account(&mut genesis_config);
521
522 genesis_config
523}
524
525#[expect(clippy::too_many_arguments)]
526pub fn create_genesis_config_with_leader_ex(
527 mint_lamports: u64,
528 mint_pubkey: &Pubkey,
529 validator_pubkey: &Pubkey,
530 validator_vote_account_pubkey: &Pubkey,
531 validator_stake_account_pubkey: &Pubkey,
532 validator_bls_pubkey: Option<[u8; BLS_PUBLIC_KEY_COMPRESSED_SIZE]>,
533 validator_stake_lamports: u64,
534 validator_lamports: u64,
535 fee_rate_governor: FeeRateGovernor,
536 rent: Rent,
537 cluster_type: ClusterType,
538 feature_set: &FeatureSet,
539 initial_accounts: Vec<(Pubkey, AccountSharedData)>,
540) -> GenesisConfig {
541 let mut genesis_config = create_genesis_config_with_leader_ex_no_features(
542 mint_lamports,
543 mint_pubkey,
544 validator_pubkey,
545 validator_vote_account_pubkey,
546 validator_stake_account_pubkey,
547 validator_bls_pubkey,
548 validator_stake_lamports,
549 validator_lamports,
550 fee_rate_governor,
551 rent,
552 cluster_type,
553 initial_accounts,
554 );
555
556 for feature_id in feature_set.active().keys() {
557 if *feature_id == agave_feature_set::alpenglow::id() {
559 continue;
560 }
561 activate_feature(&mut genesis_config, *feature_id);
562 }
563
564 genesis_config
565}
566
567#[expect(deprecated)]
568pub fn add_genesis_stake_config_account(genesis_config: &mut GenesisConfig) -> u64 {
569 let mut data = serialize(&ConfigKeys { keys: vec![] }).unwrap();
570 data.extend_from_slice(&serialize(&StakeConfig::default()).unwrap());
571 let lamports = std::cmp::max(genesis_config.rent.minimum_balance(data.len()), 1);
572 let account = AccountSharedData::from(Account {
573 lamports,
574 data,
575 owner: solana_sdk_ids::config::id(),
576 ..Account::default()
577 });
578
579 genesis_config.add_account(solana_stake_interface::config::id(), account);
580
581 lamports
582}
583
584pub fn add_genesis_epoch_rewards_account(genesis_config: &mut GenesisConfig) -> u64 {
585 let data = vec![0; EpochRewards::size_of()];
586 let lamports = std::cmp::max(genesis_config.rent.minimum_balance(data.len()), 1);
587
588 let account = AccountSharedData::create_from_existing_shared_data(
589 lamports,
590 Arc::new(data),
591 sysvar::id(),
592 false,
593 u64::MAX,
594 );
595
596 genesis_config.add_account(epoch_rewards::id(), account);
597
598 lamports
599}
600
601pub fn create_lockup_stake_account(
603 authorized: &Authorized,
604 lockup: &Lockup,
605 rent: &Rent,
606 lamports: u64,
607) -> AccountSharedData {
608 let mut stake_account =
609 AccountSharedData::new(lamports, StakeStateV2::size_of(), &stake_program::id());
610
611 let rent_exempt_reserve = rent.minimum_balance(stake_account.data().len());
612 assert!(
613 lamports >= rent_exempt_reserve,
614 "lamports: {lamports} is less than rent_exempt_reserve {rent_exempt_reserve}"
615 );
616
617 stake_account
618 .set_state(&StakeStateV2::Initialized(Meta {
619 authorized: *authorized,
620 lockup: *lockup,
621 #[expect(deprecated)]
622 rent_exempt_reserve,
623 }))
624 .expect("set_state");
625
626 stake_account
627}