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 bitvec::vec::BitVec,
17 log::*,
18 solana_account::{
19 Account, AccountSharedData, ReadableAccount, state_traits::StateMutWincode as _,
20 },
21 solana_bls_signatures::{
22 BLS_SIGNATURE_AFFINE_SIZE, Pubkey as BLSPubkey, Signature as BLSSignature,
23 keypair::Keypair as BLSKeypair, pubkey::PubkeyCompressed as BLSPubkeyCompressed,
24 },
25 solana_clock::Epoch,
26 solana_cluster_type::ClusterType,
27 solana_config_interface::state::ConfigKeys,
28 solana_feature_gate_interface::{self as feature, Feature},
29 solana_fee_calculator::FeeRateGovernor,
30 solana_genesis_config::GenesisConfig,
31 solana_hash::Hash,
32 solana_keypair::Keypair,
33 solana_native_token::LAMPORTS_PER_SOL,
34 solana_pubkey::Pubkey,
35 solana_rent::Rent,
36 solana_sdk_ids::{stake as stake_program, sysvar},
37 solana_seed_derivable::SeedDerivable,
38 solana_signer::Signer,
39 solana_signer_store::encode_base2,
40 solana_stake_interface::state::{Authorized, Lockup, Meta, StakeStateV2},
41 solana_system_interface::program as system_program,
42 solana_sysvar::epoch_rewards,
43 solana_vote_interface::state::{BLS_PUBLIC_KEY_COMPRESSED_SIZE, VoteStateV4},
44 solana_vote_program::vote_state,
45 std::{borrow::Borrow, sync::Arc},
46 wincode::{SchemaRead, SchemaWrite},
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 = wincode::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 wincode::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#[cfg_attr(feature = "frozen-abi", derive(StableAbi, StableAbiSample))]
576#[derive(SchemaRead, SchemaWrite)]
577struct SerializableStakeConfig {
578 warmup_cooldown_rate: f64,
579 slash_penalty: u8,
580}
581
582#[cfg_attr(
587 feature = "frozen-abi",
588 derive(StableAbi, StableAbiSample),
589 frozen_abi(
590 abi_digest = "FrxVmDThystn6yVz3PjV9BTxGKocrq7LcKfk4VYk5efq",
591 abi_serializer = "wincode",
592 test_roundtrip = "wire_only"
593 )
594)]
595#[derive(SchemaRead, SchemaWrite)]
596struct GenesisStakeConfigAccount {
597 #[cfg_attr(
599 feature = "frozen-abi",
600 stable_abi_sample(with = "sample_config_keys(rng)")
601 )]
602 keys: ConfigKeys,
603 config: SerializableStakeConfig,
604}
605
606#[cfg(feature = "frozen-abi")]
607fn sample_config_keys(rng: &mut (impl solana_frozen_abi::rand::RngCore + ?Sized)) -> ConfigKeys {
608 use solana_frozen_abi::stable_abi::{context::SequenceLenMax, sample_collection_sized};
609 ConfigKeys {
610 keys: sample_collection_sized(rng, SequenceLenMax(4)),
611 }
612}
613
614#[expect(deprecated)]
615pub fn add_genesis_stake_config_account(genesis_config: &mut GenesisConfig) -> u64 {
616 let StakeConfig {
617 warmup_cooldown_rate,
618 slash_penalty,
619 } = StakeConfig::default();
620 let data = wincode::serialize(&GenesisStakeConfigAccount {
621 keys: ConfigKeys { keys: vec![] },
622 config: SerializableStakeConfig {
623 warmup_cooldown_rate,
624 slash_penalty,
625 },
626 })
627 .unwrap();
628 let lamports = std::cmp::max(genesis_config.rent.minimum_balance(data.len()), 1);
629 let account = AccountSharedData::from(Account {
630 lamports,
631 data,
632 owner: solana_sdk_ids::config::id(),
633 ..Account::default()
634 });
635
636 genesis_config.add_account(solana_stake_interface::config::id(), account);
637
638 lamports
639}
640
641pub fn add_genesis_epoch_rewards_account(genesis_config: &mut GenesisConfig) -> u64 {
642 let data = vec![0; epoch_rewards::SIZE];
643 let lamports = std::cmp::max(genesis_config.rent.minimum_balance(data.len()), 1);
644
645 let account = AccountSharedData::create_from_existing_shared_data(
646 lamports,
647 Arc::new(data),
648 sysvar::id(),
649 false,
650 u64::MAX,
651 );
652
653 genesis_config.add_account(epoch_rewards::id(), account);
654
655 lamports
656}
657
658pub fn create_lockup_stake_account(
660 authorized: &Authorized,
661 lockup: &Lockup,
662 rent: &Rent,
663 lamports: u64,
664) -> AccountSharedData {
665 let mut stake_account =
666 AccountSharedData::new(lamports, StakeStateV2::size_of(), &stake_program::id());
667
668 let rent_exempt_reserve = rent.minimum_balance(stake_account.data().len());
669 assert!(
670 lamports >= rent_exempt_reserve,
671 "lamports: {lamports} is less than rent_exempt_reserve {rent_exempt_reserve}"
672 );
673
674 stake_account
675 .set_state(&StakeStateV2::Initialized(Meta {
676 authorized: *authorized,
677 lockup: *lockup,
678 #[expect(deprecated)]
679 rent_exempt_reserve,
680 }))
681 .expect("set_state");
682
683 stake_account
684}
685
686#[cfg(test)]
687mod tests {
688 use super::*;
689
690 #[test]
693 #[expect(deprecated)]
694 fn test_genesis_stake_config_account_layout() {
695 let config = StakeConfig::default();
696 let mut expected = wincode::serialize(&ConfigKeys { keys: vec![] }).unwrap();
697 expected.extend_from_slice(
698 &wincode::serialize(&SerializableStakeConfig {
699 warmup_cooldown_rate: config.warmup_cooldown_rate,
700 slash_penalty: config.slash_penalty,
701 })
702 .unwrap(),
703 );
704
705 let mut genesis_config = GenesisConfig::default();
706 add_genesis_stake_config_account(&mut genesis_config);
707 let account = &genesis_config.accounts[&solana_stake_interface::config::id()];
708 assert_eq!(account.data, expected);
709 }
710}