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