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