solana_runtime/
genesis_utils.rs

1use {
2    log::*,
3    solana_feature_set::{FeatureSet, FEATURE_NAMES},
4    solana_sdk::{
5        account::{Account, AccountSharedData},
6        feature::{self, Feature},
7        fee_calculator::FeeRateGovernor,
8        genesis_config::{ClusterType, GenesisConfig},
9        native_token::sol_to_lamports,
10        pubkey::Pubkey,
11        rent::Rent,
12        signature::{Keypair, Signer},
13        signer::SeedDerivable,
14        stake::state::StakeStateV2,
15        system_program,
16    },
17    solana_stake_program::stake_state,
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 = 9;
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(genesis_config: &mut GenesisConfig) {
224    // Activate all features at genesis in development mode
225    for feature_id in FeatureSet::default().inactive {
226        activate_feature(genesis_config, feature_id);
227    }
228}
229
230pub fn deactivate_features(
231    genesis_config: &mut GenesisConfig,
232    features_to_deactivate: &Vec<Pubkey>,
233) {
234    // Remove all features in `features_to_skip` from genesis
235    for deactivate_feature_pk in features_to_deactivate {
236        if FEATURE_NAMES.contains_key(deactivate_feature_pk) {
237            genesis_config.accounts.remove(deactivate_feature_pk);
238        } else {
239            warn!(
240                "Feature {:?} set for deactivation is not a known Feature public key",
241                deactivate_feature_pk
242            );
243        }
244    }
245}
246
247pub fn activate_feature(genesis_config: &mut GenesisConfig, feature_id: Pubkey) {
248    genesis_config.accounts.insert(
249        feature_id,
250        Account::from(feature::create_account(
251            &Feature {
252                activated_at: Some(0),
253            },
254            std::cmp::max(genesis_config.rent.minimum_balance(Feature::size_of()), 1),
255        )),
256    );
257}
258
259#[allow(clippy::too_many_arguments)]
260pub fn create_genesis_config_with_leader_ex_no_features(
261    mint_lamports: u64,
262    mint_pubkey: &Pubkey,
263    validator_pubkey: &Pubkey,
264    validator_vote_account_pubkey: &Pubkey,
265    validator_stake_account_pubkey: &Pubkey,
266    validator_stake_lamports: u64,
267    validator_lamports: u64,
268    fee_rate_governor: FeeRateGovernor,
269    rent: Rent,
270    cluster_type: ClusterType,
271    mut initial_accounts: Vec<(Pubkey, AccountSharedData)>,
272) -> GenesisConfig {
273    let validator_vote_account = vote_state::create_account(
274        validator_vote_account_pubkey,
275        validator_pubkey,
276        0,
277        validator_stake_lamports,
278    );
279
280    let validator_stake_account = stake_state::create_account(
281        validator_stake_account_pubkey,
282        validator_vote_account_pubkey,
283        &validator_vote_account,
284        &rent,
285        validator_stake_lamports,
286    );
287
288    initial_accounts.push((
289        *mint_pubkey,
290        AccountSharedData::new(mint_lamports, 0, &system_program::id()),
291    ));
292    initial_accounts.push((
293        *validator_pubkey,
294        AccountSharedData::new(validator_lamports, 0, &system_program::id()),
295    ));
296    initial_accounts.push((*validator_vote_account_pubkey, validator_vote_account));
297    initial_accounts.push((*validator_stake_account_pubkey, validator_stake_account));
298
299    let native_mint_account = solana_sdk::account::AccountSharedData::from(Account {
300        owner: solana_inline_spl::token::id(),
301        data: solana_inline_spl::token::native_mint::ACCOUNT_DATA.to_vec(),
302        lamports: sol_to_lamports(1.),
303        executable: false,
304        rent_epoch: 1,
305    });
306    initial_accounts.push((
307        solana_inline_spl::token::native_mint::id(),
308        native_mint_account,
309    ));
310
311    let mut genesis_config = GenesisConfig {
312        accounts: initial_accounts
313            .iter()
314            .cloned()
315            .map(|(key, account)| (key, Account::from(account)))
316            .collect(),
317        fee_rate_governor,
318        rent,
319        cluster_type,
320        ..GenesisConfig::default()
321    };
322
323    solana_stake_program::add_genesis_accounts(&mut genesis_config);
324
325    genesis_config
326}
327
328#[allow(clippy::too_many_arguments)]
329pub fn create_genesis_config_with_leader_ex(
330    mint_lamports: u64,
331    mint_pubkey: &Pubkey,
332    validator_pubkey: &Pubkey,
333    validator_vote_account_pubkey: &Pubkey,
334    validator_stake_account_pubkey: &Pubkey,
335    validator_stake_lamports: u64,
336    validator_lamports: u64,
337    fee_rate_governor: FeeRateGovernor,
338    rent: Rent,
339    cluster_type: ClusterType,
340    initial_accounts: Vec<(Pubkey, AccountSharedData)>,
341) -> GenesisConfig {
342    let mut genesis_config = create_genesis_config_with_leader_ex_no_features(
343        mint_lamports,
344        mint_pubkey,
345        validator_pubkey,
346        validator_vote_account_pubkey,
347        validator_stake_account_pubkey,
348        validator_stake_lamports,
349        validator_lamports,
350        fee_rate_governor,
351        rent,
352        cluster_type,
353        initial_accounts,
354    );
355
356    if genesis_config.cluster_type == ClusterType::Development {
357        activate_all_features(&mut genesis_config);
358    }
359
360    genesis_config
361}