solana_runtime/
genesis_utils.rs

1use {
2    agave_feature_set::{FeatureSet, FEATURE_NAMES},
3    log::*,
4    solana_account::{Account, AccountSharedData},
5    solana_feature_gate_interface::{self as feature, Feature},
6    solana_fee_calculator::FeeRateGovernor,
7    solana_genesis_config::{ClusterType, GenesisConfig},
8    solana_keypair::Keypair,
9    solana_native_token::sol_to_lamports,
10    solana_pubkey::Pubkey,
11    solana_rent::Rent,
12    solana_seed_derivable::SeedDerivable,
13    solana_signer::Signer,
14    solana_stake_interface::state::StakeStateV2,
15    solana_stake_program::stake_state,
16    solana_system_interface::program as system_program,
17    solana_vote_program::vote_state,
18    std::borrow::Borrow,
19};
20
21// Default amount received by the validator
22const VALIDATOR_LAMPORTS: u64 = 42;
23
24// fun fact: rustc is very close to make this const fn.
25pub fn bootstrap_validator_stake_lamports() -> u64 {
26    Rent::default().minimum_balance(StakeStateV2::size_of())
27}
28
29// Number of lamports automatically used for genesis accounts
30pub const fn genesis_sysvar_and_builtin_program_lamports() -> u64 {
31    const NUM_BUILTIN_PROGRAMS: u64 = 7;
32    const NUM_PRECOMPILES: u64 = 2;
33    const STAKE_HISTORY_MIN_BALANCE: u64 = 114_979_200;
34    const CLOCK_SYSVAR_MIN_BALANCE: u64 = 1_169_280;
35    const RENT_SYSVAR_MIN_BALANCE: u64 = 1_009_200;
36    const EPOCH_SCHEDULE_SYSVAR_MIN_BALANCE: u64 = 1_120_560;
37    const RECENT_BLOCKHASHES_SYSVAR_MIN_BALANCE: u64 = 42_706_560;
38
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_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    // Use deterministic keypair so we don't get confused by randomness in tests
173    let mint_keypair = Keypair::from_seed(&[
174        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,
175        25, 26, 27, 28, 29, 30, 31,
176    ])
177    .unwrap();
178
179    create_genesis_config_with_leader_with_mint_keypair(
180        mint_keypair,
181        mint_lamports,
182        validator_pubkey,
183        validator_stake_lamports,
184    )
185}
186
187pub fn create_genesis_config_with_leader_with_mint_keypair(
188    mint_keypair: Keypair,
189    mint_lamports: u64,
190    validator_pubkey: &Pubkey,
191    validator_stake_lamports: u64,
192) -> GenesisConfigInfo {
193    // Use deterministic keypair so we don't get confused by randomness in tests
194    let voting_keypair = Keypair::from_seed(&[
195        32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54,
196        55, 56, 57, 58, 59, 60, 61, 62, 63,
197    ])
198    .unwrap();
199
200    let genesis_config = create_genesis_config_with_leader_ex(
201        mint_lamports,
202        &mint_keypair.pubkey(),
203        validator_pubkey,
204        &voting_keypair.pubkey(),
205        &Pubkey::new_unique(),
206        validator_stake_lamports,
207        VALIDATOR_LAMPORTS,
208        FeeRateGovernor::new(0, 0), // most tests can't handle transaction fees
209        Rent::free(),               // most tests don't expect rent
210        ClusterType::Development,
211        vec![],
212    );
213
214    GenesisConfigInfo {
215        genesis_config,
216        mint_keypair,
217        voting_keypair,
218        validator_pubkey: *validator_pubkey,
219    }
220}
221
222pub fn activate_all_features(genesis_config: &mut GenesisConfig) {
223    // Activate all features at genesis in development mode
224    for feature_id in FeatureSet::default().inactive() {
225        activate_feature(genesis_config, *feature_id);
226    }
227}
228
229pub fn deactivate_features(
230    genesis_config: &mut GenesisConfig,
231    features_to_deactivate: &Vec<Pubkey>,
232) {
233    // Remove all features in `features_to_skip` from genesis
234    for deactivate_feature_pk in features_to_deactivate {
235        if FEATURE_NAMES.contains_key(deactivate_feature_pk) {
236            genesis_config.accounts.remove(deactivate_feature_pk);
237        } else {
238            warn!(
239                "Feature {:?} set for deactivation is not a known Feature public key",
240                deactivate_feature_pk
241            );
242        }
243    }
244}
245
246pub fn activate_feature(genesis_config: &mut GenesisConfig, feature_id: Pubkey) {
247    genesis_config.accounts.insert(
248        feature_id,
249        Account::from(feature::create_account(
250            &Feature {
251                activated_at: Some(0),
252            },
253            std::cmp::max(genesis_config.rent.minimum_balance(Feature::size_of()), 1),
254        )),
255    );
256}
257
258#[allow(clippy::too_many_arguments)]
259pub fn create_genesis_config_with_leader_ex_no_features(
260    mint_lamports: u64,
261    mint_pubkey: &Pubkey,
262    validator_pubkey: &Pubkey,
263    validator_vote_account_pubkey: &Pubkey,
264    validator_stake_account_pubkey: &Pubkey,
265    validator_stake_lamports: u64,
266    validator_lamports: u64,
267    fee_rate_governor: FeeRateGovernor,
268    rent: Rent,
269    cluster_type: ClusterType,
270    mut initial_accounts: Vec<(Pubkey, AccountSharedData)>,
271) -> GenesisConfig {
272    let validator_vote_account = vote_state::create_account(
273        validator_vote_account_pubkey,
274        validator_pubkey,
275        0,
276        validator_stake_lamports,
277    );
278
279    let validator_stake_account = stake_state::create_account(
280        validator_stake_account_pubkey,
281        validator_vote_account_pubkey,
282        &validator_vote_account,
283        &rent,
284        validator_stake_lamports,
285    );
286
287    initial_accounts.push((
288        *mint_pubkey,
289        AccountSharedData::new(mint_lamports, 0, &system_program::id()),
290    ));
291    initial_accounts.push((
292        *validator_pubkey,
293        AccountSharedData::new(validator_lamports, 0, &system_program::id()),
294    ));
295    initial_accounts.push((*validator_vote_account_pubkey, validator_vote_account));
296    initial_accounts.push((*validator_stake_account_pubkey, validator_stake_account));
297
298    let native_mint_account = solana_account::AccountSharedData::from(Account {
299        owner: spl_generic_token::token::id(),
300        data: spl_generic_token::token::native_mint::ACCOUNT_DATA.to_vec(),
301        lamports: sol_to_lamports(1.),
302        executable: false,
303        rent_epoch: 1,
304    });
305    initial_accounts.push((
306        spl_generic_token::token::native_mint::id(),
307        native_mint_account,
308    ));
309
310    let mut genesis_config = GenesisConfig {
311        accounts: initial_accounts
312            .iter()
313            .cloned()
314            .map(|(key, account)| (key, Account::from(account)))
315            .collect(),
316        fee_rate_governor,
317        rent,
318        cluster_type,
319        ..GenesisConfig::default()
320    };
321
322    solana_stake_program::add_genesis_accounts(&mut genesis_config);
323
324    genesis_config
325}
326
327#[allow(clippy::too_many_arguments)]
328pub fn create_genesis_config_with_leader_ex(
329    mint_lamports: u64,
330    mint_pubkey: &Pubkey,
331    validator_pubkey: &Pubkey,
332    validator_vote_account_pubkey: &Pubkey,
333    validator_stake_account_pubkey: &Pubkey,
334    validator_stake_lamports: u64,
335    validator_lamports: u64,
336    fee_rate_governor: FeeRateGovernor,
337    rent: Rent,
338    cluster_type: ClusterType,
339    initial_accounts: Vec<(Pubkey, AccountSharedData)>,
340) -> GenesisConfig {
341    let mut genesis_config = create_genesis_config_with_leader_ex_no_features(
342        mint_lamports,
343        mint_pubkey,
344        validator_pubkey,
345        validator_vote_account_pubkey,
346        validator_stake_account_pubkey,
347        validator_stake_lamports,
348        validator_lamports,
349        fee_rate_governor,
350        rent,
351        cluster_type,
352        initial_accounts,
353    );
354
355    if genesis_config.cluster_type == ClusterType::Development {
356        activate_all_features(&mut genesis_config);
357    }
358
359    genesis_config
360}