Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/flamenco/features/fd_features_generated.c
Original file line number Diff line number Diff line change
Expand Up @@ -1691,6 +1691,12 @@ fd_feature_id_t const ids[] = {
.name = "poseidon_enforce_padding",
.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 */
Expand Down Expand Up @@ -1944,6 +1950,7 @@ fd_feature_id_query( ulong prefix ) {
case 0x520c5e674243fab5: return &ids[ 244 ];
case 0xf08a42c3c040e908: return &ids[ 245 ];
case 0x8c7bee4552d93e0c: return &ids[ 246 ];
case 0x8921a3abf23afaec: return &ids[ 247 ];
default: break;
}
return NULL;
Expand Down Expand Up @@ -2196,4 +2203,5 @@ FD_STATIC_ASSERT( offsetof( fd_features_t, stricter_abi_and_runtime_constraints
FD_STATIC_ASSERT( offsetof( fd_features_t, account_data_direct_mapping )>>3==244UL, layout );
FD_STATIC_ASSERT( offsetof( fd_features_t, fix_alt_bn128_pairing_length_check )>>3==245UL, layout );
FD_STATIC_ASSERT( offsetof( fd_features_t, poseidon_enforce_padding )>>3==246UL, layout );
FD_STATIC_ASSERT( offsetof( fd_features_t, vote_state_v4 )>>3==247UL, layout );
FD_STATIC_ASSERT( sizeof( fd_features_t )>>3==FD_FEATURE_ID_CNT, layout );
5 changes: 3 additions & 2 deletions src/flamenco/features/fd_features_generated.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@
#endif

/* FEATURE_ID_CNT is the number of features in ids */
#define FD_FEATURE_ID_CNT (247UL)
#define FD_FEATURE_ID_CNT (248UL)

/* Feature set ID calculated from all feature names */
#define FD_FEATURE_SET_ID (2146234083U)
#define FD_FEATURE_SET_ID (1260307723U)

union fd_features {
ulong f[ FD_FEATURE_ID_CNT ];
Expand Down Expand Up @@ -263,5 +263,6 @@ union fd_features {
/* 0x520c5e674243fab5 */ ulong account_data_direct_mapping;
/* 0xf08a42c3c040e908 */ ulong fix_alt_bn128_pairing_length_check;
/* 0x8c7bee4552d93e0c */ ulong poseidon_enforce_padding;
/* 0x8921a3abf23afaec */ ulong vote_state_v4;
};
};
3 changes: 2 additions & 1 deletion src/flamenco/features/feature_map.json
Original file line number Diff line number Diff line change
Expand Up @@ -245,5 +245,6 @@
{"name":"stricter_abi_and_runtime_constraints","pubkey":"sD3uVpaavUXQRvDXrMFCQ2CqLqnbz5mK8ttWNXbtD3r","old":"CxeBn9PVeeXbmjbNwLv6U4C6svNxnC4JX6mfkvgeMocM"},
{"name":"account_data_direct_mapping","pubkey":"DFN8MyKpQqFW31qczcahgnnxcAHQc6P94wtTEX5EP1RA","old":"9s3RKimHWS44rJcJ9P1rwCmn2TvMqtZQBmz815ZUUHqJ"},
{"name":"fix_alt_bn128_pairing_length_check","pubkey":"bnYzodLwmybj7e1HAe98yZrdJTd7we69eMMLgCXqKZm"},
{"name":"poseidon_enforce_padding","pubkey":"poUdAqRXXsNmfqAZ6UqpjbeYgwBygbfQLEvWSqVhSnb"}
{"name":"poseidon_enforce_padding","pubkey":"poUdAqRXXsNmfqAZ6UqpjbeYgwBygbfQLEvWSqVhSnb"},
{"name":"vote_state_v4","pubkey":"Gx4XFcrVMt4HUvPzTpTSVkdDVgcDSjKhDN1RqRS6KDuZ"}
]
3 changes: 3 additions & 0 deletions src/flamenco/rewards/fd_rewards.c
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,9 @@ get_vote_credits( uchar const * account_data,
case fd_vote_state_versioned_enum_current:
*credits = vsv->inner.current.epoch_credits;
break;
case fd_vote_state_versioned_enum_v4:
*credits = vsv->inner.v4.epoch_credits;
break;
default:
__builtin_unreachable();
}
Expand Down
1 change: 1 addition & 0 deletions src/flamenco/runtime/fd_runtime.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
2,004 changes: 716 additions & 1,288 deletions src/flamenco/runtime/program/fd_vote_program.c

Large diffs are not rendered by default.

87 changes: 87 additions & 0 deletions src/flamenco/runtime/program/fd_vote_program.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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

Expand Down
17 changes: 17 additions & 0 deletions src/flamenco/runtime/program/vote/Local.mk
Original file line number Diff line number Diff line change
@@ -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)
164 changes: 164 additions & 0 deletions src/flamenco/runtime/program/vote/fd_authorized_voters.c
Original file line number Diff line number Diff line change
@@ -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_current:
return fd_vote_state_v3_get_and_update_authorized_voter( &self->inner.current, 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();
}
}
Loading