Line data Source code
1 : #include "fd_dump_pb.h"
2 : #include "harness/generated/block.pb.h"
3 : #include "harness/generated/invoke.pb.h"
4 : #include "harness/generated/txn.pb.h"
5 : #include "harness/generated/vm.pb.h"
6 : #include "../fd_system_ids.h"
7 : #include "../fd_runtime.h"
8 : #include "../program/fd_address_lookup_table_program.h"
9 : #include "../../../ballet/lthash/fd_lthash.h"
10 : #include "../../../ballet/nanopb/pb_encode.h"
11 : #include "../program/fd_program_cache.h"
12 :
13 :
14 : #include <errno.h>
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 : /***** UTILITY FUNCTIONS *****/
25 :
26 : /** GENERAL UTILITY FUNCTIONS AND MACROS **/
27 :
28 : static int
29 : is_builtin_account( fd_pubkey_t const * loaded_builtins,
30 : ulong num_loaded_builtins,
31 0 : fd_pubkey_t const * account_key ) {
32 0 : for( ulong j = 0; j < num_loaded_builtins; ++j ) {
33 0 : if( !memcmp( account_key, &loaded_builtins[j], sizeof(fd_pubkey_t) ) ) {
34 0 : return 1;
35 0 : }
36 0 : }
37 0 : return 0;
38 0 : }
39 :
40 : /** FEATURE DUMPING **/
41 : static void
42 : dump_sorted_features( fd_features_t const * features,
43 : fd_exec_test_feature_set_t * output_feature_set,
44 0 : fd_spad_t * spad ) {
45 : /* NOTE: Caller must have a spad frame prepared */
46 0 : uint64_t * unsorted_features = fd_spad_alloc( spad, alignof(uint64_t), FD_FEATURE_ID_CNT * sizeof(uint64_t) );
47 0 : ulong num_features = 0;
48 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 ) ) {
49 0 : if (features->f[current_feature->index] != FD_FEATURE_DISABLED) {
50 0 : unsorted_features[num_features++] = (uint64_t) current_feature->id.ul[0];
51 0 : }
52 0 : }
53 : // Sort the features
54 0 : void * scratch = fd_spad_alloc( spad, sort_uint64_t_stable_scratch_align(), sort_uint64_t_stable_scratch_footprint(num_features) );
55 0 : uint64_t * sorted_features = sort_uint64_t_stable_fast( unsorted_features, num_features, scratch );
56 :
57 : // Set feature set in message
58 0 : output_feature_set->features_count = (pb_size_t) num_features;
59 0 : output_feature_set->features = sorted_features;
60 0 : }
61 :
62 : /** ACCOUNT DUMPING **/
63 : static void
64 : dump_account_state( fd_txn_account_t const * txn_account,
65 : fd_exec_test_acct_state_t * output_account,
66 0 : fd_spad_t * spad ) {
67 : // Address
68 0 : fd_memcpy(output_account->address, txn_account->pubkey, sizeof(fd_pubkey_t));
69 :
70 : // Lamports
71 0 : output_account->lamports = (uint64_t) txn_account->vt->get_lamports( txn_account );
72 :
73 : // Data
74 0 : output_account->data = fd_spad_alloc( spad, alignof(pb_bytes_array_t), PB_BYTES_ARRAY_T_ALLOCSIZE( txn_account->vt->get_data_len( txn_account ) ) );
75 0 : output_account->data->size = (pb_size_t) txn_account->vt->get_data_len( txn_account );
76 0 : fd_memcpy(output_account->data->bytes, txn_account->vt->get_data( txn_account ), txn_account->vt->get_data_len( txn_account ) );
77 :
78 : // Executable
79 0 : output_account->executable = (bool) txn_account->vt->is_executable( txn_account );
80 :
81 : // Rent epoch
82 0 : output_account->rent_epoch = (uint64_t) txn_account->vt->get_rent_epoch( txn_account );
83 :
84 : // Owner
85 0 : fd_memcpy(output_account->owner, txn_account->vt->get_owner( txn_account ), sizeof(fd_pubkey_t));
86 :
87 : // Seed address (not present)
88 0 : output_account->has_seed_addr = false;
89 0 : }
90 :
91 : static uchar
92 : account_already_dumped( fd_exec_test_acct_state_t const * dumped_accounts,
93 : ulong dumped_cnt,
94 0 : fd_pubkey_t const * account_key ) {
95 0 : for( ulong i=0UL; i<dumped_cnt; i++ ) {
96 0 : if( !memcmp( account_key, dumped_accounts[i].address, sizeof(fd_pubkey_t) ) ) {
97 0 : return 1;
98 0 : }
99 0 : }
100 0 : return 0;
101 0 : }
102 :
103 : /* Dumps a borrowed account if it exists and has not been dumped yet. Sets up the output borrowed
104 : account if it exists. Returns 0 if the account exists, 1 otherwise. */
105 : static uchar
106 : dump_account_if_not_already_dumped( fd_funk_t const * funk,
107 : fd_funk_txn_t const * funk_txn,
108 : fd_pubkey_t const * account_key,
109 : fd_spad_t * spad,
110 : fd_exec_test_acct_state_t * out_acct_states,
111 : pb_size_t * out_acct_states_cnt,
112 0 : fd_txn_account_t * opt_out_borrowed_account ) {
113 0 : FD_TXN_ACCOUNT_DECL( account );
114 0 : if( fd_txn_account_init_from_funk_readonly( account, account_key, funk, funk_txn ) ) {
115 0 : return 1;
116 0 : }
117 :
118 0 : if( !account_already_dumped( out_acct_states, *out_acct_states_cnt, account_key ) ) {
119 0 : dump_account_state( account, &out_acct_states[*out_acct_states_cnt], spad );
120 0 : (*out_acct_states_cnt)++;
121 0 : }
122 :
123 0 : if( opt_out_borrowed_account ) {
124 0 : *opt_out_borrowed_account = *account;
125 0 : }
126 0 : return 0;
127 0 : }
128 :
129 : /* TODO: This can be made slightly more efficient by dumping only the referenced ALUT accounts instead of all accounts */
130 : static void
131 : dump_lut_account_and_contained_accounts( fd_exec_slot_ctx_t const * slot_ctx,
132 : uchar const * txn_payload,
133 : fd_txn_acct_addr_lut_t const * lookup_table,
134 : fd_spad_t * spad,
135 : fd_exec_test_acct_state_t * out_account_states,
136 0 : pb_size_t * out_account_states_count ) {
137 0 : FD_TXN_ACCOUNT_DECL( alut_account );
138 0 : fd_pubkey_t const * alut_pubkey = (fd_pubkey_t const *)((uchar *)txn_payload + lookup_table->addr_off);
139 0 : uchar account_exists = dump_account_if_not_already_dumped( slot_ctx->funk, slot_ctx->funk_txn, alut_pubkey, spad, out_account_states, out_account_states_count, alut_account );
140 0 : if( !account_exists || alut_account->vt->get_data_len( alut_account )<FD_LOOKUP_TABLE_META_SIZE ) {
141 0 : return;
142 0 : }
143 :
144 : /* Decode the ALUT account and find its referenced writable and readonly indices */
145 0 : if( alut_account->vt->get_data_len( alut_account ) & 0x1fUL ) {
146 0 : return;
147 0 : }
148 :
149 0 : fd_pubkey_t * lookup_addrs = (fd_pubkey_t *)&alut_account->vt->get_data( alut_account )[FD_LOOKUP_TABLE_META_SIZE];
150 0 : ulong lookup_addrs_cnt = ( alut_account->vt->get_data_len( alut_account ) - FD_LOOKUP_TABLE_META_SIZE ) >> 5UL; // = (dlen - 56) / 32
151 0 : for( ulong i=0UL; i<lookup_addrs_cnt; i++ ) {
152 0 : fd_pubkey_t const * referenced_pubkey = &lookup_addrs[i];
153 0 : dump_account_if_not_already_dumped( slot_ctx->funk, slot_ctx->funk_txn, referenced_pubkey, spad, out_account_states, out_account_states_count, NULL );
154 0 : }
155 0 : }
156 :
157 : static void
158 : dump_executable_account_if_exists( fd_funk_t const * funk,
159 : fd_funk_txn_t const * funk_txn,
160 : fd_exec_test_acct_state_t const * program_account,
161 : fd_spad_t * spad,
162 : fd_exec_test_acct_state_t * out_account_states,
163 0 : pb_size_t * out_account_states_count ) {
164 0 : if( FD_LIKELY( memcmp( program_account->owner, fd_solana_bpf_loader_upgradeable_program_id.key, sizeof(fd_pubkey_t) ) ) ) {
165 0 : return;
166 0 : }
167 :
168 0 : int err;
169 0 : fd_bpf_upgradeable_loader_state_t * program_loader_state = fd_bincode_decode_spad(
170 0 : bpf_upgradeable_loader_state,
171 0 : spad,
172 0 : program_account->data->bytes,
173 0 : program_account->data->size,
174 0 : &err );
175 0 : if( FD_UNLIKELY( err ) ) return;
176 :
177 0 : if( !fd_bpf_upgradeable_loader_state_is_program( program_loader_state ) ) {
178 0 : return;
179 0 : }
180 :
181 0 : fd_pubkey_t * programdata_acc = &program_loader_state->inner.program.programdata_address;
182 0 : dump_account_if_not_already_dumped( funk, funk_txn, programdata_acc, spad, out_account_states, out_account_states_count, NULL );
183 0 : }
184 :
185 : /** VOTE ACCOUNTS DUMPING **/
186 : static void
187 : dump_vote_accounts( fd_exec_slot_ctx_t const * slot_ctx,
188 : fd_vote_accounts_global_t const * vote_accounts,
189 : fd_spad_t * spad,
190 : fd_exec_test_vote_account_t ** out_vote_accounts,
191 : pb_size_t * out_vote_accounts_count,
192 : fd_exec_test_acct_state_t * out_acct_states,
193 0 : pb_size_t * out_acct_states_cnt ) {
194 :
195 0 : fd_vote_accounts_pair_global_t_mapnode_t * vote_accounts_pool = fd_vote_accounts_vote_accounts_pool_join( vote_accounts );
196 0 : fd_vote_accounts_pair_global_t_mapnode_t * vote_accounts_root = fd_vote_accounts_vote_accounts_root_join( vote_accounts );
197 :
198 0 : pb_size_t idx = 0UL;
199 0 : ulong vote_account_t_cnt = fd_vote_accounts_pair_global_t_map_size( vote_accounts_pool,
200 0 : vote_accounts_root );
201 0 : fd_exec_test_vote_account_t * vote_account_out = fd_spad_alloc( spad,
202 0 : alignof(fd_exec_test_vote_account_t),
203 0 : vote_account_t_cnt * sizeof(fd_exec_test_vote_account_t) );
204 :
205 0 : for( fd_vote_accounts_pair_global_t_mapnode_t const * curr = fd_vote_accounts_pair_global_t_map_minimum_const(
206 0 : vote_accounts_pool,
207 0 : vote_accounts_root );
208 0 : curr;
209 0 : curr = fd_vote_accounts_pair_global_t_map_successor_const( vote_accounts_pool, curr ) ) {
210 0 : fd_exec_test_vote_account_t * vote_out = &vote_account_out[idx++];
211 :
212 0 : vote_out->has_vote_account = true;
213 0 : vote_out->stake = curr->elem.stake;
214 0 : vote_out->vote_account.lamports = curr->elem.value.lamports;
215 0 : vote_out->vote_account.rent_epoch = curr->elem.value.rent_epoch;
216 0 : vote_out->vote_account.executable = curr->elem.value.executable;
217 0 : vote_out->vote_account.has_seed_addr = false;
218 :
219 0 : fd_memcpy( &vote_out->vote_account.address, &curr->elem.key, sizeof(fd_pubkey_t) );
220 0 : fd_memcpy( &vote_out->vote_account.owner, &curr->elem.value.owner, sizeof(fd_pubkey_t) );
221 :
222 0 : vote_out->vote_account.data = fd_spad_alloc( spad, alignof(pb_bytes_array_t), PB_BYTES_ARRAY_T_ALLOCSIZE( curr->elem.value.data_len ) );
223 0 : vote_out->vote_account.data->size = (pb_size_t) curr->elem.value.data_len;
224 :
225 0 : uchar * data = fd_solana_account_data_join( &curr->elem.value );
226 0 : fd_memcpy( &vote_out->vote_account.data->bytes, data, curr->elem.value.data_len );
227 :
228 : // Dump the vote account
229 0 : dump_account_if_not_already_dumped( slot_ctx->funk, slot_ctx->funk_txn, &curr->elem.key, spad, out_acct_states, out_acct_states_cnt, NULL );
230 0 : }
231 :
232 0 : *out_vote_accounts = vote_account_out;
233 0 : *out_vote_accounts_count = idx;
234 0 : }
235 :
236 : /** TRANSACTION DUMPING **/
237 :
238 : static void
239 : dump_sanitized_transaction( fd_funk_t * funk,
240 : fd_funk_txn_t const * funk_txn,
241 : fd_txn_t const * txn_descriptor,
242 : uchar const * txn_payload,
243 : fd_spad_t * spad,
244 0 : fd_exec_test_sanitized_transaction_t * sanitized_transaction ) {
245 0 : fd_txn_acct_addr_lut_t const * address_lookup_tables = fd_txn_get_address_tables_const( txn_descriptor );
246 :
247 : /* Transaction Context -> tx -> message */
248 0 : sanitized_transaction->has_message = true;
249 0 : fd_exec_test_transaction_message_t * message = &sanitized_transaction->message;
250 :
251 : /* Transaction Context -> tx -> message -> is_legacy */
252 0 : message->is_legacy = txn_descriptor->transaction_version == FD_TXN_VLEGACY;
253 :
254 : /* Transaction Context -> tx -> message -> header */
255 0 : message->has_header = true;
256 0 : fd_exec_test_message_header_t * header = &message->header;
257 :
258 : /* Transaction Context -> tx -> message -> header -> num_required_signatures */
259 0 : header->num_required_signatures = txn_descriptor->signature_cnt;
260 :
261 : /* Transaction Context -> tx -> message -> header -> num_readonly_signed_accounts */
262 0 : header->num_readonly_signed_accounts = txn_descriptor->readonly_signed_cnt;
263 :
264 : /* Transaction Context -> tx -> message -> header -> num_readonly_unsigned_accounts */
265 0 : header->num_readonly_unsigned_accounts = txn_descriptor->readonly_unsigned_cnt;
266 :
267 : /* Transaction Context -> tx -> message -> account_keys */
268 0 : message->account_keys_count = txn_descriptor->acct_addr_cnt;
269 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 *)) );
270 0 : fd_acct_addr_t const * account_keys = fd_txn_get_acct_addrs( txn_descriptor, txn_payload );
271 0 : for( ulong i = 0; i < txn_descriptor->acct_addr_cnt; i++ ) {
272 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)) );
273 0 : account_key->size = sizeof(fd_pubkey_t);
274 0 : memcpy( account_key->bytes, &account_keys[i], sizeof(fd_pubkey_t) );
275 0 : message->account_keys[i] = account_key;
276 0 : }
277 :
278 : /* Transaction Context -> tx -> message -> recent_blockhash */
279 0 : uchar const * recent_blockhash = fd_txn_get_recent_blockhash( txn_descriptor, txn_payload );
280 0 : message->recent_blockhash = fd_spad_alloc( spad, alignof(pb_bytes_array_t), PB_BYTES_ARRAY_T_ALLOCSIZE(sizeof(fd_hash_t)) );
281 0 : message->recent_blockhash->size = sizeof(fd_hash_t);
282 0 : memcpy( message->recent_blockhash->bytes, recent_blockhash, sizeof(fd_hash_t) );
283 :
284 : /* Transaction Context -> tx -> message -> instructions */
285 0 : message->instructions_count = txn_descriptor->instr_cnt;
286 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) );
287 0 : for( ulong i = 0; i < txn_descriptor->instr_cnt; ++i ) {
288 0 : fd_txn_instr_t instr = txn_descriptor->instr[i];
289 0 : fd_exec_test_compiled_instruction_t * compiled_instruction = &message->instructions[i];
290 :
291 : // compiled instruction -> program_id_index
292 0 : compiled_instruction->program_id_index = instr.program_id;
293 :
294 : // compiled instruction -> accounts
295 0 : compiled_instruction->accounts_count = instr.acct_cnt;
296 0 : compiled_instruction->accounts = fd_spad_alloc( spad, alignof(uint32_t), instr.acct_cnt * sizeof(uint32_t) );
297 0 : uchar const * instr_accounts = fd_txn_get_instr_accts( &instr, txn_payload );
298 0 : for( ulong j = 0; j < instr.acct_cnt; ++j ) {
299 0 : uchar instr_acct_index = instr_accounts[j];
300 0 : compiled_instruction->accounts[j] = instr_acct_index;
301 0 : }
302 :
303 : // compiled instruction -> data
304 0 : uchar const * instr_data = fd_txn_get_instr_data( &instr, txn_payload );
305 0 : compiled_instruction->data = fd_spad_alloc( spad, alignof(pb_bytes_array_t), PB_BYTES_ARRAY_T_ALLOCSIZE(instr.data_sz) );
306 0 : compiled_instruction->data->size = instr.data_sz;
307 0 : memcpy( compiled_instruction->data->bytes, instr_data, instr.data_sz );
308 0 : }
309 :
310 : /* ALUT stuff (non-legacy) */
311 0 : message->address_table_lookups_count = 0;
312 0 : if( !message->is_legacy ) {
313 : /* Transaction Context -> tx -> message -> address_table_lookups */
314 0 : message->address_table_lookups_count = txn_descriptor->addr_table_lookup_cnt;
315 0 : message->address_table_lookups = fd_spad_alloc( spad,
316 0 : alignof(fd_exec_test_message_address_table_lookup_t),
317 0 : txn_descriptor->addr_table_lookup_cnt * sizeof(fd_exec_test_message_address_table_lookup_t) );
318 0 : for( ulong i = 0; i < txn_descriptor->addr_table_lookup_cnt; ++i ) {
319 : // alut -> account_key
320 0 : fd_pubkey_t * alut_key = (fd_pubkey_t *) (txn_payload + address_lookup_tables[i].addr_off);
321 0 : memcpy( message->address_table_lookups[i].account_key, alut_key, sizeof(fd_pubkey_t) );
322 :
323 : // Access ALUT account data to access its keys
324 0 : FD_TXN_ACCOUNT_DECL(addr_lut_rec);
325 0 : int err = fd_txn_account_init_from_funk_readonly( addr_lut_rec, alut_key, funk, funk_txn );
326 0 : if( FD_UNLIKELY( err != FD_ACC_MGR_SUCCESS ) ) {
327 0 : FD_LOG_ERR(( "addr lut not found" ));
328 0 : }
329 :
330 : // alut -> writable_indexes
331 0 : message->address_table_lookups[i].writable_indexes_count = address_lookup_tables[i].writable_cnt;
332 0 : message->address_table_lookups[i].writable_indexes = fd_spad_alloc( spad, alignof(uint32_t), address_lookup_tables[i].writable_cnt * sizeof(uint32_t) );
333 0 : uchar * writable_indexes = (uchar *) (txn_payload + address_lookup_tables[i].writable_off);
334 0 : for( ulong j = 0; j < address_lookup_tables[i].writable_cnt; ++j ) {
335 0 : message->address_table_lookups[i].writable_indexes[j] = writable_indexes[j];
336 0 : }
337 :
338 : // alut -> readonly_indexes
339 0 : message->address_table_lookups[i].readonly_indexes_count = address_lookup_tables[i].readonly_cnt;
340 0 : message->address_table_lookups[i].readonly_indexes = fd_spad_alloc( spad, alignof(uint32_t), address_lookup_tables[i].readonly_cnt * sizeof(uint32_t) );
341 0 : uchar * readonly_indexes = (uchar *) (txn_payload + address_lookup_tables[i].readonly_off);
342 0 : for( ulong j = 0; j < address_lookup_tables[i].readonly_cnt; ++j ) {
343 0 : message->address_table_lookups[i].readonly_indexes[j] = readonly_indexes[j];
344 0 : }
345 0 : }
346 0 : }
347 :
348 : /* Transaction Context -> tx -> message_hash */
349 : // Skip because it does not matter what's in here
350 :
351 : /* Transaction Context -> tx -> signatures */
352 0 : sanitized_transaction->signatures_count = txn_descriptor->signature_cnt;
353 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 *)) );
354 0 : fd_ed25519_sig_t const * signatures = fd_txn_get_signatures( txn_descriptor, txn_payload );
355 0 : for( uchar i = 0; i < txn_descriptor->signature_cnt; ++i ) {
356 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)) );
357 0 : signature->size = sizeof(fd_ed25519_sig_t);
358 0 : memcpy( signature->bytes, &signatures[i], sizeof(fd_ed25519_sig_t) );
359 0 : sanitized_transaction->signatures[i] = signature;
360 0 : }
361 0 : }
362 :
363 : /** BLOCKHASH QUEUE DUMPING **/
364 :
365 : static void
366 : dump_blockhash_queue( fd_blockhashes_t const * queue,
367 : fd_spad_t * spad,
368 : pb_bytes_array_t ** output_blockhash_queue,
369 0 : pb_size_t * output_blockhash_queue_count ) {
370 0 : pb_size_t cnt = 0;
371 :
372 : // Iterate over all block hashes in the queue and save them in the output
373 0 : for( fd_blockhash_deq_iter_t iter=fd_blockhash_deq_iter_init_rev( queue->d.deque );
374 0 : !fd_blockhash_deq_iter_done_rev( queue->d.deque, iter );
375 0 : iter=fd_blockhash_deq_iter_prev( queue->d.deque, iter ) ) {
376 0 : fd_blockhash_info_t const * ele = fd_blockhash_deq_iter_ele_const( queue->d.deque, iter );
377 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)) );
378 0 : output_blockhash->size = sizeof(fd_hash_t);
379 0 : fd_memcpy( output_blockhash->bytes, &ele->hash, sizeof(fd_hash_t) );
380 0 : output_blockhash_queue[ cnt ] = output_blockhash;
381 0 : cnt++;
382 0 : }
383 :
384 0 : *output_blockhash_queue_count = cnt;
385 0 : }
386 :
387 : /** SECONDARY FUNCTIONS **/
388 :
389 : static void
390 : create_block_context_protobuf_from_block( fd_exec_test_block_context_t * block_context,
391 : fd_exec_slot_ctx_t const * slot_ctx,
392 0 : fd_spad_t * spad ) {
393 :
394 : /* BlockContext -> acct_states */
395 : // Dump sysvars + builtins
396 0 : fd_pubkey_t const fd_relevant_sysvar_ids[] = {
397 0 : fd_sysvar_recent_block_hashes_id,
398 0 : fd_sysvar_clock_id,
399 0 : fd_sysvar_slot_history_id,
400 0 : fd_sysvar_slot_hashes_id,
401 0 : fd_sysvar_epoch_schedule_id,
402 0 : fd_sysvar_epoch_rewards_id,
403 0 : fd_sysvar_fees_id,
404 0 : fd_sysvar_rent_id,
405 0 : fd_sysvar_stake_history_id,
406 0 : fd_sysvar_last_restart_slot_id,
407 0 : };
408 :
409 0 : fd_pubkey_t const loaded_builtins[] = {
410 0 : fd_solana_system_program_id,
411 0 : fd_solana_vote_program_id,
412 0 : fd_solana_stake_program_id,
413 0 : fd_solana_config_program_id,
414 0 : fd_solana_zk_token_proof_program_id,
415 0 : fd_solana_bpf_loader_v4_program_id,
416 0 : fd_solana_address_lookup_table_program_id,
417 0 : fd_solana_bpf_loader_deprecated_program_id,
418 0 : fd_solana_bpf_loader_program_id,
419 0 : fd_solana_bpf_loader_upgradeable_program_id,
420 0 : fd_solana_compute_budget_program_id,
421 0 : fd_solana_keccak_secp_256k_program_id,
422 0 : fd_solana_secp256r1_program_id,
423 0 : fd_solana_zk_elgamal_proof_program_id,
424 0 : fd_solana_ed25519_sig_verify_program_id,
425 0 : };
426 0 : ulong num_sysvar_entries = (sizeof(fd_relevant_sysvar_ids) / sizeof(fd_pubkey_t));
427 0 : ulong num_loaded_builtins = (sizeof(loaded_builtins) / sizeof(fd_pubkey_t));
428 :
429 0 : fd_account_keys_global_t const * stake_account_keys = fd_bank_stake_account_keys_locking_query( slot_ctx->bank );
430 0 : fd_account_keys_pair_t_mapnode_t * stake_account_keys_pool = fd_account_keys_account_keys_pool_join( stake_account_keys );
431 0 : fd_account_keys_pair_t_mapnode_t * stake_account_keys_root = fd_account_keys_account_keys_root_join( stake_account_keys );
432 :
433 :
434 0 : fd_stakes_global_t const * stakes = fd_bank_stakes_locking_query( slot_ctx->bank );
435 0 : fd_delegation_pair_t_mapnode_t * stake_delegations_pool = fd_stakes_stake_delegations_pool_join( stakes );
436 0 : fd_delegation_pair_t_mapnode_t * stake_delegations_root = fd_stakes_stake_delegations_root_join( stakes );
437 :
438 0 : fd_vote_accounts_pair_global_t_mapnode_t * stakes_vote_accounts_pool = fd_vote_accounts_vote_accounts_pool_join( &stakes->vote_accounts );
439 0 : fd_vote_accounts_pair_global_t_mapnode_t * stakes_vote_accounts_root = fd_vote_accounts_vote_accounts_root_join( &stakes->vote_accounts );
440 :
441 0 : ulong new_stake_account_cnt = fd_account_keys_pair_t_map_size( stake_account_keys_pool, stake_account_keys_root );
442 0 : ulong stake_account_cnt = fd_delegation_pair_t_map_size( stake_delegations_pool,
443 0 : stake_delegations_root );
444 :
445 0 : ulong vote_account_t_cnt = fd_vote_accounts_pair_global_t_map_size( stakes_vote_accounts_pool,
446 0 : stakes_vote_accounts_root );
447 :
448 0 : fd_bank_stake_account_keys_end_locking_query( slot_ctx->bank );
449 0 : fd_bank_stakes_end_locking_query( slot_ctx->bank );
450 :
451 :
452 0 : fd_vote_accounts_global_t const * next_epoch_stakes = fd_bank_next_epoch_stakes_locking_query( slot_ctx->bank );
453 0 : fd_vote_accounts_pair_global_t_mapnode_t * next_epoch_stakes_pool = fd_vote_accounts_vote_accounts_pool_join( next_epoch_stakes );
454 0 : fd_vote_accounts_pair_global_t_mapnode_t * next_epoch_stakes_root = fd_vote_accounts_vote_accounts_root_join( next_epoch_stakes );
455 0 : ulong vote_account_t_1_cnt = fd_vote_accounts_pair_global_t_map_size( next_epoch_stakes_pool,
456 0 : next_epoch_stakes_root );
457 0 : fd_bank_next_epoch_stakes_end_locking_query( slot_ctx->bank );
458 :
459 0 : fd_vote_accounts_global_t const * epoch_stakes = fd_bank_epoch_stakes_locking_query( slot_ctx->bank );
460 0 : fd_vote_accounts_pair_global_t_mapnode_t * epoch_stakes_pool = fd_vote_accounts_vote_accounts_pool_join( epoch_stakes );
461 0 : fd_vote_accounts_pair_global_t_mapnode_t * epoch_stakes_root = fd_vote_accounts_vote_accounts_root_join( epoch_stakes );
462 0 : ulong vote_account_t_2_cnt = fd_vote_accounts_pair_global_t_map_size( epoch_stakes_pool,
463 0 : epoch_stakes_root );
464 0 : fd_bank_epoch_stakes_end_locking_query( slot_ctx->bank );
465 :
466 0 : ulong total_num_accounts = num_sysvar_entries +
467 0 : num_loaded_builtins +
468 0 : new_stake_account_cnt +
469 0 : stake_account_cnt +
470 0 : stake_account_cnt +
471 0 : vote_account_t_cnt +
472 0 : vote_account_t_1_cnt +
473 0 : vote_account_t_2_cnt;
474 :
475 0 : block_context->acct_states_count = 0;
476 0 : block_context->acct_states = fd_spad_alloc( spad,
477 0 : alignof(fd_exec_test_acct_state_t),
478 0 : total_num_accounts * sizeof(fd_exec_test_acct_state_t) );
479 :
480 :
481 0 : for( ulong i=0UL; i<num_sysvar_entries; i++ ) {
482 0 : dump_account_if_not_already_dumped( slot_ctx->funk, slot_ctx->funk_txn, &fd_relevant_sysvar_ids[i], spad, block_context->acct_states, &block_context->acct_states_count, NULL );
483 0 : }
484 :
485 0 : for( ulong i=0UL; i<num_loaded_builtins; i++ ) {
486 0 : dump_account_if_not_already_dumped( slot_ctx->funk, slot_ctx->funk_txn, &loaded_builtins[i], spad, block_context->acct_states, &block_context->acct_states_count, NULL );
487 0 : }
488 :
489 : /* BlockContext -> blockhash_queue */
490 0 : pb_bytes_array_t ** output_blockhash_queue = fd_spad_alloc( spad,
491 0 : alignof(pb_bytes_array_t *),
492 0 : PB_BYTES_ARRAY_T_ALLOCSIZE((FD_BLOCKHASHES_MAX) * sizeof(pb_bytes_array_t *)) );
493 0 : block_context->blockhash_queue = output_blockhash_queue;
494 :
495 0 : fd_blockhashes_t const * bhq = fd_bank_block_hash_queue_query( slot_ctx->bank );
496 0 : dump_blockhash_queue( bhq, spad, block_context->blockhash_queue, &block_context->blockhash_queue_count );
497 :
498 : /* BlockContext -> SlotContext */
499 0 : block_context->has_slot_ctx = true;
500 0 : block_context->slot_ctx.slot = fd_bank_slot_get( slot_ctx->bank );
501 : // HACK FOR NOW: block height gets incremented in process_new_epoch, so we should dump block height + 1
502 0 : block_context->slot_ctx.block_height = fd_bank_block_height_get( slot_ctx->bank ) + 1UL;
503 : // fd_memcpy( block_context->slot_ctx.poh, &slot_ctx->slot_bank.poh, sizeof(fd_pubkey_t) ); // TODO: dump here when process epoch happens after poh verification
504 0 : fd_memcpy( block_context->slot_ctx.parent_bank_hash, fd_bank_bank_hash_query( slot_ctx->bank ), sizeof(fd_pubkey_t) );
505 0 : block_context->slot_ctx.prev_slot = fd_bank_parent_slot_get( slot_ctx->bank );
506 0 : block_context->slot_ctx.prev_lps = fd_bank_prev_lamports_per_signature_get( slot_ctx->bank );
507 0 : block_context->slot_ctx.prev_epoch_capitalization = fd_bank_capitalization_get( slot_ctx->bank );
508 :
509 : /* BlockContext -> EpochContext */
510 0 : block_context->has_epoch_ctx = true;
511 0 : block_context->epoch_ctx.has_features = true;
512 0 : dump_sorted_features( fd_bank_features_query( slot_ctx->bank ), &block_context->epoch_ctx.features, spad );
513 0 : block_context->epoch_ctx.hashes_per_tick = fd_bank_hashes_per_tick_get( slot_ctx->bank );
514 0 : block_context->epoch_ctx.ticks_per_slot = fd_bank_ticks_per_slot_get( slot_ctx->bank );
515 0 : block_context->epoch_ctx.slots_per_year = fd_bank_slots_per_year_get( slot_ctx->bank );
516 0 : block_context->epoch_ctx.has_inflation = true;
517 :
518 0 : fd_inflation_t const * inflation = fd_bank_inflation_query( slot_ctx->bank );
519 0 : block_context->epoch_ctx.inflation = (fd_exec_test_inflation_t) {
520 0 : .initial = inflation->initial,
521 0 : .terminal = inflation->terminal,
522 0 : .taper = inflation->taper,
523 0 : .foundation = inflation->foundation,
524 0 : .foundation_term = inflation->foundation_term,
525 0 : };
526 0 : block_context->epoch_ctx.genesis_creation_time = fd_bank_genesis_creation_time_get( slot_ctx->bank );
527 :
528 : /* Dumping stake accounts for this epoch */
529 :
530 0 : stakes = fd_bank_stakes_locking_query( slot_ctx->bank );
531 0 : stake_delegations_pool = fd_stakes_stake_delegations_pool_join( stakes );
532 0 : stake_delegations_root = fd_stakes_stake_delegations_root_join( stakes );
533 :
534 : /* Dumping all existing stake accounts */
535 0 : for( fd_delegation_pair_t_mapnode_t const * curr = fd_delegation_pair_t_map_minimum_const(
536 0 : stake_delegations_pool,
537 0 : stake_delegations_root );
538 0 : curr;
539 0 : curr = fd_delegation_pair_t_map_successor_const( stake_delegations_pool, curr ) ) {
540 0 : dump_account_if_not_already_dumped( slot_ctx->funk, slot_ctx->funk_txn, &curr->elem.account, spad, block_context->acct_states, &block_context->acct_states_count, NULL );
541 0 : }
542 :
543 0 : fd_bank_stakes_end_locking_query( slot_ctx->bank );
544 :
545 0 : stake_account_keys = fd_bank_stake_account_keys_locking_query( slot_ctx->bank );
546 0 : stake_account_keys_pool = fd_account_keys_account_keys_pool_join( stake_account_keys );
547 0 : stake_account_keys_root = fd_account_keys_account_keys_root_join( stake_account_keys );
548 :
549 : /* Dump all new stake accounts */
550 0 : for( fd_account_keys_pair_t_mapnode_t const * curr = fd_account_keys_pair_t_map_minimum_const(
551 0 : stake_account_keys_pool,
552 0 : stake_account_keys_root );
553 0 : curr;
554 0 : curr = fd_account_keys_pair_t_map_successor_const( stake_account_keys_pool, curr ) ) {
555 0 : dump_account_if_not_already_dumped( slot_ctx->funk, slot_ctx->funk_txn, &curr->elem.key, spad, block_context->acct_states, &block_context->acct_states_count, NULL );
556 0 : }
557 :
558 0 : fd_bank_stake_account_keys_end_locking_query( slot_ctx->bank );
559 :
560 : /* Dumping vote accounts for this epoch */
561 :
562 0 : stakes = fd_bank_stakes_locking_query( slot_ctx->bank );
563 0 : fd_vote_accounts_pair_global_t_mapnode_t * vote_accounts_pool = fd_vote_accounts_vote_accounts_pool_join( &stakes->vote_accounts );
564 0 : fd_vote_accounts_pair_global_t_mapnode_t * vote_accounts_root = fd_vote_accounts_vote_accounts_root_join( &stakes->vote_accounts );
565 :
566 : /* Dump all existing vote accounts */
567 0 : for( fd_vote_accounts_pair_global_t_mapnode_t const * curr = fd_vote_accounts_pair_global_t_map_minimum_const(
568 0 : vote_accounts_pool,
569 0 : vote_accounts_root );
570 0 : curr;
571 0 : curr = fd_vote_accounts_pair_global_t_map_successor_const( vote_accounts_pool, curr ) ) {
572 0 : dump_account_if_not_already_dumped( slot_ctx->funk, slot_ctx->funk_txn, &curr->elem.key, spad, block_context->acct_states, &block_context->acct_states_count, NULL );
573 0 : }
574 :
575 0 : fd_bank_stakes_end_locking_query( slot_ctx->bank );
576 :
577 0 : fd_account_keys_global_t const * vote_account_keys = fd_bank_vote_account_keys_locking_query( slot_ctx->bank );
578 0 : fd_account_keys_pair_t_mapnode_t * vote_account_keys_pool = fd_account_keys_account_keys_pool_join( vote_account_keys );
579 0 : fd_account_keys_pair_t_mapnode_t * vote_account_keys_root = fd_account_keys_account_keys_root_join( vote_account_keys );
580 :
581 :
582 : /* Dump all new vote accounts */
583 0 : for( fd_account_keys_pair_t_mapnode_t const * curr = fd_account_keys_pair_t_map_minimum_const(
584 0 : vote_account_keys_pool,
585 0 : vote_account_keys_root );
586 0 : curr;
587 0 : curr = fd_account_keys_pair_t_map_successor_const( vote_account_keys_pool, curr ) ) {
588 0 : dump_account_if_not_already_dumped( slot_ctx->funk, slot_ctx->funk_txn, &curr->elem.key, spad, block_context->acct_states, &block_context->acct_states_count, NULL );
589 0 : }
590 :
591 0 : fd_bank_vote_account_keys_end_locking_query( slot_ctx->bank );
592 :
593 : // BlockContext -> EpochContext -> vote_accounts_t_1 (vote accounts at epoch T-1)
594 0 : fd_vote_accounts_global_t const * next_epoch_stakes_vaccs = fd_bank_next_epoch_stakes_locking_query( slot_ctx->bank );
595 0 : dump_vote_accounts( slot_ctx,
596 0 : next_epoch_stakes_vaccs,
597 0 : spad,
598 0 : &block_context->epoch_ctx.vote_accounts_t_1,
599 0 : &block_context->epoch_ctx.vote_accounts_t_1_count,
600 0 : block_context->acct_states,
601 0 : &block_context->acct_states_count );
602 :
603 : // BlockContext -> EpochContext -> vote_accounts_t_2 (vote accounts at epoch T-2)
604 0 : fd_vote_accounts_global_t const * epoch_stakes_vaccs = fd_bank_epoch_stakes_locking_query( slot_ctx->bank );
605 0 : dump_vote_accounts( slot_ctx,
606 0 : epoch_stakes_vaccs,
607 0 : spad,
608 0 : &block_context->epoch_ctx.vote_accounts_t_2,
609 0 : &block_context->epoch_ctx.vote_accounts_t_2_count,
610 0 : block_context->acct_states,
611 0 : &block_context->acct_states_count );
612 0 : fd_bank_epoch_stakes_end_locking_query( slot_ctx->bank );
613 0 : }
614 :
615 : static void
616 : create_block_context_protobuf_from_block_tx_only( fd_exec_test_block_context_t * block_context,
617 : fd_runtime_block_info_t const * block_info,
618 : fd_exec_slot_ctx_t const * slot_ctx,
619 0 : fd_spad_t * spad ) {
620 : /* BlockContext -> txns */
621 0 : block_context->txns_count = 0U;
622 0 : block_context->txns = fd_spad_alloc( spad, alignof(fd_exec_test_sanitized_transaction_t), block_info->txn_cnt * sizeof(fd_exec_test_sanitized_transaction_t) );
623 0 : fd_memset( block_context->txns, 0, block_info->txn_cnt * sizeof(fd_exec_test_sanitized_transaction_t) );
624 :
625 : /* BlockContext -> acct_states
626 : Allocate additional space for the remaining accounts */
627 0 : fd_exec_test_acct_state_t * current_accounts = block_context->acct_states;
628 0 : block_context->acct_states = fd_spad_alloc( spad,
629 0 : alignof(fd_exec_test_acct_state_t),
630 0 : ( ( block_info->txn_cnt * 128UL ) + (ulong)block_context->acct_states_count ) *
631 0 : sizeof(fd_exec_test_acct_state_t) );
632 0 : fd_memcpy( block_context->acct_states, current_accounts, block_context->acct_states_count * sizeof(fd_exec_test_acct_state_t) );
633 :
634 : /* BlockContext -> slot_ctx -> poh
635 : This currently needs to be done because POH verification is done after epoch boundary processing. That should probably be changed */
636 0 : fd_memcpy( block_context->slot_ctx.poh, fd_bank_poh_query( slot_ctx->bank )->hash, sizeof(fd_pubkey_t) );
637 :
638 : /* When iterating over microblocks batches and microblocks, we flatten the batches for the output block context (essentially just one big batch with several microblocks) */
639 0 : for( ulong i=0UL; i<block_info->microblock_batch_cnt; i++ ) {
640 0 : fd_microblock_batch_info_t const * microblock_batch = &block_info->microblock_batch_infos[i];
641 :
642 0 : for( ulong j=0UL; j<microblock_batch->microblock_cnt; j++ ) {
643 0 : fd_microblock_info_t const * microblock_info = µblock_batch->microblock_infos[j];
644 0 : ulong txn_cnt = microblock_info->microblock.hdr->txn_cnt;
645 0 : if (txn_cnt==0UL) continue;
646 :
647 : /* BlockContext -> txns */
648 0 : for( ulong k=0UL; k<txn_cnt; k++ ) {
649 0 : fd_txn_p_t const * txn_ptr = µblock_info->txns[k];
650 0 : fd_txn_t const * txn_descriptor = TXN( txn_ptr );
651 0 : dump_sanitized_transaction( slot_ctx->funk, slot_ctx->funk_txn, txn_descriptor, txn_ptr->payload, spad, &block_context->txns[block_context->txns_count++] );
652 :
653 : /* BlockContext -> acct_states */
654 : /* Dump account + alut + programdata accounts (if applicable). There's a lot more brute force work since none of the borrowed accounts are set up yet. We have to:
655 : 1. Dump the raw txn account keys
656 : 2. Dump the ALUT accounts
657 : 3. Dump all referenced accounts in the ALUTs
658 : 4. Dump any executable accounts
659 : 5. Dump any sysvars + builtin accounts (occurs outside of this loop) */
660 :
661 : // 1. Dump any account keys that are referenced by transactions
662 0 : fd_acct_addr_t const * account_keys = fd_txn_get_acct_addrs( txn_descriptor, txn_ptr->payload );
663 0 : for( ushort l=0; l<txn_descriptor->acct_addr_cnt; l++ ) {
664 0 : fd_pubkey_t const * account_key = fd_type_pun_const( &account_keys[l] );
665 0 : dump_account_if_not_already_dumped( slot_ctx->funk, slot_ctx->funk_txn, account_key, spad, block_context->acct_states, &block_context->acct_states_count, NULL );
666 0 : }
667 :
668 : // 2 + 3. Dump any ALUT accounts + any accounts referenced in the ALUTs
669 0 : fd_txn_acct_addr_lut_t const * txn_lookup_tables = fd_txn_get_address_tables_const( txn_descriptor );
670 0 : for( ushort l=0; l<txn_descriptor->addr_table_lookup_cnt; l++ ) {
671 0 : fd_txn_acct_addr_lut_t const * lookup_table = &txn_lookup_tables[l];
672 0 : dump_lut_account_and_contained_accounts( slot_ctx, txn_ptr->payload, lookup_table, spad, block_context->acct_states, &block_context->acct_states_count );
673 0 : }
674 :
675 : // 4. Go through all dumped accounts and dump any executable accounts
676 0 : ulong dumped_accounts = block_context->acct_states_count;
677 0 : for( ulong l=0; l<dumped_accounts; l++ ) {
678 0 : fd_exec_test_acct_state_t const * maybe_program_account = &block_context->acct_states[l];
679 0 : dump_executable_account_if_exists( slot_ctx->funk, slot_ctx->funk_txn, maybe_program_account, spad, block_context->acct_states, &block_context->acct_states_count );
680 0 : }
681 0 : }
682 0 : }
683 0 : }
684 0 : }
685 :
686 : static void
687 : create_txn_context_protobuf_from_txn( fd_exec_test_txn_context_t * txn_context_msg,
688 : fd_exec_txn_ctx_t * txn_ctx,
689 0 : fd_spad_t * spad ) {
690 0 : fd_txn_t const * txn_descriptor = txn_ctx->txn_descriptor;
691 0 : uchar const * txn_payload = (uchar const *) txn_ctx->_txn_raw->raw;
692 :
693 : /* We don't want to store builtins in account shared data */
694 0 : fd_pubkey_t const loaded_builtins[] = {
695 0 : fd_solana_system_program_id,
696 0 : fd_solana_vote_program_id,
697 0 : fd_solana_stake_program_id,
698 : // fd_solana_config_program_id, // migrated to BPF, so we should dump it
699 : // fd_solana_zk_token_proof_program_id,
700 0 : fd_solana_bpf_loader_v4_program_id,
701 : // fd_solana_address_lookup_table_program_id, // migrated to BPF, so we should dump it
702 0 : fd_solana_bpf_loader_deprecated_program_id,
703 0 : fd_solana_bpf_loader_program_id,
704 0 : fd_solana_bpf_loader_upgradeable_program_id,
705 0 : fd_solana_compute_budget_program_id,
706 0 : fd_solana_keccak_secp_256k_program_id,
707 0 : fd_solana_secp256r1_program_id,
708 0 : fd_solana_zk_elgamal_proof_program_id,
709 0 : fd_solana_ed25519_sig_verify_program_id,
710 0 : };
711 0 : const ulong num_loaded_builtins = (sizeof(loaded_builtins) / sizeof(fd_pubkey_t));
712 :
713 : /* Prepare sysvar cache accounts */
714 0 : fd_pubkey_t const fd_relevant_sysvar_ids[] = {
715 0 : fd_sysvar_recent_block_hashes_id,
716 0 : fd_sysvar_clock_id,
717 0 : fd_sysvar_slot_history_id,
718 0 : fd_sysvar_slot_hashes_id,
719 0 : fd_sysvar_epoch_schedule_id,
720 0 : fd_sysvar_epoch_rewards_id,
721 0 : fd_sysvar_fees_id,
722 0 : fd_sysvar_rent_id,
723 0 : fd_sysvar_stake_history_id,
724 0 : fd_sysvar_last_restart_slot_id,
725 0 : };
726 0 : const ulong num_sysvar_entries = (sizeof(fd_relevant_sysvar_ids) / sizeof(fd_pubkey_t));
727 :
728 : /* Transaction Context -> account_shared_data
729 : Contains:
730 : - Account data for regular accounts
731 : - Account data for LUT accounts
732 : - Account data for executable accounts
733 : - Account data for (almost) all sysvars
734 : */
735 : // Dump regular accounts first
736 0 : txn_context_msg->account_shared_data_count = 0;
737 0 : txn_context_msg->account_shared_data = fd_spad_alloc( spad,
738 0 : alignof(fd_exec_test_acct_state_t),
739 0 : (256UL*2UL + txn_descriptor->addr_table_lookup_cnt + num_sysvar_entries) * sizeof(fd_exec_test_acct_state_t) );
740 0 : for( ulong i = 0; i < txn_ctx->accounts_cnt; ++i ) {
741 0 : FD_TXN_ACCOUNT_DECL( txn_account );
742 0 : int ret = fd_txn_account_init_from_funk_readonly( txn_account, &txn_ctx->account_keys[i], txn_ctx->funk, txn_ctx->funk_txn );
743 0 : if( FD_UNLIKELY( ret ) ) {
744 0 : continue;
745 0 : }
746 :
747 : // Make sure account is not a builtin
748 0 : if( !is_builtin_account( loaded_builtins, num_loaded_builtins, &txn_ctx->account_keys[i] ) ) {
749 0 : dump_account_state( txn_account, &txn_context_msg->account_shared_data[txn_context_msg->account_shared_data_count++], spad );
750 :
751 0 : }
752 0 : }
753 :
754 : // Dump LUT accounts
755 0 : fd_txn_acct_addr_lut_t const * address_lookup_tables = fd_txn_get_address_tables_const( txn_descriptor );
756 0 : for( ulong i = 0; i < txn_descriptor->addr_table_lookup_cnt; ++i ) {
757 0 : FD_TXN_ACCOUNT_DECL( txn_account );
758 0 : fd_txn_acct_addr_lut_t const * addr_lut = &address_lookup_tables[i];
759 0 : fd_pubkey_t * alut_key = (fd_pubkey_t *) (txn_payload + addr_lut[i].addr_off);
760 0 : int ret = fd_txn_account_init_from_funk_readonly( txn_account, alut_key, txn_ctx->funk, txn_ctx->funk_txn );
761 0 : if( FD_UNLIKELY( ret ) ) continue;
762 :
763 0 : dump_account_state( txn_account, &txn_context_msg->account_shared_data[txn_context_msg->account_shared_data_count++], spad );
764 :
765 0 : fd_acct_addr_t * lookup_addrs = (fd_acct_addr_t *)&txn_account->vt->get_data( txn_account )[FD_LOOKUP_TABLE_META_SIZE];
766 0 : ulong lookup_addrs_cnt = (txn_account->vt->get_data_len( txn_account ) - FD_LOOKUP_TABLE_META_SIZE) >> 5UL; // = (dlen - 56) / 32
767 :
768 : /* Dump any account state refererenced in ALUTs */
769 0 : uchar const * writable_lut_idxs = txn_payload + addr_lut->writable_off;
770 0 : for( ulong j=0; j<addr_lut->writable_cnt; j++ ) {
771 0 : if( writable_lut_idxs[j] >= lookup_addrs_cnt ) {
772 0 : continue;
773 0 : }
774 0 : fd_pubkey_t const * referenced_addr = fd_type_pun( &lookup_addrs[writable_lut_idxs[j]] );
775 0 : if( is_builtin_account( loaded_builtins, num_loaded_builtins, referenced_addr ) ) continue;
776 :
777 0 : FD_TXN_ACCOUNT_DECL( referenced_account );
778 0 : ret = fd_txn_account_init_from_funk_readonly( referenced_account, referenced_addr, txn_ctx->funk, txn_ctx->funk_txn );
779 0 : if( FD_UNLIKELY( ret ) ) continue;
780 0 : dump_account_state( referenced_account, &txn_context_msg->account_shared_data[txn_context_msg->account_shared_data_count++], spad );
781 0 : }
782 :
783 0 : uchar const * readonly_lut_idxs = txn_payload + addr_lut->readonly_off;
784 0 : for( ulong j = 0; j < addr_lut->readonly_cnt; j++ ) {
785 0 : if( readonly_lut_idxs[j] >= lookup_addrs_cnt ) {
786 0 : continue;
787 0 : }
788 0 : fd_pubkey_t const * referenced_addr = fd_type_pun( &lookup_addrs[readonly_lut_idxs[j]] );
789 0 : if( is_builtin_account( loaded_builtins, num_loaded_builtins, referenced_addr ) ) continue;
790 :
791 0 : FD_TXN_ACCOUNT_DECL( referenced_account );
792 0 : ret = fd_txn_account_init_from_funk_readonly( referenced_account, referenced_addr, txn_ctx->funk, txn_ctx->funk_txn );
793 0 : if( FD_UNLIKELY( ret ) ) continue;
794 0 : dump_account_state( referenced_account, &txn_context_msg->account_shared_data[txn_context_msg->account_shared_data_count++], spad );
795 0 : }
796 0 : }
797 :
798 : /* Dump the programdata accounts for any potential v3-owned program accounts */
799 0 : uint accounts_dumped_so_far = txn_context_msg->account_shared_data_count;
800 0 : for( uint i=0U; i<accounts_dumped_so_far; i++ ) {
801 0 : fd_exec_test_acct_state_t const * maybe_program_account = &txn_context_msg->account_shared_data[i];
802 0 : dump_executable_account_if_exists( txn_ctx->funk, txn_ctx->funk_txn, maybe_program_account, spad, txn_context_msg->account_shared_data, &txn_context_msg->account_shared_data_count );
803 0 : }
804 :
805 : /* Dump sysvars */
806 0 : for( ulong i = 0; i < num_sysvar_entries; i++ ) {
807 0 : FD_TXN_ACCOUNT_DECL( txn_account );
808 0 : int ret = fd_txn_account_init_from_funk_readonly( txn_account, &fd_relevant_sysvar_ids[i], txn_ctx->funk, txn_ctx->funk_txn );
809 0 : if( ret != FD_ACC_MGR_SUCCESS ) {
810 0 : continue;
811 0 : }
812 :
813 : // Make sure the account doesn't exist in the output accounts yet
814 0 : int account_exists = 0;
815 0 : for( ulong j = 0; j < txn_ctx->accounts_cnt; j++ ) {
816 0 : if ( 0 == memcmp( txn_ctx->account_keys[j].key, fd_relevant_sysvar_ids[i].uc, sizeof(fd_pubkey_t) ) ) {
817 0 : account_exists = true;
818 0 : break;
819 0 : }
820 0 : }
821 : // Copy it into output
822 0 : if (!account_exists) {
823 0 : dump_account_state( txn_account, &txn_context_msg->account_shared_data[txn_context_msg->account_shared_data_count++], spad );
824 0 : }
825 0 : }
826 :
827 : /* Transaction Context -> tx */
828 0 : txn_context_msg->has_tx = true;
829 0 : fd_exec_test_sanitized_transaction_t * sanitized_transaction = &txn_context_msg->tx;
830 0 : dump_sanitized_transaction( txn_ctx->funk, txn_ctx->funk_txn, txn_descriptor, txn_payload, spad, sanitized_transaction );
831 :
832 : /* Transaction Context -> blockhash_queue
833 : NOTE: Agave's implementation of register_hash incorrectly allows the blockhash queue to hold max_age + 1 (max 301)
834 : entries. We have this incorrect logic implemented in fd_sysvar_recent_hashes:register_blockhash and it's not a
835 : huge issue, but something to keep in mind. */
836 0 : pb_bytes_array_t ** output_blockhash_queue = fd_spad_alloc(
837 0 : spad,
838 0 : alignof(pb_bytes_array_t *),
839 0 : PB_BYTES_ARRAY_T_ALLOCSIZE((FD_BLOCKHASHES_MAX) * sizeof(pb_bytes_array_t *)) );
840 0 : txn_context_msg->blockhash_queue = output_blockhash_queue;
841 0 : fd_blockhashes_t const * block_hash_queue = fd_bank_block_hash_queue_query( txn_ctx->bank );
842 0 : dump_blockhash_queue( block_hash_queue, spad, output_blockhash_queue, &txn_context_msg->blockhash_queue_count );
843 :
844 : /* Transaction Context -> epoch_ctx */
845 0 : txn_context_msg->has_epoch_ctx = true;
846 0 : txn_context_msg->epoch_ctx.has_features = true;
847 0 : dump_sorted_features( &txn_ctx->features, &txn_context_msg->epoch_ctx.features, spad );
848 :
849 : /* Transaction Context -> slot_ctx */
850 0 : txn_context_msg->has_slot_ctx = true;
851 0 : txn_context_msg->slot_ctx.slot = txn_ctx->slot;
852 0 : }
853 :
854 : static void
855 : create_instr_context_protobuf_from_instructions( fd_exec_test_instr_context_t * instr_context,
856 : fd_exec_txn_ctx_t const * txn_ctx,
857 0 : fd_instr_info_t const * instr ) {
858 : /* Prepare sysvar cache accounts */
859 0 : fd_pubkey_t const fd_relevant_sysvar_ids[] = {
860 0 : fd_sysvar_recent_block_hashes_id,
861 0 : fd_sysvar_clock_id,
862 0 : fd_sysvar_slot_history_id,
863 0 : fd_sysvar_slot_hashes_id,
864 0 : fd_sysvar_epoch_schedule_id,
865 0 : fd_sysvar_epoch_rewards_id,
866 0 : fd_sysvar_fees_id,
867 0 : fd_sysvar_rent_id,
868 0 : fd_sysvar_stake_history_id,
869 0 : fd_sysvar_last_restart_slot_id,
870 0 : fd_sysvar_instructions_id,
871 0 : };
872 0 : const ulong num_sysvar_entries = (sizeof(fd_relevant_sysvar_ids) / sizeof(fd_pubkey_t));
873 :
874 : /* Program ID */
875 0 : fd_memcpy( instr_context->program_id, txn_ctx->account_keys[ instr->program_id ].uc, sizeof(fd_pubkey_t) );
876 :
877 : /* Accounts */
878 0 : instr_context->accounts_count = (pb_size_t) txn_ctx->accounts_cnt;
879 0 : instr_context->accounts = fd_spad_alloc( txn_ctx->spad, alignof(fd_exec_test_acct_state_t), (instr_context->accounts_count + num_sysvar_entries + txn_ctx->executable_cnt) * sizeof(fd_exec_test_acct_state_t));
880 0 : for( ulong i = 0; i < txn_ctx->accounts_cnt; i++ ) {
881 : // Copy account information over
882 0 : fd_txn_account_t const * txn_account = &txn_ctx->accounts[i];
883 0 : fd_exec_test_acct_state_t * output_account = &instr_context->accounts[i];
884 0 : dump_account_state( txn_account, output_account, txn_ctx->spad );
885 0 : }
886 :
887 : /* Add sysvar cache variables */
888 0 : for( ulong i = 0; i < num_sysvar_entries; i++ ) {
889 0 : FD_TXN_ACCOUNT_DECL( txn_account );
890 0 : int ret = fd_txn_account_init_from_funk_readonly( txn_account, &fd_relevant_sysvar_ids[i], txn_ctx->funk, txn_ctx->funk_txn );
891 0 : if( ret != FD_ACC_MGR_SUCCESS ) {
892 0 : continue;
893 0 : }
894 : // Make sure the account doesn't exist in the output accounts yet
895 0 : int account_exists = 0;
896 0 : for( ulong j = 0; j < txn_ctx->accounts_cnt; j++ ) {
897 0 : if ( 0 == memcmp( txn_ctx->account_keys[j].key, fd_relevant_sysvar_ids[i].uc, sizeof(fd_pubkey_t) ) ) {
898 0 : account_exists = true;
899 0 : break;
900 0 : }
901 0 : }
902 :
903 : // Copy it into output
904 0 : if (!account_exists) {
905 0 : fd_exec_test_acct_state_t * output_account = &instr_context->accounts[instr_context->accounts_count++];
906 0 : dump_account_state( txn_account, output_account, txn_ctx->spad );
907 0 : }
908 0 : }
909 :
910 : /* Add executable accounts */
911 0 : for( ulong i = 0; i < txn_ctx->executable_cnt; i++ ) {
912 0 : FD_TXN_ACCOUNT_DECL( txn_account );
913 0 : int ret = fd_txn_account_init_from_funk_readonly( txn_account, txn_ctx->executable_accounts[i].pubkey, txn_ctx->funk, txn_ctx->funk_txn );
914 0 : if( ret != FD_ACC_MGR_SUCCESS ) {
915 0 : continue;
916 0 : }
917 : // Make sure the account doesn't exist in the output accounts yet
918 0 : bool account_exists = false;
919 0 : for( ulong j = 0; j < instr_context->accounts_count; j++ ) {
920 0 : if( 0 == memcmp( instr_context->accounts[j].address, txn_ctx->executable_accounts[i].pubkey->uc, sizeof(fd_pubkey_t) ) ) {
921 0 : account_exists = true;
922 0 : break;
923 0 : }
924 0 : }
925 : // Copy it into output
926 0 : if( !account_exists ) {
927 0 : fd_exec_test_acct_state_t * output_account = &instr_context->accounts[instr_context->accounts_count++];
928 0 : dump_account_state( txn_account, output_account, txn_ctx->spad );
929 0 : }
930 0 : }
931 :
932 : /* Instruction Accounts */
933 0 : instr_context->instr_accounts_count = (pb_size_t) instr->acct_cnt;
934 0 : instr_context->instr_accounts = fd_spad_alloc( txn_ctx->spad, alignof(fd_exec_test_instr_acct_t), instr_context->instr_accounts_count * sizeof(fd_exec_test_instr_acct_t) );
935 0 : for( ushort i = 0; i < instr->acct_cnt; i++ ) {
936 0 : fd_exec_test_instr_acct_t * output_instr_account = &instr_context->instr_accounts[i];
937 :
938 0 : output_instr_account->index = instr->accounts[i].index_in_transaction;
939 0 : output_instr_account->is_writable = instr->accounts[i].is_writable;
940 0 : output_instr_account->is_signer = instr->accounts[i].is_signer;
941 0 : }
942 :
943 : /* Data */
944 0 : instr_context->data = fd_spad_alloc( txn_ctx->spad, alignof(pb_bytes_array_t), PB_BYTES_ARRAY_T_ALLOCSIZE( instr->data_sz ) );
945 0 : instr_context->data->size = (pb_size_t) instr->data_sz;
946 0 : fd_memcpy( instr_context->data->bytes, instr->data, instr->data_sz );
947 :
948 : /* Compute Units */
949 0 : instr_context->cu_avail = txn_ctx->compute_budget_details.compute_meter;
950 :
951 : /* Slot Context */
952 0 : instr_context->has_slot_context = true;
953 :
954 : /* Epoch Context */
955 0 : instr_context->has_epoch_context = true;
956 0 : instr_context->epoch_context.has_features = true;
957 0 : dump_sorted_features( &txn_ctx->features, &instr_context->epoch_context.features, txn_ctx->spad );
958 0 : }
959 :
960 : /***** PUBLIC APIs *****/
961 :
962 : void
963 : fd_dump_instr_to_protobuf( fd_exec_txn_ctx_t * txn_ctx,
964 : fd_instr_info_t * instr,
965 0 : ushort instruction_idx ) {
966 0 : FD_SPAD_FRAME_BEGIN( txn_ctx->spad ) {
967 : // Get base58-encoded tx signature
968 0 : const fd_ed25519_sig_t * signatures = fd_txn_get_signatures( txn_ctx->txn_descriptor, txn_ctx->_txn_raw->raw );
969 0 : fd_ed25519_sig_t signature; fd_memcpy( signature, signatures[0], sizeof(fd_ed25519_sig_t) );
970 0 : char encoded_signature[FD_BASE58_ENCODED_64_SZ];
971 0 : ulong out_size;
972 0 : fd_base58_encode_64( signature, &out_size, encoded_signature );
973 :
974 0 : if (txn_ctx->capture_ctx->dump_proto_sig_filter) {
975 0 : ulong filter_strlen = (ulong) strlen(txn_ctx->capture_ctx->dump_proto_sig_filter);
976 :
977 : // Terminate early if the signature does not match
978 0 : if( memcmp( txn_ctx->capture_ctx->dump_proto_sig_filter, encoded_signature, filter_strlen < out_size ? filter_strlen : out_size ) ) {
979 0 : return;
980 0 : }
981 0 : }
982 :
983 0 : fd_exec_test_instr_context_t instr_context = FD_EXEC_TEST_INSTR_CONTEXT_INIT_DEFAULT;
984 0 : create_instr_context_protobuf_from_instructions( &instr_context, txn_ctx, instr );
985 :
986 : /* Output to file */
987 0 : ulong out_buf_size = 100 * 1024 * 1024;
988 0 : uint8_t * out = fd_spad_alloc( txn_ctx->spad, alignof(uchar) , out_buf_size );
989 0 : pb_ostream_t stream = pb_ostream_from_buffer( out, out_buf_size );
990 0 : if (pb_encode(&stream, FD_EXEC_TEST_INSTR_CONTEXT_FIELDS, &instr_context)) {
991 0 : char output_filepath[256]; fd_memset(output_filepath, 0, sizeof(output_filepath));
992 0 : char * position = fd_cstr_init(output_filepath);
993 0 : position = fd_cstr_append_cstr(position, txn_ctx->capture_ctx->dump_proto_output_dir);
994 0 : position = fd_cstr_append_cstr(position, "/instr-");
995 0 : position = fd_cstr_append_cstr(position, encoded_signature);
996 0 : position = fd_cstr_append_cstr(position, "-");
997 0 : position = fd_cstr_append_ushort_as_text(position, '0', 0, instruction_idx, 3); // Assume max 3 digits
998 0 : position = fd_cstr_append_cstr(position, ".instrctx");
999 0 : fd_cstr_fini(position);
1000 :
1001 0 : FILE * file = fopen(output_filepath, "wb");
1002 0 : if( file ) {
1003 0 : fwrite( out, 1, stream.bytes_written, file );
1004 0 : fclose( file );
1005 0 : }
1006 0 : }
1007 0 : } FD_SPAD_FRAME_END;
1008 0 : }
1009 :
1010 : void
1011 0 : fd_dump_txn_to_protobuf( fd_exec_txn_ctx_t * txn_ctx, fd_spad_t * spad ) {
1012 0 : FD_SPAD_FRAME_BEGIN( spad ) {
1013 : // Get base58-encoded tx signature
1014 0 : const fd_ed25519_sig_t * signatures = fd_txn_get_signatures( txn_ctx->txn_descriptor, txn_ctx->_txn_raw->raw );
1015 0 : fd_ed25519_sig_t signature; fd_memcpy( signature, signatures[0], sizeof(fd_ed25519_sig_t) );
1016 0 : char encoded_signature[FD_BASE58_ENCODED_64_SZ];
1017 0 : ulong out_size;
1018 0 : fd_base58_encode_64( signature, &out_size, encoded_signature );
1019 :
1020 0 : if( txn_ctx->capture_ctx->dump_proto_sig_filter ) {
1021 : // Terminate early if the signature does not match
1022 0 : if( strcmp( txn_ctx->capture_ctx->dump_proto_sig_filter, encoded_signature ) ) {
1023 0 : return;
1024 0 : }
1025 0 : }
1026 :
1027 0 : fd_exec_test_txn_context_t txn_context_msg = FD_EXEC_TEST_TXN_CONTEXT_INIT_DEFAULT;
1028 0 : create_txn_context_protobuf_from_txn( &txn_context_msg, txn_ctx, spad );
1029 :
1030 : /* Output to file */
1031 0 : ulong out_buf_size = 100 * 1024 * 1024;
1032 0 : uint8_t * out = fd_spad_alloc( spad, alignof(uint8_t), out_buf_size );
1033 0 : pb_ostream_t stream = pb_ostream_from_buffer( out, out_buf_size );
1034 0 : if( pb_encode( &stream, FD_EXEC_TEST_TXN_CONTEXT_FIELDS, &txn_context_msg ) ) {
1035 0 : char output_filepath[256]; fd_memset( output_filepath, 0, sizeof(output_filepath) );
1036 0 : char * position = fd_cstr_init( output_filepath );
1037 0 : position = fd_cstr_append_cstr( position, txn_ctx->capture_ctx->dump_proto_output_dir );
1038 0 : position = fd_cstr_append_cstr( position, "/txn-" );
1039 0 : position = fd_cstr_append_cstr( position, encoded_signature );
1040 0 : position = fd_cstr_append_cstr(position, ".txnctx");
1041 0 : fd_cstr_fini(position);
1042 :
1043 0 : FILE * file = fopen(output_filepath, "wb");
1044 0 : if( file ) {
1045 0 : fwrite( out, 1, stream.bytes_written, file );
1046 0 : fclose( file );
1047 0 : }
1048 0 : }
1049 0 : } FD_SPAD_FRAME_END;
1050 0 : }
1051 :
1052 : void
1053 : fd_dump_block_to_protobuf( fd_exec_slot_ctx_t const * slot_ctx,
1054 : fd_capture_ctx_t const * capture_ctx,
1055 : fd_spad_t * spad,
1056 0 : fd_exec_test_block_context_t * block_context_msg /* output */ ) {
1057 : /* No spad frame because these allocations must persist beyond the lifetime of this function call */
1058 0 : if( FD_UNLIKELY( capture_ctx==NULL ) ) {
1059 0 : FD_LOG_WARNING(( "Capture context may not be NULL when dumping blocks." ));
1060 0 : return;
1061 0 : }
1062 0 : create_block_context_protobuf_from_block( block_context_msg, slot_ctx, spad );
1063 0 : }
1064 :
1065 : void
1066 : fd_dump_block_to_protobuf_tx_only( fd_runtime_block_info_t const * block_info,
1067 : fd_exec_slot_ctx_t const * slot_ctx,
1068 : fd_capture_ctx_t const * capture_ctx,
1069 : fd_spad_t * spad,
1070 0 : fd_exec_test_block_context_t * block_context_msg ) {
1071 0 : FD_SPAD_FRAME_BEGIN( spad ) {
1072 0 : if( FD_UNLIKELY( capture_ctx==NULL ) ) {
1073 0 : FD_LOG_WARNING(( "Capture context may not be NULL when dumping blocks." ));
1074 0 : return;
1075 0 : }
1076 :
1077 0 : if( FD_UNLIKELY( block_info==NULL ) ) {
1078 0 : FD_LOG_WARNING(( "Block info may not be NULL when dumping blocks." ));
1079 0 : return;
1080 0 : }
1081 :
1082 0 : create_block_context_protobuf_from_block_tx_only( block_context_msg, block_info, slot_ctx, spad );
1083 :
1084 : /* Output to file */
1085 0 : ulong out_buf_size = 5UL<<30UL; /* 5 GB */
1086 0 : uint8_t * out = fd_spad_alloc( spad, alignof(uint8_t), out_buf_size );
1087 0 : pb_ostream_t stream = pb_ostream_from_buffer( out, out_buf_size );
1088 0 : if( pb_encode( &stream, FD_EXEC_TEST_BLOCK_CONTEXT_FIELDS, block_context_msg ) ) {
1089 0 : char output_filepath[256]; fd_memset( output_filepath, 0, sizeof(output_filepath) );
1090 0 : char * position = fd_cstr_init( output_filepath );
1091 0 : position = fd_cstr_append_printf( position, "%s/block-%lu.blockctx", capture_ctx->dump_proto_output_dir, fd_bank_slot_get( slot_ctx->bank ) );
1092 0 : fd_cstr_fini( position );
1093 :
1094 0 : FILE * file = fopen(output_filepath, "wb");
1095 0 : if( file ) {
1096 0 : fwrite( out, 1, stream.bytes_written, file );
1097 0 : fclose( file );
1098 0 : }
1099 0 : }
1100 0 : } FD_SPAD_FRAME_END;
1101 0 : }
1102 :
1103 : void
1104 : fd_dump_vm_syscall_to_protobuf( fd_vm_t const * vm,
1105 0 : char const * fn_name ) {
1106 0 : FD_SPAD_FRAME_BEGIN( vm->instr_ctx->txn_ctx->spad ) {
1107 :
1108 0 : fd_ed25519_sig_t signature;
1109 0 : memcpy( signature, (uchar const *)vm->instr_ctx->txn_ctx->_txn_raw->raw + vm->instr_ctx->txn_ctx->txn_descriptor->signature_off, sizeof(fd_ed25519_sig_t) );
1110 0 : char encoded_signature[FD_BASE58_ENCODED_64_SZ];
1111 0 : fd_base58_encode_64( signature, NULL, encoded_signature );
1112 :
1113 0 : char filename[256];
1114 0 : sprintf( filename,
1115 0 : "%s/syscall-%s-%s-%d-%hhu-%lu.sysctx",
1116 0 : vm->instr_ctx->txn_ctx->capture_ctx->dump_proto_output_dir,
1117 0 : fn_name,
1118 0 : encoded_signature,
1119 0 : vm->instr_ctx->txn_ctx->current_instr_idx,
1120 0 : vm->instr_ctx->txn_ctx->instr_stack_sz,
1121 0 : vm->cu );
1122 :
1123 : /* The generated filename should be unique for every call. Silently return otherwise. */
1124 0 : if( FD_UNLIKELY( access( filename, F_OK )!=-1 ) ) {
1125 0 : return;
1126 0 : }
1127 :
1128 0 : fd_exec_test_syscall_context_t sys_ctx = FD_EXEC_TEST_SYSCALL_CONTEXT_INIT_ZERO;
1129 :
1130 : /* SyscallContext -> vm_ctx */
1131 0 : sys_ctx.has_vm_ctx = 1;
1132 :
1133 : /* SyscallContext -> vm_ctx -> heap_max */
1134 0 : sys_ctx.vm_ctx.heap_max = vm->heap_max; /* should be equiv. to txn_ctx->heap_sz */
1135 :
1136 : /* SyscallContext -> vm_ctx -> rodata */
1137 0 : sys_ctx.vm_ctx.rodata = fd_spad_alloc( vm->instr_ctx->txn_ctx->spad, alignof(pb_bytes_array_t), PB_BYTES_ARRAY_T_ALLOCSIZE( vm->rodata_sz ) );
1138 0 : sys_ctx.vm_ctx.rodata->size = (pb_size_t) vm->rodata_sz;
1139 0 : fd_memcpy( sys_ctx.vm_ctx.rodata->bytes, vm->rodata, vm->rodata_sz );
1140 :
1141 : /* SyscallContext -> vm_ctx -> rodata_text_section_offset */
1142 0 : sys_ctx.vm_ctx.rodata_text_section_offset = vm->text_off;
1143 :
1144 : /* SyscallContext -> vm_ctx -> rodata_text_section_length */
1145 0 : sys_ctx.vm_ctx.rodata_text_section_length = vm->text_sz;
1146 :
1147 : /* SyscallContext -> vm_ctx -> r0-11 */
1148 0 : sys_ctx.vm_ctx.r0 = vm->reg[0];
1149 0 : sys_ctx.vm_ctx.r1 = vm->reg[1];
1150 0 : sys_ctx.vm_ctx.r2 = vm->reg[2];
1151 0 : sys_ctx.vm_ctx.r3 = vm->reg[3];
1152 0 : sys_ctx.vm_ctx.r4 = vm->reg[4];
1153 0 : sys_ctx.vm_ctx.r5 = vm->reg[5];
1154 0 : sys_ctx.vm_ctx.r6 = vm->reg[6];
1155 0 : sys_ctx.vm_ctx.r7 = vm->reg[7];
1156 0 : sys_ctx.vm_ctx.r8 = vm->reg[8];
1157 0 : sys_ctx.vm_ctx.r9 = vm->reg[9];
1158 0 : sys_ctx.vm_ctx.r10 = vm->reg[10];
1159 0 : sys_ctx.vm_ctx.r11 = vm->reg[11];
1160 :
1161 : /* SyscallContext -> vm_ctx -> entry_pc */
1162 0 : sys_ctx.vm_ctx.entry_pc = vm->entry_pc;
1163 :
1164 : /* SyscallContext -> vm_ctx -> return_data */
1165 0 : sys_ctx.vm_ctx.has_return_data = 1;
1166 :
1167 : /* SyscallContext -> vm_ctx -> return_data -> data */
1168 0 : sys_ctx.vm_ctx.return_data.data = fd_spad_alloc( vm->instr_ctx->txn_ctx->spad, alignof(pb_bytes_array_t), PB_BYTES_ARRAY_T_ALLOCSIZE( vm->instr_ctx->txn_ctx->return_data.len ) );
1169 0 : sys_ctx.vm_ctx.return_data.data->size = (pb_size_t)vm->instr_ctx->txn_ctx->return_data.len;
1170 0 : fd_memcpy( sys_ctx.vm_ctx.return_data.data->bytes, vm->instr_ctx->txn_ctx->return_data.data, vm->instr_ctx->txn_ctx->return_data.len );
1171 :
1172 : /* SyscallContext -> vm_ctx -> return_data -> program_id */
1173 0 : sys_ctx.vm_ctx.return_data.program_id = fd_spad_alloc( vm->instr_ctx->txn_ctx->spad, alignof(pb_bytes_array_t), sizeof(fd_pubkey_t) );
1174 0 : sys_ctx.vm_ctx.return_data.program_id->size = sizeof(fd_pubkey_t);
1175 0 : fd_memcpy( sys_ctx.vm_ctx.return_data.program_id->bytes, vm->instr_ctx->txn_ctx->return_data.program_id.key, sizeof(fd_pubkey_t) );
1176 :
1177 : /* SyscallContext -> vm_ctx -> sbpf_version */
1178 0 : sys_ctx.vm_ctx.sbpf_version = (uint)vm->sbpf_version;
1179 :
1180 : /* SyscallContext -> instr_ctx */
1181 0 : sys_ctx.has_instr_ctx = 1;
1182 0 : create_instr_context_protobuf_from_instructions( &sys_ctx.instr_ctx,
1183 0 : vm->instr_ctx->txn_ctx,
1184 0 : vm->instr_ctx->instr );
1185 :
1186 : /* SyscallContext -> syscall_invocation */
1187 0 : sys_ctx.has_syscall_invocation = 1;
1188 :
1189 : /* SyscallContext -> syscall_invocation -> function_name */
1190 0 : sys_ctx.syscall_invocation.function_name.size = fd_uint_min( (uint) strlen(fn_name), sizeof(sys_ctx.syscall_invocation.function_name.bytes) );
1191 0 : fd_memcpy( sys_ctx.syscall_invocation.function_name.bytes,
1192 0 : fn_name,
1193 0 : sys_ctx.syscall_invocation.function_name.size );
1194 :
1195 : /* SyscallContext -> syscall_invocation -> heap_prefix */
1196 0 : sys_ctx.syscall_invocation.heap_prefix = fd_spad_alloc( vm->instr_ctx->txn_ctx->spad, 8UL, PB_BYTES_ARRAY_T_ALLOCSIZE( vm->heap_max ) );
1197 0 : sys_ctx.syscall_invocation.heap_prefix->size = (pb_size_t) vm->instr_ctx->txn_ctx->compute_budget_details.heap_size;
1198 0 : fd_memcpy( sys_ctx.syscall_invocation.heap_prefix->bytes, vm->heap, vm->instr_ctx->txn_ctx->compute_budget_details.heap_size );
1199 :
1200 : /* SyscallContext -> syscall_invocation -> stack_prefix */
1201 0 : pb_size_t stack_sz = (pb_size_t)FD_VM_STACK_MAX;
1202 0 : sys_ctx.syscall_invocation.stack_prefix = fd_spad_alloc( vm->instr_ctx->txn_ctx->spad, 8UL, PB_BYTES_ARRAY_T_ALLOCSIZE( stack_sz ) );
1203 0 : sys_ctx.syscall_invocation.stack_prefix->size = stack_sz;
1204 0 : fd_memcpy( sys_ctx.syscall_invocation.stack_prefix->bytes, vm->stack, stack_sz );
1205 :
1206 : /* Output to file */
1207 0 : ulong out_buf_size = 1UL<<29UL; /* 128 MB */
1208 0 : uint8_t * out = fd_spad_alloc( vm->instr_ctx->txn_ctx->spad, alignof(uint8_t), out_buf_size );
1209 0 : pb_ostream_t stream = pb_ostream_from_buffer( out, out_buf_size );
1210 0 : if( pb_encode( &stream, FD_EXEC_TEST_SYSCALL_CONTEXT_FIELDS, &sys_ctx ) ) {
1211 0 : FILE * file = fopen(filename, "wb");
1212 0 : if( file ) {
1213 0 : fwrite( out, 1, stream.bytes_written, file );
1214 0 : fclose( file );
1215 0 : }
1216 0 : }
1217 0 : } FD_SPAD_FRAME_END;
1218 0 : }
1219 :
1220 : void
1221 : fd_dump_elf_to_protobuf( fd_exec_txn_ctx_t * txn_ctx,
1222 0 : fd_txn_account_t * program_acc ) {
1223 0 : FD_SPAD_FRAME_BEGIN( txn_ctx->spad ) {
1224 :
1225 : /* Get the programdata for the account */
1226 0 : ulong program_data_len = 0UL;
1227 0 : uchar const * program_data = fd_program_cache_get_account_programdata( txn_ctx->funk,
1228 0 : txn_ctx->funk_txn,
1229 0 : program_acc,
1230 0 : &program_data_len,
1231 0 : txn_ctx->spad );
1232 0 : if( program_data==NULL ) {
1233 0 : return;
1234 0 : }
1235 :
1236 : /* Serialize the ELF to protobuf */
1237 0 : fd_ed25519_sig_t signature;
1238 0 : memcpy( signature, (uchar const *)txn_ctx->_txn_raw->raw + txn_ctx->txn_descriptor->signature_off, sizeof(fd_ed25519_sig_t) );
1239 0 : char encoded_signature[FD_BASE58_ENCODED_64_SZ];
1240 0 : fd_base58_encode_64( signature, NULL, encoded_signature );
1241 :
1242 0 : char filename[256];
1243 0 : sprintf( filename,
1244 0 : "%s/elf-%s-%s-%lu.elfctx",
1245 0 : txn_ctx->capture_ctx->dump_proto_output_dir,
1246 0 : encoded_signature,
1247 0 : FD_BASE58_ENC_32_ALLOCA( program_acc->pubkey ),
1248 0 : txn_ctx->slot );
1249 :
1250 : /* The generated filename should be unique for every call. Silently return otherwise. */
1251 0 : if( FD_UNLIKELY( access( filename, F_OK )!=-1 ) ) {
1252 0 : return;
1253 0 : }
1254 :
1255 0 : fd_exec_test_elf_loader_ctx_t elf_ctx = FD_EXEC_TEST_ELF_LOADER_CTX_INIT_ZERO;
1256 :
1257 : /* ElfLoaderCtx -> elf */
1258 0 : elf_ctx.has_elf = true;
1259 0 : elf_ctx.elf.data = fd_spad_alloc( txn_ctx->spad, alignof(pb_bytes_array_t), PB_BYTES_ARRAY_T_ALLOCSIZE( program_data_len ) );
1260 0 : elf_ctx.elf.data->size = (pb_size_t)program_data_len;
1261 0 : fd_memcpy( elf_ctx.elf.data->bytes, program_data, program_data_len );
1262 :
1263 : /* ElfLoaderCtx -> features */
1264 0 : elf_ctx.has_features = true;
1265 0 : dump_sorted_features( &txn_ctx->features, &elf_ctx.features, txn_ctx->spad );
1266 :
1267 : /* ElfLoaderCtx -> deploy_checks
1268 : We hardcode this to true and rely the fuzzer to toggle this as it pleases */
1269 0 : elf_ctx.deploy_checks = true;
1270 :
1271 : /* Output to file */
1272 0 : ulong out_buf_size = 1UL<<29UL; /* 128 MB */
1273 0 : uint8_t * out = fd_spad_alloc( txn_ctx->spad, alignof(uint8_t), out_buf_size );
1274 0 : pb_ostream_t stream = pb_ostream_from_buffer( out, out_buf_size );
1275 0 : if( pb_encode( &stream, FD_EXEC_TEST_ELF_LOADER_CTX_FIELDS, &elf_ctx ) ) {
1276 0 : FILE * file = fopen(filename, "wb");
1277 0 : if( file ) {
1278 0 : fwrite( out, 1, stream.bytes_written, file );
1279 0 : fclose( file );
1280 0 : }
1281 0 : }
1282 0 : } FD_SPAD_FRAME_END;
1283 0 : }
|