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