Line data Source code
1 : #include "fd_dump_pb.h"
2 : #include "generated/block.pb.h"
3 : #include "generated/invoke.pb.h"
4 : #include "generated/txn.pb.h"
5 : #include "generated/vm.pb.h"
6 : #include "../fd_system_ids.h"
7 : #include "../fd_bank.h"
8 : #include "../fd_runtime.h"
9 : #include "../program/fd_address_lookup_table_program.h"
10 : #include "../../../ballet/nanopb/pb_encode.h"
11 : #include "../../accdb/fd_accdb_sync.h"
12 : #include "../../progcache/fd_prog_load.h"
13 :
14 : #include <stdio.h> /* fopen */
15 : #include <sys/mman.h> /* mmap */
16 : #include <unistd.h> /* ftruncate */
17 :
18 : #define SORT_NAME sort_uint64_t
19 0 : #define SORT_KEY_T uint64_t
20 0 : #define SORT_BEFORE(a,b) (a)<(b)
21 : #include "../../../util/tmpl/fd_sort.c"
22 :
23 : struct fd_dump_account_key_node {
24 : fd_pubkey_t key;
25 : ulong redblack_parent;
26 : ulong redblack_left;
27 : ulong redblack_right;
28 : int redblack_color;
29 : };
30 : typedef struct fd_dump_account_key_node fd_dump_account_key_node_t;
31 0 : #define REDBLK_T fd_dump_account_key_node_t
32 : #define REDBLK_NAME fd_dump_account_key_map
33 0 : long fd_dump_account_key_map_compare( fd_dump_account_key_node_t * left, fd_dump_account_key_node_t * right ) {
34 0 : return memcmp( left->key.uc, right->key.uc, sizeof(fd_pubkey_t) );
35 0 : }
36 : #include "../../../util/tmpl/fd_redblack.c"
37 :
38 : /***** CONSTANTS *****/
39 : static fd_pubkey_t const * fd_dump_sysvar_ids[] = {
40 : &fd_sysvar_recent_block_hashes_id,
41 : &fd_sysvar_clock_id,
42 : &fd_sysvar_slot_history_id,
43 : &fd_sysvar_slot_hashes_id,
44 : &fd_sysvar_epoch_schedule_id,
45 : &fd_sysvar_epoch_rewards_id,
46 : &fd_sysvar_fees_id,
47 : &fd_sysvar_rent_id,
48 : &fd_sysvar_stake_history_id,
49 : &fd_sysvar_last_restart_slot_id,
50 : &fd_sysvar_instructions_id,
51 : };
52 : static ulong const num_sysvar_entries = (sizeof(fd_dump_sysvar_ids) / sizeof(fd_pubkey_t *));
53 :
54 : static fd_pubkey_t const * fd_dump_builtin_ids[] = {
55 : &fd_solana_system_program_id,
56 : &fd_solana_vote_program_id,
57 : &fd_solana_stake_program_id,
58 : &fd_solana_bpf_loader_v4_program_id,
59 : &fd_solana_bpf_loader_deprecated_program_id,
60 : &fd_solana_bpf_loader_program_id,
61 : &fd_solana_bpf_loader_upgradeable_program_id,
62 : &fd_solana_compute_budget_program_id,
63 : &fd_solana_keccak_secp_256k_program_id,
64 : &fd_solana_secp256r1_program_id,
65 : &fd_solana_zk_elgamal_proof_program_id,
66 : &fd_solana_ed25519_sig_verify_program_id,
67 : };
68 : static ulong const num_loaded_builtins = (sizeof(fd_dump_builtin_ids) / sizeof(fd_pubkey_t *));
69 :
70 : /***** UTILITY FUNCTIONS *****/
71 :
72 : /** GENERAL UTILITY FUNCTIONS AND MACROS **/
73 :
74 : static inline int
75 0 : is_builtin_account( fd_pubkey_t const * account_key ) {
76 0 : for( ulong j=0UL; j<num_loaded_builtins; j++ ) {
77 0 : if( !memcmp( account_key, fd_dump_builtin_ids[j], sizeof(fd_pubkey_t) ) ) {
78 0 : return 1;
79 0 : }
80 0 : }
81 0 : return 0;
82 0 : }
83 :
84 : /** FEATURE DUMPING **/
85 : static void
86 : dump_sorted_features( fd_features_t const * features,
87 : fd_exec_test_feature_set_t * output_feature_set,
88 0 : fd_spad_t * spad ) {
89 : /* NOTE: Caller must have a spad frame prepared */
90 0 : uint64_t * unsorted_features = fd_spad_alloc( spad, alignof(uint64_t), FD_FEATURE_ID_CNT * sizeof(uint64_t) );
91 0 : ulong num_features = 0;
92 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 ) ) {
93 0 : if (features->f[current_feature->index] != FD_FEATURE_DISABLED) {
94 0 : unsorted_features[num_features++] = (uint64_t) current_feature->id.ul[0];
95 0 : }
96 0 : }
97 : // Sort the features
98 0 : void * scratch = fd_spad_alloc( spad, sort_uint64_t_stable_scratch_align(), sort_uint64_t_stable_scratch_footprint(num_features) );
99 0 : uint64_t * sorted_features = sort_uint64_t_stable_fast( unsorted_features, num_features, scratch );
100 :
101 : // Set feature set in message
102 0 : output_feature_set->features_count = (pb_size_t) num_features;
103 0 : output_feature_set->features = sorted_features;
104 0 : }
105 :
106 : /** ACCOUNT DUMPING **/
107 : static void
108 : dump_account_state( fd_pubkey_t const * account_key,
109 : fd_account_meta_t const * account_meta,
110 : fd_exec_test_acct_state_t * output_account,
111 0 : fd_spad_t * spad ) {
112 : // Address
113 0 : fd_memcpy(output_account->address, account_key, sizeof(fd_pubkey_t));
114 :
115 : // Lamports
116 0 : output_account->lamports = (uint64_t)account_meta->lamports;
117 :
118 : // Data
119 0 : output_account->data = fd_spad_alloc( spad, alignof(pb_bytes_array_t), PB_BYTES_ARRAY_T_ALLOCSIZE( account_meta->dlen ) );
120 0 : output_account->data->size = (pb_size_t) account_meta->dlen;
121 0 : fd_memcpy(output_account->data->bytes, fd_account_data( account_meta ), account_meta->dlen );
122 :
123 : // Executable
124 0 : output_account->executable = (bool)account_meta->executable;
125 :
126 : // Owner
127 0 : fd_memcpy(output_account->owner, account_meta->owner, sizeof(fd_pubkey_t));
128 0 : }
129 :
130 : static uchar
131 : account_already_dumped( fd_exec_test_acct_state_t const * dumped_accounts,
132 : ulong dumped_cnt,
133 0 : fd_pubkey_t const * account_key ) {
134 0 : for( ulong i=0UL; i<dumped_cnt; i++ ) {
135 0 : if( !memcmp( account_key, dumped_accounts[i].address, sizeof(fd_pubkey_t) ) ) {
136 0 : return 1;
137 0 : }
138 0 : }
139 0 : return 0;
140 0 : }
141 :
142 : /* Dumps a borrowed account if it exists and has not been dumped yet.
143 : Sets up the output borrowed account if it exists. Returns 0 if the
144 : account exists, 1 otherwise.
145 : TODO: This can be optimized by using a set. */
146 : static uchar
147 : dump_account_if_not_already_dumped( fd_accdb_user_t * accdb,
148 : fd_funk_txn_xid_t const * xid,
149 : fd_pubkey_t const * account_key,
150 : fd_spad_t * spad,
151 : fd_exec_test_acct_state_t * out_acct_states,
152 : pb_size_t * out_acct_states_cnt,
153 0 : fd_accdb_ro_t * out_ro ) {
154 0 : fd_accdb_ro_t ro[1];
155 0 : if( !fd_accdb_open_ro( accdb, ro, xid, account_key ) ) {
156 0 : return 1;
157 0 : }
158 :
159 0 : if( !account_already_dumped( out_acct_states, *out_acct_states_cnt, account_key ) ) {
160 0 : dump_account_state( account_key, ro->meta, &out_acct_states[*out_acct_states_cnt], spad );
161 0 : (*out_acct_states_cnt)++;
162 0 : }
163 :
164 0 : if( out_ro ) {
165 0 : *out_ro = *ro;
166 0 : } else {
167 0 : fd_accdb_close_ro( accdb, ro );
168 0 : }
169 0 : return 0;
170 0 : }
171 :
172 : static void
173 : dump_executable_account_if_exists( fd_accdb_user_t * accdb,
174 : fd_funk_txn_xid_t const * xid,
175 : fd_exec_test_acct_state_t const * program_account,
176 : fd_spad_t * spad,
177 : fd_exec_test_acct_state_t * out_account_states,
178 0 : pb_size_t * out_account_states_count ) {
179 0 : if( FD_LIKELY( memcmp( program_account->owner, fd_solana_bpf_loader_upgradeable_program_id.key, sizeof(fd_pubkey_t) ) ) ) {
180 0 : return;
181 0 : }
182 :
183 0 : int err;
184 0 : fd_bpf_upgradeable_loader_state_t * program_loader_state = fd_bincode_decode_spad(
185 0 : bpf_upgradeable_loader_state,
186 0 : spad,
187 0 : program_account->data->bytes,
188 0 : program_account->data->size,
189 0 : &err );
190 0 : if( FD_UNLIKELY( err ) ) return;
191 :
192 0 : if( !fd_bpf_upgradeable_loader_state_is_program( program_loader_state ) ) {
193 0 : return;
194 0 : }
195 :
196 0 : fd_pubkey_t * programdata_acc = &program_loader_state->inner.program.programdata_address;
197 0 : dump_account_if_not_already_dumped( accdb, xid, programdata_acc, spad, out_account_states, out_account_states_count, NULL );
198 0 : }
199 :
200 : static void
201 : dump_sanitized_transaction( fd_accdb_user_t * accdb,
202 : fd_funk_txn_xid_t const * xid,
203 : fd_txn_t const * txn_descriptor,
204 : uchar const * txn_payload,
205 : fd_spad_t * spad,
206 0 : fd_exec_test_sanitized_transaction_t * sanitized_transaction ) {
207 0 : fd_txn_acct_addr_lut_t const * address_lookup_tables = fd_txn_get_address_tables_const( txn_descriptor );
208 :
209 : /* Transaction Context -> tx -> message */
210 0 : sanitized_transaction->has_message = true;
211 0 : fd_exec_test_transaction_message_t * message = &sanitized_transaction->message;
212 :
213 : /* Transaction Context -> tx -> message -> is_legacy */
214 0 : message->is_legacy = txn_descriptor->transaction_version == FD_TXN_VLEGACY;
215 :
216 : /* Transaction Context -> tx -> message -> header */
217 0 : message->has_header = true;
218 0 : fd_exec_test_message_header_t * header = &message->header;
219 :
220 : /* Transaction Context -> tx -> message -> header -> num_required_signatures */
221 0 : header->num_required_signatures = txn_descriptor->signature_cnt;
222 :
223 : /* Transaction Context -> tx -> message -> header -> num_readonly_signed_accounts */
224 0 : header->num_readonly_signed_accounts = txn_descriptor->readonly_signed_cnt;
225 :
226 : /* Transaction Context -> tx -> message -> header -> num_readonly_unsigned_accounts */
227 0 : header->num_readonly_unsigned_accounts = txn_descriptor->readonly_unsigned_cnt;
228 :
229 : /* Transaction Context -> tx -> message -> account_keys */
230 0 : message->account_keys_count = txn_descriptor->acct_addr_cnt;
231 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 *)) );
232 0 : fd_acct_addr_t const * account_keys = fd_txn_get_acct_addrs( txn_descriptor, txn_payload );
233 0 : for( ulong i = 0; i < txn_descriptor->acct_addr_cnt; i++ ) {
234 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)) );
235 0 : account_key->size = sizeof(fd_pubkey_t);
236 0 : memcpy( account_key->bytes, &account_keys[i], sizeof(fd_pubkey_t) );
237 0 : message->account_keys[i] = account_key;
238 0 : }
239 :
240 : /* Transaction Context -> tx -> message -> recent_blockhash */
241 0 : uchar const * recent_blockhash = fd_txn_get_recent_blockhash( txn_descriptor, txn_payload );
242 0 : message->recent_blockhash = fd_spad_alloc( spad, alignof(pb_bytes_array_t), PB_BYTES_ARRAY_T_ALLOCSIZE(sizeof(fd_hash_t)) );
243 0 : message->recent_blockhash->size = sizeof(fd_hash_t);
244 0 : memcpy( message->recent_blockhash->bytes, recent_blockhash, sizeof(fd_hash_t) );
245 :
246 : /* Transaction Context -> tx -> message -> instructions */
247 0 : message->instructions_count = txn_descriptor->instr_cnt;
248 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) );
249 0 : for( ulong i = 0; i < txn_descriptor->instr_cnt; ++i ) {
250 0 : fd_txn_instr_t instr = txn_descriptor->instr[i];
251 0 : fd_exec_test_compiled_instruction_t * compiled_instruction = &message->instructions[i];
252 :
253 : // compiled instruction -> program_id_index
254 0 : compiled_instruction->program_id_index = instr.program_id;
255 :
256 : // compiled instruction -> accounts
257 0 : compiled_instruction->accounts_count = instr.acct_cnt;
258 0 : compiled_instruction->accounts = fd_spad_alloc( spad, alignof(uint32_t), instr.acct_cnt * sizeof(uint32_t) );
259 0 : uchar const * instr_accounts = fd_txn_get_instr_accts( &instr, txn_payload );
260 0 : for( ulong j = 0; j < instr.acct_cnt; ++j ) {
261 0 : uchar instr_acct_index = instr_accounts[j];
262 0 : compiled_instruction->accounts[j] = instr_acct_index;
263 0 : }
264 :
265 : // compiled instruction -> data
266 0 : uchar const * instr_data = fd_txn_get_instr_data( &instr, txn_payload );
267 0 : compiled_instruction->data = fd_spad_alloc( spad, alignof(pb_bytes_array_t), PB_BYTES_ARRAY_T_ALLOCSIZE(instr.data_sz) );
268 0 : compiled_instruction->data->size = instr.data_sz;
269 0 : memcpy( compiled_instruction->data->bytes, instr_data, instr.data_sz );
270 0 : }
271 :
272 : /* ALUT stuff (non-legacy) */
273 0 : message->address_table_lookups_count = 0;
274 0 : if( !message->is_legacy ) {
275 : /* Transaction Context -> tx -> message -> address_table_lookups */
276 0 : message->address_table_lookups_count = txn_descriptor->addr_table_lookup_cnt;
277 0 : message->address_table_lookups = fd_spad_alloc( spad,
278 0 : alignof(fd_exec_test_message_address_table_lookup_t),
279 0 : txn_descriptor->addr_table_lookup_cnt * sizeof(fd_exec_test_message_address_table_lookup_t) );
280 0 : for( ulong i = 0; i < txn_descriptor->addr_table_lookup_cnt; ++i ) {
281 : // alut -> account_key
282 0 : fd_pubkey_t * alut_key = (fd_pubkey_t *) (txn_payload + address_lookup_tables[i].addr_off);
283 0 : memcpy( message->address_table_lookups[i].account_key, alut_key, sizeof(fd_pubkey_t) );
284 :
285 : // Access ALUT account data to access its keys
286 0 : fd_accdb_ro_t addr_lut_ro[1];
287 0 : if( FD_UNLIKELY( !fd_accdb_open_ro( accdb, addr_lut_ro, xid, alut_key ) ) ) {
288 0 : FD_LOG_ERR(( "addr lut not found" ));
289 0 : }
290 :
291 : // alut -> writable_indexes
292 0 : message->address_table_lookups[i].writable_indexes_count = address_lookup_tables[i].writable_cnt;
293 0 : message->address_table_lookups[i].writable_indexes = fd_spad_alloc( spad, alignof(uint32_t), address_lookup_tables[i].writable_cnt * sizeof(uint32_t) );
294 0 : uchar * writable_indexes = (uchar *) (txn_payload + address_lookup_tables[i].writable_off);
295 0 : for( ulong j = 0; j < address_lookup_tables[i].writable_cnt; ++j ) {
296 0 : message->address_table_lookups[i].writable_indexes[j] = writable_indexes[j];
297 0 : }
298 :
299 : // alut -> readonly_indexes
300 0 : message->address_table_lookups[i].readonly_indexes_count = address_lookup_tables[i].readonly_cnt;
301 0 : message->address_table_lookups[i].readonly_indexes = fd_spad_alloc( spad, alignof(uint32_t), address_lookup_tables[i].readonly_cnt * sizeof(uint32_t) );
302 0 : uchar * readonly_indexes = (uchar *) (txn_payload + address_lookup_tables[i].readonly_off);
303 0 : for( ulong j = 0; j < address_lookup_tables[i].readonly_cnt; ++j ) {
304 0 : message->address_table_lookups[i].readonly_indexes[j] = readonly_indexes[j];
305 0 : }
306 :
307 0 : fd_accdb_close_ro( accdb, addr_lut_ro );
308 0 : }
309 0 : }
310 :
311 : /* Transaction Context -> tx -> message_hash */
312 : // Skip because it does not matter what's in here
313 :
314 : /* Transaction Context -> tx -> signatures */
315 0 : sanitized_transaction->signatures_count = txn_descriptor->signature_cnt;
316 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 *)) );
317 0 : fd_ed25519_sig_t const * signatures = fd_txn_get_signatures( txn_descriptor, txn_payload );
318 0 : for( uchar i = 0; i < txn_descriptor->signature_cnt; ++i ) {
319 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)) );
320 0 : signature->size = sizeof(fd_ed25519_sig_t);
321 0 : memcpy( signature->bytes, &signatures[i], sizeof(fd_ed25519_sig_t) );
322 0 : sanitized_transaction->signatures[i] = signature;
323 0 : }
324 0 : }
325 :
326 : /** BLOCKHASH QUEUE DUMPING **/
327 :
328 : static void
329 : dump_blockhash_queue( fd_blockhashes_t const * queue,
330 : fd_spad_t * spad,
331 : pb_bytes_array_t ** output_blockhash_queue,
332 0 : pb_size_t * output_blockhash_queue_count ) {
333 0 : ulong bhq_size = fd_ulong_min( FD_BLOCKHASHES_MAX, fd_blockhash_deq_cnt( queue->d.deque ) );
334 :
335 : // Iterate over all block hashes in the queue and save them in the output
336 0 : pb_size_t cnt = 0U;
337 0 : for( fd_blockhash_deq_iter_t iter=fd_blockhash_deq_iter_init_rev( queue->d.deque );
338 0 : !fd_blockhash_deq_iter_done_rev( queue->d.deque, iter ) && cnt<FD_BLOCKHASHES_MAX;
339 0 : iter=fd_blockhash_deq_iter_prev( queue->d.deque, iter ), cnt++ ) {
340 0 : fd_blockhash_info_t const * ele = fd_blockhash_deq_iter_ele_const( queue->d.deque, iter );
341 0 : pb_bytes_array_t * output_blockhash = fd_spad_alloc( spad, alignof(pb_bytes_array_t), PB_BYTES_ARRAY_T_ALLOCSIZE(sizeof(fd_hash_t)) );
342 0 : output_blockhash->size = sizeof(fd_hash_t);
343 0 : fd_memcpy( output_blockhash->bytes, &ele->hash, sizeof(fd_hash_t) );
344 0 : output_blockhash_queue[ bhq_size-cnt-1UL ] = output_blockhash;
345 0 : }
346 :
347 0 : *output_blockhash_queue_count = cnt;
348 0 : }
349 :
350 : /** SECONDARY FUNCTIONS **/
351 :
352 : /* add_account_to_dumped_accounts adds an account to the dumped accounts
353 : set if it does not exist already. Returns 0 if the account already
354 : exists, and 1 if the account was added successfully.
355 :
356 : TODO: Txn dumping should be optimized to use these functions. */
357 : static uchar
358 : add_account_to_dumped_accounts( fd_dump_account_key_node_t * pool,
359 : fd_dump_account_key_node_t ** root,
360 0 : fd_pubkey_t const * pubkey ) {
361 : /* If the key already exists, return early. */
362 0 : fd_dump_account_key_node_t node = {
363 0 : .key = *pubkey,
364 0 : };
365 0 : if( fd_dump_account_key_map_find( pool, *root, &node ) ) {
366 0 : return 0;
367 0 : }
368 :
369 0 : fd_dump_account_key_node_t * new_node = fd_dump_account_key_map_acquire( pool );
370 0 : new_node->key = *pubkey;
371 0 : fd_dump_account_key_map_insert( pool, root, new_node );
372 0 : return 1;
373 0 : }
374 :
375 : /* add_account_and_programdata_to_dumped_accounts adds an account and
376 : its programdata account (if the account is a v3 program) to the
377 : dumped accounts set if they do not exist already. */
378 : static void
379 : add_account_and_programdata_to_dumped_accounts( fd_accdb_user_t * accdb,
380 : fd_funk_txn_xid_t const * xid,
381 : fd_dump_account_key_node_t * pool,
382 : fd_dump_account_key_node_t ** root,
383 0 : fd_pubkey_t const * pubkey ) {
384 : /* Add the current account to the dumped accounts set. We can save
385 : some time by enforcing an invariant that "if current account was
386 : dumped, then programdata account was also dumped," so we save
387 : ourselves a call to Funk. */
388 0 : uchar ret = add_account_to_dumped_accounts( pool, root, pubkey );
389 0 : if( ret==0 ) return;
390 :
391 : /* Read the account from Funk to see if its a program account and if
392 : it needs to be dumped. */
393 0 : fd_accdb_ro_t program_account[1];
394 0 : if( FD_UNLIKELY( !fd_accdb_open_ro( accdb, program_account, xid, pubkey ) ) ) {
395 0 : return;
396 0 : }
397 :
398 : /* Return if its not owned by the v3 loader */
399 0 : if( FD_LIKELY( !fd_pubkey_eq( fd_accdb_ref_owner( program_account ), &fd_solana_bpf_loader_upgradeable_program_id ) ) ) {
400 0 : fd_accdb_close_ro( accdb, program_account );
401 0 : return;
402 0 : }
403 :
404 : /* Get the program account state */
405 0 : fd_bpf_upgradeable_loader_state_t program_account_state[1];
406 0 : if( FD_UNLIKELY( !fd_bincode_decode_static(
407 0 : bpf_upgradeable_loader_state,
408 0 : program_account_state,
409 0 : fd_accdb_ref_data_const( program_account ),
410 0 : fd_accdb_ref_data_sz ( program_account ),
411 0 : NULL ) ) ) {
412 0 : fd_accdb_close_ro( accdb, program_account );
413 0 : return;
414 0 : }
415 0 : if( !fd_bpf_upgradeable_loader_state_is_program( program_account_state ) ) {
416 0 : fd_accdb_close_ro( accdb, program_account );
417 0 : return;
418 0 : }
419 :
420 : /* Dump the programdata address */
421 0 : add_account_to_dumped_accounts( pool, root, &program_account_state->inner.program.programdata_address );
422 0 : fd_accdb_close_ro( accdb, program_account );
423 0 : }
424 :
425 : /* add_lut_account_to_dumped_accounts adds an address lookup table
426 : account AND all pubkeys in the lookup table to the dumped accounts
427 : set if they do not exist already. */
428 : static void
429 : add_lut_accounts_to_dumped_accounts( fd_accdb_user_t * accdb,
430 : fd_funk_txn_xid_t const * xid,
431 : fd_dump_account_key_node_t * pool,
432 : fd_dump_account_key_node_t ** root,
433 0 : fd_pubkey_t const * pubkey ) {
434 : /* Add the current account to the dumped accounts set. */
435 0 : add_account_to_dumped_accounts( pool, root, pubkey );
436 :
437 : /* Read the account and dump all pubkeys within the lookup table. */
438 0 : fd_accdb_ro_t lut_account[1];
439 0 : if( FD_UNLIKELY( !fd_accdb_open_ro( accdb, lut_account, xid, pubkey ) ) ) {
440 0 : return;
441 0 : }
442 :
443 0 : uchar const * data = fd_accdb_ref_data_const( lut_account );
444 0 : ulong data_len = fd_accdb_ref_data_sz ( lut_account );
445 :
446 : /* Decode the ALUT account and dump all pubkeys within the lookup
447 : table. */
448 0 : if( data_len<FD_LOOKUP_TABLE_META_SIZE || (data_len&0x1fUL) ) {
449 0 : fd_accdb_close_ro( accdb, lut_account );
450 0 : return;
451 0 : }
452 0 : fd_pubkey_t const * lookup_addrs = fd_type_pun_const( data+FD_LOOKUP_TABLE_META_SIZE );
453 0 : ulong lookup_addrs_cnt = ( data_len-FD_LOOKUP_TABLE_META_SIZE)>>5UL; // = (dlen - 56) / 32
454 0 : for( ulong i=0UL; i<lookup_addrs_cnt; i++ ) {
455 0 : fd_pubkey_t const * referenced_pubkey = &lookup_addrs[i];
456 0 : add_account_and_programdata_to_dumped_accounts( accdb, xid, pool, root, referenced_pubkey );
457 0 : }
458 0 : fd_accdb_close_ro( accdb, lut_account );
459 0 : }
460 :
461 : /* create_synthetic_vote_account_from_vote_state creates a synthetic
462 : vote account from a vote state cache element. It fills in default
463 : values for unspecified fields and encodes the vote state into
464 : out_vote_account's data field. */
465 : static void
466 : create_synthetic_vote_account_from_vote_state( fd_vote_state_ele_t const * vote_state,
467 : fd_spad_t * spad,
468 0 : fd_exec_test_vote_account_t * out_vote_account ) {
469 0 : out_vote_account->has_vote_account = true;
470 0 : fd_memcpy( out_vote_account->vote_account.address, &vote_state->vote_account, sizeof(fd_pubkey_t) );
471 0 : out_vote_account->vote_account.executable = false;
472 0 : out_vote_account->vote_account.lamports = 100000UL;
473 0 : fd_memcpy( out_vote_account->vote_account.owner, fd_solana_vote_program_id.key, sizeof(fd_pubkey_t) );
474 0 : out_vote_account->stake = vote_state->stake;
475 :
476 : /* Construct the vote account data. Fill in missing fields with
477 : arbitrary defaults (since they're not used anyways) */
478 0 : fd_vote_state_versioned_t vsv = {
479 0 : .discriminant = fd_vote_state_versioned_enum_v3,
480 0 : .inner = {
481 0 : .v3 = {
482 0 : .node_pubkey = vote_state->node_account,
483 0 : .authorized_withdrawer = vote_state->node_account,
484 0 : .commission = vote_state->commission,
485 0 : .root_slot = 0UL,
486 0 : .has_root_slot = 0,
487 0 : .last_timestamp = {
488 0 : .timestamp = vote_state->last_vote_timestamp,
489 0 : .slot = vote_state->last_vote_slot,
490 0 : },
491 0 : }
492 0 : }
493 0 : };
494 0 : fd_vote_state_v3_t * synthetic_vote_state = &vsv.inner.v3;
495 :
496 : /* Create synthetic landed votes */
497 0 : synthetic_vote_state->votes = deq_fd_landed_vote_t_join(
498 0 : deq_fd_landed_vote_t_new(
499 0 : fd_spad_alloc(
500 0 : spad,
501 0 : deq_fd_landed_vote_t_align(),
502 0 : deq_fd_landed_vote_t_footprint( 32UL ) ),
503 0 : 32UL ) );
504 0 : for( ulong i=0UL; i<32UL; i++ ) {
505 0 : fd_landed_vote_t elem = {0};
506 0 : deq_fd_landed_vote_t_push_tail( synthetic_vote_state->votes, elem );
507 0 : }
508 :
509 : /* Populate authoritzed voters */
510 0 : void * authorized_voters_pool_mem = fd_spad_alloc(
511 0 : spad,
512 0 : fd_vote_authorized_voters_pool_align(),
513 0 : fd_vote_authorized_voters_pool_footprint( 5UL ) );
514 0 : void * authorized_voters_treap_mem = fd_spad_alloc(
515 0 : spad,
516 0 : fd_vote_authorized_voters_treap_align(),
517 0 : fd_vote_authorized_voters_treap_footprint( 5UL ) );
518 0 : synthetic_vote_state->authorized_voters.pool = fd_vote_authorized_voters_pool_join( fd_vote_authorized_voters_pool_new( authorized_voters_pool_mem, 5UL ) );
519 0 : synthetic_vote_state->authorized_voters.treap = fd_vote_authorized_voters_treap_join( fd_vote_authorized_voters_treap_new( authorized_voters_treap_mem, 5UL ) );
520 :
521 : /* Encode the synthetic vote state */
522 0 : ulong encoded_sz = fd_vote_state_versioned_size( &vsv );
523 0 : out_vote_account->vote_account.data = fd_spad_alloc( spad, alignof(pb_bytes_array_t), PB_BYTES_ARRAY_T_ALLOCSIZE( encoded_sz ) );
524 0 : out_vote_account->vote_account.data->size = (pb_size_t)encoded_sz;
525 :
526 0 : fd_bincode_encode_ctx_t encode_ctx = {
527 0 : .data = out_vote_account->vote_account.data->bytes,
528 0 : .dataend = out_vote_account->vote_account.data->bytes+encoded_sz,
529 0 : };
530 0 : fd_vote_state_versioned_encode( &vsv, &encode_ctx );
531 0 : }
532 :
533 : static void
534 : dump_prior_vote_accounts( fd_vote_states_t const * vote_states,
535 : fd_dump_account_key_node_t * dumped_accounts_pool,
536 : fd_dump_account_key_node_t ** dumped_accounts_root,
537 : fd_exec_test_vote_account_t * out_vote_accounts,
538 : pb_size_t * out_vote_accounts_count,
539 0 : fd_spad_t * spad ) {
540 :
541 0 : fd_vote_states_iter_t iter_[1];
542 0 : for( fd_vote_states_iter_t * iter = fd_vote_states_iter_init( iter_, vote_states );
543 0 : !fd_vote_states_iter_done( iter );
544 0 : fd_vote_states_iter_next( iter ) ) {
545 0 : fd_vote_state_ele_t const * vote_state = fd_vote_states_iter_ele( iter );
546 0 : add_account_to_dumped_accounts( dumped_accounts_pool, dumped_accounts_root, &vote_state->vote_account );
547 :
548 0 : create_synthetic_vote_account_from_vote_state(
549 0 : vote_state,
550 0 : spad,
551 0 : &out_vote_accounts[(*out_vote_accounts_count)++] );
552 0 : }
553 0 : }
554 :
555 : static void
556 : create_block_context_protobuf_from_block( fd_block_dump_ctx_t * dump_ctx,
557 : fd_banks_t * banks,
558 : fd_bank_t * bank,
559 0 : fd_accdb_user_t * accdb ) {
560 : /* We should use the bank fields and funk txn from the parent slot in
561 : order to capture the block context from before the current block
562 : was executed, since dumping is happening in the block finalize
563 : step. */
564 0 : fd_bank_t parent_bank[1];
565 0 : fd_banks_get_parent( parent_bank, banks, bank );
566 0 : ulong current_slot = fd_bank_slot_get( bank );
567 0 : ulong parent_slot = fd_bank_slot_get( parent_bank );
568 0 : fd_funk_txn_xid_t parent_xid = { .ul = { parent_slot, parent_bank->data->idx } };
569 0 : fd_exec_test_block_context_t * block_context = &dump_ctx->block_context;
570 0 : ulong dump_txn_count = dump_ctx->txns_to_dump_cnt;
571 0 : fd_spad_t * spad = dump_ctx->spad;
572 :
573 : /* Get vote and stake delegation infos */
574 0 : fd_vote_states_t const * vote_states = fd_bank_vote_states_locking_query( parent_bank );
575 0 : ulong vote_account_t_cnt = fd_vote_states_cnt( vote_states );
576 0 : fd_bank_vote_states_end_locking_query( parent_bank );
577 :
578 0 : fd_stake_delegations_t const * stake_delegations = fd_bank_stake_delegations_frontier_query( banks, parent_bank );
579 0 : ulong stake_account_cnt = fd_stake_delegations_cnt( stake_delegations );
580 :
581 : /* Collect account states in a temporary set before iterating over
582 : them and dumping them out. */
583 0 : ulong total_num_accounts = num_sysvar_entries + /* Sysvars */
584 0 : num_loaded_builtins + /* Builtins */
585 0 : stake_account_cnt + /* Stake accounts */
586 0 : vote_account_t_cnt + /* Current vote accounts */
587 0 : dump_txn_count*128UL; /* Txn accounts upper bound */
588 0 : void * dumped_accounts_mem = fd_spad_alloc( spad, fd_dump_account_key_map_align(), fd_dump_account_key_map_footprint( total_num_accounts ) );
589 0 : fd_dump_account_key_node_t * dumped_accounts_pool = fd_dump_account_key_map_join( fd_dump_account_key_map_new( dumped_accounts_mem, total_num_accounts ) );
590 0 : fd_dump_account_key_node_t * dumped_accounts_root = NULL;
591 :
592 : /* BlockContext -> txns */
593 0 : block_context->txns_count = (pb_size_t)dump_txn_count;
594 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) );
595 0 : fd_memset( block_context->txns, 0, dump_ctx->txns_to_dump_cnt * sizeof(fd_exec_test_sanitized_transaction_t) );
596 :
597 : /* Dump sanitized transactions from the transaction descriptors */
598 0 : for( ulong i=0UL; i<dump_ctx->txns_to_dump_cnt; i++ ) {
599 0 : fd_txn_p_t const * txn_ptr = &dump_ctx->txns_to_dump[i];
600 0 : fd_txn_t const * txn_descriptor = TXN( txn_ptr );
601 0 : dump_sanitized_transaction( accdb, &parent_xid, txn_descriptor, txn_ptr->payload, spad, &block_context->txns[i] );
602 :
603 : /* Dump account + alut + programdata accounts (if applicable).
604 : 1. Dump the raw txn account keys
605 : 2. Dump the ALUT accounts
606 : 3. Dump all referenced accounts in the ALUTs
607 : 4. Dump any executable accounts */
608 :
609 : // 1 + 4. Dump any account keys that are referenced by transactions
610 : // + any programdata accounts (if applicable).
611 0 : fd_acct_addr_t const * account_keys = fd_txn_get_acct_addrs( txn_descriptor, txn_ptr->payload );
612 0 : for( ushort l=0; l<txn_descriptor->acct_addr_cnt; l++ ) {
613 0 : fd_pubkey_t const * account_key = fd_type_pun_const( &account_keys[l] );
614 0 : add_account_and_programdata_to_dumped_accounts( accdb, &parent_xid, dumped_accounts_pool, &dumped_accounts_root, account_key );
615 0 : }
616 :
617 : // 2 + 3 + 4. Dump any ALUT accounts + any accounts referenced in
618 : // the ALUTs + any programdata accounts (if applicable).
619 0 : fd_txn_acct_addr_lut_t const * txn_lookup_tables = fd_txn_get_address_tables_const( txn_descriptor );
620 0 : for( ushort l=0; l<txn_descriptor->addr_table_lookup_cnt; l++ ) {
621 0 : fd_txn_acct_addr_lut_t const * lookup_table = &txn_lookup_tables[l];
622 0 : fd_pubkey_t const * lut_key = fd_type_pun_const( txn_ptr->payload+lookup_table->addr_off );
623 0 : add_lut_accounts_to_dumped_accounts( accdb, &parent_xid, dumped_accounts_pool, &dumped_accounts_root, lut_key );
624 0 : }
625 0 : }
626 :
627 : /* Dump sysvars */
628 0 : for( ulong i=0UL; i<num_sysvar_entries; i++ ) {
629 0 : add_account_to_dumped_accounts( dumped_accounts_pool, &dumped_accounts_root, fd_dump_sysvar_ids[i] );
630 0 : }
631 :
632 : /* Dump builtins */
633 0 : for( ulong i=0UL; i<num_loaded_builtins; i++ ) {
634 0 : add_account_to_dumped_accounts( dumped_accounts_pool, &dumped_accounts_root, fd_dump_builtin_ids[i] );
635 0 : }
636 :
637 : /* Dump stake accounts for this epoch */
638 0 : fd_stake_delegations_iter_t iter_[1];
639 0 : for( fd_stake_delegations_iter_t * iter = fd_stake_delegations_iter_init( iter_, stake_delegations );
640 0 : !fd_stake_delegations_iter_done( iter );
641 0 : fd_stake_delegations_iter_next( iter ) ) {
642 0 : fd_stake_delegation_t * stake_delegation = fd_stake_delegations_iter_ele( iter );
643 0 : add_account_to_dumped_accounts( dumped_accounts_pool, &dumped_accounts_root, &stake_delegation->stake_account );
644 0 : }
645 :
646 : /* Dump vote accounts for this epoch */
647 0 : vote_states = fd_bank_vote_states_locking_query( parent_bank );
648 0 : fd_vote_states_iter_t vote_iter_[1];
649 0 : for( fd_vote_states_iter_t * iter = fd_vote_states_iter_init( vote_iter_, vote_states ); !fd_vote_states_iter_done( iter ); fd_vote_states_iter_next( iter ) ) {
650 0 : fd_vote_state_ele_t const * vote_state = fd_vote_states_iter_ele( iter );
651 0 : add_account_to_dumped_accounts( dumped_accounts_pool, &dumped_accounts_root, &vote_state->vote_account );
652 0 : }
653 0 : fd_bank_vote_states_end_locking_query( parent_bank );
654 :
655 : // BlockContext -> EpochContext -> vote_accounts_t_1 (vote accounts at epoch T-1)
656 0 : fd_vote_states_t const * vote_states_prev = fd_bank_vote_states_prev_query( parent_bank );
657 0 : block_context->epoch_ctx.vote_accounts_t_1 = fd_spad_alloc(
658 0 : spad,
659 0 : alignof(fd_exec_test_vote_account_t),
660 0 : sizeof(fd_exec_test_vote_account_t)*fd_vote_states_cnt( vote_states_prev ) );
661 0 : block_context->epoch_ctx.vote_accounts_t_1_count = 0U;
662 0 : dump_prior_vote_accounts(
663 0 : vote_states_prev,
664 0 : dumped_accounts_pool,
665 0 : &dumped_accounts_root,
666 0 : block_context->epoch_ctx.vote_accounts_t_1,
667 0 : &block_context->epoch_ctx.vote_accounts_t_1_count,
668 0 : spad );
669 :
670 : // BlockContext -> EpochContext -> vote_accounts_t_2 (vote accounts at epoch T-2)
671 0 : fd_vote_states_t const * vote_states_prev_prev = fd_bank_vote_states_prev_prev_query( parent_bank );
672 0 : block_context->epoch_ctx.vote_accounts_t_2 = fd_spad_alloc(
673 0 : spad,
674 0 : alignof(fd_exec_test_vote_account_t),
675 0 : sizeof(fd_exec_test_vote_account_t)*fd_vote_states_cnt( vote_states_prev_prev ) );
676 0 : block_context->epoch_ctx.vote_accounts_t_2_count = 0U;
677 0 : dump_prior_vote_accounts(
678 0 : vote_states_prev_prev,
679 0 : dumped_accounts_pool,
680 0 : &dumped_accounts_root,
681 0 : block_context->epoch_ctx.vote_accounts_t_2,
682 0 : &block_context->epoch_ctx.vote_accounts_t_2_count,
683 0 : spad );
684 :
685 : /* BlockContext -> acct_states
686 : Iterate over the set and dump all the account keys in one pass. */
687 0 : block_context->acct_states_count = 0U;
688 0 : block_context->acct_states = fd_spad_alloc(
689 0 : spad,
690 0 : alignof(fd_exec_test_acct_state_t),
691 0 : fd_dump_account_key_map_size( dumped_accounts_pool, dumped_accounts_root )*sizeof(fd_exec_test_acct_state_t) );
692 0 : for( fd_dump_account_key_node_t * node = fd_dump_account_key_map_minimum( dumped_accounts_pool, dumped_accounts_root );
693 0 : node;
694 0 : node = fd_dump_account_key_map_successor( dumped_accounts_pool, node ) ) {
695 0 : fd_accdb_ro_t ro[1];
696 0 : if( FD_UNLIKELY( !fd_accdb_open_ro( accdb, ro, &parent_xid, &node->key ) ) ) {
697 0 : continue;
698 0 : }
699 0 : dump_account_state(
700 0 : fd_accdb_ref_address( ro ),
701 0 : ro->meta,
702 0 : &block_context->acct_states[block_context->acct_states_count++],
703 0 : spad );
704 0 : fd_accdb_close_ro( accdb, ro );
705 0 : }
706 :
707 : /* BlockContext -> blockhash_queue */
708 0 : fd_blockhashes_t const * bhq = fd_bank_block_hash_queue_query( parent_bank );
709 0 : block_context->blockhash_queue = fd_spad_alloc(
710 0 : spad,
711 0 : alignof(pb_bytes_array_t *),
712 0 : PB_BYTES_ARRAY_T_ALLOCSIZE((FD_BLOCKHASHES_MAX) * sizeof(pb_bytes_array_t *)) );
713 0 : block_context->blockhash_queue_count = 0U;
714 0 : dump_blockhash_queue( bhq, spad, block_context->blockhash_queue, &block_context->blockhash_queue_count );
715 :
716 : /* BlockContext -> SlotContext */
717 0 : block_context->has_slot_ctx = true;
718 0 : block_context->slot_ctx.slot = current_slot;
719 0 : block_context->slot_ctx.block_height = fd_bank_block_height_get( bank );
720 0 : block_context->slot_ctx.prev_slot = fd_bank_parent_slot_get( bank );
721 :
722 : // We need to store the POH hash for the current block since we don't
723 : // recalculate it in the harnesses.
724 0 : fd_memcpy( block_context->slot_ctx.poh, fd_bank_poh_query( bank ), sizeof(fd_pubkey_t) );
725 0 : fd_memcpy( block_context->slot_ctx.parent_bank_hash, fd_bank_bank_hash_query( parent_bank ), sizeof(fd_pubkey_t) );
726 :
727 0 : fd_lthash_value_t const * parent_lthash = fd_bank_lthash_locking_query( parent_bank );
728 0 : fd_memcpy( block_context->slot_ctx.parent_lthash, parent_lthash, sizeof(fd_lthash_value_t) );
729 0 : fd_bank_lthash_end_locking_query( parent_bank );
730 :
731 0 : block_context->slot_ctx.prev_epoch_capitalization = fd_bank_capitalization_get( parent_bank );
732 :
733 : /* BlockContext -> SlotContext -> fee_rate_governor */
734 0 : fd_fee_rate_governor_t const * fee_rate_governor = fd_bank_fee_rate_governor_query( parent_bank );
735 0 : block_context->slot_ctx.has_fee_rate_governor = true;
736 0 : block_context->slot_ctx.fee_rate_governor = (fd_exec_test_fee_rate_governor_t){
737 0 : .target_lamports_per_signature = fee_rate_governor->target_lamports_per_signature,
738 0 : .target_signatures_per_slot = fee_rate_governor->target_signatures_per_slot,
739 0 : .min_lamports_per_signature = fee_rate_governor->min_lamports_per_signature,
740 0 : .max_lamports_per_signature = fee_rate_governor->max_lamports_per_signature,
741 0 : .burn_percent = fee_rate_governor->burn_percent,
742 0 : };
743 :
744 : /* BlockContext -> EpochContext */
745 0 : block_context->has_epoch_ctx = true;
746 0 : block_context->epoch_ctx.has_features = true;
747 0 : dump_sorted_features( fd_bank_features_query( parent_bank ), &block_context->epoch_ctx.features, spad );
748 0 : block_context->epoch_ctx.hashes_per_tick = fd_bank_hashes_per_tick_get( parent_bank );
749 0 : block_context->epoch_ctx.ticks_per_slot = fd_bank_ticks_per_slot_get( parent_bank );
750 0 : block_context->epoch_ctx.slots_per_year = fd_bank_slots_per_year_get( parent_bank );
751 0 : block_context->epoch_ctx.has_inflation = true;
752 :
753 0 : fd_inflation_t const * inflation = fd_bank_inflation_query( parent_bank );
754 0 : block_context->epoch_ctx.inflation = (fd_exec_test_inflation_t) {
755 0 : .initial = inflation->initial,
756 0 : .terminal = inflation->terminal,
757 0 : .taper = inflation->taper,
758 0 : .foundation = inflation->foundation,
759 0 : .foundation_term = inflation->foundation_term,
760 0 : };
761 0 : block_context->epoch_ctx.genesis_creation_time = fd_bank_genesis_creation_time_get( parent_bank );
762 0 : }
763 :
764 : static void
765 : create_txn_context_protobuf_from_txn( fd_exec_test_txn_context_t * txn_context_msg,
766 : fd_runtime_t * runtime,
767 : fd_bank_t * bank,
768 : fd_txn_in_t const * txn_in,
769 : fd_txn_out_t * txn_out,
770 0 : fd_spad_t * spad ) {
771 0 : fd_txn_t const * txn_descriptor = TXN( txn_in->txn );
772 0 : uchar const * txn_payload = (uchar const *) txn_in->txn->payload;
773 :
774 : /* Transaction Context -> account_shared_data
775 : Contains:
776 : - Account data for regular accounts
777 : - Account data for LUT accounts
778 : - Account data for executable accounts
779 : - Account data for (almost) all sysvars
780 :
781 : We also don't want to store builtins in account shared data due to
782 : how Agave's bank handles them in the init phase. */
783 : // Dump regular accounts first
784 0 : txn_context_msg->account_shared_data_count = 0;
785 0 : txn_context_msg->account_shared_data = fd_spad_alloc( spad,
786 0 : alignof(fd_exec_test_acct_state_t),
787 0 : (256UL*2UL + txn_descriptor->addr_table_lookup_cnt + num_sysvar_entries) * sizeof(fd_exec_test_acct_state_t) );
788 0 : fd_funk_txn_xid_t xid = { .ul = { fd_bank_slot_get( bank ), bank->data->idx } };
789 0 : for( ulong i = 0; i < txn_out->accounts.cnt; ++i ) {
790 : // Make sure account is not a non-migrating builtin
791 0 : if( !is_builtin_account( &txn_out->accounts.keys[i] ) ) {
792 0 : dump_account_if_not_already_dumped(
793 0 : runtime->accdb,
794 0 : &xid,
795 0 : &txn_out->accounts.keys[i],
796 0 : spad,
797 0 : txn_context_msg->account_shared_data,
798 0 : &txn_context_msg->account_shared_data_count,
799 0 : NULL
800 0 : );
801 0 : }
802 0 : }
803 :
804 : // Dump LUT accounts
805 0 : fd_txn_acct_addr_lut_t const * address_lookup_tables = fd_txn_get_address_tables_const( txn_descriptor );
806 0 : for( ulong i = 0; i < txn_descriptor->addr_table_lookup_cnt; ++i ) {
807 0 : fd_txn_acct_addr_lut_t const * addr_lut = &address_lookup_tables[i];
808 0 : fd_pubkey_t * alut_key = (fd_pubkey_t *) (txn_payload + addr_lut->addr_off);
809 :
810 : // Dump the LUT account itself if not already dumped
811 0 : fd_accdb_ro_t ro[1];
812 0 : int ret = dump_account_if_not_already_dumped(
813 0 : runtime->accdb,
814 0 : &xid,
815 0 : alut_key,
816 0 : spad,
817 0 : txn_context_msg->account_shared_data,
818 0 : &txn_context_msg->account_shared_data_count,
819 0 : ro
820 0 : );
821 0 : if( FD_UNLIKELY( ret ) ) continue;
822 :
823 0 : uchar const * alut_data = fd_accdb_ref_data_const( ro );
824 0 : ulong alut_sz = fd_accdb_ref_data_sz ( ro );
825 :
826 0 : if( FD_UNLIKELY( alut_sz<FD_LOOKUP_TABLE_META_SIZE ) ) {
827 : /* Skip over invalid address lookup tables */
828 0 : fd_accdb_close_ro( runtime->accdb, ro );
829 0 : continue;
830 0 : }
831 :
832 0 : fd_pubkey_t const * lookup_addrs = fd_type_pun_const( alut_data+FD_LOOKUP_TABLE_META_SIZE );
833 0 : ulong lookup_addrs_cnt = (alut_sz - FD_LOOKUP_TABLE_META_SIZE) / sizeof(fd_pubkey_t);
834 :
835 : /* Dump any account state refererenced in ALUTs */
836 0 : uchar const * writable_lut_idxs = txn_payload + addr_lut->writable_off;
837 0 : for( ulong j=0; j<addr_lut->writable_cnt; j++ ) {
838 0 : if( writable_lut_idxs[j] >= lookup_addrs_cnt ) {
839 0 : continue;
840 0 : }
841 0 : fd_pubkey_t const * referenced_addr = lookup_addrs + writable_lut_idxs[j];
842 0 : if( is_builtin_account( referenced_addr ) ) continue;
843 :
844 0 : dump_account_if_not_already_dumped(
845 0 : runtime->accdb,
846 0 : &xid,
847 0 : referenced_addr,
848 0 : spad,
849 0 : txn_context_msg->account_shared_data,
850 0 : &txn_context_msg->account_shared_data_count,
851 0 : NULL
852 0 : );
853 0 : }
854 :
855 0 : uchar const * readonly_lut_idxs = txn_payload + addr_lut->readonly_off;
856 0 : for( ulong j = 0; j < addr_lut->readonly_cnt; j++ ) {
857 0 : if( readonly_lut_idxs[j] >= lookup_addrs_cnt ) {
858 0 : continue;
859 0 : }
860 0 : fd_pubkey_t const * referenced_addr = lookup_addrs + readonly_lut_idxs[j];
861 0 : if( is_builtin_account( referenced_addr ) ) continue;
862 :
863 0 : dump_account_if_not_already_dumped(
864 0 : runtime->accdb,
865 0 : &xid,
866 0 : referenced_addr,
867 0 : spad,
868 0 : txn_context_msg->account_shared_data,
869 0 : &txn_context_msg->account_shared_data_count,
870 0 : NULL
871 0 : );
872 0 : }
873 :
874 0 : fd_accdb_close_ro( runtime->accdb, ro );
875 0 : }
876 :
877 : /* Dump the programdata accounts for any potential v3-owned program accounts */
878 0 : uint accounts_dumped_so_far = txn_context_msg->account_shared_data_count;
879 0 : for( uint i=0U; i<accounts_dumped_so_far; i++ ) {
880 0 : fd_exec_test_acct_state_t const * maybe_program_account = &txn_context_msg->account_shared_data[i];
881 0 : dump_executable_account_if_exists( runtime->accdb, &xid, maybe_program_account, spad, txn_context_msg->account_shared_data, &txn_context_msg->account_shared_data_count );
882 0 : }
883 :
884 : /* Dump sysvars */
885 0 : for( ulong i = 0; i < num_sysvar_entries; i++ ) {
886 0 : dump_account_if_not_already_dumped(
887 0 : runtime->accdb,
888 0 : &xid,
889 0 : fd_dump_sysvar_ids[i],
890 0 : spad,
891 0 : txn_context_msg->account_shared_data,
892 0 : &txn_context_msg->account_shared_data_count,
893 0 : NULL
894 0 : );
895 0 : }
896 :
897 : /* Transaction Context -> tx */
898 0 : txn_context_msg->has_tx = true;
899 0 : fd_exec_test_sanitized_transaction_t * sanitized_transaction = &txn_context_msg->tx;
900 0 : dump_sanitized_transaction( runtime->accdb, &xid, txn_descriptor, txn_payload, spad, sanitized_transaction );
901 :
902 : /* Transaction Context -> blockhash_queue
903 : NOTE: Agave's implementation of register_hash incorrectly allows the blockhash queue to hold max_age + 1 (max 301)
904 : entries. We have this incorrect logic implemented in fd_sysvar_recent_hashes:register_blockhash and it's not a
905 : huge issue, but something to keep in mind. */
906 0 : pb_bytes_array_t ** output_blockhash_queue = fd_spad_alloc(
907 0 : spad,
908 0 : alignof(pb_bytes_array_t *),
909 0 : PB_BYTES_ARRAY_T_ALLOCSIZE((FD_BLOCKHASHES_MAX) * sizeof(pb_bytes_array_t *)) );
910 0 : txn_context_msg->blockhash_queue = output_blockhash_queue;
911 0 : fd_blockhashes_t const * block_hash_queue = fd_bank_block_hash_queue_query( bank );
912 0 : dump_blockhash_queue( block_hash_queue, spad, output_blockhash_queue, &txn_context_msg->blockhash_queue_count );
913 :
914 : /* Transaction Context -> epoch_ctx */
915 0 : txn_context_msg->has_epoch_ctx = true;
916 0 : txn_context_msg->epoch_ctx.has_features = true;
917 0 : dump_sorted_features( fd_bank_features_query( bank ), &txn_context_msg->epoch_ctx.features, spad );
918 :
919 : /* Transaction Context -> slot_ctx */
920 0 : txn_context_msg->has_slot_ctx = true;
921 0 : txn_context_msg->slot_ctx.slot = fd_bank_slot_get( bank );
922 0 : }
923 :
924 : static void
925 : create_instr_context_protobuf_from_instructions( fd_exec_test_instr_context_t * instr_context,
926 : fd_runtime_t * runtime,
927 : fd_bank_t * bank,
928 : fd_txn_out_t * txn_out,
929 : fd_instr_info_t const * instr,
930 0 : fd_spad_t * spad ) {
931 : /* Program ID */
932 0 : fd_memcpy( instr_context->program_id, txn_out->accounts.keys[ instr->program_id ].uc, sizeof(fd_pubkey_t) );
933 :
934 0 : fd_funk_txn_xid_t xid = { .ul = { fd_bank_slot_get( bank ), bank->data->idx } };
935 :
936 : /* Accounts */
937 0 : instr_context->accounts_count = (pb_size_t) txn_out->accounts.cnt;
938 0 : instr_context->accounts = fd_spad_alloc( spad, alignof(fd_exec_test_acct_state_t), (instr_context->accounts_count + num_sysvar_entries + runtime->accounts.executable_cnt) * sizeof(fd_exec_test_acct_state_t));
939 0 : for( ulong i = 0; i < txn_out->accounts.cnt; i++ ) {
940 : // Copy account information over
941 0 : fd_account_meta_t * account_meta = txn_out->accounts.account[i].meta;
942 0 : fd_exec_test_acct_state_t * output_account = &instr_context->accounts[i];
943 0 : dump_account_state( &txn_out->accounts.keys[i], account_meta, output_account, spad );
944 0 : }
945 :
946 : /* Add sysvar cache variables */
947 0 : for( ulong i = 0; i < num_sysvar_entries; i++ ) {
948 0 : fd_accdb_ro_t ro[1];
949 0 : if( !fd_accdb_open_ro( runtime->accdb, ro, &xid, fd_dump_sysvar_ids[i] ) ) {
950 0 : continue;
951 0 : }
952 : // Make sure the account doesn't exist in the output accounts yet
953 0 : int account_exists = 0;
954 0 : for( ulong j = 0; j < txn_out->accounts.cnt; j++ ) {
955 0 : if( fd_pubkey_eq( &txn_out->accounts.keys[j], fd_dump_sysvar_ids[i] ) ) {
956 0 : account_exists = true;
957 0 : break;
958 0 : }
959 0 : }
960 :
961 : // Copy it into output
962 0 : if( !account_exists ) {
963 0 : fd_exec_test_acct_state_t * output_account = &instr_context->accounts[instr_context->accounts_count++];
964 0 : dump_account_state( fd_accdb_ref_address( ro ), ro->meta, output_account, spad );
965 0 : }
966 0 : fd_accdb_close_ro( runtime->accdb, ro );
967 0 : }
968 :
969 : /* Add executable accounts */
970 0 : for( ulong i = 0; i < runtime->accounts.executable_cnt; i++ ) {
971 : // Make sure the account doesn't exist in the output accounts yet
972 0 : fd_accdb_ro_t const * ro = &runtime->accounts.executable[i];
973 0 : bool account_exists = false;
974 0 : for( ulong j = 0; j < instr_context->accounts_count; j++ ) {
975 0 : if( 0 == memcmp( instr_context->accounts[j].address, fd_accdb_ref_address( ro ), sizeof(fd_pubkey_t) ) ) {
976 0 : account_exists = true;
977 0 : break;
978 0 : }
979 0 : }
980 : // Copy it into output
981 0 : if( !account_exists ) {
982 0 : fd_exec_test_acct_state_t * output_account = &instr_context->accounts[instr_context->accounts_count++];
983 0 : dump_account_state( fd_accdb_ref_address( ro ), ro->meta, output_account, spad );
984 0 : }
985 0 : }
986 :
987 : /* Instruction Accounts */
988 0 : instr_context->instr_accounts_count = (pb_size_t) instr->acct_cnt;
989 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) );
990 0 : for( ushort i = 0; i < instr->acct_cnt; i++ ) {
991 0 : fd_exec_test_instr_acct_t * output_instr_account = &instr_context->instr_accounts[i];
992 :
993 0 : output_instr_account->index = instr->accounts[i].index_in_transaction;
994 0 : output_instr_account->is_writable = instr->accounts[i].is_writable;
995 0 : output_instr_account->is_signer = instr->accounts[i].is_signer;
996 0 : }
997 :
998 : /* Data */
999 0 : instr_context->data = fd_spad_alloc( spad, alignof(pb_bytes_array_t), PB_BYTES_ARRAY_T_ALLOCSIZE( instr->data_sz ) );
1000 0 : instr_context->data->size = (pb_size_t) instr->data_sz;
1001 0 : fd_memcpy( instr_context->data->bytes, instr->data, instr->data_sz );
1002 :
1003 : /* Compute Units */
1004 0 : instr_context->cu_avail = txn_out->details.compute_budget.compute_meter;
1005 :
1006 : /* Slot Context */
1007 0 : instr_context->has_slot_context = true;
1008 :
1009 : /* Epoch Context */
1010 0 : instr_context->has_epoch_context = true;
1011 0 : instr_context->epoch_context.has_features = true;
1012 0 : dump_sorted_features( fd_bank_features_query( bank ), &instr_context->epoch_context.features, spad );
1013 0 : }
1014 :
1015 : /***** PUBLIC APIs *****/
1016 :
1017 : void
1018 : fd_dump_instr_to_protobuf( fd_runtime_t * runtime,
1019 : fd_bank_t * bank,
1020 : fd_txn_in_t const * txn_in,
1021 : fd_txn_out_t * txn_out,
1022 : fd_instr_info_t * instr,
1023 0 : ushort instruction_idx ) {
1024 0 : fd_spad_t * spad = fd_spad_join( fd_spad_new( runtime->log.dumping_mem, 1UL<<28UL ) );
1025 :
1026 0 : FD_SPAD_FRAME_BEGIN( spad ) {
1027 : // Get base58-encoded tx signature
1028 0 : const fd_ed25519_sig_t * signatures = fd_txn_get_signatures( TXN( txn_in->txn ), txn_in->txn->payload );
1029 0 : fd_ed25519_sig_t signature; fd_memcpy( signature, signatures[0], sizeof(fd_ed25519_sig_t) );
1030 0 : char encoded_signature[FD_BASE58_ENCODED_64_SZ];
1031 0 : ulong out_size;
1032 0 : fd_base58_encode_64( signature, &out_size, encoded_signature );
1033 :
1034 0 : if( runtime->log.capture_ctx->dump_proto_sig_filter ) {
1035 0 : ulong filter_strlen = (ulong) strlen(runtime->log.capture_ctx->dump_proto_sig_filter);
1036 :
1037 : // Terminate early if the signature does not match
1038 0 : if( memcmp( runtime->log.capture_ctx->dump_proto_sig_filter, encoded_signature, filter_strlen < out_size ? filter_strlen : out_size ) ) {
1039 0 : return;
1040 0 : }
1041 0 : }
1042 :
1043 0 : fd_exec_test_instr_context_t instr_context = FD_EXEC_TEST_INSTR_CONTEXT_INIT_DEFAULT;
1044 0 : create_instr_context_protobuf_from_instructions( &instr_context, runtime, bank, txn_out, instr, spad );
1045 :
1046 : /* Output to file */
1047 0 : ulong out_buf_size = 100 * 1024 * 1024;
1048 0 : uint8_t * out = fd_spad_alloc( spad, alignof(uchar) , out_buf_size );
1049 0 : pb_ostream_t stream = pb_ostream_from_buffer( out, out_buf_size );
1050 0 : if (pb_encode(&stream, FD_EXEC_TEST_INSTR_CONTEXT_FIELDS, &instr_context)) {
1051 0 : char output_filepath[ PATH_MAX ];
1052 0 : snprintf( output_filepath, PATH_MAX, "%s/instr-%s-%hu.instrctx", runtime->log.capture_ctx->dump_proto_output_dir, encoded_signature, instruction_idx );
1053 0 : FILE * file = fopen(output_filepath, "wb");
1054 0 : if( file ) {
1055 0 : fwrite( out, 1, stream.bytes_written, file );
1056 0 : fclose( file );
1057 0 : }
1058 0 : }
1059 0 : } FD_SPAD_FRAME_END;
1060 0 : }
1061 :
1062 : void
1063 : fd_dump_txn_to_protobuf( fd_runtime_t * runtime,
1064 : fd_bank_t * bank,
1065 : fd_txn_in_t const * txn_in,
1066 0 : fd_txn_out_t * txn_out ) {
1067 0 : fd_spad_t * spad = fd_spad_join( fd_spad_new( runtime->log.dumping_mem, 1UL<<28UL ) );
1068 :
1069 0 : FD_SPAD_FRAME_BEGIN( spad ) {
1070 : // Get base58-encoded tx signature
1071 0 : const fd_ed25519_sig_t * signatures = fd_txn_get_signatures( TXN( txn_in->txn ), txn_in->txn->payload );
1072 0 : fd_ed25519_sig_t signature; fd_memcpy( signature, signatures[0], sizeof(fd_ed25519_sig_t) );
1073 0 : char encoded_signature[FD_BASE58_ENCODED_64_SZ];
1074 0 : ulong out_size;
1075 0 : fd_base58_encode_64( signature, &out_size, encoded_signature );
1076 :
1077 0 : if( runtime->log.capture_ctx->dump_proto_sig_filter ) {
1078 : // Terminate early if the signature does not match
1079 0 : if( strcmp( runtime->log.capture_ctx->dump_proto_sig_filter, encoded_signature ) ) {
1080 0 : return;
1081 0 : }
1082 0 : }
1083 :
1084 0 : fd_exec_test_txn_context_t txn_context_msg = FD_EXEC_TEST_TXN_CONTEXT_INIT_DEFAULT;
1085 0 : create_txn_context_protobuf_from_txn( &txn_context_msg, runtime, bank, txn_in, txn_out, spad );
1086 :
1087 : /* Output to file */
1088 0 : ulong out_buf_size = 100UL<<20UL; // 100 MB
1089 0 : uchar * out = fd_spad_alloc( spad, alignof(uchar), out_buf_size );
1090 0 : pb_ostream_t stream = pb_ostream_from_buffer( out, out_buf_size );
1091 0 : if( pb_encode( &stream, FD_EXEC_TEST_TXN_CONTEXT_FIELDS, &txn_context_msg ) ) {
1092 0 : char output_filepath[ PATH_MAX ];
1093 0 : snprintf( output_filepath, PATH_MAX, "%s/txn-%s.txnctx", runtime->log.capture_ctx->dump_proto_output_dir, encoded_signature );
1094 0 : FILE * file = fopen(output_filepath, "wb");
1095 0 : if( file ) {
1096 0 : fwrite( out, 1, stream.bytes_written, file );
1097 0 : fclose( file );
1098 0 : }
1099 0 : }
1100 0 : } FD_SPAD_FRAME_END;
1101 0 : }
1102 :
1103 : void
1104 : fd_dump_block_to_protobuf_collect_tx( fd_block_dump_ctx_t * dump_ctx,
1105 0 : fd_txn_p_t const * txn ) {
1106 0 : if( FD_UNLIKELY( dump_ctx->txns_to_dump_cnt>=FD_BLOCK_DUMP_CTX_MAX_TXN_CNT ) ) {
1107 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 ));
1108 0 : return;
1109 0 : }
1110 0 : fd_memcpy( &dump_ctx->txns_to_dump[dump_ctx->txns_to_dump_cnt++], txn, sizeof(fd_txn_p_t) );
1111 0 : }
1112 :
1113 : void
1114 : fd_dump_block_to_protobuf( fd_block_dump_ctx_t * dump_ctx,
1115 : fd_banks_t * banks,
1116 : fd_bank_t * bank,
1117 : fd_accdb_user_t * accdb,
1118 0 : fd_capture_ctx_t const * capture_ctx ) {
1119 0 : FD_SPAD_FRAME_BEGIN( dump_ctx->spad ) {
1120 0 : if( FD_UNLIKELY( capture_ctx==NULL ) ) {
1121 0 : FD_LOG_WARNING(( "Capture context may not be NULL when dumping blocks." ));
1122 0 : return;
1123 0 : }
1124 :
1125 0 : if( FD_UNLIKELY( dump_ctx==NULL ) ) {
1126 0 : FD_LOG_WARNING(( "Block dumping context may not be NULL when dumping blocks." ));
1127 0 : return;
1128 0 : }
1129 :
1130 : /* Dump the block context */
1131 0 : create_block_context_protobuf_from_block( dump_ctx, banks, bank, accdb );
1132 :
1133 : /* Output to file */
1134 0 : ulong out_buf_size = 1UL<<30UL; /* 1 GB */
1135 0 : uint8_t * out = fd_spad_alloc( dump_ctx->spad, alignof(uint8_t), out_buf_size );
1136 0 : pb_ostream_t stream = pb_ostream_from_buffer( out, out_buf_size );
1137 0 : if( pb_encode( &stream, FD_EXEC_TEST_BLOCK_CONTEXT_FIELDS, &dump_ctx->block_context ) ) {
1138 0 : char output_filepath[ PATH_MAX ];
1139 0 : snprintf( output_filepath, PATH_MAX, "%s/block-%lu.blockctx", capture_ctx->dump_proto_output_dir, fd_bank_slot_get( bank ) );
1140 0 : FILE * file = fopen(output_filepath, "wb");
1141 0 : if( file ) {
1142 0 : fwrite( out, 1, stream.bytes_written, file );
1143 0 : fclose( file );
1144 0 : }
1145 0 : }
1146 0 : } FD_SPAD_FRAME_END;
1147 0 : }
1148 :
1149 : void
1150 : fd_dump_vm_syscall_to_protobuf( fd_vm_t const * vm,
1151 0 : char const * fn_name ) {
1152 :
1153 0 : fd_spad_t * spad = fd_spad_join( fd_spad_new( vm->instr_ctx->runtime->log.dumping_mem, 1UL<<28UL ) );
1154 :
1155 0 : FD_SPAD_FRAME_BEGIN( spad ) {
1156 :
1157 0 : fd_ed25519_sig_t signature;
1158 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) );
1159 0 : char encoded_signature[FD_BASE58_ENCODED_64_SZ];
1160 0 : fd_base58_encode_64( signature, NULL, encoded_signature );
1161 :
1162 0 : char filename[ PATH_MAX ];
1163 0 : snprintf( filename,
1164 0 : PATH_MAX,
1165 0 : "%s/syscall-%s-%s-%d-%hhu-%lu.sysctx",
1166 0 : vm->instr_ctx->runtime->log.capture_ctx->dump_proto_output_dir,
1167 0 : fn_name,
1168 0 : encoded_signature,
1169 0 : vm->instr_ctx->runtime->instr.current_idx,
1170 0 : vm->instr_ctx->runtime->instr.stack_sz,
1171 0 : vm->cu );
1172 :
1173 : /* The generated filename should be unique for every call. Silently return otherwise. */
1174 0 : if( FD_UNLIKELY( access( filename, F_OK )!=-1 ) ) {
1175 0 : return;
1176 0 : }
1177 :
1178 0 : fd_exec_test_syscall_context_t sys_ctx = FD_EXEC_TEST_SYSCALL_CONTEXT_INIT_ZERO;
1179 :
1180 : /* SyscallContext -> vm_ctx */
1181 0 : sys_ctx.has_vm_ctx = 1;
1182 :
1183 : /* SyscallContext -> vm_ctx -> heap_max */
1184 0 : sys_ctx.vm_ctx.heap_max = vm->heap_max; /* should be equiv. to txn_ctx->heap_sz */
1185 :
1186 : /* SyscallContext -> vm_ctx -> rodata */
1187 0 : sys_ctx.vm_ctx.rodata = fd_spad_alloc( spad, alignof(pb_bytes_array_t), PB_BYTES_ARRAY_T_ALLOCSIZE( vm->rodata_sz ) );
1188 0 : sys_ctx.vm_ctx.rodata->size = (pb_size_t) vm->rodata_sz;
1189 0 : fd_memcpy( sys_ctx.vm_ctx.rodata->bytes, vm->rodata, vm->rodata_sz );
1190 :
1191 : /* SyscallContext -> vm_ctx -> rodata_text_section_offset */
1192 0 : sys_ctx.vm_ctx.rodata_text_section_offset = vm->text_off;
1193 :
1194 : /* SyscallContext -> vm_ctx -> rodata_text_section_length */
1195 0 : sys_ctx.vm_ctx.rodata_text_section_length = vm->text_sz;
1196 :
1197 : /* SyscallContext -> vm_ctx -> r0-11 */
1198 0 : sys_ctx.vm_ctx.r0 = vm->reg[0];
1199 0 : sys_ctx.vm_ctx.r1 = vm->reg[1];
1200 0 : sys_ctx.vm_ctx.r2 = vm->reg[2];
1201 0 : sys_ctx.vm_ctx.r3 = vm->reg[3];
1202 0 : sys_ctx.vm_ctx.r4 = vm->reg[4];
1203 0 : sys_ctx.vm_ctx.r5 = vm->reg[5];
1204 0 : sys_ctx.vm_ctx.r6 = vm->reg[6];
1205 0 : sys_ctx.vm_ctx.r7 = vm->reg[7];
1206 0 : sys_ctx.vm_ctx.r8 = vm->reg[8];
1207 0 : sys_ctx.vm_ctx.r9 = vm->reg[9];
1208 0 : sys_ctx.vm_ctx.r10 = vm->reg[10];
1209 0 : sys_ctx.vm_ctx.r11 = vm->reg[11];
1210 :
1211 : /* SyscallContext -> vm_ctx -> entry_pc */
1212 0 : sys_ctx.vm_ctx.entry_pc = vm->entry_pc;
1213 :
1214 : /* SyscallContext -> vm_ctx -> return_data */
1215 0 : sys_ctx.vm_ctx.has_return_data = 1;
1216 :
1217 : /* SyscallContext -> vm_ctx -> return_data -> data */
1218 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 ) );
1219 0 : sys_ctx.vm_ctx.return_data.data->size = (pb_size_t)vm->instr_ctx->txn_out->details.return_data.len;
1220 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 );
1221 :
1222 : /* SyscallContext -> vm_ctx -> return_data -> program_id */
1223 0 : sys_ctx.vm_ctx.return_data.program_id = fd_spad_alloc( spad, alignof(pb_bytes_array_t), sizeof(fd_pubkey_t) );
1224 0 : sys_ctx.vm_ctx.return_data.program_id->size = sizeof(fd_pubkey_t);
1225 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) );
1226 :
1227 : /* SyscallContext -> vm_ctx -> sbpf_version */
1228 0 : sys_ctx.vm_ctx.sbpf_version = (uint)vm->sbpf_version;
1229 :
1230 : /* SyscallContext -> instr_ctx */
1231 0 : sys_ctx.has_instr_ctx = 1;
1232 0 : create_instr_context_protobuf_from_instructions( &sys_ctx.instr_ctx,
1233 0 : vm->instr_ctx->runtime,
1234 0 : vm->instr_ctx->bank,
1235 0 : vm->instr_ctx->txn_out,
1236 0 : vm->instr_ctx->instr,
1237 0 : spad );
1238 :
1239 : /* SyscallContext -> syscall_invocation */
1240 0 : sys_ctx.has_syscall_invocation = 1;
1241 :
1242 : /* SyscallContext -> syscall_invocation -> function_name */
1243 0 : sys_ctx.syscall_invocation.function_name.size = fd_uint_min( (uint) strlen(fn_name), sizeof(sys_ctx.syscall_invocation.function_name.bytes) );
1244 0 : fd_memcpy( sys_ctx.syscall_invocation.function_name.bytes,
1245 0 : fn_name,
1246 0 : sys_ctx.syscall_invocation.function_name.size );
1247 :
1248 : /* SyscallContext -> syscall_invocation -> heap_prefix */
1249 0 : sys_ctx.syscall_invocation.heap_prefix = fd_spad_alloc( spad, 8UL, PB_BYTES_ARRAY_T_ALLOCSIZE( vm->heap_max ) );
1250 0 : sys_ctx.syscall_invocation.heap_prefix->size = (pb_size_t) vm->instr_ctx->txn_out->details.compute_budget.heap_size;
1251 0 : fd_memcpy( sys_ctx.syscall_invocation.heap_prefix->bytes, vm->heap, vm->instr_ctx->txn_out->details.compute_budget.heap_size );
1252 :
1253 : /* SyscallContext -> syscall_invocation -> stack_prefix */
1254 0 : pb_size_t stack_sz = (pb_size_t)FD_VM_STACK_MAX;
1255 0 : sys_ctx.syscall_invocation.stack_prefix = fd_spad_alloc( spad, 8UL, PB_BYTES_ARRAY_T_ALLOCSIZE( stack_sz ) );
1256 0 : sys_ctx.syscall_invocation.stack_prefix->size = stack_sz;
1257 0 : fd_memcpy( sys_ctx.syscall_invocation.stack_prefix->bytes, vm->stack, stack_sz );
1258 :
1259 : /* Output to file */
1260 0 : ulong out_buf_size = 1UL<<29UL; /* 128 MB */
1261 0 : uint8_t * out = fd_spad_alloc( spad, alignof(uint8_t), out_buf_size );
1262 0 : pb_ostream_t stream = pb_ostream_from_buffer( out, out_buf_size );
1263 0 : if( pb_encode( &stream, FD_EXEC_TEST_SYSCALL_CONTEXT_FIELDS, &sys_ctx ) ) {
1264 0 : FILE * file = fopen(filename, "wb");
1265 0 : if( file ) {
1266 0 : fwrite( out, 1, stream.bytes_written, file );
1267 0 : fclose( file );
1268 0 : }
1269 0 : }
1270 0 : } FD_SPAD_FRAME_END;
1271 0 : }
1272 :
1273 : void
1274 : fd_dump_elf_to_protobuf( fd_runtime_t * runtime,
1275 : fd_bank_t * bank,
1276 : fd_txn_in_t const * txn_in,
1277 : fd_pubkey_t const * acc_pubkey,
1278 0 : fd_account_meta_t const * acc_meta ) {
1279 0 : fd_spad_t * spad = fd_spad_join( fd_spad_new( runtime->log.dumping_mem, 1UL<<28UL ) );
1280 :
1281 0 : FD_SPAD_FRAME_BEGIN( spad ) {
1282 :
1283 0 : fd_funk_txn_xid_t xid = { .ul = { fd_bank_slot_get( bank ), bank->data->idx } };
1284 :
1285 0 : fd_accdb_ro_t program_acc_ro[1];
1286 0 : fd_accdb_ro_init_nodb( program_acc_ro, acc_pubkey, acc_meta );
1287 :
1288 : /* Get the programdata for the account */
1289 0 : ulong program_data_off = 0UL;
1290 0 : ulong program_data_len = 0UL;
1291 0 : fd_accdb_ro_t * program_data =
1292 0 : fd_prog_load_elf( runtime->accdb, &xid, program_acc_ro, acc_pubkey, &program_data_off );
1293 0 : if( program_data==NULL ) {
1294 0 : return;
1295 0 : }
1296 :
1297 : /* Serialize the ELF to protobuf */
1298 0 : fd_ed25519_sig_t signature;
1299 0 : memcpy( signature, (uchar const *)txn_in->txn->payload + TXN( txn_in->txn )->signature_off, sizeof(fd_ed25519_sig_t) );
1300 0 : char encoded_signature[FD_BASE58_ENCODED_64_SZ];
1301 0 : fd_base58_encode_64( signature, NULL, encoded_signature );
1302 :
1303 0 : FD_BASE58_ENCODE_32_BYTES( acc_pubkey->uc, program_acc_b58 );
1304 0 : char filename[ PATH_MAX ];
1305 0 : snprintf( filename,
1306 0 : PATH_MAX,
1307 0 : "%s/elf-%s-%s-%lu.elfctx",
1308 0 : runtime->log.capture_ctx->dump_proto_output_dir,
1309 0 : encoded_signature,
1310 0 : program_acc_b58,
1311 0 : fd_bank_slot_get( bank ) );
1312 :
1313 : /* The generated filename should be unique for every call. Silently return otherwise. */
1314 0 : if( FD_UNLIKELY( access( filename, F_OK )!=-1 ) ) {
1315 0 : return;
1316 0 : }
1317 :
1318 0 : fd_exec_test_elf_loader_ctx_t elf_ctx = FD_EXEC_TEST_ELF_LOADER_CTX_INIT_ZERO;
1319 :
1320 : /* ElfLoaderCtx -> elf */
1321 0 : elf_ctx.has_elf = true;
1322 0 : elf_ctx.elf.data = fd_spad_alloc( spad, alignof(pb_bytes_array_t), PB_BYTES_ARRAY_T_ALLOCSIZE( program_data_len ) );
1323 0 : elf_ctx.elf.data->size = (pb_size_t)program_data_len;
1324 0 : fd_memcpy( elf_ctx.elf.data->bytes + program_data_off,
1325 0 : fd_accdb_ref_data_const( program_acc_ro ),
1326 0 : program_data_len );
1327 :
1328 : /* ElfLoaderCtx -> features */
1329 0 : elf_ctx.has_features = true;
1330 0 : dump_sorted_features( fd_bank_features_query( bank ), &elf_ctx.features, spad );
1331 :
1332 : /* ElfLoaderCtx -> deploy_checks
1333 : We hardcode this to true and rely the fuzzer to toggle this as it pleases */
1334 0 : elf_ctx.deploy_checks = true;
1335 :
1336 : /* Output to file */
1337 0 : ulong out_buf_size = 1UL<<29UL; /* 128 MB */
1338 0 : uint8_t * out = fd_spad_alloc( spad, alignof(uint8_t), out_buf_size );
1339 0 : pb_ostream_t stream = pb_ostream_from_buffer( out, out_buf_size );
1340 0 : if( pb_encode( &stream, FD_EXEC_TEST_ELF_LOADER_CTX_FIELDS, &elf_ctx ) ) {
1341 0 : FILE * file = fopen(filename, "wb");
1342 0 : if( file ) {
1343 0 : fwrite( out, 1, stream.bytes_written, file );
1344 0 : fclose( file );
1345 0 : }
1346 0 : }
1347 0 : } FD_SPAD_FRAME_END;
1348 0 : }
|