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

Generated by: LCOV version 1.14