solana_runtime/
genesis_utils.rs

1use {
2    solana_sdk::{
3        account::{Account, AccountSharedData},
4        feature::{self, Feature},
5        feature_set::FeatureSet,
6        fee_calculator::FeeRateGovernor,
7        genesis_config::{ClusterType, GenesisConfig},
8        pubkey::Pubkey,
9        rent::Rent,
10        signature::{Keypair, Signer},
11        stake::state::StakeState,
12        system_program,
13    },
14    solana_stake_program::stake_state,
15    solana_vote_program::vote_state,
16    std::borrow::Borrow,
17};
18
19// Default amount received by the validator
20const VALIDATOR_LAMPORTS: u64 = 42;
21
22// fun fact: rustc is very close to make this const fn.
23pub fn bootstrap_validator_stake_lamports() -> u64 {
24    Rent::default().minimum_balance(StakeState::size_of())
25}
26
27// Number of lamports automatically used for genesis accounts
28pub const fn genesis_sysvar_and_builtin_program_lamports() -> u64 {
29    const NUM_BUILTIN_PROGRAMS: u64 = 4;
30    const FEES_SYSVAR_MIN_BALANCE: u64 = 946_560;
31    const STAKE_HISTORY_MIN_BALANCE: u64 = 114_979_200;
32    const CLOCK_SYSVAR_MIN_BALANCE: u64 = 1_169_280;
33    const RENT_SYSVAR_MIN_BALANCE: u64 = 1_009_200;
34    const EPOCH_SCHEDULE_SYSVAR_MIN_BALANCE: u64 = 1_120_560;
35    const RECENT_BLOCKHASHES_SYSVAR_MIN_BALANCE: u64 = 42_706_560;
36
37    FEES_SYSVAR_MIN_BALANCE
38        + STAKE_HISTORY_MIN_BALANCE
39        + CLOCK_SYSVAR_MIN_BALANCE
40        + RENT_SYSVAR_MIN_BALANCE
41        + EPOCH_SCHEDULE_SYSVAR_MIN_BALANCE
42        + RECENT_BLOCKHASHES_SYSVAR_MIN_BALANCE
43        + NUM_BUILTIN_PROGRAMS
44}
45
46pub struct ValidatorVoteKeypairs {
47    pub node_keypair: Keypair,
48    pub vote_keypair: Keypair,
49    pub stake_keypair: Keypair,
50}
51
52impl ValidatorVoteKeypairs {
53    pub fn new(node_keypair: Keypair, vote_keypair: Keypair, stake_keypair: Keypair) -> Self {
54        Self {
55            node_keypair,
56            vote_keypair,
57            stake_keypair,
58        }
59    }
60
61    pub fn new_rand() -> Self {
62        Self {
63            node_keypair: Keypair::new(),
64            vote_keypair: Keypair::new(),
65            stake_keypair: Keypair::new(),
66        }
67    }
68}
69
70pub struct GenesisConfigInfo {
71    pub genesis_config: GenesisConfig,
72    pub mint_keypair: Keypair,
73    pub voting_keypair: Keypair,
74    pub validator_pubkey: Pubkey,
75}
76
77pub fn create_genesis_config(mint_lamports: u64) -> GenesisConfigInfo {
78    // Note that zero lamports for validator stake will result in stake account
79    // not being stored in accounts-db but still cached in bank stakes. This
80    // causes discrepancy between cached stakes accounts in bank and
81    // accounts-db which in particular will break snapshots test.
82    create_genesis_config_with_leader(
83        mint_lamports,
84        &solana_sdk::pubkey::new_rand(), // validator_pubkey
85        0,                               // validator_stake_lamports
86    )
87}
88
89pub fn create_genesis_config_with_vote_accounts(
90    mint_lamports: u64,
91    voting_keypairs: &[impl Borrow<ValidatorVoteKeypairs>],
92    stakes: Vec<u64>,
93) -> GenesisConfigInfo {
94    create_genesis_config_with_vote_accounts_and_cluster_type(
95        mint_lamports,
96        voting_keypairs,
97        stakes,
98        ClusterType::Development,
99    )
100}
101
102pub fn create_genesis_config_with_vote_accounts_and_cluster_type(
103    mint_lamports: u64,
104    voting_keypairs: &[impl Borrow<ValidatorVoteKeypairs>],
105    stakes: Vec<u64>,
106    cluster_type: ClusterType,
107) -> GenesisConfigInfo {
108    assert!(!voting_keypairs.is_empty());
109    assert_eq!(voting_keypairs.len(), stakes.len());
110
111    let mint_keypair = Keypair::new();
112    let voting_keypair =
113        Keypair::from_bytes(&voting_keypairs[0].borrow().vote_keypair.to_bytes()).unwrap();
114
115    let validator_pubkey = voting_keypairs[0].borrow().node_keypair.pubkey();
116    let genesis_config = create_genesis_config_with_leader_ex(
117        mint_lamports,
118        &mint_keypair.pubkey(),
119        &validator_pubkey,
120        &voting_keypairs[0].borrow().vote_keypair.pubkey(),
121        &voting_keypairs[0].borrow().stake_keypair.pubkey(),
122        stakes[0],
123        VALIDATOR_LAMPORTS,
124        FeeRateGovernor::new(0, 0), // most tests can't handle transaction fees
125        Rent::free(),               // most tests don't expect rent
126        cluster_type,
127        vec![],
128    );
129
130    let mut genesis_config_info = GenesisConfigInfo {
131        genesis_config,
132        mint_keypair,
133        voting_keypair,
134        validator_pubkey,
135    };
136
137    for (validator_voting_keypairs, stake) in voting_keypairs[1..].iter().zip(&stakes[1..]) {
138        let node_pubkey = validator_voting_keypairs.borrow().node_keypair.pubkey();
139        let vote_pubkey = validator_voting_keypairs.borrow().vote_keypair.pubkey();
140        let stake_pubkey = validator_voting_keypairs.borrow().stake_keypair.pubkey();
141
142        // Create accounts
143        let node_account = Account::new(VALIDATOR_LAMPORTS, 0, &system_program::id());
144        let vote_account = vote_state::create_account(&vote_pubkey, &node_pubkey, 0, *stake);
145        let stake_account = Account::from(stake_state::create_account(
146            &stake_pubkey,
147            &vote_pubkey,
148            &vote_account,
149            &genesis_config_info.genesis_config.rent,
150            *stake,
151        ));
152
153        let vote_account = Account::from(vote_account);
154
155        // Put newly created accounts into genesis
156        genesis_config_info.genesis_config.accounts.extend(vec![
157            (node_pubkey, node_account),
158            (vote_pubkey, vote_account),
159            (stake_pubkey, stake_account),
160        ]);
161    }
162
163    genesis_config_info
164}
165
166pub fn create_genesis_config_with_leader(
167    mint_lamports: u64,
168    validator_pubkey: &Pubkey,
169    validator_stake_lamports: u64,
170) -> GenesisConfigInfo {
171    let mint_keypair = Keypair::new();
172    let voting_keypair = Keypair::new();
173
174    let genesis_config = create_genesis_config_with_leader_ex(
175        mint_lamports,
176        &mint_keypair.pubkey(),
177        validator_pubkey,
178        &voting_keypair.pubkey(),
179        &solana_sdk::pubkey::new_rand(),
180        validator_stake_lamports,
181        VALIDATOR_LAMPORTS,
182        FeeRateGovernor::new(0, 0), // most tests can't handle transaction fees
183        Rent::free(),               // most tests don't expect rent
184        ClusterType::Development,
185        vec![],
186    );
187
188    GenesisConfigInfo {
189        genesis_config,
190        mint_keypair,
191        voting_keypair,
192        validator_pubkey: *validator_pubkey,
193    }
194}
195
196pub fn activate_all_features(genesis_config: &mut GenesisConfig) {
197    // Activate all features at genesis in development mode
198    for feature_id in FeatureSet::default().inactive {
199        activate_feature(genesis_config, feature_id);
200    }
201}
202
203pub fn activate_feature(genesis_config: &mut GenesisConfig, feature_id: Pubkey) {
204    genesis_config.accounts.insert(
205        feature_id,
206        Account::from(feature::create_account(
207            &Feature {
208                activated_at: Some(0),
209            },
210            std::cmp::max(genesis_config.rent.minimum_balance(Feature::size_of()), 1),
211        )),
212    );
213}
214
215#[allow(clippy::too_many_arguments)]
216pub fn create_genesis_config_with_leader_ex(
217    mint_lamports: u64,
218    mint_pubkey: &Pubkey,
219    validator_pubkey: &Pubkey,
220    validator_vote_account_pubkey: &Pubkey,
221    validator_stake_account_pubkey: &Pubkey,
222    validator_stake_lamports: u64,
223    validator_lamports: u64,
224    fee_rate_governor: FeeRateGovernor,
225    rent: Rent,
226    cluster_type: ClusterType,
227    mut initial_accounts: Vec<(Pubkey, AccountSharedData)>,
228) -> GenesisConfig {
229    let validator_vote_account = vote_state::create_account(
230        validator_vote_account_pubkey,
231        validator_pubkey,
232        0,
233        validator_stake_lamports,
234    );
235
236    let validator_stake_account = stake_state::create_account(
237        validator_stake_account_pubkey,
238        validator_vote_account_pubkey,
239        &validator_vote_account,
240        &rent,
241        validator_stake_lamports,
242    );
243
244    initial_accounts.push((
245        *mint_pubkey,
246        AccountSharedData::new(mint_lamports, 0, &system_program::id()),
247    ));
248    initial_accounts.push((
249        *validator_pubkey,
250        AccountSharedData::new(validator_lamports, 0, &system_program::id()),
251    ));
252    initial_accounts.push((*validator_vote_account_pubkey, validator_vote_account));
253    initial_accounts.push((*validator_stake_account_pubkey, validator_stake_account));
254
255    let mut genesis_config = GenesisConfig {
256        accounts: initial_accounts
257            .iter()
258            .cloned()
259            .map(|(key, account)| (key, Account::from(account)))
260            .collect(),
261        fee_rate_governor,
262        rent,
263        cluster_type,
264        ..GenesisConfig::default()
265    };
266
267    solana_stake_program::add_genesis_accounts(&mut genesis_config);
268    if genesis_config.cluster_type == ClusterType::Development {
269        activate_all_features(&mut genesis_config);
270    }
271
272    genesis_config
273}