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