LCOV - code coverage report
Current view: top level - choreo/voter - fd_voter.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 26 0.0 %
Date: 2025-09-19 04:41:14 Functions: 0 36 0.0 %

          Line data    Source code
       1             : #ifndef HEADER_fd_src_choreo_voter_fd_voter_h
       2             : #define HEADER_fd_src_choreo_voter_fd_voter_h
       3             : 
       4             : /* fd_voter is an API for accessing voters' on-chain accounts, known as
       5             :    "vote states".  The accounts are in bincode-serialized form and voter
       6             :    intentionally X-rays the accounts ie. interprets the bytes without
       7             :    deserializing. */
       8             : 
       9             : #include "../fd_choreo_base.h"
      10             : #include "../../funk/fd_funk_rec.h"
      11             : 
      12             : /* FD_VOTER_USE_HANDHOLDING:  Define this to non-zero at compile time
      13             :    to turn on additional runtime checks and logging. */
      14             : 
      15             : #ifndef FD_VOTER_USE_HANDHOLDING
      16             : #define FD_VOTER_USE_HANDHOLDING 1
      17             : #endif
      18             : 
      19             : #define FD_VOTER_STATE_V0_23_5  (0)
      20             : #define FD_VOTER_STATE_V1_14_11 (1)
      21             : #define FD_VOTER_STATE_CURRENT  (2)
      22             : FD_STATIC_ASSERT(FD_VOTER_STATE_V0_23_5 ==fd_vote_state_versioned_enum_v0_23_5,  FD_VOTER_STATE_V0_23_5 );
      23             : FD_STATIC_ASSERT(FD_VOTER_STATE_V1_14_11==fd_vote_state_versioned_enum_v1_14_11, FD_VOTER_STATE_V1_14_11);
      24             : FD_STATIC_ASSERT(FD_VOTER_STATE_CURRENT ==fd_vote_state_versioned_enum_current,  FD_VOTER_STATE_CURRENT );
      25             : 
      26             : 
      27             : 
      28             : /* Agave VoteAccount https://github.com/anza-xyz/agave/blob/v2.3.7/vote/src/vote_state_view.rs#L182 */
      29             : /* fd_voter_v2_serde defines a serialization / deserialization schema
      30             :    for a bincode-encoded vote account v2.  This corresponds exactly with
      31             :    the binary layout of a an Agave VoteState1_14_11.
      32             : 
      33             :    The serde is structured for zero-copy access ie. x-raying individual
      34             :    fields. */
      35             : 
      36             : struct fd_voter_v2_serde {
      37             :   fd_pubkey_t const * node_pubkey;
      38             :   fd_pubkey_t const * authorized_withdrawer;
      39             :   uchar       const * commission;
      40             : 
      41             :   struct /* VecDeque<Lockout> */ {
      42             :     ulong const * votes_cnt;
      43             :     struct {
      44             :       ulong const * slot;
      45             :       uint  const * confirmation_count;
      46             :     } votes[31]; /* idx >= votes_cnt are invalid */
      47             :   };
      48             : 
      49             :   struct /* Option<Slot> */ {
      50             :     uchar const * root_slot_option;
      51             :     ulong const * root_slot;
      52             :   };
      53             : 
      54             :   struct /* AuthorizedVoters */ {
      55             :     ulong const * authorized_voters_cnt;
      56             :     struct {
      57             :       ulong       const * epoch;
      58             :       fd_pubkey_t const * pubkey;
      59             :     } authorized_voters[32]; /* idx >= authorized_voters_cnt are invalid */
      60             :   };
      61             : 
      62             :   struct /* CircBuf<Pubkey, Epoch, Epoch> */ {
      63             :     struct {
      64             :       fd_pubkey_t const * pubkey;
      65             :       ulong       const * start_epoch;
      66             :       ulong       const * end_epoch;
      67             :     } buf[32];
      68             :     ulong const * idx;
      69             :     uchar const * is_empty;
      70             :   } prior_voters;
      71             : 
      72             :   struct /* Vec<Epoch, u64, u64> */ {
      73             :     ulong const * epoch_credits_cnt;
      74             :     struct {
      75             :       ulong const * epoch;
      76             :       ulong const * credits;
      77             :       ulong const * prev_credits;
      78             :     } epoch_credits[32]; /* idx >= epoch_credits_cnt are invalid */
      79             :   };
      80             : 
      81             :   struct /* BlockTimestamp */ {
      82             :     ulong const * slot;
      83             :     long  const * timestamp;
      84             :   } last_timestamp;
      85             : };
      86             : typedef struct fd_voter_v2_serde fd_voter_v2_serde_t;
      87             : 
      88             : /* fd_voter_v3_serde defines a serialization / deserialization schema
      89             :    for a bincode-encoded vote account v3.  This corresponds exactly with
      90             :    the binary layout of a an Agave VoteState (also known as
      91             :    VoteStateVersioned::Current).
      92             : 
      93             :    The serde is structured for zero-copy access ie. x-raying individual
      94             :    fields. */
      95             : 
      96             : struct fd_voter_v3_serde {
      97             :   fd_pubkey_t const * node_pubkey;
      98             :   fd_pubkey_t const * authorized_withdrawer;
      99             :   uchar       const * commission;
     100             : 
     101             :   struct /* VecDeque<LandedVote> */ {
     102             :     ulong const * votes_cnt;
     103             :     struct {
     104             :       uchar const * latency;
     105             :       ulong const * slot;
     106             :       uint  const * confirmation_count;
     107             :     } votes[31]; /* idx >= votes_cnt are invalid */
     108             :   };
     109             : 
     110             :   struct /* Option<Slot> */ {
     111             :     uchar const * root_slot_option;
     112             :     ulong const * root_slot;
     113             :   };
     114             : 
     115             :   struct /* AuthorizedVoters */ {
     116             :     ulong const * authorized_voters_cnt;
     117             :     struct {
     118             :       ulong       const * epoch;
     119             :       fd_pubkey_t const * pubkey;
     120             :     } authorized_voters[32]; /* idx >= authorized_voters_cnt are invalid */
     121             :   };
     122             : 
     123             :   struct /* CircBuf<Pubkey, Epoch, Epoch> */ {
     124             :     struct {
     125             :       fd_pubkey_t const * pubkey;
     126             :       ulong       const * start_epoch;
     127             :       ulong       const * end_epoch;
     128             :     } buf[32];
     129             :     ulong const * idx;
     130             :     uchar const * is_empty;
     131             :   } prior_voters;
     132             : 
     133             :   struct /* Vec<Epoch, u64, u64> */ {
     134             :     ulong const * epoch_credits_cnt;
     135             :     struct {
     136             :       ulong const * epoch;
     137             :       ulong const * credits;
     138             :       ulong const * prev_credits;
     139             :     } epoch_credits[32]; /* idx >= epoch_credits_cnt are invalid */
     140             :   };
     141             : 
     142             :   struct /* BlockTimestamp */ {
     143             :     ulong const * slot;
     144             :     long  const * timestamp;
     145             :   } last_timestamp;
     146             : };
     147             : typedef struct fd_voter_v3_serde fd_voter_v3_serde_t;
     148             : 
     149             : /* Useful to keep both the block_id and slot in the vote record,
     150             :    for handling equivocation cases. Potentially re-evaluate removing the
     151             :    slot altogether.*/
     152             : 
     153             : struct fd_vote_record {
     154             :   ulong     slot;
     155             :   fd_hash_t hash;
     156             : };
     157             : typedef struct fd_vote_record fd_vote_record_t;
     158             : 
     159             : /* A fd_voter_t describes a voter.  The voter is generic to the context
     160             :    in which it is used, eg. it might be a voter in a slot-level context
     161             :    in which its stake value may be different from the same voter in an
     162             :    epoch-level context which in turn is different from the same voter in
     163             :    the prior epoch's context.
     164             : 
     165             :    The voter is used by various choreo APIs including fd_epoch which
     166             :    tracks all the voters in a given epoch, fd_forks which performs
     167             :    choreo-related fork updates after replaying a slot, and ghost and
     168             :    tower which both require bookkeeping the epoch voters. */
     169             : 
     170             : struct fd_voter {
     171             :   fd_pubkey_t key;  /* vote account address */
     172             :   uint        hash; /* reserved for fd_map_dynamic.c */
     173             : 
     174             :   /* IMPORTANT! The values below should only be modified by fd_epoch and
     175             :      fd_ghost. */
     176             : 
     177             :   ulong            stake;       /* voter's stake */
     178             :   fd_vote_record_t replay_vote; /* cached read of last tower vote via replay */
     179             :   fd_vote_record_t gossip_vote; /* cached read of last tower vote via gossip */
     180             :   fd_vote_record_t rooted_vote; /* cached read of last tower root via replay */
     181             : };
     182             : typedef struct fd_voter fd_voter_t;
     183             : 
     184             : /* fd_voter_{vote_old, vote, meta, meta_old, state} are struct
     185             :    representations of the bincode-serialized layout of a voter's state
     186             :    stored in a vote account. These structs are used to support zero-copy
     187             :    reads of the vote account.
     188             : 
     189             :    The voter's state is versioned, and the serialized formats differ
     190             :    depending on this.  Currently, the only version that differs from the
     191             :    others that is relevant here is v0.23.5.  Thus v0.23.5 has its own
     192             :    dedicated struct definition with a different set of fields that
     193             :    precede the votes than the other versions.  Furthermore, v0.23.5
     194             :    contains votes of type `fd_vote_lockout_t` vs. the other versions
     195             :    which are of type `fd_landed_vote_t`.  The only difference between
     196             :    `fd_vote_lockout_t` and `fd_landed_vote_t` is there is an additional
     197             :    uchar field `latency`, so that is we include an offset of 0 or 1
     198             :    depending on which vote state type it is.
     199             : 
     200             :    The layout begins with a set of fields providing important metadata
     201             :    about the voter.  Immediately following these fields is the tower
     202             :    itself. The tower layout begins with the number of votes currently in
     203             :    the tower ie. `cnt`.  Then the votes themselves follow. The format of
     204             :    the votes varies depending on the version.  Finally the layout
     205             :    concludes with the tower's root slot.
     206             : 
     207             :    --------
     208             :    metadata <- sizeof(fd_voter_meta_t) or sizeof(fd_voter_meta_old_t)
     209             :    --------
     210             :    votes    <- {sizeof(vote) or sizeof(vote_old)} * cnt
     211             :    --------
     212             :    root     <- 5 or 1 byte(s). bincode-serialized Option<u64>
     213             :    --------
     214             : */
     215             : 
     216             : struct __attribute__((packed)) fd_voter_vote_old {
     217             :   ulong slot;
     218             :   uint  conf;
     219             : };
     220             : typedef struct fd_voter_vote_old fd_voter_vote_old_t;
     221             : 
     222             : struct __attribute__((packed)) fd_voter_vote {
     223             :   uchar latency;
     224             :   ulong slot;
     225             :   uint  conf;
     226             : };
     227             : typedef struct fd_voter_vote fd_voter_vote_t;
     228             : 
     229             : struct __attribute__((packed)) fd_voter_meta_old {
     230             :   fd_pubkey_t node_pubkey;
     231             :   fd_pubkey_t authorized_voter;
     232             :   ulong       authorized_voter_epoch;
     233             :   uchar       prior_voters[ (32*56+sizeof(ulong)) /* serialized bincode sz */ ];
     234             :   fd_pubkey_t authorized_withdrawer;
     235             :   uchar       commission;
     236             : };
     237             : typedef struct fd_voter_meta_old fd_voter_meta_old_t;
     238             : 
     239             : struct __attribute__((packed)) fd_voter_meta {
     240             :   fd_pubkey_t node_pubkey;
     241             :   fd_pubkey_t authorized_withdrawer;
     242             :   uchar       commission;
     243             : };
     244             : typedef struct fd_voter_meta fd_voter_meta_t;
     245             : 
     246             : struct __attribute__((packed)) fd_voter_state {
     247             :   uint kind;
     248             :   union {
     249             :     struct __attribute__((packed)) {
     250             :       fd_voter_meta_old_t meta;
     251             :       ulong               cnt;
     252             :       fd_voter_vote_old_t votes[31];
     253             :     } v0_23_5;
     254             : 
     255             :     struct __attribute__((packed)) {
     256             :       fd_voter_meta_t     meta;
     257             :       ulong               cnt;
     258             :       fd_voter_vote_old_t votes[31];
     259             :     } v1_14_11;
     260             : 
     261             :     struct __attribute__((packed)) {
     262             :       fd_voter_meta_t meta;
     263             :       ulong           cnt;
     264             :       fd_voter_vote_t votes[31];
     265             :     };
     266             : 
     267             :     /* The voter's root (a bincode-serialized Option<u64>) follows
     268             :        votes. Because the preceding votes are variable-length in
     269             :        serialized form, we cannot encode the root directly inside the
     270             :        struct. */
     271             :   };
     272             : };
     273             : typedef struct fd_voter_state fd_voter_state_t;
     274             : 
     275             : struct __attribute__((packed)) fd_voter_tower {
     276             :   ulong               cnt;
     277             :   fd_voter_vote_old_t votes[31];
     278             : };
     279             : typedef struct fd_voter_tower fd_voter_tower_t;
     280             : 
     281             : struct __attribute__((packed)) fd_voter_footer {
     282             :   uchar some; /* 0 = None, 1 = Some */
     283             :   ulong root;
     284             : };
     285             : 
     286             : /* fd_voter_state_cnt returns the number of votes in the voter's tower.
     287             :    Assumes `state` is a valid fd_voter_state_t. */
     288             : 
     289             : FD_FN_PURE static inline ulong
     290           0 : fd_voter_state_cnt( fd_voter_state_t const * state ) {
     291           0 :   if( FD_UNLIKELY( state->kind == FD_VOTER_STATE_V0_23_5 ) )  return state->v0_23_5.cnt;
     292           0 :   if( FD_UNLIKELY( state->kind == FD_VOTER_STATE_V1_14_11 ) ) return state->v1_14_11.cnt;
     293           0 :   return state->cnt;
     294           0 : }
     295             : 
     296             : /* fd_voter_root_laddr returns a pointer to the voter's root by x-raying
     297             :    the bincode-serialized vote state. */
     298             : 
     299             : static inline uchar *
     300           0 : fd_voter_root_laddr( fd_voter_state_t const * state ) {
     301           0 :   ulong cnt = fd_voter_state_cnt( state );
     302           0 :   if( FD_UNLIKELY( !cnt ) ) return NULL;
     303           0 :   uchar * root = NULL;
     304           0 :   if     ( FD_UNLIKELY( state->kind == FD_VOTER_STATE_V0_23_5  ) ) root = (uchar *)&state->v0_23_5.votes[cnt];
     305           0 :   else if( FD_UNLIKELY( state->kind == FD_VOTER_STATE_V1_14_11 ) ) root = (uchar *)&state->v1_14_11.votes[cnt];
     306           0 :   else                                                             root = (uchar *)&state->votes[cnt];
     307           0 :   FD_TEST( root );
     308           0 :   return root;
     309           0 : }
     310             : 
     311             : /* fd_voter_state queries funk for the record in the provided `txn` and
     312             :    `key`.  Returns a pointer to the start of the voter's state.  Assumes
     313             :    `key` is a vote account address and the record is a voter's state
     314             :    (fd_voter_state_t).  U.B. if `key` does not point to a valid vote
     315             :    account.
     316             : 
     317             :    It will update the given Funk query with the version at the point of
     318             :    querying. fd_funk_rec_query_test must be called after usage to check
     319             :    that the record has not been modified. */
     320             : 
     321             : fd_voter_state_t const *
     322             : fd_voter_state( fd_funk_t const * funk, fd_funk_rec_t const * rec );
     323             : 
     324             : /* fd_voter_state_vote returns the voter's most recent vote (ie. the
     325             :    last vote of the tower in the voter's state).  Assumes `state` is a
     326             :    valid fd_voter_state_t. */
     327             : 
     328             : FD_FN_PURE static inline ulong
     329           0 : fd_voter_state_vote( fd_voter_state_t const * state ) {
     330           0 :   ulong cnt = fd_voter_state_cnt( state );
     331           0 :   if( FD_UNLIKELY( !cnt ) ) return FD_SLOT_NULL;
     332             : 
     333           0 :   if( FD_UNLIKELY( state->kind == FD_VOTER_STATE_V0_23_5 ) )  return state->v0_23_5.votes[cnt - 1].slot;
     334           0 :   if( FD_UNLIKELY( state->kind == FD_VOTER_STATE_V1_14_11 ) ) return state->v1_14_11.votes[cnt - 1].slot;
     335           0 :   return state->votes[cnt - 1].slot;
     336           0 : }
     337             : 
     338             : /* fd_voter_root_slot returns the voter's root slot.  Assumes `state`
     339             :    is a valid fd_voter_state_t. */
     340             : 
     341             : static inline ulong
     342           0 : fd_voter_root_slot( fd_voter_state_t const * state ) {
     343           0 :   uchar * root = fd_voter_root_laddr( state );
     344           0 :   return *(uchar *)root ? *(ulong *)(root+sizeof(uchar)) /* Some(root) */ : FD_SLOT_NULL /* None */;
     345           0 : }
     346             : 
     347             : #endif /* HEADER_fd_src_choreo_voter_fd_voter_h */

Generated by: LCOV version 1.14