solana_runtime/
genesis_utils.rs

1use {
2    agave_feature_set::{FeatureSet, FEATURE_NAMES},
3    log::*,
4    solana_account::{Account, AccountSharedData},
5    solana_cluster_type::ClusterType,
6    solana_feature_gate_interface::{self as feature, Feature},
7    solana_fee_calculator::FeeRateGovernor,
8    solana_genesis_config::GenesisConfig,
9    solana_keypair::Keypair,
10    solana_native_token::LAMPORTS_PER_SOL,
11    solana_pubkey::Pubkey,
12    solana_rent::Rent,
13    solana_seed_derivable::SeedDerivable,
14    solana_signer::Signer,
15    solana_stake_interface::state::StakeStateV2,
16    solana_stake_program::stake_state,
17    solana_system_interface::program as system_program,
18    solana_vote_program::vote_state,
19    std::borrow::Borrow,
20};
21
22// Default amount received by the validator
23const VALIDATOR_LAMPORTS: u64 = 42;
24
25// fun fact: rustc is very close to make this const fn.
26pub fn bootstrap_validator_stake_lamports() -> u64 {
27    Rent::default().minimum_balance(StakeStateV2::size_of())
28}
29
30// Number of lamports automatically used for genesis accounts
31pub const fn genesis_sysvar_and_builtin_program_lamports() -> u64 {
32    const NUM_BUILTIN_PROGRAMS: u64 = 7;
33    const NUM_PRECOMPILES: u64 = 2;
34    const STAKE_HISTORY_MIN_BALANCE: u64 = 114_979_200;
35    const CLOCK_SYSVAR_MIN_BALANCE: u64 = 1_169_280;
36    const RENT_SYSVAR_MIN_BALANCE: u64 = 1_009_200;
37    const EPOCH_SCHEDULE_SYSVAR_MIN_BALANCE: u64 = 1_120_560;
38    const RECENT_BLOCKHASHES_SYSVAR_MIN_BALANCE: u64 = 42_706_560;
39
40    STAKE_HISTORY_MIN_BALANCE
41        + CLOCK_SYSVAR_MIN_BALANCE
42        + RENT_SYSVAR_MIN_BALANCE
43        + EPOCH_SCHEDULE_SYSVAR_MIN_BALANCE
44        + RECENT_BLOCKHASHES_SYSVAR_MIN_BALANCE
45        + NUM_BUILTIN_PROGRAMS
46        + NUM_PRECOMPILES
47}
48
49pub struct ValidatorVoteKeypairs {
50    pub node_keypair: Keypair,
51    pub vote_keypair: Keypair,
52    pub stake_keypair: Keypair,
53}
54
55impl ValidatorVoteKeypairs {
56    pub fn new(node_keypair: Keypair, vote_keypair: Keypair, stake_keypair: Keypair) -> Self {
57        Self {
58            node_keypair,
59            vote_keypair,
60            stake_keypair,
61        }
62    }
63
64    pub fn new_rand() -> Self {
65        Self {
66            node_keypair: Keypair::new(),
67            vote_keypair: Keypair::new(),
68            stake_keypair: Keypair::new(),
69        }
70    }
71}
72
73pub struct GenesisConfigInfo {
74    pub genesis_config: GenesisConfig,
75    pub mint_keypair: Keypair,
76    pub voting_keypair: Keypair,
77    pub validator_pubkey: Pubkey,
78}
79
80pub fn create_genesis_config(mint_lamports: u64) -> GenesisConfigInfo {
81    // Note that zero lamports for validator stake will result in stake account
82    // not being stored in accounts-db but still cached in bank stakes. This
83    // causes discrepancy between cached stakes accounts in bank and
84    // accounts-db which in particular will break snapshots test.
85    create_genesis_config_with_leader(
86        mint_lamports,
87        &solana_pubkey::new_rand(), // validator_pubkey
88        0,                          // validator_stake_lamports
89    )
90}
91
92pub fn create_genesis_config_with_vote_accounts(
93    mint_lamports: u64,
94    voting_keypairs: &[impl Borrow<ValidatorVoteKeypairs>],
95    stakes: Vec<u64>,
96) -> GenesisConfigInfo {
97    create_genesis_config_with_vote_accounts_and_cluster_type(
98        mint_lamports,
99        voting_keypairs,
100        stakes,
101        ClusterType::Development,
102    )
103}
104
105pub fn create_genesis_config_with_vote_accounts_and_cluster_type(
106    mint_lamports: u64,
107    voting_keypairs: &[impl Borrow<ValidatorVoteKeypairs>],
108    stakes: Vec<u64>,
109    cluster_type: ClusterType,
110) -> GenesisConfigInfo {
111    assert!(!voting_keypairs.is_empty());
112    assert_eq!(voting_keypairs.len(), stakes.len());
113
114    let mint_keypair = Keypair::new();
115    let voting_keypair = voting_keypairs[0].borrow().vote_keypair.insecure_clone();
116
117    let validator_pubkey = voting_keypairs[0].borrow().node_keypair.pubkey();
118    let genesis_config = create_genesis_config_with_leader_ex(
119        mint_lamports,
120        &mint_keypair.pubkey(),
121        &validator_pubkey,
122        &voting_keypairs[0].borrow().vote_keypair.pubkey(),
123        &voting_keypairs[0].borrow().stake_keypair.pubkey(),
124        stakes[0],
125        VALIDATOR_LAMPORTS,
126        FeeRateGovernor::new(0, 0), // most tests can't handle transaction fees
127        Rent::free(),               // most tests don't expect rent
128        cluster_type,
129        vec![],
130    );
131
132    let mut genesis_config_info = GenesisConfigInfo {
133        genesis_config,
134        mint_keypair,
135        voting_keypair,
136        validator_pubkey,
137    };
138
139    for (validator_voting_keypairs, stake) in voting_keypairs[1..].iter().zip(&stakes[1..]) {
140        let node_pubkey = validator_voting_keypairs.borrow().node_keypair.pubkey();
141        let vote_pubkey = validator_voting_keypairs.borrow().vote_keypair.pubkey();
142        let stake_pubkey = validator_voting_keypairs.borrow().stake_keypair.pubkey();
143
144        // Create accounts
145        let node_account = Account::new(VALIDATOR_LAMPORTS, 0, &system_program::id());
146        let vote_account = vote_state::create_account(&vote_pubkey, &node_pubkey, 0, *stake);
147        let stake_account = Account::from(stake_state::create_account(
148            &stake_pubkey,
149            &vote_pubkey,
150            &vote_account,
151            &genesis_config_info.genesis_config.rent,
152            *stake,
153        ));
154
155        let vote_account = Account::from(vote_account);
156
157        // Put newly created accounts into genesis
158        genesis_config_info.genesis_config.accounts.extend(vec![
159            (node_pubkey, node_account),
160            (vote_pubkey, vote_account),
161            (stake_pubkey, stake_account),
162        ]);
163    }
164
165    genesis_config_info
166}
167
168pub fn create_genesis_config_with_leader(
169    mint_lamports: u64,
170    validator_pubkey: &Pubkey,
171    validator_stake_lamports: u64,
172) -> GenesisConfigInfo {
173    // Use deterministic keypair so we don't get confused by randomness in tests
174    let mint_keypair = Keypair::from_seed(&[
175        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,
176        25, 26, 27, 28, 29, 30, 31,
177    ])
178    .unwrap();
179
180    create_genesis_config_with_leader_with_mint_keypair(
181        mint_keypair,
182        mint_lamports,
183        validator_pubkey,
184        validator_stake_lamports,
185    )
186}
187
188pub fn create_genesis_config_with_leader_with_mint_keypair(
189    mint_keypair: Keypair,
190    mint_lamports: u64,
191    validator_pubkey: &Pubkey,
192    validator_stake_lamports: u64,
193) -> GenesisConfigInfo {
194    // Use deterministic keypair so we don't get confused by randomness in tests
195    let voting_keypair = Keypair::from_seed(&[
196        32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54,
197        55, 56, 57, 58, 59, 60, 61, 62, 63,
198    ])
199    .unwrap();
200
201    let genesis_config = create_genesis_config_with_leader_ex(
202        mint_lamports,
203        &mint_keypair.pubkey(),
204        validator_pubkey,
205        &voting_keypair.pubkey(),
206        &Pubkey::new_unique(),
207        validator_stake_lamports,
208        VALIDATOR_LAMPORTS,
209        FeeRateGovernor::new(0, 0), // most tests can't handle transaction fees
210        Rent::free(),               // most tests don't expect rent
211        ClusterType::Development,
212        vec![],
213    );
214
215    GenesisConfigInfo {
216        genesis_config,
217        mint_keypair,
218        voting_keypair,
219        validator_pubkey: *validator_pubkey,
220    }
221}
222
223pub fn activate_all_features_alpenglow(genesis_config: &mut GenesisConfig) {
224    do_activate_all_features::<true>(genesis_config);
225}
226
227pub fn activate_all_features(genesis_config: &mut GenesisConfig) {
228    do_activate_all_features::<false>(genesis_config);
229}
230
231fn do_activate_all_features<const IS_ALPENGLOW: bool>(genesis_config: &mut GenesisConfig) {
232    // Activate all features at genesis in development mode
233    for feature_id in FeatureSet::default().inactive() {
234        if IS_ALPENGLOW || *feature_id != agave_feature_set::alpenglow::id() {
235            activate_feature(genesis_config, *feature_id);
236        }
237    }
238}
239
240pub fn deactivate_features(
241    genesis_config: &mut GenesisConfig,
242    features_to_deactivate: &Vec<Pubkey>,
243) {
244    // Remove all features in `features_to_skip` from genesis
245    for deactivate_feature_pk in features_to_deactivate {
246        if FEATURE_NAMES.contains_key(deactivate_feature_pk) {
247            genesis_config.accounts.remove(deactivate_feature_pk);
248        } else {
249            warn!(
250                "Feature {deactivate_feature_pk:?} set for deactivation is not a known Feature \
251                 public key"
252            );
253        }
254    }
255}
256
257pub fn activate_feature(genesis_config: &mut GenesisConfig, feature_id: Pubkey) {
258    genesis_config.accounts.insert(
259        feature_id,
260        Account::from(feature::create_account(
261            &Feature {
262                activated_at: Some(0),
263            },
264            std::cmp::max(genesis_config.rent.minimum_balance(Feature::size_of()), 1),
265        )),
266    );
267}
268
269#[allow(clippy::too_many_arguments)]
270pub fn create_genesis_config_with_leader_ex_no_features(
271    mint_lamports: u64,
272    mint_pubkey: &Pubkey,
273    validator_pubkey: &Pubkey,
274    validator_vote_account_pubkey: &Pubkey,
275    validator_stake_account_pubkey: &Pubkey,
276    validator_stake_lamports: u64,
277    validator_lamports: u64,
278    fee_rate_governor: FeeRateGovernor,
279    rent: Rent,
280    cluster_type: ClusterType,
281    mut initial_accounts: Vec<(Pubkey, AccountSharedData)>,
282) -> GenesisConfig {
283    let validator_vote_account = vote_state::create_account(
284        validator_vote_account_pubkey,
285        validator_pubkey,
286        0,
287        validator_stake_lamports,
288    );
289
290    let validator_stake_account = stake_state::create_account(
291        validator_stake_account_pubkey,
292        validator_vote_account_pubkey,
293        &validator_vote_account,
294        &rent,
295        validator_stake_lamports,
296    );
297
298    initial_accounts.push((
299        *mint_pubkey,
300        AccountSharedData::new(mint_lamports, 0, &system_program::id()),
301    ));
302    initial_accounts.push((
303        *validator_pubkey,
304        AccountSharedData::new(validator_lamports, 0, &system_program::id()),
305    ));
306    initial_accounts.push((*validator_vote_account_pubkey, validator_vote_account));
307    initial_accounts.push((*validator_stake_account_pubkey, validator_stake_account));
308
309    let native_mint_account = solana_account::AccountSharedData::from(Account {
310        owner: spl_generic_token::token::id(),
311        data: spl_generic_token::token::native_mint::ACCOUNT_DATA.to_vec(),
312        lamports: LAMPORTS_PER_SOL,
313        executable: false,
314        rent_epoch: 1,
315    });
316    initial_accounts.push((
317        spl_generic_token::token::native_mint::id(),
318        native_mint_account,
319    ));
320
321    let mut genesis_config = GenesisConfig {
322        accounts: initial_accounts
323            .iter()
324            .cloned()
325            .map(|(key, account)| (key, Account::from(account)))
326            .collect(),
327        fee_rate_governor,
328        rent,
329        cluster_type,
330        ..GenesisConfig::default()
331    };
332
333    solana_stake_program::add_genesis_accounts(&mut genesis_config);
334
335    genesis_config
336}
337
338#[allow(clippy::too_many_arguments)]
339pub fn create_genesis_config_with_leader_ex(
340    mint_lamports: u64,
341    mint_pubkey: &Pubkey,
342    validator_pubkey: &Pubkey,
343    validator_vote_account_pubkey: &Pubkey,
344    validator_stake_account_pubkey: &Pubkey,
345    validator_stake_lamports: u64,
346    validator_lamports: u64,
347    fee_rate_governor: FeeRateGovernor,
348    rent: Rent,
349    cluster_type: ClusterType,
350    initial_accounts: Vec<(Pubkey, AccountSharedData)>,
351) -> GenesisConfig {
352    let mut genesis_config = create_genesis_config_with_leader_ex_no_features(
353        mint_lamports,
354        mint_pubkey,
355        validator_pubkey,
356        validator_vote_account_pubkey,
357        validator_stake_account_pubkey,
358        validator_stake_lamports,
359        validator_lamports,
360        fee_rate_governor,
361        rent,
362        cluster_type,
363        initial_accounts,
364    );
365
366    if genesis_config.cluster_type == ClusterType::Development {
367        activate_all_features(&mut genesis_config);
368    }
369
370    genesis_config
371}