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