1#[allow(deprecated)]
2use solana_stake_interface::config::Config as StakeConfig;
3use {
4 crate::stake_utils,
5 agave_feature_set::{vote_state_v4, 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 &FeatureSet::all_enabled(),
120 false,
121 )
122}
123
124pub fn create_genesis_config_with_alpenglow_vote_accounts(
125 mint_lamports: u64,
126 voting_keypairs: &[impl Borrow<ValidatorVoteKeypairs>],
127 stakes: Vec<u64>,
128) -> GenesisConfigInfo {
129 create_genesis_config_with_vote_accounts_and_cluster_type(
130 mint_lamports,
131 voting_keypairs,
132 stakes,
133 ClusterType::Development,
134 &FeatureSet::all_enabled(),
135 true,
136 )
137}
138
139pub fn create_genesis_config_with_vote_accounts_and_cluster_type(
140 mint_lamports: u64,
141 voting_keypairs: &[impl Borrow<ValidatorVoteKeypairs>],
142 stakes: Vec<u64>,
143 cluster_type: ClusterType,
144 feature_set: &FeatureSet,
145 is_alpenglow: bool,
146) -> GenesisConfigInfo {
147 assert!(!voting_keypairs.is_empty());
148 assert_eq!(voting_keypairs.len(), stakes.len());
149
150 let mint_keypair = Keypair::new();
151 let voting_keypair = voting_keypairs[0].borrow().vote_keypair.insecure_clone();
152
153 let validator_pubkey = voting_keypairs[0].borrow().node_keypair.pubkey();
154 let validator_bls_pubkey = if is_alpenglow {
155 let bls_keypair = BLSKeypair::derive_from_signer(
156 &voting_keypairs[0].borrow().vote_keypair,
157 BLS_KEYPAIR_DERIVE_SEED,
158 )
159 .unwrap();
160 Some(bls_pubkey_to_compressed_bytes(&bls_keypair.public))
161 } else {
162 None
163 };
164 let genesis_config = create_genesis_config_with_leader_ex(
165 mint_lamports,
166 &mint_keypair.pubkey(),
167 &validator_pubkey,
168 &voting_keypairs[0].borrow().vote_keypair.pubkey(),
169 &voting_keypairs[0].borrow().stake_keypair.pubkey(),
170 validator_bls_pubkey,
171 stakes[0],
172 VALIDATOR_LAMPORTS,
173 FeeRateGovernor::new(0, 0), Rent::free(), cluster_type,
176 feature_set,
177 vec![],
178 );
179
180 let mut genesis_config_info = GenesisConfigInfo {
181 genesis_config,
182 mint_keypair,
183 voting_keypair,
184 validator_pubkey,
185 };
186
187 for (validator_voting_keypairs, stake) in voting_keypairs[1..].iter().zip(&stakes[1..]) {
188 let node_pubkey = validator_voting_keypairs.borrow().node_keypair.pubkey();
189 let vote_pubkey = validator_voting_keypairs.borrow().vote_keypair.pubkey();
190 let stake_pubkey = validator_voting_keypairs.borrow().stake_keypair.pubkey();
191
192 let node_account = Account::new(VALIDATOR_LAMPORTS, 0, &system_program::id());
194 let bls_pubkey_compressed = if is_alpenglow {
195 let bls_keypair = BLSKeypair::derive_from_signer(
196 &validator_voting_keypairs.borrow().vote_keypair,
197 BLS_KEYPAIR_DERIVE_SEED,
198 )
199 .unwrap();
200 Some(bls_pubkey_to_compressed_bytes(&bls_keypair.public))
201 } else {
202 None
203 };
204 let vote_account = if feature_set.is_active(&vote_state_v4::id()) {
205 vote_state::create_v4_account_with_authorized(
207 &node_pubkey,
208 &vote_pubkey,
209 &vote_pubkey,
210 bls_pubkey_compressed,
211 0,
212 *stake,
213 )
214 } else {
215 if bls_pubkey_compressed.is_some() {
217 warn!(
218 "BLS pubkey provided but vote_state_v4 feature is not active. BLS pubkey will \
219 be ignored."
220 );
221 }
222 vote_state::create_v3_account_with_authorized(
223 &node_pubkey,
224 &vote_pubkey,
225 &vote_pubkey,
226 0,
227 *stake,
228 )
229 };
230 let stake_account = Account::from(stake_utils::create_stake_account(
231 &stake_pubkey,
232 &vote_pubkey,
233 &vote_account,
234 &genesis_config_info.genesis_config.rent,
235 *stake,
236 ));
237
238 let vote_account = Account::from(vote_account);
239
240 genesis_config_info.genesis_config.accounts.extend(vec![
242 (node_pubkey, node_account),
243 (vote_pubkey, vote_account),
244 (stake_pubkey, stake_account),
245 ]);
246 }
247
248 genesis_config_info
249}
250
251pub fn create_genesis_config_with_leader(
252 mint_lamports: u64,
253 validator_pubkey: &Pubkey,
254 validator_stake_lamports: u64,
255) -> GenesisConfigInfo {
256 let mint_keypair = Keypair::from_seed(&[
258 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,
259 25, 26, 27, 28, 29, 30, 31,
260 ])
261 .unwrap();
262
263 create_genesis_config_with_leader_with_mint_keypair(
264 mint_keypair,
265 mint_lamports,
266 validator_pubkey,
267 validator_stake_lamports,
268 )
269}
270
271pub fn create_genesis_config_with_leader_with_mint_keypair(
272 mint_keypair: Keypair,
273 mint_lamports: u64,
274 validator_pubkey: &Pubkey,
275 validator_stake_lamports: u64,
276) -> GenesisConfigInfo {
277 let voting_keypair = Keypair::from_seed(&[
279 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54,
280 55, 56, 57, 58, 59, 60, 61, 62, 63,
281 ])
282 .unwrap();
283
284 let genesis_config = create_genesis_config_with_leader_ex(
285 mint_lamports,
286 &mint_keypair.pubkey(),
287 validator_pubkey,
288 &voting_keypair.pubkey(),
289 &Pubkey::new_unique(),
290 None,
291 validator_stake_lamports,
292 VALIDATOR_LAMPORTS,
293 FeeRateGovernor::new(0, 0), Rent::free(), ClusterType::Development,
296 &FeatureSet::all_enabled(),
297 vec![],
298 );
299
300 GenesisConfigInfo {
301 genesis_config,
302 mint_keypair,
303 voting_keypair,
304 validator_pubkey: *validator_pubkey,
305 }
306}
307
308pub fn activate_all_features_alpenglow(genesis_config: &mut GenesisConfig) {
309 do_activate_all_features::<true>(genesis_config);
310}
311
312pub fn activate_all_features(genesis_config: &mut GenesisConfig) {
313 do_activate_all_features::<false>(genesis_config);
314}
315
316fn do_activate_all_features<const IS_ALPENGLOW: bool>(genesis_config: &mut GenesisConfig) {
317 for feature_id in FeatureSet::default().inactive() {
319 if IS_ALPENGLOW || *feature_id != agave_feature_set::alpenglow::id() {
320 activate_feature(genesis_config, *feature_id);
321 }
322 }
323}
324
325pub fn deactivate_features(
326 genesis_config: &mut GenesisConfig,
327 features_to_deactivate: &Vec<Pubkey>,
328) {
329 for deactivate_feature_pk in features_to_deactivate {
331 if FEATURE_NAMES.contains_key(deactivate_feature_pk) {
332 genesis_config.accounts.remove(deactivate_feature_pk);
333 } else {
334 warn!(
335 "Feature {deactivate_feature_pk:?} set for deactivation is not a known Feature \
336 public key"
337 );
338 }
339 }
340}
341
342pub fn activate_feature(genesis_config: &mut GenesisConfig, feature_id: Pubkey) {
343 genesis_config.accounts.insert(
344 feature_id,
345 Account::from(feature::create_account(
346 &Feature {
347 activated_at: Some(0),
348 },
349 std::cmp::max(genesis_config.rent.minimum_balance(Feature::size_of()), 1),
350 )),
351 );
352}
353
354pub fn bls_pubkey_to_compressed_bytes(
355 bls_pubkey: &BLSPubkey,
356) -> [u8; BLS_PUBLIC_KEY_COMPRESSED_SIZE] {
357 let key = BLSPubkeyCompressed::try_from(bls_pubkey).unwrap();
358 bincode::serialize(&key).unwrap().try_into().unwrap()
359}
360
361#[allow(clippy::too_many_arguments)]
362pub fn create_genesis_config_with_leader_ex_no_features(
363 mint_lamports: u64,
364 mint_pubkey: &Pubkey,
365 validator_pubkey: &Pubkey,
366 validator_vote_account_pubkey: &Pubkey,
367 validator_stake_account_pubkey: &Pubkey,
368 validator_bls_pubkey: Option<[u8; BLS_PUBLIC_KEY_COMPRESSED_SIZE]>,
369 validator_stake_lamports: u64,
370 validator_lamports: u64,
371 fee_rate_governor: FeeRateGovernor,
372 rent: Rent,
373 cluster_type: ClusterType,
374 feature_set: &FeatureSet,
375 mut initial_accounts: Vec<(Pubkey, AccountSharedData)>,
376) -> GenesisConfig {
377 let validator_vote_account = if feature_set.is_active(&vote_state_v4::id()) {
378 vote_state::create_v4_account_with_authorized(
380 validator_pubkey,
381 validator_vote_account_pubkey,
382 validator_vote_account_pubkey,
383 validator_bls_pubkey,
384 0,
385 validator_stake_lamports,
386 )
387 } else {
388 if validator_bls_pubkey.is_some() {
390 warn!(
391 "BLS pubkey provided but vote_state_v4 feature is not active. BLS pubkey will be \
392 ignored."
393 );
394 }
395 vote_state::create_v3_account_with_authorized(
396 validator_pubkey,
397 validator_vote_account_pubkey,
398 validator_vote_account_pubkey,
399 0,
400 validator_stake_lamports,
401 )
402 };
403
404 let validator_stake_account = stake_utils::create_stake_account(
405 validator_stake_account_pubkey,
406 validator_vote_account_pubkey,
407 &validator_vote_account,
408 &rent,
409 validator_stake_lamports,
410 );
411
412 initial_accounts.push((
413 *mint_pubkey,
414 AccountSharedData::new(mint_lamports, 0, &system_program::id()),
415 ));
416 initial_accounts.push((
417 *validator_pubkey,
418 AccountSharedData::new(validator_lamports, 0, &system_program::id()),
419 ));
420 initial_accounts.push((*validator_vote_account_pubkey, validator_vote_account));
421 initial_accounts.push((*validator_stake_account_pubkey, validator_stake_account));
422
423 let native_mint_account = solana_account::AccountSharedData::from(Account {
424 owner: spl_generic_token::token::id(),
425 data: spl_generic_token::token::native_mint::ACCOUNT_DATA.to_vec(),
426 lamports: LAMPORTS_PER_SOL,
427 executable: false,
428 rent_epoch: 1,
429 });
430 initial_accounts.push((
431 spl_generic_token::token::native_mint::id(),
432 native_mint_account,
433 ));
434
435 let mut genesis_config = GenesisConfig {
436 accounts: initial_accounts
437 .iter()
438 .cloned()
439 .map(|(key, account)| (key, Account::from(account)))
440 .collect(),
441 fee_rate_governor,
442 rent,
443 cluster_type,
444 ..GenesisConfig::default()
445 };
446
447 add_genesis_stake_config_account(&mut genesis_config);
448 add_genesis_epoch_rewards_account(&mut genesis_config);
449
450 genesis_config
451}
452
453#[allow(clippy::too_many_arguments)]
454pub fn create_genesis_config_with_leader_ex(
455 mint_lamports: u64,
456 mint_pubkey: &Pubkey,
457 validator_pubkey: &Pubkey,
458 validator_vote_account_pubkey: &Pubkey,
459 validator_stake_account_pubkey: &Pubkey,
460 validator_bls_pubkey: Option<[u8; BLS_PUBLIC_KEY_COMPRESSED_SIZE]>,
461 validator_stake_lamports: u64,
462 validator_lamports: u64,
463 fee_rate_governor: FeeRateGovernor,
464 rent: Rent,
465 cluster_type: ClusterType,
466 feature_set: &FeatureSet,
467 initial_accounts: Vec<(Pubkey, AccountSharedData)>,
468) -> GenesisConfig {
469 let mut genesis_config = create_genesis_config_with_leader_ex_no_features(
470 mint_lamports,
471 mint_pubkey,
472 validator_pubkey,
473 validator_vote_account_pubkey,
474 validator_stake_account_pubkey,
475 validator_bls_pubkey,
476 validator_stake_lamports,
477 validator_lamports,
478 fee_rate_governor,
479 rent,
480 cluster_type,
481 feature_set,
482 initial_accounts,
483 );
484
485 for feature_id in feature_set.active().keys() {
486 if *feature_id == agave_feature_set::alpenglow::id() {
488 continue;
489 }
490 activate_feature(&mut genesis_config, *feature_id);
491 }
492
493 genesis_config
494}
495
496#[allow(deprecated)]
497pub fn add_genesis_stake_config_account(genesis_config: &mut GenesisConfig) -> u64 {
498 let mut data = serialize(&ConfigKeys { keys: vec![] }).unwrap();
499 data.extend_from_slice(&serialize(&StakeConfig::default()).unwrap());
500 let lamports = std::cmp::max(genesis_config.rent.minimum_balance(data.len()), 1);
501 let account = AccountSharedData::from(Account {
502 lamports,
503 data,
504 owner: solana_sdk_ids::config::id(),
505 ..Account::default()
506 });
507
508 genesis_config.add_account(solana_stake_interface::config::id(), account);
509
510 lamports
511}
512
513pub fn add_genesis_epoch_rewards_account(genesis_config: &mut GenesisConfig) -> u64 {
514 let data = vec![0; EpochRewards::size_of()];
515 let lamports = std::cmp::max(genesis_config.rent.minimum_balance(data.len()), 1);
516
517 let account = AccountSharedData::create(lamports, data, sysvar::id(), false, u64::MAX);
518
519 genesis_config.add_account(epoch_rewards::id(), account);
520
521 lamports
522}
523
524pub fn create_lockup_stake_account(
526 authorized: &Authorized,
527 lockup: &Lockup,
528 rent: &Rent,
529 lamports: u64,
530) -> AccountSharedData {
531 let mut stake_account =
532 AccountSharedData::new(lamports, StakeStateV2::size_of(), &stake_program::id());
533
534 let rent_exempt_reserve = rent.minimum_balance(stake_account.data().len());
535 assert!(
536 lamports >= rent_exempt_reserve,
537 "lamports: {lamports} is less than rent_exempt_reserve {rent_exempt_reserve}"
538 );
539
540 stake_account
541 .set_state(&StakeStateV2::Initialized(Meta {
542 authorized: *authorized,
543 lockup: *lockup,
544 rent_exempt_reserve,
545 }))
546 .expect("set_state");
547
548 stake_account
549}