Line data Source code
1 : #include "fd_solfuzz_private.h"
2 : #include "../fd_cost_tracker.h"
3 : #include "fd_txn_harness.h"
4 : #include "../fd_runtime.h"
5 : #include "../fd_system_ids.h"
6 : #include "../fd_txn_account.h"
7 : #include "../info/fd_runtime_block_info.h"
8 : #include "../program/fd_stake_program.h"
9 : #include "../program/fd_vote_program.h"
10 : #include "../sysvar/fd_sysvar_epoch_schedule.h"
11 : #include "../sysvar/fd_sysvar_rent.h"
12 : #include "../sysvar/fd_sysvar_recent_hashes.h"
13 : #include "../../rewards/fd_rewards.h"
14 : #include "../../stakes/fd_stakes.h"
15 : #include "../../types/fd_types.h"
16 : #include "../../../disco/pack/fd_pack.h"
17 : #include "generated/block.pb.h"
18 :
19 : /* Stripped down version of `fd_refresh_vote_accounts()` that simply refreshes the stake delegation amount
20 : for each of the vote accounts using the stake delegations cache. */
21 : static void
22 : fd_runtime_fuzz_block_refresh_vote_accounts( fd_vote_states_t * vote_states,
23 0 : fd_stake_delegations_t * stake_delegations ) {
24 0 : fd_stake_delegations_iter_t iter_[1];
25 0 : for( fd_stake_delegations_iter_t * iter = fd_stake_delegations_iter_init( iter_, stake_delegations );
26 0 : !fd_stake_delegations_iter_done( iter );
27 0 : fd_stake_delegations_iter_next( iter ) ) {
28 0 : fd_stake_delegation_t * node = fd_stake_delegations_iter_ele( iter );
29 :
30 0 : fd_pubkey_t * voter_pubkey = &node->vote_account;
31 0 : ulong stake = node->stake;
32 :
33 : /* Find the voter in the vote accounts cache and update their
34 : delegation amount */
35 0 : fd_vote_state_ele_t * vote_state = fd_vote_states_query( vote_states, voter_pubkey );
36 0 : if( !vote_state ) continue;
37 :
38 0 : ulong vote_stake = vote_state->stake;
39 0 : fd_vote_states_update_stake( vote_states, voter_pubkey, vote_stake + stake );
40 :
41 0 : }
42 0 : }
43 :
44 : /* Registers a single vote account into the current votes cache. The entry is derived
45 : from the current present account state. This function also registers a vote timestamp
46 : for the vote account */
47 : static void
48 : fd_runtime_fuzz_block_register_vote_account( fd_funk_t * funk,
49 : fd_funk_txn_xid_t const * xid,
50 : fd_vote_states_t * vote_states,
51 : fd_pubkey_t * pubkey,
52 0 : fd_spad_t * spad ) {
53 0 : FD_TXN_ACCOUNT_DECL( acc );
54 0 : if( FD_UNLIKELY( fd_txn_account_init_from_funk_readonly( acc, pubkey, funk, xid ) ) ) {
55 0 : return;
56 0 : }
57 :
58 : /* Account must be owned by the vote program */
59 0 : if( memcmp( fd_txn_account_get_owner( acc ), fd_solana_vote_program_id.key, sizeof(fd_pubkey_t) ) ) {
60 0 : return;
61 0 : }
62 :
63 : /* Account must have > 0 lamports */
64 0 : if( fd_txn_account_get_lamports( acc )==0UL ) {
65 0 : return;
66 0 : }
67 :
68 : /* Account must be initialized correctly */
69 0 : if( FD_UNLIKELY( !fd_vote_state_versions_is_correct_and_initialized( acc ) ) ) {
70 0 : return;
71 0 : }
72 :
73 : /* Get the vote state from the account data */
74 0 : fd_vote_state_versioned_t * vsv = NULL;
75 0 : int err = fd_vote_get_state( acc, spad, &vsv );
76 0 : if( FD_UNLIKELY( err ) ) {
77 0 : return;
78 0 : }
79 :
80 0 : fd_vote_states_update_from_account(
81 0 : vote_states,
82 0 : acc->pubkey,
83 0 : fd_txn_account_get_data( acc ),
84 0 : fd_txn_account_get_data_len( acc ) );
85 0 : }
86 :
87 : /* Stores an entry in the stake delegations cache for the given vote account. Deserializes and uses the present
88 : account state to derive delegation information. */
89 : static void
90 : fd_runtime_fuzz_block_register_stake_delegation( fd_funk_t * funk,
91 : fd_funk_txn_xid_t const * xid,
92 : fd_stake_delegations_t * stake_delegations,
93 0 : fd_pubkey_t * pubkey ) {
94 0 : FD_TXN_ACCOUNT_DECL( acc );
95 0 : if( FD_UNLIKELY( fd_txn_account_init_from_funk_readonly( acc, pubkey, funk, xid ) ) ) {
96 0 : return;
97 0 : }
98 :
99 : /* Account must be owned by the stake program */
100 0 : if( memcmp( fd_txn_account_get_owner( acc ), fd_solana_stake_program_id.key, sizeof(fd_pubkey_t) ) ) {
101 0 : return;
102 0 : }
103 :
104 : /* Account must have > 0 lamports */
105 0 : if( fd_txn_account_get_lamports( acc )==0UL ) {
106 0 : return;
107 0 : }
108 :
109 : /* Stake state must exist and be initialized correctly */
110 0 : fd_stake_state_v2_t stake_state;
111 0 : if( FD_UNLIKELY( fd_stake_get_state( acc, &stake_state ) || !fd_stake_state_v2_is_stake( &stake_state ) ) ) {
112 0 : return;
113 0 : }
114 :
115 : /* Skip 0-stake accounts */
116 0 : if( FD_UNLIKELY( stake_state.inner.stake.stake.delegation.stake==0UL ) ) {
117 0 : return;
118 0 : }
119 :
120 : /* Nothing to do if the account already exists in the cache */
121 0 : fd_stake_delegations_update(
122 0 : stake_delegations,
123 0 : pubkey,
124 0 : &stake_state.inner.stake.stake.delegation.voter_pubkey,
125 0 : stake_state.inner.stake.stake.delegation.stake,
126 0 : stake_state.inner.stake.stake.delegation.activation_epoch,
127 0 : stake_state.inner.stake.stake.delegation.deactivation_epoch,
128 0 : stake_state.inner.stake.stake.credits_observed,
129 0 : stake_state.inner.stake.stake.delegation.warmup_cooldown_rate );
130 0 : }
131 :
132 : /* Common helper method for populating a previous epoch's vote cache. */
133 : static void
134 : fd_runtime_fuzz_block_update_prev_epoch_votes_cache( fd_vote_states_t * vote_states,
135 : fd_exec_test_vote_account_t * vote_accounts,
136 : pb_size_t vote_accounts_cnt,
137 0 : fd_spad_t * spad ) {
138 0 : FD_SPAD_FRAME_BEGIN( spad ) {
139 0 : for( uint i=0U; i<vote_accounts_cnt; i++ ) {
140 0 : fd_exec_test_acct_state_t * vote_account = &vote_accounts[i].vote_account;
141 0 : ulong stake = vote_accounts[i].stake;
142 0 : uchar * vote_data = vote_account->data->bytes;
143 0 : ulong vote_data_len = vote_account->data->size;
144 0 : fd_pubkey_t vote_address = {0};
145 0 : fd_memcpy( &vote_address, vote_account->address, sizeof(fd_pubkey_t) );
146 :
147 : /* Try decoding the vote state from the account data. If it isn't
148 : decodable, don't try inserting it into the cache. */
149 0 : fd_vote_state_versioned_t * res = fd_bincode_decode_spad(
150 0 : vote_state_versioned, spad,
151 0 : vote_data,
152 0 : vote_data_len,
153 0 : NULL );
154 0 : if( res==NULL ) continue;
155 :
156 0 : fd_vote_states_update_from_account( vote_states, &vote_address, vote_data, vote_data_len );
157 0 : fd_vote_states_update_stake( vote_states, &vote_address, stake );
158 0 : }
159 0 : } FD_SPAD_FRAME_END;
160 0 : }
161 :
162 : static void
163 0 : fd_runtime_fuzz_block_ctx_destroy( fd_solfuzz_runner_t * runner ) {
164 0 : fd_funk_txn_cancel_all( runner->funk );
165 0 : }
166 :
167 : /* Sets up block execution context from an input test case to execute against the runtime.
168 : Returns block_info on success and NULL on failure. */
169 : static fd_runtime_block_info_t *
170 : fd_runtime_fuzz_block_ctx_create( fd_solfuzz_runner_t * runner,
171 0 : fd_exec_test_block_context_t const * test_ctx ) {
172 0 : fd_funk_t * funk = runner->funk;
173 0 : fd_bank_t * bank = runner->bank;
174 0 : fd_banks_t * banks = runner->banks;
175 :
176 0 : fd_banks_clear_bank( banks, bank );
177 :
178 : /* Generate unique ID for funk txn */
179 0 : fd_funk_txn_xid_t xid[1] = {0};
180 0 : xid[0] = fd_funk_generate_xid();
181 :
182 : /* Create temporary funk transaction and slot / epoch contexts */
183 0 : fd_funk_txn_xid_t parent_xid; fd_funk_txn_xid_set_root( &parent_xid );
184 0 : fd_funk_txn_prepare( funk, &parent_xid, xid );
185 :
186 : /* Restore feature flags */
187 0 : fd_features_t features = {0};
188 0 : if( !fd_runtime_fuzz_restore_features( &features, &test_ctx->epoch_ctx.features ) ) {
189 0 : return NULL;
190 0 : }
191 0 : fd_bank_features_set( bank, features );
192 :
193 : /* Set up slot context */
194 0 : ulong slot = test_ctx->slot_ctx.slot;
195 :
196 0 : fd_hash_t * bank_hash = fd_bank_bank_hash_modify( bank );
197 0 : fd_memcpy( bank_hash, test_ctx->slot_ctx.parent_bank_hash, sizeof(fd_hash_t) );
198 :
199 : /* All bank mgr stuff here. */
200 :
201 0 : fd_bank_slot_set( bank, slot );
202 :
203 0 : fd_bank_parent_slot_set( bank, test_ctx->slot_ctx.prev_slot );
204 :
205 0 : fd_bank_block_height_set( bank, test_ctx->slot_ctx.block_height );
206 :
207 0 : fd_bank_capitalization_set( bank, test_ctx->slot_ctx.prev_epoch_capitalization );
208 :
209 0 : fd_bank_lamports_per_signature_set( bank, 5000UL );
210 :
211 0 : fd_bank_prev_lamports_per_signature_set( bank, test_ctx->slot_ctx.prev_lps );
212 :
213 : // self.max_tick_height = (self.slot + 1) * self.ticks_per_slot;
214 0 : fd_bank_hashes_per_tick_set( bank, test_ctx->epoch_ctx.hashes_per_tick );
215 :
216 0 : fd_bank_ticks_per_slot_set( bank, test_ctx->epoch_ctx.ticks_per_slot );
217 :
218 0 : fd_bank_ns_per_slot_set( bank, 400000000 ); // TODO: restore from input
219 :
220 0 : fd_bank_genesis_creation_time_set( bank, test_ctx->epoch_ctx.genesis_creation_time );
221 :
222 0 : fd_bank_slots_per_year_set( bank, test_ctx->epoch_ctx.slots_per_year );
223 :
224 0 : fd_bank_parent_signature_cnt_set( bank, test_ctx->slot_ctx.parent_signature_count );
225 :
226 0 : fd_fee_rate_governor_t * fee_rate_governor = fd_bank_fee_rate_governor_modify( bank );
227 0 : *fee_rate_governor = (fd_fee_rate_governor_t){
228 0 : .target_lamports_per_signature = test_ctx->slot_ctx.fee_rate_governor.target_lamports_per_signature,
229 0 : .target_signatures_per_slot = test_ctx->slot_ctx.fee_rate_governor.target_signatures_per_slot,
230 0 : .min_lamports_per_signature = test_ctx->slot_ctx.fee_rate_governor.min_lamports_per_signature,
231 0 : .max_lamports_per_signature = test_ctx->slot_ctx.fee_rate_governor.max_lamports_per_signature,
232 0 : .burn_percent = (uchar)test_ctx->slot_ctx.fee_rate_governor.burn_percent
233 0 : };
234 :
235 0 : fd_inflation_t * inflation = fd_bank_inflation_modify( bank );
236 0 : *inflation = (fd_inflation_t){
237 0 : .initial = test_ctx->epoch_ctx.inflation.initial,
238 0 : .terminal = test_ctx->epoch_ctx.inflation.terminal,
239 0 : .taper = test_ctx->epoch_ctx.inflation.taper,
240 0 : .foundation = test_ctx->epoch_ctx.inflation.foundation,
241 0 : .foundation_term = test_ctx->epoch_ctx.inflation.foundation_term
242 0 : };
243 :
244 0 : fd_bank_block_height_set( bank, test_ctx->slot_ctx.block_height );
245 :
246 : /* Initialize the current running epoch stake and vote accounts */
247 :
248 0 : fd_vote_states_t * vote_states = fd_bank_vote_states_locking_modify( bank );
249 0 : vote_states = fd_vote_states_join( fd_vote_states_new( vote_states, FD_RUNTIME_MAX_VOTE_ACCOUNTS, 999UL ) );
250 0 : fd_bank_vote_states_end_locking_modify( bank );
251 :
252 0 : fd_vote_states_t * vote_states_prev = fd_bank_vote_states_prev_locking_modify( bank );
253 0 : vote_states_prev = fd_vote_states_join( fd_vote_states_new( vote_states_prev, FD_RUNTIME_MAX_VOTE_ACCOUNTS, 999UL ) );
254 0 : fd_bank_vote_states_prev_end_locking_modify( bank );
255 :
256 0 : fd_vote_states_t * vote_states_prev_prev = fd_bank_vote_states_prev_prev_locking_modify( bank );
257 0 : vote_states_prev_prev = fd_vote_states_join( fd_vote_states_new( vote_states_prev_prev, FD_RUNTIME_MAX_VOTE_ACCOUNTS, 999UL ) );
258 0 : fd_bank_vote_states_prev_prev_end_locking_modify( bank );
259 :
260 0 : fd_stake_delegations_t * stake_delegations = fd_banks_stake_delegations_root_query( banks );
261 0 : stake_delegations = fd_stake_delegations_join( fd_stake_delegations_new( stake_delegations, FD_RUNTIME_MAX_STAKE_ACCOUNTS, 0 ) );
262 :
263 : /* Load in all accounts with > 0 lamports provided in the context. The input expects unique account pubkeys. */
264 0 : vote_states = fd_bank_vote_states_locking_modify( bank );
265 0 : for( ushort i=0; i<test_ctx->acct_states_count; i++ ) {
266 0 : FD_TXN_ACCOUNT_DECL(acc);
267 0 : fd_runtime_fuzz_load_account( acc, funk, xid, &test_ctx->acct_states[i], 1 );
268 :
269 : /* Update vote accounts cache for epoch T */
270 0 : fd_pubkey_t pubkey;
271 0 : memcpy( &pubkey, test_ctx->acct_states[i].address, sizeof(fd_pubkey_t) );
272 0 : fd_runtime_fuzz_block_register_vote_account(
273 0 : funk,
274 0 : xid,
275 0 : vote_states,
276 0 : &pubkey,
277 0 : runner->spad );
278 :
279 : /* Update the stake delegations cache for epoch T */
280 0 : fd_runtime_fuzz_block_register_stake_delegation( funk,
281 0 : xid,
282 0 : stake_delegations,
283 0 : &pubkey );
284 0 : }
285 :
286 : /* Refresh vote accounts to calculate stake delegations */
287 0 : fd_runtime_fuzz_block_refresh_vote_accounts( vote_states, stake_delegations );
288 0 : fd_bank_vote_states_end_locking_modify( bank );
289 :
290 : /* Finish init epoch bank sysvars */
291 0 : fd_epoch_schedule_t epoch_schedule_[1];
292 0 : fd_epoch_schedule_t * epoch_schedule = fd_sysvar_epoch_schedule_read( funk, xid, epoch_schedule_ );
293 0 : FD_TEST( epoch_schedule );
294 0 : fd_bank_epoch_schedule_set( bank, *epoch_schedule );
295 :
296 0 : fd_rent_t const * rent = fd_sysvar_rent_read( funk, xid, runner->spad );
297 0 : FD_TEST( rent );
298 0 : fd_bank_rent_set( bank, *rent );
299 :
300 0 : fd_bank_epoch_set( bank, fd_slot_to_epoch( epoch_schedule, slot, NULL ) );
301 :
302 :
303 : /* Refresh the program cache */
304 0 : fd_runtime_fuzz_refresh_program_cache( bank, funk, xid, test_ctx->acct_states, test_ctx->acct_states_count, runner->spad );
305 :
306 : /* Update vote cache for epoch T-1 */
307 0 : vote_states_prev = fd_bank_vote_states_prev_locking_modify( bank );
308 0 : fd_runtime_fuzz_block_update_prev_epoch_votes_cache( vote_states_prev,
309 0 : test_ctx->epoch_ctx.vote_accounts_t_1,
310 0 : test_ctx->epoch_ctx.vote_accounts_t_1_count,
311 0 : runner->spad );
312 0 : fd_bank_vote_states_prev_end_locking_modify( bank );
313 :
314 : /* Update vote cache for epoch T-2 */
315 0 : vote_states_prev_prev = fd_bank_vote_states_prev_prev_locking_modify( bank );
316 0 : fd_runtime_fuzz_block_update_prev_epoch_votes_cache( vote_states_prev_prev,
317 0 : test_ctx->epoch_ctx.vote_accounts_t_2,
318 0 : test_ctx->epoch_ctx.vote_accounts_t_2_count,
319 0 : runner->spad );
320 0 : fd_bank_vote_states_prev_prev_end_locking_modify( bank );
321 :
322 : /* Update leader schedule */
323 0 : fd_runtime_update_leaders( bank, runner->spad );
324 :
325 : /* Initialize the blockhash queue and recent blockhashes sysvar from the input blockhash queue */
326 0 : ulong blockhash_seed; FD_TEST( fd_rng_secure( &blockhash_seed, sizeof(ulong) ) );
327 0 : fd_blockhashes_init( fd_bank_block_hash_queue_modify( bank ), blockhash_seed );
328 :
329 : /* TODO: We might need to load this in from the input. We also need to
330 : size this out for worst case, but this also blows up the memory
331 : requirement. */
332 : /* Allocate all the memory for the rent fresh accounts list */
333 :
334 : // Set genesis hash to {0}
335 0 : fd_hash_t * genesis_hash = fd_bank_genesis_hash_modify( bank );
336 0 : fd_memset( genesis_hash->hash, 0, sizeof(fd_hash_t) );
337 :
338 : // Use the latest lamports per signature
339 0 : fd_recent_block_hashes_t const * rbh = fd_sysvar_recent_hashes_read( funk, xid, runner->spad );
340 0 : if( rbh && !deq_fd_block_block_hash_entry_t_empty( rbh->hashes ) ) {
341 0 : fd_block_block_hash_entry_t const * last = deq_fd_block_block_hash_entry_t_peek_head_const( rbh->hashes );
342 0 : if( last && last->fee_calculator.lamports_per_signature!=0UL ) {
343 0 : fd_bank_lamports_per_signature_set( bank, last->fee_calculator.lamports_per_signature );
344 0 : fd_bank_prev_lamports_per_signature_set( bank, last->fee_calculator.lamports_per_signature );
345 0 : }
346 0 : }
347 :
348 : /* Make a new funk transaction since we're done loading in accounts for context */
349 0 : fd_funk_txn_xid_t fork_xid = { .ul = { slot, slot } };
350 0 : fd_funk_txn_prepare( funk, xid, &fork_xid );
351 0 : xid[0] = fork_xid;
352 :
353 : /* Reset the lthash to zero, because we are in a new Funk transaction now */
354 0 : fd_lthash_value_t lthash = {0};
355 0 : fd_bank_lthash_set( bank, lthash );
356 :
357 : // Populate blockhash queue and recent blockhashes sysvar
358 0 : for( ushort i=0; i<test_ctx->blockhash_queue_count; ++i ) {
359 0 : fd_hash_t hash;
360 0 : memcpy( &hash, test_ctx->blockhash_queue[i]->bytes, sizeof(fd_hash_t) );
361 0 : fd_bank_poh_set( bank, hash );
362 0 : fd_sysvar_recent_hashes_update( bank, funk, xid, NULL ); /* appends an entry */
363 0 : }
364 :
365 : // Set the current poh from the input (we skip POH verification in this fuzzing target)
366 0 : fd_hash_t * poh = fd_bank_poh_modify( bank );
367 0 : fd_memcpy( poh->hash, test_ctx->slot_ctx.poh, sizeof(fd_hash_t) );
368 :
369 : /* Restore sysvar cache */
370 0 : fd_sysvar_cache_restore_fuzz( bank, funk, xid );
371 :
372 : /* Prepare raw transaction pointers and block / microblock infos */
373 0 : ulong txn_cnt = test_ctx->txns_count;
374 :
375 : // For fuzzing, we're using a single microblock batch that contains a single microblock containing all transactions
376 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) );
377 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) );
378 0 : fd_microblock_info_t * microblock_info = fd_spad_alloc( runner->spad, alignof(fd_microblock_info_t), sizeof(fd_microblock_info_t) );
379 0 : fd_memset( block_info, 0, sizeof(fd_runtime_block_info_t) );
380 0 : fd_memset( batch_info, 0, sizeof(fd_microblock_batch_info_t) );
381 0 : fd_memset( microblock_info, 0, sizeof(fd_microblock_info_t) );
382 :
383 0 : block_info->microblock_batch_cnt = 1UL;
384 0 : block_info->microblock_cnt = 1UL;
385 0 : block_info->microblock_batch_infos = batch_info;
386 :
387 0 : batch_info->microblock_cnt = 1UL;
388 0 : batch_info->microblock_infos = microblock_info;
389 :
390 0 : ulong batch_signature_cnt = 0UL;
391 0 : ulong batch_txn_cnt = 0UL;
392 0 : ulong batch_account_cnt = 0UL;
393 0 : ulong signature_cnt = 0UL;
394 0 : ulong account_cnt = 0UL;
395 :
396 0 : fd_microblock_hdr_t * microblock_hdr = fd_spad_alloc( runner->spad, alignof(fd_microblock_hdr_t), sizeof(fd_microblock_hdr_t) );
397 0 : fd_memset( microblock_hdr, 0, sizeof(fd_microblock_hdr_t) );
398 :
399 0 : fd_txn_p_t * txn_ptrs = fd_spad_alloc( runner->spad, alignof(fd_txn_p_t), txn_cnt * sizeof(fd_txn_p_t) );
400 0 : for( ulong i=0UL; i<txn_cnt; i++ ) {
401 0 : fd_txn_p_t * txn = &txn_ptrs[i];
402 0 : ulong msg_sz = fd_runtime_fuzz_serialize_txn( txn->payload, &test_ctx->txns[i] );
403 :
404 : // Reject any transactions over 1232 bytes
405 0 : if( FD_UNLIKELY( msg_sz==ULONG_MAX ) ) {
406 0 : return NULL;
407 0 : }
408 0 : txn->payload_sz = msg_sz;
409 :
410 : // Reject any transactions that cannot be parsed
411 0 : if( FD_UNLIKELY( !fd_txn_parse( txn->payload, msg_sz, TXN( txn ), NULL ) ) ) {
412 0 : return NULL;
413 0 : }
414 :
415 0 : signature_cnt += TXN( txn )->signature_cnt;
416 0 : account_cnt += fd_txn_account_cnt( TXN( txn ), FD_TXN_ACCT_CAT_ALL );
417 0 : }
418 :
419 0 : microblock_hdr->txn_cnt = txn_cnt;
420 0 : microblock_info->microblock.raw = (uchar *)microblock_hdr;
421 :
422 0 : microblock_info->signature_cnt = signature_cnt;
423 0 : microblock_info->account_cnt = account_cnt;
424 0 : microblock_info->txns = txn_ptrs;
425 :
426 0 : batch_signature_cnt += signature_cnt;
427 0 : batch_txn_cnt += txn_cnt;
428 0 : batch_account_cnt += account_cnt;
429 :
430 0 : block_info->signature_cnt = batch_info->signature_cnt = batch_signature_cnt;
431 0 : block_info->txn_cnt = batch_info->txn_cnt = batch_txn_cnt;
432 0 : block_info->account_cnt = batch_info->account_cnt = batch_account_cnt;
433 :
434 0 : return block_info;
435 0 : }
436 :
437 : /* Takes in a block_info created from `fd_runtime_fuzz_block_ctx_create()`
438 : and executes it against the runtime. Returns the execution result. */
439 : static int
440 : fd_runtime_fuzz_block_ctx_exec( fd_solfuzz_runner_t * runner,
441 : fd_funk_txn_xid_t const * xid,
442 0 : fd_runtime_block_info_t * block_info ) {
443 0 : int res = 0;
444 :
445 : // Prepare. Execute. Finalize.
446 0 : FD_SPAD_FRAME_BEGIN( runner->spad ) {
447 0 : fd_capture_ctx_t * capture_ctx = NULL;
448 0 : if( runner->solcap ) {
449 0 : void * capture_ctx_mem = fd_spad_alloc( runner->spad, fd_capture_ctx_align(), fd_capture_ctx_footprint() );
450 0 : capture_ctx = fd_capture_ctx_new( capture_ctx_mem );
451 0 : if( FD_UNLIKELY( capture_ctx==NULL ) ) {
452 0 : FD_LOG_ERR(("capture_ctx_mem is NULL, cannot write solcap"));
453 0 : }
454 0 : capture_ctx->capture = runner->solcap;
455 0 : capture_ctx->solcap_start_slot = fd_bank_slot_get( runner->bank );
456 0 : fd_solcap_writer_set_slot( capture_ctx->capture, fd_bank_slot_get( runner->bank ) );
457 0 : }
458 :
459 0 : fd_rewards_recalculate_partitioned_rewards( runner->banks, runner->bank, runner->funk, xid, capture_ctx, runner->spad );
460 :
461 : /* Process new epoch may push a new spad frame onto the runtime spad. We should make sure this frame gets
462 : cleared (if it was allocated) before executing the block. */
463 0 : int is_epoch_boundary = 0;
464 0 : fd_runtime_block_pre_execute_process_new_epoch( runner->banks, runner->bank, runner->funk, xid, capture_ctx, runner->spad, &is_epoch_boundary );
465 :
466 0 : res = fd_runtime_block_execute_prepare( runner->bank, runner->funk, xid, capture_ctx, runner->spad );
467 0 : if( FD_UNLIKELY( res ) ) {
468 0 : return res;
469 0 : }
470 :
471 0 : fd_txn_p_t * txn_ptrs = block_info->microblock_batch_infos[0].microblock_infos[0].txns;
472 0 : ulong txn_cnt = block_info->microblock_batch_infos[0].txn_cnt;
473 :
474 : /* Sequential transaction execution */
475 0 : for( ulong i=0UL; i<txn_cnt; i++ ) {
476 0 : fd_txn_p_t * txn = &txn_ptrs[i];
477 :
478 : /* Update the program cache */
479 0 : fd_runtime_update_program_cache( runner->bank, runner->funk, xid, txn, runner->spad );
480 :
481 : /* Execute the transaction against the runtime */
482 0 : res = FD_RUNTIME_EXECUTE_SUCCESS;
483 0 : fd_exec_txn_ctx_t * txn_ctx = fd_runtime_fuzz_txn_ctx_exec( runner, xid, txn, &res );
484 0 : txn_ctx->exec_err = res;
485 :
486 0 : if( FD_UNLIKELY( !(txn_ctx->flags & FD_TXN_P_FLAGS_EXECUTE_SUCCESS) ) ) {
487 0 : break;
488 0 : }
489 :
490 : /* Finalize the transaction */
491 0 : fd_runtime_finalize_txn(
492 0 : runner->funk,
493 0 : NULL,
494 0 : xid,
495 0 : txn_ctx,
496 0 : runner->bank,
497 0 : capture_ctx );
498 :
499 0 : if( FD_UNLIKELY( !(txn_ctx->flags & FD_TXN_P_FLAGS_EXECUTE_SUCCESS) ) ) {
500 0 : break;
501 0 : }
502 :
503 0 : res = FD_RUNTIME_EXECUTE_SUCCESS;
504 0 : }
505 :
506 : /* Finalize the block */
507 0 : fd_runtime_block_execute_finalize( runner->bank, runner->funk, xid, capture_ctx, 1 );
508 0 : } FD_SPAD_FRAME_END;
509 :
510 0 : return res;
511 0 : }
512 :
513 : ulong
514 : fd_solfuzz_block_run( fd_solfuzz_runner_t * runner,
515 : void const * input_,
516 : void ** output_,
517 : void * output_buf,
518 0 : ulong output_bufsz ) {
519 0 : fd_exec_test_block_context_t const * input = fd_type_pun_const( input_ );
520 0 : fd_exec_test_block_effects_t ** output = fd_type_pun( output_ );
521 :
522 0 : FD_SPAD_FRAME_BEGIN( runner->spad ) {
523 : /* Set up the block execution context */
524 0 : fd_runtime_block_info_t * block_info = fd_runtime_fuzz_block_ctx_create( runner, input );
525 0 : if( block_info==NULL ) {
526 0 : fd_runtime_fuzz_block_ctx_destroy( runner );
527 0 : return 0;
528 0 : }
529 :
530 0 : fd_funk_txn_xid_t xid = { .ul = { fd_bank_slot_get( runner->bank ), fd_bank_slot_get( runner->bank ) } };
531 :
532 : /* Execute the constructed block against the runtime. */
533 0 : int res = fd_runtime_fuzz_block_ctx_exec( runner, &xid, block_info );
534 :
535 : /* Start saving block exec results */
536 0 : FD_SCRATCH_ALLOC_INIT( l, output_buf );
537 0 : ulong output_end = (ulong)output_buf + output_bufsz;
538 :
539 0 : fd_exec_test_block_effects_t * effects =
540 0 : FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_exec_test_block_effects_t),
541 0 : sizeof (fd_exec_test_block_effects_t) );
542 0 : if( FD_UNLIKELY( _l > output_end ) ) {
543 0 : abort();
544 0 : }
545 0 : fd_memset( effects, 0, sizeof(fd_exec_test_block_effects_t) );
546 :
547 : /* Capture error status */
548 0 : effects->has_error = !!( res );
549 :
550 : /* Capture capitalization */
551 0 : effects->slot_capitalization = fd_bank_capitalization_get( runner->bank );
552 :
553 : /* Capture hashes */
554 0 : fd_hash_t bank_hash = fd_bank_bank_hash_get( runner->bank );
555 0 : fd_memcpy( effects->bank_hash, bank_hash.hash, sizeof(fd_hash_t) );
556 :
557 : /* Capture cost tracker */
558 0 : fd_cost_tracker_t const * cost_tracker = fd_bank_cost_tracker_locking_query( runner->bank );
559 0 : effects->has_cost_tracker = 1;
560 0 : effects->cost_tracker = (fd_exec_test_cost_tracker_t) {
561 0 : .block_cost = cost_tracker ? cost_tracker->block_cost : 0UL,
562 0 : .vote_cost = cost_tracker ? cost_tracker->vote_cost : 0UL,
563 0 : };
564 0 : fd_bank_cost_tracker_end_locking_query( runner->bank );
565 :
566 0 : ulong actual_end = FD_SCRATCH_ALLOC_FINI( l, 1UL );
567 0 : fd_runtime_fuzz_block_ctx_destroy( runner );
568 :
569 0 : *output = effects;
570 0 : return actual_end - (ulong)output_buf;
571 0 : } FD_SPAD_FRAME_END;
572 0 : }
|