miraland_runtime/
genesis_utils.rs

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