Line data Source code
1 : #define FD_SCRATCH_USE_HANDHOLDING 1
2 : #include "fd_genesis_create.h"
3 :
4 : #include "../runtime/fd_system_ids.h"
5 : #include "../runtime/program/fd_stake_program.h"
6 : #include "../runtime/program/fd_vote_program.h"
7 : #include "../runtime/sysvar/fd_sysvar_clock.h"
8 : #include "../runtime/sysvar/fd_sysvar_rent.h"
9 : #include "../types/fd_types.h"
10 :
11 : #define SORT_NAME sort_acct
12 744 : #define SORT_KEY_T fd_pubkey_account_pair_t
13 597 : #define SORT_BEFORE(a,b) (0>memcmp( (a).key.ul, (b).key.ul, sizeof(fd_pubkey_t) ))
14 : #include "../../util/tmpl/fd_sort.c"
15 :
16 : static ulong
17 : genesis_create( void * buf,
18 : ulong bufsz,
19 12 : fd_genesis_options_t const * options ) {
20 :
21 12 : # define REQUIRE(c) \
22 72 : do { \
23 72 : if( FD_UNLIKELY( !(c) ) ) { \
24 0 : FD_LOG_WARNING(( "FAIL: %s", #c )); \
25 0 : return 0UL; \
26 0 : } \
27 72 : } while(0);
28 :
29 12 : fd_genesis_solana_t genesis[1];
30 12 : fd_genesis_solana_new( genesis );
31 :
32 12 : genesis->cluster_type = 3; /* development */
33 :
34 12 : genesis->creation_time = options->creation_time;
35 12 : genesis->ticks_per_slot = options->ticks_per_slot;
36 12 : REQUIRE( genesis->ticks_per_slot );
37 :
38 12 : genesis->unused = 1024UL; /* match Anza genesis byte-for-byte */
39 :
40 12 : genesis->poh_config.has_hashes_per_tick = !!options->hashes_per_tick;
41 12 : genesis->poh_config.hashes_per_tick = options->hashes_per_tick;
42 :
43 12 : ulong target_tick_micros = options->target_tick_duration_micros;
44 12 : REQUIRE( target_tick_micros );
45 12 : genesis->poh_config.target_tick_duration = (fd_rust_duration_t) {
46 12 : .seconds = target_tick_micros / 1000000UL,
47 12 : .nanoseconds = (uint)( target_tick_micros % 1000000UL * 1000UL ),
48 12 : };
49 :
50 : /* Create fee rate governor */
51 :
52 12 : genesis->fee_rate_governor = (fd_fee_rate_governor_t) {
53 12 : .target_lamports_per_signature = 10000UL,
54 12 : .target_signatures_per_slot = 20000UL,
55 12 : .min_lamports_per_signature = 5000UL,
56 12 : .max_lamports_per_signature = 100000UL,
57 12 : .burn_percent = 50,
58 12 : };
59 :
60 : /* Create rent configuration */
61 :
62 12 : genesis->rent = (fd_rent_t) {
63 12 : .lamports_per_uint8_year = 3480,
64 12 : .exemption_threshold = 2.0,
65 12 : .burn_percent = 50,
66 12 : };
67 :
68 : /* Create inflation configuration */
69 :
70 12 : genesis->inflation = (fd_inflation_t) {
71 12 : .initial = 0.08,
72 12 : .terminal = 0.015,
73 12 : .taper = 0.15,
74 12 : .foundation = 0.05,
75 12 : .foundation_term = 7.0,
76 12 : };
77 :
78 : /* Create epoch schedule */
79 : /* TODO The epoch schedule should be configurable! */
80 :
81 : /* If warmup is enabled:
82 : MINIMUM_SLOTS_PER_EPOCH = 32
83 : first_normal_epoch = log2( slots_per_epoch ) - log2( MINIMUM_SLOTS_PER_EPOCH )
84 : first_normal_slot = MINIMUM_SLOTS_PER_EPOCH * ( 2^( first_normal_epoch ) - 1 )
85 : */
86 :
87 12 : genesis->epoch_schedule = (fd_epoch_schedule_t) {
88 12 : .slots_per_epoch = 8192UL,
89 12 : .leader_schedule_slot_offset = 8192UL,
90 12 : .warmup = fd_uchar_if( options->warmup_epochs, 1, 0 ),
91 12 : .first_normal_epoch = fd_ulong_if( options->warmup_epochs, 8UL, 0UL ),
92 12 : .first_normal_slot = fd_ulong_if( options->warmup_epochs, 8160UL, 0UL ),
93 12 : };
94 :
95 : /* Create faucet account */
96 :
97 12 : fd_pubkey_account_pair_t const faucet_account = {
98 12 : .key = options->faucet_pubkey,
99 12 : .account = {
100 12 : .lamports = options->faucet_balance,
101 12 : .owner = fd_solana_system_program_id
102 12 : }
103 12 : };
104 12 : ulong const faucet_account_index = genesis->accounts_len++;
105 :
106 : /* Create identity account (vote authority, withdraw authority) */
107 :
108 12 : fd_pubkey_account_pair_t const identity_account = {
109 12 : .key = options->identity_pubkey,
110 12 : .account = {
111 12 : .lamports = 500000000000UL /* 500 SOL */,
112 12 : .owner = fd_solana_system_program_id
113 12 : }
114 12 : };
115 12 : ulong const identity_account_index = genesis->accounts_len++;
116 :
117 : /* Create vote account */
118 :
119 12 : ulong const vote_account_index = genesis->accounts_len++;
120 :
121 12 : uchar vote_state_data[ FD_VOTE_STATE_V3_SZ ] = {0};
122 :
123 12 : FD_SCRATCH_SCOPE_BEGIN {
124 12 : fd_vote_state_versioned_t vsv[1];
125 12 : fd_vote_state_versioned_new_disc( vsv, fd_vote_state_versioned_enum_current );
126 :
127 12 : fd_vote_state_t * vs = &vsv->inner.current;
128 12 : vs->node_pubkey = options->identity_pubkey;
129 12 : vs->authorized_withdrawer = options->identity_pubkey;
130 12 : vs->commission = 100;
131 12 : vs->authorized_voters.pool = fd_vote_authorized_voters_pool_alloc ( fd_scratch_virtual(), 1UL );
132 12 : vs->authorized_voters.treap = fd_vote_authorized_voters_treap_alloc( fd_scratch_virtual(), 1UL );
133 :
134 12 : fd_vote_authorized_voter_t * ele =
135 12 : fd_vote_authorized_voters_pool_ele_acquire( vs->authorized_voters.pool );
136 12 : *ele = (fd_vote_authorized_voter_t) {
137 12 : .epoch = 0UL,
138 12 : .pubkey = options->identity_pubkey,
139 12 : .prio = options->identity_pubkey.ul[0], /* treap prio */
140 12 : };
141 12 : fd_vote_authorized_voters_treap_ele_insert( vs->authorized_voters.treap, ele, vs->authorized_voters.pool );
142 :
143 12 : fd_bincode_encode_ctx_t encode =
144 12 : { .data = vote_state_data,
145 12 : .dataend = vote_state_data + sizeof(vote_state_data) };
146 12 : REQUIRE( fd_vote_state_versioned_encode( vsv, &encode ) == FD_BINCODE_SUCCESS );
147 12 : }
148 12 : FD_SCRATCH_SCOPE_END;
149 :
150 : /* Create stake account */
151 :
152 12 : ulong const stake_account_index = genesis->accounts_len++;
153 :
154 12 : uchar stake_data[ FD_STAKE_STATE_V2_SZ ];
155 :
156 12 : ulong stake_state_min_bal = fd_rent_exempt_minimum_balance( &genesis->rent, FD_STAKE_STATE_V2_SZ );
157 12 : ulong vote_min_bal = fd_rent_exempt_minimum_balance( &genesis->rent, FD_VOTE_STATE_V3_SZ );
158 :
159 12 : do {
160 12 : fd_stake_state_v2_t state[1];
161 12 : fd_stake_state_v2_new_disc( state, fd_stake_state_v2_enum_stake );
162 :
163 12 : fd_stake_state_v2_stake_t * stake = &state->inner.stake;
164 12 : stake->meta = (fd_stake_meta_t) {
165 12 : .rent_exempt_reserve = stake_state_min_bal,
166 12 : .authorized = {
167 12 : .staker = options->identity_pubkey,
168 12 : .withdrawer = options->identity_pubkey,
169 12 : }
170 12 : };
171 12 : stake->stake = (fd_stake_t) {
172 12 : .delegation = (fd_delegation_t) {
173 12 : .voter_pubkey = options->vote_pubkey,
174 12 : .stake = fd_ulong_max( stake_state_min_bal, options->vote_account_stake ),
175 12 : .activation_epoch = ULONG_MAX, /* bootstrap stake denoted with ULONG_MAX */
176 12 : .deactivation_epoch = ULONG_MAX
177 12 : },
178 12 : .credits_observed = 0UL
179 12 : };
180 :
181 12 : fd_bincode_encode_ctx_t encode =
182 12 : { .data = stake_data,
183 12 : .dataend = stake_data + sizeof(stake_data) };
184 12 : REQUIRE( fd_stake_state_v2_encode( state, &encode ) == FD_BINCODE_SUCCESS );
185 12 : } while(0);
186 :
187 : /* Create stake config account */
188 :
189 12 : ulong const stake_cfg_account_index = genesis->accounts_len++;
190 :
191 12 : uchar stake_cfg_data[10];
192 12 : do {
193 12 : fd_stake_config_t config[1] = {{
194 12 : .config_keys_len = 0,
195 12 : .warmup_cooldown_rate = 0.25,
196 12 : .slash_penalty = 12
197 12 : }};
198 :
199 12 : fd_bincode_encode_ctx_t encode =
200 12 : { .data = stake_cfg_data,
201 12 : .dataend = stake_cfg_data + sizeof(stake_cfg_data) };
202 12 : REQUIRE( fd_stake_config_encode( config, &encode ) == FD_BINCODE_SUCCESS );
203 12 : REQUIRE( encode.data == encode.dataend );
204 12 : } while(0);
205 :
206 : /* Read enabled features */
207 :
208 12 : ulong feature_cnt = 0UL;
209 12 : fd_pubkey_t * features =
210 12 : fd_scratch_alloc( alignof(fd_pubkey_t), FD_FEATURE_ID_CNT * sizeof(fd_pubkey_t) );
211 :
212 12 : if( options->features ) {
213 3 : for( fd_feature_id_t const * id = fd_feature_iter_init();
214 612 : !fd_feature_iter_done( id );
215 609 : id = fd_feature_iter_next( id ) ) {
216 609 : if( fd_features_get( options->features, id ) == 0UL )
217 3 : features[ feature_cnt++ ] = id->id;
218 609 : }
219 3 : }
220 :
221 : /* Allocate the account table */
222 :
223 12 : ulong default_funded_cnt = options->fund_initial_accounts;
224 :
225 12 : ulong default_funded_idx = genesis->accounts_len; genesis->accounts_len += default_funded_cnt;
226 12 : ulong feature_gate_idx = genesis->accounts_len; genesis->accounts_len += feature_cnt;
227 :
228 12 : genesis->accounts = fd_scratch_alloc( alignof(fd_pubkey_account_pair_t),
229 12 : genesis->accounts_len * sizeof(fd_pubkey_account_pair_t) );
230 12 : fd_memset( genesis->accounts, 0, genesis->accounts_len * sizeof(fd_pubkey_account_pair_t) );
231 :
232 12 : genesis->accounts[ faucet_account_index ] = faucet_account;
233 12 : genesis->accounts[ identity_account_index ] = identity_account;
234 12 : genesis->accounts[ stake_account_index ] = (fd_pubkey_account_pair_t) {
235 12 : .key = options->stake_pubkey,
236 12 : .account = (fd_solana_account_t) {
237 12 : .lamports = fd_ulong_max( stake_state_min_bal, options->vote_account_stake ),
238 12 : .data_len = FD_STAKE_STATE_V2_SZ,
239 12 : .data = stake_data,
240 12 : .owner = fd_solana_stake_program_id
241 12 : }
242 12 : };
243 12 : genesis->accounts[ stake_cfg_account_index ] = (fd_pubkey_account_pair_t) {
244 12 : .key = fd_solana_stake_program_config_id,
245 12 : .account = (fd_solana_account_t) {
246 12 : .lamports = fd_rent_exempt_minimum_balance( &genesis->rent, sizeof(stake_cfg_data) ),
247 12 : .data_len = sizeof(stake_cfg_data),
248 12 : .data = stake_cfg_data,
249 12 : .owner = fd_solana_config_program_id
250 12 : }
251 12 : };
252 12 : genesis->accounts[ vote_account_index ] = (fd_pubkey_account_pair_t) {
253 12 : .key = options->vote_pubkey,
254 12 : .account = (fd_solana_account_t) {
255 12 : .lamports = vote_min_bal,
256 12 : .data_len = FD_VOTE_STATE_V3_SZ,
257 12 : .data = vote_state_data,
258 12 : .owner = fd_solana_vote_program_id
259 12 : }
260 12 : };
261 :
262 : /* Set up primordial accounts */
263 :
264 12 : ulong default_funded_balance = options->fund_initial_amount_lamports;
265 108 : for( ulong j=0UL; j<default_funded_cnt; j++ ) {
266 96 : fd_pubkey_account_pair_t * pair = &genesis->accounts[ default_funded_idx+j ];
267 :
268 96 : uchar privkey[ 32 ] = {0};
269 96 : FD_STORE( ulong, privkey, j );
270 96 : fd_sha512_t sha[1];
271 96 : fd_ed25519_public_from_private( pair->key.key, privkey, sha );
272 :
273 96 : pair->account = (fd_solana_account_t) {
274 96 : .lamports = default_funded_balance,
275 96 : .data_len = 0UL,
276 96 : .owner = fd_solana_system_program_id
277 96 : };
278 96 : }
279 :
280 15 : #define FEATURE_ENABLED_SZ 9UL
281 12 : static const uchar feature_enabled_data[ FEATURE_ENABLED_SZ ] = { 1, 0, 0, 0, 0, 0, 0, 0, 0 };
282 12 : ulong default_feature_enabled_balance = fd_rent_exempt_minimum_balance( &genesis->rent, FEATURE_ENABLED_SZ );
283 :
284 : /* Set up feature gate accounts */
285 15 : for( ulong j=0UL; j<feature_cnt; j++ ) {
286 3 : fd_pubkey_account_pair_t * pair = &genesis->accounts[ feature_gate_idx+j ];
287 :
288 3 : pair->key = features[ j ];
289 3 : pair->account = (fd_solana_account_t) {
290 3 : .lamports = default_feature_enabled_balance,
291 3 : .data_len = FEATURE_ENABLED_SZ,
292 3 : .data = (uchar *)feature_enabled_data,
293 3 : .owner = fd_solana_feature_program_id
294 3 : };
295 3 : }
296 12 : #undef FEATURE_ENABLED_SZ
297 :
298 : /* Sort and check for duplicates */
299 :
300 12 : sort_acct_inplace( genesis->accounts, genesis->accounts_len );
301 :
302 159 : for( ulong j=1UL; j < genesis->accounts_len; j++ ) {
303 147 : if( 0==memcmp( genesis->accounts[j-1].key.ul, genesis->accounts[j].key.ul, sizeof(fd_pubkey_t) ) ) {
304 0 : char dup_cstr[ FD_BASE58_ENCODED_32_SZ ];
305 0 : fd_base58_encode_32( genesis->accounts[j].key.uc, NULL, dup_cstr );
306 0 : FD_LOG_WARNING(( "Account %s is duplicate", dup_cstr ));
307 0 : return 0UL;
308 0 : }
309 147 : }
310 :
311 : /* Serialize bincode blob */
312 :
313 12 : fd_bincode_encode_ctx_t encode =
314 12 : { .data = buf,
315 12 : .dataend = (uchar *)buf + bufsz };
316 12 : int encode_err = fd_genesis_solana_encode( genesis, &encode );
317 12 : if( FD_UNLIKELY( encode_err ) ) {
318 3 : FD_LOG_WARNING(( "Failed to encode genesis blob (bufsz=%lu)", bufsz ));
319 3 : return 0UL;
320 3 : }
321 9 : return (ulong)encode.data - (ulong)buf;
322 :
323 12 : # undef REQUIRE
324 12 : }
325 :
326 : ulong
327 : fd_genesis_create( void * buf,
328 : ulong bufsz,
329 12 : fd_genesis_options_t const * options ) {
330 12 : fd_scratch_push();
331 12 : ulong ret = genesis_create( buf, bufsz, options );
332 12 : fd_scratch_pop();
333 12 : return ret;
334 12 : }
|