Line data Source code
1 : #include "fd_dump_pb.h"
2 : #include "fd_solfuzz_private.h"
3 : #include "generated/block.pb.h"
4 : #include "generated/instr.pb.h"
5 : #include "generated/txn.pb.h"
6 : #include "generated/vm.pb.h"
7 : #include "../fd_system_ids.h"
8 : #include "../fd_bank.h"
9 : #include "../fd_runtime.h"
10 : #include "../fd_alut.h"
11 : #include "../sysvar/fd_sysvar_epoch_schedule.h"
12 : #include "../program/fd_precompiles.h"
13 : #include "../../../third_party/nanopb/pb_encode.h"
14 : #include "../fd_runtime_stack_tmpl.h"
15 : #include "../../../util/fd_hash32.h"
16 :
17 : #include <stdio.h> /* fopen */
18 : #include <unistd.h> /* access */
19 :
20 :
21 : struct fd_dump_account_key {
22 : fd_pubkey_t key;
23 : };
24 : typedef struct fd_dump_account_key fd_dump_account_t;
25 :
26 0 : #define FD_DUMP_ACCOUNT_KEY_MAP_LG_SLOT_CNT (20)
27 :
28 : #define MAP_NAME fd_dump_account_key_map
29 0 : #define MAP_T fd_dump_account_t
30 0 : #define MAP_LG_SLOT_CNT FD_DUMP_ACCOUNT_KEY_MAP_LG_SLOT_CNT
31 0 : #define MAP_KEY_T fd_pubkey_t
32 0 : #define MAP_KEY_NULL fd_solana_system_program_id
33 0 : #define MAP_KEY_INVAL(k) fd_pubkey_eq( &(k), &fd_solana_system_program_id )
34 0 : #define MAP_KEY_EQUAL(k0,k1) fd_pubkey_eq( &(k0), &(k1) )
35 : #define MAP_KEY_EQUAL_IS_SLOW (0)
36 : #define MAP_MEMOIZE (0)
37 0 : #define MAP_KEY_HASH(key) ((uint)fd_hash32( (key).uc, 0UL ))
38 : #include "../../../util/tmpl/fd_map.c"
39 :
40 : struct fd_dump_account_key_set {
41 : fd_dump_account_t * map;
42 : ulong cnt;
43 : uchar system_program_dumped;
44 : };
45 : typedef struct fd_dump_account_key_set fd_dump_account_key_set_t;
46 :
47 : struct fd_dump_account_key_iter {
48 : fd_dump_account_key_set_t const * set;
49 : ulong slot_idx;
50 : uchar system_program;
51 : };
52 : typedef struct fd_dump_account_key_iter fd_dump_account_key_iter_t;
53 :
54 : static void
55 0 : fd_dump_account_key_iter_advance_map( fd_dump_account_key_iter_t * iter ) {
56 0 : while( iter->slot_idx<fd_dump_account_key_map_slot_cnt() &&
57 0 : fd_dump_account_key_map_key_inval( iter->set->map[iter->slot_idx].key ) ) {
58 0 : iter->slot_idx++;
59 0 : }
60 0 : }
61 :
62 : static fd_dump_account_key_iter_t *
63 : fd_dump_account_key_iter_init( fd_dump_account_key_iter_t * iter,
64 0 : fd_dump_account_key_set_t const * set ) {
65 0 : iter->set = set;
66 0 : iter->slot_idx = 0UL;
67 0 : iter->system_program = set->system_program_dumped;
68 0 : if( !iter->system_program ) fd_dump_account_key_iter_advance_map( iter );
69 0 : return iter;
70 0 : }
71 :
72 : static int
73 0 : fd_dump_account_key_iter_done( fd_dump_account_key_iter_t const * iter ) {
74 0 : return (!iter->system_program) & (iter->slot_idx>=fd_dump_account_key_map_slot_cnt());
75 0 : }
76 :
77 : static fd_pubkey_t const *
78 0 : fd_dump_account_key_iter_ele( fd_dump_account_key_iter_t const * iter ) {
79 0 : return iter->system_program ? &fd_solana_system_program_id : &iter->set->map[iter->slot_idx].key;
80 0 : }
81 :
82 : static void
83 0 : fd_dump_account_key_iter_next( fd_dump_account_key_iter_t * iter ) {
84 0 : if( iter->system_program ) {
85 0 : iter->system_program = 0;
86 0 : } else {
87 0 : iter->slot_idx++;
88 0 : }
89 0 : fd_dump_account_key_iter_advance_map( iter );
90 0 : }
91 :
92 : /***** CONSTANTS *****/
93 : static fd_pubkey_t const * fd_dump_sysvar_ids[] = {
94 : &fd_sysvar_recent_block_hashes_id,
95 : &fd_sysvar_clock_id,
96 : &fd_sysvar_slot_history_id,
97 : &fd_sysvar_slot_hashes_id,
98 : &fd_sysvar_epoch_schedule_id,
99 : &fd_sysvar_epoch_rewards_id,
100 : &fd_sysvar_fees_id,
101 : &fd_sysvar_rent_id,
102 : &fd_sysvar_stake_history_id,
103 : &fd_sysvar_last_restart_slot_id,
104 : &fd_sysvar_instructions_id,
105 : };
106 : static ulong const num_sysvar_entries = (sizeof(fd_dump_sysvar_ids) / sizeof(fd_pubkey_t *));
107 :
108 : static fd_pubkey_t const * fd_dump_builtin_ids[] = {
109 : &fd_solana_system_program_id,
110 : &fd_solana_vote_program_id,
111 : &fd_solana_stake_program_id,
112 : &fd_solana_bpf_loader_v4_program_id,
113 : &fd_solana_bpf_loader_deprecated_program_id,
114 : &fd_solana_bpf_loader_program_id,
115 : &fd_solana_bpf_loader_upgradeable_program_id,
116 : &fd_solana_compute_budget_program_id,
117 : &fd_solana_keccak_secp_256k_program_id,
118 : &fd_solana_secp256r1_program_id,
119 : &fd_solana_zk_elgamal_proof_program_id,
120 : &fd_solana_ed25519_sig_verify_program_id,
121 : };
122 : static ulong const num_loaded_builtins = (sizeof(fd_dump_builtin_ids) / sizeof(fd_pubkey_t *));
123 :
124 : /***** UTILITY FUNCTIONS *****/
125 :
126 : /** FEATURE DUMPING **/
127 : static void
128 : dump_sorted_features( fd_features_t const * features,
129 : fd_exec_test_feature_set_t * output_feature_set,
130 0 : fd_spad_t * spad ) {
131 : /* NOTE: Caller must have a spad frame prepared */
132 0 : uint64_t * unsorted_features = fd_spad_alloc( spad, alignof(uint64_t), FD_FEATURE_ID_CNT * sizeof(uint64_t) );
133 0 : ulong num_features = 0;
134 0 : for( const fd_feature_id_t * current_feature = fd_feature_iter_init(); !fd_feature_iter_done( current_feature ); current_feature = fd_feature_iter_next( current_feature ) ) {
135 0 : if (features->f[current_feature->index] != FD_FEATURE_DISABLED) {
136 0 : unsorted_features[num_features++] = (uint64_t) current_feature->id.ul[0];
137 0 : }
138 0 : }
139 :
140 : // Set feature set in message
141 0 : output_feature_set->features_count = (pb_size_t)num_features;
142 0 : output_feature_set->features = unsorted_features;
143 0 : }
144 :
145 : /** ACCOUNT DUMPING **/
146 : static void
147 : dump_account_state( fd_acc_t const * acc,
148 : fd_exec_test_acct_state_t * output_account,
149 0 : fd_spad_t * spad ) {
150 : // Address
151 0 : fd_memcpy(output_account->address, acc->pubkey, sizeof(fd_pubkey_t));
152 :
153 : // Lamports
154 0 : output_account->lamports = (uint64_t)acc->lamports;
155 :
156 : // Data
157 0 : pb_bytes_array_t * data = fd_spad_alloc( spad, alignof(pb_bytes_array_t), PB_BYTES_ARRAY_T_ALLOCSIZE( acc->data_len ) );
158 0 : data->size = (pb_size_t) acc->data_len;
159 0 : fd_memcpy( data->bytes, acc->data, acc->data_len );
160 0 : output_account->which_data_repr = FD_EXEC_TEST_ACCT_STATE_DATA_TAG;
161 0 : output_account->data_repr.data = data;
162 :
163 : // Executable
164 0 : output_account->executable = (bool)acc->executable;
165 :
166 : // Owner
167 0 : fd_memcpy(output_account->owner, acc->owner, sizeof(fd_pubkey_t));
168 0 : }
169 :
170 : static uchar
171 : account_already_dumped( fd_exec_test_acct_state_t const * dumped_accounts,
172 : ulong dumped_cnt,
173 0 : fd_pubkey_t const * account_key ) {
174 0 : for( ulong i=0UL; i<dumped_cnt; i++ ) {
175 0 : if( !memcmp( account_key, dumped_accounts[i].address, sizeof(fd_pubkey_t) ) ) {
176 0 : return 1;
177 0 : }
178 0 : }
179 0 : return 0;
180 0 : }
181 :
182 : /* Dumps a borrowed account if it exists and has not been dumped yet.
183 : Sets up the output borrowed account if it exists. Returns 0 if the
184 : account exists, 1 otherwise.
185 : TODO: This can be optimized by using a set. */
186 : static uchar
187 : dump_account_if_not_already_dumped( fd_accdb_t * accdb,
188 : fd_accdb_fork_id_t fork_id,
189 : fd_pubkey_t const * account_key,
190 : fd_spad_t * spad,
191 : fd_exec_test_acct_state_t * out_acct_states,
192 : pb_size_t * out_acct_states_cnt,
193 0 : fd_acc_t * out_ro ) {
194 0 : fd_acc_t ro = fd_accdb_read_one( accdb, fork_id, account_key->uc );
195 0 : if( FD_UNLIKELY( !ro.lamports ) ) {
196 0 : fd_accdb_unread_one( accdb, &ro );
197 0 : return 1;
198 0 : }
199 :
200 0 : if( !account_already_dumped( out_acct_states, *out_acct_states_cnt, account_key ) ) {
201 0 : dump_account_state( &ro, &out_acct_states[*out_acct_states_cnt], spad );
202 0 : (*out_acct_states_cnt)++;
203 0 : }
204 :
205 0 : if( out_ro ) {
206 0 : *out_ro = ro;
207 0 : } else {
208 0 : fd_accdb_unread_one( accdb, &ro );
209 0 : }
210 0 : return 0;
211 0 : }
212 :
213 : static void
214 : dump_executable_account_if_exists( fd_accdb_t * accdb,
215 : fd_accdb_fork_id_t fork_id,
216 : fd_exec_test_acct_state_t const * program_account,
217 : fd_spad_t * spad,
218 : fd_exec_test_acct_state_t * out_account_states,
219 0 : pb_size_t * out_account_states_count ) {
220 0 : if( FD_LIKELY( memcmp( program_account->owner, fd_solana_bpf_loader_upgradeable_program_id.key, sizeof(fd_pubkey_t) ) ) ) {
221 0 : return;
222 0 : }
223 :
224 0 : pb_bytes_array_t const * program_data = fd_solfuzz_acct_data( program_account );
225 0 : if( FD_UNLIKELY( !program_data ) ) {
226 0 : return;
227 0 : }
228 :
229 0 : fd_bpf_state_t program_loader_state[1];
230 0 : if( FD_UNLIKELY( fd_bpf_state_decode(
231 0 : program_loader_state,
232 0 : program_data->bytes,
233 0 : program_data->size ) ) ) {
234 0 : return;
235 0 : }
236 0 : if( program_loader_state->discriminant!=FD_BPF_STATE_PROGRAM ) {
237 0 : return;
238 0 : }
239 :
240 0 : fd_pubkey_t * programdata_acc = &program_loader_state->inner.program.programdata_address;
241 0 : dump_account_if_not_already_dumped( accdb, fork_id, programdata_acc, spad, out_account_states, out_account_states_count, NULL );
242 0 : }
243 :
244 : static void
245 : dump_sanitized_transaction( fd_accdb_t * accdb,
246 : fd_accdb_fork_id_t fork_id,
247 : fd_txn_t const * txn_descriptor,
248 : uchar const * txn_payload,
249 : fd_spad_t * spad,
250 0 : fd_exec_test_sanitized_transaction_t * sanitized_transaction ) {
251 0 : fd_txn_acct_addr_lut_t const * address_lookup_tables = fd_txn_get_address_tables_const( txn_descriptor );
252 :
253 : /* Transaction Context -> tx -> message */
254 0 : sanitized_transaction->has_message = true;
255 0 : fd_exec_test_transaction_message_t * message = &sanitized_transaction->message;
256 :
257 : /* Transaction Context -> tx -> message -> version / v1_config */
258 0 : int is_v1 = txn_descriptor->transaction_version==FD_TXN_V1;
259 0 : int is_legacy = txn_descriptor->transaction_version==FD_TXN_VLEGACY;
260 0 : message->version = is_v1 ? FD_EXEC_TEST_TRANSACTION_VERSION_TRANSACTION_VERSION_V1
261 0 : : is_legacy ? FD_EXEC_TEST_TRANSACTION_VERSION_TRANSACTION_VERSION_LEGACY
262 0 : : FD_EXEC_TEST_TRANSACTION_VERSION_TRANSACTION_VERSION_V0;
263 0 : message->has_v1_config = (bool)is_v1;
264 0 : if( is_v1 ) {
265 0 : uint mask = fd_uint_load_4( txn_payload+4UL );
266 0 : uchar const * v = txn_payload + txn_descriptor->v1_txn_config_values_off;
267 0 : fd_exec_test_transaction_config_t * cfg = &message->v1_config;
268 0 : if( mask & 0x01U ) { cfg->has_priority_fee = 1; cfg->priority_fee = FD_LOAD( ulong, v ); v += 8UL; }
269 0 : if( mask & 0x04U ) { cfg->has_compute_unit_limit = 1; cfg->compute_unit_limit = FD_LOAD( uint, v ); v += 4UL; }
270 0 : if( mask & 0x08U ) { cfg->has_loaded_accounts_data_size_limit = 1; cfg->loaded_accounts_data_size_limit = FD_LOAD( uint, v ); v += 4UL; }
271 0 : if( mask & 0x10U ) { cfg->has_heap_size = 1; cfg->heap_size = FD_LOAD( uint, v ); }
272 0 : }
273 :
274 : /* Transaction Context -> tx -> message -> header */
275 0 : message->has_header = true;
276 0 : fd_exec_test_message_header_t * header = &message->header;
277 :
278 : /* Transaction Context -> tx -> message -> header -> num_required_signatures */
279 0 : header->num_required_signatures = txn_descriptor->signature_cnt;
280 :
281 : /* Transaction Context -> tx -> message -> header -> num_readonly_signed_accounts */
282 0 : header->num_readonly_signed_accounts = txn_descriptor->readonly_signed_cnt;
283 :
284 : /* Transaction Context -> tx -> message -> header -> num_readonly_unsigned_accounts */
285 0 : header->num_readonly_unsigned_accounts = txn_descriptor->readonly_unsigned_cnt;
286 :
287 : /* Transaction Context -> tx -> message -> account_keys */
288 0 : message->account_keys_count = txn_descriptor->acct_addr_cnt;
289 0 : message->account_keys = fd_spad_alloc( spad, alignof(pb_bytes_array_t *), PB_BYTES_ARRAY_T_ALLOCSIZE(txn_descriptor->acct_addr_cnt * sizeof(pb_bytes_array_t *)) );
290 0 : fd_acct_addr_t const * account_keys = fd_txn_get_acct_addrs( txn_descriptor, txn_payload );
291 0 : for( ulong i = 0; i < txn_descriptor->acct_addr_cnt; i++ ) {
292 0 : pb_bytes_array_t * account_key = fd_spad_alloc( spad, alignof(pb_bytes_array_t), PB_BYTES_ARRAY_T_ALLOCSIZE(sizeof(fd_pubkey_t)) );
293 0 : account_key->size = sizeof(fd_pubkey_t);
294 0 : memcpy( account_key->bytes, &account_keys[i], sizeof(fd_pubkey_t) );
295 0 : message->account_keys[i] = account_key;
296 0 : }
297 :
298 : /* Transaction Context -> tx -> message -> recent_blockhash */
299 0 : uchar const * recent_blockhash = fd_txn_get_recent_blockhash( txn_descriptor, txn_payload );
300 0 : memcpy( message->recent_blockhash, recent_blockhash, sizeof(fd_hash_t) );
301 :
302 : /* Transaction Context -> tx -> message -> instructions */
303 0 : message->instructions_count = txn_descriptor->instr_cnt;
304 0 : message->instructions = fd_spad_alloc( spad, alignof(fd_exec_test_compiled_instruction_t), txn_descriptor->instr_cnt * sizeof(fd_exec_test_compiled_instruction_t) );
305 0 : for( ulong i = 0; i < txn_descriptor->instr_cnt; ++i ) {
306 0 : fd_txn_instr_t instr = txn_descriptor->instr[i];
307 0 : fd_exec_test_compiled_instruction_t * compiled_instruction = &message->instructions[i];
308 :
309 : // compiled instruction -> program_id_index
310 0 : compiled_instruction->program_id_index = instr.program_id;
311 :
312 : // compiled instruction -> accounts
313 0 : compiled_instruction->accounts_count = instr.acct_cnt;
314 0 : compiled_instruction->accounts = fd_spad_alloc( spad, alignof(uint32_t), instr.acct_cnt * sizeof(uint32_t) );
315 0 : uchar const * instr_accounts = fd_txn_get_instr_accts( &instr, txn_payload );
316 0 : for( ulong j = 0; j < instr.acct_cnt; ++j ) {
317 0 : uchar instr_acct_index = instr_accounts[j];
318 0 : compiled_instruction->accounts[j] = instr_acct_index;
319 0 : }
320 :
321 : // compiled instruction -> data
322 0 : uchar const * instr_data = fd_txn_get_instr_data( &instr, txn_payload );
323 0 : compiled_instruction->data = fd_spad_alloc( spad, alignof(pb_bytes_array_t), PB_BYTES_ARRAY_T_ALLOCSIZE(instr.data_sz) );
324 0 : compiled_instruction->data->size = instr.data_sz;
325 0 : memcpy( compiled_instruction->data->bytes, instr_data, instr.data_sz );
326 0 : }
327 :
328 : /* ALUT stuff (V0 only: legacy and V1 have none) */
329 0 : message->address_table_lookups_count = 0;
330 0 : if( !is_legacy && !is_v1 ) {
331 : /* Transaction Context -> tx -> message -> address_table_lookups */
332 0 : message->address_table_lookups_count = txn_descriptor->addr_table_lookup_cnt;
333 0 : message->address_table_lookups = fd_spad_alloc( spad,
334 0 : alignof(fd_exec_test_message_address_table_lookup_t),
335 0 : txn_descriptor->addr_table_lookup_cnt * sizeof(fd_exec_test_message_address_table_lookup_t) );
336 0 : for( ulong i = 0; i < txn_descriptor->addr_table_lookup_cnt; ++i ) {
337 : // alut -> account_key
338 0 : fd_pubkey_t * alut_key = (fd_pubkey_t *) (txn_payload + address_lookup_tables[i].addr_off);
339 0 : memcpy( message->address_table_lookups[i].account_key, alut_key, sizeof(fd_pubkey_t) );
340 :
341 : // Access ALUT account data to access its keys
342 0 : fd_acc_t acc = fd_accdb_read_one( accdb, fork_id, alut_key->uc );
343 0 : if( FD_UNLIKELY( !acc.lamports ) ) FD_LOG_ERR(( "addr lut not found" ));
344 :
345 : // alut -> writable_indexes
346 0 : message->address_table_lookups[i].writable_indexes_count = address_lookup_tables[i].writable_cnt;
347 0 : message->address_table_lookups[i].writable_indexes = fd_spad_alloc( spad, alignof(uint32_t), address_lookup_tables[i].writable_cnt * sizeof(uint32_t) );
348 0 : uchar * writable_indexes = (uchar *) (txn_payload + address_lookup_tables[i].writable_off);
349 0 : for( ulong j = 0; j < address_lookup_tables[i].writable_cnt; ++j ) {
350 0 : message->address_table_lookups[i].writable_indexes[j] = writable_indexes[j];
351 0 : }
352 :
353 : // alut -> readonly_indexes
354 0 : message->address_table_lookups[i].readonly_indexes_count = address_lookup_tables[i].readonly_cnt;
355 0 : message->address_table_lookups[i].readonly_indexes = fd_spad_alloc( spad, alignof(uint32_t), address_lookup_tables[i].readonly_cnt * sizeof(uint32_t) );
356 0 : uchar * readonly_indexes = (uchar *) (txn_payload + address_lookup_tables[i].readonly_off);
357 0 : for( ulong j = 0; j < address_lookup_tables[i].readonly_cnt; ++j ) {
358 0 : message->address_table_lookups[i].readonly_indexes[j] = readonly_indexes[j];
359 0 : }
360 :
361 0 : fd_accdb_unread_one( accdb, &acc );
362 0 : }
363 0 : }
364 :
365 : /* Transaction Context -> tx -> message_hash */
366 : // Skip because it does not matter what's in here
367 :
368 : /* Transaction Context -> tx -> signatures */
369 0 : sanitized_transaction->signatures_count = txn_descriptor->signature_cnt;
370 0 : sanitized_transaction->signatures = fd_spad_alloc( spad, alignof(pb_bytes_array_t *), PB_BYTES_ARRAY_T_ALLOCSIZE(txn_descriptor->signature_cnt * sizeof(pb_bytes_array_t *)) );
371 0 : fd_ed25519_sig_t const * signatures = fd_txn_get_signatures( txn_descriptor, txn_payload );
372 0 : for( uchar i = 0; i < txn_descriptor->signature_cnt; ++i ) {
373 0 : pb_bytes_array_t * signature = fd_spad_alloc( spad, alignof(pb_bytes_array_t), PB_BYTES_ARRAY_T_ALLOCSIZE(sizeof(fd_ed25519_sig_t)) );
374 0 : signature->size = sizeof(fd_ed25519_sig_t);
375 0 : memcpy( signature->bytes, &signatures[i], sizeof(fd_ed25519_sig_t) );
376 0 : sanitized_transaction->signatures[i] = signature;
377 0 : }
378 0 : }
379 :
380 : static void
381 : dump_fee_rate_governor( fd_bank_t * bank,
382 0 : fd_exec_test_fee_rate_governor_t * out ) {
383 0 : fd_fee_rate_governor_t const * frg = &bank->f.fee_rate_governor;
384 0 : *out = (fd_exec_test_fee_rate_governor_t){
385 0 : .target_lamports_per_signature = frg->target_lamports_per_signature,
386 0 : .target_signatures_per_slot = frg->target_signatures_per_slot,
387 0 : .min_lamports_per_signature = frg->min_lamports_per_signature,
388 0 : .max_lamports_per_signature = frg->max_lamports_per_signature,
389 0 : .burn_percent = frg->burn_percent,
390 0 : };
391 0 : }
392 :
393 : static void
394 : dump_blockhash_queue( fd_bank_t * bank,
395 : fd_spad_t * spad,
396 : fd_exec_test_blockhash_queue_entry_t ** entries_out,
397 0 : pb_size_t * count_out ) {
398 0 : fd_blockhashes_t const * bhq = &bank->f.block_hash_queue;
399 0 : ulong total = fd_blockhash_deq_cnt( bhq->d.deque );
400 :
401 : /* Dump the entries with age at most FD_BLOCKHASHES_MAX-1, oldest
402 : first. The deque is in ascending hash_index order, so they are a
403 : suffix. Note the protobuf has no hash_index, so a restored queue
404 : gets consecutive indices: skipped indices (see fd_blockhashes.h) do
405 : not survive the round trip. */
406 :
407 0 : ulong to_skip = 0UL;
408 0 : while( to_skip<total &&
409 0 : fd_blockhashes_age( bhq, fd_blockhash_deq_peek_index_const( bhq->d.deque, to_skip ) )>FD_BLOCKHASHES_MAX-1UL ) to_skip++;
410 0 : ulong live = total - to_skip;
411 :
412 0 : fd_exec_test_blockhash_queue_entry_t * entries = fd_spad_alloc( spad,
413 0 : alignof(fd_exec_test_blockhash_queue_entry_t),
414 0 : live * sizeof(fd_exec_test_blockhash_queue_entry_t) );
415 :
416 0 : for( ulong i=0UL; i<live; i++ ) {
417 0 : fd_blockhash_info_t const * ele = fd_blockhash_deq_peek_index_const( bhq->d.deque, to_skip+i );
418 0 : fd_exec_test_blockhash_queue_entry_t * entry = &entries[ i ];
419 0 : fd_memcpy( entry->blockhash, ele->hash.uc, sizeof(fd_hash_t) );
420 0 : entry->lamports_per_signature = ele->lamports_per_signature;
421 0 : }
422 :
423 0 : *entries_out = entries;
424 0 : *count_out = (pb_size_t)live;
425 0 : }
426 :
427 : static void
428 : dump_txn_bank( fd_bank_t * bank,
429 : fd_spad_t * spad,
430 0 : fd_exec_test_txn_context_t * txn_context ) {
431 0 : txn_context->has_bank = true;
432 0 : fd_exec_test_txn_bank_t * txn_bank = &txn_context->bank;
433 :
434 : /* TxnBank -> blockhash_queue */
435 0 : dump_blockhash_queue( bank, spad, &txn_bank->blockhash_queue, &txn_bank->blockhash_queue_count );
436 :
437 : /* TxnBank -> rbh_lamports_per_signature */
438 0 : txn_bank->rbh_lamports_per_signature = (uint)bank->f.rbh_lamports_per_sig;
439 :
440 : /* TxnBank -> fee_rate_governor */
441 0 : txn_bank->has_fee_rate_governor = true;
442 0 : dump_fee_rate_governor( bank, &txn_bank->fee_rate_governor );
443 :
444 : /* TxnBank -> total_epoch_stake */
445 0 : txn_bank->total_epoch_stake = bank->f.total_epoch_stake;
446 :
447 : /* TxnBank -> features */
448 0 : txn_bank->has_features = true;
449 0 : dump_sorted_features( &bank->f.features, &txn_bank->features, spad );
450 0 : }
451 :
452 : /** SECONDARY FUNCTIONS **/
453 :
454 : /* add_account_to_dumped_accounts adds an account to the dumped accounts
455 : set if it does not exist already. Returns 0 if the account already
456 : exists, and 1 if the account was added successfully.
457 :
458 : TODO: Txn dumping should be optimized to use these functions. */
459 : static uchar
460 : add_account_to_dumped_accounts( fd_dump_account_key_set_t * dumped_accounts,
461 0 : fd_pubkey_t const * pubkey ) {
462 0 : if( fd_pubkey_eq( pubkey, &fd_solana_system_program_id ) ) {
463 0 : if( dumped_accounts->system_program_dumped ) return 0;
464 0 : dumped_accounts->system_program_dumped = 1;
465 0 : dumped_accounts->cnt++;
466 0 : return 1;
467 0 : }
468 :
469 : /* If the key already exists, return early. */
470 0 : if( fd_dump_account_key_map_query( dumped_accounts->map, *pubkey, NULL ) ) {
471 0 : return 0;
472 0 : }
473 :
474 0 : if( FD_UNLIKELY( dumped_accounts->cnt-dumped_accounts->system_program_dumped>=fd_dump_account_key_map_key_max() ) ) {
475 0 : FD_LOG_CRIT(( "too many dumped accounts for fd_dump_account_key_map" ));
476 0 : }
477 0 : fd_dump_account_t * new_ele = fd_dump_account_key_map_insert( dumped_accounts->map, *pubkey );
478 0 : if( FD_UNLIKELY( !new_ele ) ) FD_LOG_CRIT(( "fd_dump_account_key_map_insert failed" ));
479 0 : dumped_accounts->cnt++;
480 0 : return 1;
481 0 : }
482 :
483 : /* add_account_and_programdata_to_dumped_accounts adds an account and
484 : its programdata account (if the account is a v3 program) to the
485 : dumped accounts set if they do not exist already. */
486 : static void
487 : add_account_and_programdata_to_dumped_accounts( fd_accdb_t * accdb,
488 : fd_accdb_fork_id_t fork_id,
489 : fd_dump_account_key_set_t * dumped_accounts,
490 0 : fd_pubkey_t const * pubkey ) {
491 : /* Add the current account to the dumped accounts set. We can save
492 : some time by enforcing an invariant that "if current account was
493 : dumped, then programdata account was also dumped," so we save
494 : ourselves a call to accdb. */
495 0 : uchar ret = add_account_to_dumped_accounts( dumped_accounts, pubkey );
496 0 : if( ret==0 ) return;
497 :
498 : /* Read the account from accdb to see if its a program account and if
499 : it needs to be dumped. */
500 0 : fd_acc_t program_account = fd_accdb_read_one( accdb, fork_id, pubkey->uc );
501 0 : if( FD_UNLIKELY( !program_account.lamports ) ) {
502 0 : fd_accdb_unread_one( accdb, &program_account );
503 0 : return;
504 0 : }
505 :
506 : /* Return if its not owned by the v3 loader */
507 0 : if( FD_LIKELY( !fd_pubkey_eq( (fd_pubkey_t*)program_account.owner, &fd_solana_bpf_loader_upgradeable_program_id ) ) ) {
508 0 : fd_accdb_unread_one( accdb, &program_account );
509 0 : return;
510 0 : }
511 :
512 : /* Get the program account state */
513 0 : fd_bpf_state_t program_account_state[1];
514 0 : if( FD_UNLIKELY( fd_bpf_state_decode(
515 0 : program_account_state,
516 0 : program_account.data,
517 0 : program_account.data_len ) ) ) {
518 0 : fd_accdb_unread_one( accdb, &program_account );
519 0 : return;
520 0 : }
521 0 : if( program_account_state->discriminant!=FD_BPF_STATE_PROGRAM ) {
522 0 : fd_accdb_unread_one( accdb, &program_account );
523 0 : return;
524 0 : }
525 :
526 : /* Dump the programdata address */
527 0 : add_account_to_dumped_accounts( dumped_accounts, &program_account_state->inner.program.programdata_address );
528 0 : fd_accdb_unread_one( accdb, &program_account );
529 0 : }
530 :
531 : /* add_lut_account_to_dumped_accounts adds an address lookup table
532 : account AND all pubkeys in the lookup table to the dumped accounts
533 : set if they do not exist already. */
534 : static void
535 : add_lut_accounts_to_dumped_accounts( fd_accdb_t * accdb,
536 : fd_accdb_fork_id_t fork_id,
537 : fd_dump_account_key_set_t * dumped_accounts,
538 0 : fd_pubkey_t const * pubkey ) {
539 : /* Add the current account to the dumped accounts set. */
540 0 : add_account_to_dumped_accounts( dumped_accounts, pubkey );
541 :
542 : /* Read the account and dump all pubkeys within the lookup table. */
543 0 : fd_acc_t lut_account = fd_accdb_read_one( accdb, fork_id, pubkey->uc );
544 0 : if( FD_UNLIKELY( !lut_account.lamports ) ) {
545 0 : fd_accdb_unread_one( accdb, &lut_account );
546 0 : return;
547 0 : }
548 :
549 : /* Decode the ALUT account and dump all pubkeys within the lookup
550 : table. */
551 0 : if( lut_account.data_len<FD_LOOKUP_TABLE_META_SIZE || (lut_account.data_len&0x1fUL) ) {
552 0 : fd_accdb_unread_one( accdb, &lut_account );
553 0 : return;
554 0 : }
555 :
556 : /* Copy the addresses out and release before nested acquires. */
557 0 : fd_pubkey_t lookup_addrs[ 256 ]; /* LOOKUP_TABLE_MAX_ADDRESSES */
558 0 : ulong lookup_addrs_cnt = fd_ulong_min( (lut_account.data_len-FD_LOOKUP_TABLE_META_SIZE)>>5UL, 256UL );
559 0 : fd_memcpy( lookup_addrs, lut_account.data+FD_LOOKUP_TABLE_META_SIZE, lookup_addrs_cnt*sizeof(fd_pubkey_t) );
560 0 : fd_accdb_unread_one( accdb, &lut_account );
561 :
562 0 : for( ulong i=0UL; i<lookup_addrs_cnt; i++ ) {
563 0 : fd_pubkey_t const * referenced_pubkey = &lookup_addrs[i];
564 0 : add_account_and_programdata_to_dumped_accounts( accdb, fork_id, dumped_accounts, referenced_pubkey );
565 0 : }
566 0 : }
567 :
568 : static void
569 : create_block_context_protobuf_from_block( fd_block_dump_ctx_t * dump_ctx,
570 : fd_banks_t * banks,
571 : fd_bank_t * bank,
572 : fd_accdb_t * accdb,
573 0 : fd_runtime_stack_t * runtime_stack ) {
574 : /* We should use the bank fields from the parent slot in order to
575 : capture the block context from before the current block was
576 : executed, since dumping is happening in the block finalize step. */
577 0 : fd_bank_t * parent_bank = fd_banks_get_parent( banks, bank );
578 0 : fd_exec_test_block_context_t * block_context = &dump_ctx->block_context;
579 0 : ulong dump_txn_count = dump_ctx->txns_to_dump_cnt;
580 0 : fd_spad_t * spad = dump_ctx->spad;
581 :
582 : /* Get vote and stake delegation infos */
583 0 : fd_vote_stakes_t * vote_stakes = fd_bank_vote_stakes( parent_bank );
584 0 : ulong fork_id = parent_bank->vote_stakes_fork_id;
585 0 : ulong vote_account_t_1_cnt = fd_vote_stakes_cnt_t_1( vote_stakes, fork_id );
586 0 : ulong vote_account_t_2_cnt = fd_vote_stakes_cnt_t_2( vote_stakes, fork_id );
587 0 : uchar vote_stakes_iter_mem[ FD_VOTE_STAKES_ITER_FOOTPRINT ] __attribute__((aligned(FD_VOTE_STAKES_ITER_ALIGN)));
588 :
589 0 : fd_stake_history_t stake_delegations_history_[1];
590 0 : fd_stake_history_t * stake_delegations_history = fd_sysvar_cache_stake_history_view( &parent_bank->f.sysvar_cache, stake_delegations_history_ );
591 :
592 0 : fd_stake_delegations_t * stake_delegations = fd_bank_stake_delegations_modify( parent_bank );
593 0 : fd_stake_delegations_view_begin( stake_delegations,
594 0 : parent_bank->f.epoch,
595 0 : stake_delegations_history,
596 0 : &parent_bank->f.warmup_cooldown_rate_epoch,
597 0 : FD_FEATURE_ACTIVE_BANK( parent_bank, upgrade_bpf_stake_program_to_v5_1 ),
598 0 : parent_bank->stake_delegations_fork_id );
599 :
600 : /* Collect account states in a temporary set before iterating over
601 : them and dumping them out. */
602 0 : void * dumped_accounts_mem = fd_spad_alloc( spad, fd_dump_account_key_map_align(), fd_dump_account_key_map_footprint() );
603 0 : fd_dump_account_t * dumped_accounts_map = fd_dump_account_key_map_join( fd_dump_account_key_map_new( dumped_accounts_mem ) );
604 0 : fd_dump_account_key_set_t dumped_accounts[1] = {{ dumped_accounts_map, 0UL, 0 }};
605 :
606 : /* BlockContext -> txns */
607 0 : block_context->txns_count = (pb_size_t)dump_txn_count;
608 0 : block_context->txns = fd_spad_alloc( spad, alignof(fd_exec_test_sanitized_transaction_t), dump_ctx->txns_to_dump_cnt * sizeof(fd_exec_test_sanitized_transaction_t) );
609 0 : fd_memset( block_context->txns, 0, dump_ctx->txns_to_dump_cnt * sizeof(fd_exec_test_sanitized_transaction_t) );
610 :
611 : /* Dump sanitized transactions from the transaction descriptors */
612 0 : for( ulong i=0UL; i<dump_ctx->txns_to_dump_cnt; i++ ) {
613 0 : fd_txn_p_t const * txn_ptr = &dump_ctx->txns_to_dump[i];
614 0 : fd_txn_t const * txn_descriptor = TXN( txn_ptr );
615 0 : dump_sanitized_transaction( accdb, parent_bank->accdb_fork_id, txn_descriptor, txn_ptr->payload, spad, &block_context->txns[i] );
616 :
617 : /* Dump account + alut + programdata accounts (if applicable).
618 : 1. Dump the raw txn account keys
619 : 2. Dump the ALUT accounts
620 : 3. Dump all referenced accounts in the ALUTs
621 : 4. Dump any executable accounts */
622 :
623 : // 1 + 4. Dump any account keys that are referenced by transactions
624 : // + any programdata accounts (if applicable).
625 0 : fd_acct_addr_t const * account_keys = fd_txn_get_acct_addrs( txn_descriptor, txn_ptr->payload );
626 0 : for( ushort l=0; l<txn_descriptor->acct_addr_cnt; l++ ) {
627 0 : fd_pubkey_t const * account_key = fd_type_pun_const( &account_keys[l] );
628 0 : add_account_and_programdata_to_dumped_accounts( accdb, parent_bank->accdb_fork_id, dumped_accounts, account_key );
629 0 : }
630 :
631 : // 2 + 3 + 4. Dump any ALUT accounts + any accounts referenced in
632 : // the ALUTs + any programdata accounts (if applicable).
633 0 : fd_txn_acct_addr_lut_t const * txn_lookup_tables = fd_txn_get_address_tables_const( txn_descriptor );
634 0 : for( ushort l=0; l<txn_descriptor->addr_table_lookup_cnt; l++ ) {
635 0 : fd_txn_acct_addr_lut_t const * lookup_table = &txn_lookup_tables[l];
636 0 : fd_pubkey_t const * lut_key = fd_type_pun_const( txn_ptr->payload+lookup_table->addr_off );
637 0 : add_lut_accounts_to_dumped_accounts( accdb, parent_bank->accdb_fork_id, dumped_accounts, lut_key );
638 0 : }
639 0 : }
640 :
641 : /* Dump sysvars */
642 0 : for( ulong i=0UL; i<num_sysvar_entries; i++ ) {
643 0 : add_account_to_dumped_accounts( dumped_accounts, fd_dump_sysvar_ids[i] );
644 0 : }
645 :
646 : /* Dump builtins */
647 0 : for( ulong i=0UL; i<num_loaded_builtins; i++ ) {
648 0 : add_account_to_dumped_accounts( dumped_accounts, fd_dump_builtin_ids[i] );
649 0 : }
650 :
651 : /* Dump stake accounts for this epoch */
652 0 : fd_stake_delegations_iter_t iter_[1];
653 0 : for( fd_stake_delegations_iter_t * iter = fd_stake_delegations_iter_init( iter_, stake_delegations );
654 0 : !fd_stake_delegations_iter_done( iter );
655 0 : fd_stake_delegations_iter_next( iter ) ) {
656 0 : fd_stake_delegation_t const * stake_delegation = fd_stake_delegations_iter_ele( iter );
657 0 : add_account_to_dumped_accounts( dumped_accounts, &stake_delegation->stake_account );
658 0 : }
659 0 : fd_stake_delegations_view_end( stake_delegations,
660 0 : stake_delegations_history,
661 0 : &parent_bank->f.warmup_cooldown_rate_epoch,
662 0 : FD_FEATURE_ACTIVE_BANK( parent_bank, upgrade_bpf_stake_program_to_v5_1 ) );
663 :
664 : /* BlockBank -> vote_accounts_t_1 and vote_accounts_t_2 */
665 0 : fd_exec_test_prev_vote_account_t * va_t1 = fd_spad_alloc( spad,
666 0 : alignof(fd_exec_test_prev_vote_account_t),
667 0 : vote_account_t_1_cnt * sizeof(fd_exec_test_prev_vote_account_t) );
668 0 : fd_exec_test_prev_vote_account_t * va_t2 = fd_spad_alloc( spad,
669 0 : alignof(fd_exec_test_prev_vote_account_t),
670 0 : vote_account_t_2_cnt * sizeof(fd_exec_test_prev_vote_account_t) );
671 0 : pb_size_t va_t1_cnt = 0U;
672 0 : pb_size_t va_t2_cnt = 0U;
673 :
674 : /* SIMD-0232 collectors: empty protobuf fields represent the vote/node
675 : defaults. */
676 :
677 0 : for( fd_vote_stakes_iter_t * iter = fd_vote_stakes_iter_init( vote_stakes, fork_id, FD_VOTE_STAKES_ITER_T_1, vote_stakes_iter_mem );
678 0 : !fd_vote_stakes_iter_done( vote_stakes, fork_id, FD_VOTE_STAKES_ITER_T_1, iter );
679 0 : fd_vote_stakes_iter_next( vote_stakes, fork_id, FD_VOTE_STAKES_ITER_T_1, iter ) ) {
680 0 : fd_vote_stakes_ele_t ele[1];
681 0 : fd_vote_stakes_iter_ele( vote_stakes, fork_id, FD_VOTE_STAKES_ITER_T_1, iter, ele );
682 0 : add_account_to_dumped_accounts( dumped_accounts, &ele->pubkey );
683 :
684 0 : fd_exec_test_prev_vote_account_t * acc = &va_t1[ va_t1_cnt++ ];
685 0 : fd_memcpy( acc->address, &ele->pubkey, sizeof(fd_pubkey_t) );
686 0 : fd_memcpy( acc->node_pubkey, &ele->node_account, sizeof(fd_pubkey_t) );
687 0 : acc->stake = ele->stake;
688 0 : acc->commission_bps = ele->commission;
689 0 : acc->block_revenue_commission_bps = ele->block_revenue_commission_bps;
690 0 : acc->pending_delegator_rewards = ele->pending_delegator_rewards;
691 0 : acc->version = FD_EXEC_TEST_VOTE_ACCOUNT_VERSION_V4;
692 0 : acc->epoch_credits_count = 0U;
693 0 : acc->inflation_rewards_collector.size = 0U;
694 0 : acc->block_revenue_collector.size = 0U;
695 :
696 0 : if( !fd_pubkey_eq( &ele->inflation_collector, &ele->pubkey ) ) {
697 0 : acc->inflation_rewards_collector.size = 32U;
698 0 : fd_memcpy( acc->inflation_rewards_collector.bytes, &ele->inflation_collector, sizeof(fd_pubkey_t) );
699 0 : }
700 0 : if( !fd_pubkey_eq( &ele->block_collector, &ele->node_account ) ) {
701 0 : acc->block_revenue_collector.size = 32U;
702 0 : fd_memcpy( acc->block_revenue_collector.bytes, &ele->block_collector, sizeof(fd_pubkey_t) );
703 0 : }
704 0 : }
705 :
706 0 : for( fd_vote_stakes_iter_t * iter = fd_vote_stakes_iter_init( vote_stakes, fork_id, FD_VOTE_STAKES_ITER_T_2, vote_stakes_iter_mem );
707 0 : !fd_vote_stakes_iter_done( vote_stakes, fork_id, FD_VOTE_STAKES_ITER_T_2, iter );
708 0 : fd_vote_stakes_iter_next( vote_stakes, fork_id, FD_VOTE_STAKES_ITER_T_2, iter ) ) {
709 0 : fd_vote_stakes_ele_t ele[1];
710 0 : fd_vote_stakes_iter_ele( vote_stakes, fork_id, FD_VOTE_STAKES_ITER_T_2, iter, ele );
711 0 : add_account_to_dumped_accounts( dumped_accounts, &ele->pubkey );
712 :
713 0 : fd_exec_test_prev_vote_account_t * acc = &va_t2[ va_t2_cnt++ ];
714 0 : fd_memcpy( acc->address, &ele->pubkey, sizeof(fd_pubkey_t) );
715 0 : fd_memcpy( acc->node_pubkey, &ele->node_account, sizeof(fd_pubkey_t) );
716 0 : acc->stake = ele->stake;
717 0 : acc->commission_bps = ele->commission;
718 0 : acc->block_revenue_commission_bps = ele->block_revenue_commission_bps;
719 0 : acc->pending_delegator_rewards = ele->pending_delegator_rewards;
720 0 : acc->version = FD_EXEC_TEST_VOTE_ACCOUNT_VERSION_V4;
721 0 : acc->epoch_credits_count = 0U;
722 0 : acc->inflation_rewards_collector.size = 0U;
723 0 : acc->block_revenue_collector.size = 0U;
724 :
725 0 : if( !fd_pubkey_eq( &ele->inflation_collector, &ele->pubkey ) ) {
726 0 : acc->inflation_rewards_collector.size = 32U;
727 0 : fd_memcpy( acc->inflation_rewards_collector.bytes, &ele->inflation_collector, sizeof(fd_pubkey_t) );
728 0 : }
729 0 : if( !fd_pubkey_eq( &ele->block_collector, &ele->node_account ) ) {
730 0 : acc->block_revenue_collector.size = 32U;
731 0 : fd_memcpy( acc->block_revenue_collector.bytes, &ele->block_collector, sizeof(fd_pubkey_t) );
732 0 : }
733 0 : }
734 :
735 : /* Dump epoch_credits from runtime_stack->stakes.vote_ele if the
736 : vote_ele_map has been populated (happens after epoch boundary
737 : reward calculation). Needed for the harness to correctly
738 : recalculate partitioned epoch rewards. */
739 0 : fd_vote_rewards_map_t * vote_ele_map = runtime_stack->stakes.vote_map;
740 0 : ulong ag_migration_slot = fd_alpenglow_migration_slot( parent_bank, accdb );
741 0 : ulong ag_migration_epoch = fd_slot_to_epoch( &parent_bank->f.epoch_schedule, ag_migration_slot, NULL );
742 0 : fd_epoch_credits_view_t epoch_credits_view[1];
743 0 : FD_TEST( fd_epoch_credits_view_init( epoch_credits_view, fd_bank_epoch_credits( parent_bank ), parent_bank->epoch_credits_fork_id ) );
744 0 : for( pb_size_t i=0U; i<va_t1_cnt; i++ ) {
745 0 : fd_pubkey_t va_pubkey = FD_LOAD( fd_pubkey_t, va_t1[i].address );
746 0 : uint idx = (uint)fd_vote_rewards_map_idx_query( vote_ele_map, &va_pubkey, UINT_MAX, runtime_stack->stakes.vote_ele );
747 0 : if( idx==UINT_MAX ) continue;
748 0 : fd_epoch_credits_t const * ec = &epoch_credits_view->credits[idx];
749 0 : ulong base = ec->base_credits;
750 0 : int has_ag_marker = ec->has_ag_migration_marker;
751 0 : ulong ag_marker_idx = fd_epoch_credits_ag_marker_idx( ec, ag_migration_epoch );
752 0 : ulong cnt = (ulong)ec->cnt + (ulong)has_ag_marker;
753 0 : va_t1[i].epoch_credits_count = (pb_size_t)cnt;
754 0 : va_t1[i].epoch_credits = fd_spad_alloc( spad, alignof(fd_exec_test_epoch_credit_t), cnt * sizeof(fd_exec_test_epoch_credit_t) );
755 0 : for( ulong k=0UL, j=0UL; k<cnt; k++ ) {
756 0 : if( FD_UNLIKELY( k==ag_marker_idx ) ) {
757 0 : va_t1[i].epoch_credits[k].epoch = ULONG_MAX;
758 0 : va_t1[i].epoch_credits[k].credits = ULONG_MAX;
759 0 : va_t1[i].epoch_credits[k].prev_credits = ULONG_MAX;
760 0 : continue;
761 0 : }
762 0 : va_t1[i].epoch_credits[k].epoch = ec->epoch[j];
763 0 : va_t1[i].epoch_credits[k].credits = base + ec->credits_delta[j];
764 0 : va_t1[i].epoch_credits[k].prev_credits = base + ec->prev_credits_delta[j];
765 0 : j++;
766 0 : }
767 0 : }
768 0 : fd_epoch_credits_view_fini( epoch_credits_view );
769 :
770 : /* BlockContext -> acct_states
771 : Iterate over the set and dump all the account keys in one pass. */
772 0 : block_context->acct_states_count = 0U;
773 0 : block_context->acct_states = fd_spad_alloc(
774 0 : spad,
775 0 : alignof(fd_exec_test_acct_state_t),
776 0 : dumped_accounts->cnt*sizeof(fd_exec_test_acct_state_t) );
777 0 : fd_dump_account_key_iter_t dumped_accounts_iter_[1];
778 0 : for( fd_dump_account_key_iter_t * iter = fd_dump_account_key_iter_init( dumped_accounts_iter_, dumped_accounts );
779 0 : !fd_dump_account_key_iter_done( iter );
780 0 : fd_dump_account_key_iter_next( iter ) ) {
781 0 : fd_pubkey_t const * pubkey = fd_dump_account_key_iter_ele( iter );
782 0 : fd_acc_t acc = fd_accdb_read_one( accdb, parent_bank->accdb_fork_id, pubkey->uc );
783 0 : if( FD_UNLIKELY( !acc.lamports ) ) {
784 0 : fd_accdb_unread_one( accdb, &acc );
785 0 : continue;
786 0 : }
787 0 : dump_account_state(
788 0 : &acc,
789 0 : &block_context->acct_states[block_context->acct_states_count++],
790 0 : spad );
791 0 : fd_accdb_unread_one( accdb, &acc );
792 0 : }
793 :
794 : /* BlockContext -> bank */
795 0 : block_context->has_bank = true;
796 0 : fd_exec_test_block_bank_t * block_bank = &block_context->bank;
797 :
798 : /* BlockBank -> blockhash_queue */
799 0 : dump_blockhash_queue( parent_bank, spad, &block_bank->blockhash_queue, &block_bank->blockhash_queue_count );
800 :
801 : /* BlockBank -> rbh_lamports_per_signature */
802 0 : block_bank->rbh_lamports_per_signature = (uint)parent_bank->f.rbh_lamports_per_sig;
803 :
804 : /* BlockBank -> fee_rate_governor */
805 0 : block_bank->has_fee_rate_governor = true;
806 0 : dump_fee_rate_governor( parent_bank, &block_bank->fee_rate_governor );
807 :
808 : /* BlockBank -> slot */
809 0 : block_bank->slot = bank->f.slot;
810 :
811 : /* BlockBank -> parent_slot */
812 0 : block_bank->parent_slot = bank->f.parent_slot;
813 :
814 : /* BlockBank -> capitalization */
815 0 : block_bank->capitalization = parent_bank->f.capitalization;
816 :
817 : /* BlockBank -> ns_per_slot */
818 0 : fd_w_u128_t ns_per_slot = { .ud = bank->f.slot_params.ns_per_slot };
819 0 : fd_memcpy( block_bank->ns_per_slot, &ns_per_slot.ud, sizeof(uint128) );
820 :
821 : /* BlockBank -> inflation */
822 0 : block_bank->has_inflation = true;
823 0 : fd_inflation_t const * inflation = &parent_bank->f.inflation;
824 0 : block_bank->inflation = (fd_exec_test_inflation_t){
825 0 : .initial = inflation->initial,
826 0 : .terminal = inflation->terminal,
827 0 : .taper = inflation->taper,
828 0 : .foundation = inflation->foundation,
829 0 : .foundation_term = inflation->foundation_term,
830 0 : };
831 :
832 : /* BlockBank -> block_height */
833 0 : block_bank->block_height = bank->f.block_height;
834 :
835 : /* BlockBank -> poh */
836 0 : fd_memcpy( block_bank->poh, &bank->f.poh, sizeof(fd_hash_t) );
837 :
838 : /* BlockBank -> parent_bank_hash */
839 0 : fd_memcpy( block_bank->parent_bank_hash, &parent_bank->f.bank_hash, sizeof(fd_hash_t) );
840 :
841 : /* BlockBank -> parent_lt_hash */
842 0 : fd_lthash_value_t const * parent_lthash = fd_bank_lthash_locking_query( parent_bank );
843 0 : fd_memcpy( block_bank->parent_lt_hash, parent_lthash, sizeof(fd_lthash_value_t) );
844 0 : fd_bank_lthash_end_locking_query( parent_bank );
845 :
846 : /* BlockBank -> parent_signature_count */
847 0 : block_bank->parent_signature_count = parent_bank->f.parent_signature_cnt;
848 :
849 : /* BlockBank -> features */
850 0 : block_bank->has_features = true;
851 0 : dump_sorted_features( &parent_bank->f.features, &block_bank->features, spad );
852 :
853 : /* BlockBank -> vote_accounts_t_1 / vote_accounts_t_2 */
854 0 : block_bank->vote_accounts_t_1 = va_t1;
855 0 : block_bank->vote_accounts_t_1_count = va_t1_cnt;
856 0 : block_bank->vote_accounts_t_2 = va_t2;
857 0 : block_bank->vote_accounts_t_2_count = va_t2_cnt;
858 0 : }
859 :
860 : static void
861 : create_txn_context_protobuf_from_txn( fd_exec_test_txn_context_t * txn_context_msg,
862 : fd_runtime_t * runtime,
863 : fd_bank_t * bank,
864 : fd_txn_in_t const * txn_in,
865 : fd_txn_out_t * txn_out,
866 0 : fd_spad_t * spad ) {
867 0 : fd_txn_t const * txn_descriptor = TXN( txn_in->txn );
868 0 : uchar const * txn_payload = (uchar const *) txn_in->txn->payload;
869 0 : (void)txn_out;
870 :
871 : /* Transaction Context -> account_shared_data
872 : Contains:
873 : - Account data for regular accounts
874 : - Account data for LUT accounts
875 : - Account data for executable accounts
876 : - Account data for (almost) all sysvars */
877 0 : txn_context_msg->account_shared_data_count = 0;
878 0 : txn_context_msg->account_shared_data = fd_spad_alloc( spad,
879 0 : alignof(fd_exec_test_acct_state_t),
880 0 : (256UL*2UL + txn_descriptor->addr_table_lookup_cnt + num_sysvar_entries) * sizeof(fd_exec_test_acct_state_t) );
881 :
882 : /* Dump regular accounts first */
883 0 : fd_acct_addr_t const * account_keys = fd_txn_get_acct_addrs( txn_descriptor, txn_payload );
884 0 : for( ushort i=0; i<txn_descriptor->acct_addr_cnt; i++ ) {
885 0 : dump_account_if_not_already_dumped(
886 0 : runtime->accdb,
887 0 : bank->accdb_fork_id,
888 0 : fd_type_pun_const( &account_keys[i] ),
889 0 : spad,
890 0 : txn_context_msg->account_shared_data,
891 0 : &txn_context_msg->account_shared_data_count,
892 0 : NULL
893 0 : );
894 0 : }
895 :
896 : // Dump LUT accounts
897 0 : fd_txn_acct_addr_lut_t const * address_lookup_tables = fd_txn_get_address_tables_const( txn_descriptor );
898 0 : for( ulong i = 0; i < txn_descriptor->addr_table_lookup_cnt; ++i ) {
899 0 : fd_txn_acct_addr_lut_t const * addr_lut = &address_lookup_tables[i];
900 0 : fd_pubkey_t * alut_key = (fd_pubkey_t *) (txn_payload + addr_lut->addr_off);
901 :
902 : // Dump the LUT account itself if not already dumped
903 0 : fd_acc_t acc;
904 0 : int ret = dump_account_if_not_already_dumped(
905 0 : runtime->accdb,
906 0 : bank->accdb_fork_id,
907 0 : alut_key,
908 0 : spad,
909 0 : txn_context_msg->account_shared_data,
910 0 : &txn_context_msg->account_shared_data_count,
911 0 : &acc
912 0 : );
913 0 : if( FD_UNLIKELY( ret ) ) continue;
914 :
915 0 : if( FD_UNLIKELY( acc.data_len < FD_LOOKUP_TABLE_META_SIZE ) ) {
916 : /* Skip over invalid address lookup tables */
917 0 : fd_accdb_unread_one( runtime->accdb, &acc );
918 0 : continue;
919 0 : }
920 :
921 : /* Copy the addresses out and release before nested acquires. */
922 0 : fd_pubkey_t lookup_addrs[ 256 ]; /* LOOKUP_TABLE_MAX_ADDRESSES */
923 0 : ulong lookup_addrs_cnt = fd_ulong_min( (acc.data_len - FD_LOOKUP_TABLE_META_SIZE) / sizeof(fd_pubkey_t), 256UL );
924 0 : fd_memcpy( lookup_addrs, acc.data + FD_LOOKUP_TABLE_META_SIZE, lookup_addrs_cnt*sizeof(fd_pubkey_t) );
925 0 : fd_accdb_unread_one( runtime->accdb, &acc );
926 :
927 : /* Dump any account state referenced in ALUTs */
928 0 : uchar const * writable_lut_idxs = txn_payload + addr_lut->writable_off;
929 0 : for( ulong j=0; j<addr_lut->writable_cnt; j++ ) {
930 0 : if( writable_lut_idxs[j] >= lookup_addrs_cnt ) {
931 0 : continue;
932 0 : }
933 0 : fd_pubkey_t const * referenced_addr = lookup_addrs + writable_lut_idxs[j];
934 0 : dump_account_if_not_already_dumped(
935 0 : runtime->accdb,
936 0 : bank->accdb_fork_id,
937 0 : referenced_addr,
938 0 : spad,
939 0 : txn_context_msg->account_shared_data,
940 0 : &txn_context_msg->account_shared_data_count,
941 0 : NULL
942 0 : );
943 0 : }
944 :
945 0 : uchar const * readonly_lut_idxs = txn_payload + addr_lut->readonly_off;
946 0 : for( ulong j = 0; j < addr_lut->readonly_cnt; j++ ) {
947 0 : if( readonly_lut_idxs[j] >= lookup_addrs_cnt ) {
948 0 : continue;
949 0 : }
950 0 : fd_pubkey_t const * referenced_addr = lookup_addrs + readonly_lut_idxs[j];
951 0 : dump_account_if_not_already_dumped(
952 0 : runtime->accdb,
953 0 : bank->accdb_fork_id,
954 0 : referenced_addr,
955 0 : spad,
956 0 : txn_context_msg->account_shared_data,
957 0 : &txn_context_msg->account_shared_data_count,
958 0 : NULL
959 0 : );
960 0 : }
961 0 : }
962 :
963 : /* Dump the programdata accounts for any potential v3-owned program accounts */
964 0 : uint accounts_dumped_so_far = txn_context_msg->account_shared_data_count;
965 0 : for( uint i=0U; i<accounts_dumped_so_far; i++ ) {
966 0 : fd_exec_test_acct_state_t const * maybe_program_account = &txn_context_msg->account_shared_data[i];
967 0 : dump_executable_account_if_exists( runtime->accdb, bank->accdb_fork_id, maybe_program_account, spad, txn_context_msg->account_shared_data, &txn_context_msg->account_shared_data_count );
968 0 : }
969 :
970 : /* Dump sysvars */
971 0 : for( ulong i = 0; i < num_sysvar_entries; i++ ) {
972 0 : dump_account_if_not_already_dumped(
973 0 : runtime->accdb,
974 0 : bank->accdb_fork_id,
975 0 : fd_dump_sysvar_ids[i],
976 0 : spad,
977 0 : txn_context_msg->account_shared_data,
978 0 : &txn_context_msg->account_shared_data_count,
979 0 : NULL
980 0 : );
981 0 : }
982 :
983 : /* Transaction Context -> tx */
984 0 : txn_context_msg->has_tx = true;
985 0 : fd_exec_test_sanitized_transaction_t * sanitized_transaction = &txn_context_msg->tx;
986 0 : dump_sanitized_transaction( runtime->accdb, bank->accdb_fork_id, txn_descriptor, txn_payload, spad, sanitized_transaction );
987 :
988 : /* Transaction Context -> bank */
989 0 : dump_txn_bank( bank, spad, txn_context_msg );
990 0 : }
991 :
992 : static void
993 : create_instr_context_protobuf_from_instructions( fd_exec_test_instr_context_t * instr_context,
994 : fd_runtime_t * runtime,
995 : fd_bank_t * bank,
996 : fd_txn_out_t * txn_out,
997 : fd_instr_info_t const * instr,
998 0 : fd_spad_t * spad ) {
999 : /* Program ID */
1000 0 : fd_memcpy( instr_context->program_id, txn_out->accounts.keys[ instr->program_id ].uc, sizeof(fd_pubkey_t) );
1001 :
1002 : /* Accounts */
1003 0 : instr_context->accounts_count = (pb_size_t) txn_out->accounts.cnt;
1004 0 : instr_context->accounts = fd_spad_alloc( spad, alignof(fd_exec_test_acct_state_t), (instr_context->accounts_count + num_sysvar_entries + txn_out->accounts.executable_cnt) * sizeof(fd_exec_test_acct_state_t));
1005 0 : for( ulong i = 0; i < txn_out->accounts.cnt; i++ ) {
1006 : // Copy account information over
1007 0 : fd_exec_test_acct_state_t * output_account = &instr_context->accounts[i];
1008 0 : dump_account_state( txn_out->accounts.account[ i ], output_account, spad );
1009 0 : }
1010 :
1011 : /* Add sysvar cache variables */
1012 0 : uchar * sysvar_data = fd_spad_alloc( spad, 1UL, FD_RUNTIME_ACC_SZ_MAX );
1013 0 : for( ulong i = 0; i < num_sysvar_entries; i++ ) {
1014 0 : if( account_already_dumped( instr_context->accounts, instr_context->accounts_count, fd_dump_sysvar_ids[i] ) ) continue;
1015 :
1016 0 : fd_acc_t acc = {0};
1017 0 : fd_memcpy( acc.pubkey, fd_dump_sysvar_ids[i]->uc, sizeof(fd_pubkey_t) );
1018 0 : acc.data = sysvar_data;
1019 0 : fd_accdb_read_one_nocache(
1020 0 : runtime->accdb,
1021 0 : bank->accdb_fork_id,
1022 0 : fd_dump_sysvar_ids[i]->uc,
1023 0 : &acc.lamports,
1024 0 : &acc.executable,
1025 0 : acc.owner,
1026 0 : acc.data,
1027 0 : &acc.data_len
1028 0 : );
1029 0 : if( FD_UNLIKELY( !acc.lamports ) ) continue;
1030 :
1031 0 : fd_exec_test_acct_state_t * output_account = &instr_context->accounts[instr_context->accounts_count++];
1032 0 : dump_account_state( &acc, output_account, spad );
1033 0 : }
1034 :
1035 : /* Add executable accounts */
1036 0 : for( ulong i = 0; i < txn_out->accounts.executable_cnt; i++ ) {
1037 : // Make sure the account doesn't exist in the output accounts yet
1038 0 : fd_acc_t const * ro = txn_out->accounts.executable[i];
1039 0 : bool account_exists = false;
1040 0 : for( ulong j = 0; j < instr_context->accounts_count; j++ ) {
1041 0 : if( 0 == memcmp( instr_context->accounts[j].address, ro->pubkey, sizeof(fd_pubkey_t) ) ) {
1042 0 : account_exists = true;
1043 0 : break;
1044 0 : }
1045 0 : }
1046 : // Copy it into output
1047 0 : if( !account_exists ) {
1048 0 : fd_exec_test_acct_state_t * output_account = &instr_context->accounts[instr_context->accounts_count++];
1049 0 : dump_account_state( ro, output_account, spad );
1050 0 : }
1051 0 : }
1052 :
1053 : /* Instruction Accounts */
1054 0 : instr_context->instr_accounts_count = (pb_size_t) instr->acct_cnt;
1055 0 : instr_context->instr_accounts = fd_spad_alloc( spad, alignof(fd_exec_test_instr_acct_t), instr_context->instr_accounts_count * sizeof(fd_exec_test_instr_acct_t) );
1056 0 : for( ushort i = 0; i < instr->acct_cnt; i++ ) {
1057 0 : fd_exec_test_instr_acct_t * output_instr_account = &instr_context->instr_accounts[i];
1058 :
1059 0 : output_instr_account->index = instr->accounts[i].index_in_transaction;
1060 0 : output_instr_account->is_writable = instr->accounts[i].is_writable;
1061 0 : output_instr_account->is_signer = instr->accounts[i].is_signer;
1062 0 : }
1063 :
1064 : /* Data */
1065 0 : instr_context->data = fd_spad_alloc( spad, alignof(pb_bytes_array_t), PB_BYTES_ARRAY_T_ALLOCSIZE( instr->data_sz ) );
1066 0 : instr_context->data->size = (pb_size_t) instr->data_sz;
1067 0 : fd_memcpy( instr_context->data->bytes, instr->data, instr->data_sz );
1068 :
1069 : /* Compute Units */
1070 0 : instr_context->cu_avail = txn_out->details.compute_budget.compute_meter;
1071 :
1072 : /* Feature set */
1073 0 : instr_context->has_features = true;
1074 0 : dump_sorted_features( &bank->f.features, &instr_context->features, spad );
1075 0 : }
1076 :
1077 : /***** PUBLIC APIs *****/
1078 :
1079 : void
1080 : fd_dump_instr_to_protobuf( fd_runtime_t * runtime,
1081 : fd_bank_t * bank,
1082 : fd_txn_in_t const * txn_in,
1083 : fd_txn_out_t * txn_out,
1084 : fd_instr_info_t * instr,
1085 0 : ushort instruction_idx ) {
1086 : /* Check program ID filter, if it exists */
1087 0 : if( runtime->log.dump_proto_ctx->has_dump_instr_program_id_filter &&
1088 0 : memcmp( txn_out->accounts.keys[ instr->program_id ].uc, runtime->log.dump_proto_ctx->dump_instr_program_id_filter, sizeof(fd_pubkey_t) ) ) {
1089 0 : return;
1090 0 : }
1091 :
1092 0 : fd_spad_t * spad = fd_spad_join( fd_spad_new( runtime->log.dumping_mem, 1UL<<28UL ) );
1093 :
1094 0 : FD_SPAD_FRAME_BEGIN( spad ) {
1095 : // Get base58-encoded tx signature
1096 0 : const fd_ed25519_sig_t * signatures = fd_txn_get_signatures( TXN( txn_in->txn ), txn_in->txn->payload );
1097 0 : char encoded_signature[FD_BASE58_ENCODED_64_SZ];
1098 0 : fd_base58_encode_64( signatures[0], NULL, encoded_signature );
1099 :
1100 0 : fd_exec_test_instr_context_t instr_context = FD_EXEC_TEST_INSTR_CONTEXT_INIT_DEFAULT;
1101 0 : create_instr_context_protobuf_from_instructions( &instr_context, runtime, bank, txn_out, instr, spad );
1102 :
1103 : /* Output to file */
1104 0 : ulong out_buf_size = 100 * 1024 * 1024;
1105 0 : uint8_t * out = fd_spad_alloc( spad, alignof(uchar) , out_buf_size );
1106 0 : pb_ostream_t stream = pb_ostream_from_buffer( out, out_buf_size );
1107 0 : if (pb_encode(&stream, FD_EXEC_TEST_INSTR_CONTEXT_FIELDS, &instr_context)) {
1108 0 : char output_filepath[ PATH_MAX ];
1109 0 : snprintf( output_filepath, PATH_MAX, "%s/instr-%s-%hu.instrctx", runtime->log.dump_proto_ctx->dump_proto_output_dir, encoded_signature, instruction_idx );
1110 0 : FILE * file = fopen(output_filepath, "wb");
1111 0 : if( file ) {
1112 0 : fwrite( out, 1, stream.bytes_written, file );
1113 0 : fclose( file );
1114 0 : }
1115 0 : }
1116 0 : } FD_SPAD_FRAME_END;
1117 0 : }
1118 :
1119 : /* Writes a single account state into the resulting_state field of a
1120 : TxnResult protobuf. Sub-allocations for account data are bump-
1121 : allocated from the caller's scratch region via _l. */
1122 : static void
1123 : write_account_to_result( fd_acc_t const * acc,
1124 : fd_exec_test_acct_state_t * out_accounts,
1125 : pb_size_t * out_accounts_cnt,
1126 : ulong * scratch_cur,
1127 0 : ulong scratch_end ) {
1128 0 : fd_exec_test_acct_state_t * out_acct = &out_accounts[ *out_accounts_cnt ];
1129 0 : (*out_accounts_cnt)++;
1130 :
1131 0 : memset( out_acct, 0, sizeof(fd_exec_test_acct_state_t) );
1132 0 : memcpy( out_acct->address, acc->pubkey, sizeof(fd_pubkey_t) );
1133 0 : out_acct->lamports = acc->lamports;
1134 :
1135 0 : fd_solfuzz_acct_set_data_hash( out_acct, acc->data, acc->data_len );
1136 0 : (void)scratch_cur; (void)scratch_end;
1137 :
1138 0 : out_acct->executable = acc->executable;
1139 0 : memcpy( out_acct->owner, acc->owner, sizeof(fd_pubkey_t) );
1140 0 : }
1141 :
1142 : static void
1143 : write_account_to_result1( uchar const * pubkey,
1144 : ulong lamports,
1145 : uchar const * owner,
1146 : ulong data_len,
1147 : uchar const * _data,
1148 : int executable,
1149 : fd_exec_test_acct_state_t * out_accounts,
1150 : pb_size_t * out_accounts_cnt,
1151 : ulong * scratch_cur,
1152 0 : ulong scratch_end ) {
1153 0 : fd_exec_test_acct_state_t * out_acct = &out_accounts[ *out_accounts_cnt ];
1154 0 : (*out_accounts_cnt)++;
1155 :
1156 0 : memset( out_acct, 0, sizeof(fd_exec_test_acct_state_t) );
1157 0 : memcpy( out_acct->address, pubkey, sizeof(fd_pubkey_t) );
1158 0 : out_acct->lamports = lamports;
1159 :
1160 0 : fd_solfuzz_acct_set_data_hash( out_acct, _data, data_len );
1161 0 : (void)scratch_cur; (void)scratch_end;
1162 :
1163 0 : out_acct->executable = executable;
1164 0 : memcpy( out_acct->owner, owner, sizeof(fd_pubkey_t) );
1165 0 : }
1166 :
1167 : ulong
1168 : create_txn_result_protobuf_from_txn( fd_exec_test_txn_result_t ** txn_result_out,
1169 : void * out_buf,
1170 : ulong out_bufsz,
1171 : fd_txn_in_t const * txn_in,
1172 : fd_txn_out_t * txn_out,
1173 0 : int exec_res ) {
1174 0 : FD_SCRATCH_ALLOC_INIT( l, out_buf );
1175 0 : ulong out_end = (ulong)out_buf + out_bufsz;
1176 :
1177 0 : fd_exec_test_txn_result_t * txn_result =
1178 0 : FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_exec_test_txn_result_t),
1179 0 : sizeof(fd_exec_test_txn_result_t) );
1180 0 : if( FD_UNLIKELY( _l > out_end ) ) abort();
1181 0 : fd_memset( txn_result, 0, sizeof(fd_exec_test_txn_result_t) );
1182 :
1183 : /* Map nonce errors into the agave expected ones. */
1184 0 : if( FD_UNLIKELY( exec_res==FD_RUNTIME_TXN_ERR_BLOCKHASH_NONCE_ALREADY_ADVANCED ||
1185 0 : exec_res==FD_RUNTIME_TXN_ERR_BLOCKHASH_FAIL_ADVANCE_NONCE_INSTR ||
1186 0 : exec_res==FD_RUNTIME_TXN_ERR_BLOCKHASH_FAIL_WRONG_NONCE )) {
1187 0 : exec_res = FD_RUNTIME_TXN_ERR_BLOCKHASH_NOT_FOUND;
1188 0 : }
1189 :
1190 : /* Basic result fields */
1191 0 : txn_result->executed = txn_out->err.is_committable;
1192 0 : txn_result->modified_accounts_count = 0;
1193 0 : txn_result->rollback_accounts_count = 0;
1194 0 : txn_result->txn_error = (uint32_t) -exec_res;
1195 0 : txn_result->instruction_error = 0;
1196 0 : txn_result->instruction_error_index = 0;
1197 0 : txn_result->custom_error = 0;
1198 0 : txn_result->has_fee_details = false;
1199 0 : txn_result->loaded_accounts_data_size = txn_out->details.loaded_accounts_data_size;
1200 :
1201 0 : if( !txn_out->err.is_committable ) {
1202 0 : if( txn_out->err.is_fees_only ) {
1203 0 : txn_result->has_fee_details = true;
1204 0 : txn_result->fee_details.prioritization_fee = txn_out->details.priority_fee;
1205 0 : txn_result->fee_details.transaction_fee = txn_out->details.execution_fee;
1206 0 : }
1207 :
1208 0 : if( exec_res==FD_RUNTIME_TXN_ERR_INSTRUCTION_ERROR ) {
1209 0 : txn_result->instruction_error = (uint32_t) -txn_out->err.exec_err;
1210 0 : txn_result->instruction_error_index = txn_out->err.exec_err_idx;
1211 0 : if( txn_out->err.exec_err==FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR ) {
1212 0 : txn_result->custom_error = txn_out->err.custom_err;
1213 0 : }
1214 0 : }
1215 :
1216 0 : *txn_result_out = txn_result;
1217 0 : return FD_SCRATCH_ALLOC_FINI( l, 1UL ) - (ulong)out_buf;
1218 0 : }
1219 :
1220 : /* Capture instruction error code for executed transactions */
1221 0 : if( exec_res==FD_RUNTIME_TXN_ERR_INSTRUCTION_ERROR ) {
1222 0 : fd_txn_t const * txn = TXN( txn_in->txn );
1223 0 : uint instr_err_idx = txn_out->err.exec_err_idx;
1224 0 : int program_id_idx = txn->instr[instr_err_idx].program_id;
1225 :
1226 0 : txn_result->instruction_error = (uint32_t) -txn_out->err.exec_err;
1227 0 : txn_result->instruction_error_index = instr_err_idx;
1228 :
1229 0 : if( txn_out->err.exec_err==FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR &&
1230 0 : fd_executor_lookup_native_precompile_program( &txn_out->accounts.keys[ program_id_idx ] )==NULL ) {
1231 0 : txn_result->custom_error = txn_out->err.custom_err;
1232 0 : }
1233 0 : }
1234 :
1235 0 : txn_result->has_fee_details = true;
1236 0 : txn_result->fee_details.transaction_fee = txn_out->details.execution_fee;
1237 0 : txn_result->fee_details.prioritization_fee = txn_out->details.priority_fee;
1238 0 : txn_result->executed_units = txn_out->details.compute_budget.compute_unit_limit - txn_out->details.compute_budget.compute_meter;
1239 :
1240 : /* Return data */
1241 0 : txn_result->return_data_hash = fd_solfuzz_hash( txn_out->details.return_data.data, txn_out->details.return_data.len );
1242 :
1243 : /* Modified accounts */
1244 0 : txn_result->modified_accounts = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_exec_test_acct_state_t), sizeof(fd_exec_test_acct_state_t) * txn_out->accounts.cnt );
1245 0 : txn_result->rollback_accounts = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_exec_test_acct_state_t), sizeof(fd_exec_test_acct_state_t) * 2UL );
1246 0 : if( FD_UNLIKELY( _l > out_end ) ) abort();
1247 :
1248 0 : if( ( txn_out->err.is_fees_only || exec_res!=FD_RUNTIME_EXECUTE_SUCCESS ) && !txn_out->err.is_noop ) {
1249 : /* If the transaction errored, capture the rollback accounts (fee payer and nonce). */
1250 0 : if( FD_LIKELY( txn_out->accounts.nonce_idx_in_txn!=FD_FEE_PAYER_TXN_IDX ) ) {
1251 0 : write_account_to_result1(
1252 0 : txn_out->accounts.keys[FD_FEE_PAYER_TXN_IDX].uc,
1253 0 : txn_out->accounts.fee_payer_rollback_lamports,
1254 0 : txn_out->accounts.account[ FD_FEE_PAYER_TXN_IDX ]->prior_owner,
1255 0 : txn_out->accounts.account[ FD_FEE_PAYER_TXN_IDX ]->prior_data_len,
1256 0 : txn_out->accounts.account[ FD_FEE_PAYER_TXN_IDX ]->prior_data,
1257 0 : txn_out->accounts.account[ FD_FEE_PAYER_TXN_IDX ]->prior_executable,
1258 0 : txn_result->rollback_accounts,
1259 0 : &txn_result->rollback_accounts_count,
1260 0 : &_l,
1261 0 : out_end
1262 0 : );
1263 0 : }
1264 :
1265 0 : if( txn_out->accounts.nonce_idx_in_txn!=ULONG_MAX ) {
1266 : /* When the nonce account is also the fee payer, the rollback must
1267 : reflect the fee debit (the fee is still charged on a failed
1268 : nonce txn). prior_lamports is the pre-fee balance, so use the
1269 : post-fee fee_payer_rollback_lamports for that account instead. */
1270 0 : ulong nonce_rollback_lamports =
1271 0 : ( txn_out->accounts.nonce_idx_in_txn==FD_FEE_PAYER_TXN_IDX )
1272 0 : ? txn_out->accounts.fee_payer_rollback_lamports
1273 0 : : txn_out->accounts.account[ txn_out->accounts.nonce_idx_in_txn ]->prior_lamports;
1274 0 : write_account_to_result1(
1275 0 : txn_out->accounts.keys[txn_out->accounts.nonce_idx_in_txn].uc,
1276 0 : nonce_rollback_lamports,
1277 0 : txn_out->accounts.account[ txn_out->accounts.nonce_idx_in_txn ]->prior_owner,
1278 0 : txn_out->accounts.nonce_rollback_data_len,
1279 0 : txn_out->accounts.nonce_rollback_data,
1280 0 : txn_out->accounts.account[ txn_out->accounts.nonce_idx_in_txn ]->prior_executable,
1281 0 : txn_result->rollback_accounts,
1282 0 : &txn_result->rollback_accounts_count,
1283 0 : &_l,
1284 0 : out_end
1285 0 : );
1286 0 : }
1287 0 : }
1288 :
1289 0 : if( !txn_out->err.is_fees_only && !txn_out->err.is_noop ) {
1290 : /* Executed: writable accounts. */
1291 0 : for( ulong j=0UL; j<txn_out->accounts.cnt; j++ ) {
1292 0 : if( !fd_runtime_account_is_writable_idx( txn_in, txn_out, (ushort)j ) ) {
1293 0 : continue;
1294 0 : }
1295 :
1296 0 : write_account_to_result(
1297 0 : txn_out->accounts.account[j],
1298 0 : txn_result->modified_accounts,
1299 0 : &txn_result->modified_accounts_count,
1300 0 : &_l,
1301 0 : out_end
1302 0 : );
1303 0 : }
1304 0 : }
1305 :
1306 0 : *txn_result_out = txn_result;
1307 0 : return FD_SCRATCH_ALLOC_FINI( l, 1UL ) - (ulong)out_buf;
1308 0 : }
1309 :
1310 : void
1311 : fd_dump_txn_to_protobuf( fd_runtime_t * runtime,
1312 : fd_bank_t * bank,
1313 : fd_txn_in_t const * txn_in,
1314 0 : fd_txn_out_t * txn_out ) {
1315 0 : fd_spad_t * spad = fd_spad_join( fd_spad_new( runtime->log.dumping_mem, 1UL<<28UL ) );
1316 :
1317 0 : FD_SPAD_FRAME_BEGIN( spad ) {
1318 : // Get base58-encoded tx signature
1319 0 : const fd_ed25519_sig_t * signatures = fd_txn_get_signatures( TXN( txn_in->txn ), txn_in->txn->payload );
1320 0 : char encoded_signature[FD_BASE58_ENCODED_64_SZ];
1321 0 : fd_base58_encode_64( signatures[0], NULL, encoded_signature );
1322 :
1323 0 : fd_exec_test_txn_context_t txn_context_msg = FD_EXEC_TEST_TXN_CONTEXT_INIT_DEFAULT;
1324 0 : create_txn_context_protobuf_from_txn( &txn_context_msg, runtime, bank, txn_in, txn_out, spad );
1325 :
1326 : /* Output to file */
1327 0 : ulong out_buf_size = 100UL<<20UL; // 100 MB
1328 0 : uchar * out = fd_spad_alloc( spad, alignof(uchar), out_buf_size );
1329 0 : pb_ostream_t stream = pb_ostream_from_buffer( out, out_buf_size );
1330 0 : if( pb_encode( &stream, FD_EXEC_TEST_TXN_CONTEXT_FIELDS, &txn_context_msg ) ) {
1331 0 : char output_filepath[ PATH_MAX ];
1332 0 : snprintf( output_filepath, PATH_MAX, "%s/txn-%s.txnctx", runtime->log.dump_proto_ctx->dump_proto_output_dir, encoded_signature );
1333 0 : FILE * file = fopen(output_filepath, "wb");
1334 0 : if( file ) {
1335 0 : fwrite( out, 1, stream.bytes_written, file );
1336 0 : fclose( file );
1337 0 : }
1338 0 : }
1339 0 : } FD_SPAD_FRAME_END;
1340 0 : }
1341 :
1342 : void
1343 : fd_dump_txn_context_to_protobuf( fd_txn_dump_ctx_t * txn_dump_ctx,
1344 : fd_runtime_t * runtime,
1345 : fd_bank_t * bank,
1346 : fd_txn_in_t const * txn_in,
1347 0 : fd_txn_out_t * txn_out ) {
1348 0 : fd_txn_dump_context_reset( txn_dump_ctx );
1349 :
1350 0 : txn_dump_ctx->fixture.has_metadata = true;
1351 0 : strncpy(
1352 0 : txn_dump_ctx->fixture.metadata.fn_entrypoint,
1353 0 : "sol_compat_txn_execute_v1",
1354 0 : sizeof(txn_dump_ctx->fixture.metadata.fn_entrypoint)-1UL
1355 0 : );
1356 :
1357 0 : txn_dump_ctx->fixture.has_input = true;
1358 0 : create_txn_context_protobuf_from_txn( &txn_dump_ctx->fixture.input,
1359 0 : runtime, bank, txn_in, txn_out,
1360 0 : txn_dump_ctx->spad );
1361 0 : }
1362 :
1363 : void
1364 : fd_dump_txn_result_to_protobuf( fd_txn_dump_ctx_t * txn_dump_ctx,
1365 : fd_txn_in_t const * txn_in,
1366 : fd_txn_out_t * txn_out,
1367 0 : int exec_res ) {
1368 0 : txn_dump_ctx->fixture.has_output = true;
1369 :
1370 0 : ulong buf_sz = 100UL<<20UL;
1371 0 : void * buf = fd_spad_alloc( txn_dump_ctx->spad, alignof(fd_exec_test_txn_result_t), buf_sz );
1372 0 : fd_exec_test_txn_result_t * result = NULL;
1373 0 : create_txn_result_protobuf_from_txn( &result, buf, buf_sz, txn_in, txn_out, exec_res );
1374 0 : txn_dump_ctx->fixture.output = *result;
1375 0 : }
1376 :
1377 : void
1378 : fd_dump_txn_fixture_to_file( fd_txn_dump_ctx_t * txn_dump_ctx,
1379 : fd_dump_proto_ctx_t const * dump_proto_ctx,
1380 0 : fd_txn_in_t const * txn_in ) {
1381 0 : const fd_ed25519_sig_t * signatures = fd_txn_get_signatures( TXN( txn_in->txn ), txn_in->txn->payload );
1382 0 : char encoded_signature[FD_BASE58_ENCODED_64_SZ];
1383 0 : fd_base58_encode_64( signatures[0], NULL, encoded_signature );
1384 :
1385 0 : FD_SPAD_FRAME_BEGIN( txn_dump_ctx->spad ) {
1386 0 : ulong out_buf_size = 100UL<<20UL;
1387 0 : uchar * out = fd_spad_alloc( txn_dump_ctx->spad, alignof(uchar), out_buf_size );
1388 0 : pb_ostream_t stream = pb_ostream_from_buffer( out, out_buf_size );
1389 :
1390 0 : char output_filepath[ PATH_MAX ];
1391 :
1392 0 : if( dump_proto_ctx->dump_txn_as_fixture ) {
1393 0 : if( pb_encode( &stream, FD_EXEC_TEST_TXN_FIXTURE_FIELDS, &txn_dump_ctx->fixture ) ) {
1394 0 : snprintf( output_filepath, PATH_MAX, "%s/txn-%s.fix", dump_proto_ctx->dump_proto_output_dir, encoded_signature );
1395 0 : FILE * file = fopen( output_filepath, "wb" );
1396 0 : if( file ) {
1397 0 : fwrite( out, 1, stream.bytes_written, file );
1398 0 : fclose( file );
1399 0 : }
1400 0 : }
1401 0 : } else {
1402 0 : if( pb_encode( &stream, FD_EXEC_TEST_TXN_CONTEXT_FIELDS, &txn_dump_ctx->fixture.input ) ) {
1403 0 : snprintf( output_filepath, PATH_MAX, "%s/txn-%s.txnctx", dump_proto_ctx->dump_proto_output_dir, encoded_signature );
1404 0 : FILE * file = fopen( output_filepath, "wb" );
1405 0 : if( file ) {
1406 0 : fwrite( out, 1, stream.bytes_written, file );
1407 0 : fclose( file );
1408 0 : }
1409 0 : }
1410 0 : }
1411 0 : } FD_SPAD_FRAME_END;
1412 0 : }
1413 :
1414 : void
1415 : fd_dump_block_to_protobuf_collect_tx( fd_block_dump_ctx_t * dump_block_ctx,
1416 0 : fd_txn_p_t const * txn ) {
1417 0 : if( FD_UNLIKELY( dump_block_ctx->txns_to_dump_cnt>=FD_BLOCK_DUMP_CTX_MAX_TXN_CNT ) ) {
1418 0 : FD_LOG_ERR(( "Please increase FD_BLOCK_DUMP_CTX_MAX_TXN_CNT to dump more than %lu transactions.", FD_BLOCK_DUMP_CTX_MAX_TXN_CNT ));
1419 0 : return;
1420 0 : }
1421 0 : fd_memcpy( &dump_block_ctx->txns_to_dump[dump_block_ctx->txns_to_dump_cnt++], txn, sizeof(fd_txn_p_t) );
1422 0 : }
1423 :
1424 : void
1425 : fd_dump_block_to_protobuf( fd_block_dump_ctx_t * dump_block_ctx,
1426 : fd_banks_t * banks,
1427 : fd_bank_t * bank,
1428 : fd_accdb_t * accdb,
1429 : fd_dump_proto_ctx_t const * dump_proto_ctx,
1430 0 : fd_runtime_stack_t * runtime_stack ) {
1431 0 : if( FD_UNLIKELY( dump_block_ctx==NULL ) ) {
1432 0 : FD_LOG_WARNING(( "Block dumping context may not be NULL when dumping blocks." ));
1433 0 : return;
1434 0 : }
1435 :
1436 0 : FD_SPAD_FRAME_BEGIN( dump_block_ctx->spad ) {
1437 0 : if( FD_UNLIKELY( dump_proto_ctx==NULL ) ) {
1438 0 : FD_LOG_WARNING(( "Protobuf dumping context may not be NULL when dumping blocks." ));
1439 0 : return;
1440 0 : }
1441 :
1442 : /* Dump the block context */
1443 0 : create_block_context_protobuf_from_block( dump_block_ctx, banks, bank, accdb, runtime_stack );
1444 :
1445 : /* Output to file */
1446 0 : ulong out_buf_size = 1UL<<30UL; /* 1 GB */
1447 0 : uint8_t * out = fd_spad_alloc( dump_block_ctx->spad, alignof(uint8_t), out_buf_size );
1448 0 : pb_ostream_t stream = pb_ostream_from_buffer( out, out_buf_size );
1449 0 : if( pb_encode( &stream, FD_EXEC_TEST_BLOCK_CONTEXT_FIELDS, &dump_block_ctx->block_context ) ) {
1450 0 : char output_filepath[ PATH_MAX ];
1451 0 : snprintf( output_filepath, PATH_MAX, "%s/block-%lu.blockctx", dump_proto_ctx->dump_proto_output_dir, bank->f.slot );
1452 0 : FILE * file = fopen(output_filepath, "wb");
1453 0 : if( file ) {
1454 0 : fwrite( out, 1, stream.bytes_written, file );
1455 0 : fclose( file );
1456 0 : }
1457 0 : }
1458 0 : } FD_SPAD_FRAME_END;
1459 0 : }
1460 :
1461 : void
1462 : fd_dump_vm_syscall_to_protobuf( fd_vm_t const * vm,
1463 0 : char const * fn_name ) {
1464 0 : char const * syscall_name_filter = vm->instr_ctx->runtime->log.dump_proto_ctx->dump_syscall_name_filter;
1465 0 : if( syscall_name_filter && strlen( syscall_name_filter ) && strcmp( syscall_name_filter, fn_name ) ) {
1466 0 : return;
1467 0 : }
1468 :
1469 0 : fd_spad_t * spad = fd_spad_join( fd_spad_new( vm->instr_ctx->runtime->log.dumping_mem, 1UL<<28UL ) );
1470 :
1471 0 : FD_SPAD_FRAME_BEGIN( spad ) {
1472 :
1473 0 : fd_ed25519_sig_t signature;
1474 0 : memcpy( signature, (uchar const *)vm->instr_ctx->txn_in->txn->payload + TXN( vm->instr_ctx->txn_in->txn )->signature_off, sizeof(fd_ed25519_sig_t) );
1475 0 : char encoded_signature[FD_BASE58_ENCODED_64_SZ];
1476 0 : fd_base58_encode_64( signature, NULL, encoded_signature );
1477 :
1478 0 : char filename[ PATH_MAX ];
1479 0 : snprintf( filename,
1480 0 : PATH_MAX,
1481 0 : "%s/syscall-%s-%s-%d-%hhu-%lu.sysctx",
1482 0 : vm->instr_ctx->runtime->log.dump_proto_ctx->dump_proto_output_dir,
1483 0 : fn_name,
1484 0 : encoded_signature,
1485 0 : vm->instr_ctx->runtime->instr.current_idx,
1486 0 : vm->instr_ctx->runtime->instr.stack_sz,
1487 0 : vm->cu );
1488 :
1489 : /* The generated filename should be unique for every call. Silently return otherwise. */
1490 0 : if( FD_UNLIKELY( access( filename, F_OK )!=-1 ) ) {
1491 0 : return;
1492 0 : }
1493 :
1494 0 : fd_exec_test_syscall_context_t sys_ctx = FD_EXEC_TEST_SYSCALL_CONTEXT_INIT_ZERO;
1495 :
1496 : /* SyscallContext -> vm_ctx */
1497 0 : sys_ctx.has_vm_ctx = 1;
1498 :
1499 : /* SyscallContext -> vm_ctx -> heap_max */
1500 0 : sys_ctx.vm_ctx.heap_max = vm->heap_max; /* should be equiv. to txn_ctx->heap_sz */
1501 :
1502 : /* SyscallContext -> vm_ctx -> rodata */
1503 0 : sys_ctx.vm_ctx.rodata = fd_spad_alloc( spad, alignof(pb_bytes_array_t), PB_BYTES_ARRAY_T_ALLOCSIZE( vm->rodata_sz ) );
1504 0 : sys_ctx.vm_ctx.rodata->size = (pb_size_t) vm->rodata_sz;
1505 0 : fd_memcpy( sys_ctx.vm_ctx.rodata->bytes, vm->rodata, vm->rodata_sz );
1506 :
1507 : /* SyscallContext -> vm_ctx -> r0-11 */
1508 0 : sys_ctx.vm_ctx.r0 = vm->reg[0];
1509 0 : sys_ctx.vm_ctx.r1 = vm->reg[1];
1510 0 : sys_ctx.vm_ctx.r2 = vm->reg[2];
1511 0 : sys_ctx.vm_ctx.r3 = vm->reg[3];
1512 0 : sys_ctx.vm_ctx.r4 = vm->reg[4];
1513 0 : sys_ctx.vm_ctx.r5 = vm->reg[5];
1514 0 : sys_ctx.vm_ctx.r6 = vm->reg[6];
1515 0 : sys_ctx.vm_ctx.r7 = vm->reg[7];
1516 0 : sys_ctx.vm_ctx.r8 = vm->reg[8];
1517 0 : sys_ctx.vm_ctx.r9 = vm->reg[9];
1518 0 : sys_ctx.vm_ctx.r10 = vm->reg[10];
1519 0 : sys_ctx.vm_ctx.r11 = vm->reg[11];
1520 :
1521 : /* SyscallContext -> vm_ctx -> entry_pc */
1522 0 : sys_ctx.vm_ctx.entry_pc = vm->entry_pc;
1523 :
1524 : /* SyscallContext -> vm_ctx -> return_data */
1525 0 : sys_ctx.vm_ctx.has_return_data = 1;
1526 :
1527 : /* SyscallContext -> vm_ctx -> return_data -> data */
1528 0 : sys_ctx.vm_ctx.return_data.data = fd_spad_alloc( spad, alignof(pb_bytes_array_t), PB_BYTES_ARRAY_T_ALLOCSIZE( vm->instr_ctx->txn_out->details.return_data.len ) );
1529 0 : sys_ctx.vm_ctx.return_data.data->size = (pb_size_t)vm->instr_ctx->txn_out->details.return_data.len;
1530 0 : fd_memcpy( sys_ctx.vm_ctx.return_data.data->bytes, vm->instr_ctx->txn_out->details.return_data.data, vm->instr_ctx->txn_out->details.return_data.len );
1531 :
1532 : /* SyscallContext -> vm_ctx -> return_data -> program_id */
1533 0 : sys_ctx.vm_ctx.return_data.program_id = fd_spad_alloc( spad, alignof(pb_bytes_array_t), sizeof(fd_pubkey_t) );
1534 0 : sys_ctx.vm_ctx.return_data.program_id->size = sizeof(fd_pubkey_t);
1535 0 : fd_memcpy( sys_ctx.vm_ctx.return_data.program_id->bytes, vm->instr_ctx->txn_out->details.return_data.program_id.key, sizeof(fd_pubkey_t) );
1536 :
1537 : /* SyscallContext -> vm_ctx -> sbpf_version */
1538 0 : sys_ctx.vm_ctx.sbpf_version = (uint)vm->sbpf_version;
1539 :
1540 : /* SyscallContext -> instr_ctx */
1541 0 : sys_ctx.has_instr_ctx = 1;
1542 0 : create_instr_context_protobuf_from_instructions( &sys_ctx.instr_ctx,
1543 0 : vm->instr_ctx->runtime,
1544 0 : vm->instr_ctx->bank,
1545 0 : vm->instr_ctx->txn_out,
1546 0 : vm->instr_ctx->instr,
1547 0 : spad );
1548 :
1549 : /* SyscallContext -> syscall_invocation */
1550 0 : sys_ctx.has_syscall_invocation = 1;
1551 :
1552 : /* SyscallContext -> syscall_invocation -> function_name */
1553 0 : sys_ctx.syscall_invocation.function_name.size = fd_uint_min( (uint) strlen(fn_name), sizeof(sys_ctx.syscall_invocation.function_name.bytes) );
1554 0 : fd_memcpy( sys_ctx.syscall_invocation.function_name.bytes,
1555 0 : fn_name,
1556 0 : sys_ctx.syscall_invocation.function_name.size );
1557 :
1558 : /* SyscallContext -> syscall_invocation -> heap_prefix */
1559 0 : sys_ctx.syscall_invocation.heap_prefix = fd_spad_alloc( spad, 8UL, PB_BYTES_ARRAY_T_ALLOCSIZE( vm->heap_max ) );
1560 :
1561 0 : ulong heap_dump_sz = vm->instr_ctx->txn_out->details.compute_budget.heap_size;
1562 0 : ulong heap_init_sz = fd_ulong_min( vm->heap_clean, heap_dump_sz );
1563 0 : sys_ctx.syscall_invocation.heap_prefix->size = (pb_size_t)heap_dump_sz;
1564 0 : fd_memcpy( sys_ctx.syscall_invocation.heap_prefix->bytes, vm->heap, heap_init_sz );
1565 0 : fd_memset( sys_ctx.syscall_invocation.heap_prefix->bytes + heap_init_sz, 0, heap_dump_sz - heap_init_sz );
1566 :
1567 : /* SyscallContext -> syscall_invocation -> stack_prefix */
1568 0 : pb_size_t stack_sz = (pb_size_t)FD_VM_STACK_MAX;
1569 0 : sys_ctx.syscall_invocation.stack_prefix = fd_spad_alloc( spad, 8UL, PB_BYTES_ARRAY_T_ALLOCSIZE( stack_sz ) );
1570 0 : ulong stack_init_sz = fd_ulong_min( vm->stack_clean, (ulong)stack_sz );
1571 0 : sys_ctx.syscall_invocation.stack_prefix->size = stack_sz;
1572 0 : fd_memcpy( sys_ctx.syscall_invocation.stack_prefix->bytes, vm->stack, stack_init_sz );
1573 0 : fd_memset( sys_ctx.syscall_invocation.stack_prefix->bytes + stack_init_sz, 0, (ulong)stack_sz - stack_init_sz );
1574 :
1575 : /* Output to file */
1576 0 : ulong out_buf_size = 1UL<<29UL; /* 128 MB */
1577 0 : uint8_t * out = fd_spad_alloc( spad, alignof(uint8_t), out_buf_size );
1578 0 : pb_ostream_t stream = pb_ostream_from_buffer( out, out_buf_size );
1579 0 : if( pb_encode( &stream, FD_EXEC_TEST_SYSCALL_CONTEXT_FIELDS, &sys_ctx ) ) {
1580 0 : FILE * file = fopen(filename, "wb");
1581 0 : if( file ) {
1582 0 : fwrite( out, 1, stream.bytes_written, file );
1583 0 : fclose( file );
1584 0 : }
1585 0 : }
1586 0 : } FD_SPAD_FRAME_END;
1587 0 : }
|