From b289efa92562bd87bd51458912e75be2d49fd89d Mon Sep 17 00:00:00 2001 From: Manik Jain Date: Tue, 18 Nov 2025 03:10:22 +0000 Subject: [PATCH 01/19] flamenco: vote states v4 --- src/choreo/voter/fd_voter.h | 8 +- src/flamenco/features/fd_features_generated.c | 8 + src/flamenco/features/fd_features_generated.h | 5 +- src/flamenco/features/feature_map.json | 3 +- src/flamenco/genesis/fd_genesis_create.c | 4 +- src/flamenco/rewards/fd_rewards.c | 7 +- src/flamenco/runtime/fd_runtime.h | 1 + .../runtime/program/fd_stake_program.c | 20 +- .../runtime/program/fd_vote_program.c | 2019 ++++++----------- .../runtime/program/fd_vote_program.h | 91 +- src/flamenco/runtime/program/vote/Local.mk | 17 + .../program/vote/fd_authorized_voters.c | 164 ++ .../program/vote/fd_authorized_voters.h | 51 + .../runtime/program/vote/fd_vote_common.c | 78 + .../runtime/program/vote/fd_vote_common.h | 32 + .../runtime/program/vote/fd_vote_lockout.c | 61 + .../runtime/program/vote/fd_vote_lockout.h | 29 + .../runtime/program/vote/fd_vote_state_v3.c | 219 ++ .../runtime/program/vote/fd_vote_state_v3.h | 51 + .../runtime/program/vote/fd_vote_state_v4.c | 117 + .../runtime/program/vote/fd_vote_state_v4.h | 40 + .../program/vote/fd_vote_state_versioned.c | 762 +++++++ .../program/vote/fd_vote_state_versioned.h | 167 ++ src/flamenco/runtime/tests/fd_block_harness.c | 8 +- src/flamenco/runtime/tests/fd_dump_pb.c | 6 +- src/flamenco/runtime/tests/test_dump_block.c | 4 +- src/flamenco/stakes/fd_stakes.c | 4 +- src/flamenco/stakes/fd_vote_states.c | 16 +- src/flamenco/types/fd_types.c | 323 ++- src/flamenco/types/fd_types.h | 75 +- src/flamenco/types/fd_types.json | 33 +- src/util/bits/fd_sat.h | 12 + 32 files changed, 3016 insertions(+), 1419 deletions(-) create mode 100644 src/flamenco/runtime/program/vote/Local.mk create mode 100644 src/flamenco/runtime/program/vote/fd_authorized_voters.c create mode 100644 src/flamenco/runtime/program/vote/fd_authorized_voters.h create mode 100644 src/flamenco/runtime/program/vote/fd_vote_common.c create mode 100644 src/flamenco/runtime/program/vote/fd_vote_common.h create mode 100644 src/flamenco/runtime/program/vote/fd_vote_lockout.c create mode 100644 src/flamenco/runtime/program/vote/fd_vote_lockout.h create mode 100644 src/flamenco/runtime/program/vote/fd_vote_state_v3.c create mode 100644 src/flamenco/runtime/program/vote/fd_vote_state_v3.h create mode 100644 src/flamenco/runtime/program/vote/fd_vote_state_v4.c create mode 100644 src/flamenco/runtime/program/vote/fd_vote_state_v4.h create mode 100644 src/flamenco/runtime/program/vote/fd_vote_state_versioned.c create mode 100644 src/flamenco/runtime/program/vote/fd_vote_state_versioned.h diff --git a/src/choreo/voter/fd_voter.h b/src/choreo/voter/fd_voter.h index 76c2f3b8f3d..8c09b8684f5 100644 --- a/src/choreo/voter/fd_voter.h +++ b/src/choreo/voter/fd_voter.h @@ -16,10 +16,14 @@ #define FD_VOTER_V2 (1) #define FD_VOTER_V3 (2) +#define FD_VOTER_V4 (3) FD_STATIC_ASSERT( FD_VOTER_V2==fd_vote_state_versioned_enum_v1_14_11, FD_VOTER_V2 ); -FD_STATIC_ASSERT( FD_VOTER_V3==fd_vote_state_versioned_enum_current, FD_VOTER_V3 ); +FD_STATIC_ASSERT( FD_VOTER_V3==fd_vote_state_versioned_enum_v3, FD_VOTER_V3 ); +FD_STATIC_ASSERT( FD_VOTER_V4==fd_vote_state_versioned_enum_v4, FD_VOTER_V4 ); -/* fd_voter describes the layout of a vote state stored in a vote +/* TODO: Update for vote state v4 + + fd_voter describes the layout of a vote state stored in a vote account. These structs are used to support zero-copy access (direct casts) of byte arrays containing the vote account data. diff --git a/src/flamenco/features/fd_features_generated.c b/src/flamenco/features/fd_features_generated.c index 88021f1c655..5c058964ee5 100644 --- a/src/flamenco/features/fd_features_generated.c +++ b/src/flamenco/features/fd_features_generated.c @@ -1715,6 +1715,12 @@ fd_feature_id_t const ids[] = { .name = "increase_cpi_account_info_limit", .cleaned_up = {UINT_MAX, UINT_MAX, UINT_MAX} }, + { .index = offsetof(fd_features_t, vote_state_v4)>>3, + .id = {"\xec\xfa\x3a\xf2\xab\xa3\x21\x89\x19\xf4\xc3\x4c\x05\xdf\x88\xf0\x79\x57\x48\xf1\x3e\x35\x12\x0b\x7b\x56\xaa\xae\x3f\xe9\x98\x58"}, + /* Gx4XFcrVMt4HUvPzTpTSVkdDVgcDSjKhDN1RqRS6KDuZ */ + .name = "vote_state_v4", + .cleaned_up = {UINT_MAX, UINT_MAX, UINT_MAX} }, + { .index = ULONG_MAX } }; /* TODO replace this with fd_map_perfect */ @@ -1972,6 +1978,7 @@ fd_feature_id_query( ulong prefix ) { case 0x7c4802b8ba3fa849: return &ids[ 248 ]; case 0xab2a2311ca83eb09: return &ids[ 249 ]; case 0x55792888a8cf31ef: return &ids[ 250 ]; + case 0x8921a3abf23afaec: return &ids[ 251 ]; default: break; } return NULL; @@ -2228,4 +2235,5 @@ FD_STATIC_ASSERT( offsetof( fd_features_t, relax_intrabatch_account_locks FD_STATIC_ASSERT( offsetof( fd_features_t, provide_instruction_data_offset_in_vm_r2 )>>3==248UL, layout ); FD_STATIC_ASSERT( offsetof( fd_features_t, enforce_fixed_fec_set )>>3==249UL, layout ); FD_STATIC_ASSERT( offsetof( fd_features_t, increase_cpi_account_info_limit )>>3==250UL, layout ); +FD_STATIC_ASSERT( offsetof( fd_features_t, vote_state_v4 )>>3==251UL, layout ); FD_STATIC_ASSERT( sizeof( fd_features_t )>>3==FD_FEATURE_ID_CNT, layout ); diff --git a/src/flamenco/features/fd_features_generated.h b/src/flamenco/features/fd_features_generated.h index e9fe6f2ea8b..1686db0e1fc 100644 --- a/src/flamenco/features/fd_features_generated.h +++ b/src/flamenco/features/fd_features_generated.h @@ -8,10 +8,10 @@ #endif /* FEATURE_ID_CNT is the number of features in ids */ -#define FD_FEATURE_ID_CNT (251UL) +#define FD_FEATURE_ID_CNT (252UL) /* Feature set ID calculated from all feature names */ -#define FD_FEATURE_SET_ID (1776864602U) +#define FD_FEATURE_SET_ID (4221067627U) union fd_features { ulong f[ FD_FEATURE_ID_CNT ]; @@ -267,5 +267,6 @@ union fd_features { /* 0x7c4802b8ba3fa849 */ ulong provide_instruction_data_offset_in_vm_r2; /* 0xab2a2311ca83eb09 */ ulong enforce_fixed_fec_set; /* 0x55792888a8cf31ef */ ulong increase_cpi_account_info_limit; + /* 0x8921a3abf23afaec */ ulong vote_state_v4; }; }; diff --git a/src/flamenco/features/feature_map.json b/src/flamenco/features/feature_map.json index 92e6b4f6177..002d9004974 100644 --- a/src/flamenco/features/feature_map.json +++ b/src/flamenco/features/feature_map.json @@ -249,5 +249,6 @@ {"name":"relax_intrabatch_account_locks","pubkey":"ENTRYnPAoT5Swwx73YDGzMp3XnNH1kxacyvLosRHza1i"}, {"name":"provide_instruction_data_offset_in_vm_r2","pubkey":"5xXZc66h4UdB6Yq7FzdBxBiRAFMMScMLwHxk2QZDaNZL"}, {"name":"enforce_fixed_fec_set","pubkey":"fixfecLZYMfkGzwq6NJA11Yw6KYztzXiK9QcL3K78in"}, - {"name":"increase_cpi_account_info_limit","pubkey":"H6iVbVaDZgDphcPbcZwc5LoznMPWQfnJ1AM7L1xzqvt5"} + {"name":"increase_cpi_account_info_limit","pubkey":"H6iVbVaDZgDphcPbcZwc5LoznMPWQfnJ1AM7L1xzqvt5"}, + {"name":"vote_state_v4","pubkey":"Gx4XFcrVMt4HUvPzTpTSVkdDVgcDSjKhDN1RqRS6KDuZ"} ] diff --git a/src/flamenco/genesis/fd_genesis_create.c b/src/flamenco/genesis/fd_genesis_create.c index f57309795ec..919bd3187b5 100644 --- a/src/flamenco/genesis/fd_genesis_create.c +++ b/src/flamenco/genesis/fd_genesis_create.c @@ -122,9 +122,9 @@ genesis_create( void * buf, FD_SCRATCH_SCOPE_BEGIN { fd_vote_state_versioned_t vsv[1]; - fd_vote_state_versioned_new_disc( vsv, fd_vote_state_versioned_enum_current ); + fd_vote_state_versioned_new_disc( vsv, fd_vote_state_versioned_enum_v3 ); - fd_vote_state_t * vs = &vsv->inner.current; + fd_vote_state_v3_t * vs = &vsv->inner.v3; vs->node_pubkey = options->identity_pubkey; vs->authorized_withdrawer = options->identity_pubkey; vs->commission = 100; diff --git a/src/flamenco/rewards/fd_rewards.c b/src/flamenco/rewards/fd_rewards.c index d449ac9d39a..2585e533c00 100644 --- a/src/flamenco/rewards/fd_rewards.c +++ b/src/flamenco/rewards/fd_rewards.c @@ -113,8 +113,11 @@ get_vote_credits( uchar const * account_data, case fd_vote_state_versioned_enum_v1_14_11: *credits = vsv->inner.v1_14_11.epoch_credits; break; - case fd_vote_state_versioned_enum_current: - *credits = vsv->inner.current.epoch_credits; + case fd_vote_state_versioned_enum_v3: + *credits = vsv->inner.v3.epoch_credits; + break; + case fd_vote_state_versioned_enum_v4: + *credits = vsv->inner.v4.epoch_credits; break; default: __builtin_unreachable(); diff --git a/src/flamenco/runtime/fd_runtime.h b/src/flamenco/runtime/fd_runtime.h index 39ea6d3ceea..64c9a51cb40 100644 --- a/src/flamenco/runtime/fd_runtime.h +++ b/src/flamenco/runtime/fd_runtime.h @@ -96,6 +96,7 @@ struct fd_runtime { uchar authorized_voters_mem [ FD_AUTHORIZED_VOTERS_FOOTPRINT ] __attribute__((aligned(FD_AUTHORIZED_VOTERS_ALIGN))); uchar vote_state_landed_votes_mem[ FD_LANDED_VOTES_FOOTPRINT ] __attribute__((aligned(FD_LANDED_VOTES_ALIGN))); uchar tower_sync_landed_votes_mem[ FD_LANDED_VOTES_FOOTPRINT ] __attribute__((aligned(FD_LANDED_VOTES_ALIGN))); + uchar vote_lockout_mem [ FD_VOTE_LOCKOUTS_FOOTPRINT ] __attribute__((aligned(FD_VOTE_LOCKOUTS_ALIGN))); } tower_sync; struct { diff --git a/src/flamenco/runtime/program/fd_stake_program.c b/src/flamenco/runtime/program/fd_stake_program.c index 5d245838c00..b395aae15d8 100644 --- a/src/flamenco/runtime/program/fd_stake_program.c +++ b/src/flamenco/runtime/program/fd_stake_program.c @@ -1189,7 +1189,7 @@ get_stake_status( fd_exec_instr_ctx_t const * invoke_context, // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/sdk/program/src/vote/state/mod.rs#L740 static ulong -get_credits( fd_vote_state_t const * vote_state ) { +get_credits( fd_vote_state_v3_t const * vote_state ) { return ( deq_fd_vote_epoch_credits_t_empty( vote_state->epoch_credits ) ? 0 @@ -1205,7 +1205,7 @@ redelegate_stake( fd_exec_instr_ctx_t const * ctx, fd_stake_t * stake, ulong stake_lamports, fd_pubkey_t const * voter_pubkey, - fd_vote_state_t const * vote_state, + fd_vote_state_v3_t const * vote_state, fd_sol_sysvar_clock_t const * clock, fd_stake_history_t const * stake_history, uint * custom_err ) { @@ -1247,10 +1247,10 @@ redelegate_stake( fd_exec_instr_ctx_t const * ctx, // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L202 static fd_stake_t -new_stake( ulong stake, - fd_pubkey_t const * voter_pubkey, - fd_vote_state_t const * vote_state, - ulong activation_epoch ) { +new_stake( ulong stake, + fd_pubkey_t const * voter_pubkey, + fd_vote_state_v3_t const * vote_state, + ulong activation_epoch ) { // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L208 return ( fd_stake_t ){ .delegation = {.voter_pubkey = *voter_pubkey, @@ -1465,7 +1465,7 @@ delegate( fd_exec_instr_ctx_t const * ctx, ctx->runtime->stake_program.delegate.landed_votes_mem ); fd_stake_t stake = new_stake( stake_amount, vote_pubkey, - &vote_state->inner.current, + &vote_state->inner.v3, clock->epoch ); // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L343 fd_stake_state_v2_t new_stake_state = { .discriminant = fd_stake_state_v2_enum_stake, @@ -1502,7 +1502,7 @@ delegate( fd_exec_instr_ctx_t const * ctx, &stake, stake_amount, vote_pubkey, - &vote_state->inner.current, + &vote_state->inner.v3, clock, stake_history, &ctx->txn_out->err.custom_err ); @@ -2443,7 +2443,7 @@ deactivate_delinquent( fd_exec_instr_ctx_t * ctx, fd_vote_convert_to_current( delinquent_vote_state_versioned, ctx->runtime->stake_program.deactivate_delinquent.delinquent_authorized_voters_mem, ctx->runtime->stake_program.deactivate_delinquent.delinquent_landed_votes_mem ); - fd_vote_state_t delinquent_vote_state = delinquent_vote_state_versioned->inner.current; + fd_vote_state_v3_t delinquent_vote_state = delinquent_vote_state_versioned->inner.v3; /* https://github.com/anza-xyz/agave/blob/v2.1.14/programs/stake/src/stake_state.rs#L924 */ fd_guarded_borrowed_account_t reference_vote_account = {0}; @@ -2459,7 +2459,7 @@ deactivate_delinquent( fd_exec_instr_ctx_t * ctx, fd_vote_convert_to_current( reference_vote_state_versioned, ctx->runtime->stake_program.deactivate_delinquent.reference_authorized_voters_mem, ctx->runtime->stake_program.deactivate_delinquent.reference_landed_votes_mem ); - fd_vote_state_t reference_vote_state = reference_vote_state_versioned->inner.current; + fd_vote_state_v3_t reference_vote_state = reference_vote_state_versioned->inner.v3; // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_state.rs#L933 if( !acceptable_reference_epoch_credits( reference_vote_state.epoch_credits, current_epoch ) ) { diff --git a/src/flamenco/runtime/program/fd_vote_program.c b/src/flamenco/runtime/program/fd_vote_program.c index 6a1121143d3..b096c399191 100644 --- a/src/flamenco/runtime/program/fd_vote_program.c +++ b/src/flamenco/runtime/program/fd_vote_program.c @@ -6,30 +6,21 @@ #include "../sysvar/fd_sysvar_rent.h" #include "../sysvar/fd_sysvar.h" #include "../fd_system_ids.h" +#include "vote/fd_authorized_voters.h" +#include "vote/fd_vote_common.h" +#include "vote/fd_vote_lockout.h" +#include "vote/fd_vote_state_versioned.h" +#include "vote/fd_vote_state_v3.h" +#include "vote/fd_vote_state_v4.h" #include #include #include #include -// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L35 -#define MAX_LOCKOUT_HISTORY 31UL - // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L36 #define INITIAL_LOCKOUT 2UL -// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L36 -#define MAX_EPOCH_CREDITS_HISTORY 64UL - -// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L42 -#define DEFAULT_PRIOR_VOTERS_OFFSET 114 - -// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L45 -#define VOTE_CREDITS_GRACE_SLOTS 2 - -// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L48 -#define VOTE_CREDITS_MAXIMUM_PER_SLOT 16 - // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L51 #define VOTE_CREDITS_MAXIMUM_PER_SLOT_OLD 8 @@ -39,868 +30,154 @@ // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/clock.rs#L147 #define SLOT_MAX ULONG_MAX -// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L886 -#define VERSION_OFFSET (4UL) - -// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L887 -#define DEFAULT_PRIOR_VOTERS_END (118) - -// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/vote_state_1_14_11.rs#L6 -#define DEFAULT_PRIOR_VOTERS_OFFSET_1_14_11 (82UL) - -// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/vote_state_1_14_11.rs#L60 -#define DEFAULT_PRIOR_VOTERS_END_1_14_11 (86UL) - #define ACCOUNTS_MAX 4 /* Vote instructions take in at most 4 accounts */ #define DEFAULT_COMPUTE_UNITS 2100UL /**********************************************************************/ -/* size_of */ -/**********************************************************************/ - -// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/vote_state_versions.rs#L82 -static inline ulong -size_of_versioned( int is_current ) { - return fd_ulong_if( is_current, FD_VOTE_STATE_V3_SZ, FD_VOTE_STATE_V2_SZ ); -} - -/**********************************************************************/ -/* impl Lockout */ +/* VoteStateHandler */ /**********************************************************************/ -// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L104 -static inline ulong -lockout( fd_vote_lockout_t * self ) { - /* Confirmation count can never be greater than MAX_LOCKOUT_HISTORY, preventing overflow. - Although Agave does not consider overflow, we do for fuzzing conformance. */ - ulong confirmation_count = fd_ulong_min( self->confirmation_count, MAX_LOCKOUT_HISTORY ); - return 1UL<slot, lockout( self ) ); -} - -// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L114 -static inline ulong -is_locked_out_at_slot( fd_vote_lockout_t * self, ulong slot ) { - return last_locked_out_slot( self ) >= slot; -} - -// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L122 -static void -increase_confirmation_count( fd_vote_lockout_t * self, uint by ) { - self->confirmation_count = fd_uint_sat_add( self->confirmation_count, by ); -} - -/**********************************************************************/ -/* impl From for VoteState1_14_11 */ -/**********************************************************************/ - -/* from_vote_state_1_14_11 converts a "current" vote state object into - the older "v1.14.11" version. This destroys the "current" object in - the process. */ - -// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/vote_state_1_14_11.rs#L67 -static void -from_vote_state_1_14_11( fd_vote_state_t * vote_state, - fd_vote_state_1_14_11_t * vote_state_1_14_11, /* out */ - uchar * vote_lockout_mem ) { - vote_state_1_14_11->node_pubkey = vote_state->node_pubkey; /* copy */ - vote_state_1_14_11->authorized_withdrawer = vote_state->authorized_withdrawer; /* copy */ - vote_state_1_14_11->commission = vote_state->commission; /* copy */ - - // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/vote_state_1_14_11.rs#L72 - if( vote_state->votes ) { - vote_state_1_14_11->votes = deq_fd_vote_lockout_t_join( - deq_fd_vote_lockout_t_new( vote_lockout_mem, 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 ); - deq_fd_vote_lockout_t_push_tail_wrap( vote_state_1_14_11->votes, landed_vote->lockout ); - } - } - - vote_state_1_14_11->has_root_slot = vote_state->has_root_slot; /* copy */ - vote_state_1_14_11->root_slot = vote_state->root_slot; /* copy */ - vote_state_1_14_11->authorized_voters = vote_state->authorized_voters; /* move */ - vote_state_1_14_11->prior_voters = vote_state->prior_voters; /* deep copy */ - vote_state_1_14_11->epoch_credits = vote_state->epoch_credits; /* move */ - vote_state_1_14_11->last_timestamp = vote_state->last_timestamp; /* deep copy */ - - /* Clear moved objects */ - vote_state->authorized_voters.treap = NULL; - vote_state->authorized_voters.pool = NULL; - vote_state->epoch_credits = NULL; - -} - -/**********************************************************************/ -/* impl VoteAccount */ -/**********************************************************************/ - -/* https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1074 */ +/* This is a temporary method in Agave (until the vote state v4 feature + is cleaned up) to check the vote state and, in some cases, check + if the vote account is uninitialized or not. Initializes a v3 or v4 + vote account depending on the target version. + https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L45-L77 */ static int -get_state( fd_account_meta_t const * meta, - uchar * res ) { - - fd_bincode_decode_ctx_t decode = { - .data = fd_account_data( meta ), - .dataend = fd_account_data( meta ) + meta->dlen, - }; - - ulong total_sz = 0UL; - int err = fd_vote_state_versioned_decode_footprint( &decode, &total_sz ); - if( FD_UNLIKELY( err ) ) { - return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA; - } - - FD_TEST( total_sz<=FD_VOTE_STATE_VERSIONED_FOOTPRINT ); - - fd_vote_state_versioned_decode( res, &decode ); - - return FD_EXECUTOR_INSTR_SUCCESS; - -} - -static int -set_state( fd_borrowed_account_t * self, - fd_vote_state_versioned_t * state ) { - /* https://github.com/anza-xyz/agave/blob/v2.1.14/sdk/src/transaction_context.rs#L974 */ - uchar * data = NULL; - ulong dlen = 0UL; - int err = fd_borrowed_account_get_data_mut( self, &data, &dlen ); - if( FD_UNLIKELY( err ) ) { - return err; - } - - // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/src/transaction_context.rs#L978 - ulong serialized_size = fd_vote_state_versioned_size( state ); - if( FD_UNLIKELY( serialized_size > dlen ) ) - return FD_EXECUTOR_INSTR_ERR_ACC_DATA_TOO_SMALL; - - // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/src/transaction_context.rs#L983 - fd_bincode_encode_ctx_t encode = - { .data = data, - .dataend = data + dlen }; - do { - int err = fd_vote_state_versioned_encode( state, &encode ); - if( FD_UNLIKELY( err ) ) FD_LOG_ERR(( "fd_vote_state_versioned_encode failed (%d)", err )); - } while(0); - - return FD_EXECUTOR_INSTR_SUCCESS; -} - -/**********************************************************************/ -/* impl AuthorizedVoters */ -/**********************************************************************/ - -// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/authorized_voters.rs#L17 -static fd_vote_authorized_voters_t * -authorized_voters_new( ulong epoch, - fd_pubkey_t const * pubkey, - uchar * mem ) { - - FD_SCRATCH_ALLOC_INIT( l, mem ); - fd_vote_authorized_voters_t * authorized_voters = FD_SCRATCH_ALLOC_APPEND( l, fd_vote_authorized_voters_align(), sizeof(fd_vote_authorized_voters_t) ); - void * pool_mem = FD_SCRATCH_ALLOC_APPEND( l, fd_vote_authorized_voters_pool_align(), fd_vote_authorized_voters_pool_footprint( FD_VOTE_AUTHORIZED_VOTERS_MIN ) ); - void * treap_mem = FD_SCRATCH_ALLOC_APPEND( l, fd_vote_authorized_voters_treap_align(), fd_vote_authorized_voters_treap_footprint( FD_VOTE_AUTHORIZED_VOTERS_MIN ) ); - - authorized_voters->pool = fd_vote_authorized_voters_pool_join( fd_vote_authorized_voters_pool_new( pool_mem, FD_VOTE_AUTHORIZED_VOTERS_MIN ) ); - authorized_voters->treap = fd_vote_authorized_voters_treap_join( fd_vote_authorized_voters_treap_new( treap_mem, FD_VOTE_AUTHORIZED_VOTERS_MIN ) ); - if( 0 == fd_vote_authorized_voters_pool_free( authorized_voters->pool ) ) { - FD_LOG_ERR(( "Authorized_voter pool is empty" )); - } - fd_vote_authorized_voter_t * ele = fd_vote_authorized_voters_pool_ele_acquire( authorized_voters->pool ); - ele->epoch = epoch; - ele->pubkey = *pubkey; - ele->prio = (ulong)&ele->pubkey; - fd_vote_authorized_voters_treap_ele_insert( authorized_voters->treap, ele, authorized_voters->pool ); - return authorized_voters; -} - -// Helper to create an empty AuthorizedVoters structure (for default/uninitialized states) -static fd_vote_authorized_voters_t * -authorized_voters_new_empty( uchar * mem ) { - FD_SCRATCH_ALLOC_INIT( l, mem ); - fd_vote_authorized_voters_t * authorized_voters = FD_SCRATCH_ALLOC_APPEND( l, fd_vote_authorized_voters_align(), sizeof(fd_vote_authorized_voters_t) ); - void * pool_mem = FD_SCRATCH_ALLOC_APPEND( l, fd_vote_authorized_voters_pool_align(), fd_vote_authorized_voters_pool_footprint( FD_VOTE_AUTHORIZED_VOTERS_MIN ) ); - void * treap_mem = FD_SCRATCH_ALLOC_APPEND( l, fd_vote_authorized_voters_treap_align(), fd_vote_authorized_voters_treap_footprint( FD_VOTE_AUTHORIZED_VOTERS_MIN ) ); - - authorized_voters->pool = fd_vote_authorized_voters_pool_join( fd_vote_authorized_voters_pool_new( pool_mem, FD_VOTE_AUTHORIZED_VOTERS_MIN ) ); - authorized_voters->treap = fd_vote_authorized_voters_treap_join( fd_vote_authorized_voters_treap_new( treap_mem, FD_VOTE_AUTHORIZED_VOTERS_MIN ) ); - return authorized_voters; -} - -static inline int -authorized_voters_is_empty( fd_vote_authorized_voters_t * self ) { - return fd_vote_authorized_voters_treap_ele_cnt( self->treap ) == 0; -} - -// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/authorized_voters.rs#L80 -static inline int -authorized_voters_contains( fd_vote_authorized_voters_t * self, ulong epoch ) { - return !!fd_vote_authorized_voters_treap_ele_query( self->treap, epoch, self->pool ); -} - -// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/authorized_voters.rs#L72 -static inline fd_vote_authorized_voter_t * -authorized_voters_last( fd_vote_authorized_voters_t * self ) { - fd_vote_authorized_voters_treap_rev_iter_t iter = - fd_vote_authorized_voters_treap_rev_iter_init( self->treap, self->pool ); - return fd_vote_authorized_voters_treap_rev_iter_ele( iter, self->pool ); -} - -// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/authorized_voters.rs#L43 -static void -authorized_voters_purge_authorized_voters( fd_vote_authorized_voters_t * self, - ulong current_epoch ) { - - // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/authorized_voters.rs#L46 - ulong expired_keys[ FD_VOTE_AUTHORIZED_VOTERS_MIN ]; - 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 ); - !fd_vote_authorized_voters_treap_fwd_iter_done( iter ); - iter = fd_vote_authorized_voters_treap_fwd_iter_next( iter, self->pool ) ) { - fd_vote_authorized_voter_t * ele = - fd_vote_authorized_voters_treap_fwd_iter_ele( iter, self->pool ); - if( ele->epoch < current_epoch ) expired_keys[key_cnt++] = ele->epoch; - } - - // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/authorized_voters.rs#L52 - for( ulong i = 0; i < key_cnt; i++ ) { - fd_vote_authorized_voter_t * ele = - fd_vote_authorized_voters_treap_ele_query( self->treap, expired_keys[i], self->pool ); - fd_vote_authorized_voters_treap_ele_remove( self->treap, ele, self->pool ); - fd_vote_authorized_voters_pool_ele_release( self->pool, ele ); - } - - // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/authorized_voters.rs#L60 - FD_TEST( !authorized_voters_is_empty( self ) ); - -} - -// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/authorized_voters.rs#L91 -static fd_vote_authorized_voter_t * -authorized_voters_get_or_calculate_authorized_voter_for_epoch( fd_vote_authorized_voters_t * self, - ulong epoch, - int * existed ) { - *existed = 0; - ulong latest_epoch = 0; - fd_vote_authorized_voter_t * res = - fd_vote_authorized_voters_treap_ele_query( self->treap, epoch, self->pool ); - // "predecessor" would be more big-O optimal here, but mirroring labs logic - // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/authorized_voters.rs#L93 - if( FD_UNLIKELY( !res ) ) { - for( fd_vote_authorized_voters_treap_fwd_iter_t iter = - fd_vote_authorized_voters_treap_fwd_iter_init( self->treap, self->pool ); - !fd_vote_authorized_voters_treap_fwd_iter_done( iter ); - iter = fd_vote_authorized_voters_treap_fwd_iter_next( iter, self->pool ) ) { - fd_vote_authorized_voter_t * ele = - fd_vote_authorized_voters_treap_fwd_iter_ele( iter, self->pool ); - if( ele->epoch < epoch && ( latest_epoch == 0 || ele->epoch > latest_epoch ) ) { - latest_epoch = ele->epoch; - res = ele; +get_vote_state_handler_checked( fd_borrowed_account_t const * vote_account, + int target_version, + uchar check_initialized, + uchar * vote_state_mem, + uchar * authorized_voters_mem, + uchar * landed_votes_mem ) { + int rc; + switch( target_version ) { + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L50-L62 */ + case VOTE_STATE_TARGET_VERSION_V3: { + rc = fd_vote_state_v3_deserialize( vote_account, vote_state_mem, authorized_voters_mem, landed_votes_mem ); + if( FD_UNLIKELY( rc ) ) return rc; + + fd_vote_state_versioned_t * versioned = (fd_vote_state_versioned_t *)vote_state_mem; + if( FD_UNLIKELY( check_initialized && fd_vsv_is_uninitialized( versioned ) ) ) { + return FD_EXECUTOR_INSTR_ERR_UNINITIALIZED_ACCOUNT; } - } - *existed = 0; - return res; - } else { - *existed = 1; - return res; - } - return res; -} -// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/authorized_voters.rs#L28 -static fd_vote_authorized_voter_t * -authorized_voters_get_and_cache_authorized_voter_for_epoch( fd_vote_authorized_voters_t * self, - ulong epoch ) { - int existed = 0; - // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/authorized_voters.rs#L29 - fd_vote_authorized_voter_t * res = - authorized_voters_get_or_calculate_authorized_voter_for_epoch( self, epoch, &existed ); - if( !res ) return NULL; - // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/authorized_voters.rs#L32 - if( !existed ) { - /* insert cannot fail because !existed */ - if( 0 == fd_vote_authorized_voters_pool_free( self->pool) ) { - FD_LOG_ERR(( "Authorized_voter pool is empty" )); + return FD_EXECUTOR_INSTR_SUCCESS; } - fd_vote_authorized_voter_t * ele = fd_vote_authorized_voters_pool_ele_acquire( self->pool ); - ele->epoch = epoch; - ele->pubkey = res->pubkey; - ele->prio = (ulong)&res->pubkey; - // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/authorized_voters.rs#L33 - fd_vote_authorized_voters_treap_ele_insert( self->treap, ele, self->pool ); - } - return res; -} - -/**********************************************************************/ -/* impl VoteStateVersions */ -/**********************************************************************/ - -// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/vote_state_versions.rs#L66 -static fd_landed_vote_t * -landed_votes_from_lockouts( fd_vote_lockout_t * lockouts, - uchar * mem ) { - if( !lockouts ) return NULL; - - /* 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_join( deq_fd_landed_vote_t_new( mem, cnt ) ); - if( FD_UNLIKELY( !landed_votes ) ) { - FD_LOG_CRIT(( "failed to join landed votes" )); - } - - 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 ); - iter = deq_fd_vote_lockout_t_iter_next( lockouts, iter ) ) { - fd_vote_lockout_t const * ele = deq_fd_vote_lockout_t_iter_ele_const( lockouts, iter ); - - fd_landed_vote_t * elem = deq_fd_landed_vote_t_push_tail_nocopy( landed_votes ); - fd_landed_vote_new( elem ); - - elem->latency = 0; - elem->lockout.slot = ele->slot; - elem->lockout.confirmation_count = ele->confirmation_count; - } - - return landed_votes; -} - - -// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/vote_state_versions.rs#L70 -static inline int -is_uninitialized( fd_vote_state_versioned_t * self ) { - switch( self->discriminant ) { - case fd_vote_state_versioned_enum_v0_23_5:; - // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/vote_state_versions.rs#L73 - fd_pubkey_t pubkey_default = { 0 }; - return 0 == - memcmp( &self->inner.v0_23_5.authorized_voter, &pubkey_default, sizeof( fd_pubkey_t ) ); - case fd_vote_state_versioned_enum_v1_14_11:; - return authorized_voters_is_empty( &self->inner.v1_14_11.authorized_voters ); - case fd_vote_state_versioned_enum_current: - return authorized_voters_is_empty( &self->inner.current.authorized_voters ); - default: - FD_LOG_ERR(( "missing handler or invalid vote state version: %u", self->discriminant )); - } -} - -// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/vote_state_versions.rs#L73 -static void -convert_to_current( fd_vote_state_versioned_t * self, - uchar * authorized_voters_mem, - uchar * landed_votes_mem ) { - switch( self->discriminant ) { - // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/vote_state_versions.rs#L19 - case fd_vote_state_versioned_enum_v0_23_5: { - fd_vote_state_0_23_5_t * state = &self->inner.v0_23_5; - // Check if uninitialized (authorized_voter is all zeros) - int is_uninitialized = 1; - for( ulong i = 0; i < sizeof(fd_pubkey_t); i++ ) { - if( state->authorized_voter.uc[i] != 0 ) { - is_uninitialized = 0; - break; + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L63-L75 */ + case VOTE_STATE_TARGET_VERSION_V4: { + rc = fd_vsv_deserialize( vote_account, vote_state_mem ); + if( FD_UNLIKELY( rc ) ) return rc; + + fd_vote_state_versioned_t * versioned = (fd_vote_state_versioned_t *)vote_state_mem; + if( FD_UNLIKELY( fd_vsv_is_uninitialized( versioned ) ) ) { + return FD_EXECUTOR_INSTR_ERR_UNINITIALIZED_ACCOUNT; } - } - fd_vote_authorized_voters_t * authorized_voters; - if( is_uninitialized ) { - // Create empty AuthorizedVoters (default), initialized but with no entries - authorized_voters = authorized_voters_new_empty( authorized_voters_mem ); - } else { - authorized_voters = authorized_voters_new( - state->authorized_voter_epoch, &state->authorized_voter, authorized_voters_mem ); - } + rc = fd_vsv_try_convert_to_v4( versioned, vote_account->pubkey, landed_votes_mem ); + if( FD_UNLIKELY( rc ) ) return rc; - /* Temporary to hold current */ - // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/vote_state_versions.rs#L23 - fd_vote_state_t current = { - .node_pubkey = state->node_pubkey, /* copy */ - .authorized_withdrawer = state->authorized_withdrawer, /* copy */ - .commission = state->commission, /* copy */ - .votes = landed_votes_from_lockouts( state->votes, landed_votes_mem ), - .has_root_slot = state->has_root_slot, /* copy */ - .root_slot = state->root_slot, /* copy */ - .authorized_voters = *authorized_voters, - .prior_voters = (fd_vote_prior_voters_t) { - .idx = 31UL, - .is_empty = 1, - }, - .epoch_credits = state->epoch_credits, /* move */ - .last_timestamp = state->last_timestamp, /* deep copy */ - }; - - /* Move objects */ - state->epoch_credits = NULL; - - /* Emplace new vote state into target */ - self->discriminant = fd_vote_state_versioned_enum_current; - self->inner.current = current; - - break; - } - // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/vote_state_versions.rs#L44 - case fd_vote_state_versioned_enum_v1_14_11: { - fd_vote_state_1_14_11_t * state = &self->inner.v1_14_11; - - /* Temporary to hold current */ - fd_vote_state_t current = { - .node_pubkey = state->node_pubkey, /* copy */ - .authorized_withdrawer = state->authorized_withdrawer, /* copy */ - .commission = state->commission, /* copy */ - .votes = landed_votes_from_lockouts( state->votes, landed_votes_mem ), - .has_root_slot = state->has_root_slot, /* copy */ - .root_slot = state->root_slot, /* copy */ - .authorized_voters = state->authorized_voters, /* move */ - .prior_voters = state->prior_voters, /* deep copy */ - .epoch_credits = state->epoch_credits, /* move */ - .last_timestamp = state->last_timestamp /* deep copy */ - }; - - /* Move objects */ - state->authorized_voters.treap = NULL; - state->authorized_voters.pool = NULL; - state->epoch_credits = NULL; - - /* Emplace new vote state into target */ - self->discriminant = fd_vote_state_versioned_enum_current; - self->inner.current = current; - - break; - } - case fd_vote_state_versioned_enum_current: - break; - default: - FD_LOG_ERR(( "unsupported vote state version: %u", self->discriminant )); - } -} - -/**********************************************************************/ -/* impl VoteState */ -/**********************************************************************/ - -// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L428 -static void -vote_state_new( fd_vote_init_t * vote_init, - fd_sol_sysvar_clock_t const * clock, - uchar * authorized_voters_mem, - fd_vote_state_t * vote_state /* out */ ) { - vote_state->node_pubkey = vote_init->node_pubkey; - vote_state->authorized_voters = *authorized_voters_new( clock->epoch, &vote_init->authorized_voter, authorized_voters_mem ); - // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L431 - vote_state->authorized_withdrawer = vote_init->authorized_withdrawer; - vote_state->commission = vote_init->commission; - // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L434 - vote_state->prior_voters.idx = 31; - vote_state->prior_voters.is_empty = 1; -} - -// https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L985 -static inline int -verify_authorized_signer( fd_pubkey_t const * authorized, - fd_pubkey_t const * signers[static FD_TXN_SIG_MAX] ) { - // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L989 - return fd_signers_contains( signers, authorized ) ? - FD_EXECUTOR_INSTR_SUCCESS : - FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE; -} - -// lambda function: https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L873 -static inline int -verify( fd_pubkey_t * epoch_authorized_voter, - int authorized_withdrawer_signer, - fd_pubkey_t const * signers[static FD_TXN_SIG_MAX] ) { - if( authorized_withdrawer_signer ) - return 0; - else - return verify_authorized_signer( epoch_authorized_voter, signers ); -} - -// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L845 -static void -pop_expired_votes( fd_vote_state_t * self, ulong next_vote_slot ) { - while( !deq_fd_landed_vote_t_empty( self->votes ) ) { - fd_landed_vote_t * vote = deq_fd_landed_vote_t_peek_tail( self->votes ); - if( !( is_locked_out_at_slot( &vote->lockout, next_vote_slot ) ) ) { - deq_fd_landed_vote_t_pop_tail( self->votes ); - } else { - break; + return FD_EXECUTOR_INSTR_SUCCESS; } + default: + __builtin_unreachable(); } } -// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L855 -static void -double_lockouts( fd_vote_state_t * self ) { - // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L856 - ulong stack_depth = deq_fd_landed_vote_t_cnt( self->votes ); - ulong i = 0; - // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L857 - for( deq_fd_landed_vote_t_iter_t iter = deq_fd_landed_vote_t_iter_init( self->votes ); - !deq_fd_landed_vote_t_iter_done( self->votes, iter ); - iter = deq_fd_landed_vote_t_iter_next( self->votes, iter ) ) { - fd_landed_vote_t * v = deq_fd_landed_vote_t_iter_ele( self->votes, iter ); - // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L860 - if( stack_depth > - fd_ulong_checked_add_expect( - i, - (ulong)v->lockout.confirmation_count, - "`confirmation_count` and tower_size should be bounded by `MAX_LOCKOUT_HISTORY`" ) ) - { - // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L864 - increase_confirmation_count( &v->lockout, 1 ); - } - i++; - } -} -// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L668 -static inline uchar -compute_vote_latency( ulong voted_for_slot, ulong current_slot ) { - return (uchar)fd_ulong_min( fd_ulong_sat_sub( current_slot, voted_for_slot ), UCHAR_MAX ); -} - -// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L673 -static ulong -credits_for_vote_at_index( fd_vote_state_t * self, ulong index ) { - // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L679 - fd_landed_vote_t * landed_vote = deq_fd_landed_vote_t_peek_index( self->votes, index ); - ulong latency = landed_vote == NULL ? 0 : landed_vote->latency; - // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L683 - ulong max_credits = VOTE_CREDITS_MAXIMUM_PER_SLOT; - - // If latency is 0, this means that the Lockout was created and stored from a software version - // that did not store vote latencies; in this case, 1 credit is awarded - // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L691 - if( FD_UNLIKELY( latency == 0 ) ) { - return 1; - } - - ulong diff = 0; - int cf = fd_ulong_checked_sub( latency, VOTE_CREDITS_GRACE_SLOTS, &diff ); - if( cf != 0 || diff == 0 ) { - // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L697 - return max_credits; - } - - ulong credits = 0; - cf = fd_ulong_checked_sub( max_credits, diff, &credits ); - if( cf != 0 || credits == 0 ) { - // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L705 - return 1; - } - // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L707 - return credits; -} - -/* https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v3.0.0/vote-interface/src/state/vote_state_v3.rs#L282-L309 */ -static void -increment_credits( fd_vote_state_t * self, ulong epoch, ulong credits ) { - /* https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v3.0.0/vote-interface/src/state/vote_state_v3.rs#L286-L305 */ - if( FD_UNLIKELY( deq_fd_vote_epoch_credits_t_empty( self->epoch_credits ) ) ) { - /* https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v3.0.0/vote-interface/src/state/vote_state_v3.rs#L286-L288 */ - deq_fd_vote_epoch_credits_t_push_tail_wrap( - self->epoch_credits, - ( fd_vote_epoch_credits_t ){ .epoch = epoch, .credits = 0, .prev_credits = 0 } ); - } else if( FD_LIKELY( epoch != - deq_fd_vote_epoch_credits_t_peek_tail( self->epoch_credits )->epoch ) ) { - /* https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v3.0.0/vote-interface/src/state/vote_state_v3.rs#L290 */ - fd_vote_epoch_credits_t * last = deq_fd_vote_epoch_credits_t_peek_tail( self->epoch_credits ); - - ulong credits = last->credits; - ulong prev_credits = last->prev_credits; - - /* https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v3.0.0/vote-interface/src/state/vote_state_v3.rs#L292-L299 */ - if( FD_LIKELY( credits!=prev_credits ) ) { - if( FD_UNLIKELY( deq_fd_vote_epoch_credits_t_cnt( self->epoch_credits )>=MAX_EPOCH_CREDITS_HISTORY ) ) { - /* Although Agave performs a `.remove(0)` AFTER the call to - `.push()`, there is an edge case where the epoch credits is - full, making the call to `_push_tail()` unsafe. Since Agave's - structures are dynamically allocated, it is safe for them to - simply call `.push()` and then popping afterwards. We have to - reverse the order of operations to maintain correct behavior - and avoid overflowing the deque. - https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v3.0.0/vote-interface/src/state/vote_state_v3.rs#L303 */ - deq_fd_vote_epoch_credits_t_pop_head( self->epoch_credits ); - } - - /* This will not fail because we already popped if we're at - capacity, since the epoch_credits deque is allocated with a - minimum capacity of MAX_EPOCH_CREDITS_HISTORY. */ - deq_fd_vote_epoch_credits_t_push_tail( - self->epoch_credits, - ( fd_vote_epoch_credits_t ){ - .epoch = epoch, .credits = credits, .prev_credits = credits } ); - } else { - /* https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v3.0.0/vote-interface/src/state/vote_state_v3.rs#L297-L298 */ - deq_fd_vote_epoch_credits_t_peek_tail( self->epoch_credits )->epoch = epoch; - - /* Here we can perform the same deque size check and pop if - we're beyond the maximum epoch credits len. */ - if( FD_UNLIKELY( deq_fd_vote_epoch_credits_t_cnt( self->epoch_credits )>MAX_EPOCH_CREDITS_HISTORY ) ) { - deq_fd_vote_epoch_credits_t_pop_head( self->epoch_credits ); - } - } +/* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L888-L902 */ +static int +check_vote_account_length( fd_borrowed_account_t const * vote_account, + int target_version ) { + ulong length = fd_borrowed_account_get_data_len( vote_account ); + ulong expected; + switch( target_version ) { + case VOTE_STATE_TARGET_VERSION_V3: + expected = FD_VOTE_STATE_V3_SZ; + break; + case VOTE_STATE_TARGET_VERSION_V4: + expected = FD_VOTE_STATE_V4_SZ; + break; + default: + __builtin_unreachable(); } - - // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L663 - deq_fd_vote_epoch_credits_t_peek_tail( self->epoch_credits )->credits = fd_ulong_sat_add( - deq_fd_vote_epoch_credits_t_peek_tail( self->epoch_credits )->credits, credits ); -} - -static inline ulong * -last_voted_slot( fd_vote_state_t * self ); - -// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L595 -static void -process_next_vote_slot( fd_vote_state_t * self, - ulong next_vote_slot, - ulong epoch, - ulong current_slot ) { - ulong * last_voted_slot_ = last_voted_slot( self ); - if( FD_UNLIKELY( last_voted_slot_ && next_vote_slot <= *last_voted_slot_ ) ) return; - - pop_expired_votes( self, next_vote_slot ); - - fd_landed_vote_t landed_vote = { .latency = compute_vote_latency( next_vote_slot, current_slot ), - ( fd_vote_lockout_t ){ .slot = next_vote_slot } }; - - // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L623 - if( FD_UNLIKELY( deq_fd_landed_vote_t_cnt( self->votes ) == MAX_LOCKOUT_HISTORY ) ) { - ulong credits = credits_for_vote_at_index( self, 0 ); - fd_landed_vote_t landed_vote = deq_fd_landed_vote_t_pop_head( self->votes ); - self->has_root_slot = 1; - self->root_slot = landed_vote.lockout.slot; - - increment_credits( self, epoch, credits ); + if( FD_UNLIKELY( length!=expected ) ) { + return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA; } - - // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L634 - deq_fd_landed_vote_t_push_tail_wrap( self->votes, landed_vote ); - double_lockouts( self ); -} - -// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L828 -static int -get_and_update_authorized_voter( fd_vote_state_t * self, - ulong current_epoch, - fd_pubkey_t ** pubkey /* out */ ) { - // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L832 - fd_vote_authorized_voter_t * authorized_voter = - authorized_voters_get_and_cache_authorized_voter_for_epoch( &self->authorized_voters, - current_epoch ); - // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L835 - if( FD_UNLIKELY( !authorized_voter ) ) return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA; - *pubkey = &authorized_voter->pubkey; - // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L837 - authorized_voters_purge_authorized_voters( &self->authorized_voters, current_epoch ); return FD_EXECUTOR_INSTR_SUCCESS; } -// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L768 +/* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L855-L870 */ static int -set_new_authorized_voter( fd_vote_state_t * self, - fd_pubkey_t const * authorized_pubkey, - ulong current_epoch, - ulong target_epoch, - /* "verify" closure */ int authorized_withdrawer_signer, - /* "verify" closure */ fd_pubkey_t const * signers[static FD_TXN_SIG_MAX], - fd_exec_instr_ctx_t const * ctx ) { - int rc; - fd_pubkey_t * epoch_authorized_voter = NULL; - - // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L778 - rc = get_and_update_authorized_voter( self, current_epoch, &epoch_authorized_voter ); - if( FD_UNLIKELY( rc ) ) return rc; - - // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L779 - rc = verify( epoch_authorized_voter, authorized_withdrawer_signer, signers ); - if( FD_UNLIKELY( rc ) ) return rc; - - // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L786 - if( FD_UNLIKELY( authorized_voters_contains( &self->authorized_voters, target_epoch ) ) ) { - ctx->txn_out->err.custom_err = FD_VOTE_ERR_TOO_SOON_TO_REAUTHORIZE; - return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR; - } - - // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L791 - fd_vote_authorized_voter_t * latest_authorized = - authorized_voters_last( &self->authorized_voters ); - // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L794 - if( FD_UNLIKELY( ( !latest_authorized ) ) ) return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA; - ulong latest_epoch = latest_authorized->epoch; - fd_pubkey_t * latest_authorized_pubkey = &latest_authorized->pubkey; - - // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L799 - if( 0 != memcmp( latest_authorized_pubkey, authorized_pubkey, sizeof( fd_pubkey_t ) ) ) { - fd_vote_prior_voters_t * prior_voters = &self->prior_voters; - - // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L801 - ulong epoch_of_last_authorized_switch = 0UL; - if( (!prior_voters->is_empty) & (prior_voters->idx < 32) ) { - epoch_of_last_authorized_switch = prior_voters->buf[prior_voters->idx].epoch_end; - } - - // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L810 - if( target_epoch <= latest_epoch ) - return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA; - - // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L815 - prior_voters->idx += 1UL; - prior_voters->idx %= 32UL; - prior_voters->buf[prior_voters->idx] = - ( fd_vote_prior_voter_t ){ .pubkey = *latest_authorized_pubkey, - .epoch_start = epoch_of_last_authorized_switch, - .epoch_end = target_epoch }; - prior_voters->is_empty = 0; - } - - // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L822 - if( 0 == fd_vote_authorized_voters_pool_free( self->authorized_voters.pool) ) { - FD_LOG_ERR(( "Authorized_voter pool is empty" )); - } - - fd_vote_authorized_voter_t * ele = - fd_vote_authorized_voters_pool_ele_acquire( self->authorized_voters.pool ); - ele->epoch = target_epoch; - ele->pubkey = *authorized_pubkey; - ele->prio = (ulong)&ele->pubkey; - fd_vote_authorized_voters_treap_ele_insert( - self->authorized_voters.treap, ele, self->authorized_voters.pool ); - - return 0; -} +init_vote_account_state( fd_exec_instr_ctx_t * ctx, + fd_borrowed_account_t * vote_account, + fd_vote_state_versioned_t * versioned, + int target_version, + fd_vote_init_t * vote_init, + fd_sol_sysvar_clock_t const * clock ) { + /* + * N.B. Technically we should destroy() to release memory before + * newing, otherwise the pointers are wiped and memory is leaked. + * We are probably fine for now since we are bump allocating + * everything and the enclosing frame will free everything when + * popped. + */ + /* Reset the object */ + fd_vote_state_versioned_new( versioned ); -// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L869 -static int -process_timestamp( fd_vote_state_t * self, - ulong slot, - long timestamp, - fd_exec_instr_ctx_t const * ctx ) { - if( FD_UNLIKELY( - ( slot < self->last_timestamp.slot || timestamp < self->last_timestamp.timestamp ) || - ( slot == self->last_timestamp.slot && - ( slot != self->last_timestamp.slot || timestamp != self->last_timestamp.timestamp ) && - self->last_timestamp.slot != 0 ) ) ) { - ctx->txn_out->err.custom_err = FD_VOTE_ERR_TIMESTAMP_TOO_OLD; - return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR; + switch( target_version ) { + case VOTE_STATE_TARGET_VERSION_V3: + fd_vote_program_v3_create_new( + vote_init, + clock, + ctx->runtime->vote_program.init_account.authorized_voters_mem, + versioned + ); + return fd_vote_state_v3_set_vote_account_state( + ctx, + vote_account, + versioned, + ctx->runtime->vote_program.init_account.vote_lockout_mem + ); + case VOTE_STATE_TARGET_VERSION_V4: + fd_vote_state_v4_create_new( + vote_account->pubkey, + vote_init, + clock, + ctx->runtime->vote_program.init_account.authorized_voters_mem, + versioned + ); + return fd_vote_state_v4_set_vote_account_state( ctx, vote_account, versioned ); + default: + __builtin_unreachable(); } - self->last_timestamp.slot = slot; - self->last_timestamp.timestamp = timestamp; - - return 0; } /**********************************************************************/ /* mod vote_state */ /**********************************************************************/ -// https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L166 -__attribute__((warn_unused_result)) static int -set_vote_account_state( fd_borrowed_account_t * vote_account, - fd_vote_state_t * vote_state, - fd_exec_instr_ctx_t const * ctx /* feature_set */, - uchar * vote_lockout_mem ) { - /* This is a horrible conditional expression in Agave. - The terms were broken up into their own variables. */ - - ulong vsz = size_of_versioned( 1 ); - - // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L175 - fd_rent_t const rent = fd_sysvar_cache_rent_read_nofail( ctx->sysvar_cache ); - int resize_needed = fd_borrowed_account_get_data_len( vote_account ) < vsz; - int resize_rent_exempt = fd_rent_exempt_minimum_balance( &rent, vsz ) <= fd_borrowed_account_get_lamports( vote_account ); - - /* The resize operation itself is part of the horrible conditional, - but behind a short-circuit operator. */ - int resize_failed = 0; - if( resize_needed && resize_rent_exempt ) { - // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L179 - resize_failed = - fd_borrowed_account_set_data_length( vote_account, vsz ) != FD_EXECUTOR_INSTR_SUCCESS; - } - - if( FD_UNLIKELY( resize_needed && ( !resize_rent_exempt || resize_failed ) ) ) { - // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L184 - fd_vote_state_versioned_t v1_14_11; - fd_vote_state_versioned_new_disc( &v1_14_11, fd_vote_state_versioned_enum_v1_14_11 ); - from_vote_state_1_14_11( vote_state, &v1_14_11.inner.v1_14_11, vote_lockout_mem ); - return set_state( vote_account, &v1_14_11 ); - } - - // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L189 - // TODO: This is stupid... optimize this... - fd_vote_state_versioned_t new_current = { .discriminant = fd_vote_state_versioned_enum_current, - .inner = { .current = *vote_state } }; - return set_state( vote_account, &new_current ); -} - -// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L727 -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 ); - return &last_vote->lockout; -} - -// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L731 -static inline ulong * -last_voted_slot( fd_vote_state_t * self ) { - fd_vote_lockout_t * last_lockout_ = last_lockout( self ); - if( FD_UNLIKELY( !last_lockout_ ) ) return NULL; - return &last_lockout_->slot; -} - -// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L573 -static uchar -contains_slot( fd_vote_state_t * vote_state, ulong slot ) { - /* Logic is copied from slice::binary_search_by() in Rust. While not fully optimized, - it aims to achieve fuzzing conformance for both sorted and unsorted inputs. */ - ulong size = deq_fd_landed_vote_t_cnt( vote_state->votes ); - if( FD_UNLIKELY( size==0UL ) ) return 0; - - ulong base = 0UL; - while( size>1UL ) { - ulong half = size / 2UL; - ulong mid = base + half; - ulong mid_slot = deq_fd_landed_vote_t_peek_index_const( vote_state->votes, mid )->lockout.slot; - base = (slotvotes, base )->lockout.slot==slot; -} - -// https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L201 +/* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L82-L324 */ static int -check_and_filter_proposed_vote_state( fd_vote_state_t * vote_state, +check_and_filter_proposed_vote_state( fd_exec_instr_ctx_t * ctx, + fd_vote_state_versioned_t * versioned, fd_vote_lockout_t * proposed_lockouts, uchar * proposed_has_root, ulong * proposed_root, fd_hash_t const * proposed_hash, - fd_slot_hash_t const * slot_hashes, /* deque */ - fd_exec_instr_ctx_t const * ctx ) { + fd_slot_hash_t const * slot_hashes /* deque */ ) { + fd_landed_vote_t const * votes = fd_vsv_get_votes( versioned ); + ulong const * root_slot = fd_vsv_get_root_slot( versioned ); + uchar has_root_slot = !!(root_slot!=NULL); + // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L208 if( FD_UNLIKELY( deq_fd_vote_lockout_t_empty( proposed_lockouts ) ) ) { ctx->txn_out->err.custom_err = FD_VOTE_ERR_EMPTY_SLOTS; return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR; } fd_landed_vote_t const * last_vote = NULL; - if( !deq_fd_landed_vote_t_empty( vote_state->votes ) ) { + if( !deq_fd_landed_vote_t_empty( votes ) ) { // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L212 - last_vote = deq_fd_landed_vote_t_peek_tail( vote_state->votes ); + last_vote = deq_fd_landed_vote_t_peek_tail_const( votes ); } // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L218 if( FD_LIKELY( last_vote ) ) { @@ -939,22 +216,22 @@ check_and_filter_proposed_vote_state( fd_vote_state_t * vote_state, the root to the latest vote in the current vote that's less than R. */ // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L242 if( proposed_root_ < earliest_slot_hash_in_history ) { - *proposed_has_root = vote_state->has_root_slot; - *proposed_root = vote_state->root_slot; - for( deq_fd_landed_vote_t_iter_t iter = - deq_fd_landed_vote_t_iter_init_rev( vote_state->votes ); - !deq_fd_landed_vote_t_iter_done_rev( vote_state->votes, iter ); - iter = deq_fd_landed_vote_t_iter_prev( vote_state->votes, iter ) ) { + *proposed_has_root = has_root_slot; + if( has_root_slot ) { + *proposed_root = *root_slot; + } + for( deq_fd_landed_vote_t_iter_t iter = deq_fd_landed_vote_t_iter_init_rev( votes ); + !deq_fd_landed_vote_t_iter_done_rev( votes, iter ); + iter = deq_fd_landed_vote_t_iter_prev( votes, iter ) ) { /* Ensure we're iterating from biggest to smallest vote in the current vote state */ - fd_landed_vote_t const * vote = deq_fd_landed_vote_t_iter_ele_const( vote_state->votes, iter ); + fd_landed_vote_t const * vote = deq_fd_landed_vote_t_iter_ele_const( votes, iter ); // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L248 if( vote->lockout.slot <= proposed_root_ ) { *proposed_has_root = 1; *proposed_root = vote->lockout.slot; break; } - } } } @@ -1035,7 +312,7 @@ check_and_filter_proposed_vote_state( fd_vote_state_t * vote_state, return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR; } // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L310 - if( !contains_slot( vote_state, proposed_vote_slot ) && !has_root_to_check ) { + if( !fd_vote_contains_slot( votes, proposed_vote_slot ) && !has_root_to_check ) { /* If the vote slot is both: 1) Too old 2) Doesn't already exist in vote state @@ -1155,18 +432,18 @@ check_and_filter_proposed_vote_state( fd_vote_state_t * vote_state, // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L440 static int -check_slots_are_valid( fd_vote_state_t * vote_state, - ulong const * vote_slots, - fd_hash_t const * vote_hash, - fd_slot_hash_t const * slot_hashes, /* deque */ - fd_exec_instr_ctx_t const * ctx ) { +check_slots_are_valid( fd_exec_instr_ctx_t * ctx, + fd_vote_state_versioned_t * versioned, + ulong const * vote_slots, + fd_hash_t const * vote_hash, + fd_slot_hash_t const * slot_hashes /* deque */ ) { ulong i = 0; ulong j = deq_fd_slot_hash_t_cnt( slot_hashes ); ulong vote_slots_len = deq_ulong_cnt( vote_slots ); // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L462 while( i < vote_slots_len && j > 0 ) { - ulong * last_voted_slot_ = last_voted_slot( vote_state ); + ulong const * last_voted_slot_ = fd_vsv_get_last_voted_slot( versioned ); if( FD_UNLIKELY( last_voted_slot_ && *deq_ulong_peek_index_const( vote_slots, i ) <= *last_voted_slot_ ) ) { // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L469 @@ -1212,16 +489,19 @@ check_slots_are_valid( fd_vote_state_t * vote_state, // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L565 static int -process_new_vote_state( fd_vote_state_t * vote_state, +process_new_vote_state( fd_exec_instr_ctx_t * ctx, + fd_vote_state_versioned_t * versioned, fd_landed_vote_t * new_state, int has_new_root, ulong new_root, int has_timestamp, long timestamp, ulong epoch, - ulong current_slot, - fd_exec_instr_ctx_t const * ctx /* feature_set */ ) { + ulong current_slot ) { int rc; + fd_landed_vote_t * votes = fd_vsv_get_votes_mutable( versioned ); + ulong const * root_slot = fd_vsv_get_root_slot( versioned ); + uchar has_root_slot = !!(root_slot!=NULL); FD_TEST( !deq_fd_landed_vote_t_empty( new_state ) ); // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L575 @@ -1231,13 +511,13 @@ process_new_vote_state( fd_vote_state_t * vote_state, }; // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L579 - if( FD_UNLIKELY( has_new_root && vote_state->has_root_slot ) ) { + if( FD_UNLIKELY( has_new_root && has_root_slot ) ) { // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L581 - if( FD_UNLIKELY( new_root < vote_state->root_slot ) ) { + if( FD_UNLIKELY( new_root<*root_slot ) ) { ctx->txn_out->err.custom_err = FD_VOTE_ERR_ROOT_ROLL_BACK; return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR; } - } else if( FD_UNLIKELY( !has_new_root && vote_state->has_root_slot ) ) { + } else if( FD_UNLIKELY( !has_new_root && has_root_slot ) ) { // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L586 ctx->txn_out->err.custom_err = FD_VOTE_ERR_ROOT_ROLL_BACK; return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR; @@ -1273,7 +553,7 @@ process_new_vote_state( fd_vote_state_t * vote_state, ctx->txn_out->err.custom_err = FD_VOTE_ERR_CONFIRMATIONS_NOT_ORDERED; return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR; } else if( FD_UNLIKELY( vote->lockout.slot > - last_locked_out_slot( &previous_vote->lockout ) ) ) { + fd_vote_lockout_last_locked_out_slot( &previous_vote->lockout ) ) ) { ctx->txn_out->err.custom_err = FD_VOTE_ERR_NEW_VOTE_STATE_LOCKOUT_MISMATCH; return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR; } @@ -1295,16 +575,18 @@ process_new_vote_state( fd_vote_state_t * vote_state, ulong earned_credits = 0; if( FD_LIKELY( has_new_root ) ) { - 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 * current_vote = deq_fd_landed_vote_t_iter_ele( vote_state->votes, iter ); + for( deq_fd_landed_vote_t_iter_t iter = deq_fd_landed_vote_t_iter_init( votes ); + !deq_fd_landed_vote_t_iter_done( votes, iter ); + iter = deq_fd_landed_vote_t_iter_next( votes, iter ) ) { + fd_landed_vote_t const * current_vote = deq_fd_landed_vote_t_iter_ele_const( votes, iter ); // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L647 if( FD_UNLIKELY( current_vote->lockout.slot <= new_root ) ) { // this is safe because we're inside if has_new_root earned_credits = fd_ulong_checked_add_expect( - credits_for_vote_at_index( vote_state, - current_vote_state_index ), + fd_vote_credits_for_vote_at_index( + votes, + current_vote_state_index + ), earned_credits, "`earned_credits` does not overflow" ); current_vote_state_index = fd_ulong_checked_add_expect( @@ -1339,10 +621,10 @@ process_new_vote_state( fd_vote_state_t * vote_state, // must have been expired by later votes. Check that the lockouts match this assumption. // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L686 - while( current_vote_state_index < deq_fd_landed_vote_t_cnt( vote_state->votes ) && + while( current_vote_state_index < deq_fd_landed_vote_t_cnt( votes ) && new_vote_state_index < deq_fd_landed_vote_t_cnt( new_state ) ) { - fd_landed_vote_t * current_vote = - deq_fd_landed_vote_t_peek_index( vote_state->votes, current_vote_state_index ); + fd_landed_vote_t const * current_vote = deq_fd_landed_vote_t_peek_index_const( votes, current_vote_state_index ); + // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L690 fd_landed_vote_t * new_vote = deq_fd_landed_vote_t_peek_index( new_state, new_vote_state_index ); @@ -1378,8 +660,7 @@ process_new_vote_state( fd_vote_state_t * vote_state, } // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L712 - new_vote->latency = - deq_fd_landed_vote_t_peek_index( vote_state->votes, current_vote_state_index )->latency; + new_vote->latency = deq_fd_landed_vote_t_peek_index_const( votes, current_vote_state_index )->latency; // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L714 current_vote_state_index = @@ -1412,144 +693,165 @@ process_new_vote_state( fd_vote_state_t * vote_state, if( FD_UNLIKELY( new_vote->latency == 0 ) ) { // this is unlikely because as validators upgrade, it should converge to the new vote state // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L739 - new_vote->latency = compute_vote_latency( new_vote->lockout.slot, current_slot ); + new_vote->latency = fd_vote_compute_vote_latency( new_vote->lockout.slot, current_slot ); } } // doesn't matter what the value of slot if `is_some = 0` i.e. `Option::None` // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L744 - int both_none = !vote_state->has_root_slot && !has_new_root; - if( ( !both_none && ( vote_state->has_root_slot != has_new_root || - vote_state->root_slot != new_root ) ) ) { - increment_credits( vote_state, epoch, earned_credits ); + int both_none = !has_root_slot && !has_new_root; + if( ( !both_none && ( has_root_slot!=has_new_root || + *root_slot!=new_root ) ) ) { + fd_vsv_increment_credits( versioned, epoch, earned_credits ); } // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L750 if( FD_LIKELY( has_timestamp ) ) { /* new_state asserted nonempty at function beginning */ if( deq_fd_landed_vote_t_empty( new_state ) ) { - FD_LOG_ERR(( "solana panic" )); - // TODO: solana panics ... unclear what to return - ctx->txn_out->err.custom_err = 0; - return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR; + FD_LOG_ERR(( "Landed votes is empty" )); } ulong last_slot = deq_fd_landed_vote_t_peek_tail( new_state )->lockout.slot; - rc = process_timestamp( vote_state, last_slot, timestamp, ctx ); + rc = fd_vsv_process_timestamp( ctx, versioned, last_slot, timestamp ); if( FD_UNLIKELY( rc ) ) { return rc; } - vote_state->last_timestamp.timestamp = timestamp; + fd_vsv_process_timestamp( ctx, versioned, last_slot, timestamp ); } // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L754 - vote_state->has_root_slot = (uchar)has_new_root; - vote_state->root_slot = new_root; + fd_vsv_set_root_slot( versioned, has_new_root ? &new_root : NULL ); // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L755 - deq_fd_landed_vote_t_remove_all( vote_state->votes ); + deq_fd_landed_vote_t_remove_all( votes ); for( deq_fd_landed_vote_t_iter_t iter = deq_fd_landed_vote_t_iter_init( new_state ); !deq_fd_landed_vote_t_iter_done( new_state, iter ); iter = deq_fd_landed_vote_t_iter_next( new_state, iter ) ) { fd_landed_vote_t * landed_vote = deq_fd_landed_vote_t_iter_ele( new_state, iter ); - deq_fd_landed_vote_t_push_tail_wrap( vote_state->votes, *landed_vote ); + deq_fd_landed_vote_t_push_tail_wrap( votes, *landed_vote ); } return FD_EXECUTOR_INSTR_SUCCESS; } -// https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L849 +/* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L716-L759 */ static int -authorize( fd_borrowed_account_t * vote_account, +authorize( fd_exec_instr_ctx_t * ctx, + fd_borrowed_account_t * vote_account, + int target_version, fd_pubkey_t const * authorized, fd_vote_authorize_t vote_authorize, fd_pubkey_t const * signers[static FD_TXN_SIG_MAX], - fd_sol_sysvar_clock_t const * clock, - fd_exec_instr_ctx_t const * ctx /* feature_set */ ) { - int rc = 0; - - // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L857 - - rc = get_state( vote_account->meta, ctx->runtime->vote_program.authorize.vote_state_mem ); + fd_sol_sysvar_clock_t const * clock ) { + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L724-L727 */ + int rc = get_vote_state_handler_checked( + vote_account, + target_version, + 0, + ctx->runtime->vote_program.authorize.vote_state_mem, + ctx->runtime->vote_program.authorize.authorized_voters_mem, + ctx->runtime->vote_program.authorize.landed_votes_mem + ); if( FD_UNLIKELY( rc ) ) return rc; - fd_vote_state_versioned_t * vote_state_versioned = (fd_vote_state_versioned_t *)ctx->runtime->vote_program.authorize.vote_state_mem; - convert_to_current( vote_state_versioned, - ctx->runtime->vote_program.authorize.authorized_voters_mem, - ctx->runtime->vote_program.authorize.landed_votes_mem ); - fd_vote_state_t * vote_state = &vote_state_versioned->inner.current; - - // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L861 + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L729-L756 */ + fd_vote_state_versioned_t * vote_state_versioned = (fd_vote_state_versioned_t *)ctx->runtime->vote_program.authorize.vote_state_mem; switch( vote_authorize.discriminant ) { - // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L862 - case fd_vote_authorize_enum_voter:; + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L730-L750 */ + case fd_vote_authorize_enum_voter: { - // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L863 - int authorized_withdrawer_signer = - FD_EXECUTOR_INSTR_SUCCESS == - verify_authorized_signer( &vote_state->authorized_withdrawer, signers ); + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L731-L732 */ + int authorized_withdrawer_signer = !fd_vote_verify_authorized_signer( + fd_vsv_get_authorized_withdrawer( vote_state_versioned ), + signers + ); - // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L869-L872 - ulong target_epoch; - rc = fd_ulong_checked_add( clock->leader_schedule_epoch, 1UL, &target_epoch ); - if( FD_UNLIKELY( rc!=FD_EXECUTOR_INSTR_SUCCESS ) ) { - return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA; - } - - // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L866 - rc = set_new_authorized_voter( vote_state, - authorized, - clock->epoch, - target_epoch, - authorized_withdrawer_signer, - signers, - ctx ); - if( FD_UNLIKELY( rc ) ) return rc; - break; + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L737-L740 */ + ulong target_epoch; + rc = fd_ulong_checked_add( clock->leader_schedule_epoch, 1UL, &target_epoch ); + if( FD_UNLIKELY( rc!=FD_EXECUTOR_INSTR_SUCCESS ) ) { + return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA; + } - // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L883 - case fd_vote_authorize_enum_withdrawer: - rc = verify_authorized_signer( &vote_state->authorized_withdrawer, signers ); - if( FD_UNLIKELY( rc ) ) return rc; - vote_state->authorized_withdrawer = *authorized; - break; + // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L866 + rc = fd_vsv_set_new_authorized_voter( + ctx, + vote_state_versioned, + authorized, + clock->epoch, + target_epoch, + authorized_withdrawer_signer, + signers + ); + if( FD_UNLIKELY( rc ) ) return rc; + break; + } - // failing exhaustive check is fatal - default: - __builtin_unreachable(); + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L751-L756 */ + case fd_vote_authorize_enum_withdrawer: { + rc = fd_vote_verify_authorized_signer( + fd_vsv_get_authorized_withdrawer( vote_state_versioned ), + signers + ); + if( FD_UNLIKELY( rc ) ) return rc; + fd_vsv_set_authorized_withdrawer( vote_state_versioned, authorized ); + break; + } + default: + __builtin_unreachable(); } - // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L890 - return set_vote_account_state( vote_account, vote_state, ctx, ctx->runtime->vote_program.authorize.vote_lockout_mem ); + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L758 */ + return fd_vsv_set_vote_account_state( + ctx, + vote_account, + vote_state_versioned, + ctx->runtime->vote_program.authorize.vote_lockout_mem + ); } -// https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L894 +/* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L761-L785 */ static int -update_validator_identity( fd_borrowed_account_t * vote_account, - fd_pubkey_t const * node_pubkey, - fd_pubkey_t const * signers[static FD_TXN_SIG_MAX], - fd_exec_instr_ctx_t const * ctx /* feature_set */ ) { - int rc = 0; - - // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L900 - rc = get_state( vote_account->meta, ctx->runtime->vote_program.update_validator_identity.vote_state_mem ); +update_validator_identity( fd_exec_instr_ctx_t * ctx, + int target_version, + fd_borrowed_account_t * vote_account, + fd_pubkey_t const * node_pubkey, + fd_pubkey_t const * signers[static FD_TXN_SIG_MAX] ) { + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L768-L771 */ + int rc = get_vote_state_handler_checked( + vote_account, + target_version, + 0, + ctx->runtime->vote_program.update_validator_identity.vote_state_mem, + ctx->runtime->vote_program.update_validator_identity.authorized_voters_mem, + ctx->runtime->vote_program.update_validator_identity.landed_votes_mem + ); if( FD_UNLIKELY( rc ) ) return rc; + fd_vote_state_versioned_t * vote_state_versioned = (fd_vote_state_versioned_t *)ctx->runtime->vote_program.update_validator_identity.vote_state_mem; - convert_to_current( vote_state_versioned, - ctx->runtime->vote_program.update_validator_identity.authorized_voters_mem, - ctx->runtime->vote_program.update_validator_identity.landed_votes_mem ); - fd_vote_state_t * vote_state = &vote_state_versioned->inner.current; - // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L905 - rc = verify_authorized_signer( &vote_state->authorized_withdrawer, signers ); + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L774 */ + rc = fd_vote_verify_authorized_signer( + fd_vsv_get_authorized_withdrawer( vote_state_versioned ), + signers + ); if( FD_UNLIKELY( rc ) ) return rc; - // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L908 - rc = verify_authorized_signer( node_pubkey, signers ); + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L777 */ + rc = fd_vote_verify_authorized_signer( node_pubkey, signers ); if( FD_UNLIKELY( rc ) ) return rc; - // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L910 - vote_state->node_pubkey = *node_pubkey; + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L779 */ + fd_vsv_set_node_pubkey( vote_state_versioned, node_pubkey ); + + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L782 */ + fd_vsv_set_block_revenue_collector( vote_state_versioned, node_pubkey ); - // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L912 - return set_vote_account_state( vote_account, vote_state, ctx, ctx->runtime->vote_program.update_validator_identity.vote_lockout_mem ); + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L784 */ + return fd_vsv_set_vote_account_state( + ctx, + vote_account, + vote_state_versioned, + ctx->runtime->vote_program.update_validator_identity.vote_lockout_mem + ); } // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L971 @@ -1565,181 +867,181 @@ is_commission_update_allowed( ulong slot, fd_epoch_schedule_t const * epoch_sche } } -// https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L916 +/* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L787-L818 */ static int -update_commission( fd_borrowed_account_t * vote_account, - uchar commission, - fd_pubkey_t const * signers[static FD_TXN_SIG_MAX], - fd_epoch_schedule_t const * epoch_schedule, - fd_sol_sysvar_clock_t const * clock, - fd_exec_instr_ctx_t const * ctx /* feature_set */ ) { - int rc = 0; - - // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L925 - fd_vote_state_versioned_t * vote_state_versioned = NULL; - fd_vote_state_t * vote_state = NULL; - - // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L927 - int enforce_commission_update_rule = 1; - rc = get_state( vote_account->meta, ctx->runtime->vote_program.update_commission.vote_state_mem ); - if( FD_LIKELY( rc==FD_EXECUTOR_INSTR_SUCCESS ) ) { - vote_state_versioned = (fd_vote_state_versioned_t *)ctx->runtime->vote_program.update_commission.vote_state_mem; - convert_to_current( vote_state_versioned, - ctx->runtime->vote_program.update_commission.authorized_voters_mem, - ctx->runtime->vote_program.update_commission.landed_votes_mem ); - vote_state = &vote_state_versioned->inner.current; - enforce_commission_update_rule = commission > vote_state->commission; - } - - // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L940 - if( FD_LIKELY( enforce_commission_update_rule ) ) { - if( FD_UNLIKELY( !is_commission_update_allowed( clock->slot, epoch_schedule ) ) ) { - ctx->txn_out->err.custom_err = FD_VOTE_ERR_COMMISSION_UPDATE_TOO_LATE; - return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR; - } +update_commission( fd_exec_instr_ctx_t * ctx, + int target_version, + fd_borrowed_account_t * vote_account, + uchar commission, + fd_pubkey_t const * signers[static FD_TXN_SIG_MAX], + fd_epoch_schedule_t const * epoch_schedule, + fd_sol_sysvar_clock_t const * clock ) { + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L796-L799 */ + int rc = 0; + int get_vsv_rc = get_vote_state_handler_checked( + vote_account, + target_version, + false, + ctx->runtime->vote_program.update_commission.vote_state_mem, + ctx->runtime->vote_program.update_commission.authorized_voters_mem, + ctx->runtime->vote_program.update_commission.landed_votes_mem + ); + + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L800-L804 */ + fd_vote_state_versioned_t * vote_state_versioned = NULL; + int enforce_commission_update_rule = 1; + if( FD_LIKELY( get_vsv_rc==FD_EXECUTOR_INSTR_SUCCESS ) ) { + vote_state_versioned = (fd_vote_state_versioned_t *)ctx->runtime->vote_program.update_commission.vote_state_mem; + enforce_commission_update_rule = (commission>fd_vsv_get_commission( vote_state_versioned )); + } + + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L806-L808 */ + if( FD_UNLIKELY( enforce_commission_update_rule && !is_commission_update_allowed( clock->slot, epoch_schedule ) ) ) { + ctx->txn_out->err.custom_err = FD_VOTE_ERR_COMMISSION_UPDATE_TOO_LATE; + return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR; } - // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L949 - if( !vote_state ) { - rc = get_state( vote_account->meta, ctx->runtime->vote_program.update_commission.vote_state_mem ); - if( FD_UNLIKELY( rc ) ) return rc; - vote_state_versioned = (fd_vote_state_versioned_t *)ctx->runtime->vote_program.update_commission.vote_state_mem; - convert_to_current( vote_state_versioned, - ctx->runtime->vote_program.update_commission.authorized_voters_mem, - ctx->runtime->vote_program.update_commission.landed_votes_mem ); - vote_state = &vote_state_versioned->inner.current; - } + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L810 */ + if( FD_UNLIKELY( get_vsv_rc ) ) return get_vsv_rc; - // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L957 - rc = verify_authorized_signer( &vote_state->authorized_withdrawer, signers ); + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L813 */ + rc = fd_vote_verify_authorized_signer( + fd_vsv_get_authorized_withdrawer( vote_state_versioned ), + signers + ); if( FD_UNLIKELY( rc ) ) return rc; - // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L959 - vote_state->commission = commission; + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L815 */ + fd_vsv_set_commission( vote_state_versioned, commission ); - // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L961 - return set_vote_account_state( vote_account, vote_state, ctx, ctx->runtime->vote_program.update_commission.vote_lockout_mem ); + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L817 */ + return fd_vsv_set_vote_account_state( + ctx, + vote_account, + vote_state_versioned, + ctx->runtime->vote_program.update_commission.vote_lockout_mem + ); } -// https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L997 +/* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L848C8-L903 */ static int -withdraw( fd_exec_instr_ctx_t const * ctx, +withdraw( fd_exec_instr_ctx_t * ctx, fd_borrowed_account_t * vote_account, + int target_version, ulong lamports, ushort to_account_index, fd_pubkey_t const * signers[static FD_TXN_SIG_MAX], fd_rent_t const * rent_sysvar, fd_sol_sysvar_clock_t const * clock ) { - int rc = 0; - - // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1010 - rc = get_state( vote_account->meta, ctx->runtime->vote_program.withdraw.vote_state_mem ); + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L860-L863 */ + int rc = get_vote_state_handler_checked( + vote_account, + target_version, + 0, + ctx->runtime->vote_program.withdraw.vote_state_mem, + ctx->runtime->vote_program.withdraw.authorized_voters_mem, + ctx->runtime->vote_program.withdraw.landed_votes_mem + ); if( FD_UNLIKELY( rc ) ) return rc; - fd_vote_state_versioned_t * vote_state_versioned = (fd_vote_state_versioned_t *)ctx->runtime->vote_program.withdraw.vote_state_mem; - convert_to_current( vote_state_versioned, - ctx->runtime->vote_program.withdraw.authorized_voters_mem, - ctx->runtime->vote_program.withdraw.landed_votes_mem ); - fd_vote_state_t * vote_state = &vote_state_versioned->inner.current; + fd_vote_state_versioned_t * versioned = (fd_vote_state_versioned_t *)ctx->runtime->vote_program.withdraw.vote_state_mem; - // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1014 - rc = verify_authorized_signer( &vote_state->authorized_withdrawer, signers ); + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L865 */ + rc = fd_vote_verify_authorized_signer( + fd_vsv_get_authorized_withdrawer( versioned ), + signers + ); if( FD_UNLIKELY( rc ) ) return rc; - // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1016 - if( FD_UNLIKELY( lamports > fd_borrowed_account_get_lamports( vote_account ) ) ) + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L867-L870 */ + ulong vote_account_lamports = fd_borrowed_account_get_lamports( vote_account ); + if( FD_UNLIKELY( lamports>vote_account_lamports ) ) { return FD_EXECUTOR_INSTR_ERR_INSUFFICIENT_FUNDS; - ulong remaining_balance = fd_borrowed_account_get_lamports( vote_account ) - lamports; - - // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1021 - if( FD_UNLIKELY( remaining_balance == 0 ) ) { - // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1014 - int reject_active_vote_account_close = 0; - - ulong last_epoch_with_credits; - if( FD_LIKELY( !deq_fd_vote_epoch_credits_t_empty( vote_state->epoch_credits ) ) ) { - last_epoch_with_credits = - deq_fd_vote_epoch_credits_t_peek_tail_const( vote_state->epoch_credits )->epoch; - ulong current_epoch = clock->epoch; - reject_active_vote_account_close = - fd_ulong_sat_sub( current_epoch, last_epoch_with_credits ) < 2; + } + ulong remaining_balance = vote_account_lamports-lamports; + + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L872-L896 */ + if( FD_UNLIKELY( remaining_balance==0UL ) ) { + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L873-L883 */ + fd_vote_epoch_credits_t const * epoch_credits = fd_vsv_get_epoch_credits( versioned ); + ulong last_epoch_with_credits; + int reject_active_vote_account_close = 0; + if( FD_LIKELY( !deq_fd_vote_epoch_credits_t_empty( epoch_credits ) ) ) { + ulong current_epoch = clock->epoch; + last_epoch_with_credits = deq_fd_vote_epoch_credits_t_peek_tail_const( epoch_credits )->epoch; + reject_active_vote_account_close = fd_ulong_sat_sub( current_epoch, last_epoch_with_credits )<2UL; } - // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1034 + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L885-L890 */ if( FD_UNLIKELY( reject_active_vote_account_close ) ) { - // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1036 ctx->txn_out->err.custom_err = FD_VOTE_ERR_ACTIVE_VOTE_ACCOUNT_CLOSE; return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR; } else { - // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1040 - fd_vote_state_versioned_t vote_state_versions; - fd_vote_state_versioned_new_disc( &vote_state_versions, - fd_vote_state_versioned_enum_current ); - vote_state_versions.inner.current.prior_voters.idx = 31; - vote_state_versions.inner.current.prior_voters.is_empty = 1; - fd_vote_state_t * default_vote_state = &vote_state_versions.inner.current; - rc = 0; - rc = set_vote_account_state( vote_account, default_vote_state, ctx, ctx->runtime->vote_program.withdraw.vote_lockout_mem ); - if( FD_UNLIKELY( rc != 0 ) ) return rc; + rc = fd_vsv_deinitialize_vote_account_state( + ctx, + vote_account, + target_version, + ctx->runtime->vote_program.withdraw.vote_lockout_mem + ); + if( FD_UNLIKELY( rc ) ) return rc; } } else { - // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1043 - ulong min_rent_exempt_balance = - fd_rent_exempt_minimum_balance( rent_sysvar, fd_borrowed_account_get_data_len( vote_account ) ); - if( remaining_balance < min_rent_exempt_balance ) { + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L892-L895 */ + ulong min_rent_exempt_balance = fd_rent_exempt_minimum_balance( rent_sysvar, fd_borrowed_account_get_data_len( vote_account ) ); + if( FD_UNLIKELY( remaining_balancehash, slot_hashes, ctx ); + rc = check_slots_are_valid( ctx, versioned, vote_slots, &vote->hash, slot_hashes ); if( FD_UNLIKELY( rc ) ) return rc; 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 ) ) { ulong * ele = deq_ulong_iter_ele( vote_slots, iter ); // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L772 - process_next_vote_slot( vote_state, *ele, epoch, current_slot ); + fd_vsv_process_next_vote_slot( versioned, *ele, epoch, current_slot ); } return 0; } // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L783 static int -process_vote( fd_vote_state_t * vote_state, +process_vote( fd_exec_instr_ctx_t * ctx, + fd_vote_state_versioned_t * versioned, fd_vote_t const * vote, fd_slot_hash_t const * slot_hashes, /* deque */ ulong epoch, - ulong current_slot, - fd_exec_instr_ctx_t const * ctx ) { + ulong current_slot ) { // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L792 if( FD_UNLIKELY( deq_ulong_empty( vote->slots ) ) ) { ctx->txn_out->err.custom_err = FD_VOTE_ERR_EMPTY_SLOTS; @@ -1789,163 +1091,136 @@ process_vote( fd_vote_state_t * vote_state, // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L805 return process_vote_unfiltered( - vote_state, vote_slots, vote, slot_hashes, epoch, current_slot, ctx ); + ctx, + versioned, + vote_slots, + vote, + slot_hashes, + epoch, + current_slot + ); } -// https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1060 +/* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L905-L926 */ static int -initialize_account( fd_borrowed_account_t * vote_account, +initialize_account( fd_exec_instr_ctx_t * ctx, + fd_borrowed_account_t * vote_account, + int target_version, fd_vote_init_t * vote_init, fd_pubkey_t const * signers[static FD_TXN_SIG_MAX], - fd_sol_sysvar_clock_t const * clock, - fd_exec_instr_ctx_t const * ctx /* feature_set */ ) { + fd_sol_sysvar_clock_t const * clock ) { int rc; - // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1067 - ulong data_len = fd_borrowed_account_get_data_len( vote_account ); - if( FD_UNLIKELY( data_len != size_of_versioned( 1 ) ) ) { - return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA; - } + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L915 */ + rc = check_vote_account_length( vote_account, target_version ); + if( FD_UNLIKELY( rc ) ) return rc; - // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1074 - rc = get_state( vote_account->meta, ctx->runtime->vote_program.init_account.vote_state_mem ); + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L916 */ + rc = fd_vsv_get_state( vote_account->meta, ctx->runtime->vote_program.init_account.vote_state_mem ); if( FD_UNLIKELY( rc ) ) return rc; fd_vote_state_versioned_t * versioned = (fd_vote_state_versioned_t *)ctx->runtime->vote_program.init_account.vote_state_mem; - // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1076 - if( FD_UNLIKELY( !is_uninitialized( versioned ) ) ) { + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L918-L920 */ + if( FD_UNLIKELY( !fd_vsv_is_uninitialized( versioned ) ) ) { return FD_EXECUTOR_INSTR_ERR_ACC_ALREADY_INITIALIZED; } - // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1081 - rc = verify_authorized_signer( &vote_init->node_pubkey, signers ); + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L923 */ + rc = fd_vote_verify_authorized_signer( &vote_init->node_pubkey, signers ); if( FD_UNLIKELY( rc ) ) { return rc; } - /* - * N.B. Technically we should destroy() to release memory before - * newing, otherwise the pointers are wiped and memory is leaked. - * We are probably fine for now since we are bump allocating - * everything and the enclosing frame will free everything when - * popped. - */ - // reset the object - fd_vote_state_versioned_new( versioned ); - - // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1083 - vote_state_new( vote_init, - clock, - ctx->runtime->vote_program.init_account.authorized_voters_mem, - &versioned->inner.current ); - return set_vote_account_state( vote_account, &versioned->inner.current, ctx, ctx->runtime->vote_program.init_account.vote_lockout_mem ); + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L925 */ + return init_vote_account_state( ctx, vote_account, versioned, target_version, vote_init, clock ); } -// https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1086 +/* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L928-L953 */ static int -verify_and_get_vote_state( fd_borrowed_account_t * vote_account, +process_vote_with_account( fd_exec_instr_ctx_t * ctx, + fd_borrowed_account_t * vote_account, + int target_version, + fd_slot_hash_t const * slot_hashes, /* deque */ fd_sol_sysvar_clock_t const * clock, - fd_pubkey_t const * signers[FD_TXN_SIG_MAX], - fd_vote_state_t * vote_state /* out */, - uchar * vote_state_mem, - uchar * authorized_voters_mem, - uchar * landed_votes_mem ) { - int rc = 0; - - // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1091 - rc = get_state( vote_account->meta, vote_state_mem ); + fd_vote_t * vote, + fd_pubkey_t const * signers[static FD_TXN_SIG_MAX] ) { + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L936-L939 */ + int rc = get_vote_state_handler_checked( + vote_account, + target_version, + 1, + ctx->runtime->vote_program.process_vote.vote_state_mem, + ctx->runtime->vote_program.process_vote.authorized_voters_mem, + ctx->runtime->vote_program.process_vote.landed_votes_mem + ); if( FD_UNLIKELY( rc ) ) return rc; - fd_vote_state_versioned_t * versioned = (fd_vote_state_versioned_t *)vote_state_mem; - - // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1093 - if( FD_UNLIKELY( is_uninitialized( versioned ) ) ) - return FD_EXECUTOR_INSTR_ERR_UNINITIALIZED_ACCOUNT; - // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1097 - convert_to_current( versioned, authorized_voters_mem, landed_votes_mem ); - *vote_state = versioned->inner.current; + fd_vote_state_versioned_t * versioned = (fd_vote_state_versioned_t *)ctx->runtime->vote_program.process_vote.vote_state_mem; - // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1098 + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L941 */ fd_pubkey_t * authorized_voter = NULL; - rc = get_and_update_authorized_voter( vote_state, clock->epoch, &authorized_voter ); + rc = fd_authorized_voters_get_and_update_authorized_voter( versioned, clock->epoch, &authorized_voter ); if( FD_UNLIKELY( rc ) ) return rc; - // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1099 - rc = verify_authorized_signer( authorized_voter, signers ); + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L942 */ + rc = fd_vote_verify_authorized_signer( authorized_voter, signers ); if( FD_UNLIKELY( rc ) ) return rc; - return FD_EXECUTOR_INSTR_SUCCESS; -} - -// https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1104 -static int -process_vote_with_account( fd_borrowed_account_t * vote_account, - fd_slot_hash_t const * slot_hashes, /* deque */ - fd_sol_sysvar_clock_t const * clock, - fd_vote_t * vote, - fd_pubkey_t const * signers[static FD_TXN_SIG_MAX], - fd_exec_instr_ctx_t const * ctx ) { - - int rc; - fd_vote_state_t vote_state; - // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1112 - rc = verify_and_get_vote_state( vote_account, - clock, - signers, - &vote_state, - ctx->runtime->vote_program.process_vote.vote_state_mem, - ctx->runtime->vote_program.process_vote.authorized_voters_mem, - ctx->runtime->vote_program.process_vote.landed_votes_mem ); + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L944 */ + rc = process_vote( ctx, versioned, vote, slot_hashes, clock->epoch, clock->slot ); if( FD_UNLIKELY( rc ) ) return rc; - - // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1117 - rc = process_vote( &vote_state, vote, slot_hashes, clock->epoch, clock->slot, ctx ); - if( FD_UNLIKELY( rc ) ) return rc; - - // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1126 + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L945-L951 */ if( FD_LIKELY( vote->has_timestamp ) ) { - if( FD_UNLIKELY( deq_ulong_cnt( vote->slots ) == 0 ) ) { + /* Calling max() on an empty iterator returns None */ + if( FD_UNLIKELY( deq_ulong_cnt( vote->slots )==0 ) ) { ctx->txn_out->err.custom_err = FD_VOTE_ERR_EMPTY_SLOTS; return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR; } - ulong max = deq_ulong_peek_head( vote->slots ) ? *deq_ulong_peek_head( vote->slots ) : 0UL; - // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1127 + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L948 */ + ulong max = 0UL; 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 ) ) { + !deq_ulong_iter_done( vote->slots, iter ); + iter = deq_ulong_iter_next( vote->slots, iter ) ) { ulong * ele = deq_ulong_iter_ele( vote->slots, iter ); max = fd_ulong_max( max, *ele ); } - if( FD_UNLIKELY( !max ) ) { - ctx->txn_out->err.custom_err = FD_VOTE_ERR_EMPTY_SLOTS; - return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR; - } - // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1131 - rc = process_timestamp( &vote_state, max, vote->timestamp, ctx ); + + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L950 */ + rc = fd_vsv_process_timestamp( ctx, versioned, max, vote->timestamp ); if( FD_UNLIKELY( rc ) ) return rc; } - // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1133 - return set_vote_account_state( vote_account, &vote_state, ctx, ctx->runtime->vote_program.process_vote.vote_lockout_mem ); + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L952 */ + return fd_vsv_set_vote_account_state( + ctx, + vote_account, + versioned, + ctx->runtime->vote_program.process_vote.vote_lockout_mem + ); } // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1156 static int -do_process_vote_state_update( fd_vote_state_t * vote_state, +do_process_vote_state_update( fd_exec_instr_ctx_t * ctx, + fd_vote_state_versioned_t * versioned, fd_slot_hash_t const * slot_hashes, /* deque */ ulong epoch, ulong slot, - fd_vote_state_update_t * vote_state_update, - fd_exec_instr_ctx_t const * ctx /* feature_set */ ) { + fd_vote_state_update_t * vote_state_update ) { int rc; // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1164 rc = check_and_filter_proposed_vote_state( - vote_state, - vote_state_update->lockouts, &vote_state_update->has_root, &vote_state_update->root, &vote_state_update->hash, - slot_hashes, ctx ); + ctx, + versioned, + vote_state_update->lockouts, + &vote_state_update->has_root, + &vote_state_update->root, + &vote_state_update->hash, + slot_hashes + ); if( FD_UNLIKELY( rc ) ) return rc; // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1177 @@ -1961,109 +1236,157 @@ do_process_vote_state_update( fd_vote_state_t * vote_state, } // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1171 - return process_new_vote_state( vote_state, - landed_votes, - vote_state_update->has_root, - vote_state_update->root, - vote_state_update->has_timestamp, - vote_state_update->timestamp, - epoch, - slot, - ctx ); + return process_new_vote_state( + ctx, + versioned, + landed_votes, + vote_state_update->has_root, + vote_state_update->root, + vote_state_update->has_timestamp, + vote_state_update->timestamp, + epoch, + slot + ); } +/* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L955-L979 */ static int -process_vote_state_update( fd_borrowed_account_t * vote_account, +process_vote_state_update( fd_exec_instr_ctx_t * ctx, + fd_borrowed_account_t * vote_account, + int target_version, fd_slot_hash_t const * slot_hashes, fd_sol_sysvar_clock_t const * clock, fd_vote_state_update_t * vote_state_update, - fd_pubkey_t const * signers[static FD_TXN_SIG_MAX], - fd_exec_instr_ctx_t const * ctx /* feature_set */ ) { - fd_vote_state_t vote_state; - // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1144 - int rc = verify_and_get_vote_state( vote_account, - clock, - signers, - &vote_state, - ctx->runtime->vote_program.process_vote.vote_state_mem, - ctx->runtime->vote_program.process_vote.authorized_voters_mem, - ctx->runtime->vote_program.process_vote.landed_votes_mem ); + fd_pubkey_t const * signers[static FD_TXN_SIG_MAX] ) { + + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L963-L966 */ + int rc = get_vote_state_handler_checked( + vote_account, + target_version, + 1, + ctx->runtime->vote_program.process_vote.vote_state_mem, + ctx->runtime->vote_program.process_vote.authorized_voters_mem, + ctx->runtime->vote_program.process_vote.landed_votes_mem + ); if( FD_UNLIKELY( rc ) ) return rc; + fd_vote_state_versioned_t * versioned = (fd_vote_state_versioned_t *)ctx->runtime->vote_program.process_vote.vote_state_mem; + + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L968 */ + fd_pubkey_t * authorized_voter = NULL; + rc = fd_authorized_voters_get_and_update_authorized_voter( versioned, clock->epoch, &authorized_voter ); + if( FD_UNLIKELY( rc ) ) return rc; + + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L969 */ + rc = fd_vote_verify_authorized_signer( authorized_voter, signers ); + if( FD_UNLIKELY( rc ) ) return rc; - // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1145 + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L971-L977 */ rc = do_process_vote_state_update( - &vote_state, slot_hashes, clock->epoch, clock->slot, vote_state_update, ctx ); + ctx, + versioned, + slot_hashes, + clock->epoch, + clock->slot, + vote_state_update + ); if( FD_UNLIKELY( rc ) ) { return rc; } - // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1153 - rc = set_vote_account_state( vote_account, &vote_state, ctx, ctx->runtime->vote_program.process_vote.vote_lockout_mem ); - - return rc; + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L978 */ + return fd_vsv_set_vote_account_state( + ctx, + vote_account, + versioned, + ctx->runtime->vote_program.process_vote.vote_lockout_mem + ); } -// https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1206 +/* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L1035-L1061 */ static int -do_process_tower_sync( fd_vote_state_t * vote_state, +do_process_tower_sync( fd_exec_instr_ctx_t * ctx, + fd_vote_state_versioned_t * versioned, fd_slot_hash_t const * slot_hashes, /* deque */ ulong epoch, ulong slot, - fd_tower_sync_t * tower_sync, - fd_exec_instr_ctx_t const * ctx /* feature_set */ ) { - - do { - // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1214 - int err = check_and_filter_proposed_vote_state( - 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_tower_sync_t * tower_sync ) { + + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L1042-L1048 */ + int rc = check_and_filter_proposed_vote_state( + ctx, + versioned, + tower_sync->lockouts, + &tower_sync->has_root, + &tower_sync->root, + &tower_sync->hash, + slot_hashes + ); + if( FD_UNLIKELY( rc ) ) return rc; - // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1221 + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L1049-L1060 */ return process_new_vote_state( - vote_state, - landed_votes_from_lockouts( tower_sync->lockouts, ctx->runtime->vote_program.tower_sync.tower_sync_landed_votes_mem ), + ctx, + versioned, + fd_vote_lockout_landed_votes_from_lockouts( tower_sync->lockouts, ctx->runtime->vote_program.tower_sync.tower_sync_landed_votes_mem ), tower_sync->has_root, tower_sync->root, tower_sync->has_timestamp, tower_sync->timestamp, epoch, - slot, - ctx ); + slot + ); } -// https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1186 +/* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L1009-L1033 */ static int -process_tower_sync( fd_borrowed_account_t * vote_account, +process_tower_sync( fd_exec_instr_ctx_t * ctx, + fd_borrowed_account_t * vote_account, + int target_version, fd_slot_hash_t const * slot_hashes, /* deque */ 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/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1194 - fd_vote_state_t vote_state; - do { - int err = verify_and_get_vote_state( vote_account, - clock, - signers, - &vote_state, - ctx->runtime->vote_program.tower_sync.vote_state_mem, - ctx->runtime->vote_program.tower_sync.authorized_voters_mem, - ctx->runtime->vote_program.tower_sync.vote_state_landed_votes_mem ); - if( FD_UNLIKELY( err ) ) return err; - } while(0); + fd_pubkey_t const * signers[static FD_TXN_SIG_MAX] ) { + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L1017-L1020 */ + int rc = get_vote_state_handler_checked( + vote_account, + target_version, + 1, + ctx->runtime->vote_program.tower_sync.vote_state_mem, + ctx->runtime->vote_program.tower_sync.authorized_voters_mem, + ctx->runtime->vote_program.tower_sync.vote_state_landed_votes_mem + ); + if( FD_UNLIKELY( rc ) ) return rc; - // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1195 - 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); + fd_vote_state_versioned_t * versioned = (fd_vote_state_versioned_t *)ctx->runtime->vote_program.tower_sync.vote_state_mem; + + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L1022 */ + fd_pubkey_t * authorized_voter = NULL; + rc = fd_authorized_voters_get_and_update_authorized_voter( versioned, clock->epoch, &authorized_voter ); + if( FD_UNLIKELY( rc ) ) return rc; - // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1203 - return set_vote_account_state( vote_account, &vote_state, ctx, ctx->runtime->vote_program.process_vote.vote_lockout_mem ); + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L1023 */ + rc = fd_vote_verify_authorized_signer( authorized_voter, signers ); + if( FD_UNLIKELY( rc ) ) return rc; + + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L1025-L1031 */ + rc = do_process_tower_sync( + ctx, + versioned, + slot_hashes, + clock->epoch, + clock->slot, + tower_sync + ); + if( FD_UNLIKELY( rc ) ) return rc; + + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L1032 */ + return fd_vsv_set_vote_account_state( + ctx, + vote_account, + versioned, + ctx->runtime->vote_program.tower_sync.vote_lockout_mem + ); } /**********************************************************************/ @@ -2115,21 +1438,20 @@ fd_vote_decode_compact_update( fd_compact_vote_state_update_t * compact_update, /* mod vote_processor */ /**********************************************************************/ -// https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L21 +/* https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L21-L51 */ static int -process_authorize_with_seed_instruction( - /* invoke_context */ - fd_exec_instr_ctx_t const * ctx, - /* transaction_context */ - fd_borrowed_account_t * vote_account, - fd_pubkey_t const * new_authority, - fd_vote_authorize_t authorization_type, - fd_pubkey_t const * current_authority_derived_key_owner, - uchar const * current_authority_derived_key_seed, - ulong current_authority_derived_key_seed_len ) { +process_authorize_with_seed_instruction( /* invoke_context */ + fd_exec_instr_ctx_t * ctx, + int target_version, + fd_borrowed_account_t * vote_account, + fd_pubkey_t const * new_authority, + fd_vote_authorize_t authorization_type, + fd_pubkey_t const * current_authority_derived_key_owner, + uchar const * current_authority_derived_key_seed, + ulong current_authority_derived_key_seed_len ) { int rc = 0; - // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L31 + /* https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L31 */ rc = fd_sysvar_instr_acct_check( ctx, 1, &fd_sysvar_clock_id ); if( FD_UNLIKELY( rc ) ) return rc; @@ -2140,18 +1462,15 @@ process_authorize_with_seed_instruction( fd_pubkey_t * expected_authority_keys[FD_TXN_SIG_MAX] = { 0 }; fd_pubkey_t single_signer = { 0 }; - if( ctx->instr->acct_cnt < 3 ) - return FD_EXECUTOR_INSTR_ERR_MISSING_ACC; - - // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L33 + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_processor.rs#L30-L42 */ if( fd_instr_acc_is_signer_idx( ctx->instr, 2, &rc ) ) { - // https://github.com/anza-xyz/agave/blob/v2.1.14/programs/vote/src/vote_processor.rs#L34 + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_processor.rs#L31 */ fd_pubkey_t const * base_pubkey = NULL; rc = fd_exec_instr_ctx_get_key_of_account_at_index( ctx, 2UL, &base_pubkey ); if( FD_UNLIKELY( rc ) ) return rc; - // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L37 + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_processor.rs#L34-L40 */ expected_authority_keys[0] = &single_signer; rc = fd_pubkey_create_with_seed( ctx, base_pubkey->uc, @@ -2162,13 +1481,16 @@ process_authorize_with_seed_instruction( if( FD_UNLIKELY( rc ) ) return rc; } - // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L43 - return authorize( vote_account, - new_authority, - authorization_type, - (fd_pubkey_t const **)expected_authority_keys, - clock, - ctx ); + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_processor.rs#L43-L50 */ + return authorize( + ctx, + vote_account, + target_version, + new_authority, + authorization_type, + (fd_pubkey_t const **)expected_authority_keys, + clock + ); } /**********************************************************************/ @@ -2184,33 +1506,18 @@ fd_vote_program_execute( fd_exec_instr_ctx_t * ctx ) { // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L57 FD_EXEC_CU_UPDATE( ctx, DEFAULT_COMPUTE_UNITS ); - // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L64 - if( FD_UNLIKELY( ctx->instr->acct_cnt < 1 ) ) { - return FD_EXECUTOR_INSTR_ERR_MISSING_ACC; - } - + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_processor.rs#L64-L67 */ fd_guarded_borrowed_account_t me = {0}; FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( ctx, 0, &me ); - - switch( rc ) { - case FD_ACC_MGR_SUCCESS: - break; - case FD_ACC_MGR_ERR_UNKNOWN_ACCOUNT: - // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/src/transaction_context.rs#L637 - return FD_EXECUTOR_INSTR_ERR_MISSING_ACC; - default: - // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/src/transaction_context.rs#L639 - return FD_EXECUTOR_INSTR_ERR_ACC_BORROW_FAILED; - } - - // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L65 if( FD_UNLIKELY( 0 != memcmp( fd_borrowed_account_get_owner( &me ), fd_solana_vote_program_id.key, sizeof( fd_pubkey_t ) ) ) ) { - // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L66 return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_OWNER; } + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_processor.rs#L69-L74 */ + int target_version = FD_FEATURE_ACTIVE_BANK( ctx->bank, vote_state_v4 ) ? VOTE_STATE_TARGET_VERSION_V4 : VOTE_STATE_TARGET_VERSION_V3; + // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L69 fd_pubkey_t const * signers[FD_TXN_SIG_MAX] = { 0 }; fd_exec_instr_ctx_get_signers( ctx, signers ); @@ -2259,11 +1566,7 @@ fd_vote_program_execute( fd_exec_instr_ctx_t * ctx ) { if( FD_UNLIKELY( !clock ) ) return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR; // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L78 - rc = initialize_account( &me, - &instruction->inner.initialize_account, - signers, - clock, - ctx ); + rc = initialize_account( ctx, &me, target_version, &instruction->inner.initialize_account, signers, clock ); break; } @@ -2291,7 +1594,7 @@ fd_vote_program_execute( fd_exec_instr_ctx_t * ctx ) { fd_pubkey_t const * voter_pubkey = &instruction->inner.authorize.pubkey; fd_vote_authorize_t vote_authorize = instruction->inner.authorize.vote_authorize; - rc = authorize( &me, voter_pubkey, vote_authorize, signers, clock, ctx ); + rc = authorize( ctx, &me, target_version, voter_pubkey, vote_authorize, signers, clock ); break; } @@ -2314,13 +1617,16 @@ fd_vote_program_execute( fd_exec_instr_ctx_t * ctx ) { // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L100 fd_vote_authorize_with_seed_args_t * args = &instruction->inner.authorize_with_seed; - rc = process_authorize_with_seed_instruction( ctx, - &me, - &args->new_authority, - args->authorization_type, - &args->current_authority_derived_key_owner, - args->current_authority_derived_key_seed, - args->current_authority_derived_key_seed_len ); + rc = process_authorize_with_seed_instruction( + ctx, + target_version, + &me, + &args->new_authority, + args->authorization_type, + &args->current_authority_derived_key_owner, + args->current_authority_derived_key_seed, + args->current_authority_derived_key_seed_len + ); break; } @@ -2358,13 +1664,15 @@ fd_vote_program_execute( fd_exec_instr_ctx_t * ctx ) { } // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L119 - rc = process_authorize_with_seed_instruction( ctx, - &me, - new_authority, - args->authorization_type, - &args->current_authority_derived_key_owner, - args->current_authority_derived_key_seed, - args->current_authority_derived_key_seed_len ); + rc = process_authorize_with_seed_instruction( + ctx, + target_version, + &me, + new_authority, + args->authorization_type, + &args->current_authority_derived_key_owner, + args->current_authority_derived_key_seed, + args->current_authority_derived_key_seed_len ); break; } @@ -2390,7 +1698,7 @@ fd_vote_program_execute( fd_exec_instr_ctx_t * ctx ) { if( FD_UNLIKELY( rc ) ) return rc; // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L135 - rc = update_validator_identity( &me, node_pubkey, signers, ctx ); + rc = update_validator_identity( ctx, target_version, &me, node_pubkey, signers ); break; } @@ -2413,12 +1721,15 @@ fd_vote_program_execute( fd_exec_instr_ctx_t * ctx ) { } // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L145 - rc = update_commission( &me, - instruction->inner.update_commission, - signers, - epoch_schedule, - clock, - ctx ); + rc = update_commission( + ctx, + target_version, + &me, + instruction->inner.update_commission, + signers, + epoch_schedule, + clock + ); break; } @@ -2472,7 +1783,15 @@ fd_vote_program_execute( fd_exec_instr_ctx_t * ctx ) { if( FD_UNLIKELY( !clock ) ) return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR; fd_slot_hash_t const * slot_hashes = fd_sysvar_cache_slot_hashes_join_const( ctx->sysvar_cache ); /* guaranteed to succeed */ - rc = process_vote_with_account( &me, slot_hashes, clock, vote, signers, ctx ); + rc = process_vote_with_account( + ctx, + &me, + target_version, + slot_hashes, + clock, + vote, + signers + ); fd_sysvar_cache_slot_hashes_leave_const( ctx->sysvar_cache, slot_hashes ); break; @@ -2526,7 +1845,15 @@ fd_vote_program_execute( fd_exec_instr_ctx_t * ctx ) { // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L173 fd_slot_hash_t const * slot_hashes = fd_sysvar_cache_slot_hashes_join_const( ctx->sysvar_cache ); - rc = process_vote_state_update( &me, slot_hashes, clock, vote_state_update, signers, ctx ); + rc = process_vote_state_update( + ctx, + &me, + target_version, + slot_hashes, + clock, + vote_state_update, + signers + ); fd_sysvar_cache_slot_hashes_leave_const( ctx->sysvar_cache, slot_hashes ); break; @@ -2588,7 +1915,15 @@ fd_vote_program_execute( fd_exec_instr_ctx_t * ctx ) { // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L187 fd_slot_hash_t const * slot_hashes = fd_sysvar_cache_slot_hashes_join_const( ctx->sysvar_cache ); /* guaranteed to succeed */ - rc = process_vote_state_update( &me, slot_hashes, clock, &vote_update, signers, ctx ); + rc = process_vote_state_update( + ctx, + &me, + target_version, + slot_hashes, + clock, + &vote_update, + signers + ); fd_sysvar_cache_slot_hashes_leave_const( ctx->sysvar_cache, slot_hashes ); break; @@ -2621,7 +1956,15 @@ fd_vote_program_execute( fd_exec_instr_ctx_t * ctx ) { fd_slot_hash_t const * slot_hashes = fd_sysvar_cache_slot_hashes_join_const( ctx->sysvar_cache ); FD_TEST( slot_hashes ); - rc = process_tower_sync( &me, slot_hashes, clock, tower_sync, signers, ctx ); + rc = process_tower_sync( + ctx, + &me, + target_version, + slot_hashes, + clock, + tower_sync, + signers + ); fd_sysvar_cache_slot_hashes_leave_const( ctx->sysvar_cache, slot_hashes ); break; @@ -2649,13 +1992,16 @@ fd_vote_program_execute( fd_exec_instr_ctx_t * ctx ) { if( FD_UNLIKELY( !clock_sysvar ) ) return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR; - rc = withdraw( ctx, - &me, - instruction->inner.withdraw, - 1UL, - signers, - rent_sysvar, - clock_sysvar ); + rc = withdraw( + ctx, + &me, + target_version, + instruction->inner.withdraw, + 1UL, + signers, + rent_sysvar, + clock_sysvar + ); break; } @@ -2699,12 +2045,15 @@ fd_vote_program_execute( fd_exec_instr_ctx_t * ctx ) { fd_sol_sysvar_clock_t const * clock = fd_sysvar_cache_clock_read( ctx->sysvar_cache, &clock_ ); if( FD_UNLIKELY( !clock ) ) return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR; - rc = authorize( &me, - voter_pubkey, - instruction->inner.authorize_checked, - signers, - clock, - ctx ); + rc = authorize( + ctx, + &me, + target_version, + voter_pubkey, + instruction->inner.authorize_checked, + signers, + clock + ); break; } @@ -2719,37 +2068,17 @@ fd_vote_program_execute( fd_exec_instr_ctx_t * ctx ) { /* Public API */ /**********************************************************************/ -uint -fd_vote_state_versions_is_correct_and_initialized( fd_account_meta_t const * meta ) { - // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L885 - uint data_len_check = meta->dlen==FD_VOTE_STATE_V3_SZ; - uchar test_data[DEFAULT_PRIOR_VOTERS_OFFSET] = {0}; - uint data_check = memcmp(( - fd_account_data( meta )+VERSION_OFFSET), test_data, DEFAULT_PRIOR_VOTERS_OFFSET)!=0; - if (data_check && data_len_check) { - return 1; - } - - // VoteState1_14_11::is_correct_size_and_initialized - // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/vote_state_1_14_11.rs#L58 - data_len_check = meta->dlen==FD_VOTE_STATE_V2_SZ; - uchar test_data_1_14_11[DEFAULT_PRIOR_VOTERS_OFFSET_1_14_11] = {0}; - data_check = memcmp( ( - fd_account_data( meta )+VERSION_OFFSET), test_data_1_14_11, DEFAULT_PRIOR_VOTERS_OFFSET_1_14_11)!=0; - return data_check && data_len_check; -} - -fd_vote_state_versioned_t * -fd_vote_get_state( fd_account_meta_t const * meta, - uchar * mem /* out */ ) { - - int err = get_state( meta, mem ); - return err ? NULL : (fd_vote_state_versioned_t *)mem; -} - +/* TODO: Old code, remove whenever stake program gets cleaned up */ void fd_vote_convert_to_current( fd_vote_state_versioned_t * self, uchar * authorized_voters_mem, uchar * landed_votes_mem ) { - convert_to_current( self, authorized_voters_mem, landed_votes_mem ); + fd_vsv_try_convert_to_v3( self, authorized_voters_mem, landed_votes_mem ); +} + +fd_vote_state_versioned_t * +fd_vote_get_state( fd_account_meta_t const * self, + uchar * mem ) { + int err = fd_vsv_get_state( self, mem ); + return err ? NULL : (fd_vote_state_versioned_t *)mem; } diff --git a/src/flamenco/runtime/program/fd_vote_program.h b/src/flamenco/runtime/program/fd_vote_program.h index f062ddc8ac7..cd33a93b2b8 100644 --- a/src/flamenco/runtime/program/fd_vote_program.h +++ b/src/flamenco/runtime/program/fd_vote_program.h @@ -12,6 +12,87 @@ #include "../context/fd_exec_instr_ctx.h" #include "../fd_bank.h" +/* Some vote instruction types are dynamically sized: + - tower_sync_switch (contains deque of fd_vote_lockout_t) + - tower_sync (contains deque of fd_vote_lockout_t) + - compact_vote_state_update_switch (vector of fd_lockout_offset_t) + - compact_vote_state_update (vector of fd_lockout_offset_t) + - authorize_checked_with_seed (char vector of current_authority_derived_key_seed) + - authorize_with_seed (char vector of current_authority_derived_key_seed) + - update_vote_state_switch (contains deque of fd_vote_lockout_t) + - update_vote_state (contains deque of fd_vote_lockout_t) + - vote_switch (deque of slot numbers) + - vote (deque of slot numbers) + All other vote instruction types are statically sized. + + A loose bound on the max amount of encoded fd_vote_lockout_t + possible is 1232 bytes/(12 bytes/per lockout) = 102 lockouts. So + the worst case bound for the deque of fd_vote_lockout is + 32 + (102 * sizeof(fd_vote_lockout_t)) = 1644 bytes. + + The worst case vector of fd_lockout_offset_t is one where each + encoded element is 2 bytes. This means that we can have 1232/2 = + 616 elements. They are represented as being 16 bytes each, so the + total footprint would be 9856 bytes. + + The deque of slot numbers is a vector of ulong, which is 8 bytes. + So the worst case is 1232 bytes/8 bytes = 154 elements. So, the + total footprint is 32 + (154 * 8 bytes) = 1264 bytes. + + The worst case char vector is 1232 bytes as each element is 1 byte + up to the txn MTU. + + With this, that means that the compact_vote_state_update_switch + can have the largest worst case footprint where the struct is + 104 bytes (sizeof(fd_compact_vote_state_update_switch_t) + the + worst case lockout vector of 616 elements. */ +#define FD_LOCKOUT_OFFSET_FOOTPRINT (9856UL) +#define FD_VOTE_INSTRUCTION_FOOTPRINT (sizeof(fd_vote_instruction_t) + FD_LOCKOUT_OFFSET_FOOTPRINT) + +/* TODO: This is the value as generated by fd_types bincode decoding of + fd_vote_state_versioned_t. This should eventually be replaced. */ +#define FD_VOTE_STATE_VERSIONED_FOOTPRINT (9248UL) + +/* The footprint of a fd_vote_authorized_voters_t struct is defined as a + fd_vote_authorized_voters_t followed by a pool and then a treap. */ +#define FD_AUTHORIZED_VOTERS_ALIGN (128UL) +#define FD_AUTHORIZED_VOTERS_FOOTPRINT (4888UL) + +/* TODO: These footprints are currently overprovisioned due to test + fixtures which currently violate protocol invariants. */ + +/* The footprint of the landed votes is determined by a deque with max + cnt of 31. The footprint is as follows: + alignof(DEQUE_T) == alignof(fd_landed_vote_t) == 8 + sizeof(DEQUE_T) == sizeof(fd_landed_vote_t) == 24 + return fd_ulong_align_up( fd_ulong_align_up( 32UL, alignof(DEQUE_T) ) + sizeof(DEQUE_T)*max, alignof(DEQUE_(private_t)) ); + return fd_ulong_align_up( fd_ulong_align_up( 32UL, 8UL ) ) + 24UL*31UL, 8UL ); + return fd_ulong_align_up( 32UL + 744, 8UL ) == 776 */ +#define FD_LANDED_VOTES_ALIGN (32UL) +#define FD_LANDED_VOTES_FOOTPRINT (FD_VOTE_STATE_VERSIONED_FOOTPRINT) + +/* The calculation for the landed votes footprint is the same as the + calculation for the landed votes but the sizeof(fd_vote_lockout_t) + is 16 bytes: + return fd_ulong_align_up( 32UL + 16UL * 31UL, 8UL ) == 528UL */ +#define FD_VOTE_LOCKOUTS_ALIGN (32UL) +#define FD_VOTE_LOCKOUTS_FOOTPRINT (FD_VOTE_STATE_VERSIONED_FOOTPRINT) + +// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L35 +#define MAX_LOCKOUT_HISTORY 31UL + +// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L36 +#define MAX_EPOCH_CREDITS_HISTORY 64UL + +// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L48 +#define VOTE_CREDITS_MAXIMUM_PER_SLOT 16 + +// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L45 +#define VOTE_CREDITS_GRACE_SLOTS 2 + +/* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L597-L598 */ +#define DEFAULT_BLOCK_REVENUE_COMMISSION_BPS (10000UL) + /* Vote program custom error codes */ #define FD_VOTE_ERR_VOTE_TOO_OLD ( 0) @@ -37,6 +118,12 @@ #define FD_VOTE_STATE_V2_SZ (3731UL) #define FD_VOTE_STATE_V3_SZ (3762UL) +#define FD_VOTE_STATE_V4_SZ (3762UL) + +/* Target vote state versions + https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L639-L645 */ +#define VOTE_STATE_TARGET_VERSION_V3 (0) +#define VOTE_STATE_TARGET_VERSION_V4 (1) FD_PROTOTYPES_BEGIN @@ -46,10 +133,6 @@ FD_PROTOTYPES_BEGIN int fd_vote_program_execute( fd_exec_instr_ctx_t * ctx ); -/* https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/vote_state_versions.rs#L90 */ -uint -fd_vote_state_versions_is_correct_and_initialized( fd_account_meta_t const * meta ); - /* An implementation of solana_sdk::transaction_context::BorrowedAccount::get_state for setting the vote state. diff --git a/src/flamenco/runtime/program/vote/Local.mk b/src/flamenco/runtime/program/vote/Local.mk new file mode 100644 index 00000000000..7a1acf88bf8 --- /dev/null +++ b/src/flamenco/runtime/program/vote/Local.mk @@ -0,0 +1,17 @@ +$(call add-hdrs,fd_authorized_voters.h) +$(call add-objs,fd_authorized_voters,fd_flamenco) + +$(call add-hdrs,fd_vote_common.h) +$(call add-objs,fd_vote_common,fd_flamenco) + +$(call add-hdrs,fd_vote_lockout.h) +$(call add-objs,fd_vote_lockout,fd_flamenco) + +$(call add-hdrs,fd_vote_state_versioned.h) +$(call add-objs,fd_vote_state_versioned,fd_flamenco) + +$(call add-hdrs,fd_vote_state_v3.h) +$(call add-objs,fd_vote_state_v3,fd_flamenco) + +$(call add-hdrs,fd_vote_state_v4.h) +$(call add-objs,fd_vote_state_v4,fd_flamenco) diff --git a/src/flamenco/runtime/program/vote/fd_authorized_voters.c b/src/flamenco/runtime/program/vote/fd_authorized_voters.c new file mode 100644 index 00000000000..19ee6af31f5 --- /dev/null +++ b/src/flamenco/runtime/program/vote/fd_authorized_voters.c @@ -0,0 +1,164 @@ +#include "fd_authorized_voters.h" +#include "fd_vote_state_v3.h" +#include "fd_vote_state_v4.h" + +/**********************************************************************/ +/* impl AuthorizedVoters */ +/**********************************************************************/ + +// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/authorized_voters.rs#L17 +fd_vote_authorized_voters_t * +fd_authorized_voters_new( ulong epoch, + fd_pubkey_t const * pubkey, + uchar * mem ) { + + FD_SCRATCH_ALLOC_INIT( l, mem ); + fd_vote_authorized_voters_t * authorized_voters = FD_SCRATCH_ALLOC_APPEND( l, fd_vote_authorized_voters_align(), sizeof(fd_vote_authorized_voters_t) ); + void * pool_mem = FD_SCRATCH_ALLOC_APPEND( l, fd_vote_authorized_voters_pool_align(), fd_vote_authorized_voters_pool_footprint( FD_VOTE_AUTHORIZED_VOTERS_MIN ) ); + void * treap_mem = FD_SCRATCH_ALLOC_APPEND( l, fd_vote_authorized_voters_treap_align(), fd_vote_authorized_voters_treap_footprint( FD_VOTE_AUTHORIZED_VOTERS_MIN ) ); + + authorized_voters->pool = fd_vote_authorized_voters_pool_join( fd_vote_authorized_voters_pool_new( pool_mem, FD_VOTE_AUTHORIZED_VOTERS_MIN ) ); + authorized_voters->treap = fd_vote_authorized_voters_treap_join( fd_vote_authorized_voters_treap_new( treap_mem, FD_VOTE_AUTHORIZED_VOTERS_MIN ) ); + if( 0 == fd_vote_authorized_voters_pool_free( authorized_voters->pool ) ) { + FD_LOG_ERR(( "Authorized_voter pool is empty" )); + } + fd_vote_authorized_voter_t * ele = fd_vote_authorized_voters_pool_ele_acquire( authorized_voters->pool ); + ele->epoch = epoch; + ele->pubkey = *pubkey; + ele->prio = (ulong)&ele->pubkey; + fd_vote_authorized_voters_treap_ele_insert( authorized_voters->treap, ele, authorized_voters->pool ); + return authorized_voters; +} + +// Helper to create an empty AuthorizedVoters structure (for default/uninitialized states) +fd_vote_authorized_voters_t * +fd_authorized_voters_new_empty( uchar * mem ) { + FD_SCRATCH_ALLOC_INIT( l, mem ); + fd_vote_authorized_voters_t * authorized_voters = FD_SCRATCH_ALLOC_APPEND( l, fd_vote_authorized_voters_align(), sizeof(fd_vote_authorized_voters_t) ); + void * pool_mem = FD_SCRATCH_ALLOC_APPEND( l, fd_vote_authorized_voters_pool_align(), fd_vote_authorized_voters_pool_footprint( FD_VOTE_AUTHORIZED_VOTERS_MIN ) ); + void * treap_mem = FD_SCRATCH_ALLOC_APPEND( l, fd_vote_authorized_voters_treap_align(), fd_vote_authorized_voters_treap_footprint( FD_VOTE_AUTHORIZED_VOTERS_MIN ) ); + + authorized_voters->pool = fd_vote_authorized_voters_pool_join( fd_vote_authorized_voters_pool_new( pool_mem, FD_VOTE_AUTHORIZED_VOTERS_MIN ) ); + authorized_voters->treap = fd_vote_authorized_voters_treap_join( fd_vote_authorized_voters_treap_new( treap_mem, FD_VOTE_AUTHORIZED_VOTERS_MIN ) ); + return authorized_voters; +} + +int +fd_authorized_voters_is_empty( fd_vote_authorized_voters_t * self ) { + return fd_vote_authorized_voters_treap_ele_cnt( self->treap ) == 0; +} + +// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/authorized_voters.rs#L80 +int +fd_authorized_voters_contains( fd_vote_authorized_voters_t * self, ulong epoch ) { + return !!fd_vote_authorized_voters_treap_ele_query( self->treap, epoch, self->pool ); +} + +// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/authorized_voters.rs#L72 +fd_vote_authorized_voter_t * +fd_authorized_voters_last( fd_vote_authorized_voters_t * self ) { + fd_vote_authorized_voters_treap_rev_iter_t iter = + fd_vote_authorized_voters_treap_rev_iter_init( self->treap, self->pool ); + return fd_vote_authorized_voters_treap_rev_iter_ele( iter, self->pool ); +} + +// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/authorized_voters.rs#L43 +void +fd_authorized_voters_purge_authorized_voters( fd_vote_authorized_voters_t * self, + ulong current_epoch ) { + + // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/authorized_voters.rs#L46 + ulong expired_keys[ FD_VOTE_AUTHORIZED_VOTERS_MIN ]; + 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 ); + !fd_vote_authorized_voters_treap_fwd_iter_done( iter ); + iter = fd_vote_authorized_voters_treap_fwd_iter_next( iter, self->pool ) ) { + fd_vote_authorized_voter_t * ele = + fd_vote_authorized_voters_treap_fwd_iter_ele( iter, self->pool ); + if( ele->epoch < current_epoch ) expired_keys[key_cnt++] = ele->epoch; + } + + // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/authorized_voters.rs#L52 + for( ulong i = 0; i < key_cnt; i++ ) { + fd_vote_authorized_voter_t * ele = + fd_vote_authorized_voters_treap_ele_query( self->treap, expired_keys[i], self->pool ); + fd_vote_authorized_voters_treap_ele_remove( self->treap, ele, self->pool ); + fd_vote_authorized_voters_pool_ele_release( self->pool, ele ); + } + + // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/authorized_voters.rs#L60 + FD_TEST( !fd_authorized_voters_is_empty( self ) ); + +} + +// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/authorized_voters.rs#L91 +fd_vote_authorized_voter_t * +fd_authorized_voters_get_or_calculate_authorized_voter_for_epoch( fd_vote_authorized_voters_t * self, + ulong epoch, + int * existed ) { + *existed = 0; + ulong latest_epoch = 0; + fd_vote_authorized_voter_t * res = + fd_vote_authorized_voters_treap_ele_query( self->treap, epoch, self->pool ); + // "predecessor" would be more big-O optimal here, but mirroring labs logic + // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/authorized_voters.rs#L93 + if( FD_UNLIKELY( !res ) ) { + for( fd_vote_authorized_voters_treap_fwd_iter_t iter = + fd_vote_authorized_voters_treap_fwd_iter_init( self->treap, self->pool ); + !fd_vote_authorized_voters_treap_fwd_iter_done( iter ); + iter = fd_vote_authorized_voters_treap_fwd_iter_next( iter, self->pool ) ) { + fd_vote_authorized_voter_t * ele = + fd_vote_authorized_voters_treap_fwd_iter_ele( iter, self->pool ); + if( ele->epoch < epoch && ( latest_epoch == 0 || ele->epoch > latest_epoch ) ) { + latest_epoch = ele->epoch; + res = ele; + } + } + *existed = 0; + return res; + } else { + *existed = 1; + return res; + } + return res; +} + +// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/authorized_voters.rs#L28 +fd_vote_authorized_voter_t * +fd_authorized_voters_get_and_cache_authorized_voter_for_epoch( fd_vote_authorized_voters_t * self, + ulong epoch ) { + int existed = 0; + // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/authorized_voters.rs#L29 + fd_vote_authorized_voter_t * res = + fd_authorized_voters_get_or_calculate_authorized_voter_for_epoch( self, epoch, &existed ); + if( !res ) return NULL; + // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/authorized_voters.rs#L32 + if( !existed ) { + /* insert cannot fail because !existed */ + if( 0 == fd_vote_authorized_voters_pool_free( self->pool) ) { + FD_LOG_ERR(( "Authorized_voter pool is empty" )); + } + fd_vote_authorized_voter_t * ele = fd_vote_authorized_voters_pool_ele_acquire( self->pool ); + ele->epoch = epoch; + ele->pubkey = res->pubkey; + ele->prio = (ulong)&res->pubkey; + // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/authorized_voters.rs#L33 + fd_vote_authorized_voters_treap_ele_insert( self->treap, ele, self->pool ); + } + return res; +} + +int +fd_authorized_voters_get_and_update_authorized_voter( fd_vote_state_versioned_t * self, + ulong current_epoch, + fd_pubkey_t ** pubkey /* out */ ) { + switch( self->discriminant ) { + case fd_vote_state_versioned_enum_v3: + return fd_vote_state_v3_get_and_update_authorized_voter( &self->inner.v3, current_epoch, pubkey ); + case fd_vote_state_versioned_enum_v4: + return fd_vote_state_v4_get_and_update_authorized_voter( &self->inner.v4, current_epoch, pubkey ); + default: + __builtin_unreachable(); + } +} diff --git a/src/flamenco/runtime/program/vote/fd_authorized_voters.h b/src/flamenco/runtime/program/vote/fd_authorized_voters.h new file mode 100644 index 00000000000..47e72577d3d --- /dev/null +++ b/src/flamenco/runtime/program/vote/fd_authorized_voters.h @@ -0,0 +1,51 @@ +#ifndef HEADER_fd_src_flamenco_runtime_program_vote_fd_authorized_voters_h +#define HEADER_fd_src_flamenco_runtime_program_vote_fd_authorized_voters_h + +#include "../../../types/fd_types.h" +#include "../../../../util/fd_util_base.h" + +// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/authorized_voters.rs#L17 +fd_vote_authorized_voters_t * +fd_authorized_voters_new( ulong epoch, + fd_pubkey_t const * pubkey, + uchar * mem ); + +// Helper to create an empty AuthorizedVoters structure (for default/uninitialized states) +fd_vote_authorized_voters_t * +fd_authorized_voters_new_empty( uchar * mem ); + +int +fd_authorized_voters_is_empty( fd_vote_authorized_voters_t * self ); + +// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/authorized_voters.rs#L80 +int +fd_authorized_voters_contains( fd_vote_authorized_voters_t * self, ulong epoch ); + +// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/authorized_voters.rs#L72 +fd_vote_authorized_voter_t * +fd_authorized_voters_last( fd_vote_authorized_voters_t * self ); + +// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/authorized_voters.rs#L43 +void +fd_authorized_voters_purge_authorized_voters( fd_vote_authorized_voters_t * self, + ulong current_epoch ); + +// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/authorized_voters.rs#L91 +fd_vote_authorized_voter_t * +fd_authorized_voters_get_or_calculate_authorized_voter_for_epoch( fd_vote_authorized_voters_t * self, + ulong epoch, + int * existed ); + +// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/authorized_voters.rs#L28 +fd_vote_authorized_voter_t * +fd_authorized_voters_get_and_cache_authorized_voter_for_epoch( fd_vote_authorized_voters_t * self, + ulong epoch ); + +/* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L707-L715 */ +int +fd_authorized_voters_get_and_update_authorized_voter( fd_vote_state_versioned_t * self, + ulong current_epoch, + fd_pubkey_t ** pubkey /* out */ ); + +#endif /* HEADER_fd_src_flamenco_runtime_program_vote_fd_authorized_voters_h */ + diff --git a/src/flamenco/runtime/program/vote/fd_vote_common.c b/src/flamenco/runtime/program/vote/fd_vote_common.c new file mode 100644 index 00000000000..654a424719e --- /dev/null +++ b/src/flamenco/runtime/program/vote/fd_vote_common.c @@ -0,0 +1,78 @@ +#include "fd_vote_common.h" +#include "../fd_vote_program.h" +#include "../fd_program_util.h" + +int +fd_vote_verify_authorized_signer( fd_pubkey_t const * authorized, + fd_pubkey_t const * signers[static FD_TXN_SIG_MAX] ) { + // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L989 + return fd_signers_contains( signers, authorized ) ? + FD_EXECUTOR_INSTR_SUCCESS : + FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE; +} + +int +fd_vote_signature_verify( fd_pubkey_t * epoch_authorized_voter, + int authorized_withdrawer_signer, + fd_pubkey_t const * signers[static FD_TXN_SIG_MAX] ) { + return authorized_withdrawer_signer ? 0 : fd_vote_verify_authorized_signer( epoch_authorized_voter, signers ); +} + +// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L668 +uchar +fd_vote_compute_vote_latency( ulong voted_for_slot, ulong current_slot ) { + return (uchar)fd_ulong_min( fd_ulong_sat_sub( current_slot, voted_for_slot ), UCHAR_MAX ); +} + +ulong +fd_vote_credits_for_vote_at_index( fd_landed_vote_t const * votes, + ulong index ) { + // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L679 + fd_landed_vote_t const * landed_vote = deq_fd_landed_vote_t_peek_index_const( votes, index ); + ulong latency = landed_vote == NULL ? 0 : landed_vote->latency; + // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L683 + ulong max_credits = VOTE_CREDITS_MAXIMUM_PER_SLOT; + + // If latency is 0, this means that the Lockout was created and stored from a software version + // that did not store vote latencies; in this case, 1 credit is awarded + // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L691 + if( FD_UNLIKELY( latency == 0 ) ) { + return 1; + } + + ulong diff = 0; + int cf = fd_ulong_checked_sub( latency, VOTE_CREDITS_GRACE_SLOTS, &diff ); + if( cf != 0 || diff == 0 ) { + // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L697 + return max_credits; + } + + ulong credits = 0; + cf = fd_ulong_checked_sub( max_credits, diff, &credits ); + if( cf != 0 || credits == 0 ) { + // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L705 + return 1; + } + // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L707 + return credits; +} + +uchar +fd_vote_contains_slot( fd_landed_vote_t const * votes, + ulong slot ) { + /* Logic is copied from slice::binary_search_by() in Rust. While not fully optimized, + it aims to achieve fuzzing conformance for both sorted and unsorted inputs. */ + ulong size = deq_fd_landed_vote_t_cnt( votes ); + if( FD_UNLIKELY( size==0UL ) ) return 0; + + ulong base = 0UL; + while( size>1UL ) { + ulong half = size / 2UL; + ulong mid = base + half; + ulong mid_slot = deq_fd_landed_vote_t_peek_index_const( votes, mid )->lockout.slot; + base = (slotlockout.slot==slot; +} diff --git a/src/flamenco/runtime/program/vote/fd_vote_common.h b/src/flamenco/runtime/program/vote/fd_vote_common.h new file mode 100644 index 00000000000..3145b141133 --- /dev/null +++ b/src/flamenco/runtime/program/vote/fd_vote_common.h @@ -0,0 +1,32 @@ +#ifndef HEADER_fd_src_flamenco_runtime_program_vote_fd_vote_common_h +#define HEADER_fd_src_flamenco_runtime_program_vote_fd_vote_common_h + +#include "../../../types/fd_types.h" +#include "../../fd_executor.h" + +// https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L985 +int +fd_vote_verify_authorized_signer( fd_pubkey_t const * authorized, + fd_pubkey_t const * signers[static FD_TXN_SIG_MAX] ); + +// lambda function: https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L873 +int +fd_vote_signature_verify( fd_pubkey_t * epoch_authorized_voter, + int authorized_withdrawer_signer, + fd_pubkey_t const * signers[static FD_TXN_SIG_MAX] ); + +uchar +fd_vote_compute_vote_latency( ulong voted_for_slot, ulong current_slot ); + +// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L673 +ulong +fd_vote_credits_for_vote_at_index( fd_landed_vote_t const * votes, + ulong index ); + +/* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L773-L778 */ +uchar +fd_vote_contains_slot( fd_landed_vote_t const * votes, + ulong slot ); + +#endif /* HEADER_fd_src_flamenco_runtime_program_vote_fd_vote_common_h */ + diff --git a/src/flamenco/runtime/program/vote/fd_vote_lockout.c b/src/flamenco/runtime/program/vote/fd_vote_lockout.c new file mode 100644 index 00000000000..34ac47d7b28 --- /dev/null +++ b/src/flamenco/runtime/program/vote/fd_vote_lockout.c @@ -0,0 +1,61 @@ +#include "fd_vote_lockout.h" +#include "../fd_vote_program.h" + +// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L104 +ulong +fd_vote_lockout_get_lockout( fd_vote_lockout_t * self ) { + /* Confirmation count can never be greater than MAX_LOCKOUT_HISTORY, preventing overflow. + Although Agave does not consider overflow, we do for fuzzing conformance. */ + ulong confirmation_count = fd_ulong_min( self->confirmation_count, MAX_LOCKOUT_HISTORY ); + return 1UL<slot, fd_vote_lockout_get_lockout( self ) ); +} + +// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L114 +ulong +fd_vote_lockout_is_locked_out_at_slot( fd_vote_lockout_t * self, ulong slot ) { + return fd_vote_lockout_last_locked_out_slot( self ) >= slot; +} + +// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L122 +void +fd_vote_lockout_increase_confirmation_count( fd_vote_lockout_t * self, uint by ) { + self->confirmation_count = fd_uint_sat_add( self->confirmation_count, by ); +} + +fd_landed_vote_t * +fd_vote_lockout_landed_votes_from_lockouts( fd_vote_lockout_t * lockouts, + uchar * mem ) { + if( !lockouts ) return NULL; + + /* 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_join( deq_fd_landed_vote_t_new( mem, cnt ) ); + if( FD_UNLIKELY( !landed_votes ) ) { + FD_LOG_CRIT(( "failed to join landed votes" )); + } + + 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 ); + iter = deq_fd_vote_lockout_t_iter_next( lockouts, iter ) ) { + fd_vote_lockout_t const * ele = deq_fd_vote_lockout_t_iter_ele_const( lockouts, iter ); + + fd_landed_vote_t * elem = deq_fd_landed_vote_t_push_tail_nocopy( landed_votes ); + fd_landed_vote_new( elem ); + + elem->latency = 0; + elem->lockout.slot = ele->slot; + elem->lockout.confirmation_count = ele->confirmation_count; + } + + return landed_votes; +} diff --git a/src/flamenco/runtime/program/vote/fd_vote_lockout.h b/src/flamenco/runtime/program/vote/fd_vote_lockout.h new file mode 100644 index 00000000000..61a1bcbd01e --- /dev/null +++ b/src/flamenco/runtime/program/vote/fd_vote_lockout.h @@ -0,0 +1,29 @@ +#ifndef HEADER_fd_src_flamenco_runtime_program_vote_fd_vote_lockout_h +#define HEADER_fd_src_flamenco_runtime_program_vote_fd_vote_lockout_h + +#include "../../../types/fd_types.h" +#include "../../../../util/fd_util_base.h" + +// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L104 +ulong +fd_vote_lockout_get_lockout( fd_vote_lockout_t * self ); + +// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L110 +ulong +fd_vote_lockout_last_locked_out_slot( fd_vote_lockout_t * self ); + +// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L114 +ulong +fd_vote_lockout_is_locked_out_at_slot( fd_vote_lockout_t * self, ulong slot ); + +// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L122 +void +fd_vote_lockout_increase_confirmation_count( fd_vote_lockout_t * self, uint by ); + +/* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L1009-L1011 */ +fd_landed_vote_t * +fd_vote_lockout_landed_votes_from_lockouts( fd_vote_lockout_t * lockouts, + uchar * mem ); + +#endif /* HEADER_fd_src_flamenco_runtime_program_vote_fd_vote_lockout_h */ + diff --git a/src/flamenco/runtime/program/vote/fd_vote_state_v3.c b/src/flamenco/runtime/program/vote/fd_vote_state_v3.c new file mode 100644 index 00000000000..ee518180c0a --- /dev/null +++ b/src/flamenco/runtime/program/vote/fd_vote_state_v3.c @@ -0,0 +1,219 @@ +#include "fd_vote_state_v3.h" +#include "fd_authorized_voters.h" +#include "fd_vote_common.h" +#include "fd_vote_state_versioned.h" +#include "../fd_vote_program.h" +#include "../../fd_runtime.h" + +/* from_vote_state_1_14_11 converts a "v3" vote state object into + the older "v1.14.11" version. This destroys the "v3" object in + the process. */ + +// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/vote_state_1_14_11.rs#L67 +static void +from_vote_state_1_14_11( fd_vote_state_v3_t * vote_state, + fd_vote_state_1_14_11_t * vote_state_1_14_11, /* out */ + uchar * vote_lockout_mem ) { + vote_state_1_14_11->node_pubkey = vote_state->node_pubkey; /* copy */ + vote_state_1_14_11->authorized_withdrawer = vote_state->authorized_withdrawer; /* copy */ + vote_state_1_14_11->commission = vote_state->commission; /* copy */ + + // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/vote_state_1_14_11.rs#L72 + if( vote_state->votes ) { + vote_state_1_14_11->votes = deq_fd_vote_lockout_t_join( + deq_fd_vote_lockout_t_new( vote_lockout_mem, 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 ); + deq_fd_vote_lockout_t_push_tail_wrap( vote_state_1_14_11->votes, landed_vote->lockout ); + } + } + + vote_state_1_14_11->has_root_slot = vote_state->has_root_slot; /* copy */ + vote_state_1_14_11->root_slot = vote_state->root_slot; /* copy */ + vote_state_1_14_11->authorized_voters = vote_state->authorized_voters; /* move */ + vote_state_1_14_11->prior_voters = vote_state->prior_voters; /* deep copy */ + vote_state_1_14_11->epoch_credits = vote_state->epoch_credits; /* move */ + vote_state_1_14_11->last_timestamp = vote_state->last_timestamp; /* deep copy */ + + /* Clear moved objects */ + vote_state->authorized_voters.treap = NULL; + vote_state->authorized_voters.pool = NULL; + vote_state->epoch_credits = NULL; + +} + +/* https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v4.0.4/vote-interface/src/state/vote_state_v3.rs#L65-L73 */ +void +fd_vote_program_v3_create_new( fd_vote_init_t * const vote_init, + fd_sol_sysvar_clock_t const * clock, + uchar * authorized_voters_mem, + fd_vote_state_versioned_t * versioned /* out */ ) { + versioned->discriminant = fd_vote_state_versioned_enum_v3; + + fd_vote_state_v3_t * vote_state = &versioned->inner.v3; + vote_state->node_pubkey = vote_init->node_pubkey; + vote_state->authorized_voters = *fd_authorized_voters_new( clock->epoch, &vote_init->authorized_voter, authorized_voters_mem ); + vote_state->authorized_withdrawer = vote_init->authorized_withdrawer; + vote_state->commission = vote_init->commission; + vote_state->prior_voters.idx = 31; + vote_state->prior_voters.is_empty = 1; +} + +/* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L414-L434 */ +int +fd_vote_state_v3_set_vote_account_state( fd_exec_instr_ctx_t const * ctx, + fd_borrowed_account_t * vote_account, + fd_vote_state_versioned_t * versioned, + uchar * vote_lockout_mem ) { + /* This is a horrible conditional expression in Agave. + The terms were broken up into their own variables. */ + fd_vote_state_v3_t * v3_vote_state = &versioned->inner.v3; + + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L420-L424 */ + fd_rent_t const rent = fd_sysvar_cache_rent_read_nofail( ctx->sysvar_cache ); + int resize_needed = fd_borrowed_account_get_data_len( vote_account ) < FD_VOTE_STATE_V3_SZ; + int resize_rent_exempt = fd_rent_exempt_minimum_balance( &rent, FD_VOTE_STATE_V3_SZ ) <= fd_borrowed_account_get_lamports( vote_account ); + + /* The resize operation itself is part of the horrible conditional, + but behind a short-circuit operator. */ + int resize_failed = 0; + if( resize_needed && resize_rent_exempt ) { + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L422-L424 */ + resize_failed = + fd_borrowed_account_set_data_length( vote_account, FD_VOTE_STATE_V3_SZ ) != FD_EXECUTOR_INSTR_SUCCESS; + } + + if( FD_UNLIKELY( resize_needed && ( !resize_rent_exempt || resize_failed ) ) ) { + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L426-L430 */ + fd_vote_state_versioned_t v1_14_11; + fd_vote_state_versioned_new_disc( &v1_14_11, fd_vote_state_versioned_enum_v1_14_11 ); + from_vote_state_1_14_11( v3_vote_state, &v1_14_11.inner.v1_14_11, vote_lockout_mem ); + return fd_vsv_set_state( vote_account, &v1_14_11 ); + } + + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L432-L433 */ + return fd_vsv_set_state( vote_account, versioned ); +} + +/* This is more than just a deserialization - this function attempts + to deserialize whatever vote state version the vote account has, + and then tries to convert it into a v3 vote account (unless its a + v4 account, where it fails automatically). + https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v4.0.4/vote-interface/src/state/vote_state_v3.rs#L119-L124 */ +int +fd_vote_state_v3_deserialize( fd_borrowed_account_t const * vote_account, + uchar * vote_state_mem, + uchar * authorized_voters_mem, + uchar * landed_votes_mem ) { + /* deserialize_into_ptr is essentially a call to get_state + + try_convert_to_v3. It's written a little more verbosely in Agave + as they try to optimize the decoding steps. + https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v4.0.4/vote-interface/src/state/vote_state_v3.rs#L162-L202 */ + int rc = fd_vsv_get_state( vote_account->meta, vote_state_mem ); + if( FD_UNLIKELY( rc ) ) return rc; + + /* Unlike vote states v4 decoding, vote state v3 decoding will fail + if the discriminant is > fd_vote_state_versioned_enum_v3. + https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v4.0.4/vote-interface/src/state/vote_state_v3.rs#L198 */ + fd_vote_state_versioned_t * versioned = (fd_vote_state_versioned_t *)vote_state_mem; + if( FD_UNLIKELY( versioned->discriminant>fd_vote_state_versioned_enum_v3 ) ) { + return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA; + } + + return fd_vsv_try_convert_to_v3( versioned, authorized_voters_mem, landed_votes_mem ); +} + +// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L828 +int +fd_vote_state_v3_get_and_update_authorized_voter( fd_vote_state_v3_t * self, + ulong current_epoch, + fd_pubkey_t ** pubkey /* out */ ) { + // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L832 + fd_vote_authorized_voter_t * authorized_voter = fd_authorized_voters_get_and_cache_authorized_voter_for_epoch( + &self->authorized_voters, + current_epoch + ); + // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L835 + if( FD_UNLIKELY( !authorized_voter ) ) return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA; + *pubkey = &authorized_voter->pubkey; + // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L837 + fd_authorized_voters_purge_authorized_voters( &self->authorized_voters, current_epoch ); + return FD_EXECUTOR_INSTR_SUCCESS; +} + +/* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L263-L321 */ +int +fd_vote_state_v3_set_new_authorized_voter( fd_exec_instr_ctx_t * ctx, + fd_vote_state_v3_t * self, + fd_pubkey_t const * authorized_pubkey, + ulong current_epoch, + ulong target_epoch, + /* "verify" closure */ int authorized_withdrawer_signer, + /* "verify" closure */ fd_pubkey_t const * signers[static FD_TXN_SIG_MAX] ) { + int rc; + fd_pubkey_t * epoch_authorized_voter = NULL; + + // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L778 + rc = fd_vote_state_v3_get_and_update_authorized_voter( self, current_epoch, &epoch_authorized_voter ); + if( FD_UNLIKELY( rc ) ) return rc; + + // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L779 + rc = fd_vote_signature_verify( epoch_authorized_voter, authorized_withdrawer_signer, signers ); + if( FD_UNLIKELY( rc ) ) return rc; + + // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L786 + if( FD_UNLIKELY( fd_authorized_voters_contains( &self->authorized_voters, target_epoch ) ) ) { + ctx->txn_out->err.custom_err = FD_VOTE_ERR_TOO_SOON_TO_REAUTHORIZE; + return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR; + } + + // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L791 + fd_vote_authorized_voter_t * latest_authorized = + fd_authorized_voters_last( &self->authorized_voters ); + // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L794 + if( FD_UNLIKELY( ( !latest_authorized ) ) ) return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA; + ulong latest_epoch = latest_authorized->epoch; + fd_pubkey_t * latest_authorized_pubkey = &latest_authorized->pubkey; + + // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L799 + if( 0 != memcmp( latest_authorized_pubkey, authorized_pubkey, sizeof( fd_pubkey_t ) ) ) { + fd_vote_prior_voters_t * prior_voters = &self->prior_voters; + + // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L801 + ulong epoch_of_last_authorized_switch = 0UL; + if( (!prior_voters->is_empty) & (prior_voters->idx < 32) ) { + epoch_of_last_authorized_switch = prior_voters->buf[prior_voters->idx].epoch_end; + } + + // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L810 + if( target_epoch <= latest_epoch ) + return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA; + + // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L815 + prior_voters->idx += 1UL; + prior_voters->idx %= 32UL; + prior_voters->buf[prior_voters->idx] = + ( fd_vote_prior_voter_t ){ .pubkey = *latest_authorized_pubkey, + .epoch_start = epoch_of_last_authorized_switch, + .epoch_end = target_epoch }; + prior_voters->is_empty = 0; + } + + // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L822 + if( 0 == fd_vote_authorized_voters_pool_free( self->authorized_voters.pool) ) { + FD_LOG_ERR(( "Authorized_voter pool is empty" )); + } + + fd_vote_authorized_voter_t * ele = + fd_vote_authorized_voters_pool_ele_acquire( self->authorized_voters.pool ); + ele->epoch = target_epoch; + ele->pubkey = *authorized_pubkey; + ele->prio = (ulong)&ele->pubkey; + fd_vote_authorized_voters_treap_ele_insert( + self->authorized_voters.treap, ele, self->authorized_voters.pool ); + + return 0; +} + diff --git a/src/flamenco/runtime/program/vote/fd_vote_state_v3.h b/src/flamenco/runtime/program/vote/fd_vote_state_v3.h new file mode 100644 index 00000000000..8691adf8121 --- /dev/null +++ b/src/flamenco/runtime/program/vote/fd_vote_state_v3.h @@ -0,0 +1,51 @@ +#ifndef HEADER_fd_src_flamenco_runtime_program_vote_fd_vote_state_v3_h +#define HEADER_fd_src_flamenco_runtime_program_vote_fd_vote_state_v3_h + +#include "../../fd_borrowed_account.h" +#include "../../fd_executor.h" +#include "../../../types/fd_types.h" +#include "../../sysvar/fd_sysvar.h" + +/* https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v4.0.4/vote-interface/src/state/vote_state_v3.rs#L65-L73 */ +void +fd_vote_program_v3_create_new( fd_vote_init_t * const vote_init, + fd_sol_sysvar_clock_t const * clock, + uchar * authorized_voters_mem, + fd_vote_state_versioned_t * versioned /* out */ ); + +/* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L414-L434 */ +int +fd_vote_state_v3_set_vote_account_state( fd_exec_instr_ctx_t const * ctx, + fd_borrowed_account_t * vote_account, + fd_vote_state_versioned_t * versioned, + uchar * vote_lockout_mem ); + +/* This is more than just a deserialization - this function attempts + to deserialize whatever vote state version the vote account has, + and then tries to convert it into a v3 vote account (unless its a + v4 account, where it fails automatically). + https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v4.0.4/vote-interface/src/state/vote_state_v3.rs#L119-L124 */ +int +fd_vote_state_v3_deserialize( fd_borrowed_account_t const * vote_account, + uchar * vote_state_mem, + uchar * authorized_voters_mem, + uchar * landed_votes_mem ); + +// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L828 +int +fd_vote_state_v3_get_and_update_authorized_voter( fd_vote_state_v3_t * self, + ulong current_epoch, + fd_pubkey_t ** pubkey /* out */ ); + +/* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L263-L321 */ +int +fd_vote_state_v3_set_new_authorized_voter( fd_exec_instr_ctx_t * ctx, + fd_vote_state_v3_t * self, + fd_pubkey_t const * authorized_pubkey, + ulong current_epoch, + ulong target_epoch, + /* "verify" closure */ int authorized_withdrawer_signer, + /* "verify" closure */ fd_pubkey_t const * signers[static FD_TXN_SIG_MAX] ); + +#endif /* HEADER_fd_src_flamenco_runtime_program_vote_fd_vote_state_v3_h */ + diff --git a/src/flamenco/runtime/program/vote/fd_vote_state_v4.c b/src/flamenco/runtime/program/vote/fd_vote_state_v4.c new file mode 100644 index 00000000000..370e021a2d3 --- /dev/null +++ b/src/flamenco/runtime/program/vote/fd_vote_state_v4.c @@ -0,0 +1,117 @@ +#include "fd_vote_state_v4.h" +#include "fd_authorized_voters.h" +#include "fd_vote_state_versioned.h" +#include "fd_vote_common.h" +#include "../fd_vote_program.h" +#include "../../fd_runtime.h" + +/**********************************************************************/ +/* VoteStateV4 */ +/**********************************************************************/ + +/* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L600-L619 */ +void +fd_vote_state_v4_create_new( fd_pubkey_t const * vote_pubkey, + fd_vote_init_t const * vote_init, + fd_sol_sysvar_clock_t const * clock, + uchar * authorized_voters_mem, + fd_vote_state_versioned_t * versioned /* out */ ) { + versioned->discriminant = fd_vote_state_versioned_enum_v4; + + fd_vote_state_v4_t * vote_state = &versioned->inner.v4; + vote_state->node_pubkey = vote_init->node_pubkey; + vote_state->authorized_voters = *fd_authorized_voters_new(clock->epoch, &vote_init->authorized_voter, authorized_voters_mem); + vote_state->authorized_withdrawer = vote_init->authorized_withdrawer; + vote_state->inflation_rewards_commission_bps = ((ushort)vote_init->commission) * 100; + vote_state->inflation_rewards_collector = *vote_pubkey; + vote_state->block_revenue_collector = vote_init->node_pubkey; + vote_state->block_revenue_commission_bps = DEFAULT_BLOCK_REVENUE_COMMISSION_BPS; +} + +/* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L576-L595 */ +int +fd_vote_state_v4_set_vote_account_state( fd_exec_instr_ctx_t const * ctx, + fd_borrowed_account_t * vote_account, + fd_vote_state_versioned_t * versioned ) { + /* This is a horrible conditional expression in Agave. + The terms were broken up into their own variables. */ + + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L582-L586 */ + fd_rent_t const rent = fd_sysvar_cache_rent_read_nofail( ctx->sysvar_cache ); + int resize_needed = fd_borrowed_account_get_data_len( vote_account ) < FD_VOTE_STATE_V4_SZ; + int resize_rent_exempt = fd_rent_exempt_minimum_balance( &rent, FD_VOTE_STATE_V4_SZ ) <= fd_borrowed_account_get_lamports( vote_account ); + + /* The resize operation itself is part of the horrible conditional, + but behind a short-circuit operator. */ + int resize_failed = 0; + if( resize_needed && resize_rent_exempt ) { + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L584-L586 */ + resize_failed = + fd_borrowed_account_set_data_length( vote_account, FD_VOTE_STATE_V4_SZ ) != FD_EXECUTOR_INSTR_SUCCESS; + } + + if( FD_UNLIKELY( resize_needed && ( !resize_rent_exempt || resize_failed ) ) ) { + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L590 */ + return FD_EXECUTOR_INSTR_ERR_ACC_NOT_RENT_EXEMPT; + } + + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L593 */ + return fd_vsv_set_state( vote_account, versioned ); +} + +int +fd_vote_state_v4_get_and_update_authorized_voter( fd_vote_state_v4_t * self, + ulong current_epoch, + fd_pubkey_t ** pubkey /* out */ ) { + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L327-L330 */ + fd_vote_authorized_voter_t * authorized_voter = + fd_authorized_voters_get_and_cache_authorized_voter_for_epoch( &self->authorized_voters, + current_epoch ); + if( FD_UNLIKELY( !authorized_voter ) ) return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA; + *pubkey = &authorized_voter->pubkey; + + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L331-L332 */ + fd_authorized_voters_purge_authorized_voters( &self->authorized_voters, fd_ulong_sat_sub( current_epoch, 1UL ) ); + return FD_EXECUTOR_INSTR_SUCCESS; +} + +int +fd_vote_state_v4_set_new_authorized_voter( fd_exec_instr_ctx_t * ctx, + fd_vote_state_v4_t * self, + fd_pubkey_t const * authorized_pubkey, + ulong current_epoch, + ulong target_epoch, + /* "verify" closure */ int authorized_withdrawer_signer, + /* "verify" closure */ fd_pubkey_t const * signers[static FD_TXN_SIG_MAX] ) { + int rc; + fd_pubkey_t * epoch_authorized_voter = NULL; + + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L462 */ + rc = fd_vote_state_v4_get_and_update_authorized_voter( self, current_epoch, &epoch_authorized_voter ); + if( FD_UNLIKELY( rc ) ) return rc; + + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L463 */ + rc = fd_vote_signature_verify( epoch_authorized_voter, authorized_withdrawer_signer, signers ); + if( FD_UNLIKELY( rc ) ) return rc; + + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L470-L472 */ + if( FD_UNLIKELY( fd_authorized_voters_contains( &self->authorized_voters, target_epoch ) ) ) { + ctx->txn_out->err.custom_err = FD_VOTE_ERR_TOO_SOON_TO_REAUTHORIZE; + return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR; + } + + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L474-L475 */ + if( 0 == fd_vote_authorized_voters_pool_free( self->authorized_voters.pool) ) { + FD_LOG_ERR(( "Authorized_voter pool is empty" )); + } + + fd_vote_authorized_voter_t * ele = + fd_vote_authorized_voters_pool_ele_acquire( self->authorized_voters.pool ); + ele->epoch = target_epoch; + ele->pubkey = *authorized_pubkey; + ele->prio = (ulong)&ele->pubkey; + fd_vote_authorized_voters_treap_ele_insert( + self->authorized_voters.treap, ele, self->authorized_voters.pool ); + + return 0; +} diff --git a/src/flamenco/runtime/program/vote/fd_vote_state_v4.h b/src/flamenco/runtime/program/vote/fd_vote_state_v4.h new file mode 100644 index 00000000000..f552a52e856 --- /dev/null +++ b/src/flamenco/runtime/program/vote/fd_vote_state_v4.h @@ -0,0 +1,40 @@ +#ifndef HEADER_fd_src_flamenco_runtime_program_vote_fd_vote_state_v4_h +#define HEADER_fd_src_flamenco_runtime_program_vote_fd_vote_state_v4_h + +#include "../../fd_borrowed_account.h" +#include "../../fd_executor.h" +#include "../../../types/fd_types.h" +#include "../../sysvar/fd_sysvar.h" + +/* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L600-L619 */ +void +fd_vote_state_v4_create_new( fd_pubkey_t const * vote_pubkey, + fd_vote_init_t const * vote_init, + fd_sol_sysvar_clock_t const * clock, + uchar * authorized_voters_mem, + fd_vote_state_versioned_t * versioned /* out */ ); + +/* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L576-L595 */ +int +fd_vote_state_v4_set_vote_account_state( fd_exec_instr_ctx_t const * ctx, + fd_borrowed_account_t * vote_account, + fd_vote_state_versioned_t * versioned ); + +/* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L323-L334 */ +int +fd_vote_state_v4_get_and_update_authorized_voter( fd_vote_state_v4_t * self, + ulong current_epoch, + fd_pubkey_t ** pubkey /* out */ ); + +/* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L450-L478 */ +int +fd_vote_state_v4_set_new_authorized_voter( fd_exec_instr_ctx_t * ctx, + fd_vote_state_v4_t * self, + fd_pubkey_t const * authorized_pubkey, + ulong current_epoch, + ulong target_epoch, + /* "verify" closure */ int authorized_withdrawer_signer, + /* "verify" closure */ fd_pubkey_t const * signers[static FD_TXN_SIG_MAX] ); + +#endif /* HEADER_fd_src_flamenco_runtime_program_vote_fd_vote_state_v4_h */ + diff --git a/src/flamenco/runtime/program/vote/fd_vote_state_versioned.c b/src/flamenco/runtime/program/vote/fd_vote_state_versioned.c new file mode 100644 index 00000000000..24d80027880 --- /dev/null +++ b/src/flamenco/runtime/program/vote/fd_vote_state_versioned.c @@ -0,0 +1,762 @@ +#include "../fd_vote_program.h" +#include "fd_vote_state_versioned.h" +#include "fd_vote_common.h" +#include "fd_vote_lockout.h" +#include "fd_vote_state_v3.h" +#include "fd_vote_state_v4.h" +#include "fd_authorized_voters.h" +#include "../../fd_runtime.h" + +// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L42 +#define DEFAULT_PRIOR_VOTERS_OFFSET 114 + +// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L886 +#define VERSION_OFFSET (4UL) + +// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L887 +#define DEFAULT_PRIOR_VOTERS_END (118) + +// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/vote_state_1_14_11.rs#L6 +#define DEFAULT_PRIOR_VOTERS_OFFSET_1_14_11 (82UL) + +// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/vote_state_1_14_11.rs#L60 +#define DEFAULT_PRIOR_VOTERS_END_1_14_11 (86UL) + +/* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L780-L785 */ +static inline fd_vote_lockout_t * +last_lockout( fd_vote_state_versioned_t * self ) { + fd_landed_vote_t * votes = NULL; + switch( self->discriminant ) { + case fd_vote_state_versioned_enum_v3: + votes = self->inner.v3.votes; + break; + case fd_vote_state_versioned_enum_v4: + votes = self->inner.v4.votes; + break; + default: + FD_LOG_ERR(( "unsupported vote state version: %u", self->discriminant )); + } + + if( deq_fd_landed_vote_t_empty( votes ) ) return NULL; + fd_landed_vote_t * last_vote = deq_fd_landed_vote_t_peek_tail( votes ); + return &last_vote->lockout; +} + +/**********************************************************************/ +/* Getters */ +/**********************************************************************/ + +/* https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1074 */ +int +fd_vsv_get_state( fd_account_meta_t const * meta, + uchar * res ) { + + fd_bincode_decode_ctx_t decode = { + .data = fd_account_data( meta ), + .dataend = fd_account_data( meta ) + meta->dlen, + }; + + ulong total_sz = 0UL; + int err = fd_vote_state_versioned_decode_footprint( &decode, &total_sz ); + if( FD_UNLIKELY( err ) ) { + return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA; + } + + FD_TEST( total_sz<=FD_VOTE_STATE_VERSIONED_FOOTPRINT ); + + fd_vote_state_versioned_decode( res, &decode ); + + return FD_EXECUTOR_INSTR_SUCCESS; +} + +/* Returns a const pointer to the authorized withdrawer for the + appropriate vote state version.*/ +fd_pubkey_t const * +fd_vsv_get_authorized_withdrawer( fd_vote_state_versioned_t * self ) { + switch( self->discriminant ) { + case fd_vote_state_versioned_enum_v0_23_5: + return &self->inner.v0_23_5.authorized_withdrawer; + case fd_vote_state_versioned_enum_v1_14_11: + return &self->inner.v1_14_11.authorized_withdrawer; + case fd_vote_state_versioned_enum_v3: + return &self->inner.v3.authorized_withdrawer; + case fd_vote_state_versioned_enum_v4: + return &self->inner.v4.authorized_withdrawer; + default: + FD_LOG_ERR(( "unsupported vote state version: %u", self->discriminant )); + } +} + +uchar +fd_vsv_get_commission( fd_vote_state_versioned_t * self ) { + switch( self->discriminant ) { + case fd_vote_state_versioned_enum_v3: + return self->inner.v3.commission; + case fd_vote_state_versioned_enum_v4: + return (uchar)(self->inner.v4.inflation_rewards_commission_bps/100); + default: + FD_LOG_ERR(( "unsupported vote state version: %u", self->discriminant )); + } +} + +fd_vote_epoch_credits_t const * +fd_vsv_get_epoch_credits( fd_vote_state_versioned_t * self ) { + return fd_vsv_get_epoch_credits_mutable( self ); +} + +fd_landed_vote_t const * +fd_vsv_get_votes( fd_vote_state_versioned_t * self ) { + return fd_vsv_get_votes_mutable( self ); +} + +ulong const * +fd_vsv_get_last_voted_slot( fd_vote_state_versioned_t * self ) { + fd_vote_lockout_t * last_lockout_ = last_lockout( self ); + if( FD_UNLIKELY( !last_lockout_ ) ) return NULL; + return &last_lockout_->slot; +} + +ulong const * +fd_vsv_get_root_slot( fd_vote_state_versioned_t * self ) { + switch( self->discriminant ) { + case fd_vote_state_versioned_enum_v3: + if( !self->inner.v3.has_root_slot ) return NULL; + return &self->inner.v3.root_slot; + case fd_vote_state_versioned_enum_v4: + if( !self->inner.v4.has_root_slot ) return NULL; + return &self->inner.v4.root_slot; + default: + FD_LOG_ERR(( "unsupported vote state version: %u", self->discriminant )); + } +} + +fd_vote_block_timestamp_t const * +fd_vsv_get_last_timestamp( fd_vote_state_versioned_t * self ) { + switch( self->discriminant ) { + case fd_vote_state_versioned_enum_v3: + return &self->inner.v3.last_timestamp; + case fd_vote_state_versioned_enum_v4: + return &self->inner.v4.last_timestamp; + default: + FD_LOG_ERR(( "unsupported vote state version: %u", self->discriminant )); + } +} + +/**********************************************************************/ +/* Mutable getters */ +/**********************************************************************/ + +fd_vote_epoch_credits_t * +fd_vsv_get_epoch_credits_mutable( fd_vote_state_versioned_t * self ) { + switch( self->discriminant ) { + case fd_vote_state_versioned_enum_v3: + return self->inner.v3.epoch_credits; + case fd_vote_state_versioned_enum_v4: + return self->inner.v4.epoch_credits; + default: + FD_LOG_ERR(( "unsupported vote state version: %u", self->discriminant )); + } +} + +fd_landed_vote_t * +fd_vsv_get_votes_mutable( fd_vote_state_versioned_t * self ) { + switch( self->discriminant ) { + case fd_vote_state_versioned_enum_v3: + return self->inner.v3.votes; + case fd_vote_state_versioned_enum_v4: + return self->inner.v4.votes; + default: + FD_LOG_ERR(( "unsupported vote state version: %u", self->discriminant )); + } +} + +/**********************************************************************/ +/* Setters */ +/**********************************************************************/ + +int +fd_vsv_set_state( fd_borrowed_account_t * self, + fd_vote_state_versioned_t * state ) { + /* https://github.com/anza-xyz/agave/blob/v2.1.14/sdk/src/transaction_context.rs#L974 */ + uchar * data = NULL; + ulong dlen = 0UL; + int err = fd_borrowed_account_get_data_mut( self, &data, &dlen ); + if( FD_UNLIKELY( err ) ) { + return err; + } + + // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/src/transaction_context.rs#L978 + ulong serialized_size = fd_vote_state_versioned_size( state ); + if( FD_UNLIKELY( serialized_size > dlen ) ) + return FD_EXECUTOR_INSTR_ERR_ACC_DATA_TOO_SMALL; + + // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/src/transaction_context.rs#L983 + fd_bincode_encode_ctx_t encode = + { .data = data, + .dataend = data + dlen }; + do { + int err = fd_vote_state_versioned_encode( state, &encode ); + if( FD_UNLIKELY( err ) ) FD_LOG_ERR(( "fd_vote_state_versioned_encode failed (%d)", err )); + } while(0); + + return FD_EXECUTOR_INSTR_SUCCESS; +} + +int +fd_vsv_set_vote_account_state( fd_exec_instr_ctx_t const * ctx, + fd_borrowed_account_t * vote_account, + fd_vote_state_versioned_t * versioned, + uchar * vote_lockout_mem ) { + switch( versioned->discriminant ) { + case fd_vote_state_versioned_enum_v3: + return fd_vote_state_v3_set_vote_account_state( ctx, vote_account, versioned, vote_lockout_mem ); + case fd_vote_state_versioned_enum_v4: + return fd_vote_state_v4_set_vote_account_state( ctx, vote_account, versioned ); + default: + FD_LOG_ERR(( "unsupported vote state version: %u", versioned->discriminant )); + } +} + +void +fd_vsv_set_authorized_withdrawer( fd_vote_state_versioned_t * self, + fd_pubkey_t const * authorized_withdrawer ) { + switch( self->discriminant ) { + case fd_vote_state_versioned_enum_v3: { + self->inner.v3.authorized_withdrawer = *authorized_withdrawer; + break; + } + case fd_vote_state_versioned_enum_v4: { + self->inner.v4.authorized_withdrawer = *authorized_withdrawer; + break; + } + default: + FD_LOG_ERR(( "unsupported vote state version: %u", self->discriminant )); + } +} + +int +fd_vsv_set_new_authorized_voter( fd_exec_instr_ctx_t * ctx, + fd_vote_state_versioned_t * self, + fd_pubkey_t const * authorized_pubkey, + ulong current_epoch, + ulong target_epoch, + /* "verify" closure */ int authorized_withdrawer_signer, + /* "verify" closure */ fd_pubkey_t const * signers[static FD_TXN_SIG_MAX] ) { + switch( self->discriminant ) { + case fd_vote_state_versioned_enum_v3: + return fd_vote_state_v3_set_new_authorized_voter( + ctx, + &self->inner.v3, + authorized_pubkey, + current_epoch, + target_epoch, + authorized_withdrawer_signer, + signers + ); + case fd_vote_state_versioned_enum_v4: + return fd_vote_state_v4_set_new_authorized_voter( + ctx, + &self->inner.v4, + authorized_pubkey, + current_epoch, + target_epoch, + authorized_withdrawer_signer, + signers + ); + default: + FD_LOG_CRIT(( "unsupported vote state version: %u", self->discriminant )); + } +} + +void +fd_vsv_set_node_pubkey( fd_vote_state_versioned_t * self, + fd_pubkey_t const * node_pubkey ) { + switch( self->discriminant ) { + case fd_vote_state_versioned_enum_v3: + self->inner.v3.node_pubkey = *node_pubkey; + break; + case fd_vote_state_versioned_enum_v4: + self->inner.v4.node_pubkey = *node_pubkey; + break; + } +} + +void +fd_vsv_set_block_revenue_collector( fd_vote_state_versioned_t * self, + fd_pubkey_t const * block_revenue_collector ) { + switch( self->discriminant ) { + case fd_vote_state_versioned_enum_v4: + self->inner.v4.block_revenue_collector = *block_revenue_collector; + break; + } +} + +void +fd_vsv_set_commission( fd_vote_state_versioned_t * self, + uchar commission ) { + switch( self->discriminant ) { + case fd_vote_state_versioned_enum_v3: + self->inner.v3.commission = commission; + break; + case fd_vote_state_versioned_enum_v4: + self->inner.v4.inflation_rewards_commission_bps = commission*100; + break; + default: + FD_LOG_ERR(( "unsupported vote state version: %u", self->discriminant )); + } +} + +void +fd_vsv_set_root_slot( fd_vote_state_versioned_t * self, ulong * root_slot ) { + switch( self->discriminant ) { + case fd_vote_state_versioned_enum_v3: + self->inner.v3.has_root_slot = (root_slot!=NULL); + if( FD_LIKELY( root_slot ) ) { + self->inner.v3.root_slot = *root_slot; + } + break; + case fd_vote_state_versioned_enum_v4: + self->inner.v4.has_root_slot = (root_slot!=NULL); + if( FD_LIKELY( root_slot ) ) { + self->inner.v4.root_slot = *root_slot; + } + break; + } +} + +static void +fd_vsv_set_last_timestamp( fd_vote_state_versioned_t * self, + fd_vote_block_timestamp_t const * last_timestamp ) { + switch( self->discriminant ) { + case fd_vote_state_versioned_enum_v3: + self->inner.v3.last_timestamp = *last_timestamp; + break; + case fd_vote_state_versioned_enum_v4: + self->inner.v4.last_timestamp = *last_timestamp; + break; + default: + FD_LOG_ERR(( "unsupported vote state version: %u", self->discriminant )); + } +} + +/**********************************************************************/ +/* General functions */ +/**********************************************************************/ + +// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L855 +static void +double_lockouts( fd_vote_state_versioned_t * self ) { + fd_landed_vote_t * votes = fd_vsv_get_votes_mutable( self ); + + // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L856 + ulong stack_depth = deq_fd_landed_vote_t_cnt( votes ); + ulong i = 0; + // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L857 + for( deq_fd_landed_vote_t_iter_t iter = deq_fd_landed_vote_t_iter_init( votes ); + !deq_fd_landed_vote_t_iter_done( votes, iter ); + iter = deq_fd_landed_vote_t_iter_next( votes, iter ) ) { + fd_landed_vote_t * v = deq_fd_landed_vote_t_iter_ele( votes, iter ); + // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L860 + if( stack_depth > + fd_ulong_checked_add_expect( + i, + (ulong)v->lockout.confirmation_count, + "`confirmation_count` and tower_size should be bounded by `MAX_LOCKOUT_HISTORY`" ) ) + { + // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L864 + fd_vote_lockout_increase_confirmation_count( &v->lockout, 1 ); + } + i++; + } +} + +void +fd_vsv_increment_credits( fd_vote_state_versioned_t * self, + ulong epoch, + ulong credits ) { + fd_vote_epoch_credits_t * epoch_credits = fd_vsv_get_epoch_credits_mutable( self ); + + /* https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v3.0.0/vote-interface/src/state/vote_state_v3.rs#L286-L305 */ + if( FD_UNLIKELY( deq_fd_vote_epoch_credits_t_empty( epoch_credits ) ) ) { + /* https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v3.0.0/vote-interface/src/state/vote_state_v3.rs#L286-L288 */ + deq_fd_vote_epoch_credits_t_push_tail_wrap( + epoch_credits, + ( fd_vote_epoch_credits_t ){ .epoch = epoch, .credits = 0, .prev_credits = 0 } ); + } else if( FD_LIKELY( epoch != + deq_fd_vote_epoch_credits_t_peek_tail( epoch_credits )->epoch ) ) { + /* https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v3.0.0/vote-interface/src/state/vote_state_v3.rs#L290 */ + fd_vote_epoch_credits_t * last = deq_fd_vote_epoch_credits_t_peek_tail( epoch_credits ); + + ulong credits = last->credits; + ulong prev_credits = last->prev_credits; + + /* https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v3.0.0/vote-interface/src/state/vote_state_v3.rs#L292-L299 */ + if( FD_LIKELY( credits!=prev_credits ) ) { + if( FD_UNLIKELY( deq_fd_vote_epoch_credits_t_cnt( epoch_credits )>=MAX_EPOCH_CREDITS_HISTORY ) ) { + /* Although Agave performs a `.remove(0)` AFTER the call to + `.push()`, there is an edge case where the epoch credits is + full, making the call to `_push_tail()` unsafe. Since Agave's + structures are dynamically allocated, it is safe for them to + simply call `.push()` and then popping afterwards. We have to + reverse the order of operations to maintain correct behavior + and avoid overflowing the deque. + https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v3.0.0/vote-interface/src/state/vote_state_v3.rs#L303 */ + deq_fd_vote_epoch_credits_t_pop_head( epoch_credits ); + } + + /* This will not fail because we already popped if we're at + capacity, since the epoch_credits deque is allocated with a + minimum capacity of MAX_EPOCH_CREDITS_HISTORY. */ + deq_fd_vote_epoch_credits_t_push_tail( + epoch_credits, + ( fd_vote_epoch_credits_t ){ + .epoch = epoch, .credits = credits, .prev_credits = credits } ); + } else { + /* https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v3.0.0/vote-interface/src/state/vote_state_v3.rs#L297-L298 */ + deq_fd_vote_epoch_credits_t_peek_tail( epoch_credits )->epoch = epoch; + + /* Here we can perform the same deque size check and pop if + we're beyond the maximum epoch credits len. */ + if( FD_UNLIKELY( deq_fd_vote_epoch_credits_t_cnt( epoch_credits )>MAX_EPOCH_CREDITS_HISTORY ) ) { + deq_fd_vote_epoch_credits_t_pop_head( epoch_credits ); + } + } + } + + // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L663 + deq_fd_vote_epoch_credits_t_peek_tail( epoch_credits )->credits = fd_ulong_sat_add( + deq_fd_vote_epoch_credits_t_peek_tail( epoch_credits )->credits, credits ); +} + +int +fd_vsv_process_timestamp( fd_exec_instr_ctx_t * ctx, + fd_vote_state_versioned_t * self, + ulong slot, + long timestamp ) { + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L160 */ + fd_vote_block_timestamp_t const * last_timestamp = fd_vsv_get_last_timestamp( self ); + if( FD_UNLIKELY( + ( slotslot || timestamptimestamp ) || + ( slot==last_timestamp->slot && + ( slot!=last_timestamp->slot || timestamp!=last_timestamp->timestamp ) && + last_timestamp->slot!=0UL ) ) ) { + ctx->txn_out->err.custom_err = FD_VOTE_ERR_TIMESTAMP_TOO_OLD; + return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR; + } + + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L168 */ + fd_vote_block_timestamp_t new_timestamp = { + .slot = slot, + .timestamp = timestamp, + }; + fd_vsv_set_last_timestamp( self, &new_timestamp ); + return FD_EXECUTOR_INSTR_SUCCESS; +} + +void +fd_vsv_pop_expired_votes( fd_vote_state_versioned_t * self, ulong next_vote_slot ) { + fd_landed_vote_t * votes = fd_vsv_get_votes_mutable( self ); + + while( !deq_fd_landed_vote_t_empty( votes ) ) { + fd_landed_vote_t * vote = deq_fd_landed_vote_t_peek_tail( votes ); + if( !( fd_vote_lockout_is_locked_out_at_slot( &vote->lockout, next_vote_slot ) ) ) { + deq_fd_landed_vote_t_pop_tail( votes ); + } else { + break; + } + } +} + +void +fd_vsv_process_next_vote_slot( fd_vote_state_versioned_t * self, + ulong next_vote_slot, + ulong epoch, + ulong current_slot ) { + ulong const * last_voted_slot_ = fd_vsv_get_last_voted_slot( self ); + if( FD_UNLIKELY( last_voted_slot_ && next_vote_slot <= *last_voted_slot_ ) ) return; + + fd_vsv_pop_expired_votes( self, next_vote_slot ); + + fd_landed_vote_t * votes = fd_vsv_get_votes_mutable( self ); + + fd_landed_vote_t landed_vote = { + .latency = fd_vote_compute_vote_latency( next_vote_slot, current_slot ), + .lockout = ( fd_vote_lockout_t ){ .slot = next_vote_slot } + }; + + // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L623 + if( FD_UNLIKELY( deq_fd_landed_vote_t_cnt( votes ) == MAX_LOCKOUT_HISTORY ) ) { + ulong credits = fd_vote_credits_for_vote_at_index( votes, 0 ); + fd_landed_vote_t landed_vote = deq_fd_landed_vote_t_pop_head( votes ); + fd_vsv_set_root_slot( self, &landed_vote.lockout.slot ); + + fd_vsv_increment_credits( self, epoch, credits ); + } + + // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L634 + deq_fd_landed_vote_t_push_tail_wrap( votes, landed_vote ); + double_lockouts( self ); +} + +int +fd_vsv_try_convert_to_v3( fd_vote_state_versioned_t * self, + uchar * authorized_voters_mem, + uchar * landed_votes_mem ) { + switch( self->discriminant ) { + /* https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v4.0.4/vote-interface/src/state/vote_state_versions.rs#L47-L73 */ + case fd_vote_state_versioned_enum_v0_23_5: { + fd_vote_state_0_23_5_t * state = &self->inner.v0_23_5; + // Check if uninitialized (authorized_voter is all zeros) + int is_uninitialized = 1; + for( ulong i = 0; i < sizeof(fd_pubkey_t); i++ ) { + if( state->authorized_voter.uc[i] != 0 ) { + is_uninitialized = 0; + break; + } + } + + fd_vote_authorized_voters_t * authorized_voters; + if( is_uninitialized ) { + // Create empty AuthorizedVoters (default), initialized but with no entries + authorized_voters = fd_authorized_voters_new_empty( authorized_voters_mem ); + } else { + authorized_voters = fd_authorized_voters_new( + state->authorized_voter_epoch, &state->authorized_voter, authorized_voters_mem ); + } + + /* Temporary to hold v3 */ + /* https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v4.0.4/vote-interface/src/state/vote_state_versions.rs#L54-L72 */ + fd_vote_state_v3_t v3 = { + .node_pubkey = state->node_pubkey, + .authorized_withdrawer = state->authorized_withdrawer, + .commission = state->commission, + .votes = fd_vote_lockout_landed_votes_from_lockouts( state->votes, landed_votes_mem ), + .has_root_slot = state->has_root_slot, + .root_slot = state->root_slot, + .authorized_voters = *authorized_voters, + .prior_voters = (fd_vote_prior_voters_t) { + .idx = 31UL, + .is_empty = 1, + }, + .epoch_credits = state->epoch_credits, + .last_timestamp = state->last_timestamp, + }; + + /* Emplace new vote state into target */ + self->discriminant = fd_vote_state_versioned_enum_v3; + self->inner.v3 = v3; + + return FD_EXECUTOR_INSTR_SUCCESS; + } + /* https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v4.0.4/vote-interface/src/state/vote_state_versions.rs#L75-L91 */ + case fd_vote_state_versioned_enum_v1_14_11: { + fd_vote_state_1_14_11_t * state = &self->inner.v1_14_11; + + /* Temporary to hold v3 */ + fd_vote_state_v3_t v3 = { + .node_pubkey = state->node_pubkey, + .authorized_withdrawer = state->authorized_withdrawer, + .commission = state->commission, + .votes = fd_vote_lockout_landed_votes_from_lockouts( state->votes, landed_votes_mem ), + .has_root_slot = state->has_root_slot, + .root_slot = state->root_slot, + .authorized_voters = state->authorized_voters, + .prior_voters = state->prior_voters, + .epoch_credits = state->epoch_credits, + .last_timestamp = state->last_timestamp + }; + + /* Emplace new vote state into target */ + self->discriminant = fd_vote_state_versioned_enum_v3; + self->inner.v3 = v3; + + return FD_EXECUTOR_INSTR_SUCCESS; + } + /* https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v4.0.4/vote-interface/src/state/vote_state_versions.rs#L93 */ + case fd_vote_state_versioned_enum_v3: + return FD_EXECUTOR_INSTR_SUCCESS; + /* https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v4.0.4/vote-interface/src/state/vote_state_versions.rs#L96 */ + case fd_vote_state_versioned_enum_v4: + return FD_EXECUTOR_INSTR_ERR_INVALID_ARG; + default: + FD_LOG_ERR(( "unsupported vote state version: %u", self->discriminant )); + } +} + +int +fd_vsv_try_convert_to_v4( fd_vote_state_versioned_t * self, + fd_pubkey_t const * vote_pubkey, + uchar * landed_votes_mem ) { + switch( self->discriminant ) { + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L971-L974 */ + case fd_vote_state_versioned_enum_v0_23_5: { + return FD_EXECUTOR_INSTR_ERR_UNINITIALIZED_ACCOUNT; + } + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L975-L989 */ + case fd_vote_state_versioned_enum_v1_14_11: { + fd_vote_state_1_14_11_t * state = &self->inner.v1_14_11; + fd_vote_state_v4_t v4 = { + .node_pubkey = state->node_pubkey, + .authorized_withdrawer = state->authorized_withdrawer, + .inflation_rewards_collector = *vote_pubkey, + .block_revenue_collector = state->node_pubkey, + .inflation_rewards_commission_bps = fd_ushort_sat_mul( state->commission, 100 ), + .block_revenue_commission_bps = DEFAULT_BLOCK_REVENUE_COMMISSION_BPS, + .pending_delegator_rewards = 0, + .has_bls_pubkey_compressed = 0, + .votes = fd_vote_lockout_landed_votes_from_lockouts( state->votes, landed_votes_mem ), + .has_root_slot = state->has_root_slot, + .root_slot = state->root_slot, + .authorized_voters = state->authorized_voters, + .epoch_credits = state->epoch_credits, + .last_timestamp = state->last_timestamp + }; + + /* Emplace new vote state into target */ + self->discriminant = fd_vote_state_versioned_enum_v4; + self->inner.v4 = v4; + + return FD_EXECUTOR_INSTR_SUCCESS; + } + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L990-L1004 */ + case fd_vote_state_versioned_enum_v3: { + fd_vote_state_v3_t * state = &self->inner.v3; + fd_vote_state_v4_t v4 = { + .node_pubkey = state->node_pubkey, + .authorized_withdrawer = state->authorized_withdrawer, + .inflation_rewards_collector = *vote_pubkey, + .block_revenue_collector = state->node_pubkey, + .inflation_rewards_commission_bps = fd_ushort_sat_mul( state->commission, 100 ), + .block_revenue_commission_bps = DEFAULT_BLOCK_REVENUE_COMMISSION_BPS, + .pending_delegator_rewards = 0, + .has_bls_pubkey_compressed = 0, + .votes = state->votes, + .has_root_slot = state->has_root_slot, + .root_slot = state->root_slot, + .authorized_voters = state->authorized_voters, + .epoch_credits = state->epoch_credits, + .last_timestamp = state->last_timestamp + }; + + /* Emplace new vote state into target */ + self->discriminant = fd_vote_state_versioned_enum_v4; + self->inner.v4 = v4; + + return FD_EXECUTOR_INSTR_SUCCESS; + } + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L1005 */ + case fd_vote_state_versioned_enum_v4: + return FD_EXECUTOR_INSTR_SUCCESS; + default: + FD_LOG_ERR(( "unsupported vote state version: %u", self->discriminant )); + } +} + +int +fd_vsv_deinitialize_vote_account_state( fd_exec_instr_ctx_t * ctx, + fd_borrowed_account_t * vote_account, + int target_version, + uchar * vote_lockout_mem ) { + switch( target_version ) { + case VOTE_STATE_TARGET_VERSION_V3: { + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L878 */ + fd_vote_state_versioned_t versioned; + fd_vote_state_versioned_new_disc( &versioned, fd_vote_state_versioned_enum_v3 ); + versioned.inner.v3.prior_voters.idx = 31; + versioned.inner.v3.prior_voters.is_empty = 1; + return fd_vote_state_v3_set_vote_account_state( + ctx, + vote_account, + &versioned, + vote_lockout_mem + ); + } + case VOTE_STATE_TARGET_VERSION_V4: { + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L881-L883 */ + uchar * data; + ulong dlen; + int rc = fd_borrowed_account_get_data_mut( vote_account, &data, &dlen ); + if( FD_UNLIKELY( rc ) ) return rc; + fd_memset( data, 0, dlen ); + return FD_EXECUTOR_INSTR_SUCCESS; + } + default: + FD_LOG_ERR(( "unsupported target version" )); + } +} + +int +fd_vsv_deserialize( fd_borrowed_account_t const * vote_account, + uchar * vote_state_mem ) { + /* To keep error codes conformant, we need to read the discriminant + from the account data first because if the discriminant matches + 0_23_5, an uninitialized account error is thrown. + + TODO: I have submitted a patch in Solana-SDK to coalesce the error + codes to simplify this handling. Remove this once merged. */ + uchar const * data = fd_borrowed_account_get_data( vote_account ); + ulong data_len = fd_borrowed_account_get_data_len( vote_account ); + if( FD_UNLIKELY( data_lenmeta, vote_state_mem ); + if( FD_UNLIKELY( rc ) ) return rc; + + return FD_EXECUTOR_INSTR_SUCCESS; +} + +int +fd_vsv_is_uninitialized( fd_vote_state_versioned_t * self ) { + switch( self->discriminant ) { + case fd_vote_state_versioned_enum_v0_23_5: { + fd_pubkey_t pubkey_default = { 0 }; + return !memcmp( &self->inner.v0_23_5.authorized_voter, &pubkey_default, sizeof( fd_pubkey_t ) ); + } + case fd_vote_state_versioned_enum_v1_14_11: + return fd_authorized_voters_is_empty( &self->inner.v1_14_11.authorized_voters ); + case fd_vote_state_versioned_enum_v3: + return fd_authorized_voters_is_empty( &self->inner.v3.authorized_voters ); + case fd_vote_state_versioned_enum_v4: + return 0; // v4 vote states are always initialized + default: + __builtin_unreachable(); + } +} + +int +fd_vsv_is_correct_size_and_initialized( fd_account_meta_t const * meta ) { + uchar const * data = fd_account_data( meta ); + ulong data_len = meta->dlen; + uint const * disc_ptr = (uint const *)data; // NOT SAFE TO ACCESS YET! + + /* VoteStateV4::is_correct_size_and_initialized + https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v4.0.4/vote-interface/src/state/vote_state_v4.rs#L207-L210 */ + if( FD_LIKELY( data_len==FD_VOTE_STATE_V4_SZ && *disc_ptr==fd_vote_state_versioned_enum_v4 ) ) { + return 1; + } + + /* This is big enough for both v3 and v1_14_11 and can be reused + (DEFAULT_PRIOR_VOTERS_OFFSET>DEFAULT_PRIOR_VOTERS_OFFSET_1_14_11) */ + uchar zero_data[ DEFAULT_PRIOR_VOTERS_OFFSET ] = {0}; + + /* VoteStateV3::is_correct_size_and_initialized + https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v4.0.4/vote-interface/src/state/vote_state_v3.rs#L509-L514 */ + if( FD_LIKELY( data_len==FD_VOTE_STATE_V3_SZ && + memcmp( data+VERSION_OFFSET, zero_data, DEFAULT_PRIOR_VOTERS_OFFSET ) ) ) { + return 1; + } + + /* VoteState1_14_11::is_correct_size_and_initialized + https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v4.0.4/vote-interface/src/state/vote_state_1_14_11.rs#L63-L69 */ + if( FD_LIKELY( data_len==FD_VOTE_STATE_V2_SZ && + memcmp( data+VERSION_OFFSET, zero_data, DEFAULT_PRIOR_VOTERS_OFFSET_1_14_11 ) ) ) { + return 1; + } + + return 0; +} diff --git a/src/flamenco/runtime/program/vote/fd_vote_state_versioned.h b/src/flamenco/runtime/program/vote/fd_vote_state_versioned.h new file mode 100644 index 00000000000..b5e3f800613 --- /dev/null +++ b/src/flamenco/runtime/program/vote/fd_vote_state_versioned.h @@ -0,0 +1,167 @@ +#ifndef HEADER_fd_src_flamenco_runtime_program_vote_fd_vote_state_versioned_h +#define HEADER_fd_src_flamenco_runtime_program_vote_fd_vote_state_versioned_h + +#include "../../fd_borrowed_account.h" +#include "../../../types/fd_types.h" + +/**********************************************************************/ +/* Getters */ +/**********************************************************************/ + +/* https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1074 */ +int +fd_vsv_get_state( fd_account_meta_t const * meta, + uchar * res ); + +fd_pubkey_t const * +fd_vsv_get_authorized_withdrawer( fd_vote_state_versioned_t * self ); + +/* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L717-L722 */ +uchar +fd_vsv_get_commission( fd_vote_state_versioned_t * self ); + +fd_vote_epoch_credits_t const * +fd_vsv_get_epoch_credits( fd_vote_state_versioned_t * self ); + +/* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L752-L757 */ +fd_landed_vote_t const * +fd_vsv_get_votes( fd_vote_state_versioned_t * self ); + +/* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L787-L792 */ +ulong const * +fd_vsv_get_last_voted_slot( fd_vote_state_versioned_t * self ); + +ulong const * +fd_vsv_get_root_slot( fd_vote_state_versioned_t * self ); + +fd_vote_block_timestamp_t const * +fd_vsv_get_last_timestamp( fd_vote_state_versioned_t * self ); + +/**********************************************************************/ +/* Mutable getters */ +/**********************************************************************/ + +/* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L815-L820 */ +fd_vote_epoch_credits_t * +fd_vsv_get_epoch_credits_mutable( fd_vote_state_versioned_t * self ); + +fd_landed_vote_t * +fd_vsv_get_votes_mutable( fd_vote_state_versioned_t * self ); + +/**********************************************************************/ +/* Setters */ +/**********************************************************************/ + +int +fd_vsv_set_state( fd_borrowed_account_t * self, + fd_vote_state_versioned_t * state ); + +/* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L673-L678 */ +void +fd_vsv_set_authorized_withdrawer( fd_vote_state_versioned_t * self, + fd_pubkey_t const * authorized_withdrawer ); + +/* Sets the authorized withdrawer for the appropriate vote state + version. Only supported for v3 and v4 vote states. */ +int +fd_vsv_set_new_authorized_voter( fd_exec_instr_ctx_t * ctx, + fd_vote_state_versioned_t * self, + fd_pubkey_t const * authorized_pubkey, + ulong current_epoch, + ulong target_epoch, + /* "verify" closure */ int authorized_withdrawer_signer, + /* "verify" closure */ fd_pubkey_t const * signers[static FD_TXN_SIG_MAX] ); + +/* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L738-L743 */ +void +fd_vsv_set_node_pubkey( fd_vote_state_versioned_t * self, + fd_pubkey_t const * node_pubkey ); + +/* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L745-L750 */ +void +fd_vsv_set_block_revenue_collector( fd_vote_state_versioned_t * self, + fd_pubkey_t const * block_revenue_collector ); + +/* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L724-L729 */ +void +fd_vsv_set_commission( fd_vote_state_versioned_t * self, + uchar commission ); + +/* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L386-L388 */ +void +fd_vsv_set_root_slot( fd_vote_state_versioned_t * self, ulong * root_slot ); + +/* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L843-L851 */ +int +fd_vsv_set_vote_account_state( fd_exec_instr_ctx_t const * ctx, + fd_borrowed_account_t * vote_account, + fd_vote_state_versioned_t * versioned, + uchar * vote_lockout_mem ); + +/**********************************************************************/ +/* General functions */ +/**********************************************************************/ + +/* https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v3.0.0/vote-interface/src/state/vote_state_v3.rs#L282-L309 */ +void +fd_vsv_increment_credits( fd_vote_state_versioned_t * self, + ulong epoch, + ulong credits ); + +/* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L159-L170 */ +int +fd_vsv_process_timestamp( fd_exec_instr_ctx_t * ctx, + fd_vote_state_versioned_t * self, + ulong slot, + long timestamp ); + +/* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L172-L180 */ +void +fd_vsv_pop_expired_votes( fd_vote_state_versioned_t * self, ulong next_vote_slot ); + +/* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L638-L651 */ +void +fd_vsv_process_next_vote_slot( fd_vote_state_versioned_t * self, + ulong next_vote_slot, + ulong epoch, + ulong current_slot ); + +/* https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v4.0.4/vote-interface/src/state/vote_state_versions.rs#L40-L98 */ +int +fd_vsv_try_convert_to_v3( fd_vote_state_versioned_t * self, + uchar * authorized_voters_mem, + uchar * landed_votes_mem ); + +/* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L966-L1007 */ +int +fd_vsv_try_convert_to_v4( fd_vote_state_versioned_t * self, + fd_pubkey_t const * vote_pubkey, + uchar * landed_votes_mem ); + +/* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L872C12-L886 */ +int +fd_vsv_deinitialize_vote_account_state( fd_exec_instr_ctx_t * ctx, + fd_borrowed_account_t * vote_account, + int target_version, + uchar * vote_lockout_mem ); + +/* This function is essentially just a call to get_state, additionally + erroring out if the account is a v_0_23_5 account. Initializes + a fd_vote_state_versioned_t struct in the vote_state_mem. + https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v4.0.4/vote-interface/src/state/vote_state_versions.rs#L195-L246 */ +int +fd_vsv_deserialize( fd_borrowed_account_t const * vote_account, + uchar * vote_state_mem ); + +/* https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v4.0.4/vote-interface/src/state/vote_state_versions.rs#L176-L187 */ +int +fd_vsv_is_uninitialized( fd_vote_state_versioned_t * self ); + +/* Returns 1 if the vote account is the correct size and initialized, + 0 otherwise. + https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v4.0.4/vote-interface/src/state/vote_state_versions.rs#L189-L193 */ +int +fd_vsv_is_correct_size_and_initialized( fd_account_meta_t const * meta ); + +#endif /* HEADER_fd_src_flamenco_runtime_program_vote_fd_vote_state_versioned_h */ + diff --git a/src/flamenco/runtime/tests/fd_block_harness.c b/src/flamenco/runtime/tests/fd_block_harness.c index 4eb51b80cf8..4da4a35cbe2 100644 --- a/src/flamenco/runtime/tests/fd_block_harness.c +++ b/src/flamenco/runtime/tests/fd_block_harness.c @@ -6,7 +6,7 @@ #include "../fd_txn_account.h" #include "../fd_runtime_stack.h" #include "../program/fd_stake_program.h" -#include "../program/fd_vote_program.h" +#include "../program/vote/fd_vote_state_versioned.h" #include "../sysvar/fd_sysvar_epoch_schedule.h" #include "../sysvar/fd_sysvar_rent.h" #include "../sysvar/fd_sysvar_recent_hashes.h" @@ -113,7 +113,7 @@ fd_solfuzz_block_register_vote_account( fd_funk_t * funk, } /* Account must be initialized correctly */ - if( FD_UNLIKELY( !fd_vote_state_versions_is_correct_and_initialized( acc->meta ) ) ) { + if( FD_UNLIKELY( !fd_vsv_is_correct_size_and_initialized( acc->meta ) ) ) { return; } @@ -214,8 +214,8 @@ fd_solfuzz_pb_block_update_prev_epoch_votes_cache( fd_vote_states_t * case fd_vote_state_versioned_enum_v1_14_11: epoch_credits = res->inner.v1_14_11.epoch_credits; break; - case fd_vote_state_versioned_enum_current: - epoch_credits = res->inner.current.epoch_credits; + case fd_vote_state_versioned_enum_v3: + epoch_credits = res->inner.v3.epoch_credits; break; default: __builtin_unreachable(); diff --git a/src/flamenco/runtime/tests/fd_dump_pb.c b/src/flamenco/runtime/tests/fd_dump_pb.c index 6a34994fd7a..4d8db182b84 100644 --- a/src/flamenco/runtime/tests/fd_dump_pb.c +++ b/src/flamenco/runtime/tests/fd_dump_pb.c @@ -468,9 +468,9 @@ create_synthetic_vote_account_from_vote_state( fd_vote_state_ele_t const * vot /* Construct the vote account data. Fill in missing fields with arbitrary defaults (since they're not used anyways) */ fd_vote_state_versioned_t vsv = { - .discriminant = fd_vote_state_versioned_enum_current, + .discriminant = fd_vote_state_versioned_enum_v3, .inner = { - .current = { + .v3 = { .node_pubkey = vote_state->node_account, .authorized_withdrawer = vote_state->node_account, .commission = vote_state->commission, @@ -483,7 +483,7 @@ create_synthetic_vote_account_from_vote_state( fd_vote_state_ele_t const * vot } } }; - fd_vote_state_t * synthetic_vote_state = &vsv.inner.current; + fd_vote_state_v3_t * synthetic_vote_state = &vsv.inner.v3; /* Create synthetic landed votes */ synthetic_vote_state->votes = deq_fd_landed_vote_t_join( diff --git a/src/flamenco/runtime/tests/test_dump_block.c b/src/flamenco/runtime/tests/test_dump_block.c index f27aeb12a34..3c3be955a5f 100644 --- a/src/flamenco/runtime/tests/test_dump_block.c +++ b/src/flamenco/runtime/tests/test_dump_block.c @@ -10,7 +10,7 @@ #include "../../stakes/fd_vote_states.h" #include "../../stakes/fd_stake_delegations.h" #include "../program/fd_stake_program.h" -#include "../program/fd_vote_program.h" +#include "../program/vote/fd_vote_state_versioned.h" #include "../../../ballet/nanopb/pb_decode.h" #include "../../accdb/fd_accdb_admin.h" #include "../../accdb/fd_accdb_impl_v1.h" @@ -238,7 +238,7 @@ register_vote_account_from_funk( fd_funk_t * funk, } /* Account must be initialized correctly */ - if( FD_UNLIKELY( !fd_vote_state_versions_is_correct_and_initialized( acc->meta ) ) ) { + if( FD_UNLIKELY( !fd_vsv_is_correct_size_and_initialized( acc->meta ) ) ) { return; } diff --git a/src/flamenco/stakes/fd_stakes.c b/src/flamenco/stakes/fd_stakes.c index b1d390b3c95..c4251d74ba6 100644 --- a/src/flamenco/stakes/fd_stakes.c +++ b/src/flamenco/stakes/fd_stakes.c @@ -1,7 +1,7 @@ #include "fd_stakes.h" #include "../runtime/fd_bank.h" #include "../runtime/program/fd_stake_program.h" -#include "../runtime/program/fd_vote_program.h" +#include "../runtime/program/vote/fd_vote_state_versioned.h" #include "../runtime/sysvar/fd_sysvar_stake_history.h" #include "fd_stake_delegations.h" #include "../accdb/fd_accdb_impl_v1.h" @@ -238,7 +238,7 @@ fd_stakes_update_vote_state( fd_pubkey_t const * pubkey, return; } - if( !fd_vote_state_versions_is_correct_and_initialized( meta ) ) { + if( !fd_vsv_is_correct_size_and_initialized( meta ) ) { fd_vote_states_remove( vote_states, pubkey ); fd_bank_vote_states_end_locking_modify( bank ); return; diff --git a/src/flamenco/stakes/fd_vote_states.c b/src/flamenco/stakes/fd_vote_states.c index fe4b6d40203..299877b74ba 100644 --- a/src/flamenco/stakes/fd_vote_states.c +++ b/src/flamenco/stakes/fd_vote_states.c @@ -286,11 +286,17 @@ fd_vote_states_update_from_account( fd_vote_states_t * vote_states, last_vote_timestamp = vsv->inner.v1_14_11.last_timestamp.timestamp; last_vote_slot = vsv->inner.v1_14_11.last_timestamp.slot; break; - case fd_vote_state_versioned_enum_current: - node_account = vsv->inner.current.node_pubkey; - commission = vsv->inner.current.commission; - last_vote_timestamp = vsv->inner.current.last_timestamp.timestamp; - last_vote_slot = vsv->inner.current.last_timestamp.slot; + case fd_vote_state_versioned_enum_v3: + node_account = vsv->inner.v3.node_pubkey; + commission = vsv->inner.v3.commission; + last_vote_timestamp = vsv->inner.v3.last_timestamp.timestamp; + last_vote_slot = vsv->inner.v3.last_timestamp.slot; + break; + case fd_vote_state_versioned_enum_v4: + node_account = vsv->inner.v4.node_pubkey; + commission = (uchar)fd_ushort_min( vsv->inner.v4.inflation_rewards_commission_bps/100, UCHAR_MAX ); + last_vote_timestamp = vsv->inner.v4.last_timestamp.timestamp; + last_vote_slot = vsv->inner.v4.last_timestamp.slot; break; default: __builtin_unreachable(); diff --git a/src/flamenco/types/fd_types.c b/src/flamenco/types/fd_types.c index dd962a7fa70..92327b7309c 100644 --- a/src/flamenco/types/fd_types.c +++ b/src/flamenco/types/fd_types.c @@ -1825,6 +1825,29 @@ void * fd_landed_vote_decode( void * mem, fd_bincode_decode_ctx_t * ctx ) { fd_landed_vote_decode_inner( mem, alloc_mem, ctx ); return self; } +int fd_bls_pubkey_compressed_encode( fd_bls_pubkey_compressed_t const * self, fd_bincode_encode_ctx_t * ctx ) { + int err; + err = fd_bincode_bytes_encode( self->buf, 48, ctx ); + if( FD_UNLIKELY( err ) ) return err; + return FD_BINCODE_SUCCESS; +} +static inline int fd_bls_pubkey_compressed_decode_footprint_inner( fd_bincode_decode_ctx_t * ctx, ulong * total_sz ) { + if( (ulong)ctx->data + 48UL > (ulong)ctx->dataend ) { return FD_BINCODE_ERR_OVERFLOW; }; + ctx->data = (void *)( (ulong)ctx->data + 48UL ); + return 0; +} +static void fd_bls_pubkey_compressed_decode_inner( void * struct_mem, void * * alloc_mem, fd_bincode_decode_ctx_t * ctx ) { + fd_bls_pubkey_compressed_t * self = (fd_bls_pubkey_compressed_t *)struct_mem; + fd_bincode_bytes_decode_unsafe( self->buf, 48, ctx ); +} +void * fd_bls_pubkey_compressed_decode( void * mem, fd_bincode_decode_ctx_t * ctx ) { + fd_bls_pubkey_compressed_t * self = (fd_bls_pubkey_compressed_t *)mem; + fd_bls_pubkey_compressed_new( self ); + void * alloc_region = (uchar *)mem + sizeof(fd_bls_pubkey_compressed_t); + void * * alloc_mem = &alloc_region; + fd_bls_pubkey_compressed_decode_inner( mem, alloc_mem, ctx ); + return self; +} int fd_vote_state_0_23_5_encode( fd_vote_state_0_23_5_t const * self, fd_bincode_encode_ctx_t * ctx ) { int err; err = fd_pubkey_encode( &self->node_pubkey, ctx ); @@ -2290,7 +2313,7 @@ ulong fd_vote_state_1_14_11_size( fd_vote_state_1_14_11_t const * self ) { return size; } -int fd_vote_state_encode( fd_vote_state_t const * self, fd_bincode_encode_ctx_t * ctx ) { +int fd_vote_state_v3_encode( fd_vote_state_v3_t const * self, fd_bincode_encode_ctx_t * ctx ) { int err; err = fd_pubkey_encode( &self->node_pubkey, ctx ); if( FD_UNLIKELY( err ) ) return err; @@ -2340,7 +2363,7 @@ int fd_vote_state_encode( fd_vote_state_t const * self, fd_bincode_encode_ctx_t if( FD_UNLIKELY( err ) ) return err; return FD_BINCODE_SUCCESS; } -static int fd_vote_state_decode_footprint_inner( fd_bincode_decode_ctx_t * ctx, ulong * total_sz ) { +static int fd_vote_state_v3_decode_footprint_inner( fd_bincode_decode_ctx_t * ctx, ulong * total_sz ) { if( ctx->data>=ctx->dataend ) { return FD_BINCODE_ERR_OVERFLOW; }; int err = 0; err = fd_pubkey_decode_footprint_inner( ctx, total_sz ); @@ -2384,16 +2407,16 @@ static int fd_vote_state_decode_footprint_inner( fd_bincode_decode_ctx_t * ctx, if( FD_UNLIKELY( err ) ) return err; return 0; } -int fd_vote_state_decode_footprint( fd_bincode_decode_ctx_t * ctx, ulong * total_sz ) { - *total_sz += sizeof(fd_vote_state_t); +int fd_vote_state_v3_decode_footprint( fd_bincode_decode_ctx_t * ctx, ulong * total_sz ) { + *total_sz += sizeof(fd_vote_state_v3_t); void const * start_data = ctx->data; - int err = fd_vote_state_decode_footprint_inner( ctx, total_sz ); + int err = fd_vote_state_v3_decode_footprint_inner( ctx, total_sz ); if( ctx->data>ctx->dataend ) { return FD_BINCODE_ERR_OVERFLOW; }; ctx->data = start_data; return err; } -static void fd_vote_state_decode_inner( void * struct_mem, void * * alloc_mem, fd_bincode_decode_ctx_t * ctx ) { - fd_vote_state_t * self = (fd_vote_state_t *)struct_mem; +static void fd_vote_state_v3_decode_inner( void * struct_mem, void * * alloc_mem, fd_bincode_decode_ctx_t * ctx ) { + fd_vote_state_v3_t * self = (fd_vote_state_v3_t *)struct_mem; fd_pubkey_decode_inner( &self->node_pubkey, alloc_mem, ctx ); fd_pubkey_decode_inner( &self->authorized_withdrawer, alloc_mem, ctx ); fd_bincode_uint8_decode_unsafe( &self->commission, ctx ); @@ -2427,23 +2450,23 @@ static void fd_vote_state_decode_inner( void * struct_mem, void * * alloc_mem, f } fd_vote_block_timestamp_decode_inner( &self->last_timestamp, alloc_mem, ctx ); } -void * fd_vote_state_decode( void * mem, fd_bincode_decode_ctx_t * ctx ) { - fd_vote_state_t * self = (fd_vote_state_t *)mem; - fd_vote_state_new( self ); - void * alloc_region = (uchar *)mem + sizeof(fd_vote_state_t); +void * fd_vote_state_v3_decode( void * mem, fd_bincode_decode_ctx_t * ctx ) { + fd_vote_state_v3_t * self = (fd_vote_state_v3_t *)mem; + fd_vote_state_v3_new( self ); + void * alloc_region = (uchar *)mem + sizeof(fd_vote_state_v3_t); void * * alloc_mem = &alloc_region; - fd_vote_state_decode_inner( mem, alloc_mem, ctx ); + fd_vote_state_v3_decode_inner( mem, alloc_mem, ctx ); return self; } -void fd_vote_state_new(fd_vote_state_t * self) { - fd_memset( self, 0, sizeof(fd_vote_state_t) ); +void fd_vote_state_v3_new(fd_vote_state_v3_t * self) { + fd_memset( self, 0, sizeof(fd_vote_state_v3_t) ); fd_pubkey_new( &self->node_pubkey ); fd_pubkey_new( &self->authorized_withdrawer ); fd_vote_authorized_voters_new( &self->authorized_voters ); fd_vote_prior_voters_new( &self->prior_voters ); fd_vote_block_timestamp_new( &self->last_timestamp ); } -ulong fd_vote_state_size( fd_vote_state_t const * self ) { +ulong fd_vote_state_v3_size( fd_vote_state_v3_t const * self ) { ulong size = 0; size += fd_pubkey_size( &self->node_pubkey ); size += fd_pubkey_size( &self->authorized_withdrawer ); @@ -2476,15 +2499,251 @@ ulong fd_vote_state_size( fd_vote_state_t const * self ) { return size; } +int fd_vote_state_v4_encode( fd_vote_state_v4_t const * self, fd_bincode_encode_ctx_t * ctx ) { + int err; + err = fd_pubkey_encode( &self->node_pubkey, ctx ); + if( FD_UNLIKELY( err ) ) return err; + err = fd_pubkey_encode( &self->authorized_withdrawer, ctx ); + if( FD_UNLIKELY( err ) ) return err; + err = fd_pubkey_encode( &self->inflation_rewards_collector, ctx ); + if( FD_UNLIKELY( err ) ) return err; + err = fd_pubkey_encode( &self->block_revenue_collector, ctx ); + if( FD_UNLIKELY( err ) ) return err; + err = fd_bincode_uint16_encode( self->inflation_rewards_commission_bps, ctx ); + if( FD_UNLIKELY( err ) ) return err; + err = fd_bincode_uint16_encode( self->block_revenue_commission_bps, ctx ); + if( FD_UNLIKELY( err ) ) return err; + err = fd_bincode_uint64_encode( self->pending_delegator_rewards, ctx ); + if( FD_UNLIKELY( err ) ) return err; + err = fd_bincode_bool_encode( self->has_bls_pubkey_compressed, ctx ); + if( FD_UNLIKELY( err ) ) return err; + if( self->has_bls_pubkey_compressed ) { + err = fd_bls_pubkey_compressed_encode( &self->bls_pubkey_compressed, ctx ); + if( FD_UNLIKELY( err ) ) return err; + } + if( self->votes ) { + ulong votes_len = deq_fd_landed_vote_t_cnt( self->votes ); + err = fd_bincode_uint64_encode( votes_len, ctx ); + if( FD_UNLIKELY( err ) ) return err; + for( deq_fd_landed_vote_t_iter_t iter = deq_fd_landed_vote_t_iter_init( self->votes ); !deq_fd_landed_vote_t_iter_done( self->votes, iter ); iter = deq_fd_landed_vote_t_iter_next( self->votes, iter ) ) { + fd_landed_vote_t const * ele = deq_fd_landed_vote_t_iter_ele_const( self->votes, iter ); + err = fd_landed_vote_encode( ele, ctx ); + if( FD_UNLIKELY( err ) ) return err; + } + } else { + ulong votes_len = 0; + err = fd_bincode_uint64_encode( votes_len, ctx ); + if( FD_UNLIKELY( err ) ) return err; + } + err = fd_bincode_bool_encode( self->has_root_slot, ctx ); + if( FD_UNLIKELY( err ) ) return err; + if( self->has_root_slot ) { + err = fd_bincode_uint64_encode( self->root_slot, ctx ); + if( FD_UNLIKELY( err ) ) return err; + } + err = fd_vote_authorized_voters_encode( &self->authorized_voters, ctx ); + if( FD_UNLIKELY( err ) ) return err; + if( self->epoch_credits ) { + ulong epoch_credits_len = deq_fd_vote_epoch_credits_t_cnt( self->epoch_credits ); + err = fd_bincode_uint64_encode( epoch_credits_len, ctx ); + if( FD_UNLIKELY( err ) ) return err; + for( deq_fd_vote_epoch_credits_t_iter_t iter = deq_fd_vote_epoch_credits_t_iter_init( self->epoch_credits ); !deq_fd_vote_epoch_credits_t_iter_done( self->epoch_credits, iter ); iter = deq_fd_vote_epoch_credits_t_iter_next( self->epoch_credits, iter ) ) { + fd_vote_epoch_credits_t const * ele = deq_fd_vote_epoch_credits_t_iter_ele_const( self->epoch_credits, iter ); + err = fd_vote_epoch_credits_encode( ele, ctx ); + if( FD_UNLIKELY( err ) ) return err; + } + } else { + ulong epoch_credits_len = 0; + err = fd_bincode_uint64_encode( epoch_credits_len, ctx ); + if( FD_UNLIKELY( err ) ) return err; + } + err = fd_vote_block_timestamp_encode( &self->last_timestamp, ctx ); + if( FD_UNLIKELY( err ) ) return err; + return FD_BINCODE_SUCCESS; +} +static int fd_vote_state_v4_decode_footprint_inner( fd_bincode_decode_ctx_t * ctx, ulong * total_sz ) { + if( ctx->data>=ctx->dataend ) { return FD_BINCODE_ERR_OVERFLOW; }; + int err = 0; + err = fd_pubkey_decode_footprint_inner( ctx, total_sz ); + if( FD_UNLIKELY( err ) ) return err; + err = fd_pubkey_decode_footprint_inner( ctx, total_sz ); + if( FD_UNLIKELY( err ) ) return err; + err = fd_pubkey_decode_footprint_inner( ctx, total_sz ); + if( FD_UNLIKELY( err ) ) return err; + err = fd_pubkey_decode_footprint_inner( ctx, total_sz ); + if( FD_UNLIKELY( err ) ) return err; + err = fd_bincode_uint16_decode_footprint( ctx ); + if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; + err = fd_bincode_uint16_decode_footprint( ctx ); + if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; + err = fd_bincode_uint64_decode_footprint( ctx ); + if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; + { + uchar o; + err = fd_bincode_bool_decode( &o, ctx ); + if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; + if( o ) { + err = fd_bls_pubkey_compressed_decode_footprint_inner( ctx, total_sz ); + if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; + } + } + ulong votes_len; + err = fd_bincode_uint64_decode( &votes_len, ctx ); + if( FD_UNLIKELY( err ) ) return err; + ulong votes_max = fd_ulong_max( votes_len, 32 ); + *total_sz += deq_fd_landed_vote_t_align() + deq_fd_landed_vote_t_footprint( votes_max ); + ulong votes_sz; + if( FD_UNLIKELY( __builtin_umull_overflow( votes_len, 13, &votes_sz ) ) ) return FD_BINCODE_ERR_UNDERFLOW; + err = fd_bincode_bytes_decode_footprint( votes_sz, ctx ); + if( FD_UNLIKELY( err ) ) return err; + { + uchar o; + err = fd_bincode_bool_decode( &o, ctx ); + if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; + if( o ) { + err = fd_bincode_uint64_decode_footprint( ctx ); + if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; + } + } + err = fd_vote_authorized_voters_decode_footprint_inner( ctx, total_sz ); + 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; + ulong epoch_credits_max = fd_ulong_max( epoch_credits_len, 64 ); + *total_sz += deq_fd_vote_epoch_credits_t_align() + deq_fd_vote_epoch_credits_t_footprint( epoch_credits_max ); + 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_footprint( epoch_credits_sz, ctx ); + if( FD_UNLIKELY( err ) ) return err; + err = fd_vote_block_timestamp_decode_footprint_inner( ctx, total_sz ); + if( FD_UNLIKELY( err ) ) return err; + return 0; +} +int fd_vote_state_v4_decode_footprint( fd_bincode_decode_ctx_t * ctx, ulong * total_sz ) { + *total_sz += sizeof(fd_vote_state_v4_t); + void const * start_data = ctx->data; + int err = fd_vote_state_v4_decode_footprint_inner( ctx, total_sz ); + if( ctx->data>ctx->dataend ) { return FD_BINCODE_ERR_OVERFLOW; }; + ctx->data = start_data; + return err; +} +static void fd_vote_state_v4_decode_inner( void * struct_mem, void * * alloc_mem, fd_bincode_decode_ctx_t * ctx ) { + fd_vote_state_v4_t * self = (fd_vote_state_v4_t *)struct_mem; + fd_pubkey_decode_inner( &self->node_pubkey, alloc_mem, ctx ); + fd_pubkey_decode_inner( &self->authorized_withdrawer, alloc_mem, ctx ); + fd_pubkey_decode_inner( &self->inflation_rewards_collector, alloc_mem, ctx ); + fd_pubkey_decode_inner( &self->block_revenue_collector, alloc_mem, ctx ); + fd_bincode_uint16_decode_unsafe( &self->inflation_rewards_commission_bps, ctx ); + fd_bincode_uint16_decode_unsafe( &self->block_revenue_commission_bps, ctx ); + fd_bincode_uint64_decode_unsafe( &self->pending_delegator_rewards, ctx ); + { + uchar o; + fd_bincode_bool_decode_unsafe( &o, ctx ); + self->has_bls_pubkey_compressed = !!o; + if( o ) { + fd_bls_pubkey_compressed_new( &self->bls_pubkey_compressed ); + fd_bls_pubkey_compressed_decode_inner( &self->bls_pubkey_compressed, alloc_mem, ctx ); + } + } + ulong votes_len; + fd_bincode_uint64_decode_unsafe( &votes_len, ctx ); + ulong votes_max = fd_ulong_max( votes_len, 32 ); + self->votes = deq_fd_landed_vote_t_join_new( alloc_mem, 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 ); + fd_landed_vote_decode_inner( elem, alloc_mem, ctx ); + } + { + uchar o; + fd_bincode_bool_decode_unsafe( &o, ctx ); + self->has_root_slot = !!o; + if( o ) { + fd_bincode_uint64_decode_unsafe( &self->root_slot, ctx ); + } + } + fd_vote_authorized_voters_decode_inner( &self->authorized_voters, alloc_mem, ctx ); + ulong epoch_credits_len; + fd_bincode_uint64_decode_unsafe( &epoch_credits_len, ctx ); + ulong epoch_credits_max = fd_ulong_max( epoch_credits_len, 64 ); + self->epoch_credits = deq_fd_vote_epoch_credits_t_join_new( alloc_mem, 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 ); + fd_vote_epoch_credits_decode_inner( elem, alloc_mem, ctx ); + } + fd_vote_block_timestamp_decode_inner( &self->last_timestamp, alloc_mem, ctx ); +} +void * fd_vote_state_v4_decode( void * mem, fd_bincode_decode_ctx_t * ctx ) { + fd_vote_state_v4_t * self = (fd_vote_state_v4_t *)mem; + fd_vote_state_v4_new( self ); + void * alloc_region = (uchar *)mem + sizeof(fd_vote_state_v4_t); + void * * alloc_mem = &alloc_region; + fd_vote_state_v4_decode_inner( mem, alloc_mem, ctx ); + return self; +} +void fd_vote_state_v4_new(fd_vote_state_v4_t * self) { + fd_memset( self, 0, sizeof(fd_vote_state_v4_t) ); + fd_pubkey_new( &self->node_pubkey ); + fd_pubkey_new( &self->authorized_withdrawer ); + fd_pubkey_new( &self->inflation_rewards_collector ); + fd_pubkey_new( &self->block_revenue_collector ); + fd_vote_authorized_voters_new( &self->authorized_voters ); + fd_vote_block_timestamp_new( &self->last_timestamp ); +} +ulong fd_vote_state_v4_size( fd_vote_state_v4_t const * self ) { + ulong size = 0; + size += fd_pubkey_size( &self->node_pubkey ); + size += fd_pubkey_size( &self->authorized_withdrawer ); + size += fd_pubkey_size( &self->inflation_rewards_collector ); + size += fd_pubkey_size( &self->block_revenue_collector ); + size += sizeof(ushort); + size += sizeof(ushort); + size += sizeof(ulong); + size += sizeof(char); + if( self->has_bls_pubkey_compressed ) { + size += fd_bls_pubkey_compressed_size( &self->bls_pubkey_compressed ); + } + if( self->votes ) { + size += sizeof(ulong); + for( deq_fd_landed_vote_t_iter_t iter = deq_fd_landed_vote_t_iter_init( self->votes ); !deq_fd_landed_vote_t_iter_done( self->votes, iter ); iter = deq_fd_landed_vote_t_iter_next( self->votes, iter ) ) { + fd_landed_vote_t * ele = deq_fd_landed_vote_t_iter_ele( self->votes, iter ); + size += fd_landed_vote_size( ele ); + } + } else { + size += sizeof(ulong); + } + size += sizeof(char); + if( self->has_root_slot ) { + size += sizeof(ulong); + } + size += fd_vote_authorized_voters_size( &self->authorized_voters ); + if( self->epoch_credits ) { + size += sizeof(ulong); + for( deq_fd_vote_epoch_credits_t_iter_t iter = deq_fd_vote_epoch_credits_t_iter_init( self->epoch_credits ); !deq_fd_vote_epoch_credits_t_iter_done( self->epoch_credits, iter ); iter = deq_fd_vote_epoch_credits_t_iter_next( self->epoch_credits, iter ) ) { + fd_vote_epoch_credits_t * ele = deq_fd_vote_epoch_credits_t_iter_ele( self->epoch_credits, iter ); + size += fd_vote_epoch_credits_size( ele ); + } + } else { + size += sizeof(ulong); + } + size += fd_vote_block_timestamp_size( &self->last_timestamp ); + return size; +} + FD_FN_PURE uchar fd_vote_state_versioned_is_v0_23_5(fd_vote_state_versioned_t const * self) { return self->discriminant == 0; } FD_FN_PURE uchar fd_vote_state_versioned_is_v1_14_11(fd_vote_state_versioned_t const * self) { return self->discriminant == 1; } -FD_FN_PURE uchar fd_vote_state_versioned_is_current(fd_vote_state_versioned_t const * self) { +FD_FN_PURE uchar fd_vote_state_versioned_is_v3(fd_vote_state_versioned_t const * self) { return self->discriminant == 2; } +FD_FN_PURE uchar fd_vote_state_versioned_is_v4(fd_vote_state_versioned_t const * self) { + return self->discriminant == 3; +} void fd_vote_state_versioned_inner_new( fd_vote_state_versioned_inner_t * self, uint discriminant ); int fd_vote_state_versioned_inner_decode_footprint( uint discriminant, fd_bincode_decode_ctx_t * ctx, ulong * total_sz ) { int err; @@ -2500,7 +2759,12 @@ int fd_vote_state_versioned_inner_decode_footprint( uint discriminant, fd_bincod return FD_BINCODE_SUCCESS; } case 2: { - err = fd_vote_state_decode_footprint_inner( ctx, total_sz ); + err = fd_vote_state_v3_decode_footprint_inner( ctx, total_sz ); + if( FD_UNLIKELY( err ) ) return err; + return FD_BINCODE_SUCCESS; + } + case 3: { + err = fd_vote_state_v4_decode_footprint_inner( ctx, total_sz ); if( FD_UNLIKELY( err ) ) return err; return FD_BINCODE_SUCCESS; } @@ -2533,7 +2797,11 @@ static void fd_vote_state_versioned_inner_decode_inner( fd_vote_state_versioned_ break; } case 2: { - fd_vote_state_decode_inner( &self->current, alloc_mem, ctx ); + fd_vote_state_v3_decode_inner( &self->v3, alloc_mem, ctx ); + break; + } + case 3: { + fd_vote_state_v4_decode_inner( &self->v4, alloc_mem, ctx ); break; } } @@ -2562,7 +2830,11 @@ void fd_vote_state_versioned_inner_new( fd_vote_state_versioned_inner_t * self, break; } case 2: { - fd_vote_state_new( &self->current ); + fd_vote_state_v3_new( &self->v3 ); + break; + } + case 3: { + fd_vote_state_v4_new( &self->v4 ); break; } default: break; // FD_LOG_ERR(( "unhandled type")); @@ -2590,7 +2862,11 @@ ulong fd_vote_state_versioned_size( fd_vote_state_versioned_t const * self ) { break; } case 2: { - size += fd_vote_state_size( &self->inner.current ); + size += fd_vote_state_v3_size( &self->inner.v3 ); + break; + } + case 3: { + size += fd_vote_state_v4_size( &self->inner.v4 ); break; } } @@ -2611,7 +2887,12 @@ int fd_vote_state_versioned_inner_encode( fd_vote_state_versioned_inner_t const break; } case 2: { - err = fd_vote_state_encode( &self->current, ctx ); + err = fd_vote_state_v3_encode( &self->v3, ctx ); + if( FD_UNLIKELY( err ) ) return err; + break; + } + case 3: { + err = fd_vote_state_v4_encode( &self->v4, ctx ); if( FD_UNLIKELY( err ) ) return err; break; } diff --git a/src/flamenco/types/fd_types.h b/src/flamenco/types/fd_types.h index 1453a786042..011283b8ed2 100644 --- a/src/flamenco/types/fd_types.h +++ b/src/flamenco/types/fd_types.h @@ -419,6 +419,14 @@ struct fd_landed_vote { typedef struct fd_landed_vote fd_landed_vote_t; #define FD_LANDED_VOTE_ALIGN alignof(fd_landed_vote_t) +/* https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v4.0.4/vote-interface/src/state/vote_state_v4.rs#L52-L56 */ +/* Encoded Size: Fixed (48 bytes) */ +struct fd_bls_pubkey_compressed { + uchar buf[48]; +}; +typedef struct fd_bls_pubkey_compressed fd_bls_pubkey_compressed_t; +#define FD_BLS_PUBKEY_COMPRESSED_ALIGN alignof(fd_bls_pubkey_compressed_t) + #define DEQUE_NAME deq_fd_vote_lockout_t #define DEQUE_T fd_vote_lockout_t #include "../../util/tmpl/fd_deque_dynamic.c" @@ -537,7 +545,7 @@ deq_fd_landed_vote_t_join_new( void * * alloc_mem, ulong max ) { /* https://github.com/solana-labs/solana/blob/8f2c8b8388a495d2728909e30460aa40dcc5d733/programs/vote/src/vote_state/mod.rs#L310 */ /* Encoded Size: Dynamic */ -struct fd_vote_state { +struct fd_vote_state_v3 { fd_pubkey_t node_pubkey; fd_pubkey_t authorized_withdrawer; uchar commission; @@ -549,13 +557,36 @@ struct fd_vote_state { 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; -#define FD_VOTE_STATE_ALIGN alignof(fd_vote_state_t) +typedef struct fd_vote_state_v3 fd_vote_state_v3_t; +#define FD_VOTE_STATE_V3_ALIGN alignof(fd_vote_state_v3_t) + +/* https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v4.0.4/vote-interface/src/state/vote_state_v4.rs#L30-L71 */ +/* Encoded Size: Dynamic */ +struct fd_vote_state_v4 { + fd_pubkey_t node_pubkey; + fd_pubkey_t authorized_withdrawer; + fd_pubkey_t inflation_rewards_collector; + fd_pubkey_t block_revenue_collector; + ushort inflation_rewards_commission_bps; + ushort block_revenue_commission_bps; + ulong pending_delegator_rewards; + fd_bls_pubkey_compressed_t bls_pubkey_compressed; + uchar has_bls_pubkey_compressed; + 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_epoch_credits_t * epoch_credits; /* fd_deque_dynamic (min cnt 64) */ + fd_vote_block_timestamp_t last_timestamp; +}; +typedef struct fd_vote_state_v4 fd_vote_state_v4_t; +#define FD_VOTE_STATE_V4_ALIGN alignof(fd_vote_state_v4_t) union fd_vote_state_versioned_inner { fd_vote_state_0_23_5_t v0_23_5; fd_vote_state_1_14_11_t v1_14_11; - fd_vote_state_t current; + fd_vote_state_v3_t v3; + fd_vote_state_v4_t v4; }; typedef union fd_vote_state_versioned_inner fd_vote_state_versioned_inner_t; @@ -1825,6 +1856,17 @@ static inline int fd_landed_vote_decode_footprint( fd_bincode_decode_ctx_t * ctx } void * fd_landed_vote_decode( void * mem, fd_bincode_decode_ctx_t * ctx ); +static inline void fd_bls_pubkey_compressed_new( fd_bls_pubkey_compressed_t * self ) { fd_memset( self, 0, sizeof(fd_bls_pubkey_compressed_t) ); } +int fd_bls_pubkey_compressed_encode( fd_bls_pubkey_compressed_t const * self, fd_bincode_encode_ctx_t * ctx ); +static inline ulong fd_bls_pubkey_compressed_size( fd_bls_pubkey_compressed_t const * self ) { (void)self; return 48UL; } +static inline ulong fd_bls_pubkey_compressed_align( void ) { return FD_BLS_PUBKEY_COMPRESSED_ALIGN; } +static inline int fd_bls_pubkey_compressed_decode_footprint( fd_bincode_decode_ctx_t * ctx, ulong * total_sz ) { + *total_sz += sizeof(fd_bls_pubkey_compressed_t); + if( (ulong)ctx->data + 48UL > (ulong)ctx->dataend ) { return FD_BINCODE_ERR_OVERFLOW; }; + return 0; +} +void * fd_bls_pubkey_compressed_decode( void * mem, fd_bincode_decode_ctx_t * ctx ); + void fd_vote_state_0_23_5_new( fd_vote_state_0_23_5_t * self ); int fd_vote_state_0_23_5_encode( fd_vote_state_0_23_5_t const * self, fd_bincode_encode_ctx_t * ctx ); ulong fd_vote_state_0_23_5_size( fd_vote_state_0_23_5_t const * self ); @@ -1846,12 +1888,19 @@ static inline ulong fd_vote_state_1_14_11_align( void ) { return FD_VOTE_STATE_1 int fd_vote_state_1_14_11_decode_footprint( fd_bincode_decode_ctx_t * ctx, ulong * total_sz ); void * fd_vote_state_1_14_11_decode( void * mem, fd_bincode_decode_ctx_t * ctx ); -void fd_vote_state_new( fd_vote_state_t * self ); -int fd_vote_state_encode( fd_vote_state_t const * self, fd_bincode_encode_ctx_t * ctx ); -ulong fd_vote_state_size( fd_vote_state_t const * self ); -static inline ulong fd_vote_state_align( void ) { return FD_VOTE_STATE_ALIGN; } -int fd_vote_state_decode_footprint( fd_bincode_decode_ctx_t * ctx, ulong * total_sz ); -void * fd_vote_state_decode( void * mem, fd_bincode_decode_ctx_t * ctx ); +void fd_vote_state_v3_new( fd_vote_state_v3_t * self ); +int fd_vote_state_v3_encode( fd_vote_state_v3_t const * self, fd_bincode_encode_ctx_t * ctx ); +ulong fd_vote_state_v3_size( fd_vote_state_v3_t const * self ); +static inline ulong fd_vote_state_v3_align( void ) { return FD_VOTE_STATE_V3_ALIGN; } +int fd_vote_state_v3_decode_footprint( fd_bincode_decode_ctx_t * ctx, ulong * total_sz ); +void * fd_vote_state_v3_decode( void * mem, fd_bincode_decode_ctx_t * ctx ); + +void fd_vote_state_v4_new( fd_vote_state_v4_t * self ); +int fd_vote_state_v4_encode( fd_vote_state_v4_t const * self, fd_bincode_encode_ctx_t * ctx ); +ulong fd_vote_state_v4_size( fd_vote_state_v4_t const * self ); +static inline ulong fd_vote_state_v4_align( void ) { return FD_VOTE_STATE_V4_ALIGN; } +int fd_vote_state_v4_decode_footprint( fd_bincode_decode_ctx_t * ctx, ulong * total_sz ); +void * fd_vote_state_v4_decode( void * mem, fd_bincode_decode_ctx_t * ctx ); void fd_vote_state_versioned_new_disc( fd_vote_state_versioned_t * self, uint discriminant ); void fd_vote_state_versioned_new( fd_vote_state_versioned_t * self ); @@ -1863,11 +1912,13 @@ void * fd_vote_state_versioned_decode( void * mem, fd_bincode_decode_ctx_t * ctx FD_FN_PURE uchar fd_vote_state_versioned_is_v0_23_5( fd_vote_state_versioned_t const * self ); FD_FN_PURE uchar fd_vote_state_versioned_is_v1_14_11( fd_vote_state_versioned_t const * self ); -FD_FN_PURE uchar fd_vote_state_versioned_is_current( fd_vote_state_versioned_t const * self ); +FD_FN_PURE uchar fd_vote_state_versioned_is_v3( fd_vote_state_versioned_t const * self ); +FD_FN_PURE uchar fd_vote_state_versioned_is_v4( fd_vote_state_versioned_t const * self ); enum { fd_vote_state_versioned_enum_v0_23_5 = 0, fd_vote_state_versioned_enum_v1_14_11 = 1, -fd_vote_state_versioned_enum_current = 2, +fd_vote_state_versioned_enum_v3 = 2, +fd_vote_state_versioned_enum_v4 = 3, }; void fd_vote_state_update_new( fd_vote_state_update_t * self ); int fd_vote_state_update_encode( fd_vote_state_update_t const * self, fd_bincode_encode_ctx_t * ctx ); diff --git a/src/flamenco/types/fd_types.json b/src/flamenco/types/fd_types.json index a9af4808d2c..8e6326daf82 100644 --- a/src/flamenco/types/fd_types.json +++ b/src/flamenco/types/fd_types.json @@ -327,6 +327,14 @@ ], "comment": "https://github.com/solana-labs/solana/blob/8f2c8b8388a495d2728909e30460aa40dcc5d733/programs/vote/src/vote_state/mod.rs#L268" }, + { + "name": "bls_pubkey_compressed", + "type": "struct", + "fields": [ + { "name": "buf", "type": "array", "element": "uchar", "length": "48" } + ], + "comment": "https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v4.0.4/vote-interface/src/state/vote_state_v4.rs#L52-L56" + }, { "name": "vote_state_0_23_5", "type": "struct", @@ -376,7 +384,7 @@ "comment": "https://github.com/solana-labs/solana/blob/8f2c8b8388a495d2728909e30460aa40dcc5d733/programs/vote/src/vote_state/mod.rs#L310" }, { - "name": "vote_state", + "name": "vote_state_v3", "type": "struct", "fields": [ { "name": "node_pubkey", "type": "pubkey" }, @@ -391,6 +399,26 @@ ], "comment": "https://github.com/solana-labs/solana/blob/8f2c8b8388a495d2728909e30460aa40dcc5d733/programs/vote/src/vote_state/mod.rs#L310" }, + { + "name": "vote_state_v4", + "type": "struct", + "fields": [ + { "name": "node_pubkey", "type": "pubkey" }, + { "name": "authorized_withdrawer", "type": "pubkey" }, + { "name": "inflation_rewards_collector", "type": "pubkey" }, + { "name": "block_revenue_collector", "type": "pubkey" }, + { "name": "inflation_rewards_commission_bps", "type": "ushort" }, + { "name": "block_revenue_commission_bps", "type": "ushort" }, + { "name": "pending_delegator_rewards", "type": "ulong" }, + { "name": "bls_pubkey_compressed", "type": "option", "element": "bls_pubkey_compressed", "flat": true }, + { "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": "epoch_credits", "type": "deque", "element": "vote_epoch_credits", "min": 64 }, + { "name": "last_timestamp", "type": "vote_block_timestamp" } + ], + "comment": "https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v4.0.4/vote-interface/src/state/vote_state_v4.rs#L30-L71" + }, { "name": "vote_state_versioned", "type": "enum", @@ -398,7 +426,8 @@ "variants": [ { "name": "v0_23_5", "type": "vote_state_0_23_5" }, { "name": "v1_14_11", "type": "vote_state_1_14_11" }, - { "name": "current", "type": "vote_state" } + { "name": "v3", "type": "vote_state_v3" }, + { "name": "v4", "type": "vote_state_v4"} ], "comment": "https://github.com/solana-labs/solana/blob/8f2c8b8388a495d2728909e30460aa40dcc5d733/programs/vote/src/vote_state/vote_state_versions.rs#L4" }, diff --git a/src/util/bits/fd_sat.h b/src/util/bits/fd_sat.h index 9c85a2edef9..636c27a7860 100644 --- a/src/util/bits/fd_sat.h +++ b/src/util/bits/fd_sat.h @@ -101,6 +101,18 @@ fd_uint_sat_sub( uint x, uint y ) { return fd_uint_if( cf, 0U, res ); } +FD_FN_CONST static inline ushort +fd_ushort_sat_add( ushort x, ushort y ) { + uint res = x+y; + return fd_ushort_if( res>USHORT_MAX, USHORT_MAX, (ushort)res ); +} + +FD_FN_CONST static inline ushort +fd_ushort_sat_mul( ushort x, ushort y ) { + uint res = x*y; + return fd_ushort_if( res>USHORT_MAX, USHORT_MAX, (ushort)res ); +} + FD_FN_CONST static inline double fd_double_sat_add( double x, double y ) { // What does rust do here? From 134a5714c5e4564409e45b463b49890df3ce44f1 Mon Sep 17 00:00:00 2001 From: Manik Jain Date: Fri, 19 Dec 2025 18:29:08 +0000 Subject: [PATCH 02/19] change unreachable to crit --- src/flamenco/runtime/program/fd_vote_program.c | 12 ++++++------ .../runtime/program/vote/fd_authorized_voters.c | 2 +- .../runtime/program/vote/fd_vote_state_versioned.c | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/flamenco/runtime/program/fd_vote_program.c b/src/flamenco/runtime/program/fd_vote_program.c index b096c399191..702cce86c35 100644 --- a/src/flamenco/runtime/program/fd_vote_program.c +++ b/src/flamenco/runtime/program/fd_vote_program.c @@ -80,7 +80,7 @@ get_vote_state_handler_checked( fd_borrowed_account_t const * vote_account, return FD_EXECUTOR_INSTR_SUCCESS; } default: - __builtin_unreachable(); + FD_LOG_CRIT(( "unsupported version: %d", target_version )); } } @@ -98,7 +98,7 @@ check_vote_account_length( fd_borrowed_account_t const * vote_account, expected = FD_VOTE_STATE_V4_SZ; break; default: - __builtin_unreachable(); + FD_LOG_CRIT(( "unsupported version: %d", target_version )); } if( FD_UNLIKELY( length!=expected ) ) { return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA; @@ -148,7 +148,7 @@ init_vote_account_state( fd_exec_instr_ctx_t * ctx, ); return fd_vote_state_v4_set_vote_account_state( ctx, vote_account, versioned ); default: - __builtin_unreachable(); + FD_LOG_CRIT(( "unsupported version: %d", target_version )); } } @@ -796,7 +796,7 @@ authorize( fd_exec_instr_ctx_t * ctx, break; } default: - __builtin_unreachable(); + FD_LOG_CRIT(( "unsupported vote_authorize discriminant: %u", vote_authorize.discriminant )); } /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L758 */ @@ -1763,7 +1763,7 @@ fd_vote_program_execute( fd_exec_instr_ctx_t * ctx ) { } else if( instruction->discriminant == fd_vote_instruction_enum_vote_switch ) { vote = &instruction->inner.vote_switch.vote; } else { - __builtin_unreachable(); + FD_LOG_CRIT(( "unsupported instruction discriminant: %u", instruction->discriminant )); } // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L155 @@ -1829,7 +1829,7 @@ fd_vote_program_execute( fd_exec_instr_ctx_t * ctx ) { vote_state_update = &instruction->inner.update_vote_state_switch.vote_state_update; break; default: - __builtin_unreachable(); + FD_LOG_CRIT(( "unsupported instruction discriminant: %u", instruction->discriminant )); } // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L171 diff --git a/src/flamenco/runtime/program/vote/fd_authorized_voters.c b/src/flamenco/runtime/program/vote/fd_authorized_voters.c index 19ee6af31f5..e8019bedb02 100644 --- a/src/flamenco/runtime/program/vote/fd_authorized_voters.c +++ b/src/flamenco/runtime/program/vote/fd_authorized_voters.c @@ -159,6 +159,6 @@ fd_authorized_voters_get_and_update_authorized_voter( fd_vote_state_versioned_t case fd_vote_state_versioned_enum_v4: return fd_vote_state_v4_get_and_update_authorized_voter( &self->inner.v4, current_epoch, pubkey ); default: - __builtin_unreachable(); + FD_LOG_CRIT(( "unsupported vote state versioned discriminant: %u", self->discriminant )); } } diff --git a/src/flamenco/runtime/program/vote/fd_vote_state_versioned.c b/src/flamenco/runtime/program/vote/fd_vote_state_versioned.c index 24d80027880..012f54301f5 100644 --- a/src/flamenco/runtime/program/vote/fd_vote_state_versioned.c +++ b/src/flamenco/runtime/program/vote/fd_vote_state_versioned.c @@ -724,7 +724,7 @@ fd_vsv_is_uninitialized( fd_vote_state_versioned_t * self ) { case fd_vote_state_versioned_enum_v4: return 0; // v4 vote states are always initialized default: - __builtin_unreachable(); + FD_LOG_CRIT(( "unsupported vote state versioned discriminant: %u", self->discriminant )); } } From c84c57d4bf7e094e23689eca084a10ece62db61e Mon Sep 17 00:00:00 2001 From: Manik Jain Date: Fri, 19 Dec 2025 18:32:35 +0000 Subject: [PATCH 03/19] zero check --- src/flamenco/runtime/program/vote/fd_vote_state_versioned.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/flamenco/runtime/program/vote/fd_vote_state_versioned.c b/src/flamenco/runtime/program/vote/fd_vote_state_versioned.c index 012f54301f5..a9bdfcc00b7 100644 --- a/src/flamenco/runtime/program/vote/fd_vote_state_versioned.c +++ b/src/flamenco/runtime/program/vote/fd_vote_state_versioned.c @@ -714,8 +714,7 @@ int fd_vsv_is_uninitialized( fd_vote_state_versioned_t * self ) { switch( self->discriminant ) { case fd_vote_state_versioned_enum_v0_23_5: { - fd_pubkey_t pubkey_default = { 0 }; - return !memcmp( &self->inner.v0_23_5.authorized_voter, &pubkey_default, sizeof( fd_pubkey_t ) ); + return fd_pubkey_check_zero( &self->inner.v0_23_5.authorized_voter ); } case fd_vote_state_versioned_enum_v1_14_11: return fd_authorized_voters_is_empty( &self->inner.v1_14_11.authorized_voters ); From b7a3468202c2a595ff8d5be930796159df3e1b21 Mon Sep 17 00:00:00 2001 From: Manik Jain Date: Fri, 19 Dec 2025 18:33:23 +0000 Subject: [PATCH 04/19] simplify --- src/flamenco/runtime/program/vote/fd_vote_state_versioned.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/flamenco/runtime/program/vote/fd_vote_state_versioned.c b/src/flamenco/runtime/program/vote/fd_vote_state_versioned.c index a9bdfcc00b7..b05504c052e 100644 --- a/src/flamenco/runtime/program/vote/fd_vote_state_versioned.c +++ b/src/flamenco/runtime/program/vote/fd_vote_state_versioned.c @@ -704,10 +704,7 @@ fd_vsv_deserialize( fd_borrowed_account_t const * vote_account, return FD_EXECUTOR_INSTR_ERR_UNINITIALIZED_ACCOUNT; } - int rc = fd_vsv_get_state( vote_account->meta, vote_state_mem ); - if( FD_UNLIKELY( rc ) ) return rc; - - return FD_EXECUTOR_INSTR_SUCCESS; + return fd_vsv_get_state( vote_account->meta, vote_state_mem ); } int From 84d442ff18ca6e136252f012ea9db9d86fe89c05 Mon Sep 17 00:00:00 2001 From: Manik Jain Date: Fri, 19 Dec 2025 18:36:11 +0000 Subject: [PATCH 05/19] move err to crit --- .../runtime/program/fd_vote_program.c | 6 ++-- .../program/vote/fd_authorized_voters.c | 4 +-- .../runtime/program/vote/fd_vote_state_v3.c | 2 +- .../runtime/program/vote/fd_vote_state_v4.c | 2 +- .../program/vote/fd_vote_state_versioned.c | 30 +++++++++---------- 5 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/flamenco/runtime/program/fd_vote_program.c b/src/flamenco/runtime/program/fd_vote_program.c index 702cce86c35..d2df487d757 100644 --- a/src/flamenco/runtime/program/fd_vote_program.c +++ b/src/flamenco/runtime/program/fd_vote_program.c @@ -707,8 +707,8 @@ process_new_vote_state( fd_exec_instr_ctx_t * ctx, // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L750 if( FD_LIKELY( has_timestamp ) ) { /* new_state asserted nonempty at function beginning */ - if( deq_fd_landed_vote_t_empty( new_state ) ) { - FD_LOG_ERR(( "Landed votes is empty" )); + if( FD_UNLIKELY( deq_fd_landed_vote_t_empty( new_state ) ) ) { + FD_LOG_CRIT(( "Landed votes is empty" )); } ulong last_slot = deq_fd_landed_vote_t_peek_tail( new_state )->lockout.slot; rc = fd_vsv_process_timestamp( ctx, versioned, last_slot, timestamp ); @@ -2058,7 +2058,7 @@ fd_vote_program_execute( fd_exec_instr_ctx_t * ctx ) { } default: - FD_LOG_ERR(( "unsupported vote instruction: %u", instruction->discriminant )); + FD_LOG_CRIT(( "unsupported vote instruction: %u", instruction->discriminant )); } return rc; diff --git a/src/flamenco/runtime/program/vote/fd_authorized_voters.c b/src/flamenco/runtime/program/vote/fd_authorized_voters.c index e8019bedb02..34dcff1370b 100644 --- a/src/flamenco/runtime/program/vote/fd_authorized_voters.c +++ b/src/flamenco/runtime/program/vote/fd_authorized_voters.c @@ -20,7 +20,7 @@ fd_authorized_voters_new( ulong epoch, authorized_voters->pool = fd_vote_authorized_voters_pool_join( fd_vote_authorized_voters_pool_new( pool_mem, FD_VOTE_AUTHORIZED_VOTERS_MIN ) ); authorized_voters->treap = fd_vote_authorized_voters_treap_join( fd_vote_authorized_voters_treap_new( treap_mem, FD_VOTE_AUTHORIZED_VOTERS_MIN ) ); if( 0 == fd_vote_authorized_voters_pool_free( authorized_voters->pool ) ) { - FD_LOG_ERR(( "Authorized_voter pool is empty" )); + FD_LOG_CRIT(( "Authorized_voter pool is empty" )); } fd_vote_authorized_voter_t * ele = fd_vote_authorized_voters_pool_ele_acquire( authorized_voters->pool ); ele->epoch = epoch; @@ -137,7 +137,7 @@ fd_authorized_voters_get_and_cache_authorized_voter_for_epoch( fd_vote_authorize if( !existed ) { /* insert cannot fail because !existed */ if( 0 == fd_vote_authorized_voters_pool_free( self->pool) ) { - FD_LOG_ERR(( "Authorized_voter pool is empty" )); + FD_LOG_CRIT(( "Authorized_voter pool is empty" )); } fd_vote_authorized_voter_t * ele = fd_vote_authorized_voters_pool_ele_acquire( self->pool ); ele->epoch = epoch; diff --git a/src/flamenco/runtime/program/vote/fd_vote_state_v3.c b/src/flamenco/runtime/program/vote/fd_vote_state_v3.c index ee518180c0a..feffe79ad08 100644 --- a/src/flamenco/runtime/program/vote/fd_vote_state_v3.c +++ b/src/flamenco/runtime/program/vote/fd_vote_state_v3.c @@ -203,7 +203,7 @@ fd_vote_state_v3_set_new_authorized_voter( fd_exec_instr_ctx_t * // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L822 if( 0 == fd_vote_authorized_voters_pool_free( self->authorized_voters.pool) ) { - FD_LOG_ERR(( "Authorized_voter pool is empty" )); + FD_LOG_CRIT(( "Authorized_voter pool is empty" )); } fd_vote_authorized_voter_t * ele = diff --git a/src/flamenco/runtime/program/vote/fd_vote_state_v4.c b/src/flamenco/runtime/program/vote/fd_vote_state_v4.c index 370e021a2d3..fd7d4cc37f3 100644 --- a/src/flamenco/runtime/program/vote/fd_vote_state_v4.c +++ b/src/flamenco/runtime/program/vote/fd_vote_state_v4.c @@ -102,7 +102,7 @@ fd_vote_state_v4_set_new_authorized_voter( fd_exec_instr_ctx_t * /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L474-L475 */ if( 0 == fd_vote_authorized_voters_pool_free( self->authorized_voters.pool) ) { - FD_LOG_ERR(( "Authorized_voter pool is empty" )); + FD_LOG_CRIT(( "Authorized_voter pool is empty" )); } fd_vote_authorized_voter_t * ele = diff --git a/src/flamenco/runtime/program/vote/fd_vote_state_versioned.c b/src/flamenco/runtime/program/vote/fd_vote_state_versioned.c index b05504c052e..9dfa94bc01a 100644 --- a/src/flamenco/runtime/program/vote/fd_vote_state_versioned.c +++ b/src/flamenco/runtime/program/vote/fd_vote_state_versioned.c @@ -34,7 +34,7 @@ last_lockout( fd_vote_state_versioned_t * self ) { votes = self->inner.v4.votes; break; default: - FD_LOG_ERR(( "unsupported vote state version: %u", self->discriminant )); + FD_LOG_CRIT(( "unsupported vote state version: %u", self->discriminant )); } if( deq_fd_landed_vote_t_empty( votes ) ) return NULL; @@ -83,7 +83,7 @@ fd_vsv_get_authorized_withdrawer( fd_vote_state_versioned_t * self ) { case fd_vote_state_versioned_enum_v4: return &self->inner.v4.authorized_withdrawer; default: - FD_LOG_ERR(( "unsupported vote state version: %u", self->discriminant )); + FD_LOG_CRIT(( "unsupported vote state version: %u", self->discriminant )); } } @@ -95,7 +95,7 @@ fd_vsv_get_commission( fd_vote_state_versioned_t * self ) { case fd_vote_state_versioned_enum_v4: return (uchar)(self->inner.v4.inflation_rewards_commission_bps/100); default: - FD_LOG_ERR(( "unsupported vote state version: %u", self->discriminant )); + FD_LOG_CRIT(( "unsupported vote state version: %u", self->discriminant )); } } @@ -126,7 +126,7 @@ fd_vsv_get_root_slot( fd_vote_state_versioned_t * self ) { if( !self->inner.v4.has_root_slot ) return NULL; return &self->inner.v4.root_slot; default: - FD_LOG_ERR(( "unsupported vote state version: %u", self->discriminant )); + FD_LOG_CRIT(( "unsupported vote state version: %u", self->discriminant )); } } @@ -138,7 +138,7 @@ fd_vsv_get_last_timestamp( fd_vote_state_versioned_t * self ) { case fd_vote_state_versioned_enum_v4: return &self->inner.v4.last_timestamp; default: - FD_LOG_ERR(( "unsupported vote state version: %u", self->discriminant )); + FD_LOG_CRIT(( "unsupported vote state version: %u", self->discriminant )); } } @@ -154,7 +154,7 @@ fd_vsv_get_epoch_credits_mutable( fd_vote_state_versioned_t * self ) { case fd_vote_state_versioned_enum_v4: return self->inner.v4.epoch_credits; default: - FD_LOG_ERR(( "unsupported vote state version: %u", self->discriminant )); + FD_LOG_CRIT(( "unsupported vote state version: %u", self->discriminant )); } } @@ -166,7 +166,7 @@ fd_vsv_get_votes_mutable( fd_vote_state_versioned_t * self ) { case fd_vote_state_versioned_enum_v4: return self->inner.v4.votes; default: - FD_LOG_ERR(( "unsupported vote state version: %u", self->discriminant )); + FD_LOG_CRIT(( "unsupported vote state version: %u", self->discriminant )); } } @@ -196,7 +196,7 @@ fd_vsv_set_state( fd_borrowed_account_t * self, .dataend = data + dlen }; do { int err = fd_vote_state_versioned_encode( state, &encode ); - if( FD_UNLIKELY( err ) ) FD_LOG_ERR(( "fd_vote_state_versioned_encode failed (%d)", err )); + if( FD_UNLIKELY( err ) ) FD_LOG_CRIT(( "fd_vote_state_versioned_encode failed (%d)", err )); } while(0); return FD_EXECUTOR_INSTR_SUCCESS; @@ -213,7 +213,7 @@ fd_vsv_set_vote_account_state( fd_exec_instr_ctx_t const * ctx, case fd_vote_state_versioned_enum_v4: return fd_vote_state_v4_set_vote_account_state( ctx, vote_account, versioned ); default: - FD_LOG_ERR(( "unsupported vote state version: %u", versioned->discriminant )); + FD_LOG_CRIT(( "unsupported vote state version: %u", versioned->discriminant )); } } @@ -230,7 +230,7 @@ fd_vsv_set_authorized_withdrawer( fd_vote_state_versioned_t * self, break; } default: - FD_LOG_ERR(( "unsupported vote state version: %u", self->discriminant )); + FD_LOG_CRIT(( "unsupported vote state version: %u", self->discriminant )); } } @@ -302,7 +302,7 @@ fd_vsv_set_commission( fd_vote_state_versioned_t * self, self->inner.v4.inflation_rewards_commission_bps = commission*100; break; default: - FD_LOG_ERR(( "unsupported vote state version: %u", self->discriminant )); + FD_LOG_CRIT(( "unsupported vote state version: %u", self->discriminant )); } } @@ -335,7 +335,7 @@ fd_vsv_set_last_timestamp( fd_vote_state_versioned_t * self, self->inner.v4.last_timestamp = *last_timestamp; break; default: - FD_LOG_ERR(( "unsupported vote state version: %u", self->discriminant )); + FD_LOG_CRIT(( "unsupported vote state version: %u", self->discriminant )); } } @@ -579,7 +579,7 @@ fd_vsv_try_convert_to_v3( fd_vote_state_versioned_t * self, case fd_vote_state_versioned_enum_v4: return FD_EXECUTOR_INSTR_ERR_INVALID_ARG; default: - FD_LOG_ERR(( "unsupported vote state version: %u", self->discriminant )); + FD_LOG_CRIT(( "unsupported vote state version: %u", self->discriminant )); } } @@ -648,7 +648,7 @@ fd_vsv_try_convert_to_v4( fd_vote_state_versioned_t * self, case fd_vote_state_versioned_enum_v4: return FD_EXECUTOR_INSTR_SUCCESS; default: - FD_LOG_ERR(( "unsupported vote state version: %u", self->discriminant )); + FD_LOG_CRIT(( "unsupported vote state version: %u", self->discriminant )); } } @@ -681,7 +681,7 @@ fd_vsv_deinitialize_vote_account_state( fd_exec_instr_ctx_t * ctx, return FD_EXECUTOR_INSTR_SUCCESS; } default: - FD_LOG_ERR(( "unsupported target version" )); + FD_LOG_CRIT(( "unsupported target version" )); } } From 049fb5e44d0d5a45c2cbfb42cbf4d944d32bb779 Mon Sep 17 00:00:00 2001 From: Manik Jain Date: Fri, 19 Dec 2025 18:40:47 +0000 Subject: [PATCH 06/19] fd unlikley --- src/flamenco/runtime/program/fd_vote_program.c | 2 +- src/flamenco/runtime/program/vote/fd_authorized_voters.c | 8 ++++---- src/flamenco/runtime/program/vote/fd_vote_state_v3.c | 4 ++-- src/flamenco/runtime/program/vote/fd_vote_state_v4.c | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/flamenco/runtime/program/fd_vote_program.c b/src/flamenco/runtime/program/fd_vote_program.c index d2df487d757..14d117a4a01 100644 --- a/src/flamenco/runtime/program/fd_vote_program.c +++ b/src/flamenco/runtime/program/fd_vote_program.c @@ -708,7 +708,7 @@ process_new_vote_state( fd_exec_instr_ctx_t * ctx, if( FD_LIKELY( has_timestamp ) ) { /* new_state asserted nonempty at function beginning */ if( FD_UNLIKELY( deq_fd_landed_vote_t_empty( new_state ) ) ) { - FD_LOG_CRIT(( "Landed votes is empty" )); + FD_LOG_CRIT(( "invariant violation: landed votes is empty" )); } ulong last_slot = deq_fd_landed_vote_t_peek_tail( new_state )->lockout.slot; rc = fd_vsv_process_timestamp( ctx, versioned, last_slot, timestamp ); diff --git a/src/flamenco/runtime/program/vote/fd_authorized_voters.c b/src/flamenco/runtime/program/vote/fd_authorized_voters.c index 34dcff1370b..02bfe4af4ac 100644 --- a/src/flamenco/runtime/program/vote/fd_authorized_voters.c +++ b/src/flamenco/runtime/program/vote/fd_authorized_voters.c @@ -19,8 +19,8 @@ fd_authorized_voters_new( ulong epoch, authorized_voters->pool = fd_vote_authorized_voters_pool_join( fd_vote_authorized_voters_pool_new( pool_mem, FD_VOTE_AUTHORIZED_VOTERS_MIN ) ); authorized_voters->treap = fd_vote_authorized_voters_treap_join( fd_vote_authorized_voters_treap_new( treap_mem, FD_VOTE_AUTHORIZED_VOTERS_MIN ) ); - if( 0 == fd_vote_authorized_voters_pool_free( authorized_voters->pool ) ) { - FD_LOG_CRIT(( "Authorized_voter pool is empty" )); + if( FD_UNLIKELY( !fd_vote_authorized_voters_pool_free( authorized_voters->pool ) ) ) { + FD_LOG_CRIT(( "invariant violation: max authorized voter count of vote account exceeded" )); } fd_vote_authorized_voter_t * ele = fd_vote_authorized_voters_pool_ele_acquire( authorized_voters->pool ); ele->epoch = epoch; @@ -136,8 +136,8 @@ fd_authorized_voters_get_and_cache_authorized_voter_for_epoch( fd_vote_authorize // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/authorized_voters.rs#L32 if( !existed ) { /* insert cannot fail because !existed */ - if( 0 == fd_vote_authorized_voters_pool_free( self->pool) ) { - FD_LOG_CRIT(( "Authorized_voter pool is empty" )); + if( FD_UNLIKELY( !fd_vote_authorized_voters_pool_free( self->pool ) ) ) { + FD_LOG_CRIT(( "invariant violation: max authorized voter count of vote account exceeded" )); } fd_vote_authorized_voter_t * ele = fd_vote_authorized_voters_pool_ele_acquire( self->pool ); ele->epoch = epoch; diff --git a/src/flamenco/runtime/program/vote/fd_vote_state_v3.c b/src/flamenco/runtime/program/vote/fd_vote_state_v3.c index feffe79ad08..4e9ce9b487c 100644 --- a/src/flamenco/runtime/program/vote/fd_vote_state_v3.c +++ b/src/flamenco/runtime/program/vote/fd_vote_state_v3.c @@ -202,8 +202,8 @@ fd_vote_state_v3_set_new_authorized_voter( fd_exec_instr_ctx_t * } // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L822 - if( 0 == fd_vote_authorized_voters_pool_free( self->authorized_voters.pool) ) { - FD_LOG_CRIT(( "Authorized_voter pool is empty" )); + if( FD_UNLIKELY( !fd_vote_authorized_voters_pool_free( self->authorized_voters.pool ) ) ) { + FD_LOG_CRIT(( "invariant violation: max authorized voter count of vote account exceeded" )); } fd_vote_authorized_voter_t * ele = diff --git a/src/flamenco/runtime/program/vote/fd_vote_state_v4.c b/src/flamenco/runtime/program/vote/fd_vote_state_v4.c index fd7d4cc37f3..d354543f504 100644 --- a/src/flamenco/runtime/program/vote/fd_vote_state_v4.c +++ b/src/flamenco/runtime/program/vote/fd_vote_state_v4.c @@ -101,8 +101,8 @@ fd_vote_state_v4_set_new_authorized_voter( fd_exec_instr_ctx_t * } /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L474-L475 */ - if( 0 == fd_vote_authorized_voters_pool_free( self->authorized_voters.pool) ) { - FD_LOG_CRIT(( "Authorized_voter pool is empty" )); + if( FD_UNLIKELY( !fd_vote_authorized_voters_pool_free( self->authorized_voters.pool ) ) ) { + FD_LOG_CRIT(( "invariant violation: max authorized voter count of vote account exceeded" )); } fd_vote_authorized_voter_t * ele = From 30520e4381f516fd936f4028b377b288145a1295 Mon Sep 17 00:00:00 2001 From: Manik Jain Date: Fri, 19 Dec 2025 18:43:25 +0000 Subject: [PATCH 07/19] fd pubkey eq --- src/flamenco/runtime/program/fd_vote_program.c | 4 +--- src/flamenco/runtime/program/vote/fd_vote_state_v3.c | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/flamenco/runtime/program/fd_vote_program.c b/src/flamenco/runtime/program/fd_vote_program.c index 14d117a4a01..5402d5a6953 100644 --- a/src/flamenco/runtime/program/fd_vote_program.c +++ b/src/flamenco/runtime/program/fd_vote_program.c @@ -1509,9 +1509,7 @@ fd_vote_program_execute( fd_exec_instr_ctx_t * ctx ) { /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_processor.rs#L64-L67 */ fd_guarded_borrowed_account_t me = {0}; FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( ctx, 0, &me ); - if( FD_UNLIKELY( 0 != memcmp( fd_borrowed_account_get_owner( &me ), - fd_solana_vote_program_id.key, - sizeof( fd_pubkey_t ) ) ) ) { + if( FD_UNLIKELY( !fd_pubkey_eq( fd_borrowed_account_get_owner( &me ), &fd_solana_vote_program_id ) ) ) { return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_OWNER; } diff --git a/src/flamenco/runtime/program/vote/fd_vote_state_v3.c b/src/flamenco/runtime/program/vote/fd_vote_state_v3.c index 4e9ce9b487c..6a29b5fe51c 100644 --- a/src/flamenco/runtime/program/vote/fd_vote_state_v3.c +++ b/src/flamenco/runtime/program/vote/fd_vote_state_v3.c @@ -178,7 +178,7 @@ fd_vote_state_v3_set_new_authorized_voter( fd_exec_instr_ctx_t * fd_pubkey_t * latest_authorized_pubkey = &latest_authorized->pubkey; // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L799 - if( 0 != memcmp( latest_authorized_pubkey, authorized_pubkey, sizeof( fd_pubkey_t ) ) ) { + if( fd_pubkey_eq( latest_authorized_pubkey, authorized_pubkey ) ) { fd_vote_prior_voters_t * prior_voters = &self->prior_voters; // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L801 From 252fa759bdc9e9745905b5223a6857a425efc71d Mon Sep 17 00:00:00 2001 From: Manik Jain Date: Mon, 22 Dec 2025 17:34:44 +0000 Subject: [PATCH 08/19] fix --- src/flamenco/runtime/program/vote/fd_vote_state_v3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/flamenco/runtime/program/vote/fd_vote_state_v3.c b/src/flamenco/runtime/program/vote/fd_vote_state_v3.c index 6a29b5fe51c..049b332152d 100644 --- a/src/flamenco/runtime/program/vote/fd_vote_state_v3.c +++ b/src/flamenco/runtime/program/vote/fd_vote_state_v3.c @@ -178,7 +178,7 @@ fd_vote_state_v3_set_new_authorized_voter( fd_exec_instr_ctx_t * fd_pubkey_t * latest_authorized_pubkey = &latest_authorized->pubkey; // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L799 - if( fd_pubkey_eq( latest_authorized_pubkey, authorized_pubkey ) ) { + if( !fd_pubkey_eq( latest_authorized_pubkey, authorized_pubkey ) ) { fd_vote_prior_voters_t * prior_voters = &self->prior_voters; // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L801 From c1de0c9985949411bcd5204b0d78888add127ce5 Mon Sep 17 00:00:00 2001 From: Manik Jain Date: Mon, 22 Dec 2025 17:35:58 +0000 Subject: [PATCH 09/19] type --- src/flamenco/runtime/program/vote/fd_vote_common.c | 2 +- src/flamenco/runtime/program/vote/fd_vote_common.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/flamenco/runtime/program/vote/fd_vote_common.c b/src/flamenco/runtime/program/vote/fd_vote_common.c index 654a424719e..6da4dfbb362 100644 --- a/src/flamenco/runtime/program/vote/fd_vote_common.c +++ b/src/flamenco/runtime/program/vote/fd_vote_common.c @@ -57,7 +57,7 @@ fd_vote_credits_for_vote_at_index( fd_landed_vote_t const * votes, return credits; } -uchar +int fd_vote_contains_slot( fd_landed_vote_t const * votes, ulong slot ) { /* Logic is copied from slice::binary_search_by() in Rust. While not fully optimized, diff --git a/src/flamenco/runtime/program/vote/fd_vote_common.h b/src/flamenco/runtime/program/vote/fd_vote_common.h index 3145b141133..6c46b55c8a5 100644 --- a/src/flamenco/runtime/program/vote/fd_vote_common.h +++ b/src/flamenco/runtime/program/vote/fd_vote_common.h @@ -24,7 +24,7 @@ fd_vote_credits_for_vote_at_index( fd_landed_vote_t const * votes, ulong index ); /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L773-L778 */ -uchar +int fd_vote_contains_slot( fd_landed_vote_t const * votes, ulong slot ); From 723014f926ecf2c6ec4906869aa1082d6cf3893c Mon Sep 17 00:00:00 2001 From: Manik Jain Date: Mon, 22 Dec 2025 17:37:32 +0000 Subject: [PATCH 10/19] style --- src/flamenco/runtime/program/vote/fd_vote_common.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/flamenco/runtime/program/vote/fd_vote_common.c b/src/flamenco/runtime/program/vote/fd_vote_common.c index 6da4dfbb362..d41b4406898 100644 --- a/src/flamenco/runtime/program/vote/fd_vote_common.c +++ b/src/flamenco/runtime/program/vote/fd_vote_common.c @@ -31,18 +31,19 @@ fd_vote_credits_for_vote_at_index( fd_landed_vote_t const * votes, fd_landed_vote_t const * landed_vote = deq_fd_landed_vote_t_peek_index_const( votes, index ); ulong latency = landed_vote == NULL ? 0 : landed_vote->latency; // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L683 - ulong max_credits = VOTE_CREDITS_MAXIMUM_PER_SLOT; + ulong max_credits = VOTE_CREDITS_MAXIMUM_PER_SLOT; - // If latency is 0, this means that the Lockout was created and stored from a software version - // that did not store vote latencies; in this case, 1 credit is awarded - // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L691 - if( FD_UNLIKELY( latency == 0 ) ) { + /* If latency is 0, this means that the Lockout was created and stored + from a software version that did not store vote latencies; in this + case, 1 credit is awarded. + https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L691 */ + if( FD_UNLIKELY( latency==0UL ) ) { return 1; } ulong diff = 0; int cf = fd_ulong_checked_sub( latency, VOTE_CREDITS_GRACE_SLOTS, &diff ); - if( cf != 0 || diff == 0 ) { + if( cf || diff==0UL ) { // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L697 return max_credits; } From 3acc6da3dfe059df1ffbf3588df6c162d81577d2 Mon Sep 17 00:00:00 2001 From: Manik Jain Date: Mon, 22 Dec 2025 18:34:13 +0000 Subject: [PATCH 11/19] stray include --- src/flamenco/runtime/program/vote/fd_vote_state_v4.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/flamenco/runtime/program/vote/fd_vote_state_v4.h b/src/flamenco/runtime/program/vote/fd_vote_state_v4.h index f552a52e856..9960eed0b66 100644 --- a/src/flamenco/runtime/program/vote/fd_vote_state_v4.h +++ b/src/flamenco/runtime/program/vote/fd_vote_state_v4.h @@ -2,9 +2,6 @@ #define HEADER_fd_src_flamenco_runtime_program_vote_fd_vote_state_v4_h #include "../../fd_borrowed_account.h" -#include "../../fd_executor.h" -#include "../../../types/fd_types.h" -#include "../../sysvar/fd_sysvar.h" /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L600-L619 */ void From b78794eca68747fcaa7ed1e70781217117d7fecf Mon Sep 17 00:00:00 2001 From: Manik Jain Date: Mon, 22 Dec 2025 18:57:51 +0000 Subject: [PATCH 12/19] prototype guards --- src/flamenco/runtime/program/vote/fd_authorized_voters.h | 4 ++++ src/flamenco/runtime/program/vote/fd_vote_common.h | 4 ++++ src/flamenco/runtime/program/vote/fd_vote_lockout.h | 4 ++++ src/flamenco/runtime/program/vote/fd_vote_state_v3.h | 4 ++++ src/flamenco/runtime/program/vote/fd_vote_state_v4.h | 4 ++++ src/flamenco/runtime/program/vote/fd_vote_state_versioned.h | 4 ++++ 6 files changed, 24 insertions(+) diff --git a/src/flamenco/runtime/program/vote/fd_authorized_voters.h b/src/flamenco/runtime/program/vote/fd_authorized_voters.h index 47e72577d3d..d0905ae04db 100644 --- a/src/flamenco/runtime/program/vote/fd_authorized_voters.h +++ b/src/flamenco/runtime/program/vote/fd_authorized_voters.h @@ -4,6 +4,8 @@ #include "../../../types/fd_types.h" #include "../../../../util/fd_util_base.h" +FD_PROTOTYPES_BEGIN + // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/authorized_voters.rs#L17 fd_vote_authorized_voters_t * fd_authorized_voters_new( ulong epoch, @@ -47,5 +49,7 @@ fd_authorized_voters_get_and_update_authorized_voter( fd_vote_state_versioned_t ulong current_epoch, fd_pubkey_t ** pubkey /* out */ ); +FD_PROTOTYPES_END + #endif /* HEADER_fd_src_flamenco_runtime_program_vote_fd_authorized_voters_h */ diff --git a/src/flamenco/runtime/program/vote/fd_vote_common.h b/src/flamenco/runtime/program/vote/fd_vote_common.h index 6c46b55c8a5..ab9d901a81e 100644 --- a/src/flamenco/runtime/program/vote/fd_vote_common.h +++ b/src/flamenco/runtime/program/vote/fd_vote_common.h @@ -4,6 +4,8 @@ #include "../../../types/fd_types.h" #include "../../fd_executor.h" +FD_PROTOTYPES_BEGIN + // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L985 int fd_vote_verify_authorized_signer( fd_pubkey_t const * authorized, @@ -28,5 +30,7 @@ int fd_vote_contains_slot( fd_landed_vote_t const * votes, ulong slot ); +FD_PROTOTYPES_END + #endif /* HEADER_fd_src_flamenco_runtime_program_vote_fd_vote_common_h */ diff --git a/src/flamenco/runtime/program/vote/fd_vote_lockout.h b/src/flamenco/runtime/program/vote/fd_vote_lockout.h index 61a1bcbd01e..9b762c38210 100644 --- a/src/flamenco/runtime/program/vote/fd_vote_lockout.h +++ b/src/flamenco/runtime/program/vote/fd_vote_lockout.h @@ -4,6 +4,8 @@ #include "../../../types/fd_types.h" #include "../../../../util/fd_util_base.h" +FD_PROTOTYPES_BEGIN + // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L104 ulong fd_vote_lockout_get_lockout( fd_vote_lockout_t * self ); @@ -25,5 +27,7 @@ fd_landed_vote_t * fd_vote_lockout_landed_votes_from_lockouts( fd_vote_lockout_t * lockouts, uchar * mem ); +FD_PROTOTYPES_END + #endif /* HEADER_fd_src_flamenco_runtime_program_vote_fd_vote_lockout_h */ diff --git a/src/flamenco/runtime/program/vote/fd_vote_state_v3.h b/src/flamenco/runtime/program/vote/fd_vote_state_v3.h index 8691adf8121..c288a17852a 100644 --- a/src/flamenco/runtime/program/vote/fd_vote_state_v3.h +++ b/src/flamenco/runtime/program/vote/fd_vote_state_v3.h @@ -6,6 +6,8 @@ #include "../../../types/fd_types.h" #include "../../sysvar/fd_sysvar.h" +FD_PROTOTYPES_BEGIN + /* https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v4.0.4/vote-interface/src/state/vote_state_v3.rs#L65-L73 */ void fd_vote_program_v3_create_new( fd_vote_init_t * const vote_init, @@ -47,5 +49,7 @@ fd_vote_state_v3_set_new_authorized_voter( fd_exec_instr_ctx_t * /* "verify" closure */ int authorized_withdrawer_signer, /* "verify" closure */ fd_pubkey_t const * signers[static FD_TXN_SIG_MAX] ); +FD_PROTOTYPES_END + #endif /* HEADER_fd_src_flamenco_runtime_program_vote_fd_vote_state_v3_h */ diff --git a/src/flamenco/runtime/program/vote/fd_vote_state_v4.h b/src/flamenco/runtime/program/vote/fd_vote_state_v4.h index 9960eed0b66..c8ef3ef4e5b 100644 --- a/src/flamenco/runtime/program/vote/fd_vote_state_v4.h +++ b/src/flamenco/runtime/program/vote/fd_vote_state_v4.h @@ -3,6 +3,8 @@ #include "../../fd_borrowed_account.h" +FD_PROTOTYPES_BEGIN + /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L600-L619 */ void fd_vote_state_v4_create_new( fd_pubkey_t const * vote_pubkey, @@ -33,5 +35,7 @@ fd_vote_state_v4_set_new_authorized_voter( fd_exec_instr_ctx_t * /* "verify" closure */ int authorized_withdrawer_signer, /* "verify" closure */ fd_pubkey_t const * signers[static FD_TXN_SIG_MAX] ); +FD_PROTOTYPES_END + #endif /* HEADER_fd_src_flamenco_runtime_program_vote_fd_vote_state_v4_h */ diff --git a/src/flamenco/runtime/program/vote/fd_vote_state_versioned.h b/src/flamenco/runtime/program/vote/fd_vote_state_versioned.h index b5e3f800613..00a1e8edc0c 100644 --- a/src/flamenco/runtime/program/vote/fd_vote_state_versioned.h +++ b/src/flamenco/runtime/program/vote/fd_vote_state_versioned.h @@ -4,6 +4,8 @@ #include "../../fd_borrowed_account.h" #include "../../../types/fd_types.h" +FD_PROTOTYPES_BEGIN + /**********************************************************************/ /* Getters */ /**********************************************************************/ @@ -163,5 +165,7 @@ fd_vsv_is_uninitialized( fd_vote_state_versioned_t * self ); int fd_vsv_is_correct_size_and_initialized( fd_account_meta_t const * meta ); +FD_PROTOTYPES_END + #endif /* HEADER_fd_src_flamenco_runtime_program_vote_fd_vote_state_versioned_h */ From b753f692e62aaaad3d0d1d49750ef21b5a9ca805 Mon Sep 17 00:00:00 2001 From: Manik Jain Date: Mon, 22 Dec 2025 19:25:30 +0000 Subject: [PATCH 13/19] comments --- src/flamenco/runtime/program/fd_vote_program.h | 1 - .../runtime/program/vote/fd_authorized_voters.c | 11 ----------- .../runtime/program/vote/fd_authorized_voters.h | 16 +++++++++------- .../runtime/program/vote/fd_vote_common.c | 1 - .../runtime/program/vote/fd_vote_common.h | 7 ++++--- .../runtime/program/vote/fd_vote_state_v3.c | 14 ++------------ .../runtime/program/vote/fd_vote_state_v4.c | 6 ------ .../runtime/program/vote/fd_vote_state_v4.h | 2 ++ .../program/vote/fd_vote_state_versioned.c | 13 +++++-------- .../program/vote/fd_vote_state_versioned.h | 6 ++++++ 10 files changed, 28 insertions(+), 49 deletions(-) diff --git a/src/flamenco/runtime/program/fd_vote_program.h b/src/flamenco/runtime/program/fd_vote_program.h index cd33a93b2b8..b399493d806 100644 --- a/src/flamenco/runtime/program/fd_vote_program.h +++ b/src/flamenco/runtime/program/fd_vote_program.h @@ -129,7 +129,6 @@ FD_PROTOTYPES_BEGIN /* fd_vote_program_execute is the instruction processing entrypoint for the vote program. */ - int fd_vote_program_execute( fd_exec_instr_ctx_t * ctx ); diff --git a/src/flamenco/runtime/program/vote/fd_authorized_voters.c b/src/flamenco/runtime/program/vote/fd_authorized_voters.c index 02bfe4af4ac..66ea4794808 100644 --- a/src/flamenco/runtime/program/vote/fd_authorized_voters.c +++ b/src/flamenco/runtime/program/vote/fd_authorized_voters.c @@ -2,11 +2,6 @@ #include "fd_vote_state_v3.h" #include "fd_vote_state_v4.h" -/**********************************************************************/ -/* impl AuthorizedVoters */ -/**********************************************************************/ - -// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/authorized_voters.rs#L17 fd_vote_authorized_voters_t * fd_authorized_voters_new( ulong epoch, fd_pubkey_t const * pubkey, @@ -30,7 +25,6 @@ fd_authorized_voters_new( ulong epoch, return authorized_voters; } -// Helper to create an empty AuthorizedVoters structure (for default/uninitialized states) fd_vote_authorized_voters_t * fd_authorized_voters_new_empty( uchar * mem ) { FD_SCRATCH_ALLOC_INIT( l, mem ); @@ -48,13 +42,11 @@ fd_authorized_voters_is_empty( fd_vote_authorized_voters_t * self ) { return fd_vote_authorized_voters_treap_ele_cnt( self->treap ) == 0; } -// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/authorized_voters.rs#L80 int fd_authorized_voters_contains( fd_vote_authorized_voters_t * self, ulong epoch ) { return !!fd_vote_authorized_voters_treap_ele_query( self->treap, epoch, self->pool ); } -// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/authorized_voters.rs#L72 fd_vote_authorized_voter_t * fd_authorized_voters_last( fd_vote_authorized_voters_t * self ) { fd_vote_authorized_voters_treap_rev_iter_t iter = @@ -62,7 +54,6 @@ fd_authorized_voters_last( fd_vote_authorized_voters_t * self ) { return fd_vote_authorized_voters_treap_rev_iter_ele( iter, self->pool ); } -// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/authorized_voters.rs#L43 void fd_authorized_voters_purge_authorized_voters( fd_vote_authorized_voters_t * self, ulong current_epoch ) { @@ -92,7 +83,6 @@ fd_authorized_voters_purge_authorized_voters( fd_vote_authorized_voters_t * self } -// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/authorized_voters.rs#L91 fd_vote_authorized_voter_t * fd_authorized_voters_get_or_calculate_authorized_voter_for_epoch( fd_vote_authorized_voters_t * self, ulong epoch, @@ -124,7 +114,6 @@ fd_authorized_voters_get_or_calculate_authorized_voter_for_epoch( fd_vote_author return res; } -// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/authorized_voters.rs#L28 fd_vote_authorized_voter_t * fd_authorized_voters_get_and_cache_authorized_voter_for_epoch( fd_vote_authorized_voters_t * self, ulong epoch ) { diff --git a/src/flamenco/runtime/program/vote/fd_authorized_voters.h b/src/flamenco/runtime/program/vote/fd_authorized_voters.h index d0905ae04db..fe3c0f3b1ef 100644 --- a/src/flamenco/runtime/program/vote/fd_authorized_voters.h +++ b/src/flamenco/runtime/program/vote/fd_authorized_voters.h @@ -4,41 +4,43 @@ #include "../../../types/fd_types.h" #include "../../../../util/fd_util_base.h" +/* fd_authorized_voters mirrors Agave's AuthorizedVoters methods. */ + FD_PROTOTYPES_BEGIN -// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/authorized_voters.rs#L17 +/* https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/authorized_voters.rs#L17 */ fd_vote_authorized_voters_t * fd_authorized_voters_new( ulong epoch, fd_pubkey_t const * pubkey, uchar * mem ); -// Helper to create an empty AuthorizedVoters structure (for default/uninitialized states) +/* Helper to create an empty AuthorizedVoters structure (for default/uninitialized states) */ fd_vote_authorized_voters_t * fd_authorized_voters_new_empty( uchar * mem ); int fd_authorized_voters_is_empty( fd_vote_authorized_voters_t * self ); -// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/authorized_voters.rs#L80 +/* https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/authorized_voters.rs#L80 */ int fd_authorized_voters_contains( fd_vote_authorized_voters_t * self, ulong epoch ); -// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/authorized_voters.rs#L72 +/* https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/authorized_voters.rs#L72 */ fd_vote_authorized_voter_t * fd_authorized_voters_last( fd_vote_authorized_voters_t * self ); -// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/authorized_voters.rs#L43 +/* https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/authorized_voters.rs#L43 */ void fd_authorized_voters_purge_authorized_voters( fd_vote_authorized_voters_t * self, ulong current_epoch ); -// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/authorized_voters.rs#L91 +/* https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/authorized_voters.rs#L91 */ fd_vote_authorized_voter_t * fd_authorized_voters_get_or_calculate_authorized_voter_for_epoch( fd_vote_authorized_voters_t * self, ulong epoch, int * existed ); -// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/authorized_voters.rs#L28 +/* https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/authorized_voters.rs#L28 */ fd_vote_authorized_voter_t * fd_authorized_voters_get_and_cache_authorized_voter_for_epoch( fd_vote_authorized_voters_t * self, ulong epoch ); diff --git a/src/flamenco/runtime/program/vote/fd_vote_common.c b/src/flamenco/runtime/program/vote/fd_vote_common.c index d41b4406898..0d802305434 100644 --- a/src/flamenco/runtime/program/vote/fd_vote_common.c +++ b/src/flamenco/runtime/program/vote/fd_vote_common.c @@ -18,7 +18,6 @@ fd_vote_signature_verify( fd_pubkey_t * epoch_authorized_voter, return authorized_withdrawer_signer ? 0 : fd_vote_verify_authorized_signer( epoch_authorized_voter, signers ); } -// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L668 uchar fd_vote_compute_vote_latency( ulong voted_for_slot, ulong current_slot ) { return (uchar)fd_ulong_min( fd_ulong_sat_sub( current_slot, voted_for_slot ), UCHAR_MAX ); diff --git a/src/flamenco/runtime/program/vote/fd_vote_common.h b/src/flamenco/runtime/program/vote/fd_vote_common.h index ab9d901a81e..e31f4af9da9 100644 --- a/src/flamenco/runtime/program/vote/fd_vote_common.h +++ b/src/flamenco/runtime/program/vote/fd_vote_common.h @@ -6,21 +6,22 @@ FD_PROTOTYPES_BEGIN -// https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L985 +/* https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L985 */ int fd_vote_verify_authorized_signer( fd_pubkey_t const * authorized, fd_pubkey_t const * signers[static FD_TXN_SIG_MAX] ); -// lambda function: https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L873 +/* lambda function: https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L873 */ int fd_vote_signature_verify( fd_pubkey_t * epoch_authorized_voter, int authorized_withdrawer_signer, fd_pubkey_t const * signers[static FD_TXN_SIG_MAX] ); +/* https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L668 */ uchar fd_vote_compute_vote_latency( ulong voted_for_slot, ulong current_slot ); -// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L673 +/* https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L673 */ ulong fd_vote_credits_for_vote_at_index( fd_landed_vote_t const * votes, ulong index ); diff --git a/src/flamenco/runtime/program/vote/fd_vote_state_v3.c b/src/flamenco/runtime/program/vote/fd_vote_state_v3.c index 049b332152d..e0fc7d0269d 100644 --- a/src/flamenco/runtime/program/vote/fd_vote_state_v3.c +++ b/src/flamenco/runtime/program/vote/fd_vote_state_v3.c @@ -7,9 +7,8 @@ /* from_vote_state_1_14_11 converts a "v3" vote state object into the older "v1.14.11" version. This destroys the "v3" object in - the process. */ - -// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/vote_state_1_14_11.rs#L67 + the process. + https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/vote_state_1_14_11.rs#L67 */ static void from_vote_state_1_14_11( fd_vote_state_v3_t * vote_state, fd_vote_state_1_14_11_t * vote_state_1_14_11, /* out */ @@ -44,7 +43,6 @@ from_vote_state_1_14_11( fd_vote_state_v3_t * vote_state, } -/* https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v4.0.4/vote-interface/src/state/vote_state_v3.rs#L65-L73 */ void fd_vote_program_v3_create_new( fd_vote_init_t * const vote_init, fd_sol_sysvar_clock_t const * clock, @@ -61,7 +59,6 @@ fd_vote_program_v3_create_new( fd_vote_init_t * const vote_init, vote_state->prior_voters.is_empty = 1; } -/* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L414-L434 */ int fd_vote_state_v3_set_vote_account_state( fd_exec_instr_ctx_t const * ctx, fd_borrowed_account_t * vote_account, @@ -97,11 +94,6 @@ fd_vote_state_v3_set_vote_account_state( fd_exec_instr_ctx_t const * ctx, return fd_vsv_set_state( vote_account, versioned ); } -/* This is more than just a deserialization - this function attempts - to deserialize whatever vote state version the vote account has, - and then tries to convert it into a v3 vote account (unless its a - v4 account, where it fails automatically). - https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v4.0.4/vote-interface/src/state/vote_state_v3.rs#L119-L124 */ int fd_vote_state_v3_deserialize( fd_borrowed_account_t const * vote_account, uchar * vote_state_mem, @@ -125,7 +117,6 @@ fd_vote_state_v3_deserialize( fd_borrowed_account_t const * vote_account, return fd_vsv_try_convert_to_v3( versioned, authorized_voters_mem, landed_votes_mem ); } -// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L828 int fd_vote_state_v3_get_and_update_authorized_voter( fd_vote_state_v3_t * self, ulong current_epoch, @@ -143,7 +134,6 @@ fd_vote_state_v3_get_and_update_authorized_voter( fd_vote_state_v3_t * self, return FD_EXECUTOR_INSTR_SUCCESS; } -/* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L263-L321 */ int fd_vote_state_v3_set_new_authorized_voter( fd_exec_instr_ctx_t * ctx, fd_vote_state_v3_t * self, diff --git a/src/flamenco/runtime/program/vote/fd_vote_state_v4.c b/src/flamenco/runtime/program/vote/fd_vote_state_v4.c index d354543f504..66a441e8e84 100644 --- a/src/flamenco/runtime/program/vote/fd_vote_state_v4.c +++ b/src/flamenco/runtime/program/vote/fd_vote_state_v4.c @@ -5,11 +5,6 @@ #include "../fd_vote_program.h" #include "../../fd_runtime.h" -/**********************************************************************/ -/* VoteStateV4 */ -/**********************************************************************/ - -/* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L600-L619 */ void fd_vote_state_v4_create_new( fd_pubkey_t const * vote_pubkey, fd_vote_init_t const * vote_init, @@ -28,7 +23,6 @@ fd_vote_state_v4_create_new( fd_pubkey_t const * vote_pubkey, vote_state->block_revenue_commission_bps = DEFAULT_BLOCK_REVENUE_COMMISSION_BPS; } -/* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L576-L595 */ int fd_vote_state_v4_set_vote_account_state( fd_exec_instr_ctx_t const * ctx, fd_borrowed_account_t * vote_account, diff --git a/src/flamenco/runtime/program/vote/fd_vote_state_v4.h b/src/flamenco/runtime/program/vote/fd_vote_state_v4.h index c8ef3ef4e5b..f2f67c8b713 100644 --- a/src/flamenco/runtime/program/vote/fd_vote_state_v4.h +++ b/src/flamenco/runtime/program/vote/fd_vote_state_v4.h @@ -3,6 +3,8 @@ #include "../../fd_borrowed_account.h" +/* fd_vote_state_v4 mirrors Agave's VoteStateV4 methods. */ + FD_PROTOTYPES_BEGIN /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L600-L619 */ diff --git a/src/flamenco/runtime/program/vote/fd_vote_state_versioned.c b/src/flamenco/runtime/program/vote/fd_vote_state_versioned.c index 9dfa94bc01a..a7e0ead5479 100644 --- a/src/flamenco/runtime/program/vote/fd_vote_state_versioned.c +++ b/src/flamenco/runtime/program/vote/fd_vote_state_versioned.c @@ -7,19 +7,19 @@ #include "fd_authorized_voters.h" #include "../../fd_runtime.h" -// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L42 +/* https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L42 */ #define DEFAULT_PRIOR_VOTERS_OFFSET 114 -// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L886 +/* https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L886 */ #define VERSION_OFFSET (4UL) -// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L887 +/* https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L887 */ #define DEFAULT_PRIOR_VOTERS_END (118) -// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/vote_state_1_14_11.rs#L6 +/* https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/vote_state_1_14_11.rs#L6 */ #define DEFAULT_PRIOR_VOTERS_OFFSET_1_14_11 (82UL) -// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/vote_state_1_14_11.rs#L60 +/* https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/vote_state_1_14_11.rs#L60 */ #define DEFAULT_PRIOR_VOTERS_END_1_14_11 (86UL) /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L780-L785 */ @@ -46,7 +46,6 @@ last_lockout( fd_vote_state_versioned_t * self ) { /* Getters */ /**********************************************************************/ -/* https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1074 */ int fd_vsv_get_state( fd_account_meta_t const * meta, uchar * res ) { @@ -69,8 +68,6 @@ fd_vsv_get_state( fd_account_meta_t const * meta, return FD_EXECUTOR_INSTR_SUCCESS; } -/* Returns a const pointer to the authorized withdrawer for the - appropriate vote state version.*/ fd_pubkey_t const * fd_vsv_get_authorized_withdrawer( fd_vote_state_versioned_t * self ) { switch( self->discriminant ) { diff --git a/src/flamenco/runtime/program/vote/fd_vote_state_versioned.h b/src/flamenco/runtime/program/vote/fd_vote_state_versioned.h index 00a1e8edc0c..69c77c0f1cf 100644 --- a/src/flamenco/runtime/program/vote/fd_vote_state_versioned.h +++ b/src/flamenco/runtime/program/vote/fd_vote_state_versioned.h @@ -4,6 +4,10 @@ #include "../../fd_borrowed_account.h" #include "../../../types/fd_types.h" +/* fd_vote_state_versioned contains common logic for all supported vote + state versions, and implements most methods from Agave's + VoteStateHandle. */ + FD_PROTOTYPES_BEGIN /**********************************************************************/ @@ -15,6 +19,8 @@ int fd_vsv_get_state( fd_account_meta_t const * meta, uchar * res ); +/* Returns a const pointer to the authorized withdrawer for the + appropriate vote state version.*/ fd_pubkey_t const * fd_vsv_get_authorized_withdrawer( fd_vote_state_versioned_t * self ); From 1456fa1c958ad3ce8200c835666d83b9f279cd4d Mon Sep 17 00:00:00 2001 From: Manik Jain Date: Mon, 22 Dec 2025 19:33:12 +0000 Subject: [PATCH 14/19] document vsv methods --- .../program/vote/fd_vote_state_versioned.c | 4 ++-- .../program/vote/fd_vote_state_versioned.h | 15 +++++++++++---- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/flamenco/runtime/program/vote/fd_vote_state_versioned.c b/src/flamenco/runtime/program/vote/fd_vote_state_versioned.c index a7e0ead5479..c9e62c0a4a7 100644 --- a/src/flamenco/runtime/program/vote/fd_vote_state_versioned.c +++ b/src/flamenco/runtime/program/vote/fd_vote_state_versioned.c @@ -48,7 +48,7 @@ last_lockout( fd_vote_state_versioned_t * self ) { int fd_vsv_get_state( fd_account_meta_t const * meta, - uchar * res ) { + uchar * vote_state_mem ) { fd_bincode_decode_ctx_t decode = { .data = fd_account_data( meta ), @@ -63,7 +63,7 @@ fd_vsv_get_state( fd_account_meta_t const * meta, FD_TEST( total_sz<=FD_VOTE_STATE_VERSIONED_FOOTPRINT ); - fd_vote_state_versioned_decode( res, &decode ); + fd_vote_state_versioned_decode( vote_state_mem, &decode ); return FD_EXECUTOR_INSTR_SUCCESS; } diff --git a/src/flamenco/runtime/program/vote/fd_vote_state_versioned.h b/src/flamenco/runtime/program/vote/fd_vote_state_versioned.h index 69c77c0f1cf..dfb1a91fea7 100644 --- a/src/flamenco/runtime/program/vote/fd_vote_state_versioned.h +++ b/src/flamenco/runtime/program/vote/fd_vote_state_versioned.h @@ -14,10 +14,14 @@ FD_PROTOTYPES_BEGIN /* Getters */ /**********************************************************************/ -/* https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1074 */ +/* Decodes the vote account data and stores the decoded state in + vote_state_mem. The caller must provide a buffer at vote_state_mem + that is aligned to FD_VOTE_STATE_VERSIONED_ALIGN and has at least + FD_VOTE_STATE_VERSIONED_FOOTPRINT bytes of space. + https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1074 */ int fd_vsv_get_state( fd_account_meta_t const * meta, - uchar * res ); + uchar * vote_state_mem ); /* Returns a const pointer to the authorized withdrawer for the appropriate vote state version.*/ @@ -154,8 +158,11 @@ fd_vsv_deinitialize_vote_account_state( fd_exec_instr_ctx_t * ctx, uchar * vote_lockout_mem ); /* This function is essentially just a call to get_state, additionally - erroring out if the account is a v_0_23_5 account. Initializes - a fd_vote_state_versioned_t struct in the vote_state_mem. + erroring out if the account is a v_0_23_5 account. Decodes the + vote account data and stores the decoded state in vote_state_mem. + The caller must provide a buffer at vote_state_mem that is aligned to + FD_VOTE_STATE_VERSIONED_ALIGN and has at least + FD_VOTE_STATE_VERSIONED_FOOTPRINT bytes of space. https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v4.0.4/vote-interface/src/state/vote_state_versions.rs#L195-L246 */ int fd_vsv_deserialize( fd_borrowed_account_t const * vote_account, From 296bffbd521e58aa28a3a0d65acaa7500976aa5c Mon Sep 17 00:00:00 2001 From: Manik Jain Date: Mon, 22 Dec 2025 19:35:59 +0000 Subject: [PATCH 15/19] more comments: --- src/flamenco/runtime/program/vote/fd_vote_state_versioned.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/flamenco/runtime/program/vote/fd_vote_state_versioned.h b/src/flamenco/runtime/program/vote/fd_vote_state_versioned.h index dfb1a91fea7..d7a81475efe 100644 --- a/src/flamenco/runtime/program/vote/fd_vote_state_versioned.h +++ b/src/flamenco/runtime/program/vote/fd_vote_state_versioned.h @@ -17,7 +17,8 @@ FD_PROTOTYPES_BEGIN /* Decodes the vote account data and stores the decoded state in vote_state_mem. The caller must provide a buffer at vote_state_mem that is aligned to FD_VOTE_STATE_VERSIONED_ALIGN and has at least - FD_VOTE_STATE_VERSIONED_FOOTPRINT bytes of space. + FD_VOTE_STATE_VERSIONED_FOOTPRINT bytes of space. Returns + FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA if decoding fails. https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1074 */ int fd_vsv_get_state( fd_account_meta_t const * meta, @@ -162,7 +163,8 @@ fd_vsv_deinitialize_vote_account_state( fd_exec_instr_ctx_t * ctx, vote account data and stores the decoded state in vote_state_mem. The caller must provide a buffer at vote_state_mem that is aligned to FD_VOTE_STATE_VERSIONED_ALIGN and has at least - FD_VOTE_STATE_VERSIONED_FOOTPRINT bytes of space. + FD_VOTE_STATE_VERSIONED_FOOTPRINT bytes of space. Returns an error + if decoding fails or if the account is a v_0_23_5 account. https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v4.0.4/vote-interface/src/state/vote_state_versions.rs#L195-L246 */ int fd_vsv_deserialize( fd_borrowed_account_t const * vote_account, From db7ffa8c4b5be92efbd9e37495eb4d78e8bc555c Mon Sep 17 00:00:00 2001 From: Manik Jain Date: Mon, 22 Dec 2025 19:50:52 +0000 Subject: [PATCH 16/19] use fd_mem_iszero --- .../runtime/program/vote/fd_vote_state_versioned.c | 8 ++------ src/util/fd_util_base.h | 10 ++++++++++ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/flamenco/runtime/program/vote/fd_vote_state_versioned.c b/src/flamenco/runtime/program/vote/fd_vote_state_versioned.c index c9e62c0a4a7..96c8fcadb88 100644 --- a/src/flamenco/runtime/program/vote/fd_vote_state_versioned.c +++ b/src/flamenco/runtime/program/vote/fd_vote_state_versioned.c @@ -733,21 +733,17 @@ fd_vsv_is_correct_size_and_initialized( fd_account_meta_t const * meta ) { return 1; } - /* This is big enough for both v3 and v1_14_11 and can be reused - (DEFAULT_PRIOR_VOTERS_OFFSET>DEFAULT_PRIOR_VOTERS_OFFSET_1_14_11) */ - uchar zero_data[ DEFAULT_PRIOR_VOTERS_OFFSET ] = {0}; - /* VoteStateV3::is_correct_size_and_initialized https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v4.0.4/vote-interface/src/state/vote_state_v3.rs#L509-L514 */ if( FD_LIKELY( data_len==FD_VOTE_STATE_V3_SZ && - memcmp( data+VERSION_OFFSET, zero_data, DEFAULT_PRIOR_VOTERS_OFFSET ) ) ) { + !fd_mem_iszero( data+VERSION_OFFSET, DEFAULT_PRIOR_VOTERS_OFFSET ) ) ) { return 1; } /* VoteState1_14_11::is_correct_size_and_initialized https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v4.0.4/vote-interface/src/state/vote_state_1_14_11.rs#L63-L69 */ if( FD_LIKELY( data_len==FD_VOTE_STATE_V2_SZ && - memcmp( data+VERSION_OFFSET, zero_data, DEFAULT_PRIOR_VOTERS_OFFSET_1_14_11 ) ) ) { + !fd_mem_iszero( data+VERSION_OFFSET, DEFAULT_PRIOR_VOTERS_OFFSET_1_14_11 ) ) ) { return 1; } diff --git a/src/util/fd_util_base.h b/src/util/fd_util_base.h index 67d4890cc60..927d9188bfb 100644 --- a/src/util/fd_util_base.h +++ b/src/util/fd_util_base.h @@ -1231,6 +1231,16 @@ fd_memeq( void const * s1, #endif +/* Returns 1 if all sz bytes starting at s are zero, 0 otherwise. */ +FD_FN_PURE static inline int +fd_mem_iszero( uchar const * s, + ulong sz ) { + for( ulong i=0UL; i 64-bit hash function (memcpy_hash is often as fast as plain memcpy). Based on From abeab9359af03892fc47c6b5d0a85088f0d2dc39 Mon Sep 17 00:00:00 2001 From: Manik Jain Date: Tue, 23 Dec 2025 19:52:28 +0000 Subject: [PATCH 17/19] fix comment --- src/flamenco/runtime/program/fd_vote_program.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/flamenco/runtime/program/fd_vote_program.c b/src/flamenco/runtime/program/fd_vote_program.c index 5402d5a6953..e41d22315e2 100644 --- a/src/flamenco/runtime/program/fd_vote_program.c +++ b/src/flamenco/runtime/program/fd_vote_program.c @@ -106,7 +106,10 @@ check_vote_account_length( fd_borrowed_account_t const * vote_account, return FD_EXECUTOR_INSTR_SUCCESS; } -/* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L855-L870 */ +/* The versioned parameter must point to a buffer that is aligned to + FD_VOTE_STATE_VERSIONED_ALIGN and is at least + FD_VOTE_STATE_VERSIONED_FOOTPRINT bytes in size. + https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L855-L870 */ static int init_vote_account_state( fd_exec_instr_ctx_t * ctx, fd_borrowed_account_t * vote_account, @@ -114,13 +117,6 @@ init_vote_account_state( fd_exec_instr_ctx_t * ctx, int target_version, fd_vote_init_t * vote_init, fd_sol_sysvar_clock_t const * clock ) { - /* - * N.B. Technically we should destroy() to release memory before - * newing, otherwise the pointers are wiped and memory is leaked. - * We are probably fine for now since we are bump allocating - * everything and the enclosing frame will free everything when - * popped. - */ /* Reset the object */ fd_vote_state_versioned_new( versioned ); From 632941d7f4491e04205c638f2a206d459ed2e8a4 Mon Sep 17 00:00:00 2001 From: Manik Jain Date: Tue, 23 Dec 2025 20:16:42 +0000 Subject: [PATCH 18/19] comments --- src/flamenco/runtime/program/vote/fd_vote_lockout.h | 8 ++++---- src/flamenco/runtime/program/vote/fd_vote_state_v3.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/flamenco/runtime/program/vote/fd_vote_lockout.h b/src/flamenco/runtime/program/vote/fd_vote_lockout.h index 9b762c38210..4cfa778384e 100644 --- a/src/flamenco/runtime/program/vote/fd_vote_lockout.h +++ b/src/flamenco/runtime/program/vote/fd_vote_lockout.h @@ -6,19 +6,19 @@ FD_PROTOTYPES_BEGIN -// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L104 +/* https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L104 */ ulong fd_vote_lockout_get_lockout( fd_vote_lockout_t * self ); -// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L110 +/* https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L110 */ ulong fd_vote_lockout_last_locked_out_slot( fd_vote_lockout_t * self ); -// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L114 +/* https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L114 */ ulong fd_vote_lockout_is_locked_out_at_slot( fd_vote_lockout_t * self, ulong slot ); -// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L122 +/* https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L122 */ void fd_vote_lockout_increase_confirmation_count( fd_vote_lockout_t * self, uint by ); diff --git a/src/flamenco/runtime/program/vote/fd_vote_state_v3.h b/src/flamenco/runtime/program/vote/fd_vote_state_v3.h index c288a17852a..15058e2dd11 100644 --- a/src/flamenco/runtime/program/vote/fd_vote_state_v3.h +++ b/src/flamenco/runtime/program/vote/fd_vote_state_v3.h @@ -33,7 +33,7 @@ fd_vote_state_v3_deserialize( fd_borrowed_account_t const * vote_account, uchar * authorized_voters_mem, uchar * landed_votes_mem ); -// https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L828 +/* https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L828 */ int fd_vote_state_v3_get_and_update_authorized_voter( fd_vote_state_v3_t * self, ulong current_epoch, From 6965c9418e5ba5b9ecafbfcb2e5d4e60f7bf088f Mon Sep 17 00:00:00 2001 From: Manik Jain Date: Tue, 23 Dec 2025 20:22:43 +0000 Subject: [PATCH 19/19] verify closure --- .../runtime/program/vote/fd_vote_state_v3.c | 14 +++++++------- .../runtime/program/vote/fd_vote_state_v3.h | 18 ++++++++++-------- .../runtime/program/vote/fd_vote_state_v4.c | 14 +++++++------- .../runtime/program/vote/fd_vote_state_v4.h | 18 ++++++++++-------- .../program/vote/fd_vote_state_versioned.c | 14 +++++++------- .../program/vote/fd_vote_state_versioned.h | 19 +++++++++++-------- 6 files changed, 52 insertions(+), 45 deletions(-) diff --git a/src/flamenco/runtime/program/vote/fd_vote_state_v3.c b/src/flamenco/runtime/program/vote/fd_vote_state_v3.c index e0fc7d0269d..19a73e5f540 100644 --- a/src/flamenco/runtime/program/vote/fd_vote_state_v3.c +++ b/src/flamenco/runtime/program/vote/fd_vote_state_v3.c @@ -135,13 +135,13 @@ fd_vote_state_v3_get_and_update_authorized_voter( fd_vote_state_v3_t * self, } int -fd_vote_state_v3_set_new_authorized_voter( fd_exec_instr_ctx_t * ctx, - fd_vote_state_v3_t * self, - fd_pubkey_t const * authorized_pubkey, - ulong current_epoch, - ulong target_epoch, - /* "verify" closure */ int authorized_withdrawer_signer, - /* "verify" closure */ fd_pubkey_t const * signers[static FD_TXN_SIG_MAX] ) { +fd_vote_state_v3_set_new_authorized_voter( fd_exec_instr_ctx_t * ctx, + fd_vote_state_v3_t * self, + fd_pubkey_t const * authorized_pubkey, + ulong current_epoch, + ulong target_epoch, + int authorized_withdrawer_signer, + fd_pubkey_t const * signers[static FD_TXN_SIG_MAX] ) { int rc; fd_pubkey_t * epoch_authorized_voter = NULL; diff --git a/src/flamenco/runtime/program/vote/fd_vote_state_v3.h b/src/flamenco/runtime/program/vote/fd_vote_state_v3.h index 15058e2dd11..f321a340e79 100644 --- a/src/flamenco/runtime/program/vote/fd_vote_state_v3.h +++ b/src/flamenco/runtime/program/vote/fd_vote_state_v3.h @@ -39,15 +39,17 @@ fd_vote_state_v3_get_and_update_authorized_voter( fd_vote_state_v3_t * self, ulong current_epoch, fd_pubkey_t ** pubkey /* out */ ); -/* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L263-L321 */ +/* authorized_withdrawer_signer and signers are parameters to a closure + called verify, which is passed into the associated Agave method. + https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L263-L321 */ int -fd_vote_state_v3_set_new_authorized_voter( fd_exec_instr_ctx_t * ctx, - fd_vote_state_v3_t * self, - fd_pubkey_t const * authorized_pubkey, - ulong current_epoch, - ulong target_epoch, - /* "verify" closure */ int authorized_withdrawer_signer, - /* "verify" closure */ fd_pubkey_t const * signers[static FD_TXN_SIG_MAX] ); +fd_vote_state_v3_set_new_authorized_voter( fd_exec_instr_ctx_t * ctx, + fd_vote_state_v3_t * self, + fd_pubkey_t const * authorized_pubkey, + ulong current_epoch, + ulong target_epoch, + int authorized_withdrawer_signer, + fd_pubkey_t const * signers[static FD_TXN_SIG_MAX] ); FD_PROTOTYPES_END diff --git a/src/flamenco/runtime/program/vote/fd_vote_state_v4.c b/src/flamenco/runtime/program/vote/fd_vote_state_v4.c index 66a441e8e84..5d47e758a3d 100644 --- a/src/flamenco/runtime/program/vote/fd_vote_state_v4.c +++ b/src/flamenco/runtime/program/vote/fd_vote_state_v4.c @@ -70,13 +70,13 @@ fd_vote_state_v4_get_and_update_authorized_voter( fd_vote_state_v4_t * self, } int -fd_vote_state_v4_set_new_authorized_voter( fd_exec_instr_ctx_t * ctx, - fd_vote_state_v4_t * self, - fd_pubkey_t const * authorized_pubkey, - ulong current_epoch, - ulong target_epoch, - /* "verify" closure */ int authorized_withdrawer_signer, - /* "verify" closure */ fd_pubkey_t const * signers[static FD_TXN_SIG_MAX] ) { +fd_vote_state_v4_set_new_authorized_voter( fd_exec_instr_ctx_t * ctx, + fd_vote_state_v4_t * self, + fd_pubkey_t const * authorized_pubkey, + ulong current_epoch, + ulong target_epoch, + int authorized_withdrawer_signer, + fd_pubkey_t const * signers[static FD_TXN_SIG_MAX] ) { int rc; fd_pubkey_t * epoch_authorized_voter = NULL; diff --git a/src/flamenco/runtime/program/vote/fd_vote_state_v4.h b/src/flamenco/runtime/program/vote/fd_vote_state_v4.h index f2f67c8b713..40bfaf9dbbb 100644 --- a/src/flamenco/runtime/program/vote/fd_vote_state_v4.h +++ b/src/flamenco/runtime/program/vote/fd_vote_state_v4.h @@ -27,15 +27,17 @@ fd_vote_state_v4_get_and_update_authorized_voter( fd_vote_state_v4_t * self, ulong current_epoch, fd_pubkey_t ** pubkey /* out */ ); -/* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L450-L478 */ +/* authorized_withdrawer_signer and signers are parameters to a closure + called verify, which is passed into the associated Agave method. + https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L450-L478 */ int -fd_vote_state_v4_set_new_authorized_voter( fd_exec_instr_ctx_t * ctx, - fd_vote_state_v4_t * self, - fd_pubkey_t const * authorized_pubkey, - ulong current_epoch, - ulong target_epoch, - /* "verify" closure */ int authorized_withdrawer_signer, - /* "verify" closure */ fd_pubkey_t const * signers[static FD_TXN_SIG_MAX] ); +fd_vote_state_v4_set_new_authorized_voter( fd_exec_instr_ctx_t * ctx, + fd_vote_state_v4_t * self, + fd_pubkey_t const * authorized_pubkey, + ulong current_epoch, + ulong target_epoch, + int authorized_withdrawer_signer, + fd_pubkey_t const * signers[static FD_TXN_SIG_MAX] ); FD_PROTOTYPES_END diff --git a/src/flamenco/runtime/program/vote/fd_vote_state_versioned.c b/src/flamenco/runtime/program/vote/fd_vote_state_versioned.c index 96c8fcadb88..a239a7935af 100644 --- a/src/flamenco/runtime/program/vote/fd_vote_state_versioned.c +++ b/src/flamenco/runtime/program/vote/fd_vote_state_versioned.c @@ -232,13 +232,13 @@ fd_vsv_set_authorized_withdrawer( fd_vote_state_versioned_t * self, } int -fd_vsv_set_new_authorized_voter( fd_exec_instr_ctx_t * ctx, - fd_vote_state_versioned_t * self, - fd_pubkey_t const * authorized_pubkey, - ulong current_epoch, - ulong target_epoch, - /* "verify" closure */ int authorized_withdrawer_signer, - /* "verify" closure */ fd_pubkey_t const * signers[static FD_TXN_SIG_MAX] ) { +fd_vsv_set_new_authorized_voter( fd_exec_instr_ctx_t * ctx, + fd_vote_state_versioned_t * self, + fd_pubkey_t const * authorized_pubkey, + ulong current_epoch, + ulong target_epoch, + int authorized_withdrawer_signer, + fd_pubkey_t const * signers[static FD_TXN_SIG_MAX] ) { switch( self->discriminant ) { case fd_vote_state_versioned_enum_v3: return fd_vote_state_v3_set_new_authorized_voter( diff --git a/src/flamenco/runtime/program/vote/fd_vote_state_versioned.h b/src/flamenco/runtime/program/vote/fd_vote_state_versioned.h index d7a81475efe..60dedaf809b 100644 --- a/src/flamenco/runtime/program/vote/fd_vote_state_versioned.h +++ b/src/flamenco/runtime/program/vote/fd_vote_state_versioned.h @@ -75,15 +75,18 @@ fd_vsv_set_authorized_withdrawer( fd_vote_state_versioned_t * self, fd_pubkey_t const * authorized_withdrawer ); /* Sets the authorized withdrawer for the appropriate vote state - version. Only supported for v3 and v4 vote states. */ + version. Only supported for v3 and v4 vote states. + authorized_withdrawer_signer and signers are parameters to a closure + called verify, which is passed into the associated Agave method. + https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L855-L870 */ int -fd_vsv_set_new_authorized_voter( fd_exec_instr_ctx_t * ctx, - fd_vote_state_versioned_t * self, - fd_pubkey_t const * authorized_pubkey, - ulong current_epoch, - ulong target_epoch, - /* "verify" closure */ int authorized_withdrawer_signer, - /* "verify" closure */ fd_pubkey_t const * signers[static FD_TXN_SIG_MAX] ); +fd_vsv_set_new_authorized_voter( fd_exec_instr_ctx_t * ctx, + fd_vote_state_versioned_t * self, + fd_pubkey_t const * authorized_pubkey, + ulong current_epoch, + ulong target_epoch, + int authorized_withdrawer_signer, + fd_pubkey_t const * signers[static FD_TXN_SIG_MAX] ); /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L738-L743 */ void