From 8b176e00f9156c5667f16f9b07b106ab6075544d Mon Sep 17 00:00:00 2001 From: Richard Patel Date: Sat, 27 Apr 2024 05:05:28 +0000 Subject: [PATCH] runtime: vote program hardening, support 'tower sync' - types: fd_treap and fd_deque alloc helper to accept custom max - sysvar: define max element count for 'slot hashes', 'recent block hashes', and 'stake history' - types: change collection 'max' to 'min', update deserializer to always allocate enough capacity for collections - types: remove 'SMALL_DEQUE' error - program: support vote program 'tower sync' instructions - runtime: fix typo in FD_SYSVAR_STAKE_HISTORY_CAP A note on hardening: Previously, Firedancer used Agave's size limits for various dynamically sized bincode data structures, such as the lockouts deque of the vote instruction and vote state. A malicious user could provide a oversize structure in instruction data though. Similarly, a fuzzer could provide a corrupted vote state, though this is assumed to not be possible on a real network. We did not handle this case correctly and would instead fail to deserialize the input (potentially leading to a different execution result). The real item count limit of dynamically sized structures in native programs is derived from size limitations: instruction data size is ~1.2kB max, account size is 10MiB max. This patch updates fd_types to consider the previous 'max' item count limits as minimums instead. If any larger structure is provided, the deserializer will allocate as much as needed. (Provably bounded as the deserializer always advances when it makes an allocation, and verifies that the serialization is valid before allocating) This handles all cases correctly. If the user provides 0 vote lockouts, we allocate 31 (the minimum), which is enough space to correctly execute native program business logic, which might append more entries to the structure. If the user provides 100 vote lockouts, we allocate 100, as that exceeds the minimum. However, we do still need to ensure that the native program never attempts to add more items to a collection in the oversize case. This is out of scope for this PR. --- src/flamenco/genesis/fd_genesis_create.c | 4 +- .../runtime/context/fd_exec_epoch_ctx.c | 29 +- src/flamenco/runtime/fd_runtime.c | 7 +- .../runtime/program/fd_vote_program.c | 187 ++++++++---- .../runtime/sysvar/fd_sysvar_recent_hashes.h | 13 +- .../runtime/sysvar/fd_sysvar_slot_hashes.c | 5 +- .../runtime/sysvar/fd_sysvar_slot_hashes.h | 14 +- .../runtime/sysvar/fd_sysvar_stake_history.c | 8 +- .../runtime/sysvar/fd_sysvar_stake_history.h | 7 + .../runtime/tests/fd_exec_instr_test.c | 3 +- src/flamenco/types/fd_bincode.h | 1 - src/flamenco/types/fd_types.c | 285 +++++++++++------- src/flamenco/types/fd_types.h | 104 +++---- src/flamenco/types/fd_types.json | 28 +- src/flamenco/types/gen_stubs.py | 92 +++--- 15 files changed, 471 insertions(+), 316 deletions(-) diff --git a/src/flamenco/genesis/fd_genesis_create.c b/src/flamenco/genesis/fd_genesis_create.c index e109c4b6e4..52f2915465 100644 --- a/src/flamenco/genesis/fd_genesis_create.c +++ b/src/flamenco/genesis/fd_genesis_create.c @@ -123,8 +123,8 @@ genesis_create( void * buf, vs->node_pubkey = options->identity_pubkey; vs->authorized_withdrawer = options->identity_pubkey; vs->commission = 100; - vs->authorized_voters.pool = fd_vote_authorized_voters_pool_alloc ( fd_scratch_virtual() ); - vs->authorized_voters.treap = fd_vote_authorized_voters_treap_alloc( fd_scratch_virtual() ); + vs->authorized_voters.pool = fd_vote_authorized_voters_pool_alloc ( fd_scratch_virtual(), 1UL ); + vs->authorized_voters.treap = fd_vote_authorized_voters_treap_alloc( fd_scratch_virtual(), 1UL ); fd_vote_authorized_voter_t * ele = fd_vote_authorized_voters_pool_ele_acquire( vs->authorized_voters.pool ); diff --git a/src/flamenco/runtime/context/fd_exec_epoch_ctx.c b/src/flamenco/runtime/context/fd_exec_epoch_ctx.c index f5ae0245d5..3c33426f9d 100644 --- a/src/flamenco/runtime/context/fd_exec_epoch_ctx.c +++ b/src/flamenco/runtime/context/fd_exec_epoch_ctx.c @@ -1,5 +1,6 @@ #include "fd_exec_epoch_ctx.h" #include +#include "../sysvar/fd_sysvar_stake_history.h" /* TODO remove this */ #define MAX_LG_SLOT_CNT 10UL @@ -18,13 +19,13 @@ fd_exec_epoch_ctx_footprint_ext( fd_exec_epoch_ctx_layout_t * layout, fd_memset( layout, 0, sizeof(fd_exec_epoch_ctx_layout_t) ); layout->vote_acct_max = vote_acct_max; - ulong stake_votes_sz = fd_vote_accounts_pair_t_map_footprint( vote_acct_max ); if( !stake_votes_sz ) return 0UL; - ulong stake_delegations_sz = fd_delegation_pair_t_map_footprint ( vote_acct_max ); if( !stake_delegations_sz ) return 0UL; - ulong stake_history_treap_sz = fd_stake_history_treap_footprint( FD_STAKE_HISTORY_MAX ); if( !stake_history_treap_sz ) FD_LOG_CRIT(( "invalid fd_stake_history_treap footprint" )); - ulong stake_history_pool_sz = fd_stake_history_pool_footprint ( FD_STAKE_HISTORY_MAX ); if( !stake_history_pool_sz ) FD_LOG_CRIT(( "invalid fd_stake_history_pool footprint" )); - ulong next_epoch_stakes_sz = fd_vote_accounts_pair_t_map_footprint( vote_acct_max ); if( !next_epoch_stakes_sz ) return 0UL; - ulong leaders_sz = fd_epoch_leaders_footprint( MAX_PUB_CNT, MAX_SLOTS_CNT ); if( !leaders_sz ) FD_LOG_CRIT(( "invalid fd_epoch_leaders footprint" )); - ulong bank_hash_cmp_sz = fd_bank_hash_cmp_footprint( MAX_LG_SLOT_CNT ); if( !bank_hash_cmp_sz ) FD_LOG_CRIT(( "invalid fd_bank_hash_cmp footprint" )); + ulong stake_votes_sz = fd_vote_accounts_pair_t_map_footprint( vote_acct_max ); if( !stake_votes_sz ) return 0UL; + ulong stake_delegations_sz = fd_delegation_pair_t_map_footprint ( vote_acct_max ); if( !stake_delegations_sz ) return 0UL; + ulong stake_history_treap_sz = fd_stake_history_treap_footprint( FD_SYSVAR_STAKE_HISTORY_CAP ); if( !stake_history_treap_sz ) FD_LOG_CRIT(( "invalid fd_stake_history_treap footprint" )); + ulong stake_history_pool_sz = fd_stake_history_pool_footprint ( FD_SYSVAR_STAKE_HISTORY_CAP ); if( !stake_history_pool_sz ) FD_LOG_CRIT(( "invalid fd_stake_history_pool footprint" )); + ulong next_epoch_stakes_sz = fd_vote_accounts_pair_t_map_footprint( vote_acct_max ); if( !next_epoch_stakes_sz ) return 0UL; + ulong leaders_sz = fd_epoch_leaders_footprint( MAX_PUB_CNT, MAX_SLOTS_CNT ); if( !leaders_sz ) FD_LOG_CRIT(( "invalid fd_epoch_leaders footprint" )); + ulong bank_hash_cmp_sz = fd_bank_hash_cmp_footprint( MAX_LG_SLOT_CNT ); if( !bank_hash_cmp_sz ) FD_LOG_CRIT(( "invalid fd_bank_hash_cmp footprint" )); FD_SCRATCH_ALLOC_INIT( l, 0 ); FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_exec_epoch_ctx_t), sizeof(fd_exec_epoch_ctx_t) ); @@ -81,14 +82,14 @@ fd_exec_epoch_ctx_new( void * mem, //void * leaders_mem = (void *)( (ulong)mem + layout->leaders_off ); void * bank_hash_cmp_mem = (void *)( (ulong)mem + layout->bank_hash_cmp_off ); - fd_vote_accounts_pair_t_map_new( stake_votes_mem, vote_acct_max ); - fd_delegation_pair_t_map_new ( stake_delegations_mem, vote_acct_max ); - fd_stake_history_treap_new ( stake_history_treap_mem, FD_STAKE_HISTORY_MAX ); - fd_stake_history_pool_new ( stake_history_pool_mem, FD_STAKE_HISTORY_MAX ); - fd_vote_accounts_pair_t_map_new( next_epoch_stakes_mem, vote_acct_max ); + fd_vote_accounts_pair_t_map_new( stake_votes_mem, vote_acct_max ); + fd_delegation_pair_t_map_new ( stake_delegations_mem, vote_acct_max ); + fd_stake_history_treap_new ( stake_history_treap_mem, FD_SYSVAR_STAKE_HISTORY_CAP ); + fd_stake_history_pool_new ( stake_history_pool_mem, FD_SYSVAR_STAKE_HISTORY_CAP ); + fd_vote_accounts_pair_t_map_new( next_epoch_stakes_mem, vote_acct_max ); //TODO support separate epoch leaders new and init //fd_epoch_leaders_new ( leaders_mem, MAX_PUB_CNT, MAX_SLOTS_CNT ); - fd_bank_hash_cmp_new ( bank_hash_cmp_mem, MAX_LG_SLOT_CNT ); + fd_bank_hash_cmp_new ( bank_hash_cmp_mem, MAX_LG_SLOT_CNT ); FD_COMPILER_MFENCE(); self->magic = FD_EXEC_EPOCH_CTX_MAGIC; @@ -254,7 +255,7 @@ fd_exec_epoch_ctx_fixup_memory( fd_exec_epoch_ctx_t * epoch_ctx, if( fd_stake_history_treap_ele_cnt( new_treap ) ) FD_LOG_ERR(( "epoch_ctx->stake_history not empty" )); - if( fd_stake_history_pool_max( new_pool ) != FD_STAKE_HISTORY_MAX ) + if( fd_stake_history_pool_max( new_pool ) != FD_SYSVAR_STAKE_HISTORY_CAP ) FD_LOG_ERR(( "epoch_ctx->stake_history corrupt" )); if( FD_LIKELY( old_treap ) ) { /* not initialized by genesis */ diff --git a/src/flamenco/runtime/fd_runtime.c b/src/flamenco/runtime/fd_runtime.c index f3de0861ed..a5b6b378e0 100644 --- a/src/flamenco/runtime/fd_runtime.c +++ b/src/flamenco/runtime/fd_runtime.c @@ -5,6 +5,7 @@ #include "sysvar/fd_sysvar_cache.h" #include "sysvar/fd_sysvar_clock.h" #include "sysvar/fd_sysvar_epoch_schedule.h" +#include "sysvar/fd_sysvar_recent_hashes.h" #include "sysvar/fd_sysvar_stake_history.h" #include "sysvar/fd_sysvar.h" #include "../../ballet/base58/fd_base58.h" @@ -89,7 +90,7 @@ fd_runtime_init_bank_from_genesis( fd_exec_slot_ctx_t * slot_ctx, slot_ctx->slot_bank.block_height = 0UL; fd_block_block_hash_entry_t *hashes = slot_ctx->slot_bank.recent_block_hashes.hashes = - deq_fd_block_block_hash_entry_t_alloc(slot_ctx->valloc); + deq_fd_block_block_hash_entry_t_alloc( slot_ctx->valloc, FD_SYSVAR_RECENT_HASHES_CAP ); fd_block_block_hash_entry_t *elem = deq_fd_block_block_hash_entry_t_push_head_nocopy(hashes); fd_block_block_hash_entry_new(elem); fd_memcpy(elem->blockhash.hash, genesis_hash, FD_SHA256_HASH_SZ); @@ -1183,7 +1184,7 @@ fd_runtime_finalize_txns_tpool( fd_exec_slot_ctx_t * slot_ctx, for ( deq_fd_block_block_hash_entry_t_iter_t iter = deq_fd_block_block_hash_entry_t_iter_init( hashes_deque ); !deq_fd_block_block_hash_entry_t_iter_done( hashes_deque, iter ); iter = deq_fd_block_block_hash_entry_t_iter_next( hashes_deque, iter ) ) { - /* If the block hash entry matches the transactions recent block hash, we skip thje hash */ + /* If the block hash entry matches the transactions recent block hash, we skip the hash */ fd_block_block_hash_entry_t * entry = deq_fd_block_block_hash_entry_t_iter_ele( hashes_deque, iter ); if ( memcmp( entry->blockhash.hash, recent_blockhash->hash, sizeof(fd_hash_t) ) == 0 ) { skip_hash = 1; @@ -3796,7 +3797,7 @@ fd_runtime_replay( fd_runtime_ctx_t * state, fd_runtime_args_t * args ) { prev_slot = slot; } - if( state->tpool ) { + if( state->tpool ) { fd_tpool_fini( state->tpool ); } diff --git a/src/flamenco/runtime/program/fd_vote_program.c b/src/flamenco/runtime/program/fd_vote_program.c index 81fb406546..da8bdd0a19 100644 --- a/src/flamenco/runtime/program/fd_vote_program.c +++ b/src/flamenco/runtime/program/fd_vote_program.c @@ -112,8 +112,8 @@ from_vote_state_1_14_11( fd_vote_state_t * vote_state, // https://github.com/firedancer-io/solana/blob/da470eef4652b3b22598a1f379cacfe82bd5928d/sdk/program/src/vote/state/vote_state_1_14_11.rs#L65-L69 if( vote_state->votes ) { - vote_state_1_14_11->votes = deq_fd_vote_lockout_t_alloc( valloc ); - for( deq_fd_vote_lockout_t_iter_t iter = deq_fd_landed_vote_t_iter_init( vote_state->votes ); + vote_state_1_14_11->votes = deq_fd_vote_lockout_t_alloc( valloc, deq_fd_landed_vote_t_cnt( vote_state->votes ) ); + for( deq_fd_landed_vote_t_iter_t iter = deq_fd_landed_vote_t_iter_init( vote_state->votes ); !deq_fd_landed_vote_t_iter_done( vote_state->votes, iter ); iter = deq_fd_landed_vote_t_iter_next( vote_state->votes, iter ) ) { fd_landed_vote_t const * landed_vote = deq_fd_landed_vote_t_iter_ele_const( vote_state->votes, iter ); @@ -230,8 +230,8 @@ authorized_voters_new( ulong epoch, fd_pubkey_t const * pubkey, fd_valloc_t valloc, fd_vote_authorized_voters_t * authorized_voters /* out */ ) { - authorized_voters->pool = fd_vote_authorized_voters_pool_alloc( valloc ); - authorized_voters->treap = fd_vote_authorized_voters_treap_alloc( valloc ); + authorized_voters->pool = fd_vote_authorized_voters_pool_alloc ( valloc, FD_VOTE_AUTHORIZED_VOTERS_MIN ); + authorized_voters->treap = fd_vote_authorized_voters_treap_alloc( valloc, FD_VOTE_AUTHORIZED_VOTERS_MIN ); fd_vote_authorized_voter_t * ele = fd_vote_authorized_voters_pool_ele_acquire( authorized_voters->pool ); ele->epoch = epoch; @@ -265,8 +265,11 @@ static void authorized_voters_purge_authorized_voters( fd_vote_authorized_voters_t * self, ulong current_epoch ) { + FD_SCRATCH_SCOPE_BEGIN { + // https://github.com/firedancer-io/solana/blob/da470eef4652b3b22598a1f379cacfe82bd5928d/sdk/program/src/vote/authorized_voters.rs#L42-L46 - ulong expired_keys[FD_VOTE_AUTHORIZED_VOTERS_MAX] = { 0 }; /* TODO use fd_set */ + + ulong expired_keys[FD_VOTE_AUTHORIZED_VOTERS_MIN] = { 0 }; /* TODO use fd_set */ ulong key_cnt = 0; for( fd_vote_authorized_voters_treap_fwd_iter_t iter = fd_vote_authorized_voters_treap_fwd_iter_init( self->treap, self->pool ); @@ -288,6 +291,10 @@ authorized_voters_purge_authorized_voters( fd_vote_authorized_voters_t * self, // https://github.com/firedancer-io/solana/blob/da470eef4652b3b22598a1f379cacfe82bd5928d/sdk/program/src/vote/authorized_voters.rs#L56 FD_TEST( !authorized_voters_is_empty( self ) ); + + } + FD_SCRATCH_SCOPE_END; + } // https://github.com/firedancer-io/solana/blob/da470eef4652b3b22598a1f379cacfe82bd5928d/sdk/program/src/vote/authorized_voters.rs#L87-L108 @@ -352,7 +359,12 @@ landed_votes_from_lockouts( fd_vote_lockout_t * lockouts, fd_valloc_t valloc ) { if( !lockouts ) return NULL; - fd_landed_vote_t * landed_votes = deq_fd_landed_vote_t_alloc( valloc ); + /* Allocate MAX_LOCKOUT_HISTORY (sane case) by default. In case the + vote account is corrupt, allocate as many entries are needed. */ + + ulong cnt = deq_fd_vote_lockout_t_cnt( lockouts ); + cnt = fd_ulong_max( cnt, MAX_LOCKOUT_HISTORY ); + fd_landed_vote_t * landed_votes = deq_fd_landed_vote_t_alloc( valloc, cnt ); for( deq_fd_vote_lockout_t_iter_t iter = deq_fd_vote_lockout_t_iter_init( lockouts ); !deq_fd_vote_lockout_t_iter_done( lockouts, iter ); @@ -802,8 +814,8 @@ set_vote_account_state( ulong vote_acct_idx, static inline fd_vote_lockout_t * last_lockout( fd_vote_state_t * self ) { + if( deq_fd_landed_vote_t_empty( self->votes ) ) return NULL; fd_landed_vote_t * last_vote = deq_fd_landed_vote_t_peek_tail( self->votes ); - if( FD_UNLIKELY( !last_vote ) ) return NULL; return &last_vote->lockout; } @@ -837,10 +849,13 @@ contains_slot( fd_vote_state_t * vote_state, ulong slot ) { // https://github.com/firedancer-io/solana/blob/da470eef4652b3b22598a1f379cacfe82bd5928d/programs/vote/src/vote_state/mod.rs#L178 static int check_update_vote_state_slots_are_valid( fd_vote_state_t * vote_state, - fd_vote_state_update_t * vote_state_update, + fd_vote_lockout_t * proposed_lockouts, + uchar * proposed_has_root, + ulong * proposed_root, + fd_hash_t const * proposed_hash, fd_slot_hashes_t const * slot_hashes, fd_exec_instr_ctx_t const * ctx ) { - if( FD_UNLIKELY( deq_fd_vote_lockout_t_empty( vote_state_update->lockouts ) ) ) { + if( FD_UNLIKELY( deq_fd_vote_lockout_t_empty( proposed_lockouts ) ) ) { ctx->txn_ctx->custom_err = FD_VOTE_ERR_EMPTY_SLOTS; return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR; } @@ -848,7 +863,7 @@ check_update_vote_state_slots_are_valid( fd_vote_state_t * vote_state, if( !deq_fd_landed_vote_t_empty( vote_state->votes ) ) last_vote = deq_fd_landed_vote_t_peek_tail( vote_state->votes ); if( FD_LIKELY( last_vote ) ) { - if( FD_UNLIKELY( deq_fd_vote_lockout_t_peek_tail( vote_state_update->lockouts )->slot <= + if( FD_UNLIKELY( deq_fd_vote_lockout_t_peek_tail_const( proposed_lockouts )->slot <= last_vote->lockout.slot ) ) { ctx->txn_ctx->custom_err = FD_VOTE_ERROR_VOTE_TOO_OLD; return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR; @@ -857,7 +872,7 @@ check_update_vote_state_slots_are_valid( fd_vote_state_t * vote_state, /* must be nonempty, checked above */ ulong last_vote_state_update_slot = - deq_fd_vote_lockout_t_peek_tail( vote_state_update->lockouts )->slot; + deq_fd_vote_lockout_t_peek_tail_const( proposed_lockouts )->slot; /* https://github.com/solana-labs/solana/blob/v1.18.9/programs/vote/src/vote_state/mod.rs#L200-L202 */ @@ -879,14 +894,14 @@ check_update_vote_state_slots_are_valid( fd_vote_state_t * vote_state, return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR; } - if( vote_state_update->has_root ) { - ulong const proposed_root = vote_state_update->root; - if( proposed_root < earliest_slot_hash_in_history ) { + if( *proposed_has_root ) { + ulong const proposed_root_ = *proposed_root; + if( proposed_root_ < earliest_slot_hash_in_history ) { /* https://github.com/solana-labs/solana/blob/v1.18.9/programs/vote/src/vote_state/mod.rs#L220 */ - vote_state_update->has_root = vote_state->has_root_slot; - vote_state_update->root = vote_state->root_slot; + *proposed_has_root = vote_state->has_root_slot; + *proposed_root = vote_state->root_slot; /* https://github.com/solana-labs/solana/blob/v1.18.9/programs/vote/src/vote_state/mod.rs#L222-L228 */ @@ -896,9 +911,9 @@ check_update_vote_state_slots_are_valid( fd_vote_state_t * vote_state, iter = deq_fd_landed_vote_t_iter_prev( vote_state->votes, iter ) ) { fd_landed_vote_t const * vote = deq_fd_landed_vote_t_iter_ele_const( vote_state->votes, iter ); - if( vote->lockout.slot <= proposed_root ) { - vote_state_update->has_root = 1; - vote_state_update->root = vote->lockout.slot; + if( vote->lockout.slot <= proposed_root_ ) { + *proposed_has_root = 1; + *proposed_root = vote->lockout.slot; break; } @@ -908,10 +923,10 @@ check_update_vote_state_slots_are_valid( fd_vote_state_t * vote_state, FD_SCRATCH_SCOPE_BEGIN { - int has_root_to_check = vote_state_update->has_root; - ulong root_to_check = vote_state_update->root; + int has_root_to_check = *proposed_has_root; + ulong root_to_check = *proposed_root; ulong vote_state_update_index = 0; - ulong lockouts_len = deq_fd_vote_lockout_t_cnt( vote_state_update->lockouts ); + ulong lockouts_len = deq_fd_vote_lockout_t_cnt( proposed_lockouts ); ulong slot_hashes_index = deq_fd_slot_hash_t_cnt( slot_hashes->hashes ); ulong * vote_state_update_indexes_to_filter = fd_scratch_alloc( alignof(ulong), lockouts_len * sizeof(ulong) ); @@ -921,13 +936,13 @@ check_update_vote_state_slots_are_valid( fd_vote_state_t * vote_state, ulong proposed_vote_slot = fd_ulong_if( has_root_to_check, root_to_check, - deq_fd_vote_lockout_t_peek_index_const( vote_state_update->lockouts, + deq_fd_vote_lockout_t_peek_index_const( proposed_lockouts, vote_state_update_index ) ->slot ); if( !has_root_to_check && vote_state_update_index > 0 && proposed_vote_slot <= deq_fd_vote_lockout_t_peek_index_const( - vote_state_update->lockouts, + proposed_lockouts, fd_ulong_checked_sub_expect( vote_state_update_index, 1, @@ -999,12 +1014,12 @@ check_update_vote_state_slots_are_valid( fd_vote_state_t * vote_state, } } - if( vote_state_update_index != deq_fd_vote_lockout_t_cnt( vote_state_update->lockouts ) ) { + if( vote_state_update_index != deq_fd_vote_lockout_t_cnt( proposed_lockouts ) ) { ctx->txn_ctx->custom_err = FD_VOTE_ERR_SLOTS_MISMATCH; return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR; } if( memcmp( &deq_fd_slot_hash_t_peek_index_const( slot_hashes->hashes, slot_hashes_index )->hash, - &vote_state_update->hash, + proposed_hash, sizeof( fd_hash_t ) ) != 0 ) { ctx->txn_ctx->custom_err = FD_VOTE_ERR_SLOTS_HASH_MISMATCH; return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR; @@ -1012,7 +1027,7 @@ check_update_vote_state_slots_are_valid( fd_vote_state_t * vote_state, vote_state_update_index = 0; for( ulong i = 0; i < filter_index; i++ ) { - deq_fd_vote_lockout_t_pop_idx_tail( vote_state_update->lockouts, + deq_fd_vote_lockout_t_pop_idx_tail( proposed_lockouts, vote_state_update_indexes_to_filter[i] ); } @@ -1542,8 +1557,9 @@ process_vote( fd_vote_state_t * vote_state, } // https://github.com/firedancer-io/solana/blob/da470eef4652b3b22598a1f379cacfe82bd5928d/programs/vote/src/vote_state/mod.rs#L735-L740 - ulong scratch[128]; - ulong * vote_slots = deq_ulong_join( deq_ulong_new( scratch ) ); + ulong vote_slots_cnt = deq_ulong_cnt( vote->slots ); + uchar * vote_slots_mem = fd_scratch_alloc( deq_ulong_align(), deq_ulong_footprint( vote_slots_cnt ) ); + ulong * vote_slots = deq_ulong_join( deq_ulong_new( vote_slots_mem, vote_slots_cnt ) ); for( deq_ulong_iter_t iter = deq_ulong_iter_init( vote->slots ); !deq_ulong_iter_done( vote->slots, iter ); iter = deq_ulong_iter_next( vote->slots, iter ) ) { @@ -1688,10 +1704,13 @@ do_process_vote_state_update( fd_vote_state_t * vote_state, fd_valloc_t scratch_valloc = fd_scratch_virtual(); - rc = check_update_vote_state_slots_are_valid( vote_state, vote_state_update, slot_hashes, ctx ); + rc = check_update_vote_state_slots_are_valid( + vote_state, + vote_state_update->lockouts, &vote_state_update->has_root, &vote_state_update->root, &vote_state_update->hash, + slot_hashes, ctx ); if( FD_UNLIKELY( rc ) ) return rc; - fd_landed_vote_t * landed_votes = deq_fd_landed_vote_t_alloc( scratch_valloc ); + fd_landed_vote_t * landed_votes = deq_fd_landed_vote_t_alloc( scratch_valloc, deq_fd_vote_lockout_t_cnt( vote_state_update->lockouts ) ); for( deq_fd_vote_lockout_t_iter_t iter = deq_fd_vote_lockout_t_iter_init( vote_state_update->lockouts ); !deq_fd_vote_lockout_t_iter_done( vote_state_update->lockouts, iter ); @@ -1745,6 +1764,68 @@ process_vote_state_update( ulong vote_acct_idx, return rc; } +/* https://github.com/firedancer-io/solana/blob/main/programs/vote/src/vote_state/mod.rs#L1198-L1226 */ + +static int +do_process_tower_sync( fd_vote_state_t * vote_state, + fd_slot_hashes_t const * slot_hashes, + ulong epoch, + ulong slot, + fd_tower_sync_t * tower_sync, + fd_exec_instr_ctx_t const * ctx /* feature_set */ ) { + + do { + int err = check_update_vote_state_slots_are_valid( + vote_state, + tower_sync->lockouts, &tower_sync->has_root, &tower_sync->root, &tower_sync->hash, + slot_hashes, ctx ); + if( FD_UNLIKELY( err ) ) return err; + } while(0); + + fd_scratch_push(); + int err = process_new_vote_state( + vote_state, + landed_votes_from_lockouts( tower_sync->lockouts, fd_scratch_virtual() ), + tower_sync->has_root, + tower_sync->root, + tower_sync->has_timestamp ? &tower_sync->timestamp : NULL, + epoch, + slot, + ctx ); + fd_scratch_pop(); + + return err; +} + +/* https://github.com/firedancer-io/solana/blob/main/programs/vote/src/vote_state/mod.rs#L1178-L1196 */ + +static int +process_tower_sync( ulong vote_acct_idx, + fd_borrowed_account_t * vote_account, + fd_slot_hashes_t const * slot_hashes, + fd_sol_sysvar_clock_t const * clock, + fd_tower_sync_t * tower_sync, + fd_pubkey_t const * signers[static FD_TXN_SIG_MAX], + fd_exec_instr_ctx_t const * ctx /* feature_set */ ) { + + /* https://github.com/firedancer-io/solana/blob/main/programs/vote/src/vote_state/mod.rs#L1186 */ + + fd_vote_state_t vote_state; + do { + int err = verify_and_get_vote_state( vote_account, clock, signers, &vote_state ); + if( FD_UNLIKELY( err ) ) return err; + } while(0); + + /* https://github.com/firedancer-io/solana/blob/main/programs/vote/src/vote_state/mod.rs#L1187-L1194 */ + + do { + int err = do_process_tower_sync( &vote_state, slot_hashes, clock->epoch, clock->slot, tower_sync, ctx ); + if( FD_UNLIKELY( err ) ) return err; + } while(0); + + return set_vote_account_state( vote_acct_idx, vote_account, &vote_state, ctx ); +} + /**********************************************************************/ /* FD-only encoders / decoders (doesn't map directly to Labs impl) */ /**********************************************************************/ @@ -1761,16 +1842,13 @@ fd_vote_decode_compact_update( fd_compact_vote_state_update_t * compact_update, vote_update->has_root = 0; vote_update->root = ULONG_MAX; } - if( vote_update->lockouts ) FD_LOG_WARNING(( "MEM LEAK: %p", (void *)vote_update->lockouts )); fd_valloc_t valloc = fd_scratch_virtual(); ulong lockouts_len = compact_update->lockouts_len; - if( lockouts_len > deq_fd_vote_lockout_t_max( vote_update->lockouts ) ) { - return 0; - } + ulong lockouts_max = fd_ulong_max( lockouts_len, MAX_LOCKOUT_HISTORY ); - vote_update->lockouts = deq_fd_vote_lockout_t_alloc( valloc ); + vote_update->lockouts = deq_fd_vote_lockout_t_alloc( valloc, lockouts_max ); ulong slot = fd_ulong_if( vote_update->has_root, vote_update->root, 0 ); for( ulong i=0; i < lockouts_len; ++i ) { @@ -2075,7 +2153,7 @@ fd_vote_program_execute( fd_exec_instr_ctx_t ctx ) { if( decode_result != FD_BINCODE_SUCCESS || (ulong)ctx.instr->data + 1232UL < (ulong)decode.data ) return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA; - + /* PLEASE PRESERVE SWITCH-CASE ORDERING TO MIRROR LABS IMPL: * https://github.com/firedancer-io/solana/blob/da470eef4652b3b22598a1f379cacfe82bd5928d/programs/vote/src/vote_processor.rs#L73 */ @@ -2439,7 +2517,8 @@ fd_vote_program_execute( fd_exec_instr_ctx_t ctx ) { } break; } - /* TowerSync + + /* TowerSync(Switch) * * Instruction: * https://github.com/anza-xyz/agave/blob/master/sdk/program/src/vote/instruction.rs#L151-L157 @@ -2447,21 +2526,25 @@ fd_vote_program_execute( fd_exec_instr_ctx_t ctx ) { * Processor: * https://github.com/anza-xyz/agave/blob/master/programs/vote/src/vote_processor.rs#L196-L215 */ - case fd_vote_instruction_enum_tower_sync:; - /* clang-format off */ - __attribute__((fallthrough)); - /* clang-format on */ - - /* TowerSyncSwitch - * - * Instruction: - * https://github.com/anza-xyz/agave/blob/master/sdk/program/src/vote/instruction.rs#L159-L164 - * - * Processor: - * https://github.com/anza-xyz/agave/blob/master/programs/vote/src/vote_processor.rs#L196-L215 - */ + + case fd_vote_instruction_enum_tower_sync: case fd_vote_instruction_enum_tower_sync_switch: { - FD_LOG_ERR( ( "unimplemented" ) ); + if( !FD_FEATURE_ACTIVE( ctx.slot_ctx, enable_tower_sync_ix ) ) { + return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA; + } + + fd_tower_sync_t * tower_sync = (instruction.discriminant == fd_vote_instruction_enum_tower_sync) + ? &instruction.inner.tower_sync + : &instruction.inner.tower_sync_switch.tower_sync; + + fd_slot_hashes_t const * slot_hashes = fd_sysvar_cache_slot_hashes( ctx.slot_ctx->sysvar_cache ); + fd_sol_sysvar_clock_t const * clock = fd_sysvar_cache_clock( ctx.slot_ctx->sysvar_cache ); + if( FD_UNLIKELY( !slot_hashes || !clock ) ) { + return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR; + } + + rc = process_tower_sync( 0, me, slot_hashes, clock, tower_sync, signers, &ctx ); + break; } /* Withdraw diff --git a/src/flamenco/runtime/sysvar/fd_sysvar_recent_hashes.h b/src/flamenco/runtime/sysvar/fd_sysvar_recent_hashes.h index d6e5557ac8..4e87343e56 100644 --- a/src/flamenco/runtime/sysvar/fd_sysvar_recent_hashes.h +++ b/src/flamenco/runtime/sysvar/fd_sysvar_recent_hashes.h @@ -2,7 +2,16 @@ #define HEADER_fd_src_flamenco_runtime_sysvar_fd_recent_hashes_h #include "../../fd_flamenco_base.h" -#include "../fd_executor.h" + +/* FD_SYSVAR_RECENT_HASHES_CAP is the max number of block hash entries + the recent blockhashes sysvar will include. + + https://github.com/anza-xyz/agave/blob/6398ddf6ab8a8f81017bf675ab315a70067f0bf0/sdk/program/src/sysvar/recent_blockhashes.rs#L32 +*/ + +#define FD_SYSVAR_RECENT_HASHES_CAP (150UL) + +FD_PROTOTYPES_BEGIN /* The recent hashes sysvar */ @@ -14,5 +23,7 @@ fd_sysvar_recent_hashes_init( fd_exec_slot_ctx_t * slot_ctx ); void fd_sysvar_recent_hashes_update( fd_exec_slot_ctx_t * slot_ctx ); +FD_PROTOTYPES_END + #endif /* HEADER_fd_src_flamenco_runtime_sysvar_fd_recent_hashes_h */ diff --git a/src/flamenco/runtime/sysvar/fd_sysvar_slot_hashes.c b/src/flamenco/runtime/sysvar/fd_sysvar_slot_hashes.c index 9bf6f9daea..bba739418d 100644 --- a/src/flamenco/runtime/sysvar/fd_sysvar_slot_hashes.c +++ b/src/flamenco/runtime/sysvar/fd_sysvar_slot_hashes.c @@ -31,12 +31,13 @@ void write_slot_hashes( fd_exec_slot_ctx_t * slot_ctx, fd_slot_hashes_t* slot_ha //} /* https://github.com/solana-labs/solana/blob/8f2c8b8388a495d2728909e30460aa40dcc5d733/sdk/program/src/slot_hashes.rs#L34 */ -void fd_sysvar_slot_hashes_update( fd_exec_slot_ctx_t * slot_ctx ) { +void +fd_sysvar_slot_hashes_update( fd_exec_slot_ctx_t * slot_ctx ) { FD_SCRATCH_SCOPE_BEGIN { fd_slot_hashes_t slot_hashes; if( !fd_sysvar_slot_hashes_read( &slot_hashes, slot_ctx ) ) { - slot_hashes.hashes = deq_fd_slot_hash_t_alloc( slot_ctx->valloc ); + slot_hashes.hashes = deq_fd_slot_hash_t_alloc( slot_ctx->valloc, FD_SYSVAR_SLOT_HASHES_CAP ); FD_TEST( slot_hashes.hashes ); } diff --git a/src/flamenco/runtime/sysvar/fd_sysvar_slot_hashes.h b/src/flamenco/runtime/sysvar/fd_sysvar_slot_hashes.h index feb564269d..54e09b8811 100644 --- a/src/flamenco/runtime/sysvar/fd_sysvar_slot_hashes.h +++ b/src/flamenco/runtime/sysvar/fd_sysvar_slot_hashes.h @@ -7,11 +7,21 @@ /* The slot hashes sysvar contains the most recent hashes of the slot's parent bank hashes. */ +/* FD_SYSVAR_SLOT_HASHES_CAP is the max number of entries that the + "slot hashes" sysvar will include. + + https://github.com/anza-xyz/agave/blob/6398ddf6ab8a8f81017bf675ab315a70067f0bf0/sdk/program/src/slot_hashes.rs#L19 */ + +#define FD_SYSVAR_SLOT_HASHES_CAP (512UL) + +FD_PROTOTYPES_BEGIN + /* Initialize the slot hashes sysvar account. */ // void fd_sysvar_slot_hashes_init( fd_exec_slot_ctx_t* global ); /* Update the slot hashes sysvar account. This should be called at the end of every slot, before execution commences. */ -void fd_sysvar_slot_hashes_update( fd_exec_slot_ctx_t * slot_ctx); +void +fd_sysvar_slot_hashes_update( fd_exec_slot_ctx_t * slot_ctx); /* fd_sysvar_slot_hashes_read reads the slot hashes sysvar from the accounts manager. On success, returns 0 and writes deserialized @@ -21,5 +31,7 @@ fd_slot_hashes_t * fd_sysvar_slot_hashes_read( fd_slot_hashes_t * result, fd_exec_slot_ctx_t * slot_ctx ); +FD_PROTOTYPES_END + #endif /* HEADER_fd_src_flamenco_runtime_sysvar_fd_slot_hashes_h */ diff --git a/src/flamenco/runtime/sysvar/fd_sysvar_stake_history.c b/src/flamenco/runtime/sysvar/fd_sysvar_stake_history.c index cc7a6265cd..c871f82bb1 100644 --- a/src/flamenco/runtime/sysvar/fd_sysvar_stake_history.c +++ b/src/flamenco/runtime/sysvar/fd_sysvar_stake_history.c @@ -4,6 +4,10 @@ #include "../fd_system_ids.h" #include "../context/fd_exec_slot_ctx.h" +/* Ensure that the size declared by our header matches the minimum size + of the corresponding fd_types entry. */ +FD_STATIC_ASSERT( FD_SYSVAR_STAKE_HISTORY_CAP == FD_STAKE_HISTORY_MIN, types ); + void write_stake_history( fd_exec_slot_ctx_t * slot_ctx, fd_stake_history_t * stake_history ) { @@ -43,8 +47,8 @@ fd_sysvar_stake_history_read( fd_stake_history_t * result, void fd_sysvar_stake_history_init( fd_exec_slot_ctx_t * slot_ctx ) { fd_stake_history_t stake_history = { - .pool = fd_stake_history_pool_alloc( slot_ctx->valloc ), - .treap = fd_stake_history_treap_alloc( slot_ctx->valloc ) + .pool = fd_stake_history_pool_alloc ( slot_ctx->valloc, FD_SYSVAR_STAKE_HISTORY_CAP ), + .treap = fd_stake_history_treap_alloc( slot_ctx->valloc, FD_SYSVAR_STAKE_HISTORY_CAP ) }; write_stake_history( slot_ctx, &stake_history ); } diff --git a/src/flamenco/runtime/sysvar/fd_sysvar_stake_history.h b/src/flamenco/runtime/sysvar/fd_sysvar_stake_history.h index a5885efef9..49b54560cd 100644 --- a/src/flamenco/runtime/sysvar/fd_sysvar_stake_history.h +++ b/src/flamenco/runtime/sysvar/fd_sysvar_stake_history.h @@ -4,6 +4,13 @@ #include "../../fd_flamenco_base.h" #include "../fd_executor.h" +/* FD_SYSVAR_STAKE_HISTORY_CAP is the max number of entries that the + "stake history" sysvar will include. + + https://github.com/anza-xyz/agave/blob/6398ddf6ab8a8f81017bf675ab315a70067f0bf0/sdk/program/src/stake_history.rs#L12 */ + +#define FD_SYSVAR_STAKE_HISTORY_CAP (512UL) + FD_PROTOTYPES_BEGIN /* The stake history sysvar contains the history of cluster-wide activations and de-activations per-epoch. Updated at the start of each epoch. */ diff --git a/src/flamenco/runtime/tests/fd_exec_instr_test.c b/src/flamenco/runtime/tests/fd_exec_instr_test.c index 732c259fb1..67a89c5d40 100644 --- a/src/flamenco/runtime/tests/fd_exec_instr_test.c +++ b/src/flamenco/runtime/tests/fd_exec_instr_test.c @@ -10,6 +10,7 @@ #include "../context/fd_exec_epoch_ctx.h" #include "../context/fd_exec_slot_ctx.h" #include "../context/fd_exec_txn_ctx.h" +#include "../sysvar/fd_sysvar_recent_hashes.h" #include "../../../funk/fd_funk.h" #include "../../../util/bits/fd_float.h" #include @@ -222,7 +223,7 @@ _context_create( fd_exec_instr_test_runner_t * runner, /* TODO: Restore slot_bank */ fd_slot_bank_new( &slot_ctx->slot_bank ); - fd_block_block_hash_entry_t * recent_block_hashes = deq_fd_block_block_hash_entry_t_alloc( slot_ctx->valloc ); + fd_block_block_hash_entry_t * recent_block_hashes = deq_fd_block_block_hash_entry_t_alloc( slot_ctx->valloc, FD_SYSVAR_RECENT_HASHES_CAP ); slot_ctx->slot_bank.recent_block_hashes.hashes = recent_block_hashes; fd_block_block_hash_entry_t * recent_block_hash = deq_fd_block_block_hash_entry_t_push_tail_nocopy( recent_block_hashes ); fd_memset( recent_block_hash, 0, sizeof(fd_block_block_hash_entry_t) ); diff --git a/src/flamenco/types/fd_bincode.h b/src/flamenco/types/fd_bincode.h index e282181bc8..1be7bceba0 100644 --- a/src/flamenco/types/fd_bincode.h +++ b/src/flamenco/types/fd_bincode.h @@ -51,7 +51,6 @@ typedef struct fd_bincode_destroy_ctx fd_bincode_destroy_ctx_t; #define FD_BINCODE_ERR_UNDERFLOW (-1001) /* Attempted to read past end of buffer */ #define FD_BINCODE_ERR_OVERFLOW (-1002) /* Attempted to write past end of buffer */ #define FD_BINCODE_ERR_ENCODING (-1003) /* Invalid encoding */ -#define FD_BINCODE_ERR_SMALL_DEQUE (-1004) /* deque max size is too small */ #define FD_BINCODE_PRIMITIVE_STUBS( name, type ) \ static inline int \ diff --git a/src/flamenco/types/fd_types.c b/src/flamenco/types/fd_types.c index 42ccc2d939..261e0b6b30 100644 --- a/src/flamenco/types/fd_types.c +++ b/src/flamenco/types/fd_types.c @@ -1552,8 +1552,7 @@ int fd_stake_history_decode_preflight(fd_bincode_decode_ctx_t * ctx) { int err; ulong fd_stake_history_treap_len; err = fd_bincode_uint64_decode(&fd_stake_history_treap_len, ctx); - if ( FD_UNLIKELY(err) ) return err; - if ( fd_stake_history_treap_len > FD_STAKE_HISTORY_MAX ) return FD_BINCODE_ERR_SMALL_DEQUE; + if( FD_UNLIKELY( err ) ) return err; for (ulong i = 0; i < fd_stake_history_treap_len; ++i) { err = fd_stake_history_entry_decode_preflight( ctx ); if ( FD_UNLIKELY ( err ) ) return err; @@ -1563,8 +1562,9 @@ int fd_stake_history_decode_preflight(fd_bincode_decode_ctx_t * ctx) { void fd_stake_history_decode_unsafe(fd_stake_history_t* self, fd_bincode_decode_ctx_t * ctx) { ulong fd_stake_history_treap_len; fd_bincode_uint64_decode_unsafe(&fd_stake_history_treap_len, ctx); - self->pool = fd_stake_history_pool_alloc( ctx->valloc ); - self->treap = fd_stake_history_treap_alloc( ctx->valloc ); + ulong fd_stake_history_treap_max = fd_ulong_max( fd_stake_history_treap_len, FD_STAKE_HISTORY_MIN ); + self->pool = fd_stake_history_pool_alloc( ctx->valloc, fd_stake_history_treap_max ); + self->treap = fd_stake_history_treap_alloc( ctx->valloc, fd_stake_history_treap_max ); for (ulong i = 0; i < fd_stake_history_treap_len; ++i) { fd_stake_history_entry_t * ele = fd_stake_history_pool_ele_acquire( self->pool ); fd_stake_history_entry_new( ele ); @@ -1578,8 +1578,7 @@ int fd_stake_history_decode_offsets(fd_stake_history_off_t* self, fd_bincode_dec self->fd_stake_history_off = (uint)((ulong)ctx->data - (ulong)data); ulong fd_stake_history_treap_len; err = fd_bincode_uint64_decode(&fd_stake_history_treap_len, ctx); - if ( FD_UNLIKELY(err) ) return err; - if ( fd_stake_history_treap_len > FD_STAKE_HISTORY_MAX ) return FD_BINCODE_ERR_SMALL_DEQUE; + if( FD_UNLIKELY( err ) ) return err; for (ulong i = 0; i < fd_stake_history_treap_len; ++i) { err = fd_stake_history_entry_decode_preflight( ctx ); if ( FD_UNLIKELY ( err ) ) return err; @@ -7014,10 +7013,11 @@ int fd_vote_state_0_23_5_decode_preflight(fd_bincode_decode_ctx_t * ctx) { if ( FD_UNLIKELY(err) ) return err; ulong votes_len; err = fd_bincode_uint64_decode( &votes_len, ctx ); - if ( FD_UNLIKELY(err) ) return err; - if ( votes_len > 1228 ) return FD_BINCODE_ERR_SMALL_DEQUE; - err = fd_bincode_bytes_decode_preflight(votes_len * 12, ctx); - if ( FD_UNLIKELY(err) ) return err; + if( FD_UNLIKELY( err ) ) return err; + ulong votes_sz; + if( FD_UNLIKELY( __builtin_umull_overflow( votes_len, 12, &votes_sz ) ) ) return FD_BINCODE_ERR_UNDERFLOW; + err = fd_bincode_bytes_decode_preflight( votes_sz, ctx ); + if( FD_UNLIKELY( err ) ) return err; { uchar o; err = fd_bincode_bool_decode( &o, ctx ); @@ -7029,10 +7029,11 @@ int fd_vote_state_0_23_5_decode_preflight(fd_bincode_decode_ctx_t * ctx) { } ulong epoch_credits_len; err = fd_bincode_uint64_decode( &epoch_credits_len, ctx ); - if ( FD_UNLIKELY(err) ) return err; - if ( epoch_credits_len > 100 ) return FD_BINCODE_ERR_SMALL_DEQUE; - err = fd_bincode_bytes_decode_preflight(epoch_credits_len * 24, ctx); - if ( FD_UNLIKELY(err) ) return err; + if( FD_UNLIKELY( err ) ) return err; + ulong epoch_credits_sz; + if( FD_UNLIKELY( __builtin_umull_overflow( epoch_credits_len, 24, &epoch_credits_sz ) ) ) return FD_BINCODE_ERR_UNDERFLOW; + err = fd_bincode_bytes_decode_preflight( epoch_credits_sz, ctx ); + if( FD_UNLIKELY( err ) ) return err; err = fd_vote_block_timestamp_decode_preflight(ctx); if ( FD_UNLIKELY(err) ) return err; return FD_BINCODE_SUCCESS; @@ -7046,7 +7047,8 @@ void fd_vote_state_0_23_5_decode_unsafe(fd_vote_state_0_23_5_t* self, fd_bincode fd_bincode_uint8_decode_unsafe(&self->commission, ctx); ulong votes_len; fd_bincode_uint64_decode_unsafe( &votes_len, ctx ); - self->votes = deq_fd_vote_lockout_t_alloc( ctx->valloc ); + ulong votes_max = fd_ulong_max( votes_len, 32 ); + self->votes = deq_fd_vote_lockout_t_alloc( ctx->valloc, votes_max ); for (ulong i = 0; i < votes_len; ++i) { fd_vote_lockout_t * elem = deq_fd_vote_lockout_t_push_tail_nocopy(self->votes); fd_vote_lockout_new(elem); @@ -7062,7 +7064,8 @@ void fd_vote_state_0_23_5_decode_unsafe(fd_vote_state_0_23_5_t* self, fd_bincode } ulong epoch_credits_len; fd_bincode_uint64_decode_unsafe( &epoch_credits_len, ctx ); - self->epoch_credits = deq_fd_vote_epoch_credits_t_alloc( ctx->valloc ); + ulong epoch_credits_max = fd_ulong_max( epoch_credits_len, 64 ); + self->epoch_credits = deq_fd_vote_epoch_credits_t_alloc( ctx->valloc, epoch_credits_max ); for (ulong i = 0; i < epoch_credits_len; ++i) { fd_vote_epoch_credits_t * elem = deq_fd_vote_epoch_credits_t_push_tail_nocopy(self->epoch_credits); fd_vote_epoch_credits_new(elem); @@ -7094,10 +7097,11 @@ int fd_vote_state_0_23_5_decode_offsets(fd_vote_state_0_23_5_off_t* self, fd_bin self->votes_off = (uint)((ulong)ctx->data - (ulong)data); ulong votes_len; err = fd_bincode_uint64_decode( &votes_len, ctx ); - if ( FD_UNLIKELY(err) ) return err; - if ( votes_len > 1228 ) return FD_BINCODE_ERR_SMALL_DEQUE; - err = fd_bincode_bytes_decode_preflight(votes_len * 12, ctx); - if ( FD_UNLIKELY(err) ) return err; + if( FD_UNLIKELY( err ) ) return err; + ulong votes_sz; + if( FD_UNLIKELY( __builtin_umull_overflow( votes_len, 12, &votes_sz ) ) ) return FD_BINCODE_ERR_UNDERFLOW; + err = fd_bincode_bytes_decode_preflight( votes_sz, ctx ); + if( FD_UNLIKELY( err ) ) return err; self->root_slot_off = (uint)((ulong)ctx->data - (ulong)data); { uchar o; @@ -7111,10 +7115,11 @@ int fd_vote_state_0_23_5_decode_offsets(fd_vote_state_0_23_5_off_t* self, fd_bin self->epoch_credits_off = (uint)((ulong)ctx->data - (ulong)data); ulong epoch_credits_len; err = fd_bincode_uint64_decode( &epoch_credits_len, ctx ); - if ( FD_UNLIKELY(err) ) return err; - if ( epoch_credits_len > 100 ) return FD_BINCODE_ERR_SMALL_DEQUE; - err = fd_bincode_bytes_decode_preflight(epoch_credits_len * 24, ctx); - if ( FD_UNLIKELY(err) ) return err; + if( FD_UNLIKELY( err ) ) return err; + ulong epoch_credits_sz; + if( FD_UNLIKELY( __builtin_umull_overflow( epoch_credits_len, 24, &epoch_credits_sz ) ) ) return FD_BINCODE_ERR_UNDERFLOW; + err = fd_bincode_bytes_decode_preflight( epoch_credits_sz, ctx ); + if( FD_UNLIKELY( err ) ) return err; self->last_timestamp_off = (uint)((ulong)ctx->data - (ulong)data); err = fd_vote_block_timestamp_decode_preflight(ctx); if ( FD_UNLIKELY(err) ) return err; @@ -7302,8 +7307,7 @@ int fd_vote_authorized_voters_decode_preflight(fd_bincode_decode_ctx_t * ctx) { int err; ulong fd_vote_authorized_voters_treap_len; err = fd_bincode_uint64_decode(&fd_vote_authorized_voters_treap_len, ctx); - if ( FD_UNLIKELY(err) ) return err; - if ( fd_vote_authorized_voters_treap_len > FD_VOTE_AUTHORIZED_VOTERS_MAX ) return FD_BINCODE_ERR_SMALL_DEQUE; + if( FD_UNLIKELY( err ) ) return err; for (ulong i = 0; i < fd_vote_authorized_voters_treap_len; ++i) { err = fd_vote_authorized_voter_decode_preflight( ctx ); if ( FD_UNLIKELY ( err ) ) return err; @@ -7314,8 +7318,9 @@ void fd_vote_authorized_voters_decode_unsafe(fd_vote_authorized_voters_t* self, fd_bincode_destroy_ctx_t destroy_ctx = { .valloc = ctx->valloc }; ulong fd_vote_authorized_voters_treap_len; fd_bincode_uint64_decode_unsafe(&fd_vote_authorized_voters_treap_len, ctx); - self->pool = fd_vote_authorized_voters_pool_alloc( ctx->valloc ); - self->treap = fd_vote_authorized_voters_treap_alloc( ctx->valloc ); + ulong fd_vote_authorized_voters_treap_max = fd_ulong_max( fd_vote_authorized_voters_treap_len, FD_VOTE_AUTHORIZED_VOTERS_MIN ); + self->pool = fd_vote_authorized_voters_pool_alloc( ctx->valloc, fd_vote_authorized_voters_treap_max ); + self->treap = fd_vote_authorized_voters_treap_alloc( ctx->valloc, fd_vote_authorized_voters_treap_max ); for (ulong i = 0; i < fd_vote_authorized_voters_treap_len; ++i) { fd_vote_authorized_voter_t * ele = fd_vote_authorized_voters_pool_ele_acquire( self->pool ); fd_vote_authorized_voter_new( ele ); @@ -7335,8 +7340,7 @@ int fd_vote_authorized_voters_decode_offsets(fd_vote_authorized_voters_off_t* se self->fd_vote_authorized_voters_off = (uint)((ulong)ctx->data - (ulong)data); ulong fd_vote_authorized_voters_treap_len; err = fd_bincode_uint64_decode(&fd_vote_authorized_voters_treap_len, ctx); - if ( FD_UNLIKELY(err) ) return err; - if ( fd_vote_authorized_voters_treap_len > FD_VOTE_AUTHORIZED_VOTERS_MAX ) return FD_BINCODE_ERR_SMALL_DEQUE; + if( FD_UNLIKELY( err ) ) return err; for (ulong i = 0; i < fd_vote_authorized_voters_treap_len; ++i) { err = fd_vote_authorized_voter_decode_preflight( ctx ); if ( FD_UNLIKELY ( err ) ) return err; @@ -7429,10 +7433,11 @@ int fd_vote_state_1_14_11_decode_preflight(fd_bincode_decode_ctx_t * ctx) { if ( FD_UNLIKELY(err) ) return err; ulong votes_len; err = fd_bincode_uint64_decode( &votes_len, ctx ); - if ( FD_UNLIKELY(err) ) return err; - if ( votes_len > 1228 ) return FD_BINCODE_ERR_SMALL_DEQUE; - err = fd_bincode_bytes_decode_preflight(votes_len * 12, ctx); - if ( FD_UNLIKELY(err) ) return err; + if( FD_UNLIKELY( err ) ) return err; + ulong votes_sz; + if( FD_UNLIKELY( __builtin_umull_overflow( votes_len, 12, &votes_sz ) ) ) return FD_BINCODE_ERR_UNDERFLOW; + err = fd_bincode_bytes_decode_preflight( votes_sz, ctx ); + if( FD_UNLIKELY( err ) ) return err; { uchar o; err = fd_bincode_bool_decode( &o, ctx ); @@ -7448,10 +7453,11 @@ int fd_vote_state_1_14_11_decode_preflight(fd_bincode_decode_ctx_t * ctx) { if ( FD_UNLIKELY(err) ) return err; ulong epoch_credits_len; err = fd_bincode_uint64_decode( &epoch_credits_len, ctx ); - if ( FD_UNLIKELY(err) ) return err; - if ( epoch_credits_len > 64 ) return FD_BINCODE_ERR_SMALL_DEQUE; - err = fd_bincode_bytes_decode_preflight(epoch_credits_len * 24, ctx); - if ( FD_UNLIKELY(err) ) return err; + if( FD_UNLIKELY( err ) ) return err; + ulong epoch_credits_sz; + if( FD_UNLIKELY( __builtin_umull_overflow( epoch_credits_len, 24, &epoch_credits_sz ) ) ) return FD_BINCODE_ERR_UNDERFLOW; + err = fd_bincode_bytes_decode_preflight( epoch_credits_sz, ctx ); + if( FD_UNLIKELY( err ) ) return err; err = fd_vote_block_timestamp_decode_preflight(ctx); if ( FD_UNLIKELY(err) ) return err; return FD_BINCODE_SUCCESS; @@ -7462,7 +7468,8 @@ void fd_vote_state_1_14_11_decode_unsafe(fd_vote_state_1_14_11_t* self, fd_binco fd_bincode_uint8_decode_unsafe(&self->commission, ctx); ulong votes_len; fd_bincode_uint64_decode_unsafe( &votes_len, ctx ); - self->votes = deq_fd_vote_lockout_t_alloc( ctx->valloc ); + ulong votes_max = fd_ulong_max( votes_len, 32 ); + self->votes = deq_fd_vote_lockout_t_alloc( ctx->valloc, votes_max ); for (ulong i = 0; i < votes_len; ++i) { fd_vote_lockout_t * elem = deq_fd_vote_lockout_t_push_tail_nocopy(self->votes); fd_vote_lockout_new(elem); @@ -7480,7 +7487,8 @@ void fd_vote_state_1_14_11_decode_unsafe(fd_vote_state_1_14_11_t* self, fd_binco fd_vote_prior_voters_decode_unsafe(&self->prior_voters, ctx); ulong epoch_credits_len; fd_bincode_uint64_decode_unsafe( &epoch_credits_len, ctx ); - self->epoch_credits = deq_fd_vote_epoch_credits_t_alloc( ctx->valloc ); + ulong epoch_credits_max = fd_ulong_max( epoch_credits_len, 64 ); + self->epoch_credits = deq_fd_vote_epoch_credits_t_alloc( ctx->valloc, epoch_credits_max ); for (ulong i = 0; i < epoch_credits_len; ++i) { fd_vote_epoch_credits_t * elem = deq_fd_vote_epoch_credits_t_push_tail_nocopy(self->epoch_credits); fd_vote_epoch_credits_new(elem); @@ -7503,10 +7511,11 @@ int fd_vote_state_1_14_11_decode_offsets(fd_vote_state_1_14_11_off_t* self, fd_b self->votes_off = (uint)((ulong)ctx->data - (ulong)data); ulong votes_len; err = fd_bincode_uint64_decode( &votes_len, ctx ); - if ( FD_UNLIKELY(err) ) return err; - if ( votes_len > 1228 ) return FD_BINCODE_ERR_SMALL_DEQUE; - err = fd_bincode_bytes_decode_preflight(votes_len * 12, ctx); - if ( FD_UNLIKELY(err) ) return err; + if( FD_UNLIKELY( err ) ) return err; + ulong votes_sz; + if( FD_UNLIKELY( __builtin_umull_overflow( votes_len, 12, &votes_sz ) ) ) return FD_BINCODE_ERR_UNDERFLOW; + err = fd_bincode_bytes_decode_preflight( votes_sz, ctx ); + if( FD_UNLIKELY( err ) ) return err; self->root_slot_off = (uint)((ulong)ctx->data - (ulong)data); { uchar o; @@ -7526,10 +7535,11 @@ int fd_vote_state_1_14_11_decode_offsets(fd_vote_state_1_14_11_off_t* self, fd_b self->epoch_credits_off = (uint)((ulong)ctx->data - (ulong)data); ulong epoch_credits_len; err = fd_bincode_uint64_decode( &epoch_credits_len, ctx ); - if ( FD_UNLIKELY(err) ) return err; - if ( epoch_credits_len > 64 ) return FD_BINCODE_ERR_SMALL_DEQUE; - err = fd_bincode_bytes_decode_preflight(epoch_credits_len * 24, ctx); - if ( FD_UNLIKELY(err) ) return err; + if( FD_UNLIKELY( err ) ) return err; + ulong epoch_credits_sz; + if( FD_UNLIKELY( __builtin_umull_overflow( epoch_credits_len, 24, &epoch_credits_sz ) ) ) return FD_BINCODE_ERR_UNDERFLOW; + err = fd_bincode_bytes_decode_preflight( epoch_credits_sz, ctx ); + if( FD_UNLIKELY( err ) ) return err; self->last_timestamp_off = (uint)((ulong)ctx->data - (ulong)data); err = fd_vote_block_timestamp_decode_preflight(ctx); if ( FD_UNLIKELY(err) ) return err; @@ -7719,9 +7729,8 @@ int fd_vote_state_decode_preflight(fd_bincode_decode_ctx_t * ctx) { if ( FD_UNLIKELY(err) ) return err; ulong votes_len; err = fd_bincode_uint64_decode( &votes_len, ctx ); - if ( FD_UNLIKELY(err) ) return err; - if ( votes_len > 35 ) return FD_BINCODE_ERR_SMALL_DEQUE; - for (ulong i = 0; i < votes_len; ++i) { + if( FD_UNLIKELY( err ) ) return err; + for( ulong i = 0; i < votes_len; ++i ) { err = fd_landed_vote_decode_preflight(ctx); if ( FD_UNLIKELY(err) ) return err; } @@ -7740,10 +7749,11 @@ int fd_vote_state_decode_preflight(fd_bincode_decode_ctx_t * ctx) { if ( FD_UNLIKELY(err) ) return err; ulong epoch_credits_len; err = fd_bincode_uint64_decode( &epoch_credits_len, ctx ); - if ( FD_UNLIKELY(err) ) return err; - if ( epoch_credits_len > 100 ) return FD_BINCODE_ERR_SMALL_DEQUE; - err = fd_bincode_bytes_decode_preflight(epoch_credits_len * 24, ctx); - if ( FD_UNLIKELY(err) ) return err; + if( FD_UNLIKELY( err ) ) return err; + ulong epoch_credits_sz; + if( FD_UNLIKELY( __builtin_umull_overflow( epoch_credits_len, 24, &epoch_credits_sz ) ) ) return FD_BINCODE_ERR_UNDERFLOW; + err = fd_bincode_bytes_decode_preflight( epoch_credits_sz, ctx ); + if( FD_UNLIKELY( err ) ) return err; err = fd_vote_block_timestamp_decode_preflight(ctx); if ( FD_UNLIKELY(err) ) return err; return FD_BINCODE_SUCCESS; @@ -7754,7 +7764,8 @@ void fd_vote_state_decode_unsafe(fd_vote_state_t* self, fd_bincode_decode_ctx_t fd_bincode_uint8_decode_unsafe(&self->commission, ctx); ulong votes_len; fd_bincode_uint64_decode_unsafe( &votes_len, ctx ); - self->votes = deq_fd_landed_vote_t_alloc( ctx->valloc ); + ulong votes_max = fd_ulong_max( votes_len, 32 ); + self->votes = deq_fd_landed_vote_t_alloc( ctx->valloc, votes_max ); for (ulong i = 0; i < votes_len; ++i) { fd_landed_vote_t * elem = deq_fd_landed_vote_t_push_tail_nocopy(self->votes); fd_landed_vote_new(elem); @@ -7772,7 +7783,8 @@ void fd_vote_state_decode_unsafe(fd_vote_state_t* self, fd_bincode_decode_ctx_t fd_vote_prior_voters_decode_unsafe(&self->prior_voters, ctx); ulong epoch_credits_len; fd_bincode_uint64_decode_unsafe( &epoch_credits_len, ctx ); - self->epoch_credits = deq_fd_vote_epoch_credits_t_alloc( ctx->valloc ); + ulong epoch_credits_max = fd_ulong_max( epoch_credits_len, 64 ); + self->epoch_credits = deq_fd_vote_epoch_credits_t_alloc( ctx->valloc, epoch_credits_max ); for (ulong i = 0; i < epoch_credits_len; ++i) { fd_vote_epoch_credits_t * elem = deq_fd_vote_epoch_credits_t_push_tail_nocopy(self->epoch_credits); fd_vote_epoch_credits_new(elem); @@ -7795,9 +7807,8 @@ int fd_vote_state_decode_offsets(fd_vote_state_off_t* self, fd_bincode_decode_ct self->votes_off = (uint)((ulong)ctx->data - (ulong)data); ulong votes_len; err = fd_bincode_uint64_decode( &votes_len, ctx ); - if ( FD_UNLIKELY(err) ) return err; - if ( votes_len > 35 ) return FD_BINCODE_ERR_SMALL_DEQUE; - for (ulong i = 0; i < votes_len; ++i) { + if( FD_UNLIKELY( err ) ) return err; + for( ulong i = 0; i < votes_len; ++i ) { err = fd_landed_vote_decode_preflight(ctx); if ( FD_UNLIKELY(err) ) return err; } @@ -7820,10 +7831,11 @@ int fd_vote_state_decode_offsets(fd_vote_state_off_t* self, fd_bincode_decode_ct self->epoch_credits_off = (uint)((ulong)ctx->data - (ulong)data); ulong epoch_credits_len; err = fd_bincode_uint64_decode( &epoch_credits_len, ctx ); - if ( FD_UNLIKELY(err) ) return err; - if ( epoch_credits_len > 100 ) return FD_BINCODE_ERR_SMALL_DEQUE; - err = fd_bincode_bytes_decode_preflight(epoch_credits_len * 24, ctx); - if ( FD_UNLIKELY(err) ) return err; + if( FD_UNLIKELY( err ) ) return err; + ulong epoch_credits_sz; + if( FD_UNLIKELY( __builtin_umull_overflow( epoch_credits_len, 24, &epoch_credits_sz ) ) ) return FD_BINCODE_ERR_UNDERFLOW; + err = fd_bincode_bytes_decode_preflight( epoch_credits_sz, ctx ); + if( FD_UNLIKELY( err ) ) return err; self->last_timestamp_off = (uint)((ulong)ctx->data - (ulong)data); err = fd_vote_block_timestamp_decode_preflight(ctx); if ( FD_UNLIKELY(err) ) return err; @@ -8188,10 +8200,11 @@ int fd_vote_state_update_decode_preflight(fd_bincode_decode_ctx_t * ctx) { int err; ulong lockouts_len; err = fd_bincode_uint64_decode( &lockouts_len, ctx ); - if ( FD_UNLIKELY(err) ) return err; - if ( lockouts_len > 1228 ) return FD_BINCODE_ERR_SMALL_DEQUE; - err = fd_bincode_bytes_decode_preflight(lockouts_len * 12, ctx); - if ( FD_UNLIKELY(err) ) return err; + if( FD_UNLIKELY( err ) ) return err; + ulong lockouts_sz; + if( FD_UNLIKELY( __builtin_umull_overflow( lockouts_len, 12, &lockouts_sz ) ) ) return FD_BINCODE_ERR_UNDERFLOW; + err = fd_bincode_bytes_decode_preflight( lockouts_sz, ctx ); + if( FD_UNLIKELY( err ) ) return err; { uchar o; err = fd_bincode_bool_decode( &o, ctx ); @@ -8217,7 +8230,8 @@ int fd_vote_state_update_decode_preflight(fd_bincode_decode_ctx_t * ctx) { void fd_vote_state_update_decode_unsafe(fd_vote_state_update_t* self, fd_bincode_decode_ctx_t * ctx) { ulong lockouts_len; fd_bincode_uint64_decode_unsafe( &lockouts_len, ctx ); - self->lockouts = deq_fd_vote_lockout_t_alloc( ctx->valloc ); + ulong lockouts_max = fd_ulong_max( lockouts_len, 32 ); + self->lockouts = deq_fd_vote_lockout_t_alloc( ctx->valloc, lockouts_max ); for (ulong i = 0; i < lockouts_len; ++i) { fd_vote_lockout_t * elem = deq_fd_vote_lockout_t_push_tail_nocopy(self->lockouts); fd_vote_lockout_new(elem); @@ -8248,10 +8262,11 @@ int fd_vote_state_update_decode_offsets(fd_vote_state_update_off_t* self, fd_bin self->lockouts_off = (uint)((ulong)ctx->data - (ulong)data); ulong lockouts_len; err = fd_bincode_uint64_decode( &lockouts_len, ctx ); - if ( FD_UNLIKELY(err) ) return err; - if ( lockouts_len > 1228 ) return FD_BINCODE_ERR_SMALL_DEQUE; - err = fd_bincode_bytes_decode_preflight(lockouts_len * 12, ctx); - if ( FD_UNLIKELY(err) ) return err; + if( FD_UNLIKELY( err ) ) return err; + ulong lockouts_sz; + if( FD_UNLIKELY( __builtin_umull_overflow( lockouts_len, 12, &lockouts_sz ) ) ) return FD_BINCODE_ERR_UNDERFLOW; + err = fd_bincode_bytes_decode_preflight( lockouts_sz, ctx ); + if( FD_UNLIKELY( err ) ) return err; self->root_off = (uint)((ulong)ctx->data - (ulong)data); { uchar o; @@ -8638,10 +8653,13 @@ int fd_tower_sync_decode(fd_tower_sync_t* self, fd_bincode_decode_ctx_t * ctx) { } int fd_tower_sync_decode_preflight(fd_bincode_decode_ctx_t * ctx) { int err; - for (ulong i = 0; i < 32; ++i) { - err = fd_vote_lockout_decode_preflight(ctx); - if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; - } + ulong lockouts_len; + err = fd_bincode_uint64_decode( &lockouts_len, ctx ); + if( FD_UNLIKELY( err ) ) return err; + ulong lockouts_sz; + if( FD_UNLIKELY( __builtin_umull_overflow( lockouts_len, 12, &lockouts_sz ) ) ) return FD_BINCODE_ERR_UNDERFLOW; + err = fd_bincode_bytes_decode_preflight( lockouts_sz, ctx ); + if( FD_UNLIKELY( err ) ) return err; err = fd_bincode_uint64_decode_preflight(ctx); if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; { @@ -8669,8 +8687,13 @@ int fd_tower_sync_decode_preflight(fd_bincode_decode_ctx_t * ctx) { return FD_BINCODE_SUCCESS; } void fd_tower_sync_decode_unsafe(fd_tower_sync_t* self, fd_bincode_decode_ctx_t * ctx) { - for (ulong i = 0; i < 32; ++i) { - fd_vote_lockout_decode_unsafe(self->lockouts + i, ctx); + ulong lockouts_len; + fd_bincode_uint64_decode_unsafe( &lockouts_len, ctx ); + self->lockouts = deq_fd_vote_lockout_t_alloc( ctx->valloc, lockouts_len ); + for (ulong i = 0; i < lockouts_len; ++i) { + fd_vote_lockout_t * elem = deq_fd_vote_lockout_t_push_tail_nocopy(self->lockouts); + fd_vote_lockout_new(elem); + fd_vote_lockout_decode_unsafe(elem, ctx); } fd_bincode_uint64_decode_unsafe(&self->lockouts_cnt, ctx); { @@ -8696,10 +8719,13 @@ int fd_tower_sync_decode_offsets(fd_tower_sync_off_t* self, fd_bincode_decode_ct uchar const * data = ctx->data; int err; self->lockouts_off = (uint)((ulong)ctx->data - (ulong)data); - for (ulong i = 0; i < 32; ++i) { - err = fd_vote_lockout_decode_preflight(ctx); - if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; - } + ulong lockouts_len; + err = fd_bincode_uint64_decode( &lockouts_len, ctx ); + if( FD_UNLIKELY( err ) ) return err; + ulong lockouts_sz; + if( FD_UNLIKELY( __builtin_umull_overflow( lockouts_len, 12, &lockouts_sz ) ) ) return FD_BINCODE_ERR_UNDERFLOW; + err = fd_bincode_bytes_decode_preflight( lockouts_sz, ctx ); + if( FD_UNLIKELY( err ) ) return err; self->lockouts_cnt_off = (uint)((ulong)ctx->data - (ulong)data); err = fd_bincode_uint64_decode_preflight(ctx); if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; @@ -8733,14 +8759,18 @@ int fd_tower_sync_decode_offsets(fd_tower_sync_off_t* self, fd_bincode_decode_ct } void fd_tower_sync_new(fd_tower_sync_t* self) { fd_memset(self, 0, sizeof(fd_tower_sync_t)); - for (ulong i = 0; i < 32; ++i) - fd_vote_lockout_new(self->lockouts + i); fd_hash_new(&self->hash); fd_hash_new(&self->block_id); } void fd_tower_sync_destroy(fd_tower_sync_t* self, fd_bincode_destroy_ctx_t * ctx) { - for (ulong i = 0; i < 32; ++i) - fd_vote_lockout_destroy(self->lockouts + i, ctx); + if ( self->lockouts ) { + for ( deq_fd_vote_lockout_t_iter_t iter = deq_fd_vote_lockout_t_iter_init( self->lockouts ); !deq_fd_vote_lockout_t_iter_done( self->lockouts, iter ); iter = deq_fd_vote_lockout_t_iter_next( self->lockouts, iter ) ) { + fd_vote_lockout_t * ele = deq_fd_vote_lockout_t_iter_ele( self->lockouts, iter ); + fd_vote_lockout_destroy(ele, ctx); + } + fd_valloc_free( ctx->valloc, deq_fd_vote_lockout_t_delete( deq_fd_vote_lockout_t_leave( self->lockouts) ) ); + self->lockouts = NULL; + } if( self->has_root ) { self->has_root = 0; } @@ -8756,10 +8786,20 @@ ulong fd_tower_sync_align( void ){ return FD_TOWER_SYNC_ALIGN; } void fd_tower_sync_walk(void * w, fd_tower_sync_t const * self, fd_types_walk_fn_t fun, const char *name, uint level) { fun(w, self, name, FD_FLAMENCO_TYPE_MAP, "fd_tower_sync", level++); - fun(w, NULL, "lockouts", FD_FLAMENCO_TYPE_ARR, "vote_lockout[]", level++); - for (ulong i = 0; i < 32; ++i) - fd_vote_lockout_walk(w, self->lockouts + i, fun, "vote_lockout", level ); - fun(w, NULL, "lockouts", FD_FLAMENCO_TYPE_ARR_END, "vote_lockout[]", level--); + + /* Walk deque */ + fun( w, self->lockouts, "lockouts", FD_FLAMENCO_TYPE_ARR, "lockouts", level++ ); + if( self->lockouts ) { + for( deq_fd_vote_lockout_t_iter_t iter = deq_fd_vote_lockout_t_iter_init( self->lockouts ); + !deq_fd_vote_lockout_t_iter_done( self->lockouts, iter ); + iter = deq_fd_vote_lockout_t_iter_next( self->lockouts, iter ) ) { + fd_vote_lockout_t * ele = deq_fd_vote_lockout_t_iter_ele( self->lockouts, iter ); + fd_vote_lockout_walk(w, ele, fun, "lockouts", level ); + } + } + fun( w, self->lockouts, "lockouts", FD_FLAMENCO_TYPE_ARR_END, "lockouts", level-- ); + /* Done walking deque */ + fun( w, &self->lockouts_cnt, "lockouts_cnt", FD_FLAMENCO_TYPE_ULONG, "ulong", level ); if( !self->has_root ) { fun( w, NULL, "root", FD_FLAMENCO_TYPE_NULL, "ulong", level ); @@ -8777,8 +8817,15 @@ void fd_tower_sync_walk(void * w, fd_tower_sync_t const * self, fd_types_walk_fn } ulong fd_tower_sync_size(fd_tower_sync_t const * self) { ulong size = 0; - for (ulong i = 0; i < 32; ++i) - size += fd_vote_lockout_size(self->lockouts + i); + if ( self->lockouts ) { + size += sizeof(ulong); + for ( deq_fd_vote_lockout_t_iter_t iter = deq_fd_vote_lockout_t_iter_init( self->lockouts ); !deq_fd_vote_lockout_t_iter_done( self->lockouts, iter ); iter = deq_fd_vote_lockout_t_iter_next( self->lockouts, iter ) ) { + fd_vote_lockout_t * ele = deq_fd_vote_lockout_t_iter_ele( self->lockouts, iter ); + size += fd_vote_lockout_size(ele); + } + } else { + size += sizeof(ulong); + } size += sizeof(ulong); size += sizeof(char); if( self->has_root ) { @@ -8795,8 +8842,18 @@ ulong fd_tower_sync_size(fd_tower_sync_t const * self) { int fd_tower_sync_encode(fd_tower_sync_t const * self, fd_bincode_encode_ctx_t * ctx) { int err; - for (ulong i = 0; i < 32; ++i) { - err = fd_vote_lockout_encode(self->lockouts + i, ctx); + if ( self->lockouts ) { + ulong lockouts_len = deq_fd_vote_lockout_t_cnt(self->lockouts); + err = fd_bincode_uint64_encode(lockouts_len, ctx); + if ( FD_UNLIKELY(err) ) return err; + for ( deq_fd_vote_lockout_t_iter_t iter = deq_fd_vote_lockout_t_iter_init( self->lockouts ); !deq_fd_vote_lockout_t_iter_done( self->lockouts, iter ); iter = deq_fd_vote_lockout_t_iter_next( self->lockouts, iter ) ) { + fd_vote_lockout_t const * ele = deq_fd_vote_lockout_t_iter_ele_const( self->lockouts, iter ); + err = fd_vote_lockout_encode(ele, ctx); + if ( FD_UNLIKELY(err) ) return err; + } + } else { + ulong lockouts_len = 0; + err = fd_bincode_uint64_encode(lockouts_len, ctx); if ( FD_UNLIKELY(err) ) return err; } err = fd_bincode_uint64_encode(self->lockouts_cnt, ctx); @@ -9226,9 +9283,8 @@ int fd_slot_hashes_decode_preflight(fd_bincode_decode_ctx_t * ctx) { int err; ulong hashes_len; err = fd_bincode_uint64_decode( &hashes_len, ctx ); - if ( FD_UNLIKELY(err) ) return err; - if ( hashes_len > 512 ) return FD_BINCODE_ERR_SMALL_DEQUE; - for (ulong i = 0; i < hashes_len; ++i) { + if( FD_UNLIKELY( err ) ) return err; + for( ulong i = 0; i < hashes_len; ++i ) { err = fd_slot_hash_decode_preflight(ctx); if ( FD_UNLIKELY(err) ) return err; } @@ -9237,7 +9293,8 @@ int fd_slot_hashes_decode_preflight(fd_bincode_decode_ctx_t * ctx) { void fd_slot_hashes_decode_unsafe(fd_slot_hashes_t* self, fd_bincode_decode_ctx_t * ctx) { ulong hashes_len; fd_bincode_uint64_decode_unsafe( &hashes_len, ctx ); - self->hashes = deq_fd_slot_hash_t_alloc( ctx->valloc ); + ulong hashes_max = fd_ulong_max( hashes_len, 512 ); + self->hashes = deq_fd_slot_hash_t_alloc( ctx->valloc, hashes_max ); for (ulong i = 0; i < hashes_len; ++i) { fd_slot_hash_t * elem = deq_fd_slot_hash_t_push_tail_nocopy(self->hashes); fd_slot_hash_new(elem); @@ -9250,9 +9307,8 @@ int fd_slot_hashes_decode_offsets(fd_slot_hashes_off_t* self, fd_bincode_decode_ self->hashes_off = (uint)((ulong)ctx->data - (ulong)data); ulong hashes_len; err = fd_bincode_uint64_decode( &hashes_len, ctx ); - if ( FD_UNLIKELY(err) ) return err; - if ( hashes_len > 512 ) return FD_BINCODE_ERR_SMALL_DEQUE; - for (ulong i = 0; i < hashes_len; ++i) { + if( FD_UNLIKELY( err ) ) return err; + for( ulong i = 0; i < hashes_len; ++i ) { err = fd_slot_hash_decode_preflight(ctx); if ( FD_UNLIKELY(err) ) return err; } @@ -9406,9 +9462,8 @@ int fd_recent_block_hashes_decode_preflight(fd_bincode_decode_ctx_t * ctx) { int err; ulong hashes_len; err = fd_bincode_uint64_decode( &hashes_len, ctx ); - if ( FD_UNLIKELY(err) ) return err; - if ( hashes_len > 350 ) return FD_BINCODE_ERR_SMALL_DEQUE; - for (ulong i = 0; i < hashes_len; ++i) { + if( FD_UNLIKELY( err ) ) return err; + for( ulong i = 0; i < hashes_len; ++i ) { err = fd_block_block_hash_entry_decode_preflight(ctx); if ( FD_UNLIKELY(err) ) return err; } @@ -9417,7 +9472,8 @@ int fd_recent_block_hashes_decode_preflight(fd_bincode_decode_ctx_t * ctx) { void fd_recent_block_hashes_decode_unsafe(fd_recent_block_hashes_t* self, fd_bincode_decode_ctx_t * ctx) { ulong hashes_len; fd_bincode_uint64_decode_unsafe( &hashes_len, ctx ); - self->hashes = deq_fd_block_block_hash_entry_t_alloc( ctx->valloc ); + ulong hashes_max = fd_ulong_max( hashes_len, 151 ); + self->hashes = deq_fd_block_block_hash_entry_t_alloc( ctx->valloc, hashes_max ); for (ulong i = 0; i < hashes_len; ++i) { fd_block_block_hash_entry_t * elem = deq_fd_block_block_hash_entry_t_push_tail_nocopy(self->hashes); fd_block_block_hash_entry_new(elem); @@ -9430,9 +9486,8 @@ int fd_recent_block_hashes_decode_offsets(fd_recent_block_hashes_off_t* self, fd self->hashes_off = (uint)((ulong)ctx->data - (ulong)data); ulong hashes_len; err = fd_bincode_uint64_decode( &hashes_len, ctx ); - if ( FD_UNLIKELY(err) ) return err; - if ( hashes_len > 350 ) return FD_BINCODE_ERR_SMALL_DEQUE; - for (ulong i = 0; i < hashes_len; ++i) { + if( FD_UNLIKELY( err ) ) return err; + for( ulong i = 0; i < hashes_len; ++i ) { err = fd_block_block_hash_entry_decode_preflight(ctx); if ( FD_UNLIKELY(err) ) return err; } @@ -11165,9 +11220,8 @@ int fd_vote_decode_preflight(fd_bincode_decode_ctx_t * ctx) { int err; ulong slots_len; err = fd_bincode_uint64_decode( &slots_len, ctx ); - if ( FD_UNLIKELY(err) ) return err; - if ( slots_len > 35 ) return FD_BINCODE_ERR_SMALL_DEQUE; - for (ulong i = 0; i < slots_len; ++i) { + if( FD_UNLIKELY( err ) ) return err; + for( ulong i = 0; i < slots_len; ++i ) { err = fd_bincode_uint64_decode_preflight(ctx); if ( FD_UNLIKELY(err) ) return err; } @@ -11187,7 +11241,7 @@ int fd_vote_decode_preflight(fd_bincode_decode_ctx_t * ctx) { void fd_vote_decode_unsafe(fd_vote_t* self, fd_bincode_decode_ctx_t * ctx) { ulong slots_len; fd_bincode_uint64_decode_unsafe( &slots_len, ctx ); - self->slots = deq_ulong_alloc( ctx->valloc ); + self->slots = deq_ulong_alloc( ctx->valloc, slots_len ); for (ulong i = 0; i < slots_len; ++i) { ulong * elem = deq_ulong_push_tail_nocopy(self->slots); fd_bincode_uint64_decode_unsafe(elem, ctx); @@ -11209,9 +11263,8 @@ int fd_vote_decode_offsets(fd_vote_off_t* self, fd_bincode_decode_ctx_t * ctx) { self->slots_off = (uint)((ulong)ctx->data - (ulong)data); ulong slots_len; err = fd_bincode_uint64_decode( &slots_len, ctx ); - if ( FD_UNLIKELY(err) ) return err; - if ( slots_len > 35 ) return FD_BINCODE_ERR_SMALL_DEQUE; - for (ulong i = 0; i < slots_len; ++i) { + if( FD_UNLIKELY( err ) ) return err; + for( ulong i = 0; i < slots_len; ++i ) { err = fd_bincode_uint64_decode_preflight(ctx); if ( FD_UNLIKELY(err) ) return err; } diff --git a/src/flamenco/types/fd_types.h b/src/flamenco/types/fd_types.h index 7bd760d681..e61e138240 100644 --- a/src/flamenco/types/fd_types.h +++ b/src/flamenco/types/fd_types.h @@ -341,18 +341,18 @@ typedef struct fd_stake_history_entry_off fd_stake_history_entry_off_t; #define FD_STAKE_HISTORY_ENTRY_OFF_FOOTPRINT sizeof(fd_stake_history_entry_off_t) #define FD_STAKE_HISTORY_ENTRY_OFF_ALIGN (8UL) -#define FD_STAKE_HISTORY_MAX 512 +#define FD_STAKE_HISTORY_MIN 512 #define POOL_NAME fd_stake_history_pool #define POOL_T fd_stake_history_entry_t #define POOL_NEXT parent #include "../../util/tmpl/fd_pool.c" static inline fd_stake_history_entry_t * -fd_stake_history_pool_alloc( fd_valloc_t valloc ) { +fd_stake_history_pool_alloc( fd_valloc_t valloc, ulong num ) { return fd_stake_history_pool_join( fd_stake_history_pool_new( fd_valloc_malloc( valloc, fd_stake_history_pool_align(), - fd_stake_history_pool_footprint( FD_STAKE_HISTORY_MAX ) ), - FD_STAKE_HISTORY_MAX ) ); + fd_stake_history_pool_footprint( num ) ), + num ) ); } #define TREAP_NAME fd_stake_history_treap #define TREAP_T fd_stake_history_entry_t @@ -361,12 +361,12 @@ fd_stake_history_pool_alloc( fd_valloc_t valloc ) { #define TREAP_LT(e0,e1) ((e0)->epoch<(e1)->epoch) #include "../../util/tmpl/fd_treap.c" static inline fd_stake_history_treap_t * -fd_stake_history_treap_alloc( fd_valloc_t valloc ) { +fd_stake_history_treap_alloc( fd_valloc_t valloc, ulong num ) { return fd_stake_history_treap_join( fd_stake_history_treap_new( fd_valloc_malloc( valloc, fd_stake_history_treap_align(), - fd_stake_history_treap_footprint( FD_STAKE_HISTORY_MAX ) ), - FD_STAKE_HISTORY_MAX ) ); + fd_stake_history_treap_footprint( num ) ), + num ) ); } /* https://github.com/firedancer-io/solana/blob/v1.17/sdk/program/src/stake_history.rs#L12-L75 */ /* Encoded Size: Dynamic */ @@ -1571,27 +1571,25 @@ typedef struct fd_landed_vote_off fd_landed_vote_off_t; #define DEQUE_NAME deq_fd_vote_lockout_t #define DEQUE_T fd_vote_lockout_t -#define DEQUE_MAX 1228 -#include "../../util/tmpl/fd_deque.c" +#include "../../util/tmpl/fd_deque_dynamic.c" #undef DEQUE_NAME #undef DEQUE_T #undef DEQUE_MAX static inline fd_vote_lockout_t * -deq_fd_vote_lockout_t_alloc( fd_valloc_t valloc ) { - void * mem = fd_valloc_malloc( valloc, deq_fd_vote_lockout_t_align(), deq_fd_vote_lockout_t_footprint()); - return deq_fd_vote_lockout_t_join( deq_fd_vote_lockout_t_new( mem ) ); +deq_fd_vote_lockout_t_alloc( fd_valloc_t valloc, ulong max ) { + void * mem = fd_valloc_malloc( valloc, deq_fd_vote_lockout_t_align(), deq_fd_vote_lockout_t_footprint( max ) ); + return deq_fd_vote_lockout_t_join( deq_fd_vote_lockout_t_new( mem, max ) ); } #define DEQUE_NAME deq_fd_vote_epoch_credits_t #define DEQUE_T fd_vote_epoch_credits_t -#define DEQUE_MAX 100 -#include "../../util/tmpl/fd_deque.c" +#include "../../util/tmpl/fd_deque_dynamic.c" #undef DEQUE_NAME #undef DEQUE_T #undef DEQUE_MAX static inline fd_vote_epoch_credits_t * -deq_fd_vote_epoch_credits_t_alloc( fd_valloc_t valloc ) { - void * mem = fd_valloc_malloc( valloc, deq_fd_vote_epoch_credits_t_align(), deq_fd_vote_epoch_credits_t_footprint()); - return deq_fd_vote_epoch_credits_t_join( deq_fd_vote_epoch_credits_t_new( mem ) ); +deq_fd_vote_epoch_credits_t_alloc( fd_valloc_t valloc, ulong max ) { + void * mem = fd_valloc_malloc( valloc, deq_fd_vote_epoch_credits_t_align(), deq_fd_vote_epoch_credits_t_footprint( max ) ); + return deq_fd_vote_epoch_credits_t_join( deq_fd_vote_epoch_credits_t_new( mem, max ) ); } /* https://github.com/solana-labs/solana/blob/8f2c8b8388a495d2728909e30460aa40dcc5d733/programs/vote/src/vote_state/vote_state_0_23_5.rs#L6 */ /* Encoded Size: Dynamic */ @@ -1602,10 +1600,10 @@ struct __attribute__((aligned(8UL))) fd_vote_state_0_23_5 { fd_vote_prior_voters_0_23_5_t prior_voters; fd_pubkey_t authorized_withdrawer; uchar commission; - fd_vote_lockout_t * votes; + fd_vote_lockout_t * votes; /* fd_deque_dynamic (min cnt 32) */ ulong root_slot; uchar has_root_slot; - fd_vote_epoch_credits_t * epoch_credits; + fd_vote_epoch_credits_t * epoch_credits; /* fd_deque_dynamic (min cnt 64) */ fd_vote_block_timestamp_t last_timestamp; }; typedef struct fd_vote_state_0_23_5 fd_vote_state_0_23_5_t; @@ -1628,18 +1626,18 @@ typedef struct fd_vote_state_0_23_5_off fd_vote_state_0_23_5_off_t; #define FD_VOTE_STATE_0_23_5_OFF_FOOTPRINT sizeof(fd_vote_state_0_23_5_off_t) #define FD_VOTE_STATE_0_23_5_OFF_ALIGN (8UL) -#define FD_VOTE_AUTHORIZED_VOTERS_MAX 64 +#define FD_VOTE_AUTHORIZED_VOTERS_MIN 64 #define POOL_NAME fd_vote_authorized_voters_pool #define POOL_T fd_vote_authorized_voter_t #define POOL_NEXT parent #include "../../util/tmpl/fd_pool.c" static inline fd_vote_authorized_voter_t * -fd_vote_authorized_voters_pool_alloc( fd_valloc_t valloc ) { +fd_vote_authorized_voters_pool_alloc( fd_valloc_t valloc, ulong num ) { return fd_vote_authorized_voters_pool_join( fd_vote_authorized_voters_pool_new( fd_valloc_malloc( valloc, fd_vote_authorized_voters_pool_align(), - fd_vote_authorized_voters_pool_footprint( FD_VOTE_AUTHORIZED_VOTERS_MAX ) ), - FD_VOTE_AUTHORIZED_VOTERS_MAX ) ); + fd_vote_authorized_voters_pool_footprint( num ) ), + num ) ); } #define TREAP_NAME fd_vote_authorized_voters_treap #define TREAP_T fd_vote_authorized_voter_t @@ -1648,12 +1646,12 @@ fd_vote_authorized_voters_pool_alloc( fd_valloc_t valloc ) { #define TREAP_LT(e0,e1) ((e0)->epoch<(e1)->epoch) #include "../../util/tmpl/fd_treap.c" static inline fd_vote_authorized_voters_treap_t * -fd_vote_authorized_voters_treap_alloc( fd_valloc_t valloc ) { +fd_vote_authorized_voters_treap_alloc( fd_valloc_t valloc, ulong num ) { return fd_vote_authorized_voters_treap_join( fd_vote_authorized_voters_treap_new( fd_valloc_malloc( valloc, fd_vote_authorized_voters_treap_align(), - fd_vote_authorized_voters_treap_footprint( FD_VOTE_AUTHORIZED_VOTERS_MAX ) ), - FD_VOTE_AUTHORIZED_VOTERS_MAX ) ); + fd_vote_authorized_voters_treap_footprint( num ) ), + num ) ); } /* https://github.com/solana-labs/solana/blob/8f2c8b8388a495d2728909e30460aa40dcc5d733/programs/vote/src/vote_state/mod.rs#L310 */ /* Encoded Size: Dynamic */ @@ -1678,12 +1676,12 @@ struct __attribute__((aligned(8UL))) fd_vote_state_1_14_11 { fd_pubkey_t node_pubkey; fd_pubkey_t authorized_withdrawer; uchar commission; - fd_vote_lockout_t * votes; + fd_vote_lockout_t * votes; /* fd_deque_dynamic (min cnt 32) */ ulong root_slot; uchar has_root_slot; fd_vote_authorized_voters_t authorized_voters; fd_vote_prior_voters_t prior_voters; - fd_vote_epoch_credits_t * epoch_credits; + fd_vote_epoch_credits_t * epoch_credits; /* fd_deque_dynamic (min cnt 64) */ fd_vote_block_timestamp_t last_timestamp; }; typedef struct fd_vote_state_1_14_11 fd_vote_state_1_14_11_t; @@ -1707,15 +1705,14 @@ typedef struct fd_vote_state_1_14_11_off fd_vote_state_1_14_11_off_t; #define DEQUE_NAME deq_fd_landed_vote_t #define DEQUE_T fd_landed_vote_t -#define DEQUE_MAX 35 -#include "../../util/tmpl/fd_deque.c" +#include "../../util/tmpl/fd_deque_dynamic.c" #undef DEQUE_NAME #undef DEQUE_T #undef DEQUE_MAX static inline fd_landed_vote_t * -deq_fd_landed_vote_t_alloc( fd_valloc_t valloc ) { - void * mem = fd_valloc_malloc( valloc, deq_fd_landed_vote_t_align(), deq_fd_landed_vote_t_footprint()); - return deq_fd_landed_vote_t_join( deq_fd_landed_vote_t_new( mem ) ); +deq_fd_landed_vote_t_alloc( fd_valloc_t valloc, ulong max ) { + void * mem = fd_valloc_malloc( valloc, deq_fd_landed_vote_t_align(), deq_fd_landed_vote_t_footprint( max ) ); + return deq_fd_landed_vote_t_join( deq_fd_landed_vote_t_new( mem, max ) ); } /* https://github.com/solana-labs/solana/blob/8f2c8b8388a495d2728909e30460aa40dcc5d733/programs/vote/src/vote_state/mod.rs#L310 */ /* Encoded Size: Dynamic */ @@ -1723,12 +1720,12 @@ struct __attribute__((aligned(8UL))) fd_vote_state { fd_pubkey_t node_pubkey; fd_pubkey_t authorized_withdrawer; uchar commission; - fd_landed_vote_t * votes; + fd_landed_vote_t * votes; /* fd_deque_dynamic (min cnt 32) */ ulong root_slot; uchar has_root_slot; fd_vote_authorized_voters_t authorized_voters; fd_vote_prior_voters_t prior_voters; - fd_vote_epoch_credits_t * epoch_credits; + fd_vote_epoch_credits_t * epoch_credits; /* fd_deque_dynamic (min cnt 64) */ fd_vote_block_timestamp_t last_timestamp; }; typedef struct fd_vote_state fd_vote_state_t; @@ -1785,7 +1782,7 @@ typedef struct fd_vote_state_versioned_off fd_vote_state_versioned_off_t; /* https://github.com/solana-labs/solana/blob/8f2c8b8388a495d2728909e30460aa40dcc5d733/programs/vote/src/vote_state/mod.rs#L185 */ /* Encoded Size: Dynamic */ struct __attribute__((aligned(8UL))) fd_vote_state_update { - fd_vote_lockout_t * lockouts; + fd_vote_lockout_t * lockouts; /* fd_deque_dynamic (min cnt 32) */ ulong root; uchar has_root; fd_hash_t hash; @@ -1848,7 +1845,7 @@ typedef struct fd_compact_vote_state_update_switch_off fd_compact_vote_state_upd /* https://github.com/solana-labs/solana/blob/8f2c8b8388a495d2728909e30460aa40dcc5d733/programs/vote/src/vote_state/mod.rs#L185 */ /* Encoded Size: Dynamic */ struct __attribute__((aligned(8UL))) fd_tower_sync { - fd_vote_lockout_t lockouts[32]; + fd_vote_lockout_t * lockouts; /* fd_deque_dynamic */ ulong lockouts_cnt; ulong root; uchar has_root; @@ -1962,20 +1959,19 @@ typedef struct fd_slot_hash_off fd_slot_hash_off_t; #define DEQUE_NAME deq_fd_slot_hash_t #define DEQUE_T fd_slot_hash_t -#define DEQUE_MAX 512 -#include "../../util/tmpl/fd_deque.c" +#include "../../util/tmpl/fd_deque_dynamic.c" #undef DEQUE_NAME #undef DEQUE_T #undef DEQUE_MAX static inline fd_slot_hash_t * -deq_fd_slot_hash_t_alloc( fd_valloc_t valloc ) { - void * mem = fd_valloc_malloc( valloc, deq_fd_slot_hash_t_align(), deq_fd_slot_hash_t_footprint()); - return deq_fd_slot_hash_t_join( deq_fd_slot_hash_t_new( mem ) ); +deq_fd_slot_hash_t_alloc( fd_valloc_t valloc, ulong max ) { + void * mem = fd_valloc_malloc( valloc, deq_fd_slot_hash_t_align(), deq_fd_slot_hash_t_footprint( max ) ); + return deq_fd_slot_hash_t_join( deq_fd_slot_hash_t_new( mem, max ) ); } /* https://github.com/solana-labs/solana/blob/8f2c8b8388a495d2728909e30460aa40dcc5d733/sdk/program/src/slot_hashes.rs#L31 */ /* Encoded Size: Dynamic */ struct __attribute__((aligned(8UL))) fd_slot_hashes { - fd_slot_hash_t * hashes; + fd_slot_hash_t * hashes; /* fd_deque_dynamic (min cnt 512) */ }; typedef struct fd_slot_hashes fd_slot_hashes_t; #define FD_SLOT_HASHES_FOOTPRINT sizeof(fd_slot_hashes_t) @@ -2007,19 +2003,18 @@ typedef struct fd_block_block_hash_entry_off fd_block_block_hash_entry_off_t; #define DEQUE_NAME deq_fd_block_block_hash_entry_t #define DEQUE_T fd_block_block_hash_entry_t -#define DEQUE_MAX 350 -#include "../../util/tmpl/fd_deque.c" +#include "../../util/tmpl/fd_deque_dynamic.c" #undef DEQUE_NAME #undef DEQUE_T #undef DEQUE_MAX static inline fd_block_block_hash_entry_t * -deq_fd_block_block_hash_entry_t_alloc( fd_valloc_t valloc ) { - void * mem = fd_valloc_malloc( valloc, deq_fd_block_block_hash_entry_t_align(), deq_fd_block_block_hash_entry_t_footprint()); - return deq_fd_block_block_hash_entry_t_join( deq_fd_block_block_hash_entry_t_new( mem ) ); +deq_fd_block_block_hash_entry_t_alloc( fd_valloc_t valloc, ulong max ) { + void * mem = fd_valloc_malloc( valloc, deq_fd_block_block_hash_entry_t_align(), deq_fd_block_block_hash_entry_t_footprint( max ) ); + return deq_fd_block_block_hash_entry_t_join( deq_fd_block_block_hash_entry_t_new( mem, max ) ); } /* Encoded Size: Dynamic */ struct __attribute__((aligned(8UL))) fd_recent_block_hashes { - fd_block_block_hash_entry_t * hashes; + fd_block_block_hash_entry_t * hashes; /* fd_deque_dynamic (min cnt 151) */ }; typedef struct fd_recent_block_hashes fd_recent_block_hashes_t; #define FD_RECENT_BLOCK_HASHES_FOOTPRINT sizeof(fd_recent_block_hashes_t) @@ -2394,20 +2389,19 @@ typedef struct fd_prev_epoch_inflation_rewards_off fd_prev_epoch_inflation_rewar #define DEQUE_NAME deq_ulong #define DEQUE_T ulong -#define DEQUE_MAX 35 -#include "../../util/tmpl/fd_deque.c" +#include "../../util/tmpl/fd_deque_dynamic.c" #undef DEQUE_NAME #undef DEQUE_T #undef DEQUE_MAX static inline ulong * -deq_ulong_alloc( fd_valloc_t valloc ) { - void * mem = fd_valloc_malloc( valloc, deq_ulong_align(), deq_ulong_footprint()); - return deq_ulong_join( deq_ulong_new( mem ) ); +deq_ulong_alloc( fd_valloc_t valloc, ulong max ) { + void * mem = fd_valloc_malloc( valloc, deq_ulong_align(), deq_ulong_footprint( max ) ); + return deq_ulong_join( deq_ulong_new( mem, max ) ); } /* https://github.com/solana-labs/solana/blob/8f2c8b8388a495d2728909e30460aa40dcc5d733/programs/vote/src/vote_state/mod.rs#L133 */ /* Encoded Size: Dynamic */ struct __attribute__((aligned(8UL))) fd_vote { - ulong * slots; + ulong * slots; /* fd_deque_dynamic */ fd_hash_t hash; ulong* timestamp; }; diff --git a/src/flamenco/types/fd_types.json b/src/flamenco/types/fd_types.json index cf4d2123d5..dbeca79ae2 100644 --- a/src/flamenco/types/fd_types.json +++ b/src/flamenco/types/fd_types.json @@ -190,7 +190,7 @@ "name": "stake_history", "type": "struct", "fields": [ - { "name": "fd_stake_history", "type": "treap", "treap_t": "fd_stake_history_entry_t", "treap_query_t": "ulong", "treap_cmp": "((q == (e)->epoch) ? 0 : ((q < (e)->epoch) ? -1 : 1 ) )", "treap_lt": "((e0)->epoch<(e1)->epoch)", "max": 512, "rev": true } + { "name": "fd_stake_history", "type": "treap", "treap_t": "fd_stake_history_entry_t", "treap_query_t": "ulong", "treap_cmp": "((q == (e)->epoch) ? 0 : ((q < (e)->epoch) ? -1 : 1 ) )", "treap_lt": "((e0)->epoch<(e1)->epoch)", "min": 512, "rev": true } ], "comment": "https://github.com/firedancer-io/solana/blob/v1.17/sdk/program/src/stake_history.rs#L12-L75" }, @@ -738,9 +738,9 @@ { "name": "prior_voters", "type": "vote_prior_voters_0_23_5" }, { "name": "authorized_withdrawer", "type": "pubkey" }, { "name": "commission", "type": "uchar" }, - { "name": "votes", "type": "deque", "element": "vote_lockout", "max":1228 }, + { "name": "votes", "type": "deque", "element": "vote_lockout", "min": 32 }, { "name": "root_slot", "type": "option", "element": "ulong", "flat": true }, - { "name": "epoch_credits", "type": "deque", "element": "vote_epoch_credits", "max":100 }, + { "name": "epoch_credits", "type": "deque", "element": "vote_epoch_credits", "min": 64 }, { "name": "last_timestamp", "type": "vote_block_timestamp" } ], "comment": "https://github.com/solana-labs/solana/blob/8f2c8b8388a495d2728909e30460aa40dcc5d733/programs/vote/src/vote_state/vote_state_0_23_5.rs#L6" @@ -749,7 +749,7 @@ "name": "vote_authorized_voters", "type": "struct", "fields": [ - { "name": "fd_vote_authorized_voters", "type": "treap", "treap_t": "fd_vote_authorized_voter_t", "treap_query_t": "ulong", "treap_cmp": "( (q == (e)->epoch) ? 0 : ( (q < (e)->epoch) ? -1 : 1 ) )", "treap_lt": "((e0)->epoch<(e1)->epoch)", "max": 64, "upsert": true } + { "name": "fd_vote_authorized_voters", "type": "treap", "treap_t": "fd_vote_authorized_voter_t", "treap_query_t": "ulong", "treap_cmp": "( (q == (e)->epoch) ? 0 : ( (q < (e)->epoch) ? -1 : 1 ) )", "treap_lt": "((e0)->epoch<(e1)->epoch)", "min": 64, "upsert": true } ], "comment": "https://github.com/solana-labs/solana/blob/8f2c8b8388a495d2728909e30460aa40dcc5d733/programs/vote/src/vote_state/mod.rs#L310" }, @@ -760,11 +760,11 @@ { "name": "node_pubkey", "type": "pubkey" }, { "name": "authorized_withdrawer", "type": "pubkey" }, { "name": "commission", "type": "uchar" }, - { "name": "votes", "type": "deque", "element": "vote_lockout", "max":1228 }, + { "name": "votes", "type": "deque", "element": "vote_lockout", "min": 32 }, { "name": "root_slot", "type": "option", "element": "ulong", "flat": true }, { "name": "authorized_voters", "type": "vote_authorized_voters" }, { "name": "prior_voters", "type": "vote_prior_voters" }, - { "name": "epoch_credits", "type": "deque", "element": "vote_epoch_credits", "max":64 }, + { "name": "epoch_credits", "type": "deque", "element": "vote_epoch_credits", "min": 64 }, { "name": "last_timestamp", "type": "vote_block_timestamp" } ], "comment": "https://github.com/solana-labs/solana/blob/8f2c8b8388a495d2728909e30460aa40dcc5d733/programs/vote/src/vote_state/mod.rs#L310" @@ -776,11 +776,11 @@ { "name": "node_pubkey", "type": "pubkey" }, { "name": "authorized_withdrawer", "type": "pubkey" }, { "name": "commission", "type": "uchar" }, - { "name": "votes", "type": "deque", "element": "landed_vote", "max":35 }, + { "name": "votes", "type": "deque", "element": "landed_vote", "min": 32 }, { "name": "root_slot", "type": "option", "element": "ulong", "flat": true }, { "name": "authorized_voters", "type": "vote_authorized_voters" }, { "name": "prior_voters", "type": "vote_prior_voters" }, - { "name": "epoch_credits", "type": "deque", "element": "vote_epoch_credits", "max":100 }, + { "name": "epoch_credits", "type": "deque", "element": "vote_epoch_credits", "min": 64 }, { "name": "last_timestamp", "type": "vote_block_timestamp" } ], "comment": "https://github.com/solana-labs/solana/blob/8f2c8b8388a495d2728909e30460aa40dcc5d733/programs/vote/src/vote_state/mod.rs#L310" @@ -800,7 +800,7 @@ "name": "vote_state_update", "type": "struct", "fields": [ - { "name": "lockouts", "type": "deque", "element": "vote_lockout", "max": 1228 }, + { "name": "lockouts", "type": "deque", "element": "vote_lockout", "min": 32 }, { "name": "root", "type": "option", "element": "ulong", "flat": true }, { "name": "hash", "type": "hash" }, { "name": "timestamp", "type": "option", "element": "ulong" } @@ -830,7 +830,7 @@ "name": "tower_sync", "type": "struct", "fields": [ - { "name": "lockouts", "type": "array", "element": "vote_lockout", "length": 32 }, + { "name": "lockouts", "type": "deque", "element": "vote_lockout" }, { "name": "lockouts_cnt", "type": "ulong" }, { "name": "root", "type": "option", "element": "ulong", "flat": true }, { "name": "hash", "type": "hash" }, @@ -885,7 +885,7 @@ "name": "slot_hashes", "type": "struct", "fields": [ - { "name": "hashes", "type": "deque", "element": "slot_hash", "max":512 } + { "name": "hashes", "type": "deque", "element": "slot_hash", "min": 512 } ], "comment": "https://github.com/solana-labs/solana/blob/8f2c8b8388a495d2728909e30460aa40dcc5d733/sdk/program/src/slot_hashes.rs#L31" }, @@ -901,7 +901,7 @@ "name": "recent_block_hashes", "type": "struct", "fields": [ - { "name": "hashes", "type": "deque", "element": "block_block_hash_entry", "max":350 } + { "name": "hashes", "type": "deque", "element": "block_block_hash_entry", "min": 151 } ] }, { @@ -1077,7 +1077,7 @@ "name": "vote", "type": "struct", "fields": [ - { "name": "slots", "type": "deque", "element": "ulong", "max":35 }, + { "name": "slots", "type": "deque", "element": "ulong" }, { "name": "hash", "type": "hash" }, { "name": "timestamp", "type": "option", "element": "ulong" } ], @@ -1492,7 +1492,7 @@ "name": "config_keys", "type": "struct", "fields": [ - { "name": "keys", "type": "vector", "element": "config_keys_pair", "modifier": "compact", "max":35 } + { "name": "keys", "type": "vector", "element": "config_keys_pair", "modifier": "compact" } ], "comment": "https://github.com/solana-labs/solana/blob/a03ae63daff987912c48ee286eb8ee7e8a84bf01/programs/config/src/lib.rs#L32" }, diff --git a/src/flamenco/types/gen_stubs.py b/src/flamenco/types/gen_stubs.py index 7c44abf7da..fb5df1a970 100644 --- a/src/flamenco/types/gen_stubs.py +++ b/src/flamenco/types/gen_stubs.py @@ -546,7 +546,7 @@ def __init__(self, container, json): self.name = json["name"] self.element = json["element"] self.compact = ("modifier" in json and json["modifier"] == "compact") - self.max = (json["max"] if "max" in json else None) + self.min = json.get("min", None) self.growth = (json["growth"] if "growth" in json else None) def elem_type(self): @@ -567,40 +567,27 @@ def emitPreamble(self): return preambletypes.add(dp) element_type = self.elem_type() - if self.max is not None: - print("#define DEQUE_NAME " + dp, file=header) - print("#define DEQUE_T " + element_type, file=header) - print(f'#define DEQUE_MAX {self.max}', file=header) - print('#include "../../util/tmpl/fd_deque.c"', file=header) - print("#undef DEQUE_NAME", file=header) - print("#undef DEQUE_T", file=header) - print("#undef DEQUE_MAX", file=header) - print(f'static inline {element_type} *', file=header) - print(f'{dp}_alloc( fd_valloc_t valloc ) {{', file=header) - print(f' void * mem = fd_valloc_malloc( valloc, {dp}_align(), {dp}_footprint());', file=header) - print(f' return {dp}_join( {dp}_new( mem ) );', file=header) - print("}", file=header) - else: - print("#define DEQUE_NAME " + dp, file=header) - print("#define DEQUE_T " + element_type, file=header) - print('#include "../../util/tmpl/fd_deque_dynamic.c"', file=header) - print("#undef DEQUE_NAME", file=header) - print("#undef DEQUE_T\n", file=header) - print(f'static inline {element_type} *', file=header) - print(f'{dp}_alloc( fd_valloc_t valloc, ulong len ) {{', file=header) - if self.growth is not None: - print(f' ulong max = len + {self.growth};', file=header) # Provide headroom - else: - print(f' ulong max = len + len/5 + 10;', file=header) # Provide headroom - print(f' void * mem = fd_valloc_malloc( valloc, {dp}_align(), {dp}_footprint( max ));', file=header) - print(f' return {dp}_join( {dp}_new( mem, max ) );', file=header) - print("}", file=header) + print("#define DEQUE_NAME " + dp, file=header) + print("#define DEQUE_T " + element_type, file=header) + print('#include "../../util/tmpl/fd_deque_dynamic.c"', file=header) + print("#undef DEQUE_NAME", file=header) + print("#undef DEQUE_T", file=header) + print("#undef DEQUE_MAX", file=header) + print(f'static inline {element_type} *', file=header) + print(f'{dp}_alloc( fd_valloc_t valloc, ulong max ) {{', file=header) + print(f' void * mem = fd_valloc_malloc( valloc, {dp}_align(), {dp}_footprint( max ) );', file=header) + print(f' return {dp}_join( {dp}_new( mem, max ) );', file=header) + print("}", file=header) def emitPostamble(self): pass def emitMember(self): - print(f' {self.elem_type()} * {self.name};', file=header) + if self.min: + min_tag = f" (min cnt {self.min})" + else: + min_tag = "" + print(f' {self.elem_type()} * {self.name}; /* fd_deque_dynamic{min_tag} */', file=header) def emitOffsetMember(self): print(f' uint {self.name}_off;', file=header) @@ -628,17 +615,17 @@ def emitDecodePreflight(self): else: print(f' ulong {self.name}_len;', file=body) print(f' err = fd_bincode_uint64_decode( &{self.name}_len, ctx );', file=body) - print(f' if ( FD_UNLIKELY(err) ) return err;', file=body) - if self.max is not None: - print(f' if ( {self.name}_len > {self.max} ) return FD_BINCODE_ERR_SMALL_DEQUE;', file=body) + print(f' if( FD_UNLIKELY( err ) ) return err;', file=body) elem_type = f"{namespace}_{self.element}" if elem_type in fixedsizetypes: fixedsize = fixedsizetypes[elem_type] - print(f' err = fd_bincode_bytes_decode_preflight({self.name}_len * {fixedsize}, ctx);', file=body) - print(f' if ( FD_UNLIKELY(err) ) return err;', file=body) + print(f' ulong {self.name}_sz;', file=body) + print(f' if( FD_UNLIKELY( __builtin_umull_overflow( {self.name}_len, {fixedsize}, &{self.name}_sz ) ) ) return FD_BINCODE_ERR_UNDERFLOW;', file=body) + print(f' err = fd_bincode_bytes_decode_preflight( {self.name}_sz, ctx );', file=body) + print(f' if( FD_UNLIKELY( err ) ) return err;', file=body) else: - print(f' for (ulong i = 0; i < {self.name}_len; ++i) {{', file=body) + print(f' for( ulong i = 0; i < {self.name}_len; ++i ) {{', file=body) if self.element in simpletypes: print(f' err = fd_bincode_{simpletypes[self.element]}_decode_preflight(ctx);', file=body) @@ -655,8 +642,10 @@ def emitDecodeUnsafe(self): else: print(f' ulong {self.name}_len;', file=body) print(f' fd_bincode_uint64_decode_unsafe( &{self.name}_len, ctx );', file=body) - if self.max is not None: - print(f' self->{self.name} = {self.prefix()}_alloc( ctx->valloc );', file=body) + + if self.min: + print(f' ulong {self.name}_max = fd_ulong_max( {self.name}_len, {self.min} );', file=body) + print(f' self->{self.name} = {self.prefix()}_alloc( ctx->valloc, {self.name}_max );', file=body) else: print(f' self->{self.name} = {self.prefix()}_alloc( ctx->valloc, {self.name}_len );', file=body) @@ -965,11 +954,12 @@ def __init__(self, container, json): self.treap_query_t = json["treap_query_t"] self.treap_cmp = json["treap_cmp"] self.treap_lt = json["treap_lt"] - self.max = int(json["max"]) + self.min = int(json["min"]) self.compact = ("modifier" in json and json["modifier"] == "compact") self.treap_prio = (json["treap_prio"] if "treap_prio" in json else None) self.rev = json.get("rev", False) self.upsert = json.get("upsert", False) + self.min_name = f"{self.name.upper()}_MIN" def isFixedSize(self): return False @@ -985,19 +975,18 @@ def emitPreamble(self): treap_cmp = self.treap_cmp treap_lt = self.treap_lt pool = name + '_pool' - max_name = f"{name.upper()}_MAX" - print(f"#define {max_name} {self.max}", file=header) + print(f"#define {self.min_name} {self.min}", file=header) print(f"#define POOL_NAME {pool}", file=header) print(f"#define POOL_T {treap_t}", file=header) print(f"#define POOL_NEXT parent", file=header) print("#include \"../../util/tmpl/fd_pool.c\"", file=header) print(f'static inline {treap_t} *', file=header) - print(f'{pool}_alloc( fd_valloc_t valloc ) {{', file=header) + print(f'{pool}_alloc( fd_valloc_t valloc, ulong num ) {{', file=header) print(f' return {pool}_join( {pool}_new(', file=header) print(f' fd_valloc_malloc( valloc,', file=header) print(f' {pool}_align(),', file=header) - print(f' {pool}_footprint( {max_name} ) ),', file=header) - print(f' {max_name} ) );', file=header) + print(f' {pool}_footprint( num ) ),', file=header) + print(f' num ) );', file=header) print("}", file=header) print(f"#define TREAP_NAME {treap_name}", file=header) print(f"#define TREAP_T {treap_t}", file=header) @@ -1008,12 +997,12 @@ def emitPreamble(self): print(f"#define TREAP_PRIO {self.treap_prio}", file=header) print("#include \"../../util/tmpl/fd_treap.c\"", file=header) print(f'static inline {treap_name}_t *', file=header) - print(f'{treap_name}_alloc( fd_valloc_t valloc ) {{', file=header) + print(f'{treap_name}_alloc( fd_valloc_t valloc, ulong num ) {{', file=header) print(f' return {treap_name}_join( {treap_name}_new(', file=header) print(f' fd_valloc_malloc( valloc,', file=header) print(f' {treap_name}_align(),', file=header) - print(f' {treap_name}_footprint( {name.upper()}_MAX ) ),', file=header) - print(f' {name.upper()}_MAX ) );', file=header) + print(f' {treap_name}_footprint( num ) ),', file=header) + print(f' num ) );', file=header) print("}", file=header) def emitPostamble(self): @@ -1049,7 +1038,6 @@ def emitDestroy(self): def emitDecodePreflight(self): treap_name = self.name + '_treap' treap_t = self.treap_t - pool_name = self.name + '_pool' if self.compact: print(f' ushort {treap_name}_len;', file=body) @@ -1057,8 +1045,7 @@ def emitDecodePreflight(self): else: print(f' ulong {treap_name}_len;', file=body) print(f' err = fd_bincode_uint64_decode(&{treap_name}_len, ctx);', file=body) - print(' if ( FD_UNLIKELY(err) ) return err;', file=body) - print(f' if ( {treap_name}_len > {self.name.upper()}_MAX ) return FD_BINCODE_ERR_SMALL_DEQUE;', file=body) + print(' if( FD_UNLIKELY( err ) ) return err;', file=body) print(f' for (ulong i = 0; i < {treap_name}_len; ++i) {{', file=body) print(f' err = {treap_t.rstrip("_t")}_decode_preflight( ctx );', file=body) @@ -1080,8 +1067,9 @@ def emitDecodeUnsafe(self): print(f' ulong {treap_name}_len;', file=body) print(f' fd_bincode_uint64_decode_unsafe(&{treap_name}_len, ctx);', file=body) - print(f' self->pool = {pool_name}_alloc( ctx->valloc );', file=body) - print(f' self->treap = {treap_name}_alloc( ctx->valloc );', file=body) + print(f' ulong {treap_name}_max = fd_ulong_max( {treap_name}_len, {self.min_name} );', file=body) + print(f' self->pool = {pool_name}_alloc( ctx->valloc, {treap_name}_max );', file=body) + print(f' self->treap = {treap_name}_alloc( ctx->valloc, {treap_name}_max );', file=body) print(f' for (ulong i = 0; i < {treap_name}_len; ++i) {{', file=body) print(f' {treap_t} * ele = {pool_name}_ele_acquire( self->pool );', file=body) print(f' {treap_t.rstrip("_t")}_new( ele );', file=body)