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