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