diff --git a/src/discof/replay/fd_replay_tile.c b/src/discof/replay/fd_replay_tile.c index fe69462469..a2207072a4 100644 --- a/src/discof/replay/fd_replay_tile.c +++ b/src/discof/replay/fd_replay_tile.c @@ -24,6 +24,8 @@ #include "../../flamenco/fd_flamenco_base.h" #include "../../flamenco/runtime/sysvar/fd_sysvar_epoch_schedule.h" +#include "../../flamenco/runtime/tests/fd_dump_pb.h" + #include /* Replay concepts: @@ -358,6 +360,9 @@ struct fd_replay_tile { fd_replay_out_link_t stake_out[1]; + /* For dumping blocks to protobuf. */ + fd_block_dump_ctx_t * block_dump_ctx; + struct { fd_histf_t store_read_wait[ 1 ]; fd_histf_t store_read_work[ 1 ]; @@ -379,7 +384,6 @@ FD_FN_CONST static inline ulong scratch_align( void ) { return 128UL; } - FD_FN_PURE static inline ulong scratch_footprint( fd_topo_tile_t const * tile ) { ulong chain_cnt = fd_block_id_map_chain_cnt_est( tile->replay.max_live_slots ); @@ -394,7 +398,13 @@ scratch_footprint( fd_topo_tile_t const * tile ) { l = FD_LAYOUT_APPEND( l, fd_vote_tracker_align(), fd_vote_tracker_footprint() ); l = FD_LAYOUT_APPEND( l, fd_capture_ctx_align(), fd_capture_ctx_footprint() ); l = FD_LAYOUT_APPEND( l, fd_spad_align(), fd_spad_footprint( tile->replay.heap_size_gib<<30 ) ); - l = FD_LAYOUT_FINI ( l, scratch_align() ); + + if( FD_UNLIKELY( tile->replay.dump_block_to_pb ) ) { + l = FD_LAYOUT_APPEND( l, fd_block_dump_context_align(), fd_block_dump_context_footprint() ); + } + + l = FD_LAYOUT_FINI( l, scratch_align() ); + return l; } @@ -847,6 +857,13 @@ replay_block_finalize( fd_replay_tile_t * ctx, though we could technically do this before the hash cmp and vote tower stuff. */ publish_slot_completed( ctx, stem, bank, 0 ); + + /* If enabled, dump the block to a file and reset the dumping + context state */ + if( FD_UNLIKELY( ctx->capture_ctx && ctx->capture_ctx->dump_block_to_pb ) ) { + fd_dump_block_to_protobuf( ctx->block_dump_ctx, ctx->banks, bank, ctx->funk, ctx->capture_ctx ); + fd_block_dump_context_reset( ctx->block_dump_ctx ); + } } /**********************************************************************/ @@ -1446,6 +1463,11 @@ dispatch_task( fd_replay_tile_t * ctx, fd_funk_txn_xid_t xid = { .ul = { task->txn_exec->slot, task->txn_exec->slot } }; fd_runtime_update_program_cache( bank, ctx->funk, &xid, txn_p, ctx->runtime_spad ); + /* Add the transaction to the block dumper if necessary */ + if( FD_UNLIKELY( ctx->capture_ctx && ctx->capture_ctx->dump_block_to_pb ) ) { + fd_dump_block_to_protobuf_collect_tx( ctx->block_dump_ctx, txn_p ); + } + bank->refcnt++; if( FD_UNLIKELY( !bank->first_transaction_scheduled_nanos ) ) bank->first_transaction_scheduled_nanos = fd_log_wallclock(); @@ -2120,6 +2142,10 @@ unprivileged_init( fd_topo_t * topo, void * vote_tracker_mem = FD_SCRATCH_ALLOC_APPEND( l, fd_vote_tracker_align(), fd_vote_tracker_footprint() ); void * _capture_ctx = FD_SCRATCH_ALLOC_APPEND( l, fd_capture_ctx_align(), fd_capture_ctx_footprint() ); void * spad_mem = FD_SCRATCH_ALLOC_APPEND( l, fd_spad_align(), fd_spad_footprint( tile->replay.heap_size_gib<<30 ) ); + void * block_dump_ctx = NULL; + if( FD_UNLIKELY( tile->replay.dump_block_to_pb ) ) { + block_dump_ctx = FD_SCRATCH_ALLOC_APPEND( l, fd_block_dump_context_align(), fd_block_dump_context_footprint() ); + } ulong store_obj_id = fd_pod_query_ulong( topo->props, "store", ULONG_MAX ); FD_TEST( store_obj_id!=ULONG_MAX ); @@ -2191,6 +2217,12 @@ unprivileged_init( fd_topo_t * topo, if( FD_LIKELY( tile->replay.dump_block_to_pb ) ) ctx->capture_ctx->dump_block_to_pb = tile->replay.dump_block_to_pb; } + if( FD_UNLIKELY( tile->replay.dump_block_to_pb ) ) { + ctx->block_dump_ctx = fd_block_dump_context_join( fd_block_dump_context_new( block_dump_ctx ) ); + } else { + ctx->block_dump_ctx = NULL; + } + ctx->exec_cnt = fd_topo_tile_name_cnt( topo, "exec" ); if( FD_UNLIKELY( ctx->exec_cnt>FD_PACK_MAX_BANK_TILES ) ) FD_LOG_ERR(( "replay tile has too many exec tiles %lu", ctx->exec_cnt )); diff --git a/src/flamenco/runtime/fd_runtime.h b/src/flamenco/runtime/fd_runtime.h index a4cbde7675..3a5168e4f2 100644 --- a/src/flamenco/runtime/fd_runtime.h +++ b/src/flamenco/runtime/fd_runtime.h @@ -11,10 +11,8 @@ #include "../features/fd_features.h" #include "context/fd_capture_ctx.h" #include "context/fd_exec_txn_ctx.h" -#include "info/fd_runtime_block_info.h" #include "info/fd_instr_info.h" #include "../../disco/pack/fd_microblock.h" -#include "info/fd_microblock_info.h" #include "../../ballet/sbpf/fd_sbpf_loader.h" #include "../vm/fd_vm_base.h" diff --git a/src/flamenco/runtime/info/Local.mk b/src/flamenco/runtime/info/Local.mk index dfa5eaa943..9fceb0bd1b 100644 --- a/src/flamenco/runtime/info/Local.mk +++ b/src/flamenco/runtime/info/Local.mk @@ -1,8 +1,4 @@ ifdef FD_HAS_INT128 -$(call add-hdrs,fd_runtime_block_info.h) $(call add-hdrs,fd_instr_info.h) -$(call add-hdrs,fd_microblock_batch_info.h) -$(call add-hdrs,fd_microblock_info.h) - $(call add-objs,fd_instr_info,fd_flamenco) endif diff --git a/src/flamenco/runtime/info/fd_microblock_batch_info.h b/src/flamenco/runtime/info/fd_microblock_batch_info.h deleted file mode 100644 index 3f4dee6a84..0000000000 --- a/src/flamenco/runtime/info/fd_microblock_batch_info.h +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef HEADER_fd_src_flamenco_runtime_info_fd_microblock_batch_info_h -#define HEADER_fd_src_flamenco_runtime_info_fd_microblock_batch_info_h - -#include "../../../util/fd_util_base.h" -#include "../../../ballet/block/fd_microblock.h" -#include "../../../ballet/txn/fd_txn.h" - -#include "fd_microblock_info.h" - -struct fd_microblock_batch_info { - ulong microblock_cnt; - ulong signature_cnt; - ulong txn_cnt; - ulong account_cnt; - - fd_microblock_info_t * microblock_infos; - - void const * raw_microblock_batch; - ulong raw_microblock_batch_sz; -}; -typedef struct fd_microblock_batch_info fd_microblock_batch_info_t; - -FD_PROTOTYPES_BEGIN - -void * -fd_microblock_batch_info_new( void * mem ); - -fd_microblock_batch_info_t * -fd_microblock_batch_info_join( void * mem ); - -void * -fd_microblock_batch_info_leave( fd_microblock_batch_info_t * info ); - -void * -fd_microblock_batch_info_delete( void * mem ); - -FD_PROTOTYPES_END - -#endif /* HEADER_fd_src_flamenco_runtime_info_fd_microblock_batch_info_h */ diff --git a/src/flamenco/runtime/info/fd_microblock_info.h b/src/flamenco/runtime/info/fd_microblock_info.h deleted file mode 100644 index 784e792c5d..0000000000 --- a/src/flamenco/runtime/info/fd_microblock_info.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef HEADER_fd_src_flamenco_runtime_info_fd_microblock_info_h -#define HEADER_fd_src_flamenco_runtime_info_fd_microblock_info_h - -#include "../../fd_flamenco_base.h" -#include "../../../ballet/block/fd_microblock.h" -#include "../../../ballet/txn/fd_txn.h" -#include "../../../disco/pack/fd_microblock.h" - -struct fd_microblock_info { - union { - fd_microblock_hdr_t const * hdr; - uchar * raw; - } microblock; - /* - Always a pointer to a microblock_hdr - then transactions - */ - ulong signature_cnt; - ulong account_cnt; - ulong raw_microblock_sz; - fd_txn_p_t * txns; -}; -typedef struct fd_microblock_info fd_microblock_info_t; - -FD_PROTOTYPES_BEGIN - -void * -fd_microblock_info_new( void * mem ); - -fd_microblock_info_t * -fd_microblock_info_join( void * mem ); - -void * -fd_microblock_info_leave( fd_microblock_info_t * info ); - -void * -fd_microblock_info_delete( void * mem ); - -FD_PROTOTYPES_END - -#endif /* HEADER_fd_src_flamenco_runtime_info_fd_microblock_info_h */ diff --git a/src/flamenco/runtime/info/fd_runtime_block_info.h b/src/flamenco/runtime/info/fd_runtime_block_info.h deleted file mode 100644 index 0fef274a45..0000000000 --- a/src/flamenco/runtime/info/fd_runtime_block_info.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef HEADER_fd_src_flamenco_runtime_info_fd_runtime_block_info_h -#define HEADER_fd_src_flamenco_runtime_info_fd_runtime_block_info_h - -#include "../../../util/fd_util_base.h" -#include "../../../ballet/block/fd_microblock.h" -#include "../../../ballet/txn/fd_txn.h" - -#include "fd_microblock_batch_info.h" - -struct fd_runtime_block_info { - ulong microblock_batch_cnt; - ulong microblock_cnt; - ulong signature_cnt; - ulong txn_cnt; - ulong account_cnt; - - fd_microblock_batch_info_t * microblock_batch_infos; - - void const * raw_block; - ulong raw_block_sz; -}; -typedef struct fd_runtime_block_info fd_runtime_block_info_t; - -#endif /* HEADER_fd_src_flamenco_runtime_info_fd_runtime_block_info_h */ diff --git a/src/flamenco/runtime/tests/fd_block_harness.c b/src/flamenco/runtime/tests/fd_block_harness.c index 08761d95ae..9dd4c3b1a6 100644 --- a/src/flamenco/runtime/tests/fd_block_harness.c +++ b/src/flamenco/runtime/tests/fd_block_harness.c @@ -4,7 +4,6 @@ #include "../fd_runtime.h" #include "../fd_system_ids.h" #include "../fd_txn_account.h" -#include "../info/fd_runtime_block_info.h" #include "../program/fd_stake_program.h" #include "../program/fd_vote_program.h" #include "../sysvar/fd_sysvar_epoch_schedule.h" @@ -166,9 +165,10 @@ fd_runtime_fuzz_block_ctx_destroy( fd_solfuzz_runner_t * runner ) { /* Sets up block execution context from an input test case to execute against the runtime. Returns block_info on success and NULL on failure. */ -static fd_runtime_block_info_t * +static fd_txn_p_t * fd_runtime_fuzz_block_ctx_create( fd_solfuzz_runner_t * runner, - fd_exec_test_block_context_t const * test_ctx ) { + fd_exec_test_block_context_t const * test_ctx, + ulong * out_txn_cnt ) { fd_funk_t * funk = runner->funk; fd_bank_t * bank = runner->bank; fd_banks_t * banks = runner->banks; @@ -370,32 +370,7 @@ fd_runtime_fuzz_block_ctx_create( fd_solfuzz_runner_t * runner, fd_sysvar_cache_restore_fuzz( bank, funk, xid ); /* Prepare raw transaction pointers and block / microblock infos */ - ulong txn_cnt = test_ctx->txns_count; - - // For fuzzing, we're using a single microblock batch that contains a single microblock containing all transactions - fd_runtime_block_info_t * block_info = fd_spad_alloc( runner->spad, alignof(fd_runtime_block_info_t), sizeof(fd_runtime_block_info_t) ); - fd_microblock_batch_info_t * batch_info = fd_spad_alloc( runner->spad, alignof(fd_microblock_batch_info_t), sizeof(fd_microblock_batch_info_t) ); - fd_microblock_info_t * microblock_info = fd_spad_alloc( runner->spad, alignof(fd_microblock_info_t), sizeof(fd_microblock_info_t) ); - fd_memset( block_info, 0, sizeof(fd_runtime_block_info_t) ); - fd_memset( batch_info, 0, sizeof(fd_microblock_batch_info_t) ); - fd_memset( microblock_info, 0, sizeof(fd_microblock_info_t) ); - - block_info->microblock_batch_cnt = 1UL; - block_info->microblock_cnt = 1UL; - block_info->microblock_batch_infos = batch_info; - - batch_info->microblock_cnt = 1UL; - batch_info->microblock_infos = microblock_info; - - ulong batch_signature_cnt = 0UL; - ulong batch_txn_cnt = 0UL; - ulong batch_account_cnt = 0UL; - ulong signature_cnt = 0UL; - ulong account_cnt = 0UL; - - fd_microblock_hdr_t * microblock_hdr = fd_spad_alloc( runner->spad, alignof(fd_microblock_hdr_t), sizeof(fd_microblock_hdr_t) ); - fd_memset( microblock_hdr, 0, sizeof(fd_microblock_hdr_t) ); - + ulong txn_cnt = test_ctx->txns_count; fd_txn_p_t * txn_ptrs = fd_spad_alloc( runner->spad, alignof(fd_txn_p_t), txn_cnt * sizeof(fd_txn_p_t) ); for( ulong i=0UL; ipayload, msg_sz, TXN( txn ), NULL ) ) ) { return NULL; } - - signature_cnt += TXN( txn )->signature_cnt; - account_cnt += fd_txn_account_cnt( TXN( txn ), FD_TXN_ACCT_CAT_ALL ); } - microblock_hdr->txn_cnt = txn_cnt; - microblock_info->microblock.raw = (uchar *)microblock_hdr; - - microblock_info->signature_cnt = signature_cnt; - microblock_info->account_cnt = account_cnt; - microblock_info->txns = txn_ptrs; - - batch_signature_cnt += signature_cnt; - batch_txn_cnt += txn_cnt; - batch_account_cnt += account_cnt; - - block_info->signature_cnt = batch_info->signature_cnt = batch_signature_cnt; - block_info->txn_cnt = batch_info->txn_cnt = batch_txn_cnt; - block_info->account_cnt = batch_info->account_cnt = batch_account_cnt; - - return block_info; + *out_txn_cnt = txn_cnt; + return txn_ptrs; } -/* Takes in a block_info created from `fd_runtime_fuzz_block_ctx_create()` - and executes it against the runtime. Returns the execution result. */ +/* Takes in a list of txn_p_t created from + fd_runtime_fuzz_block_ctx_create and executes it against the runtime. + Returns the execution result. */ static int -fd_runtime_fuzz_block_ctx_exec( fd_solfuzz_runner_t * runner, - fd_funk_txn_xid_t const * xid, - fd_runtime_block_info_t * block_info ) { +fd_runtime_fuzz_block_ctx_exec( fd_solfuzz_runner_t * runner, + fd_funk_txn_xid_t const * xid, + fd_txn_p_t * txn_ptrs, + ulong txn_cnt ) { int res = 0; // Prepare. Execute. Finalize. @@ -468,9 +428,6 @@ fd_runtime_fuzz_block_ctx_exec( fd_solfuzz_runner_t * runner, return res; } - fd_txn_p_t * txn_ptrs = block_info->microblock_batch_infos[0].microblock_infos[0].txns; - ulong txn_cnt = block_info->microblock_batch_infos[0].txn_cnt; - /* Sequential transaction execution */ for( ulong i=0UL; ispad ) { /* Set up the block execution context */ - fd_runtime_block_info_t * block_info = fd_runtime_fuzz_block_ctx_create( runner, input ); - if( block_info==NULL ) { + ulong txn_cnt; + fd_txn_p_t * txn_ptrs = fd_runtime_fuzz_block_ctx_create( runner, input, &txn_cnt ); + if( txn_ptrs==NULL ) { fd_runtime_fuzz_block_ctx_destroy( runner ); return 0; } @@ -530,7 +488,7 @@ fd_solfuzz_block_run( fd_solfuzz_runner_t * runner, fd_funk_txn_xid_t xid = { .ul = { fd_bank_slot_get( runner->bank ), fd_bank_slot_get( runner->bank ) } }; /* Execute the constructed block against the runtime. */ - int res = fd_runtime_fuzz_block_ctx_exec( runner, &xid, block_info ); + int res = fd_runtime_fuzz_block_ctx_exec( runner, &xid, txn_ptrs, txn_cnt ); /* Start saving block exec results */ FD_SCRATCH_ALLOC_INIT( l, output_buf ); diff --git a/src/flamenco/runtime/tests/fd_dump_pb.c b/src/flamenco/runtime/tests/fd_dump_pb.c index 3b6d2bca8b..6ecfb51622 100644 --- a/src/flamenco/runtime/tests/fd_dump_pb.c +++ b/src/flamenco/runtime/tests/fd_dump_pb.c @@ -19,16 +19,61 @@ #define SORT_BEFORE(a,b) (a)<(b) #include "../../../util/tmpl/fd_sort.c" +struct fd_dump_account_key_node { + fd_pubkey_t key; + ulong redblack_parent; + ulong redblack_left; + ulong redblack_right; + int redblack_color; +}; +typedef struct fd_dump_account_key_node fd_dump_account_key_node_t; +#define REDBLK_T fd_dump_account_key_node_t +#define REDBLK_NAME fd_dump_account_key_map +long fd_dump_account_key_map_compare( fd_dump_account_key_node_t * left, fd_dump_account_key_node_t * right ) { + return memcmp( left->key.uc, right->key.uc, sizeof(fd_pubkey_t) ); +} +#include "../../../util/tmpl/fd_redblack.c" + +/***** CONSTANTS *****/ +static fd_pubkey_t const * fd_dump_sysvar_ids[] = { + &fd_sysvar_recent_block_hashes_id, + &fd_sysvar_clock_id, + &fd_sysvar_slot_history_id, + &fd_sysvar_slot_hashes_id, + &fd_sysvar_epoch_schedule_id, + &fd_sysvar_epoch_rewards_id, + &fd_sysvar_fees_id, + &fd_sysvar_rent_id, + &fd_sysvar_stake_history_id, + &fd_sysvar_last_restart_slot_id, + &fd_sysvar_instructions_id, +}; +static ulong const num_sysvar_entries = (sizeof(fd_dump_sysvar_ids) / sizeof(fd_pubkey_t *)); + +static fd_pubkey_t const * fd_dump_builtin_ids[] = { + &fd_solana_system_program_id, + &fd_solana_vote_program_id, + &fd_solana_stake_program_id, + &fd_solana_bpf_loader_v4_program_id, + &fd_solana_bpf_loader_deprecated_program_id, + &fd_solana_bpf_loader_program_id, + &fd_solana_bpf_loader_upgradeable_program_id, + &fd_solana_compute_budget_program_id, + &fd_solana_keccak_secp_256k_program_id, + &fd_solana_secp256r1_program_id, + &fd_solana_zk_elgamal_proof_program_id, + &fd_solana_ed25519_sig_verify_program_id, +}; +static ulong const num_loaded_builtins = (sizeof(fd_dump_builtin_ids) / sizeof(fd_pubkey_t *)); + /***** UTILITY FUNCTIONS *****/ /** GENERAL UTILITY FUNCTIONS AND MACROS **/ -static int -is_builtin_account( fd_pubkey_t const * loaded_builtins, - ulong num_loaded_builtins, - fd_pubkey_t const * account_key ) { - for( ulong j = 0; j < num_loaded_builtins; ++j ) { - if( !memcmp( account_key, &loaded_builtins[j], sizeof(fd_pubkey_t) ) ) { +static inline int +is_builtin_account( fd_pubkey_t const * account_key ) { + for( ulong j=0UL; jaddr_off); - uchar account_exists = dump_account_if_not_already_dumped( funk, xid, alut_pubkey, spad, out_account_states, out_account_states_count, alut_account ); - if( !account_exists || fd_txn_account_get_data_len( alut_account )> 5UL; // = (dlen - 56) / 32 - for( ulong i=0UL; ivote_account, spad, out_acct_states, out_acct_states_cnt, NULL ); - } -} - static void dump_sanitized_transaction( fd_funk_t * funk, fd_funk_txn_xid_t const * xid, @@ -344,237 +347,410 @@ dump_blockhash_queue( fd_blockhashes_t const * queue, /** SECONDARY FUNCTIONS **/ -static void -create_block_context_protobuf_from_block( fd_exec_test_block_context_t * block_context, - fd_banks_t * banks, - fd_bank_t * bank, - fd_funk_t * funk, - fd_funk_txn_xid_t const * xid, - fd_spad_t * spad ) { - - /* BlockContext -> acct_states */ - // Dump sysvars + builtins - fd_pubkey_t const fd_relevant_sysvar_ids[] = { - fd_sysvar_recent_block_hashes_id, - fd_sysvar_clock_id, - fd_sysvar_slot_history_id, - fd_sysvar_slot_hashes_id, - fd_sysvar_epoch_schedule_id, - fd_sysvar_epoch_rewards_id, - fd_sysvar_fees_id, - fd_sysvar_rent_id, - fd_sysvar_stake_history_id, - fd_sysvar_last_restart_slot_id, - }; +/* add_account_to_dumped_accounts adds an account to the dumped accounts + set if it does not exist already. Returns 0 if the account already + exists, and 1 if the account was added successfully. - fd_pubkey_t const loaded_builtins[] = { - fd_solana_system_program_id, - fd_solana_vote_program_id, - fd_solana_stake_program_id, - fd_solana_config_program_id, - fd_solana_zk_token_proof_program_id, - fd_solana_bpf_loader_v4_program_id, - fd_solana_address_lookup_table_program_id, - fd_solana_bpf_loader_deprecated_program_id, - fd_solana_bpf_loader_program_id, - fd_solana_bpf_loader_upgradeable_program_id, - fd_solana_compute_budget_program_id, - fd_solana_keccak_secp_256k_program_id, - fd_solana_secp256r1_program_id, - fd_solana_zk_elgamal_proof_program_id, - fd_solana_ed25519_sig_verify_program_id, + TODO: Txn dumping should be optimized to use these functions. */ +static uchar +add_account_to_dumped_accounts( fd_dump_account_key_node_t * pool, + fd_dump_account_key_node_t ** root, + fd_pubkey_t const * pubkey ) { + /* If the key already exists, return early. */ + fd_dump_account_key_node_t node = { + .key = *pubkey, }; - ulong num_sysvar_entries = (sizeof(fd_relevant_sysvar_ids) / sizeof(fd_pubkey_t)); - ulong num_loaded_builtins = (sizeof(loaded_builtins) / sizeof(fd_pubkey_t)); + if( fd_dump_account_key_map_find( pool, *root, &node ) ) { + return 0; + } - fd_stake_delegations_t const * stake_delegations = fd_bank_stake_delegations_frontier_query( banks, bank ); - ulong stake_account_cnt = fd_stake_delegations_cnt( stake_delegations ); + fd_dump_account_key_node_t * new_node = fd_dump_account_key_map_acquire( pool ); + new_node->key = *pubkey; + fd_dump_account_key_map_insert( pool, root, new_node ); + return 1; +} - fd_vote_states_t const * vote_states = fd_bank_vote_states_locking_query( bank ); - ulong vote_account_t_cnt = fd_vote_states_cnt( vote_states ); - fd_bank_vote_states_end_locking_query( bank ); +/* add_account_and_programdata_to_dumped_accounts adds an account and + its programdata account (if the account is a v3 program) to the + dumped accounts set if they do not exist already. */ +static void +add_account_and_programdata_to_dumped_accounts( fd_funk_t * funk, + fd_funk_txn_xid_t const * xid, + fd_dump_account_key_node_t * pool, + fd_dump_account_key_node_t ** root, + fd_pubkey_t const * pubkey ) { + /* Add the current account to the dumped accounts set. We can save + some time by enforcing an invariant that "if current account was + dumped, then programdata account was also dumped," so we save + ourselves a call to Funk. */ + uchar ret = add_account_to_dumped_accounts( pool, root, pubkey ); + if( ret==0 ) return; + + /* Read the account from Funk to see if its a program account and if + it needs to be dumped. */ + FD_TXN_ACCOUNT_DECL( program_account ); + int err = fd_txn_account_init_from_funk_readonly( program_account, pubkey, funk, xid ); + if( FD_UNLIKELY( err!=FD_ACC_MGR_SUCCESS ) ) { + return; + } - fd_vote_states_t const * vote_states_prev = fd_bank_vote_states_prev_locking_query( bank ); - ulong vote_account_t_1_cnt = fd_vote_states_cnt( vote_states_prev ); - fd_bank_vote_states_prev_end_locking_query( bank ); + /* Return if its not owned by the v3 loader */ + if( FD_LIKELY( memcmp( fd_txn_account_get_owner( program_account ), fd_solana_bpf_loader_upgradeable_program_id.key, sizeof(fd_pubkey_t) ) ) ) { + return; + } - fd_vote_states_t const * vote_states_prev_prev = fd_bank_vote_states_prev_prev_locking_query( bank ); - ulong vote_account_t_2_cnt = fd_vote_states_cnt( vote_states_prev_prev ); - fd_bank_vote_states_prev_prev_end_locking_query( bank ); + /* Get the program account state */ + fd_bpf_upgradeable_loader_state_t program_account_state[1]; + if( FD_UNLIKELY( !fd_bincode_decode_static( + bpf_upgradeable_loader_state, + program_account_state, + fd_txn_account_get_data( program_account ), + fd_txn_account_get_data_len( program_account ), + NULL ) ) ) { + return; + } + if( !fd_bpf_upgradeable_loader_state_is_program( program_account_state ) ) { + return; + } - ulong total_num_accounts = num_sysvar_entries + - num_loaded_builtins + - stake_account_cnt + - vote_account_t_cnt + - vote_account_t_1_cnt + - vote_account_t_2_cnt; + /* Dump the programdata address */ + add_account_to_dumped_accounts( pool, root, &program_account_state->inner.program.programdata_address ); +} - block_context->acct_states_count = 0; - block_context->acct_states = fd_spad_alloc( spad, - alignof(fd_exec_test_acct_state_t), - total_num_accounts * sizeof(fd_exec_test_acct_state_t) ); +/* add_lut_account_to_dumped_accounts adds an address lookup table + account AND all pubkeys in the lookup table to the dumped accounts + set if they do not exist already. */ +static void +add_lut_accounts_to_dumped_accounts( fd_funk_t * funk, + fd_funk_txn_xid_t const * xid, + fd_dump_account_key_node_t * pool, + fd_dump_account_key_node_t ** root, + fd_pubkey_t const * pubkey ) { + /* Add the current account to the dumped accounts set. */ + add_account_to_dumped_accounts( pool, root, pubkey ); + + /* Read the account and dump all pubkeys within the lookup table. */ + FD_TXN_ACCOUNT_DECL( lut_account ); + int err = fd_txn_account_init_from_funk_readonly( lut_account, pubkey, funk, xid ); + if( FD_UNLIKELY( err!=FD_ACC_MGR_SUCCESS ) ) { + return; + } + uchar const * data = fd_txn_account_get_data( lut_account ); + ulong data_len = fd_txn_account_get_data_len( lut_account ); - for( ulong i=0UL; iacct_states, &block_context->acct_states_count, NULL ); + /* Decode the ALUT account and dump all pubkeys within the lookup + table. */ + if( data_len>5UL; // = (dlen - 56) / 32 + for( ulong i=0UL; iacct_states, &block_context->acct_states_count, NULL ); +/* create_synthetic_vote_account_from_vote_state creates a synthetic + vote account from a vote state cache element. It fills in default + values for unspecified fields and encodes the vote state into + out_vote_account's data field. */ +static void +create_synthetic_vote_account_from_vote_state( fd_vote_state_ele_t const * vote_state, + fd_spad_t * spad, + fd_exec_test_vote_account_t * out_vote_account ) { + out_vote_account->has_vote_account = true; + fd_memcpy( out_vote_account->vote_account.address, &vote_state->vote_account, sizeof(fd_pubkey_t) ); + out_vote_account->vote_account.executable = false; + out_vote_account->vote_account.lamports = 100000UL; + fd_memcpy( out_vote_account->vote_account.owner, fd_solana_vote_program_id.key, sizeof(fd_pubkey_t) ); + out_vote_account->vote_account.has_seed_addr = false; + out_vote_account->stake = vote_state->stake; + + /* Construct the vote account data. Fill in missing fields with + arbitrary defaults (since they're not used anyways) */ + fd_vote_state_versioned_t vsv = { + .discriminant = fd_vote_state_versioned_enum_current, + .inner = { + .current = { + .node_pubkey = vote_state->node_account, + .authorized_withdrawer = vote_state->node_account, + .commission = vote_state->commission, + .root_slot = 0UL, + .has_root_slot = 0, + .prior_voters = {0}, + .last_timestamp = { + .timestamp = vote_state->last_vote_timestamp, + .slot = vote_state->last_vote_slot, + }, + } + } + }; + fd_vote_state_t * synthetic_vote_state = &vsv.inner.current; + + /* Create synthetic landed votes */ + synthetic_vote_state->votes = deq_fd_landed_vote_t_join( + deq_fd_landed_vote_t_new( + fd_spad_alloc( + spad, + deq_fd_landed_vote_t_align(), + deq_fd_landed_vote_t_footprint( 32UL ) ), + 32UL ) ); + for( ulong i=0UL; i<32UL; i++ ) { + fd_landed_vote_t elem = {0}; + deq_fd_landed_vote_t_push_tail( synthetic_vote_state->votes, elem ); } - /* BlockContext -> blockhash_queue */ - pb_bytes_array_t ** output_blockhash_queue = fd_spad_alloc( spad, - alignof(pb_bytes_array_t *), - PB_BYTES_ARRAY_T_ALLOCSIZE((FD_BLOCKHASHES_MAX) * sizeof(pb_bytes_array_t *)) ); - block_context->blockhash_queue = output_blockhash_queue; + /* Populate authoritzed voters */ + void * authorized_voters_pool_mem = fd_spad_alloc( + spad, + fd_vote_authorized_voters_pool_align(), + fd_vote_authorized_voters_pool_footprint( 5UL ) ); + void * authorized_voters_treap_mem = fd_spad_alloc( + spad, + fd_vote_authorized_voters_treap_align(), + fd_vote_authorized_voters_treap_footprint( 5UL ) ); + synthetic_vote_state->authorized_voters.pool = fd_vote_authorized_voters_pool_join( fd_vote_authorized_voters_pool_new( authorized_voters_pool_mem, 5UL ) ); + synthetic_vote_state->authorized_voters.treap = fd_vote_authorized_voters_treap_join( fd_vote_authorized_voters_treap_new( authorized_voters_treap_mem, 5UL ) ); + + /* Populate epoch credits */ + synthetic_vote_state->epoch_credits = deq_fd_vote_epoch_credits_t_join( + deq_fd_vote_epoch_credits_t_new( + fd_spad_alloc( + spad, + deq_fd_vote_epoch_credits_t_align(), + deq_fd_vote_epoch_credits_t_footprint( EPOCH_CREDITS_MAX ) ), + EPOCH_CREDITS_MAX ) ); + for( ulong i=0UL; icredits_cnt; i++ ) { + fd_vote_epoch_credits_t elem = { + .credits = vote_state->credits[i], + .prev_credits = vote_state->prev_credits[i], + .epoch = vote_state->epoch[i], + }; + deq_fd_vote_epoch_credits_t_push_tail( synthetic_vote_state->epoch_credits, elem ); + } - fd_blockhashes_t const * bhq = fd_bank_block_hash_queue_query( bank ); - dump_blockhash_queue( bhq, spad, block_context->blockhash_queue, &block_context->blockhash_queue_count ); + /* Encode the synthetic vote state */ + ulong encoded_sz = fd_vote_state_versioned_size( &vsv ); + out_vote_account->vote_account.data = fd_spad_alloc( spad, alignof(pb_bytes_array_t), PB_BYTES_ARRAY_T_ALLOCSIZE( encoded_sz ) ); + out_vote_account->vote_account.data->size = (pb_size_t)encoded_sz; - /* BlockContext -> SlotContext */ - block_context->has_slot_ctx = true; - block_context->slot_ctx.slot = fd_bank_slot_get( bank ); - // HACK FOR NOW: block height gets incremented in process_new_epoch, so we should dump block height + 1 - block_context->slot_ctx.block_height = fd_bank_block_height_get( bank ) + 1UL; - // 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 - fd_memcpy( block_context->slot_ctx.parent_bank_hash, fd_bank_bank_hash_query( bank ), sizeof(fd_pubkey_t) ); - block_context->slot_ctx.prev_slot = fd_bank_parent_slot_get( bank ); - block_context->slot_ctx.prev_lps = fd_bank_prev_lamports_per_signature_get( bank ); - block_context->slot_ctx.prev_epoch_capitalization = fd_bank_capitalization_get( bank ); + fd_bincode_encode_ctx_t encode_ctx = { + .data = out_vote_account->vote_account.data->bytes, + .dataend = out_vote_account->vote_account.data->bytes+encoded_sz, + }; + fd_vote_state_versioned_encode( &vsv, &encode_ctx ); +} - /* BlockContext -> EpochContext */ - block_context->has_epoch_ctx = true; - block_context->epoch_ctx.has_features = true; - dump_sorted_features( fd_bank_features_query( bank ), &block_context->epoch_ctx.features, spad ); - block_context->epoch_ctx.hashes_per_tick = fd_bank_hashes_per_tick_get( bank ); - block_context->epoch_ctx.ticks_per_slot = fd_bank_ticks_per_slot_get( bank ); - block_context->epoch_ctx.slots_per_year = fd_bank_slots_per_year_get( bank ); - block_context->epoch_ctx.has_inflation = true; +static void +dump_prior_vote_accounts( fd_vote_states_t const * vote_states, + fd_dump_account_key_node_t * dumped_accounts_pool, + fd_dump_account_key_node_t ** dumped_accounts_root, + fd_exec_test_vote_account_t * out_vote_accounts, + pb_size_t * out_vote_accounts_count, + fd_spad_t * spad ) { - fd_inflation_t const * inflation = fd_bank_inflation_query( bank ); - block_context->epoch_ctx.inflation = (fd_exec_test_inflation_t) { - .initial = inflation->initial, - .terminal = inflation->terminal, - .taper = inflation->taper, - .foundation = inflation->foundation, - .foundation_term = inflation->foundation_term, - }; - block_context->epoch_ctx.genesis_creation_time = fd_bank_genesis_creation_time_get( bank ); + fd_vote_states_iter_t iter_[1]; + for( fd_vote_states_iter_t * iter = fd_vote_states_iter_init( iter_, vote_states ); + !fd_vote_states_iter_done( iter ); + fd_vote_states_iter_next( iter ) ) { + fd_vote_state_ele_t const * vote_state = fd_vote_states_iter_ele( iter ); + add_account_to_dumped_accounts( dumped_accounts_pool, dumped_accounts_root, &vote_state->vote_account ); + + create_synthetic_vote_account_from_vote_state( + vote_state, + spad, + &out_vote_accounts[(*out_vote_accounts_count)++] ); + } +} + +static void +create_block_context_protobuf_from_block( fd_block_dump_ctx_t * dump_ctx, + fd_banks_t * banks, + fd_bank_t * bank, + fd_funk_t * funk ) { + /* We should use the bank fields and funk txn from the parent slot in + order to capture the block context from before the current block + was executed, since dumping is happening in the block finalize + step. */ + fd_bank_t * parent_bank = fd_banks_get_parent( banks, bank ); + fd_funk_txn_xid_t parent_xid = { .ul = { fd_bank_slot_get( parent_bank ), fd_bank_slot_get( parent_bank ) } }; + fd_exec_test_block_context_t * block_context = &dump_ctx->block_context; + ulong dump_txn_count = dump_ctx->txns_to_dump_cnt; + fd_spad_t * spad = dump_ctx->spad; + + /* Get vote and stake delegation infos */ + fd_vote_states_t const * vote_states = fd_bank_vote_states_locking_query( parent_bank ); + ulong vote_account_t_cnt = fd_vote_states_cnt( vote_states ); + fd_bank_vote_states_end_locking_query( parent_bank ); + + fd_stake_delegations_t const * stake_delegations = fd_bank_stake_delegations_frontier_query( banks, parent_bank ); + ulong stake_account_cnt = fd_stake_delegations_cnt( stake_delegations ); + + /* Collect account states in a temporary set before iterating over + them and dumping them out. */ + ulong total_num_accounts = num_sysvar_entries + /* Sysvars */ + num_loaded_builtins + /* Builtins */ + stake_account_cnt + /* Stake accounts */ + vote_account_t_cnt + /* Current vote accounts */ + dump_txn_count*128UL; /* Txn accounts upper bound */ + void * dumped_accounts_mem = fd_spad_alloc( spad, fd_dump_account_key_map_align(), fd_dump_account_key_map_footprint( total_num_accounts ) ); + fd_dump_account_key_node_t * dumped_accounts_pool = fd_dump_account_key_map_join( fd_dump_account_key_map_new( dumped_accounts_mem, total_num_accounts ) ); + fd_dump_account_key_node_t * dumped_accounts_root = NULL; + + /* BlockContext -> txns */ + block_context->txns_count = (pb_size_t)dump_txn_count; + block_context->txns = fd_spad_alloc( spad, alignof(fd_exec_test_sanitized_transaction_t), dump_ctx->txns_to_dump_cnt * sizeof(fd_exec_test_sanitized_transaction_t) ); + fd_memset( block_context->txns, 0, dump_ctx->txns_to_dump_cnt * sizeof(fd_exec_test_sanitized_transaction_t) ); + + /* Dump sanitized transactions from the transaction descriptors */ + for( ulong i=0UL; itxns_to_dump_cnt; i++ ) { + fd_txn_p_t const * txn_ptr = &dump_ctx->txns_to_dump[i]; + fd_txn_t const * txn_descriptor = TXN( txn_ptr ); + dump_sanitized_transaction( funk, &parent_xid, txn_descriptor, txn_ptr->payload, spad, &block_context->txns[i] ); + + /* Dump account + alut + programdata accounts (if applicable). + 1. Dump the raw txn account keys + 2. Dump the ALUT accounts + 3. Dump all referenced accounts in the ALUTs + 4. Dump any executable accounts */ + + // 1 + 4. Dump any account keys that are referenced by transactions + // + any programdata accounts (if applicable). + fd_acct_addr_t const * account_keys = fd_txn_get_acct_addrs( txn_descriptor, txn_ptr->payload ); + for( ushort l=0; lacct_addr_cnt; l++ ) { + fd_pubkey_t const * account_key = fd_type_pun_const( &account_keys[l] ); + add_account_and_programdata_to_dumped_accounts( funk, &parent_xid, dumped_accounts_pool, &dumped_accounts_root, account_key ); + } + + // 2 + 3 + 4. Dump any ALUT accounts + any accounts referenced in + // the ALUTs + any programdata accounts (if applicable). + fd_txn_acct_addr_lut_t const * txn_lookup_tables = fd_txn_get_address_tables_const( txn_descriptor ); + for( ushort l=0; laddr_table_lookup_cnt; l++ ) { + fd_txn_acct_addr_lut_t const * lookup_table = &txn_lookup_tables[l]; + fd_pubkey_t const * lut_key = fd_type_pun_const( txn_ptr->payload+lookup_table->addr_off ); + add_lut_accounts_to_dumped_accounts( funk, &parent_xid, dumped_accounts_pool, &dumped_accounts_root, lut_key ); + } + } - /* Dumping stake accounts for this epoch */ + /* Dump sysvars */ + for( ulong i=0UL; istake_account, spad, block_context->acct_states, &block_context->acct_states_count, NULL ); + add_account_to_dumped_accounts( dumped_accounts_pool, &dumped_accounts_root, &stake_delegation->stake_account ); } - /* Dumping vote accounts for this epoch */ - - vote_states = fd_bank_vote_states_locking_query( bank ); + /* Dump vote accounts for this epoch */ + vote_states = fd_bank_vote_states_locking_query( parent_bank ); fd_vote_states_iter_t vote_iter_[1]; for( fd_vote_states_iter_t * iter = fd_vote_states_iter_init( vote_iter_, vote_states ); !fd_vote_states_iter_done( iter ); fd_vote_states_iter_next( iter ) ) { fd_vote_state_ele_t const * vote_state = fd_vote_states_iter_ele( iter ); - dump_account_if_not_already_dumped( funk, xid, &vote_state->vote_account, spad, block_context->acct_states, &block_context->acct_states_count, NULL ); + add_account_to_dumped_accounts( dumped_accounts_pool, &dumped_accounts_root, &vote_state->vote_account ); } - - fd_bank_vote_states_end_locking_query( bank ); + fd_bank_vote_states_end_locking_query( parent_bank ); // BlockContext -> EpochContext -> vote_accounts_t_1 (vote accounts at epoch T-1) - vote_states_prev = fd_bank_vote_states_prev_locking_query( bank ); - dump_vote_accounts( funk, - xid, - vote_states_prev, - spad, - block_context->acct_states, - &block_context->acct_states_count ); - fd_bank_vote_states_prev_end_locking_query( bank ); + fd_vote_states_t const * vote_states_prev = fd_bank_vote_states_prev_locking_query( parent_bank ); + block_context->epoch_ctx.vote_accounts_t_1 = fd_spad_alloc( + spad, + alignof(fd_exec_test_vote_account_t), + sizeof(fd_exec_test_vote_account_t)*fd_vote_states_cnt( vote_states_prev ) ); + block_context->epoch_ctx.vote_accounts_t_1_count = 0U; + dump_prior_vote_accounts( + vote_states_prev, + dumped_accounts_pool, + &dumped_accounts_root, + block_context->epoch_ctx.vote_accounts_t_1, + &block_context->epoch_ctx.vote_accounts_t_1_count, + spad ); + fd_bank_vote_states_prev_end_locking_query( parent_bank ); // BlockContext -> EpochContext -> vote_accounts_t_2 (vote accounts at epoch T-2) - vote_states_prev_prev = fd_bank_vote_states_prev_prev_locking_query( bank ); - dump_vote_accounts( funk, - xid, - vote_states, - spad, - block_context->acct_states, - &block_context->acct_states_count ); - fd_bank_vote_states_prev_prev_end_locking_query( bank ); -} - -static void -create_block_context_protobuf_from_block_tx_only( fd_exec_test_block_context_t * block_context, - fd_runtime_block_info_t const * block_info, - fd_bank_t * bank, - fd_funk_t * funk, - fd_funk_txn_xid_t const * xid, - fd_spad_t * spad ) { - /* BlockContext -> txns */ - block_context->txns_count = 0U; - 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) ); - fd_memset( block_context->txns, 0, block_info->txn_cnt * sizeof(fd_exec_test_sanitized_transaction_t) ); + fd_vote_states_t const * vote_states_prev_prev = fd_bank_vote_states_prev_prev_locking_query( parent_bank ); + block_context->epoch_ctx.vote_accounts_t_2 = fd_spad_alloc( + spad, + alignof(fd_exec_test_vote_account_t), + sizeof(fd_exec_test_vote_account_t)*fd_vote_states_cnt( vote_states_prev_prev ) ); + block_context->epoch_ctx.vote_accounts_t_2_count = 0U; + dump_prior_vote_accounts( + vote_states_prev_prev, + dumped_accounts_pool, + &dumped_accounts_root, + block_context->epoch_ctx.vote_accounts_t_2, + &block_context->epoch_ctx.vote_accounts_t_2_count, + spad ); + fd_bank_vote_states_prev_prev_end_locking_query( parent_bank ); /* BlockContext -> acct_states - Allocate additional space for the remaining accounts */ - fd_exec_test_acct_state_t * current_accounts = block_context->acct_states; - block_context->acct_states = fd_spad_alloc( spad, - alignof(fd_exec_test_acct_state_t), - ( ( block_info->txn_cnt * 128UL ) + (ulong)block_context->acct_states_count ) * - sizeof(fd_exec_test_acct_state_t) ); - fd_memcpy( block_context->acct_states, current_accounts, block_context->acct_states_count * sizeof(fd_exec_test_acct_state_t) ); - - /* BlockContext -> slot_ctx -> poh - This currently needs to be done because POH verification is done after epoch boundary processing. That should probably be changed */ - fd_memcpy( block_context->slot_ctx.poh, fd_bank_poh_query( bank )->hash, sizeof(fd_pubkey_t) ); - - /* When iterating over microblocks batches and microblocks, we flatten the batches for the output block context (essentially just one big batch with several microblocks) */ - for( ulong i=0UL; imicroblock_batch_cnt; i++ ) { - fd_microblock_batch_info_t const * microblock_batch = &block_info->microblock_batch_infos[i]; - - for( ulong j=0UL; jmicroblock_cnt; j++ ) { - fd_microblock_info_t const * microblock_info = µblock_batch->microblock_infos[j]; - ulong txn_cnt = microblock_info->microblock.hdr->txn_cnt; - if (txn_cnt==0UL) continue; - - /* BlockContext -> txns */ - for( ulong k=0UL; ktxns[k]; - fd_txn_t const * txn_descriptor = TXN( txn_ptr ); - dump_sanitized_transaction( funk, xid, txn_descriptor, txn_ptr->payload, spad, &block_context->txns[block_context->txns_count++] ); - - /* BlockContext -> acct_states */ - /* 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: - 1. Dump the raw txn account keys - 2. Dump the ALUT accounts - 3. Dump all referenced accounts in the ALUTs - 4. Dump any executable accounts - 5. Dump any sysvars + builtin accounts (occurs outside of this loop) */ - - // 1. Dump any account keys that are referenced by transactions - fd_acct_addr_t const * account_keys = fd_txn_get_acct_addrs( txn_descriptor, txn_ptr->payload ); - for( ushort l=0; lacct_addr_cnt; l++ ) { - fd_pubkey_t const * account_key = fd_type_pun_const( &account_keys[l] ); - dump_account_if_not_already_dumped( funk, xid, account_key, spad, block_context->acct_states, &block_context->acct_states_count, NULL ); - } - - // 2 + 3. Dump any ALUT accounts + any accounts referenced in the ALUTs - fd_txn_acct_addr_lut_t const * txn_lookup_tables = fd_txn_get_address_tables_const( txn_descriptor ); - for( ushort l=0; laddr_table_lookup_cnt; l++ ) { - fd_txn_acct_addr_lut_t const * lookup_table = &txn_lookup_tables[l]; - dump_lut_account_and_contained_accounts( funk, xid, txn_ptr->payload, lookup_table, spad, block_context->acct_states, &block_context->acct_states_count ); - } - - // 4. Go through all dumped accounts and dump any executable accounts - ulong dumped_accounts = block_context->acct_states_count; - for( ulong l=0; lacct_states[l]; - dump_executable_account_if_exists( funk, xid, maybe_program_account, spad, block_context->acct_states, &block_context->acct_states_count ); - } - } + Iterate over the set and dump all the account keys in one pass. */ + block_context->acct_states_count = 0U; + block_context->acct_states = fd_spad_alloc( + spad, + alignof(fd_exec_test_acct_state_t), + fd_dump_account_key_map_size( dumped_accounts_pool, dumped_accounts_root )*sizeof(fd_exec_test_acct_state_t) ); + for( fd_dump_account_key_node_t * node = fd_dump_account_key_map_minimum( dumped_accounts_pool, dumped_accounts_root ); + node; + node = fd_dump_account_key_map_successor( dumped_accounts_pool, node ) ) { + FD_TXN_ACCOUNT_DECL( txn_account ); + int ret = fd_txn_account_init_from_funk_readonly( txn_account, &node->key, funk, &parent_xid ); + if( FD_UNLIKELY( ret ) ) { + continue; } + dump_account_state( txn_account, &block_context->acct_states[block_context->acct_states_count++], spad ); } + + /* BlockContext -> blockhash_queue */ + pb_bytes_array_t ** output_blockhash_queue = fd_spad_alloc( + spad, + alignof(pb_bytes_array_t *), + PB_BYTES_ARRAY_T_ALLOCSIZE((FD_BLOCKHASHES_MAX) * sizeof(pb_bytes_array_t *)) ); + block_context->blockhash_queue = output_blockhash_queue; + + fd_blockhashes_t const * bhq = fd_bank_block_hash_queue_query( parent_bank ); + dump_blockhash_queue( bhq, spad, block_context->blockhash_queue, &block_context->blockhash_queue_count ); + + /* BlockContext -> SlotContext */ + block_context->has_slot_ctx = true; + block_context->slot_ctx.slot = fd_bank_slot_get( parent_bank ); + // HACK FOR NOW: block height gets incremented in process_new_epoch, so we should dump block height + 1 + block_context->slot_ctx.block_height = fd_bank_block_height_get( parent_bank ) + 1UL; + fd_memcpy( block_context->slot_ctx.poh, fd_bank_poh_query( parent_bank ), sizeof(fd_pubkey_t) ); + fd_memcpy( block_context->slot_ctx.parent_bank_hash, fd_bank_bank_hash_query( parent_bank ), sizeof(fd_pubkey_t) ); + block_context->slot_ctx.prev_slot = fd_bank_parent_slot_get( parent_bank ); + block_context->slot_ctx.prev_lps = fd_bank_prev_lamports_per_signature_get( parent_bank ); + block_context->slot_ctx.prev_epoch_capitalization = fd_bank_capitalization_get( parent_bank ); + + /* BlockContext -> EpochContext */ + block_context->has_epoch_ctx = true; + block_context->epoch_ctx.has_features = true; + dump_sorted_features( fd_bank_features_query( parent_bank ), &block_context->epoch_ctx.features, spad ); + block_context->epoch_ctx.hashes_per_tick = fd_bank_hashes_per_tick_get( parent_bank ); + block_context->epoch_ctx.ticks_per_slot = fd_bank_ticks_per_slot_get( parent_bank ); + block_context->epoch_ctx.slots_per_year = fd_bank_slots_per_year_get( parent_bank ); + block_context->epoch_ctx.has_inflation = true; + + fd_inflation_t const * inflation = fd_bank_inflation_query( parent_bank ); + block_context->epoch_ctx.inflation = (fd_exec_test_inflation_t) { + .initial = inflation->initial, + .terminal = inflation->terminal, + .taper = inflation->taper, + .foundation = inflation->foundation, + .foundation_term = inflation->foundation_term, + }; + block_context->epoch_ctx.genesis_creation_time = fd_bank_genesis_creation_time_get( parent_bank ); } static void @@ -584,48 +760,15 @@ create_txn_context_protobuf_from_txn( fd_exec_test_txn_context_t * txn_context_m fd_txn_t const * txn_descriptor = TXN( &txn_ctx->txn ); uchar const * txn_payload = (uchar const *) txn_ctx->txn.payload; - /* We don't want to store builtins in account shared data */ - fd_pubkey_t const loaded_builtins[] = { - fd_solana_system_program_id, - fd_solana_vote_program_id, - fd_solana_stake_program_id, - // fd_solana_config_program_id, // migrated to BPF, so we should dump it - // fd_solana_zk_token_proof_program_id, - fd_solana_bpf_loader_v4_program_id, - // fd_solana_address_lookup_table_program_id, // migrated to BPF, so we should dump it - fd_solana_bpf_loader_deprecated_program_id, - fd_solana_bpf_loader_program_id, - fd_solana_bpf_loader_upgradeable_program_id, - fd_solana_compute_budget_program_id, - fd_solana_keccak_secp_256k_program_id, - fd_solana_secp256r1_program_id, - fd_solana_zk_elgamal_proof_program_id, - fd_solana_ed25519_sig_verify_program_id, - }; - const ulong num_loaded_builtins = (sizeof(loaded_builtins) / sizeof(fd_pubkey_t)); - - /* Prepare sysvar cache accounts */ - fd_pubkey_t const fd_relevant_sysvar_ids[] = { - fd_sysvar_recent_block_hashes_id, - fd_sysvar_clock_id, - fd_sysvar_slot_history_id, - fd_sysvar_slot_hashes_id, - fd_sysvar_epoch_schedule_id, - fd_sysvar_epoch_rewards_id, - fd_sysvar_fees_id, - fd_sysvar_rent_id, - fd_sysvar_stake_history_id, - fd_sysvar_last_restart_slot_id, - }; - const ulong num_sysvar_entries = (sizeof(fd_relevant_sysvar_ids) / sizeof(fd_pubkey_t)); - /* Transaction Context -> account_shared_data Contains: - Account data for regular accounts - Account data for LUT accounts - Account data for executable accounts - Account data for (almost) all sysvars - */ + + We also don't want to store builtins in account shared data due to + how Agave's bank handles them in the init phase. */ // Dump regular accounts first txn_context_msg->account_shared_data_count = 0; txn_context_msg->account_shared_data = fd_spad_alloc( spad, @@ -638,10 +781,9 @@ create_txn_context_protobuf_from_txn( fd_exec_test_txn_context_t * txn_context_m continue; } - // Make sure account is not a builtin - if( !is_builtin_account( loaded_builtins, num_loaded_builtins, &txn_ctx->account_keys[i] ) ) { + // Make sure account is not a non-migrating builtin + if( !is_builtin_account( &txn_ctx->account_keys[i] ) ) { dump_account_state( txn_account, &txn_context_msg->account_shared_data[txn_context_msg->account_shared_data_count++], spad ); - } } @@ -666,7 +808,7 @@ create_txn_context_protobuf_from_txn( fd_exec_test_txn_context_t * txn_context_m continue; } fd_pubkey_t const * referenced_addr = fd_type_pun( &lookup_addrs[writable_lut_idxs[j]] ); - if( is_builtin_account( loaded_builtins, num_loaded_builtins, referenced_addr ) ) continue; + if( is_builtin_account( referenced_addr ) ) continue; FD_TXN_ACCOUNT_DECL( referenced_account ); ret = fd_txn_account_init_from_funk_readonly( referenced_account, referenced_addr, txn_ctx->funk, txn_ctx->xid ); @@ -680,7 +822,7 @@ create_txn_context_protobuf_from_txn( fd_exec_test_txn_context_t * txn_context_m continue; } fd_pubkey_t const * referenced_addr = fd_type_pun( &lookup_addrs[readonly_lut_idxs[j]] ); - if( is_builtin_account( loaded_builtins, num_loaded_builtins, referenced_addr ) ) continue; + if( is_builtin_account( referenced_addr ) ) continue; FD_TXN_ACCOUNT_DECL( referenced_account ); ret = fd_txn_account_init_from_funk_readonly( referenced_account, referenced_addr, txn_ctx->funk, txn_ctx->xid ); @@ -699,7 +841,7 @@ create_txn_context_protobuf_from_txn( fd_exec_test_txn_context_t * txn_context_m /* Dump sysvars */ for( ulong i = 0; i < num_sysvar_entries; i++ ) { FD_TXN_ACCOUNT_DECL( txn_account ); - int ret = fd_txn_account_init_from_funk_readonly( txn_account, &fd_relevant_sysvar_ids[i], txn_ctx->funk, txn_ctx->xid ); + int ret = fd_txn_account_init_from_funk_readonly( txn_account, fd_dump_sysvar_ids[i], txn_ctx->funk, txn_ctx->xid ); if( ret != FD_ACC_MGR_SUCCESS ) { continue; } @@ -707,7 +849,7 @@ create_txn_context_protobuf_from_txn( fd_exec_test_txn_context_t * txn_context_m // Make sure the account doesn't exist in the output accounts yet int account_exists = 0; for( ulong j = 0; j < txn_ctx->accounts_cnt; j++ ) { - if ( 0 == memcmp( txn_ctx->account_keys[j].key, fd_relevant_sysvar_ids[i].uc, sizeof(fd_pubkey_t) ) ) { + if ( 0 == memcmp( txn_ctx->account_keys[j].key, fd_dump_sysvar_ids[i], sizeof(fd_pubkey_t) ) ) { account_exists = true; break; } @@ -749,22 +891,6 @@ static void create_instr_context_protobuf_from_instructions( fd_exec_test_instr_context_t * instr_context, fd_exec_txn_ctx_t const * txn_ctx, fd_instr_info_t const * instr ) { - /* Prepare sysvar cache accounts */ - fd_pubkey_t const fd_relevant_sysvar_ids[] = { - fd_sysvar_recent_block_hashes_id, - fd_sysvar_clock_id, - fd_sysvar_slot_history_id, - fd_sysvar_slot_hashes_id, - fd_sysvar_epoch_schedule_id, - fd_sysvar_epoch_rewards_id, - fd_sysvar_fees_id, - fd_sysvar_rent_id, - fd_sysvar_stake_history_id, - fd_sysvar_last_restart_slot_id, - fd_sysvar_instructions_id, - }; - const ulong num_sysvar_entries = (sizeof(fd_relevant_sysvar_ids) / sizeof(fd_pubkey_t)); - /* Program ID */ fd_memcpy( instr_context->program_id, txn_ctx->account_keys[ instr->program_id ].uc, sizeof(fd_pubkey_t) ); @@ -781,14 +907,14 @@ create_instr_context_protobuf_from_instructions( fd_exec_test_instr_context_t * /* Add sysvar cache variables */ for( ulong i = 0; i < num_sysvar_entries; i++ ) { FD_TXN_ACCOUNT_DECL( txn_account ); - int ret = fd_txn_account_init_from_funk_readonly( txn_account, &fd_relevant_sysvar_ids[i], txn_ctx->funk, txn_ctx->xid ); + int ret = fd_txn_account_init_from_funk_readonly( txn_account, fd_dump_sysvar_ids[i], txn_ctx->funk, txn_ctx->xid ); if( ret != FD_ACC_MGR_SUCCESS ) { continue; } // Make sure the account doesn't exist in the output accounts yet int account_exists = 0; for( ulong j = 0; j < txn_ctx->accounts_cnt; j++ ) { - if ( 0 == memcmp( txn_ctx->account_keys[j].key, fd_relevant_sysvar_ids[i].uc, sizeof(fd_pubkey_t) ) ) { + if ( 0 == memcmp( txn_ctx->account_keys[j].key, fd_dump_sysvar_ids[i], sizeof(fd_pubkey_t) ) ) { account_exists = true; break; } @@ -878,20 +1004,12 @@ fd_dump_instr_to_protobuf( fd_exec_txn_ctx_t * txn_ctx, create_instr_context_protobuf_from_instructions( &instr_context, txn_ctx, instr ); /* Output to file */ - ulong out_buf_size = 100 * 1024 * 1024; - uint8_t * out = fd_spad_alloc( txn_ctx->spad, alignof(uchar) , out_buf_size ); - pb_ostream_t stream = pb_ostream_from_buffer( out, out_buf_size ); + ulong out_buf_size = 100 * 1024 * 1024; + uint8_t * out = fd_spad_alloc( txn_ctx->spad, alignof(uchar) , out_buf_size ); + pb_ostream_t stream = pb_ostream_from_buffer( out, out_buf_size ); if (pb_encode(&stream, FD_EXEC_TEST_INSTR_CONTEXT_FIELDS, &instr_context)) { - char output_filepath[256]; fd_memset(output_filepath, 0, sizeof(output_filepath)); - char * position = fd_cstr_init(output_filepath); - position = fd_cstr_append_cstr(position, txn_ctx->capture_ctx->dump_proto_output_dir); - position = fd_cstr_append_cstr(position, "/instr-"); - position = fd_cstr_append_cstr(position, encoded_signature); - position = fd_cstr_append_cstr(position, "-"); - position = fd_cstr_append_ushort_as_text(position, '0', 0, instruction_idx, 3); // Assume max 3 digits - position = fd_cstr_append_cstr(position, ".instrctx"); - fd_cstr_fini(position); - + char output_filepath[1024]; + sprintf( output_filepath, "%s/instr-%s-%hu.instrctx", txn_ctx->capture_ctx->dump_proto_output_dir, encoded_signature, instruction_idx ); FILE * file = fopen(output_filepath, "wb"); if( file ) { fwrite( out, 1, stream.bytes_written, file ); @@ -922,18 +1040,12 @@ fd_dump_txn_to_protobuf( fd_exec_txn_ctx_t * txn_ctx, fd_spad_t * spad ) { create_txn_context_protobuf_from_txn( &txn_context_msg, txn_ctx, spad ); /* Output to file */ - ulong out_buf_size = 100 * 1024 * 1024; - uint8_t * out = fd_spad_alloc( spad, alignof(uint8_t), out_buf_size ); - pb_ostream_t stream = pb_ostream_from_buffer( out, out_buf_size ); + ulong out_buf_size = 100UL<<20UL; // 100 MB + uint8_t * out = fd_spad_alloc( spad, alignof(uint8_t), out_buf_size ); + pb_ostream_t stream = pb_ostream_from_buffer( out, out_buf_size ); if( pb_encode( &stream, FD_EXEC_TEST_TXN_CONTEXT_FIELDS, &txn_context_msg ) ) { - char output_filepath[256]; fd_memset( output_filepath, 0, sizeof(output_filepath) ); - char * position = fd_cstr_init( output_filepath ); - position = fd_cstr_append_cstr( position, txn_ctx->capture_ctx->dump_proto_output_dir ); - position = fd_cstr_append_cstr( position, "/txn-" ); - position = fd_cstr_append_cstr( position, encoded_signature ); - position = fd_cstr_append_cstr(position, ".txnctx"); - fd_cstr_fini(position); - + char output_filepath[1024]; + sprintf( output_filepath, "%s/txn-%s.txnctx", txn_ctx->capture_ctx->dump_proto_output_dir, encoded_signature ); FILE * file = fopen(output_filepath, "wb"); if( file ) { fwrite( out, 1, stream.bytes_written, file ); @@ -944,59 +1056,49 @@ fd_dump_txn_to_protobuf( fd_exec_txn_ctx_t * txn_ctx, fd_spad_t * spad ) { } void -fd_dump_block_to_protobuf( fd_banks_t * banks, - fd_bank_t * bank, - fd_funk_t * funk, - fd_funk_txn_xid_t const * xid, - fd_capture_ctx_t const * capture_ctx, - fd_spad_t * spad, - fd_exec_test_block_context_t * block_context_msg /* output */ ) { - /* No spad frame because these allocations must persist beyond the lifetime of this function call */ - if( FD_UNLIKELY( capture_ctx==NULL ) ) { - FD_LOG_WARNING(( "Capture context may not be NULL when dumping blocks." )); +fd_dump_block_to_protobuf_collect_tx( fd_block_dump_ctx_t * dump_ctx, + fd_txn_p_t const * txn ) { + if( FD_UNLIKELY( dump_ctx->txns_to_dump_cnt>=FD_BLOCK_DUMP_CTX_MAX_TXN_CNT ) ) { + FD_LOG_ERR(( "Please increase FD_BLOCK_DUMP_CTX_MAX_TXN_CNT to dump more than %lu transactions.", FD_BLOCK_DUMP_CTX_MAX_TXN_CNT )); return; } - create_block_context_protobuf_from_block( block_context_msg, banks, bank, funk, xid, spad ); + fd_memcpy( &dump_ctx->txns_to_dump[dump_ctx->txns_to_dump_cnt++], txn, sizeof(fd_txn_p_t) ); } void -fd_dump_block_to_protobuf_tx_only( fd_runtime_block_info_t const * block_info, - fd_bank_t * bank, - fd_funk_t * funk, - fd_funk_txn_xid_t const * xid, - fd_capture_ctx_t const * capture_ctx, - fd_spad_t * spad, - fd_exec_test_block_context_t * block_context_msg ) { - FD_SPAD_FRAME_BEGIN( spad ) { - if( FD_UNLIKELY( capture_ctx==NULL ) ) { - FD_LOG_WARNING(( "Capture context may not be NULL when dumping blocks." )); - return; - } - - if( FD_UNLIKELY( block_info==NULL ) ) { - FD_LOG_WARNING(( "Block info may not be NULL when dumping blocks." )); - return; - } +fd_dump_block_to_protobuf( fd_block_dump_ctx_t * dump_ctx, + fd_banks_t * banks, + fd_bank_t * bank, + fd_funk_t * funk, + fd_capture_ctx_t const * capture_ctx ) { +FD_SPAD_FRAME_BEGIN( dump_ctx->spad ) { + if( FD_UNLIKELY( capture_ctx==NULL ) ) { + FD_LOG_WARNING(( "Capture context may not be NULL when dumping blocks." )); + return; + } - create_block_context_protobuf_from_block_tx_only( block_context_msg, block_info, bank, funk, xid, spad ); + if( FD_UNLIKELY( dump_ctx==NULL ) ) { + FD_LOG_WARNING(( "Block dumping context may not be NULL when dumping blocks." )); + return; + } - /* Output to file */ - ulong out_buf_size = 5UL<<30UL; /* 5 GB */ - uint8_t * out = fd_spad_alloc( spad, alignof(uint8_t), out_buf_size ); - pb_ostream_t stream = pb_ostream_from_buffer( out, out_buf_size ); - if( pb_encode( &stream, FD_EXEC_TEST_BLOCK_CONTEXT_FIELDS, block_context_msg ) ) { - char output_filepath[256]; fd_memset( output_filepath, 0, sizeof(output_filepath) ); - char * position = fd_cstr_init( output_filepath ); - position = fd_cstr_append_printf( position, "%s/block-%lu.blockctx", capture_ctx->dump_proto_output_dir, fd_bank_slot_get( bank ) ); - fd_cstr_fini( position ); + /* Dump the block context */ + create_block_context_protobuf_from_block( dump_ctx, banks, bank, funk ); - FILE * file = fopen(output_filepath, "wb"); - if( file ) { - fwrite( out, 1, stream.bytes_written, file ); - fclose( file ); - } + /* Output to file */ + ulong out_buf_size = 3UL<<30UL; /* 3 GB */ + uint8_t * out = fd_spad_alloc( dump_ctx->spad, alignof(uint8_t), out_buf_size ); + pb_ostream_t stream = pb_ostream_from_buffer( out, out_buf_size ); + if( pb_encode( &stream, FD_EXEC_TEST_BLOCK_CONTEXT_FIELDS, &dump_ctx->block_context ) ) { + char output_filepath[1024]; + sprintf( output_filepath, "%s/block-%lu.blockctx", capture_ctx->dump_proto_output_dir, fd_bank_slot_get( bank ) ); + FILE * file = fopen(output_filepath, "wb"); + if( file ) { + fwrite( out, 1, stream.bytes_written, file ); + fclose( file ); } - } FD_SPAD_FRAME_END; + } +} FD_SPAD_FRAME_END; } void diff --git a/src/flamenco/runtime/tests/fd_dump_pb.h b/src/flamenco/runtime/tests/fd_dump_pb.h index d849405d9e..6ffed2a5f1 100644 --- a/src/flamenco/runtime/tests/fd_dump_pb.h +++ b/src/flamenco/runtime/tests/fd_dump_pb.h @@ -53,15 +53,86 @@ * See solana-conformance/README.md for functionality and use cases */ #include "../info/fd_instr_info.h" -#include "../info/fd_runtime_block_info.h" #include "../../vm/fd_vm.h" #include "generated/block.pb.h" #include "generated/elf.pb.h" +#include "../../../disco/fd_txn_p.h" + +/* The amount of memory allocated towards dumping blocks from ledgers */ +#define FD_BLOCK_DUMP_CTX_SPAD_MEM_MAX (5UL<<30) +#define FD_BLOCK_DUMP_CTX_MAX_TXN_CNT (10000UL) FD_PROTOTYPES_BEGIN -#define TOSTRING(x) #x -#define STRINGIFY(x) TOSTRING(x) +/***** Dumping context *****/ + +/* Persistent context for block dumping. Maintains state about + in-progress block dumping, such as any dynamic memory allocations + (which live in the spad) and the block context message. */ +struct fd_block_dump_ctx { + /* Block context message */ + fd_exec_test_block_context_t block_context; + + /* Collected transactions to dump */ + fd_txn_p_t txns_to_dump[FD_BLOCK_DUMP_CTX_MAX_TXN_CNT]; + ulong txns_to_dump_cnt; + + /* Spad for dynamic memory allocations for the block context message*/ + fd_spad_t * spad; +}; +typedef struct fd_block_dump_ctx fd_block_dump_ctx_t; + +static inline ulong +fd_block_dump_context_align( void ) { + return alignof(fd_block_dump_ctx_t); +} + +static inline ulong +fd_block_dump_context_footprint( void ) { + ulong l = FD_LAYOUT_INIT; + l = FD_LAYOUT_APPEND( l, alignof(fd_block_dump_ctx_t), sizeof(fd_block_dump_ctx_t) ); + l = FD_LAYOUT_APPEND( l, fd_spad_align(), fd_spad_footprint( FD_BLOCK_DUMP_CTX_SPAD_MEM_MAX ) ); + l = FD_LAYOUT_FINI( l, fd_spad_align() ); + return l; +} + +static inline void * +fd_block_dump_context_new( void * mem ) { + FD_SCRATCH_ALLOC_INIT( l, mem ); + fd_block_dump_ctx_t * ctx = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_block_dump_ctx_t), sizeof(fd_block_dump_ctx_t) ); + fd_spad_t * spad = FD_SCRATCH_ALLOC_APPEND( l, fd_spad_align(), fd_spad_footprint( FD_BLOCK_DUMP_CTX_SPAD_MEM_MAX ) ); + + ctx->spad = fd_spad_new( spad, FD_BLOCK_DUMP_CTX_SPAD_MEM_MAX ); + ctx->txns_to_dump_cnt = 0UL; + return ctx; +} + +static inline fd_block_dump_ctx_t * +fd_block_dump_context_join( void * mem ) { + if( FD_UNLIKELY( !mem ) ) { + FD_LOG_ERR(( "NULL mem" )); + return NULL; + } + + if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)mem, fd_block_dump_context_align() ) ) ) { + FD_LOG_ERR(( "misaligned mem" )); + return NULL; + } + + fd_block_dump_ctx_t * ctx = (fd_block_dump_ctx_t *)mem; + ctx->spad = fd_spad_join( ctx->spad ); + return ctx; +} + +/* Resets the block dump context to prepare for the next block. */ +static inline void +fd_block_dump_context_reset( fd_block_dump_ctx_t * ctx ) { + fd_memset( &ctx->block_context, 0, sizeof(ctx->block_context) ); + fd_spad_reset( ctx->spad ); + ctx->txns_to_dump_cnt = 0UL; +} + +/****** Actual dumping functions ******/ void fd_dump_instr_to_protobuf( fd_exec_txn_ctx_t * txn_ctx, @@ -71,37 +142,44 @@ fd_dump_instr_to_protobuf( fd_exec_txn_ctx_t * txn_ctx, void fd_dump_txn_to_protobuf( fd_exec_txn_ctx_t *txn_ctx, fd_spad_t * spad ); -/* Block dumping is a little bit special because the scope of the block fuzzer handles both block + new - epoch processing. Therefore, we have to dump a decent amount of state before an epoch boundary may be - crossed, and then dump the individual transactions within the block once the block has been fetched - from the blockstore. Therefore, dumping is split up into two functions. `fd_dump_block_to_protobuf` - will create an initial BlockContext type that saves some fields from the slot and epoch context, as well as any current - builtins and sysvar accounts. - - `fd_dump_block_to_protobuf_tx_only` takes an existing block context message and a runtime block and dumps - the transactions within the block to be replayed. - - CAVEATS: Currently, due to how spad frames are handled in the runtime, there is an edge case where block dumping will - fail / segfault when dumping the last block of a partitioned epoch rewards distribution run. This will be fixed once the - lifetime of the partitions can exist beyond the rewards distribution period so that we don't have to push and pop - spad frames in disjoint sections of the runtime. */ +/* Block dumping is a little bit different than the other harnesses due + to the architecture of our system. Unlike the other dumping + functions, blocks are dumped in two separate stages - transaction + execution and block finalization. Transactions are streamed into + the exec tile as they come in from the dispatcher, so we maintain a + running list of transaction descriptors to dump within the dumping + context (using fd_dump_block_to_protobuf_collect_tx). When the block + is finalized, we take the accumulated transaction descriptors and + convert them into Protobuf messages using + fd_dump_block_to_protobuf, along with other fields in the slot / + epoch context and any stake, vote, and transaction accounts. + + How it works in the replay tile: + + ...boot up backtest... + unprivledged_init() { + fd_block_dump_context_new() + } + + ...start executing transactions... + + while( txns_to_execute ) { + fd_dump_block_to_protobuf_collect_tx() + } + + ...finalize the block... + fd_dump_block_to_protobuf() + fd_block_dump_context_reset() */ void -fd_dump_block_to_protobuf( fd_banks_t * banks, - fd_bank_t * bank, - fd_funk_t * funk, - fd_funk_txn_xid_t const * xid, - fd_capture_ctx_t const * capture_ctx, - fd_spad_t * spad, - fd_exec_test_block_context_t * block_context_msg /* output */ ); +fd_dump_block_to_protobuf_collect_tx( fd_block_dump_ctx_t * dump_ctx, + fd_txn_p_t const * txn ); void -fd_dump_block_to_protobuf_tx_only( fd_runtime_block_info_t const * block_info, - fd_bank_t * bank, - fd_funk_t * funk, - fd_funk_txn_xid_t const * xid, - fd_capture_ctx_t const * capture_ctx, - fd_spad_t * spad, - fd_exec_test_block_context_t * block_context_msg ); +fd_dump_block_to_protobuf( fd_block_dump_ctx_t * dump_ctx, + fd_banks_t * banks, + fd_bank_t * bank, + fd_funk_t * funk, + fd_capture_ctx_t const * capture_ctx ); void fd_dump_vm_syscall_to_protobuf( fd_vm_t const * vm,