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 : uchar * pool_mem = fd_scratch_alloc( fd_vote_authorized_voters_pool_align(), fd_vote_authorized_voters_pool_footprint( 1UL ) );
132 12 : vs->authorized_voters.pool = fd_vote_authorized_voters_pool_join( fd_vote_authorized_voters_pool_new( pool_mem, 1UL ) );
133 12 : uchar * mem = fd_scratch_alloc( fd_vote_authorized_voters_treap_align(), fd_vote_authorized_voters_treap_footprint( 1UL ) );
134 12 : vs->authorized_voters.treap = fd_vote_authorized_voters_treap_join( fd_vote_authorized_voters_treap_new( mem, 1UL ) );
135 :
136 12 : fd_vote_authorized_voter_t * ele =
137 12 : fd_vote_authorized_voters_pool_ele_acquire( vs->authorized_voters.pool );
138 12 : *ele = (fd_vote_authorized_voter_t) {
139 12 : .epoch = 0UL,
140 12 : .pubkey = options->identity_pubkey,
141 12 : .prio = options->identity_pubkey.ul[0], /* treap prio */
142 12 : };
143 12 : fd_vote_authorized_voters_treap_ele_insert( vs->authorized_voters.treap, ele, vs->authorized_voters.pool );
144 :
145 12 : fd_bincode_encode_ctx_t encode =
146 12 : { .data = vote_state_data,
147 12 : .dataend = vote_state_data + sizeof(vote_state_data) };
148 12 : REQUIRE( fd_vote_state_versioned_encode( vsv, &encode ) == FD_BINCODE_SUCCESS );
149 12 : }
150 12 : FD_SCRATCH_SCOPE_END;
151 :
152 : /* Create stake account */
153 :
154 12 : ulong const stake_account_index = genesis->accounts_len++;
155 :
156 12 : uchar stake_data[ FD_STAKE_STATE_V2_SZ ];
157 :
158 12 : ulong stake_state_min_bal = fd_rent_exempt_minimum_balance( &genesis->rent, FD_STAKE_STATE_V2_SZ );
159 12 : ulong vote_min_bal = fd_rent_exempt_minimum_balance( &genesis->rent, FD_VOTE_STATE_V3_SZ );
160 :
161 12 : do {
162 12 : fd_stake_state_v2_t state[1];
163 12 : fd_stake_state_v2_new_disc( state, fd_stake_state_v2_enum_stake );
164 :
165 12 : fd_stake_state_v2_stake_t * stake = &state->inner.stake;
166 12 : stake->meta = (fd_stake_meta_t) {
167 12 : .rent_exempt_reserve = stake_state_min_bal,
168 12 : .authorized = {
169 12 : .staker = options->identity_pubkey,
170 12 : .withdrawer = options->identity_pubkey,
171 12 : }
172 12 : };
173 12 : stake->stake = (fd_stake_t) {
174 12 : .delegation = (fd_delegation_t) {
175 12 : .voter_pubkey = options->vote_pubkey,
176 12 : .stake = fd_ulong_max( stake_state_min_bal, options->vote_account_stake ),
177 12 : .activation_epoch = ULONG_MAX, /* bootstrap stake denoted with ULONG_MAX */
178 12 : .deactivation_epoch = ULONG_MAX
179 12 : },
180 12 : .credits_observed = 0UL
181 12 : };
182 :
183 12 : fd_bincode_encode_ctx_t encode =
184 12 : { .data = stake_data,
185 12 : .dataend = stake_data + sizeof(stake_data) };
186 12 : REQUIRE( fd_stake_state_v2_encode( state, &encode ) == FD_BINCODE_SUCCESS );
187 12 : } while(0);
188 :
189 : /* Create stake config account */
190 :
191 12 : ulong const stake_cfg_account_index = genesis->accounts_len++;
192 :
193 12 : uchar stake_cfg_data[10];
194 12 : do {
195 12 : fd_stake_config_t config[1] = {{
196 12 : .config_keys_len = 0,
197 12 : .warmup_cooldown_rate = 0.25,
198 12 : .slash_penalty = 12
199 12 : }};
200 :
201 12 : fd_bincode_encode_ctx_t encode =
202 12 : { .data = stake_cfg_data,
203 12 : .dataend = stake_cfg_data + sizeof(stake_cfg_data) };
204 12 : REQUIRE( fd_stake_config_encode( config, &encode ) == FD_BINCODE_SUCCESS );
205 12 : REQUIRE( encode.data == encode.dataend );
206 12 : } while(0);
207 :
208 : /* Read enabled features */
209 :
210 12 : ulong feature_cnt = 0UL;
211 12 : fd_pubkey_t * features =
212 12 : fd_scratch_alloc( alignof(fd_pubkey_t), FD_FEATURE_ID_CNT * sizeof(fd_pubkey_t) );
213 :
214 12 : if( options->features ) {
215 3 : for( fd_feature_id_t const * id = fd_feature_iter_init();
216 687 : !fd_feature_iter_done( id );
217 684 : id = fd_feature_iter_next( id ) ) {
218 684 : if( fd_features_get( options->features, id ) == 0UL )
219 3 : features[ feature_cnt++ ] = id->id;
220 684 : }
221 3 : }
222 :
223 : /* Allocate the account table */
224 :
225 12 : ulong default_funded_cnt = options->fund_initial_accounts;
226 :
227 12 : ulong default_funded_idx = genesis->accounts_len; genesis->accounts_len += default_funded_cnt;
228 12 : ulong feature_gate_idx = genesis->accounts_len; genesis->accounts_len += feature_cnt;
229 :
230 12 : genesis->accounts = fd_scratch_alloc( alignof(fd_pubkey_account_pair_t),
231 12 : genesis->accounts_len * sizeof(fd_pubkey_account_pair_t) );
232 12 : fd_memset( genesis->accounts, 0, genesis->accounts_len * sizeof(fd_pubkey_account_pair_t) );
233 :
234 12 : genesis->accounts[ faucet_account_index ] = faucet_account;
235 12 : genesis->accounts[ identity_account_index ] = identity_account;
236 12 : genesis->accounts[ stake_account_index ] = (fd_pubkey_account_pair_t) {
237 12 : .key = options->stake_pubkey,
238 12 : .account = (fd_solana_account_t) {
239 12 : .lamports = fd_ulong_max( stake_state_min_bal, options->vote_account_stake ),
240 12 : .data_len = FD_STAKE_STATE_V2_SZ,
241 12 : .data = stake_data,
242 12 : .owner = fd_solana_stake_program_id
243 12 : }
244 12 : };
245 12 : genesis->accounts[ stake_cfg_account_index ] = (fd_pubkey_account_pair_t) {
246 12 : .key = fd_solana_stake_program_config_id,
247 12 : .account = (fd_solana_account_t) {
248 12 : .lamports = fd_rent_exempt_minimum_balance( &genesis->rent, sizeof(stake_cfg_data) ),
249 12 : .data_len = sizeof(stake_cfg_data),
250 12 : .data = stake_cfg_data,
251 12 : .owner = fd_solana_config_program_id
252 12 : }
253 12 : };
254 12 : genesis->accounts[ vote_account_index ] = (fd_pubkey_account_pair_t) {
255 12 : .key = options->vote_pubkey,
256 12 : .account = (fd_solana_account_t) {
257 12 : .lamports = vote_min_bal,
258 12 : .data_len = FD_VOTE_STATE_V3_SZ,
259 12 : .data = vote_state_data,
260 12 : .owner = fd_solana_vote_program_id
261 12 : }
262 12 : };
263 :
264 : /* Set up primordial accounts */
265 :
266 12 : ulong default_funded_balance = options->fund_initial_amount_lamports;
267 108 : for( ulong j=0UL; j<default_funded_cnt; j++ ) {
268 96 : fd_pubkey_account_pair_t * pair = &genesis->accounts[ default_funded_idx+j ];
269 :
270 96 : uchar privkey[ 32 ] = {0};
271 96 : FD_STORE( ulong, privkey, j );
272 96 : fd_sha512_t sha[1];
273 96 : fd_ed25519_public_from_private( pair->key.key, privkey, sha );
274 :
275 96 : pair->account = (fd_solana_account_t) {
276 96 : .lamports = default_funded_balance,
277 96 : .data_len = 0UL,
278 96 : .owner = fd_solana_system_program_id
279 96 : };
280 96 : }
281 :
282 15 : #define FEATURE_ENABLED_SZ 9UL
283 12 : static const uchar feature_enabled_data[ FEATURE_ENABLED_SZ ] = { 1, 0, 0, 0, 0, 0, 0, 0, 0 };
284 12 : ulong default_feature_enabled_balance = fd_rent_exempt_minimum_balance( &genesis->rent, FEATURE_ENABLED_SZ );
285 :
286 : /* Set up feature gate accounts */
287 15 : for( ulong j=0UL; j<feature_cnt; j++ ) {
288 3 : fd_pubkey_account_pair_t * pair = &genesis->accounts[ feature_gate_idx+j ];
289 :
290 3 : pair->key = features[ j ];
291 3 : pair->account = (fd_solana_account_t) {
292 3 : .lamports = default_feature_enabled_balance,
293 3 : .data_len = FEATURE_ENABLED_SZ,
294 3 : .data = (uchar *)feature_enabled_data,
295 3 : .owner = fd_solana_feature_program_id
296 3 : };
297 3 : }
298 12 : #undef FEATURE_ENABLED_SZ
299 :
300 : /* Sort and check for duplicates */
301 :
302 12 : sort_acct_inplace( genesis->accounts, genesis->accounts_len );
303 :
304 159 : for( ulong j=1UL; j < genesis->accounts_len; j++ ) {
305 147 : if( 0==memcmp( genesis->accounts[j-1].key.ul, genesis->accounts[j].key.ul, sizeof(fd_pubkey_t) ) ) {
306 0 : char dup_cstr[ FD_BASE58_ENCODED_32_SZ ];
307 0 : fd_base58_encode_32( genesis->accounts[j].key.uc, NULL, dup_cstr );
308 0 : FD_LOG_WARNING(( "Account %s is duplicate", dup_cstr ));
309 0 : return 0UL;
310 0 : }
311 147 : }
312 :
313 : /* Serialize bincode blob */
314 :
315 12 : fd_bincode_encode_ctx_t encode =
316 12 : { .data = buf,
317 12 : .dataend = (uchar *)buf + bufsz };
318 12 : int encode_err = fd_genesis_solana_encode( genesis, &encode );
319 12 : if( FD_UNLIKELY( encode_err ) ) {
320 3 : FD_LOG_WARNING(( "Failed to encode genesis blob (bufsz=%lu)", bufsz ));
321 3 : return 0UL;
322 3 : }
323 9 : return (ulong)encode.data - (ulong)buf;
324 :
325 12 : # undef REQUIRE
326 12 : }
327 :
328 : ulong
329 : fd_genesis_create( void * buf,
330 : ulong bufsz,
331 12 : fd_genesis_options_t const * options ) {
332 12 : fd_scratch_push();
333 12 : ulong ret = genesis_create( buf, bufsz, options );
334 12 : fd_scratch_pop();
335 12 : return ret;
336 12 : }
|