Line data Source code
1 : #include "fd_block_harness.h"
2 :
3 : /* Stripped down version of `fd_refresh_vote_accounts()` that simply refreshes the stake delegation amount
4 : for each of the vote accounts using the stake delegations cache. */
5 : static void
6 : fd_runtime_fuzz_block_refresh_vote_accounts( fd_vote_accounts_pair_global_t_mapnode_t * vote_accounts_pool,
7 : fd_vote_accounts_pair_global_t_mapnode_t * vote_accounts_root,
8 : fd_delegation_pair_t_mapnode_t * stake_delegations_pool,
9 0 : fd_delegation_pair_t_mapnode_t * stake_delegations_root ) {
10 0 : for( fd_delegation_pair_t_mapnode_t * node = fd_delegation_pair_t_map_minimum( stake_delegations_pool, stake_delegations_root );
11 0 : node;
12 0 : node = fd_delegation_pair_t_map_successor( stake_delegations_pool, node ) ) {
13 0 : fd_pubkey_t * voter_pubkey = &node->elem.delegation.voter_pubkey;
14 0 : ulong stake = node->elem.delegation.stake;
15 :
16 : /* Find the voter in the vote accounts cache and update their delegation amount */
17 0 : fd_vote_accounts_pair_global_t_mapnode_t vode_node[1];
18 0 : fd_memcpy( vode_node->elem.key.uc, voter_pubkey, sizeof(fd_pubkey_t) );
19 0 : fd_vote_accounts_pair_global_t_mapnode_t * found_node = fd_vote_accounts_pair_global_t_map_find( vote_accounts_pool, vote_accounts_root, vode_node );
20 0 : if( FD_LIKELY( found_node ) ) {
21 0 : found_node->elem.stake += stake;
22 0 : }
23 0 : }
24 0 : }
25 :
26 : /* Registers a single vote account into the current votes cache. The entry is derived
27 : from the current present account state. This function also registers a vote timestamp
28 : for the vote account */
29 : static void
30 : fd_runtime_fuzz_block_register_vote_account( fd_exec_slot_ctx_t * slot_ctx,
31 : fd_vote_accounts_pair_global_t_mapnode_t * pool,
32 : fd_vote_accounts_pair_global_t_mapnode_t ** root,
33 : fd_pubkey_t * pubkey,
34 0 : fd_spad_t * spad ) {
35 0 : FD_TXN_ACCOUNT_DECL( acc );
36 0 : if( FD_UNLIKELY( fd_txn_account_init_from_funk_readonly( acc, pubkey, slot_ctx->funk, slot_ctx->funk_txn ) ) ) {
37 0 : return;
38 0 : }
39 :
40 : /* Account must be owned by the vote program */
41 0 : if( memcmp( acc->vt->get_owner( acc ), fd_solana_vote_program_id.key, sizeof(fd_pubkey_t) ) ) {
42 0 : return;
43 0 : }
44 :
45 : /* Account must have > 0 lamports */
46 0 : if( acc->vt->get_lamports( acc )==0UL ) {
47 0 : return;
48 0 : }
49 :
50 : /* Account must be initialized correctly */
51 0 : if( FD_UNLIKELY( !fd_vote_state_versions_is_correct_and_initialized( acc ) ) ) {
52 0 : return;
53 0 : }
54 :
55 : /* Get the vote state from the account data */
56 0 : fd_vote_state_versioned_t * vsv = NULL;
57 0 : int err = fd_vote_get_state( acc, spad, &vsv );
58 0 : if( FD_UNLIKELY( err ) ) {
59 0 : return;
60 0 : }
61 :
62 : /* Nothing to do if the account already exists in the cache */
63 0 : fd_vote_accounts_pair_global_t_mapnode_t existing_node[1];
64 0 : fd_memcpy( existing_node->elem.key.uc, pubkey, sizeof(fd_pubkey_t) );
65 0 : if( fd_vote_accounts_pair_global_t_map_find( pool, *root, existing_node ) ) {
66 0 : return;
67 0 : }
68 :
69 : /* At this point, the node is new and needs to be inserted into the cache. */
70 0 : fd_vote_accounts_pair_global_t_mapnode_t * node_to_insert = fd_vote_accounts_pair_global_t_map_acquire( pool );
71 0 : fd_memcpy( node_to_insert->elem.key.uc, pubkey, sizeof(fd_pubkey_t) );
72 :
73 0 : ulong account_dlen = acc->vt->get_data_len( acc );
74 0 : node_to_insert->elem.stake = 0UL; // This will get set later
75 0 : node_to_insert->elem.value.executable = !!acc->vt->is_executable( acc );
76 0 : node_to_insert->elem.value.lamports = acc->vt->get_lamports( acc );
77 0 : node_to_insert->elem.value.rent_epoch = acc->vt->get_rent_epoch( acc );
78 0 : node_to_insert->elem.value.data_len = account_dlen;
79 :
80 0 : uchar * data = fd_spad_alloc( spad, alignof(uchar), account_dlen );
81 0 : memcpy( data, acc->vt->get_data( acc ), account_dlen );
82 0 : fd_solana_account_data_update( &node_to_insert->elem.value, data );
83 :
84 0 : fd_vote_accounts_pair_global_t_map_insert( pool, root, node_to_insert );
85 :
86 : /* Record a timestamp for the vote account */
87 0 : fd_vote_block_timestamp_t const * ts = NULL;
88 0 : switch( vsv->discriminant ) {
89 0 : case fd_vote_state_versioned_enum_v0_23_5:
90 0 : ts = &vsv->inner.v0_23_5.last_timestamp;
91 0 : break;
92 0 : case fd_vote_state_versioned_enum_v1_14_11:
93 0 : ts = &vsv->inner.v1_14_11.last_timestamp;
94 0 : break;
95 0 : case fd_vote_state_versioned_enum_current:
96 0 : ts = &vsv->inner.current.last_timestamp;
97 0 : break;
98 0 : default:
99 0 : __builtin_unreachable();
100 0 : }
101 :
102 0 : fd_vote_record_timestamp_vote_with_slot( pubkey, ts->timestamp, ts->slot, slot_ctx->bank );
103 0 : }
104 :
105 : /* Stores an entry in the stake delegations cache for the given vote account. Deserializes and uses the present
106 : account state to derive delegation information. */
107 : static void
108 : fd_runtime_fuzz_block_register_stake_delegation( fd_exec_slot_ctx_t * slot_ctx,
109 : fd_delegation_pair_t_mapnode_t * pool,
110 : fd_delegation_pair_t_mapnode_t ** root,
111 0 : fd_pubkey_t * pubkey ) {
112 0 : FD_TXN_ACCOUNT_DECL( acc );
113 0 : if( FD_UNLIKELY( fd_txn_account_init_from_funk_readonly( acc, pubkey, slot_ctx->funk, slot_ctx->funk_txn ) ) ) {
114 0 : return;
115 0 : }
116 :
117 : /* Account must be owned by the stake program */
118 0 : if( memcmp( acc->vt->get_owner( acc ), fd_solana_stake_program_id.key, sizeof(fd_pubkey_t) ) ) {
119 0 : return;
120 0 : }
121 :
122 : /* Account must have > 0 lamports */
123 0 : if( acc->vt->get_lamports( acc )==0UL ) {
124 0 : return;
125 0 : }
126 :
127 : /* Stake state must exist and be initialized correctly */
128 0 : fd_stake_state_v2_t stake_state;
129 0 : if( FD_UNLIKELY( fd_stake_get_state( acc, &stake_state ) || !fd_stake_state_v2_is_stake( &stake_state ) ) ) {
130 0 : return;
131 0 : }
132 :
133 : /* Skip 0-stake accounts */
134 0 : if( FD_UNLIKELY( stake_state.inner.stake.stake.delegation.stake==0UL ) ) {
135 0 : return;
136 0 : }
137 :
138 : /* Nothing to do if the account already exists in the cache */
139 0 : fd_delegation_pair_t_mapnode_t existing_node[1];
140 0 : fd_memcpy( existing_node->elem.account.uc, pubkey, sizeof(fd_pubkey_t) );
141 0 : if( fd_delegation_pair_t_map_find( pool, *root, existing_node ) ) {
142 0 : return;
143 0 : }
144 :
145 : /* At this point, the node is new and needs to be inserted into the cache. */
146 0 : fd_delegation_pair_t_mapnode_t * node_to_insert = fd_delegation_pair_t_map_acquire( pool );
147 0 : fd_memcpy( node_to_insert->elem.account.uc, pubkey, sizeof(fd_pubkey_t) );
148 :
149 0 : node_to_insert->elem.account = *pubkey;
150 0 : node_to_insert->elem.delegation = stake_state.inner.stake.stake.delegation;
151 :
152 0 : fd_delegation_pair_t_map_insert( pool, root, node_to_insert );
153 0 : }
154 :
155 : /* Common helper method for populating a previous epoch's vote cache. */
156 : static void
157 : fd_runtime_fuzz_block_update_prev_epoch_votes_cache( fd_vote_accounts_pair_global_t_mapnode_t * pool,
158 : fd_vote_accounts_pair_global_t_mapnode_t ** root,
159 : fd_exec_test_vote_account_t * vote_accounts,
160 : pb_size_t vote_accounts_cnt,
161 0 : fd_spad_t * spad ) {
162 0 : for( uint i=0U; i<vote_accounts_cnt; i++ ) {
163 0 : fd_exec_test_acct_state_t * vote_account = &vote_accounts[i].vote_account;
164 0 : ulong stake = vote_accounts[i].stake;
165 :
166 0 : fd_vote_accounts_pair_global_t_mapnode_t * vote_node = fd_vote_accounts_pair_global_t_map_acquire( pool );
167 0 : vote_node->elem.stake = stake;
168 0 : fd_memcpy( &vote_node->elem.key, vote_account->address, sizeof(fd_pubkey_t) );
169 0 : vote_node->elem.value.executable = vote_account->executable;
170 0 : vote_node->elem.value.lamports = vote_account->lamports;
171 0 : vote_node->elem.value.rent_epoch = vote_account->rent_epoch;
172 0 : vote_node->elem.value.data_len = vote_account->data->size;
173 0 : fd_memcpy( &vote_node->elem.value.owner, vote_account->owner, sizeof(fd_pubkey_t) );
174 :
175 0 : uchar * data = fd_spad_alloc( spad, alignof(uchar), vote_account->data->size );
176 0 : memcpy( data, vote_account->data->bytes, vote_account->data->size );
177 0 : fd_solana_account_data_update( &vote_node->elem.value, data );
178 :
179 0 : fd_vote_accounts_pair_global_t_map_insert( pool, root, vote_node );
180 0 : }
181 0 : }
182 :
183 : static void
184 : fd_runtime_fuzz_block_ctx_destroy( fd_runtime_fuzz_runner_t * runner,
185 0 : fd_wksp_t * wksp ) {
186 0 : fd_funk_txn_cancel_all( runner->funk, 1 );
187 0 : fd_wksp_detach( wksp );
188 0 : }
189 :
190 : /* Sets up block execution context from an input test case to execute against the runtime.
191 : Returns block_info on success and NULL on failure. */
192 : static fd_runtime_block_info_t *
193 : fd_runtime_fuzz_block_ctx_create( fd_runtime_fuzz_runner_t * runner,
194 : fd_exec_slot_ctx_t * slot_ctx,
195 0 : fd_exec_test_block_context_t const * test_ctx ) {
196 0 : fd_funk_t * funk = runner->funk;
197 :
198 0 : slot_ctx->banks = runner->banks;
199 0 : slot_ctx->bank = runner->bank;
200 0 : fd_banks_clear_bank( slot_ctx->banks, slot_ctx->bank );
201 :
202 : /* Generate unique ID for funk txn */
203 0 : fd_funk_txn_xid_t xid[1] = {0};
204 0 : xid[0] = fd_funk_generate_xid();
205 :
206 : /* Create temporary funk transaction and slot / epoch contexts */
207 0 : fd_funk_txn_start_write( funk );
208 0 : fd_funk_txn_t * funk_txn = fd_funk_txn_prepare( funk, NULL, xid, 1 );
209 0 : fd_funk_txn_end_write( funk );
210 :
211 : /* Allocate contexts */
212 0 : ulong vote_acct_max = fd_ulong_max( 128UL, test_ctx->acct_states_count );
213 :
214 : /* Restore feature flags */
215 0 : fd_features_t features = {0};
216 0 : if( !fd_runtime_fuzz_restore_features( &features, &test_ctx->epoch_ctx.features ) ) {
217 0 : return NULL;
218 0 : }
219 0 : fd_bank_features_set( slot_ctx->bank, features );
220 :
221 : /* Set up slot context */
222 0 : ulong slot = test_ctx->slot_ctx.slot;
223 :
224 0 : slot_ctx->funk_txn = funk_txn;
225 0 : slot_ctx->funk = funk;
226 0 : runner->bank->slot_ = slot;
227 :
228 0 : fd_hash_t * bank_hash = fd_bank_bank_hash_modify( slot_ctx->bank );
229 0 : fd_memcpy( bank_hash, test_ctx->slot_ctx.parent_bank_hash, sizeof(fd_hash_t) );
230 :
231 : /* All bank mgr stuff here. */
232 :
233 : /* Initialize vote timestamps cache */
234 0 : fd_clock_timestamp_votes_global_t * clock_timestamp_votes = fd_bank_clock_timestamp_votes_locking_modify( slot_ctx->bank );
235 0 : uchar * pool_mem = (uchar *)fd_ulong_align_up( (ulong)clock_timestamp_votes + sizeof(fd_clock_timestamp_votes_global_t), fd_clock_timestamp_vote_t_map_align() );
236 0 : fd_clock_timestamp_vote_t_mapnode_t * clock_pool = fd_clock_timestamp_vote_t_map_join( fd_clock_timestamp_vote_t_map_new( pool_mem, 15000UL ) );
237 0 : fd_clock_timestamp_vote_t_mapnode_t * clock_root = NULL;
238 :
239 0 : fd_clock_timestamp_votes_votes_pool_update( clock_timestamp_votes, clock_pool );
240 0 : fd_clock_timestamp_votes_votes_root_update( clock_timestamp_votes, clock_root );
241 0 : fd_bank_clock_timestamp_votes_end_locking_modify( slot_ctx->bank );
242 :
243 0 : slot_ctx->bank->slot_ = slot;
244 :
245 0 : fd_bank_block_height_set( slot_ctx->bank, test_ctx->slot_ctx.block_height );
246 :
247 0 : fd_bank_parent_slot_set( slot_ctx->bank, test_ctx->slot_ctx.prev_slot );
248 :
249 0 : fd_bank_capitalization_set( slot_ctx->bank, test_ctx->slot_ctx.prev_epoch_capitalization );
250 :
251 0 : fd_bank_lamports_per_signature_set( slot_ctx->bank, 5000UL );
252 :
253 0 : fd_bank_prev_lamports_per_signature_set( slot_ctx->bank, test_ctx->slot_ctx.prev_lps );
254 :
255 : // self.max_tick_height = (self.slot + 1) * self.ticks_per_slot;
256 0 : fd_bank_hashes_per_tick_set( slot_ctx->bank, test_ctx->epoch_ctx.hashes_per_tick );
257 :
258 0 : fd_bank_ticks_per_slot_set( slot_ctx->bank, test_ctx->epoch_ctx.ticks_per_slot );
259 :
260 0 : fd_bank_ns_per_slot_set( slot_ctx->bank, 400000000 ); // TODO: restore from input
261 :
262 0 : fd_bank_genesis_creation_time_set( slot_ctx->bank, test_ctx->epoch_ctx.genesis_creation_time );
263 :
264 0 : fd_bank_slots_per_year_set( slot_ctx->bank, test_ctx->epoch_ctx.slots_per_year );
265 :
266 0 : fd_fee_rate_governor_t * fee_rate_governor = fd_bank_fee_rate_governor_modify( slot_ctx->bank );
267 0 : fee_rate_governor->target_lamports_per_signature = 10000UL;
268 0 : fee_rate_governor->target_signatures_per_slot = 20000UL;
269 0 : fee_rate_governor->min_lamports_per_signature = 5000UL;
270 0 : fee_rate_governor->max_lamports_per_signature = 100000UL;
271 0 : fee_rate_governor->burn_percent = 50;
272 :
273 0 : fd_inflation_t * inflation = fd_bank_inflation_modify( slot_ctx->bank );
274 0 : inflation->initial = test_ctx->epoch_ctx.inflation.initial;
275 0 : inflation->terminal = test_ctx->epoch_ctx.inflation.terminal;
276 0 : inflation->taper = test_ctx->epoch_ctx.inflation.taper;
277 0 : inflation->foundation = test_ctx->epoch_ctx.inflation.foundation;
278 0 : inflation->foundation_term = test_ctx->epoch_ctx.inflation.foundation_term;
279 :
280 0 : fd_bank_block_height_set( slot_ctx->bank, test_ctx->slot_ctx.block_height );
281 :
282 : // /* Initialize the current running epoch stake and vote accounts */
283 :
284 : /* TODO: should be stake account max */
285 0 : fd_account_keys_global_t * stake_account_keys = fd_bank_stake_account_keys_locking_modify( slot_ctx->bank );
286 0 : pool_mem = (uchar *)fd_ulong_align_up( (ulong)stake_account_keys + sizeof(fd_account_keys_global_t), fd_account_keys_pair_t_map_align() );
287 0 : fd_account_keys_pair_t_mapnode_t * account_keys_pool = fd_account_keys_pair_t_map_join( fd_account_keys_pair_t_map_new( pool_mem, vote_acct_max ) );
288 0 : fd_account_keys_pair_t_mapnode_t * account_keys_root = NULL;
289 0 : fd_account_keys_account_keys_pool_update( stake_account_keys, account_keys_pool );
290 0 : fd_account_keys_account_keys_root_update( stake_account_keys, account_keys_root );
291 0 : fd_bank_stake_account_keys_end_locking_modify( slot_ctx->bank );
292 :
293 0 : fd_account_keys_global_t * vote_account_keys = fd_bank_vote_account_keys_locking_modify( slot_ctx->bank );
294 0 : pool_mem = (uchar *)fd_ulong_align_up( (ulong)vote_account_keys + sizeof(fd_account_keys_global_t), fd_account_keys_pair_t_map_align() );
295 0 : fd_account_keys_pair_t_mapnode_t * vote_account_keys_pool = fd_account_keys_pair_t_map_join( fd_account_keys_pair_t_map_new( pool_mem, vote_acct_max ) );
296 0 : fd_account_keys_pair_t_mapnode_t * vote_account_keys_root = NULL;
297 0 : fd_account_keys_account_keys_pool_update( vote_account_keys, vote_account_keys_pool );
298 0 : fd_account_keys_account_keys_root_update( vote_account_keys, vote_account_keys_root );
299 0 : fd_bank_vote_account_keys_end_locking_modify( slot_ctx->bank );
300 :
301 :
302 : /* SETUP STAKES HERE */
303 0 : fd_stakes_global_t * stakes = fd_bank_stakes_locking_modify( slot_ctx->bank );
304 0 : pool_mem = (uchar *)fd_ulong_align_up( (ulong)stakes + sizeof(fd_stakes_global_t), fd_vote_accounts_pair_t_map_align() );
305 0 : fd_vote_accounts_pair_global_t_mapnode_t * vote_accounts_pool = fd_vote_accounts_pair_global_t_map_join( fd_vote_accounts_pair_global_t_map_new( pool_mem, vote_acct_max ) );
306 0 : fd_vote_accounts_pair_global_t_mapnode_t * vote_accounts_root = NULL;
307 0 : pool_mem = (uchar *)fd_ulong_align_up( (ulong)pool_mem + fd_vote_accounts_pair_global_t_map_footprint( vote_acct_max ), fd_delegation_pair_t_map_align() );
308 0 : fd_delegation_pair_t_mapnode_t * stake_delegations_pool = fd_delegation_pair_t_map_join( fd_delegation_pair_t_map_new( pool_mem, vote_acct_max ) );
309 0 : fd_delegation_pair_t_mapnode_t * stake_delegations_root = NULL;
310 :
311 : /* Load in all accounts with > 0 lamports provided in the context. The input expects unique account pubkeys. */
312 0 : for( ushort i=0; i<test_ctx->acct_states_count; i++ ) {
313 0 : FD_TXN_ACCOUNT_DECL(acc);
314 0 : fd_runtime_fuzz_load_account( acc, funk, funk_txn, &test_ctx->acct_states[i], 1 );
315 :
316 : /* Update vote accounts cache for epoch T */
317 0 : fd_pubkey_t pubkey;
318 0 : memcpy( &pubkey, test_ctx->acct_states[i].address, sizeof(fd_pubkey_t) );
319 0 : fd_runtime_fuzz_block_register_vote_account( slot_ctx,
320 0 : vote_accounts_pool,
321 0 : &vote_accounts_root,
322 0 : &pubkey,
323 0 : runner->spad );
324 :
325 : /* Update the stake delegations cache for epoch T */
326 0 : fd_runtime_fuzz_block_register_stake_delegation( slot_ctx,
327 0 : stake_delegations_pool,
328 0 : &stake_delegations_root,
329 0 : &pubkey );
330 0 : }
331 :
332 : /* Refresh vote accounts to calculate stake delegations */
333 0 : fd_runtime_fuzz_block_refresh_vote_accounts( vote_accounts_pool,
334 0 : vote_accounts_root,
335 0 : stake_delegations_pool,
336 0 : stake_delegations_root );
337 :
338 0 : fd_vote_accounts_vote_accounts_pool_update( &stakes->vote_accounts, vote_accounts_pool );
339 0 : fd_vote_accounts_vote_accounts_root_update( &stakes->vote_accounts, vote_accounts_root );
340 :
341 0 : fd_stakes_stake_delegations_pool_update( stakes, stake_delegations_pool );
342 0 : fd_stakes_stake_delegations_root_update( stakes, stake_delegations_root );
343 :
344 : /* Finish init epoch bank sysvars */
345 0 : fd_epoch_schedule_t epoch_schedule_[1];
346 0 : fd_epoch_schedule_t * epoch_schedule = fd_sysvar_epoch_schedule_read( funk, funk_txn, epoch_schedule_ );
347 0 : fd_bank_epoch_schedule_set( slot_ctx->bank, *epoch_schedule );
348 :
349 0 : fd_rent_t const * rent = fd_sysvar_rent_read( funk, funk_txn, runner->spad );
350 0 : fd_bank_rent_set( slot_ctx->bank, *rent );
351 :
352 0 : stakes->epoch = fd_slot_to_epoch( epoch_schedule, test_ctx->slot_ctx.prev_slot, NULL );
353 :
354 0 : fd_bank_stakes_end_locking_modify( slot_ctx->bank );
355 :
356 : /* Refresh the program cache */
357 0 : fd_runtime_fuzz_refresh_program_cache( slot_ctx, test_ctx->acct_states, test_ctx->acct_states_count, runner->spad );
358 :
359 0 : fd_vote_accounts_global_t * vote_accounts = fd_bank_next_epoch_stakes_locking_modify( slot_ctx->bank );
360 0 : pool_mem = (uchar *)fd_ulong_align_up( (ulong)vote_accounts + sizeof(fd_vote_accounts_global_t), fd_vote_accounts_pair_global_t_map_align() );
361 0 : vote_accounts_pool = fd_vote_accounts_pair_global_t_map_join( fd_vote_accounts_pair_global_t_map_new( pool_mem, vote_acct_max ) );
362 0 : vote_accounts_root = NULL;
363 :
364 : /* Update vote cache for epoch T-1 */
365 0 : fd_runtime_fuzz_block_update_prev_epoch_votes_cache( vote_accounts_pool,
366 0 : &vote_accounts_root,
367 0 : test_ctx->epoch_ctx.vote_accounts_t_1,
368 0 : test_ctx->epoch_ctx.vote_accounts_t_1_count,
369 0 : runner->spad );
370 :
371 0 : fd_vote_accounts_vote_accounts_pool_update( vote_accounts, vote_accounts_pool );
372 0 : fd_vote_accounts_vote_accounts_root_update( vote_accounts, vote_accounts_root );
373 :
374 0 : fd_bank_next_epoch_stakes_end_locking_modify( slot_ctx->bank );
375 :
376 : /* Update vote cache for epoch T-2 */
377 0 : vote_accounts = fd_bank_epoch_stakes_locking_modify( slot_ctx->bank );
378 0 : pool_mem = (uchar *)fd_ulong_align_up( (ulong)vote_accounts + sizeof(fd_vote_accounts_global_t), fd_vote_accounts_pair_global_t_map_align() );
379 0 : vote_accounts_pool = fd_vote_accounts_pair_global_t_map_join( fd_vote_accounts_pair_global_t_map_new( pool_mem, vote_acct_max ) );
380 0 : vote_accounts_root = NULL;
381 :
382 0 : fd_runtime_fuzz_block_update_prev_epoch_votes_cache( vote_accounts_pool,
383 0 : &vote_accounts_root,
384 0 : test_ctx->epoch_ctx.vote_accounts_t_2,
385 0 : test_ctx->epoch_ctx.vote_accounts_t_2_count,
386 0 : runner->spad );
387 :
388 0 : fd_vote_accounts_vote_accounts_pool_update( vote_accounts, vote_accounts_pool );
389 0 : fd_vote_accounts_vote_accounts_root_update( vote_accounts, vote_accounts_root );
390 0 : fd_bank_epoch_stakes_end_locking_modify( slot_ctx->bank );
391 :
392 : /* Update leader schedule */
393 0 : fd_runtime_update_leaders( slot_ctx->bank, fd_bank_slot_get( slot_ctx->bank ), runner->spad );
394 :
395 : /* Initialize the blockhash queue and recent blockhashes sysvar from the input blockhash queue */
396 0 : ulong blockhash_seed; FD_TEST( fd_rng_secure( &blockhash_seed, sizeof(ulong) ) );
397 0 : fd_blockhashes_init( fd_bank_block_hash_queue_modify( slot_ctx->bank ), blockhash_seed );
398 :
399 : /* TODO: We might need to load this in from the input. We also need to
400 : size this out for worst case, but this also blows up the memory
401 : requirement. */
402 : /* Allocate all the memory for the rent fresh accounts list */
403 :
404 : // Set genesis hash to {0}
405 0 : fd_hash_t * genesis_hash = fd_bank_genesis_hash_modify( slot_ctx->bank );
406 0 : fd_memset( genesis_hash->hash, 0, sizeof(fd_hash_t) );
407 :
408 : // Use the latest lamports per signature
409 0 : fd_recent_block_hashes_t const * rbh = fd_sysvar_recent_hashes_read( funk, funk_txn, runner->spad );
410 0 : if( rbh && !deq_fd_block_block_hash_entry_t_empty( rbh->hashes ) ) {
411 0 : fd_block_block_hash_entry_t const * last = deq_fd_block_block_hash_entry_t_peek_head_const( rbh->hashes );
412 0 : if( last && last->fee_calculator.lamports_per_signature!=0UL ) {
413 0 : fd_bank_lamports_per_signature_set( slot_ctx->bank, last->fee_calculator.lamports_per_signature );
414 0 : fd_bank_prev_lamports_per_signature_set( slot_ctx->bank, last->fee_calculator.lamports_per_signature );
415 0 : }
416 0 : }
417 :
418 : // Populate blockhash queue and recent blockhashes sysvar
419 0 : for( ushort i=0; i<test_ctx->blockhash_queue_count; ++i ) {
420 0 : fd_hash_t hash;
421 0 : memcpy( &hash, test_ctx->blockhash_queue[i]->bytes, sizeof(fd_hash_t) );
422 0 : fd_bank_poh_set( slot_ctx->bank, hash );
423 0 : fd_sysvar_recent_hashes_update( slot_ctx ); /* appends an entry */
424 0 : }
425 :
426 : // Set the current poh from the input (we skip POH verification in this fuzzing target)
427 0 : fd_hash_t * poh = fd_bank_poh_modify( slot_ctx->bank );
428 0 : fd_memcpy( poh->hash, test_ctx->slot_ctx.poh, sizeof(fd_hash_t) );
429 :
430 : /* Make a new funk transaction since we're done loading in accounts for context */
431 0 : fd_funk_txn_xid_t fork_xid[1] = {0};
432 0 : fork_xid[0] = fd_funk_generate_xid();
433 0 : fd_funk_txn_start_write( funk );
434 0 : slot_ctx->funk_txn = fd_funk_txn_prepare( funk, slot_ctx->funk_txn, fork_xid, 1 );
435 0 : fd_funk_txn_end_write( funk );
436 :
437 : /* Calculate epoch account hash values. This sets epoch_bank.eah_{start_slot, stop_slot, interval} */
438 0 : fd_calculate_epoch_accounts_hash_values( slot_ctx );
439 :
440 : /* Restore sysvar cache */
441 0 : fd_sysvar_cache_restore_fuzz( slot_ctx );
442 :
443 : /* Prepare raw transaction pointers and block / microblock infos */
444 0 : ulong txn_cnt = test_ctx->txns_count;
445 :
446 : // For fuzzing, we're using a single microblock batch that contains a single microblock containing all transactions
447 0 : fd_runtime_block_info_t * block_info = fd_spad_alloc( runner->spad, alignof(fd_runtime_block_info_t), sizeof(fd_runtime_block_info_t) );
448 0 : fd_microblock_batch_info_t * batch_info = fd_spad_alloc( runner->spad, alignof(fd_microblock_batch_info_t), sizeof(fd_microblock_batch_info_t) );
449 0 : fd_microblock_info_t * microblock_info = fd_spad_alloc( runner->spad, alignof(fd_microblock_info_t), sizeof(fd_microblock_info_t) );
450 0 : fd_memset( block_info, 0, sizeof(fd_runtime_block_info_t) );
451 0 : fd_memset( batch_info, 0, sizeof(fd_microblock_batch_info_t) );
452 0 : fd_memset( microblock_info, 0, sizeof(fd_microblock_info_t) );
453 :
454 0 : block_info->microblock_batch_cnt = 1UL;
455 0 : block_info->microblock_cnt = 1UL;
456 0 : block_info->microblock_batch_infos = batch_info;
457 :
458 0 : batch_info->microblock_cnt = 1UL;
459 0 : batch_info->microblock_infos = microblock_info;
460 :
461 0 : ulong batch_signature_cnt = 0UL;
462 0 : ulong batch_txn_cnt = 0UL;
463 0 : ulong batch_account_cnt = 0UL;
464 0 : ulong signature_cnt = 0UL;
465 0 : ulong account_cnt = 0UL;
466 :
467 0 : fd_microblock_hdr_t * microblock_hdr = fd_spad_alloc( runner->spad, alignof(fd_microblock_hdr_t), sizeof(fd_microblock_hdr_t) );
468 0 : fd_memset( microblock_hdr, 0, sizeof(fd_microblock_hdr_t) );
469 :
470 0 : fd_txn_p_t * txn_ptrs = fd_spad_alloc( runner->spad, alignof(fd_txn_p_t), txn_cnt * sizeof(fd_txn_p_t) );
471 0 : for( ulong i=0UL; i<txn_cnt; i++ ) {
472 0 : fd_txn_p_t * txn = &txn_ptrs[i];
473 :
474 0 : ushort _instr_count, _addr_table_cnt;
475 0 : ulong msg_sz = fd_runtime_fuzz_serialize_txn( txn->payload, &test_ctx->txns[i], &_instr_count, &_addr_table_cnt );
476 :
477 : // Reject any transactions over 1232 bytes
478 0 : if( FD_UNLIKELY( msg_sz==ULONG_MAX ) ) {
479 0 : return NULL;
480 0 : }
481 0 : txn->payload_sz = msg_sz;
482 :
483 : // Reject any transactions that cannot be parsed
484 0 : if( FD_UNLIKELY( !fd_txn_parse( txn->payload, msg_sz, TXN( txn ), NULL ) ) ) {
485 0 : return NULL;
486 0 : }
487 :
488 0 : signature_cnt += TXN( txn )->signature_cnt;
489 0 : account_cnt += fd_txn_account_cnt( TXN( txn ), FD_TXN_ACCT_CAT_ALL );
490 0 : }
491 :
492 0 : microblock_hdr->txn_cnt = txn_cnt;
493 0 : microblock_info->microblock.raw = (uchar *)microblock_hdr;
494 :
495 0 : microblock_info->signature_cnt = signature_cnt;
496 0 : microblock_info->account_cnt = account_cnt;
497 0 : microblock_info->txns = txn_ptrs;
498 :
499 0 : batch_signature_cnt += signature_cnt;
500 0 : batch_txn_cnt += txn_cnt;
501 0 : batch_account_cnt += account_cnt;
502 :
503 0 : block_info->signature_cnt = batch_info->signature_cnt = batch_signature_cnt;
504 0 : block_info->txn_cnt = batch_info->txn_cnt = batch_txn_cnt;
505 0 : block_info->account_cnt = batch_info->account_cnt = batch_account_cnt;
506 :
507 0 : return block_info;
508 0 : }
509 :
510 : /* Takes in a block_info created from `fd_runtime_fuzz_block_ctx_create()`
511 : and executes it against the runtime. Returns the execution result. */
512 : static int
513 : fd_runtime_fuzz_block_ctx_exec( fd_runtime_fuzz_runner_t * runner,
514 : fd_exec_slot_ctx_t * slot_ctx,
515 0 : fd_runtime_block_info_t * block_info ) {
516 0 : int res = 0;
517 :
518 0 : fd_spad_t * runtime_spad = runner->spad;
519 :
520 : // Prepare. Execute. Finalize.
521 0 : FD_SPAD_FRAME_BEGIN( runtime_spad ) {
522 0 : fd_capture_ctx_t * capture_ctx = NULL;
523 0 : fd_capture_ctx_t capture_ctx_[1];
524 0 : if( runner->solcap ) {
525 0 : capture_ctx_[0] = (fd_capture_ctx_t) {
526 0 : .capture = runner->solcap,
527 0 : .capture_txns = 1,
528 0 : .dump_instr_to_pb = 1,
529 0 : .dump_txn_to_pb = 1,
530 0 : .dump_block_to_pb = 1,
531 0 : .dump_syscall_to_pb = 1,
532 0 : .dump_elf_to_pb = 1
533 0 : };
534 0 : capture_ctx = capture_ctx_;
535 0 : }
536 :
537 0 : fd_rewards_recalculate_partitioned_rewards( slot_ctx, capture_ctx, runtime_spad );
538 :
539 : /* Process new epoch may push a new spad frame onto the runtime spad. We should make sure this frame gets
540 : cleared (if it was allocated) before executing the block. */
541 0 : int is_epoch_boundary = 0;
542 0 : fd_runtime_block_pre_execute_process_new_epoch( slot_ctx, capture_ctx, runtime_spad, &is_epoch_boundary );
543 :
544 0 : res = fd_runtime_block_execute( slot_ctx, capture_ctx, block_info, runtime_spad );
545 0 : } FD_SPAD_FRAME_END;
546 :
547 0 : return res;
548 0 : }
549 :
550 : ulong
551 : fd_runtime_fuzz_block_run( fd_runtime_fuzz_runner_t * runner,
552 : void const * input_,
553 : void ** output_,
554 : void * output_buf,
555 0 : ulong output_bufsz ) {
556 0 : fd_exec_test_block_context_t const * input = fd_type_pun_const( input_ );
557 0 : fd_exec_test_block_effects_t ** output = fd_type_pun( output_ );
558 :
559 0 : FD_SPAD_FRAME_BEGIN( runner->spad ) {
560 : /* Initialize memory */
561 0 : fd_wksp_t * wksp = fd_wksp_attach( "wksp" );
562 0 : uchar * slot_ctx_mem = fd_spad_alloc( runner->spad, FD_EXEC_SLOT_CTX_ALIGN, FD_EXEC_SLOT_CTX_FOOTPRINT );
563 0 : fd_exec_slot_ctx_t * slot_ctx = fd_exec_slot_ctx_join ( fd_exec_slot_ctx_new ( slot_ctx_mem ) );
564 :
565 : /* Set up the block execution context */
566 0 : fd_runtime_block_info_t * block_info = fd_runtime_fuzz_block_ctx_create( runner, slot_ctx, input );
567 0 : if( block_info==NULL ) {
568 0 : fd_runtime_fuzz_block_ctx_destroy( runner, wksp );
569 0 : return 0;
570 0 : }
571 :
572 : /* Execute the constructed block against the runtime. */
573 0 : int res = fd_runtime_fuzz_block_ctx_exec( runner, slot_ctx, block_info);
574 :
575 : /* Start saving block exec results */
576 0 : FD_SCRATCH_ALLOC_INIT( l, output_buf );
577 0 : ulong output_end = (ulong)output_buf + output_bufsz;
578 :
579 0 : fd_exec_test_block_effects_t * effects =
580 0 : FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_exec_test_block_effects_t),
581 0 : sizeof (fd_exec_test_block_effects_t) );
582 0 : if( FD_UNLIKELY( _l > output_end ) ) {
583 0 : abort();
584 0 : }
585 0 : fd_memset( effects, 0, sizeof(fd_exec_test_block_effects_t) );
586 :
587 : /* Capture error status */
588 0 : effects->has_error = !!( res );
589 :
590 : /* Capture capitalization */
591 0 : effects->slot_capitalization = fd_bank_capitalization_get( slot_ctx->bank );
592 :
593 : /* Capture hashes */
594 0 : fd_hash_t bank_hash = fd_bank_bank_hash_get( slot_ctx->bank );
595 0 : fd_memcpy( effects->bank_hash, bank_hash.hash, sizeof(fd_hash_t) );
596 :
597 0 : ulong actual_end = FD_SCRATCH_ALLOC_FINI( l, 1UL );
598 0 : fd_runtime_fuzz_block_ctx_destroy( runner, wksp );
599 :
600 0 : *output = effects;
601 0 : return actual_end - (ulong)output_buf;
602 0 : } FD_SPAD_FRAME_END;
603 0 : }
|