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