Line data Source code
1 : #include "fd_solfuzz.h"
2 : #include "fd_solfuzz_private.h"
3 : #include "fd_txn_harness.h"
4 : #include "fd_dump_pb.h"
5 : #include "../fd_runtime.h"
6 : #include "../sysvar/fd_sysvar_epoch_schedule.h"
7 : #include "../../alpenglow/fd_alpenglow.h"
8 : #include "../../progcache/fd_progcache_admin.h"
9 : #include "../../log_collector/fd_log_collector.h"
10 : #include "../../../ballet/txn/fd_compact_u16.h"
11 : #include "../../../disco/pack/fd_chkdup.h"
12 :
13 : /* Macros to append data to construct a serialized transaction
14 : without exceeding bounds */
15 0 : #define FD_CHECKED_ADD_TO_TXN_DATA( _begin, _cur_data, _to_add, _sz ) __extension__({ \
16 0 : if( FD_UNLIKELY( (*_cur_data)+_sz>_begin+FD_TXN_MTU ) ) return ULONG_MAX; \
17 0 : fd_memcpy( *_cur_data, _to_add, _sz ); \
18 0 : *_cur_data += _sz; \
19 0 : })
20 :
21 0 : #define FD_CHECKED_ADD_CU16_TO_TXN_DATA( _begin, _cur_data, _to_add ) __extension__({ \
22 0 : do { \
23 0 : uchar _buf[3]; \
24 0 : ulong _sz = (ulong)fd_cu16_enc( (ushort)_to_add, _buf ); \
25 0 : FD_CHECKED_ADD_TO_TXN_DATA( _begin, _cur_data, _buf, _sz ); \
26 0 : } while(0); \
27 0 : })
28 :
29 : static void
30 0 : fd_solfuzz_txn_ctx_destroy( fd_solfuzz_runner_t * runner ) {
31 0 : fd_progcache_reset( runner->progcache->join );
32 :
33 : /* Purge the fork attached in ctx_create so the accdb fork pool slot
34 : is released back for reuse. Without this, repeated harness
35 : invocations (e.g. under a fuzzer) exhaust max_live_slots and the
36 : next attach_child returns NULL, causing a segfault inside accdb. */
37 0 : fd_accdb_purge( runner->accdb, runner->bank->accdb_fork_id );
38 0 : int charge_busy = 0;
39 0 : fd_accdb_background( runner->accdb, &charge_busy );
40 :
41 0 : }
42 :
43 : /* Creates transaction execution context for a single test case.
44 : Returns a parsed txn descriptor on success and NULL on failure. */
45 : static fd_txn_p_t *
46 : fd_solfuzz_pb_txn_ctx_create( fd_solfuzz_runner_t * runner,
47 0 : fd_exec_test_txn_context_t const * test_ctx ) {
48 0 : fd_accdb_t * accdb = runner->accdb;
49 :
50 0 : ulong slot = fd_solfuzz_pb_get_slot( test_ctx->account_shared_data, test_ctx->account_shared_data_count );
51 :
52 : /* Initialize bank from input txn bank */
53 0 : fd_banks_clear_bank( runner->banks, runner->bank );
54 0 : runner->bank->f.slot = slot;
55 0 : runner->bank->accdb_fork_id = fd_accdb_attach_child( accdb, runner->root_fork_id );
56 0 : runner->bank->parent_accdb_fork_id = runner->bank->accdb_fork_id;
57 0 : runner->bank->progcache_fork_id = fd_progcache_attach_child( runner->progcache->join, fd_progcache_fork_id_initial() );
58 :
59 0 : FD_TEST( test_ctx->has_bank );
60 0 : fd_exec_test_txn_bank_t const * txn_bank = &test_ctx->bank;
61 :
62 : /* Blockhash queue */
63 0 : fd_solfuzz_pb_restore_blockhash_queue( runner->bank, txn_bank->blockhash_queue, txn_bank->blockhash_queue_count );
64 :
65 : /* RBH lamports per signature. In the Agave harness this is set inside
66 : the fee rate governor itself. */
67 0 : runner->bank->f.rbh_lamports_per_sig = txn_bank->rbh_lamports_per_signature;
68 :
69 : /* Fee rate governor */
70 0 : FD_TEST( txn_bank->has_fee_rate_governor );
71 0 : fd_solfuzz_pb_restore_fee_rate_governor( runner->bank, &txn_bank->fee_rate_governor );
72 :
73 : /* Parent slot */
74 0 : runner->bank->f.parent_slot = slot-1UL;
75 :
76 : /* Total epoch stake */
77 0 : runner->bank->f.total_epoch_stake = txn_bank->total_epoch_stake;
78 :
79 : /* Features */
80 0 : FD_TEST( txn_bank->has_features );
81 0 : fd_exec_test_feature_set_t const * feature_set = &txn_bank->features;
82 0 : fd_features_t * features_bm = &runner->bank->f.features;
83 0 : FD_TEST( fd_solfuzz_pb_restore_features( features_bm, feature_set ) );
84 :
85 : /* Load account states into accdb (note this is different from the account keys):
86 : Account state = accounts to populate Funk
87 : Account keys = account keys that the transaction needs */
88 0 : for( ulong i = 0; i < test_ctx->account_shared_data_count; i++ ) {
89 : /* Load the accounts into the account manager
90 : Borrowed accounts get reset anyways - we just need to load the account somewhere */
91 0 : fd_solfuzz_pb_load_account( runner->runtime, accdb, runner->bank->accdb_fork_id, &test_ctx->account_shared_data[i], i );
92 0 : }
93 :
94 0 : runner->bank->f.ticks_per_slot = 64;
95 0 : runner->bank->f.slot_params = FD_SLOT_PARAMS_400MS;
96 0 : runner->bank->f.slot_params.slots_per_year = SECONDS_PER_YEAR * (1000000000.0 / (double)6250000) / (double)(runner->bank->f.ticks_per_slot);
97 0 : runner->bank->f.slot_params_default = runner->bank->f.slot_params;
98 :
99 : /* Restore sysvars from account context */
100 0 : fd_sysvar_cache_restore_fuzz( runner->bank, runner->accdb );
101 :
102 : /* Epoch schedule */
103 0 : FD_TEST( fd_sysvar_cache_epoch_schedule_read( &runner->bank->f.sysvar_cache, &runner->bank->f.epoch_schedule ) );
104 :
105 : /* Epoch */
106 0 : runner->bank->f.epoch = fd_slot_to_epoch( &runner->bank->f.epoch_schedule, slot, NULL );
107 :
108 : /* Rent */
109 0 : FD_TEST( fd_sysvar_cache_rent_read( &runner->bank->f.sysvar_cache, &runner->bank->f.rent ) );
110 :
111 : /* Alpenglow migration slot */
112 0 : runner->bank->f.alpenglow_migration_slot = fd_alpenglow_migration_slot( runner->bank, accdb );
113 :
114 : /* Create the raw txn (https://solana.com/docs/core/transactions#transaction-size) */
115 0 : fd_txn_p_t * txn = fd_spad_alloc( runner->spad, alignof(fd_txn_p_t), sizeof(fd_txn_p_t) );
116 0 : ulong msg_sz = fd_solfuzz_pb_txn_serialize( txn->payload, &test_ctx->tx );
117 0 : if( FD_UNLIKELY( msg_sz==ULONG_MAX ) ) {
118 0 : return NULL;
119 0 : }
120 :
121 : /* Set up txn descriptor from raw data */
122 0 : if( FD_UNLIKELY( !fd_txn_parse( txn->payload, msg_sz, TXN( txn ), NULL ) ) ) {
123 0 : return NULL;
124 0 : }
125 :
126 : /* V1 transactions with duplicate addresses fail sanitization in
127 : Agave, so we should drop these. */
128 0 : fd_txn_t const * txn_desc = TXN( txn );
129 0 : if( txn_desc->transaction_version==FD_TXN_V1 ) {
130 0 : fd_rng_t rng[1];
131 0 : fd_chkdup_t chkdup[1];
132 0 : fd_chkdup_t * chk = fd_chkdup_join( fd_chkdup_new( chkdup, fd_rng_join( fd_rng_new( rng, 0U, 0UL ) ) ) );
133 0 : fd_acct_addr_t const * addrs = fd_txn_get_acct_addrs( txn_desc, txn->payload );
134 0 : if( FD_UNLIKELY( fd_chkdup_check( chk, addrs, txn_desc->acct_addr_cnt, NULL, 0UL ) ) ) {
135 0 : return NULL;
136 0 : }
137 0 : }
138 :
139 0 : txn->payload_sz = (ushort)msg_sz;
140 :
141 0 : return txn;
142 0 : }
143 :
144 : static uchar
145 0 : fd_solfuzz_pb_txn_version( fd_exec_test_transaction_message_t const * message ) {
146 0 : switch( message->version ) {
147 0 : case FD_EXEC_TEST_TRANSACTION_VERSION_TRANSACTION_VERSION_LEGACY: return FD_TXN_VLEGACY;
148 0 : case FD_EXEC_TEST_TRANSACTION_VERSION_TRANSACTION_VERSION_V1: return FD_TXN_V1;
149 0 : default: return FD_TXN_V0;
150 0 : }
151 0 : }
152 :
153 : /* Serializes a Transaction V1 (SIMD-0385): message first, then exactly
154 : num_required_signatures signatures. An absent v1_config is an empty
155 : config mask. */
156 : static ulong
157 : fd_solfuzz_pb_txn_serialize_v1( uchar * txn_raw_begin,
158 0 : fd_exec_test_sanitized_transaction_t const * tx ) {
159 0 : uchar * txn_raw_cur_ptr = txn_raw_begin;
160 0 : fd_exec_test_transaction_config_t const empty_cfg = FD_EXEC_TEST_TRANSACTION_CONFIG_INIT_ZERO;
161 0 : fd_exec_test_transaction_config_t const * cfg = tx->message.has_v1_config ? &tx->message.v1_config : &empty_cfg;
162 :
163 : /* Version byte: high bit set, version 1 */
164 0 : uchar version_byte = (uchar)(0x80U | FD_TXN_V1);
165 0 : FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &version_byte, sizeof(uchar) );
166 :
167 : /* Header (3 bytes). V1 has no separate signature count: exactly
168 : num_required_signatures signatures are appended at the end. */
169 0 : uchar num_required_signatures = fd_uchar_max( 1, (uchar)tx->message.header.num_required_signatures );
170 0 : uchar ro_signed_cnt = (uchar)tx->message.header.num_readonly_signed_accounts;
171 0 : uchar ro_unsigned_cnt = (uchar)tx->message.header.num_readonly_unsigned_accounts;
172 0 : FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &num_required_signatures, sizeof(uchar) );
173 0 : FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &ro_signed_cnt, sizeof(uchar) );
174 0 : FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &ro_unsigned_cnt, sizeof(uchar) );
175 :
176 : /* Config mask (u32 LE): bits 0+1 priority fee, 2 CU limit, 3 loaded
177 : accounts data size limit, 4 heap size. Field presence == bit set. */
178 0 : uint config_mask = 0U;
179 0 : if( cfg->has_priority_fee ) config_mask |= 0x03U;
180 0 : if( cfg->has_compute_unit_limit ) config_mask |= 0x04U;
181 0 : if( cfg->has_loaded_accounts_data_size_limit ) config_mask |= 0x08U;
182 0 : if( cfg->has_heap_size ) config_mask |= 0x10U;
183 0 : FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &config_mask, sizeof(uint) );
184 :
185 : /* Recent blockhash */
186 0 : FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, tx->message.recent_blockhash, sizeof(fd_hash_t) );
187 :
188 : /* Instruction count and static account count (u8 each) */
189 0 : uchar instr_cnt = (uchar)tx->message.instructions_count;
190 0 : uchar acct_addr_cnt = (uchar)tx->message.account_keys_count;
191 0 : FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &instr_cnt, sizeof(uchar) );
192 0 : FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &acct_addr_cnt, sizeof(uchar) );
193 :
194 : /* Static account keys */
195 0 : for( uchar i=0; i<acct_addr_cnt; i++ ) {
196 0 : FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, tx->message.account_keys[i]->bytes, sizeof(fd_pubkey_t) );
197 0 : }
198 :
199 : /* Config values, only the present ones, in ascending mask-bit order */
200 0 : if( cfg->has_priority_fee ) {
201 0 : ulong v = cfg->priority_fee;
202 0 : FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &v, sizeof(ulong) );
203 0 : }
204 0 : if( cfg->has_compute_unit_limit ) {
205 0 : uint v = cfg->compute_unit_limit;
206 0 : FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &v, sizeof(uint) );
207 0 : }
208 0 : if( cfg->has_loaded_accounts_data_size_limit ) {
209 0 : uint v = cfg->loaded_accounts_data_size_limit;
210 0 : FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &v, sizeof(uint) );
211 0 : }
212 0 : if( cfg->has_heap_size ) {
213 0 : uint v = cfg->heap_size;
214 0 : FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &v, sizeof(uint) );
215 0 : }
216 :
217 : /* Instruction headers: program_id (u8), account count (u8), data length (u16 LE) */
218 0 : for( uchar i=0; i<instr_cnt; i++ ) {
219 0 : fd_exec_test_compiled_instruction_t const * ix = &tx->message.instructions[i];
220 0 : uchar program_id_index = (uchar)ix->program_id_index;
221 0 : uchar acct_cnt = (uchar)ix->accounts_count;
222 0 : ushort data_len = (ushort)( ix->data ? ix->data->size : 0U );
223 0 : FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &program_id_index, sizeof(uchar) );
224 0 : FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &acct_cnt, sizeof(uchar) );
225 0 : FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &data_len, sizeof(ushort) );
226 0 : }
227 :
228 : /* Instruction payloads: account indices then data, same truncation as the headers */
229 0 : for( uchar i=0; i<instr_cnt; i++ ) {
230 0 : fd_exec_test_compiled_instruction_t const * ix = &tx->message.instructions[i];
231 0 : uchar acct_cnt = (uchar)ix->accounts_count;
232 0 : ushort data_len = (ushort)( ix->data ? ix->data->size : 0U );
233 0 : for( uchar j=0; j<acct_cnt; j++ ) {
234 0 : uchar account_index = (uchar)ix->accounts[j];
235 0 : FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &account_index, sizeof(uchar) );
236 0 : }
237 0 : if( data_len ) FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, ix->data->bytes, data_len );
238 0 : }
239 :
240 : /* Signatures: exactly num_required_signatures of them (zero-filled if
241 : fewer were provided, extras ignored) */
242 0 : for( uchar i=0; i<num_required_signatures; i++ ) {
243 0 : fd_signature_t sig = {0};
244 0 : if( i<tx->signatures_count && tx->signatures && tx->signatures[i] ) sig = FD_LOAD( fd_signature_t, tx->signatures[i]->bytes );
245 0 : FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &sig, FD_TXN_SIGNATURE_SZ );
246 0 : }
247 :
248 0 : return (ulong)(txn_raw_cur_ptr - txn_raw_begin);
249 0 : }
250 :
251 : ulong
252 : fd_solfuzz_pb_txn_serialize( uchar * txn_raw_begin,
253 0 : fd_exec_test_sanitized_transaction_t const * tx ) {
254 0 : uchar * txn_raw_cur_ptr = txn_raw_begin;
255 :
256 0 : uchar version = fd_solfuzz_pb_txn_version( &tx->message );
257 0 : if( version==FD_TXN_V1 ) return fd_solfuzz_pb_txn_serialize_v1( txn_raw_begin, tx );
258 :
259 : /* Compact array of signatures (https://solana.com/docs/core/transactions#transaction)
260 : Note that although documentation interchangeably refers to the signature cnt as a compact-u16
261 : and a u8, the max signature cnt is capped at 48 (due to txn size limits), so u8 and compact-u16
262 : is represented the same way anyways and can be parsed identically. */
263 : // Note: always create a valid txn with 1+ signatures, add an empty signature if none is provided
264 0 : uchar signature_cnt = fd_uchar_max( 1, (uchar) tx->signatures_count );
265 0 : FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &signature_cnt, sizeof(uchar) );
266 0 : for( uchar i = 0; i < signature_cnt; ++i ) {
267 0 : fd_signature_t sig = {0};
268 0 : if( tx->signatures && tx->signatures[i] ) sig = FD_LOAD( fd_signature_t, tx->signatures[i]->bytes );
269 0 : FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &sig, FD_TXN_SIGNATURE_SZ );
270 0 : }
271 :
272 : /* Message */
273 : /* For v0 transactions, the highest bit of the num_required_signatures is set, and an extra byte is used for the version.
274 : https://solanacookbook.com/guides/versioned-transactions.html#versioned-transactions-transactionv0
275 :
276 : We will always create a transaction with at least 1 signature, and cap the signature count to 127 to avoid
277 : collisions with the header_b0 tag. */
278 0 : uchar num_required_signatures = fd_uchar_max( 1, fd_uchar_min( 127, (uchar) tx->message.header.num_required_signatures ) );
279 0 : if( version!=FD_TXN_VLEGACY ) {
280 0 : uchar header_b0 = (uchar) 0x80UL;
281 0 : FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &header_b0, sizeof(uchar) );
282 0 : }
283 :
284 : /* Header (3 bytes) (https://solana.com/docs/core/transactions#message-header) */
285 0 : FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &num_required_signatures, sizeof(uchar) );
286 0 : FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &tx->message.header.num_readonly_signed_accounts, sizeof(uchar) );
287 0 : FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &tx->message.header.num_readonly_unsigned_accounts, sizeof(uchar) );
288 :
289 : /* Compact array of account addresses (https://solana.com/docs/core/transactions#compact-array-format) */
290 : // Array length is a compact u16
291 0 : ushort num_acct_keys = (ushort) tx->message.account_keys_count;
292 0 : FD_CHECKED_ADD_CU16_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, num_acct_keys );
293 0 : for( ushort i = 0; i < num_acct_keys; ++i ) {
294 0 : FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, tx->message.account_keys[i]->bytes, sizeof(fd_pubkey_t) );
295 0 : }
296 :
297 : /* Recent blockhash (32 bytes) (https://solana.com/docs/core/transactions#recent-blockhash) */
298 : // Note: add an empty blockhash if none is provided
299 0 : FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, tx->message.recent_blockhash, sizeof(fd_hash_t) );
300 :
301 : /* Compact array of instructions (https://solana.com/docs/core/transactions#array-of-instructions) */
302 : // Instruction count is a compact u16
303 0 : ushort instr_count = (ushort) tx->message.instructions_count;
304 0 : FD_CHECKED_ADD_CU16_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, instr_count );
305 0 : for( ushort i = 0; i < instr_count; ++i ) {
306 : // Program ID index
307 0 : uchar program_id_index = (uchar) tx->message.instructions[i].program_id_index;
308 0 : FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &program_id_index, sizeof(uchar) );
309 :
310 : // Compact array of account addresses
311 0 : ushort acct_count = (ushort) tx->message.instructions[i].accounts_count;
312 0 : FD_CHECKED_ADD_CU16_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, acct_count );
313 0 : for( ushort j = 0; j < acct_count; ++j ) {
314 0 : uchar account_index = (uchar) tx->message.instructions[i].accounts[j];
315 0 : FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &account_index, sizeof(uchar) );
316 0 : }
317 :
318 : // Compact array of 8-bit data
319 0 : pb_bytes_array_t * data = tx->message.instructions[i].data;
320 0 : ushort data_len;
321 0 : if( data ) {
322 0 : data_len = (ushort) data->size;
323 0 : FD_CHECKED_ADD_CU16_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, data_len );
324 0 : FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, data->bytes, data_len );
325 0 : } else {
326 0 : data_len = 0;
327 0 : FD_CHECKED_ADD_CU16_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, data_len );
328 0 : }
329 0 : }
330 :
331 : /* Address table lookups (N/A for legacy transactions) */
332 0 : ushort addr_table_cnt = 0;
333 0 : if( version!=FD_TXN_VLEGACY ) {
334 : /* Compact array of address table lookups (https://solanacookbook.com/guides/versioned-transactions.html#compact-array-of-address-table-lookups) */
335 : // NOTE: The diagram is slightly wrong - the account key is a 32 byte pubkey, not a u8
336 0 : addr_table_cnt = (ushort) tx->message.address_table_lookups_count;
337 0 : FD_CHECKED_ADD_CU16_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, addr_table_cnt );
338 0 : for( ushort i = 0; i < addr_table_cnt; ++i ) {
339 : // Account key
340 0 : FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, tx->message.address_table_lookups[i].account_key, sizeof(fd_pubkey_t) );
341 :
342 : // Compact array of writable indexes
343 0 : ushort writable_count = (ushort) tx->message.address_table_lookups[i].writable_indexes_count;
344 0 : FD_CHECKED_ADD_CU16_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, writable_count );
345 0 : for( ushort j = 0; j < writable_count; ++j ) {
346 0 : uchar writable_index = (uchar) tx->message.address_table_lookups[i].writable_indexes[j];
347 0 : FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &writable_index, sizeof(uchar) );
348 0 : }
349 :
350 : // Compact array of readonly indexes
351 0 : ushort readonly_count = (ushort) tx->message.address_table_lookups[i].readonly_indexes_count;
352 0 : FD_CHECKED_ADD_CU16_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, readonly_count );
353 0 : for( ushort j = 0; j < readonly_count; ++j ) {
354 0 : uchar readonly_index = (uchar) tx->message.address_table_lookups[i].readonly_indexes[j];
355 0 : FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &readonly_index, sizeof(uchar) );
356 0 : }
357 0 : }
358 0 : }
359 :
360 0 : return (ulong)(txn_raw_cur_ptr - txn_raw_begin);
361 0 : }
362 :
363 : void
364 : fd_solfuzz_txn_ctx_exec( fd_solfuzz_runner_t * runner,
365 : fd_runtime_t * runtime,
366 : fd_txn_in_t const * txn_in,
367 : int * exec_res,
368 : fd_txn_out_t * txn_out,
369 0 : int reclaim_accounts ) {
370 :
371 0 : txn_out->err.is_committable = 1;
372 :
373 0 : runtime->log.enable_vm_tracing = runner->enable_vm_tracing;
374 0 : uchar * tracing_mem = NULL;
375 0 : if( runner->enable_vm_tracing ) {
376 0 : tracing_mem = fd_spad_alloc_check( runner->spad, FD_RUNTIME_VM_TRACE_STATIC_ALIGN, FD_RUNTIME_VM_TRACE_STATIC_FOOTPRINT * FD_MAX_INSTRUCTION_STACK_DEPTH );
377 0 : }
378 :
379 0 : runtime->accdb = runner->accdb;
380 0 : runtime->progcache = runner->progcache;
381 0 : runtime->status_cache = NULL;
382 0 : runtime->log.tracing_mem = tracing_mem;
383 0 : runtime->log.dumping_mem = NULL;
384 0 : runtime->log.capture_ctx = NULL;
385 0 : runtime->log.dump_proto_ctx = NULL;
386 0 : runtime->log.txn_dump_ctx = NULL;
387 0 : runtime->fuzz.enabled = 1;
388 0 : runtime->fuzz.reclaim_accounts = reclaim_accounts;
389 :
390 0 : fd_runtime_prepare_and_execute_txn( runtime, runner->bank, txn_in, txn_out );
391 0 : *exec_res = txn_out->err.txn_err;
392 0 : }
393 :
394 : ulong
395 : fd_solfuzz_pb_txn_run( fd_solfuzz_runner_t * runner,
396 : void const * input_,
397 : void ** output_,
398 : void * output_buf,
399 0 : ulong output_bufsz ) {
400 0 : fd_exec_test_txn_context_t const * input = fd_type_pun_const( input_ );
401 0 : fd_exec_test_txn_result_t ** output = fd_type_pun( output_ );
402 :
403 0 : FD_SPAD_FRAME_BEGIN( runner->spad ) {
404 :
405 : /* Setup the transaction context */
406 0 : fd_txn_p_t * txn = fd_solfuzz_pb_txn_ctx_create( runner, input );
407 0 : if( FD_UNLIKELY( txn==NULL ) ) {
408 0 : fd_solfuzz_txn_ctx_destroy( runner );
409 0 : return 0UL;
410 0 : }
411 :
412 : /* Execute the transaction against the runtime */
413 0 : int exec_res = 0;
414 0 : fd_runtime_t * runtime = runner->runtime;
415 0 : fd_txn_in_t * txn_in = fd_spad_alloc( runner->spad, alignof(fd_txn_in_t), sizeof(fd_txn_in_t) );
416 0 : fd_txn_out_t * txn_out = fd_spad_alloc( runner->spad, alignof(fd_txn_out_t), sizeof(fd_txn_out_t) );
417 0 : fd_log_collector_t * log = fd_spad_alloc( runner->spad, alignof(fd_log_collector_t), sizeof(fd_log_collector_t) );
418 0 : runtime->log.log_collector = log;
419 0 : txn_in->txn = txn;
420 0 : txn_in->bundle.is_bundle = 0;
421 0 : fd_solfuzz_txn_ctx_exec( runner, runtime, txn_in, &exec_res, txn_out, 0 );
422 :
423 : /* Build result directly into the caller-owned output_buf */
424 0 : fd_exec_test_txn_result_t * txn_result = NULL;
425 0 : ulong result_sz = create_txn_result_protobuf_from_txn(
426 0 : &txn_result,
427 0 : output_buf,
428 0 : output_bufsz,
429 0 : txn_in,
430 0 : txn_out,
431 0 : exec_res
432 0 : );
433 :
434 0 : fd_solfuzz_direct_mapping_handle_cu_exhaustion(
435 0 : runner, txn_out->details.compute_budget.compute_meter, exec_res,
436 0 : txn_result->modified_accounts, txn_result->modified_accounts_count );
437 :
438 0 : txn_out->err.is_committable = 0;
439 0 : fd_runtime_cancel_txn( runner->runtime, NULL, NULL, txn_out );
440 0 : fd_solfuzz_txn_ctx_destroy( runner );
441 :
442 0 : *output = txn_result;
443 0 : return result_sz;
444 0 : } FD_SPAD_FRAME_END;
445 0 : }
|