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 FD_FN_UNUSED
184 : fd_runtime_fuzz_block_ctx_destroy( fd_runtime_fuzz_runner_t * runner,
185 : fd_exec_slot_ctx_t * slot_ctx,
186 : fd_wksp_t * wksp,
187 0 : fd_alloc_t * alloc ) {
188 0 : if( !slot_ctx ) return; // This shouldn't be false either
189 :
190 0 : fd_wksp_free_laddr( fd_alloc_delete( fd_alloc_leave( alloc ) ) );
191 0 : fd_wksp_detach( wksp );
192 :
193 0 : fd_funk_txn_cancel_all( runner->funk, 1 );
194 0 : }
195 :
196 : /* Sets up block execution context from an input test case to execute against the runtime.
197 : Returns block_info on success and NULL on failure. */
198 : static fd_runtime_block_info_t *
199 : fd_runtime_fuzz_block_ctx_create( fd_runtime_fuzz_runner_t * runner,
200 : fd_exec_slot_ctx_t * slot_ctx,
201 0 : fd_exec_test_block_context_t const * test_ctx ) {
202 0 : fd_funk_t * funk = runner->funk;
203 :
204 0 : slot_ctx->bank = runner->bank;
205 0 : fd_bank_clear_bank( slot_ctx->bank );
206 :
207 : /* Generate unique ID for funk txn */
208 0 : fd_funk_txn_xid_t xid[1] = {0};
209 0 : xid[0] = fd_funk_generate_xid();
210 :
211 : /* Create temporary funk transaction and slot / epoch contexts */
212 0 : fd_funk_txn_start_write( funk );
213 0 : fd_funk_txn_t * funk_txn = fd_funk_txn_prepare( funk, NULL, xid, 1 );
214 0 : fd_funk_txn_end_write( funk );
215 :
216 : /* Allocate contexts */
217 0 : ulong vote_acct_max = fd_ulong_max( 128UL, test_ctx->acct_states_count );
218 :
219 : /* Restore feature flags */
220 0 : fd_features_t features = {0};
221 0 : if( !fd_runtime_fuzz_restore_features( &features, &test_ctx->epoch_ctx.features ) ) {
222 0 : return NULL;
223 0 : }
224 0 : fd_bank_features_set( slot_ctx->bank, features );
225 :
226 : /* Set up slot context */
227 0 : ulong slot = test_ctx->slot_ctx.slot;
228 :
229 0 : slot_ctx->funk_txn = funk_txn;
230 0 : slot_ctx->funk = funk;
231 0 : slot_ctx->slot = slot;
232 :
233 0 : fd_hash_t * bank_hash = fd_bank_bank_hash_modify( slot_ctx->bank );
234 0 : fd_memcpy( bank_hash, test_ctx->slot_ctx.parent_bank_hash, sizeof(fd_hash_t) );
235 :
236 : /* All bank mgr stuff here. */
237 :
238 : /* Initialize vote timestamps cache */
239 0 : fd_clock_timestamp_votes_global_t * clock_timestamp_votes = fd_bank_clock_timestamp_votes_locking_modify( slot_ctx->bank );
240 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() );
241 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 ) );
242 0 : fd_clock_timestamp_vote_t_mapnode_t * clock_root = NULL;
243 :
244 0 : fd_clock_timestamp_votes_votes_pool_update( clock_timestamp_votes, clock_pool );
245 0 : fd_clock_timestamp_votes_votes_root_update( clock_timestamp_votes, clock_root );
246 0 : fd_bank_clock_timestamp_votes_end_locking_modify( slot_ctx->bank );
247 :
248 0 : slot_ctx->bank->slot = slot;
249 :
250 0 : fd_bank_block_height_set( slot_ctx->bank, test_ctx->slot_ctx.block_height );
251 :
252 0 : fd_bank_prev_slot_set( slot_ctx->bank, test_ctx->slot_ctx.prev_slot );
253 :
254 0 : fd_bank_capitalization_set( slot_ctx->bank, test_ctx->slot_ctx.prev_epoch_capitalization );
255 :
256 0 : fd_bank_lamports_per_signature_set( slot_ctx->bank, 5000UL );
257 :
258 0 : fd_bank_prev_lamports_per_signature_set( slot_ctx->bank, test_ctx->slot_ctx.prev_lps );
259 :
260 : // self.max_tick_height = (self.slot + 1) * self.ticks_per_slot;
261 0 : fd_bank_hashes_per_tick_set( slot_ctx->bank, test_ctx->epoch_ctx.hashes_per_tick );
262 :
263 0 : fd_bank_ticks_per_slot_set( slot_ctx->bank, test_ctx->epoch_ctx.ticks_per_slot );
264 :
265 0 : fd_bank_ns_per_slot_set( slot_ctx->bank, 400000000 ); // TODO: restore from input
266 :
267 0 : fd_bank_genesis_creation_time_set( slot_ctx->bank, test_ctx->epoch_ctx.genesis_creation_time );
268 :
269 0 : fd_bank_slots_per_year_set( slot_ctx->bank, test_ctx->epoch_ctx.slots_per_year );
270 :
271 0 : fd_fee_rate_governor_t * fee_rate_governor = fd_bank_fee_rate_governor_modify( slot_ctx->bank );
272 0 : fee_rate_governor->target_lamports_per_signature = 10000UL;
273 0 : fee_rate_governor->target_signatures_per_slot = 20000UL;
274 0 : fee_rate_governor->min_lamports_per_signature = 5000UL;
275 0 : fee_rate_governor->max_lamports_per_signature = 100000UL;
276 0 : fee_rate_governor->burn_percent = 50;
277 :
278 0 : fd_inflation_t * inflation = fd_bank_inflation_modify( slot_ctx->bank );
279 0 : inflation->initial = test_ctx->epoch_ctx.inflation.initial;
280 0 : inflation->terminal = test_ctx->epoch_ctx.inflation.terminal;
281 0 : inflation->taper = test_ctx->epoch_ctx.inflation.taper;
282 0 : inflation->foundation = test_ctx->epoch_ctx.inflation.foundation;
283 0 : inflation->foundation_term = test_ctx->epoch_ctx.inflation.foundation_term;
284 :
285 0 : fd_bank_block_height_set( slot_ctx->bank, test_ctx->slot_ctx.block_height );
286 :
287 : // /* Initialize the current running epoch stake and vote accounts */
288 :
289 : /* TODO: should be stake account max */
290 0 : fd_account_keys_global_t * stake_account_keys = fd_bank_stake_account_keys_locking_modify( slot_ctx->bank );
291 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() );
292 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 ) );
293 0 : fd_account_keys_pair_t_mapnode_t * account_keys_root = NULL;
294 0 : fd_account_keys_account_keys_pool_update( stake_account_keys, account_keys_pool );
295 0 : fd_account_keys_account_keys_root_update( stake_account_keys, account_keys_root );
296 0 : fd_bank_stake_account_keys_end_locking_modify( slot_ctx->bank );
297 :
298 0 : fd_account_keys_global_t * vote_account_keys = fd_bank_vote_account_keys_locking_modify( slot_ctx->bank );
299 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() );
300 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 ) );
301 0 : fd_account_keys_pair_t_mapnode_t * vote_account_keys_root = NULL;
302 0 : fd_account_keys_account_keys_pool_update( vote_account_keys, vote_account_keys_pool );
303 0 : fd_account_keys_account_keys_root_update( vote_account_keys, vote_account_keys_root );
304 0 : fd_bank_vote_account_keys_end_locking_modify( slot_ctx->bank );
305 :
306 :
307 : /* SETUP STAKES HERE */
308 0 : fd_stakes_global_t * stakes = fd_bank_stakes_locking_modify( slot_ctx->bank );
309 0 : pool_mem = (uchar *)fd_ulong_align_up( (ulong)stakes + sizeof(fd_stakes_global_t), fd_vote_accounts_pair_t_map_align() );
310 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 ) );
311 0 : fd_vote_accounts_pair_global_t_mapnode_t * vote_accounts_root = NULL;
312 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() );
313 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 ) );
314 0 : fd_delegation_pair_t_mapnode_t * stake_delegations_root = NULL;
315 :
316 : /* Load in all accounts with > 0 lamports provided in the context. The input expects unique account pubkeys. */
317 0 : for( ushort i=0; i<test_ctx->acct_states_count; i++ ) {
318 0 : FD_TXN_ACCOUNT_DECL(acc);
319 0 : fd_runtime_fuzz_load_account( acc, funk, funk_txn, &test_ctx->acct_states[i], 1 );
320 :
321 : /* Update vote accounts cache for epoch T */
322 0 : fd_pubkey_t pubkey;
323 0 : memcpy( &pubkey, test_ctx->acct_states[i].address, sizeof(fd_pubkey_t) );
324 0 : fd_runtime_fuzz_block_register_vote_account( slot_ctx,
325 0 : vote_accounts_pool,
326 0 : &vote_accounts_root,
327 0 : &pubkey,
328 0 : runner->spad );
329 :
330 : /* Update the stake delegations cache for epoch T */
331 0 : fd_runtime_fuzz_block_register_stake_delegation( slot_ctx,
332 0 : stake_delegations_pool,
333 0 : &stake_delegations_root,
334 0 : &pubkey );
335 0 : }
336 :
337 : /* Refresh vote accounts to calculate stake delegations */
338 0 : fd_runtime_fuzz_block_refresh_vote_accounts( vote_accounts_pool,
339 0 : vote_accounts_root,
340 0 : stake_delegations_pool,
341 0 : stake_delegations_root );
342 :
343 0 : fd_vote_accounts_vote_accounts_pool_update( &stakes->vote_accounts, vote_accounts_pool );
344 0 : fd_vote_accounts_vote_accounts_root_update( &stakes->vote_accounts, vote_accounts_root );
345 :
346 0 : fd_stakes_stake_delegations_pool_update( stakes, stake_delegations_pool );
347 0 : fd_stakes_stake_delegations_root_update( stakes, stake_delegations_root );
348 :
349 :
350 : /* Add accounts to bpf program cache */
351 0 : fd_bpf_scan_and_create_bpf_program_cache_entry( slot_ctx, runner->spad );
352 :
353 : /* Finish init epoch bank sysvars */
354 0 : fd_epoch_schedule_t * epoch_schedule = fd_sysvar_epoch_schedule_read( funk, funk_txn, runner->spad );
355 0 : fd_bank_epoch_schedule_set( slot_ctx->bank, *epoch_schedule );
356 :
357 0 : fd_rent_t const * rent = fd_sysvar_rent_read( funk, funk_txn, runner->spad );
358 0 : fd_bank_rent_set( slot_ctx->bank, *rent );
359 :
360 0 : stakes->epoch = fd_slot_to_epoch( epoch_schedule, test_ctx->slot_ctx.prev_slot, NULL );
361 :
362 0 : fd_bank_stakes_end_locking_modify( slot_ctx->bank );
363 :
364 0 : fd_vote_accounts_global_t * vote_accounts = fd_bank_next_epoch_stakes_locking_modify( slot_ctx->bank );
365 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() );
366 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 ) );
367 0 : vote_accounts_root = NULL;
368 :
369 : /* Update vote cache for epoch T-1 */
370 0 : fd_runtime_fuzz_block_update_prev_epoch_votes_cache( vote_accounts_pool,
371 0 : &vote_accounts_root,
372 0 : test_ctx->epoch_ctx.vote_accounts_t_1,
373 0 : test_ctx->epoch_ctx.vote_accounts_t_1_count,
374 0 : runner->spad );
375 :
376 0 : fd_vote_accounts_vote_accounts_pool_update( vote_accounts, vote_accounts_pool );
377 0 : fd_vote_accounts_vote_accounts_root_update( vote_accounts, vote_accounts_root );
378 :
379 0 : fd_bank_next_epoch_stakes_end_locking_modify( slot_ctx->bank );
380 :
381 : /* Update vote cache for epoch T-2 */
382 0 : vote_accounts = fd_bank_epoch_stakes_locking_modify( slot_ctx->bank );
383 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() );
384 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 ) );
385 0 : vote_accounts_root = NULL;
386 :
387 0 : fd_runtime_fuzz_block_update_prev_epoch_votes_cache( vote_accounts_pool,
388 0 : &vote_accounts_root,
389 0 : test_ctx->epoch_ctx.vote_accounts_t_2,
390 0 : test_ctx->epoch_ctx.vote_accounts_t_2_count,
391 0 : runner->spad );
392 :
393 0 : fd_vote_accounts_vote_accounts_pool_update( vote_accounts, vote_accounts_pool );
394 0 : fd_vote_accounts_vote_accounts_root_update( vote_accounts, vote_accounts_root );
395 0 : fd_bank_epoch_stakes_end_locking_modify( slot_ctx->bank );
396 :
397 : /* Update leader schedule */
398 0 : fd_runtime_update_leaders( slot_ctx->bank, slot_ctx->slot, runner->spad );
399 :
400 : /* Initialize the blockhash queue and recent blockhashes sysvar from the input blockhash queue */
401 0 : fd_block_hash_queue_global_t * block_hash_queue = (fd_block_hash_queue_global_t *)&slot_ctx->bank->block_hash_queue[0];
402 0 : uchar * last_hash_mem = (uchar *)fd_ulong_align_up( (ulong)block_hash_queue + sizeof(fd_block_hash_queue_global_t), alignof(fd_hash_t) );
403 0 : uchar * ages_pool_mem = (uchar *)fd_ulong_align_up( (ulong)last_hash_mem + sizeof(fd_hash_t), fd_hash_hash_age_pair_t_map_align() );
404 0 : fd_hash_hash_age_pair_t_mapnode_t * ages_pool = fd_hash_hash_age_pair_t_map_join( fd_hash_hash_age_pair_t_map_new( ages_pool_mem, FD_BLOCKHASH_QUEUE_MAX_ENTRIES ) );
405 :
406 0 : block_hash_queue->max_age = FD_BLOCKHASH_QUEUE_MAX_ENTRIES; // Max age is fixed at 300
407 0 : block_hash_queue->ages_root_offset = 0UL;
408 0 : block_hash_queue->ages_pool_offset = (ulong)fd_hash_hash_age_pair_t_map_leave( ages_pool ) - (ulong)block_hash_queue;
409 0 : block_hash_queue->last_hash_index = 0UL;
410 0 : block_hash_queue->last_hash_offset = (ulong)last_hash_mem - (ulong)block_hash_queue;
411 :
412 0 : fd_memset( last_hash_mem, 0, sizeof(fd_hash_t) );
413 :
414 : /* TODO: We might need to load this in from the input. We also need to
415 : size this out for worst case, but this also blows up the memory
416 : requirement. */
417 : /* Allocate all the memory for the rent fresh accounts list */
418 :
419 : // Set genesis hash to {0}
420 0 : fd_hash_t * genesis_hash = fd_bank_genesis_hash_modify( slot_ctx->bank );
421 0 : fd_memset( genesis_hash->hash, 0, sizeof(fd_hash_t) );
422 :
423 : // Use the latest lamports per signature
424 0 : fd_recent_block_hashes_global_t const * rbh_global = fd_sysvar_recent_hashes_read( funk, funk_txn, runner->spad );
425 0 : fd_recent_block_hashes_t rbh[1];
426 0 : if( rbh_global ) {
427 0 : rbh->hashes = deq_fd_block_block_hash_entry_t_join( (uchar*)rbh_global + rbh_global->hashes_offset );
428 0 : }
429 :
430 0 : if( rbh_global && !deq_fd_block_block_hash_entry_t_empty( rbh->hashes ) ) {
431 0 : fd_block_block_hash_entry_t const * last = deq_fd_block_block_hash_entry_t_peek_head_const( rbh->hashes );
432 0 : if( last && last->fee_calculator.lamports_per_signature!=0UL ) {
433 0 : fd_bank_lamports_per_signature_set( slot_ctx->bank, last->fee_calculator.lamports_per_signature );
434 0 : fd_bank_prev_lamports_per_signature_set( slot_ctx->bank, last->fee_calculator.lamports_per_signature );
435 0 : }
436 0 : }
437 :
438 : // Populate blockhash queue and recent blockhashes sysvar
439 0 : for( ushort i=0; i<test_ctx->blockhash_queue_count; ++i ) {
440 0 : fd_block_block_hash_entry_t blockhash_entry;
441 0 : memcpy( &blockhash_entry.blockhash, test_ctx->blockhash_queue[i]->bytes, sizeof(fd_hash_t) );
442 0 : fd_bank_poh_set( slot_ctx->bank, blockhash_entry.blockhash );
443 0 : fd_sysvar_recent_hashes_update( slot_ctx, runner->spad );
444 0 : }
445 :
446 : // Set the current poh from the input (we skip POH verification in this fuzzing target)
447 0 : fd_hash_t * poh = fd_bank_poh_modify( slot_ctx->bank );
448 0 : fd_memcpy( poh->hash, test_ctx->slot_ctx.poh, sizeof(fd_hash_t) );
449 :
450 : /* Make a new funk transaction since we're done loading in accounts for context */
451 0 : fd_funk_txn_xid_t fork_xid[1] = {0};
452 0 : fork_xid[0] = fd_funk_generate_xid();
453 0 : fd_funk_txn_start_write( funk );
454 0 : slot_ctx->funk_txn = fd_funk_txn_prepare( funk, slot_ctx->funk_txn, fork_xid, 1 );
455 0 : fd_funk_txn_end_write( funk );
456 :
457 : /* Calculate epoch account hash values. This sets epoch_bank.eah_{start_slot, stop_slot, interval} */
458 0 : fd_calculate_epoch_accounts_hash_values( slot_ctx );
459 :
460 : /* Prepare raw transaction pointers and block / microblock infos */
461 0 : ulong txn_cnt = test_ctx->txns_count;
462 :
463 : // For fuzzing, we're using a single microblock batch that contains a single microblock containing all transactions
464 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) );
465 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) );
466 0 : fd_microblock_info_t * microblock_info = fd_spad_alloc( runner->spad, alignof(fd_microblock_info_t), sizeof(fd_microblock_info_t) );
467 0 : fd_memset( block_info, 0, sizeof(fd_runtime_block_info_t) );
468 0 : fd_memset( batch_info, 0, sizeof(fd_microblock_batch_info_t) );
469 0 : fd_memset( microblock_info, 0, sizeof(fd_microblock_info_t) );
470 :
471 0 : block_info->microblock_batch_cnt = 1UL;
472 0 : block_info->microblock_cnt = 1UL;
473 0 : block_info->microblock_batch_infos = batch_info;
474 :
475 0 : batch_info->microblock_cnt = 1UL;
476 0 : batch_info->microblock_infos = microblock_info;
477 :
478 0 : ulong batch_signature_cnt = 0UL;
479 0 : ulong batch_txn_cnt = 0UL;
480 0 : ulong batch_account_cnt = 0UL;
481 0 : ulong signature_cnt = 0UL;
482 0 : ulong account_cnt = 0UL;
483 :
484 0 : fd_microblock_hdr_t * microblock_hdr = fd_spad_alloc( runner->spad, alignof(fd_microblock_hdr_t), sizeof(fd_microblock_hdr_t) );
485 0 : fd_memset( microblock_hdr, 0, sizeof(fd_microblock_hdr_t) );
486 :
487 0 : fd_txn_p_t * txn_ptrs = fd_spad_alloc( runner->spad, alignof(fd_txn_p_t), txn_cnt * sizeof(fd_txn_p_t) );
488 0 : for( ulong i=0UL; i<txn_cnt; i++ ) {
489 0 : fd_txn_p_t * txn = &txn_ptrs[i];
490 :
491 0 : ushort _instr_count, _addr_table_cnt;
492 0 : ulong msg_sz = fd_runtime_fuzz_serialize_txn( txn->payload, &test_ctx->txns[i], &_instr_count, &_addr_table_cnt );
493 :
494 : // Reject any transactions over 1232 bytes
495 0 : if( FD_UNLIKELY( msg_sz==ULONG_MAX ) ) {
496 0 : return NULL;
497 0 : }
498 0 : txn->payload_sz = msg_sz;
499 :
500 : // Reject any transactions that cannot be parsed
501 0 : if( FD_UNLIKELY( !fd_txn_parse( txn->payload, msg_sz, TXN( txn ), NULL ) ) ) {
502 0 : return NULL;
503 0 : }
504 :
505 0 : signature_cnt += TXN( txn )->signature_cnt;
506 0 : account_cnt += fd_txn_account_cnt( TXN( txn ), FD_TXN_ACCT_CAT_ALL );
507 0 : }
508 :
509 0 : microblock_hdr->txn_cnt = txn_cnt;
510 0 : microblock_info->microblock.raw = (uchar *)microblock_hdr;
511 :
512 0 : microblock_info->signature_cnt = signature_cnt;
513 0 : microblock_info->account_cnt = account_cnt;
514 0 : microblock_info->txns = txn_ptrs;
515 :
516 0 : batch_signature_cnt += signature_cnt;
517 0 : batch_txn_cnt += txn_cnt;
518 0 : batch_account_cnt += account_cnt;
519 :
520 0 : block_info->signature_cnt = batch_info->signature_cnt = batch_signature_cnt;
521 0 : block_info->txn_cnt = batch_info->txn_cnt = batch_txn_cnt;
522 0 : block_info->account_cnt = batch_info->account_cnt = batch_account_cnt;
523 :
524 0 : return block_info;
525 0 : }
526 :
527 : /* Takes in a block_info created from `fd_runtime_fuzz_block_ctx_create()`
528 : and executes it against the runtime. Returns the execution result. */
529 : static int
530 : fd_runtime_fuzz_block_ctx_exec( fd_runtime_fuzz_runner_t * runner,
531 : fd_exec_slot_ctx_t * slot_ctx,
532 0 : fd_runtime_block_info_t * block_info ) {
533 0 : int res = 0;
534 :
535 : /* Initialize tpool and spad(s) */
536 0 : ulong worker_max = FD_BLOCK_HARNESS_TPOOL_WORKER_CNT;
537 0 : void * tpool_mem = fd_spad_alloc( runner->spad, FD_TPOOL_ALIGN, FD_TPOOL_FOOTPRINT( worker_max ) );
538 0 : fd_tpool_t * tpool = fd_tpool_init( tpool_mem, worker_max, 0UL );
539 0 : fd_tpool_worker_push( tpool, 1UL );
540 :
541 0 : fd_spad_t * runtime_spad = runner->spad;
542 :
543 : /* Format chunks of memory for the exec spads
544 : TODO: This memory needs a better bound. */
545 0 : fd_spad_t * exec_spads[FD_BLOCK_HARNESS_TPOOL_WORKER_CNT] = { 0 };
546 0 : ulong exec_spads_cnt = FD_BLOCK_HARNESS_TPOOL_WORKER_CNT;
547 0 : for( ulong i=0UL; i<worker_max; i++ ) {
548 0 : void * exec_spad_mem = fd_spad_alloc( runtime_spad, FD_SPAD_ALIGN, FD_SPAD_FOOTPRINT( FD_BLOCK_HARNESS_MEM_PER_SPAD ) );
549 0 : fd_spad_t * exec_spad = fd_spad_join( fd_spad_new( exec_spad_mem, FD_BLOCK_HARNESS_MEM_PER_SPAD ) );
550 0 : exec_spads[i] = exec_spad;
551 0 : }
552 :
553 : // Prepare. Execute. Finalize.
554 0 : FD_SPAD_FRAME_BEGIN( runtime_spad ) {
555 0 : fd_rewards_recalculate_partitioned_rewards( slot_ctx, tpool, exec_spads, exec_spads_cnt, runtime_spad );
556 :
557 : /* Process new epoch may push a new spad frame onto the runtime spad. We should make sure this frame gets
558 : cleared (if it was allocated) before executing the block. */
559 0 : int is_epoch_boundary = 0;
560 0 : fd_runtime_block_pre_execute_process_new_epoch( slot_ctx, tpool, exec_spads, exec_spads_cnt, runtime_spad, &is_epoch_boundary );
561 :
562 0 : res = fd_runtime_block_execute_tpool( slot_ctx, NULL, NULL, block_info, tpool, exec_spads, exec_spads_cnt, runtime_spad );
563 0 : } FD_SPAD_FRAME_END;
564 :
565 0 : fd_tpool_worker_pop( tpool );
566 :
567 0 : return res;
568 0 : }
569 :
570 : ulong
571 : fd_runtime_fuzz_block_run( fd_runtime_fuzz_runner_t * runner,
572 : void const * input_,
573 : void ** output_,
574 : void * output_buf,
575 0 : ulong output_bufsz ) {
576 0 : fd_exec_test_block_context_t const * input = fd_type_pun_const( input_ );
577 0 : fd_exec_test_block_effects_t ** output = fd_type_pun( output_ );
578 :
579 0 : FD_SPAD_FRAME_BEGIN( runner->spad ) {
580 : /* Initialize memory */
581 0 : fd_wksp_t * wksp = fd_wksp_attach( "wksp" );
582 0 : fd_alloc_t * alloc = fd_alloc_join( fd_alloc_new( fd_wksp_alloc_laddr( wksp, fd_alloc_align(), fd_alloc_footprint(), 2 ), 2 ), 0 );
583 0 : uchar * slot_ctx_mem = fd_spad_alloc( runner->spad, FD_EXEC_SLOT_CTX_ALIGN, FD_EXEC_SLOT_CTX_FOOTPRINT );
584 0 : fd_exec_slot_ctx_t * slot_ctx = fd_exec_slot_ctx_join ( fd_exec_slot_ctx_new ( slot_ctx_mem ) );
585 :
586 : /* Set up the block execution context */
587 0 : fd_runtime_block_info_t * block_info = fd_runtime_fuzz_block_ctx_create( runner, slot_ctx, input );
588 0 : if( block_info==NULL ) {
589 0 : fd_runtime_fuzz_block_ctx_destroy( runner, slot_ctx, wksp, alloc );
590 0 : return 0;
591 0 : }
592 :
593 : /* Execute the constructed block against the runtime. */
594 0 : int res = fd_runtime_fuzz_block_ctx_exec( runner, slot_ctx, block_info);
595 :
596 : /* Start saving block exec results */
597 0 : FD_SCRATCH_ALLOC_INIT( l, output_buf );
598 0 : ulong output_end = (ulong)output_buf + output_bufsz;
599 :
600 0 : fd_exec_test_block_effects_t * effects =
601 0 : FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_exec_test_block_effects_t),
602 0 : sizeof (fd_exec_test_block_effects_t) );
603 0 : if( FD_UNLIKELY( _l > output_end ) ) {
604 0 : abort();
605 0 : }
606 0 : fd_memset( effects, 0, sizeof(fd_exec_test_block_effects_t) );
607 :
608 : /* Capture error status */
609 0 : effects->has_error = !!( res );
610 :
611 : /* Capture capitalization */
612 0 : effects->slot_capitalization = fd_bank_capitalization_get( slot_ctx->bank );
613 :
614 : /* Capture hashes */
615 0 : fd_hash_t bank_hash = fd_bank_bank_hash_get( slot_ctx->bank );
616 0 : fd_memcpy( effects->bank_hash, bank_hash.hash, sizeof(fd_hash_t) );
617 :
618 0 : ulong actual_end = FD_SCRATCH_ALLOC_FINI( l, 1UL );
619 0 : fd_runtime_fuzz_block_ctx_destroy( runner, slot_ctx, wksp, alloc );
620 :
621 0 : *output = effects;
622 0 : return actual_end - (ulong)output_buf;
623 0 : } FD_SPAD_FRAME_END;
624 0 : }
|