LCOV - code coverage report
Current view: top level - discof/tower - fd_tower_tile.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 11 0.0 %
Date: 2026-09-17 04:28:31 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 more than 1/3 of stake in the cluster.  This threshold
      21             :      is 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 more than 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 more than 2/3 of stake in the cluster.
      46             :      This threshold is important for end-users, who rely on the
      47             :      "confirmed" commitment status of blocks (queryable via RPC) to
      48             :      determine that their transaction has landed on a block that will
      49             :      not rollback. This is unimplemented in Firedancer and only relevant
      50             :      for RPC. (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             : #define FD_TOWER_SLOT_CONFIRMED_RATIO_CNT  FD_TOWER_SLOT_CONFIRMED_LEVEL_CNT
      84           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 }
      85           0 : #define FD_TOWER_SLOT_CONFIRMED_RATIOS     { 1.0/3,                              0.52,                              2.0/3,                              4.0/5 }
      86             : 
      87             : struct fd_tower_slot_confirmed {
      88             :   int       level;    /* the confirmation level, see FD_TOWER_SLOT_CONFIRMED_{...} above */
      89             :   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 */
      90             :   ulong     slot;     /* slot being confirmed (in general, a slot being confirmed more than once is possible but highly unlikely ) */
      91             :   fd_hash_t block_id; /* block id being confirmed (guaranteed unique) */
      92             : };
      93             : typedef struct fd_tower_slot_confirmed fd_tower_slot_confirmed_t;
      94             : 
      95             : /* In response to finishing replay of a slot, the tower tile will
      96             :    produce both a block to vote for and block to reset to, and
      97             :    potentially advance the root. */
      98             : 
      99             : struct fd_tower_slot_done {
     100             : 
     101             :   /* This tower_slot_done message is 1-to-1 with the completion of a
     102             :      replayed slot.  When that slot was done, the bank_idx was sent to
     103             :      tower, which tower used to query the bank and populate the vote
     104             :      accounts.  Tower needs to send back the bank_idx to replay so it
     105             :      can decrement the reference count on the bank. */
     106             : 
     107             :   ulong replay_slot;
     108             :   ulong replay_bank_idx;
     109             :   ulong replay_bank_seq;
     110             : 
     111             :   /* The slot being voted on.  There is not always a vote slot (locked
     112             :      out, failed switch threshold, etc.) and will be set to ULONG_MAX
     113             :      when there is no slot to vote on.  When set, the vote slot is used
     114             :      by the vote sending tile to do some internal book-keeping related
     115             :      to leader targeting. */
     116             : 
     117             :   ulong vote_slot;
     118             : 
     119             :   /* The slot to reset leader pipeline to.  Unlike vote slot, the reset
     120             :      slot is always set and represents the consensus fork to build on.
     121             :      It may be unchanged since the last slot done.  reset_block_id is
     122             :      a unique identifier in case there are multiple blocks for the reset
     123             :      slot due to equivocation. */
     124             : 
     125             :   ulong     reset_slot;
     126             :   fd_hash_t reset_block_id;
     127             :   ulong     reset_bank_seq;
     128             : 
     129             :   /* Sometimes, finishing replay of a slot may cause a new slot to be
     130             :      rooted.  If this happens, new root will be 1 and both root_slot and
     131             :      root_block_id will be set to the new root values accordingly.
     132             :      Otherwise, new_root will be 0 and root_slot and root_block_id will
     133             :      be undefined.  Note it is possible tower emits a new root slot but
     134             :      the new root slot's block_id is unavailable (eg. it is an old tower
     135             :      vote that precedes the snapshot slot).  In this case new_root will
     136             :      _not_ be set to 1. */
     137             : 
     138             :   ulong     root_slot;
     139             :   fd_hash_t root_block_id;
     140             : 
     141             :   /* The number of leaves in the forks tree. */
     142             : 
     143             :   ulong active_fork_cnt;
     144             : 
     145             :   /* is_voting reflects whether this validator is structurally a voter
     146             :      (holds the authorized voter key and its identity matches the vote
     147             :      account's node identity).  Unlike has_vote_txn, it is independent
     148             :      of whether a vote txn was produced this slot, so transient
     149             :      non-votes (lockout, failed switch threshold, empty tower, etc.) do
     150             :      not clear it.  A hot spare running under a dummy identity has
     151             :      is_voting=0. */
     152             : 
     153             :   int   is_voting;
     154             : 
     155             :   /* authority_idx is the index of the authorized voter that needs to
     156             :      sign the vote transaction, or ULONG_MAX if it is the identity. */
     157             : 
     158             :   int   has_vote_txn;
     159             :   ulong authority_idx;
     160             :   ulong vote_txn_sz;
     161             :   uchar vote_txn[ FD_TPU_MTU ];
     162             :   long  vote_created_nanos;
     163             : 
     164             :   /* The latest balance in lamports and commission in basis points of our
     165             :      vote account.  These are ULONG_MAX and USHORT_MAX respectively if
     166             :      our account is not found. */
     167             : 
     168             :   ulong  vote_acct_bal;
     169             :   ushort vote_acct_com;
     170             : };
     171             : typedef struct fd_tower_slot_done fd_tower_slot_done_t;
     172             : 
     173             : struct fd_tower_slot_duplicate {
     174             :   fd_gossip_duplicate_shred_t chunks[ FD_EQVOC_CHUNK_CNT ];
     175             : };
     176             : typedef struct fd_tower_slot_duplicate fd_tower_slot_duplicate_t;
     177             : 
     178             : struct fd_tower_slot_ignored {
     179             :   ulong slot;
     180             :   ulong bank_idx;
     181             : };
     182             : typedef struct fd_tower_slot_ignored fd_tower_slot_ignored_t;
     183             : 
     184             : union fd_tower_msg {
     185             :   fd_tower_slot_confirmed_t slot_confirmed;
     186             :   fd_tower_slot_done_t      slot_done;
     187             :   fd_tower_slot_duplicate_t slot_duplicate;
     188             :   fd_tower_slot_ignored_t   slot_ignored;
     189             :   fd_tower_slot_rooted_t    slot_rooted;
     190             : };
     191             : typedef union fd_tower_msg fd_tower_msg_t;
     192             : 
     193             : extern fd_topo_run_tile_t fd_tile_tower;
     194             : 
     195             : /* The danger of the circular reliable link between tower and replay is
     196             :    that if tower backpressures replay, and happens to have backed-up
     197             :    confirmations to publish in after_credit, then tower_out link will
     198             :    become full.  If tower doesn't drain from replay_exec in the next
     199             :    returnable_frag call, this will cause a credit starvation loop
     200             :    between tower and replay, which causes both tiles to stall
     201             :    completely.
     202             : 
     203             :    Since there's no way to guarantee tower read from a specific link,
     204             :    (and no way to guarantee replay will read from a specific link), so
     205             :    we just make sure tower_out is large enough that the likelihood that
     206             :    the link is close to full and the above scenario happens is low. */
     207             : 
     208             : #endif /* HEADER_fd_src_discof_tower_fd_tower_tile_h */

Generated by: LCOV version 1.14