LCOV - code coverage report
Current view: top level - discof/tower - fd_tower_tile.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 10 0.0 %
Date: 2026-03-19 18:19:27 Functions: 0 0 -

          Line data    Source code
       1             : #ifndef HEADER_fd_src_discof_tower_fd_tower_tile_h
       2             : #define HEADER_fd_src_discof_tower_fd_tower_tile_h
       3             : 
       4             : #include "fd_tower_slot_rooted.h"
       5             : #include "../../choreo/eqvoc/fd_eqvoc.h"
       6             : #include "../../choreo/tower/fd_tower.h"
       7             : #include "../../choreo/tower/fd_tower_serdes.h"
       8             : #include "../../disco/topo/fd_topo.h"
       9             : 
      10           0 : #define FD_TOWER_SIG_SLOT_CONFIRMED (0)
      11           0 : #define FD_TOWER_SIG_SLOT_DONE      (1)
      12           0 : #define FD_TOWER_SIG_SLOT_DUPLICATE (2)
      13           0 : #define FD_TOWER_SIG_SLOT_IGNORED   (3)
      14             : // #define FD_TOWER_SIG_SLOT_ROOTED (4)  /* defined in fd_tower_slot_rooted.h */
      15             : 
      16             : /* fd_tower_slot_confirmed describes a Tower frag that notifies protocol
      17             :    confirmations.  There are multiple confirmation levels:
      18             : 
      19             :    - propagation confirmed: a block is propagated if it has received
      20             :      votes from at least 1/3 of stake in the cluster.  This threshold is
      21             :      important in two contexts:
      22             : 
      23             :      1. When becoming leader, we need to check that our previous leader
      24             :         block _as of_ the parent slot we're building on, has propagated.
      25             :         If it has not propagated, we need to instead retransmit our last
      26             :         block that failed to propagate.  The protocol currently allows
      27             :         for a grace period of one leader rotation for leader blocks to
      28             :         propagate.
      29             : 
      30             :      2. When voting, we need to check our previous leader block _as of_
      31             :         the slot we're voting for has propagated (unless we're voting
      32             :         for one of our own leader blocks).  We cannot vote for a slot in
      33             :         which our last ancestor leader block failed to propagate.
      34             : 
      35             :    - duplicate confirmed: a block is duplicate confirmed if it has
      36             :      received votes from at least 52% of stake in the cluster.  The
      37             :      "duplicate" adjective is a bit of a misnomer, and a more accurate
      38             :      technical term is equivocation: two (or more) different blocks for
      39             :      the same slot.  This threshold is important for consensus safety,
      40             :      because it ensures Solana eventually converges to the same block
      41             :      per slot.  Specifically fork choice allows choosing a fork if it is
      42             :      duplicate confirmed, even if there is equivocation.
      43             : 
      44             :    - optimistically confirmed: a block is optimistically confirmed if it
      45             :      has received votes from at least 2/3 of stake in the cluster.  This
      46             :      threshold is important for end-users, who rely on the "confirmed"
      47             :      commitment status of blocks (queryable via RPC) to determine that
      48             :      their transaction has landed on a block that will not rollback.
      49             :      This is unimplemented in Firedancer and only relevant for RPC.
      50             :      (TODO verify this?)
      51             : 
      52             :    - super confirmed: same as optimistic, but the stake threshold is 4/5
      53             :      of stake.  This is used during boot for `--wait-for-supermajority`.
      54             : 
      55             :    It's possible Firedancer reaches a confirmation level before the
      56             :    block has actually been replayed.  Firedancer listens to votes from
      57             :    both Gossip and TPU, so if a given block id has received enough votes
      58             :    it might get "forward-confirmed".
      59             : 
      60             :    Tower will also notify of forward confirmations, denoted by the `fwd`
      61             :    field on the fd_tower_slot_confirmed frag.  Forward confirmations are
      62             :    only for the given block, and do not imply the ancestry chain leading
      63             :    up to the block are also confirmed.  This is distinct from replay
      64             :    confirmations, which are only emitted after replaying a block (`fwd`
      65             :    = 0), and imply the ancestry chain up from that block is also
      66             :    confirmed.  Forward confirmations are needed for both repair (in case
      67             :    we never got the block over Turbine) and for RPC (since RPC needs to
      68             :    know about confirmations even if replay is behind).
      69             : 
      70             :    Other guarantees include that the confirmation frags with `fwd` = 0
      71             :    are delivered in-order with no gaps from tower (there still might be
      72             :    skipped slots, but no gaps means you will always receive an ancestor
      73             :    block's confirmation before its descendants).  That is, if a consumer
      74             :    receives a confirmation frag for slot N, it will have prior received
      75             :    confirmations for all ancestor slots N - 1, N - 2, ... (if they are
      76             :    not skipped / on a different fork). */
      77             : 
      78           0 : #define FD_TOWER_SLOT_CONFIRMED_PROPAGATED (0)
      79           0 : #define FD_TOWER_SLOT_CONFIRMED_DUPLICATE  (1)
      80           0 : #define FD_TOWER_SLOT_CONFIRMED_OPTIMISTIC (2)
      81           0 : #define FD_TOWER_SLOT_CONFIRMED_SUPER      (3)
      82           0 : #define FD_TOWER_SLOT_CONFIRMED_LEVEL_CNT  (4)
      83           0 : #define FD_TOWER_SLOT_CONFIRMED_LEVELS     { FD_TOWER_SLOT_CONFIRMED_PROPAGATED, FD_TOWER_SLOT_CONFIRMED_DUPLICATE, FD_TOWER_SLOT_CONFIRMED_OPTIMISTIC, FD_TOWER_SLOT_CONFIRMED_SUPER }
      84             : 
      85             : struct fd_tower_slot_confirmed {
      86             :   int       level;    /* the confirmation level, see FD_TOWER_SLOT_CONFIRMED_{...} above */
      87             :   int       fwd;      /* whether this is a "forward confirmation" ie. we have not yet replayed but the slot is confirmed based on gossip and TPU votes */
      88             :   ulong     slot;     /* slot being confirmed (in general, a slot being confirmed more than once is possible but highly unlikely ) */
      89             :   fd_hash_t block_id; /* block id being confirmed (guaranteed unique) */
      90             : };
      91             : typedef struct fd_tower_slot_confirmed fd_tower_slot_confirmed_t;
      92             : 
      93             : /* In response to finishing replay of a slot, the tower tile will
      94             :    produce both a block to vote for and block to reset to, and
      95             :    potentially advance the root. */
      96             : 
      97             : struct fd_tower_slot_done {
      98             : 
      99             :   /* This tower_slot_done message is 1-to-1 with the completion of a
     100             :      replayed slot.  When that slot was done, the bank_idx was sent to
     101             :      tower, which tower used to query the bank and populate the vote
     102             :      accounts.  Tower needs to send back the bank_idx to replay so it
     103             :      can decrement the reference count on the bank. */
     104             : 
     105             :   ulong replay_slot;
     106             :   ulong replay_bank_idx;
     107             : 
     108             :   /* The slot being voted on.  There is not always a vote slot (locked
     109             :      out, failed switch threshhold, etc.) and will be set to ULONG_MAX
     110             :      when there is no slot to vote on.  When set, the vote slot is used
     111             :      by the vote sending tile to do some internal book-keeping related
     112             :      to leader targeting. */
     113             : 
     114             :   ulong vote_slot;
     115             : 
     116             :   /* The slot to reset leader pipeline to.  Unlike vote slot, the reset
     117             :      slot is always set and represents the consensus fork to build on.
     118             :      It may be unchanged since the last slot done.  reset_block_id is
     119             :      a unique identifier in case there are multiple blocks for the reset
     120             :      slot due to equivocation. */
     121             : 
     122             :   ulong     reset_slot;
     123             :   fd_hash_t reset_block_id;
     124             : 
     125             :   /* Sometimes, finishing replay of a slot may cause a new slot to be
     126             :      rooted.  If this happens, new root will be 1 and both root_slot and
     127             :      root_block_id will be set to the new root values accordingly.
     128             :      Otherwise, new_root will be 0 and root_slot and root_block_id will
     129             :      be undefined.  Note it is possible tower emits a new root slot but
     130             :      the new root slot's block_id is unavailable (eg. it is an old tower
     131             :      vote that precedes the snapshot slot).  In this case new_root will
     132             :      _not_ be set to 1. */
     133             : 
     134             :   ulong     root_slot;
     135             :   fd_hash_t root_block_id;
     136             : 
     137             :   /* The number of leaves in the forks tree. */
     138             : 
     139             :   ulong active_fork_cnt;
     140             : 
     141             :   /* This always contains a vote transaction with our current tower,
     142             :      regardless of whether there is a new vote slot or not (ie. vote
     143             :      slot can be ULONG_MAX and vote_txn will contain a txn of our
     144             :      current tower).  The vote is not yet signed.  This is necessary to
     145             :      support refreshing our last vote, ie. we retransmit our vote even
     146             :      when we are locked out / can't switch vote forks.  If the vote
     147             :      account's authorized voter is either the identity or one of the
     148             :      authorized voters, then is_valid_vote will be 1; otherwise it will
     149             :      be 0.
     150             : 
     151             :      The authority_idx is the index of the authorized voter that needs
     152             :      to sign the vote transaction.  If the authorized voter is the
     153             :      identity, the authority_idx will be ULONG_MAX.
     154             : 
     155             :      TODO: Need to implement "refresh last vote" logic. */
     156             : 
     157             :   int   has_vote_txn;
     158             :   ulong authority_idx;
     159             :   ulong vote_txn_sz;
     160             :   uchar vote_txn[ FD_TPU_MTU ];
     161             : 
     162             :   /* The latest balance in lamports of our vote account, or ULONG_MAX if
     163             :      our account is not found. */
     164             : 
     165             :   ulong vote_acct_bal;
     166             : 
     167             :   /* Our current on-chain tower with latencies optionally included. */
     168             : 
     169             :   ulong              tower_cnt;
     170             :   fd_vote_acc_vote_t tower[FD_TOWER_VOTE_MAX];
     171             : };
     172             : typedef struct fd_tower_slot_done fd_tower_slot_done_t;
     173             : 
     174             : struct fd_tower_slot_duplicate {
     175             :   fd_gossip_duplicate_shred_t chunks[ FD_EQVOC_CHUNK_CNT ];
     176             : };
     177             : typedef struct fd_tower_slot_duplicate fd_tower_slot_duplicate_t;
     178             : 
     179             : struct fd_tower_slot_ignored {
     180             :   ulong slot;
     181             :   ulong bank_idx;
     182             : };
     183             : typedef struct fd_tower_slot_ignored fd_tower_slot_ignored_t;
     184             : 
     185             : union fd_tower_msg {
     186             :   fd_tower_slot_confirmed_t slot_confirmed;
     187             :   fd_tower_slot_done_t      slot_done;
     188             :   fd_tower_slot_duplicate_t slot_duplicate;
     189             :   fd_tower_slot_ignored_t   slot_ignored;
     190             :   fd_tower_slot_rooted_t    slot_rooted;
     191             : };
     192             : typedef union fd_tower_msg fd_tower_msg_t;
     193             : 
     194             : extern fd_topo_run_tile_t fd_tile_tower;
     195             : 
     196             : /* The danger of the circular reliable link between tower and replay is
     197             :    that if tower backpressures replay, and happens to have backed-up
     198             :    confirmations to publish in after_credit, then tower_out link will
     199             :    become full.  If tower doesn't drain from replay_exec in the next
     200             :    returnable_frag call, this will cause a credit starvation loop
     201             :    between tower and replay, which causes both tiles to stall
     202             :    completely.
     203             : 
     204             :    Since there's no way to guarantee tower read from a specific link,
     205             :    (and no way to guarantee replay will read from a specific link), so
     206             :    we just make sure tower_out is large enough that the likelihood that
     207             :    the link is close to full and the above scenario happens is low. */
     208             : 
     209             : #endif /* HEADER_fd_src_discof_tower_fd_tower_tile_h */

Generated by: LCOV version 1.14