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