LCOV - code coverage report
Current view: top level - choreo/tower - fd_tower_blocks.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 17 17 100.0 %
Date: 2026-03-31 06:22:16 Functions: 4 64 6.2 %

          Line data    Source code
       1             : #ifndef HEADER_fd_src_choreo_tower_fd_tower_blocks_h
       2             : #define HEADER_fd_src_choreo_tower_fd_tower_blocks_h
       3             : 
       4             : #include "../fd_choreo_base.h"
       5             : #include "fd_tower_voters.h"
       6             : 
       7             : /* fd_tower_blocks maintains tower-specific metadata about every block,
       8             :    such as what block_id we last replayed, what block_id we voted for,
       9             :    and what block_id was ultimately "duplicate confirmed".
      10             : 
      11             :    This is used by tower to make voting decisions, such as whether or
      12             :    not we can switch "forks".  In this context, a fork is a branch of a
      13             :    tree that extends from the root to a leaf.  For example:
      14             : 
      15             :         /-- 3-- 4  (A)
      16             :    1-- 2
      17             :         \-- 5      (B)
      18             : 
      19             :    Here, A and B are two different forks.  A is [1, 2, 3, 4] and B is
      20             :    [1, 2, 5], two branches that each extend from the root to a leaf.
      21             : 
      22             :    Note that even though fd_tower_blocks is block_id-aware, it does not
      23             :    use them for determining parentage.  Instead, parentage is based on
      24             :    slot numbers, so in cases of equivocation (duplicate blocks), tower
      25             :    will consider something an ancestor or descendant even if the block
      26             :    ids do not chain.
      27             : 
      28             :    This behavior intentionally mirrors the Agave logic implemented in
      29             :    `make_check_switch_threshold_decision`.  Essentially, tower is unable
      30             :    to distinguish duplicates because the vote account format (in which
      31             :    towers are stored) only stores slot numbers and not block_ids. */
      32             : 
      33             : struct fd_tower_blk {
      34             :   ulong     slot;               /* map key */
      35             :   ulong     parent_slot;        /* parent slot */
      36             :   ulong     epoch;              /* epoch of this slot */
      37             :   int       replayed;           /* whether we've replayed this slot yet */
      38             :   fd_hash_t replayed_block_id;  /* the block_id we _last_ replayed for this slot */
      39             :   int       voted;              /* whether we voted for this slot yet */
      40             :   fd_hash_t voted_block_id;     /* the block_id we voted on for this slot */
      41             :   int       confirmed;          /* whether this slot has been duplicate confirmjed */
      42             :   fd_hash_t confirmed_block_id; /* the block_id that was duplicate confirmed */
      43             :   int       leader;             /* whether this slot was our own leader slot */
      44             :   int       propagated;         /* whether this slot has been propagation confirmed (1/3 stake) */
      45             :   ulong     prev_leader_slot;   /* previous slot in which we were leader as of this slot */
      46             : };
      47             : typedef struct fd_tower_blk fd_tower_blk_t;
      48             : 
      49             : #define MAP_NAME           fd_tower_blk
      50        2454 : #define MAP_T              fd_tower_blk_t
      51        5166 : #define MAP_KEY            slot
      52        2688 : #define MAP_KEY_NULL       ULONG_MAX
      53        2478 : #define MAP_KEY_INVAL(key) ((key)==ULONG_MAX)
      54             : #define MAP_MEMOIZE        0
      55             : #include "../../util/tmpl/fd_map_dynamic.c"
      56             : 
      57             : struct __attribute__((aligned(128UL))) fd_tower_blocks {
      58             :   fd_tower_blk_t * blk_map;
      59             : };
      60             : typedef struct fd_tower_blocks fd_tower_blocks_t;
      61             : 
      62             : FD_PROTOTYPES_BEGIN
      63             : 
      64             : FD_FN_CONST static inline ulong
      65         180 : fd_tower_blocks_align( void ) {
      66         180 :   return alignof(fd_tower_blocks_t);
      67         180 : }
      68             : 
      69             : FD_FN_CONST static inline ulong
      70          36 : fd_tower_blocks_footprint( ulong slot_max ) {
      71          36 :   int lg_slot_max = fd_ulong_find_msb( fd_ulong_pow2_up( slot_max ) ) + 1;
      72          36 :   return FD_LAYOUT_FINI(
      73          36 :     FD_LAYOUT_APPEND(
      74          36 :     FD_LAYOUT_APPEND(
      75          36 :     FD_LAYOUT_INIT,
      76          36 :       alignof(fd_tower_blocks_t), sizeof(fd_tower_blocks_t)             ),
      77          36 :       fd_tower_blk_align(),       fd_tower_blk_footprint( lg_slot_max ) ),
      78          36 :     fd_tower_blocks_align() );
      79          36 : }
      80             : 
      81             : /* fd_tower_blocks_new formats an unused memory region for use as a
      82             :    tower_blocks.  mem is a non-NULL pointer to this region in the local
      83             :    address space with the required footprint and alignment. */
      84             : 
      85             : void *
      86             : fd_tower_blocks_new( void * shmem,
      87             :                      ulong  slot_max,
      88             :                      ulong  seed );
      89             : 
      90             : /* fd_tower_blocks_join joins the caller to the tower_blocks. shblocks
      91             :    points to the first byte of the memory region backing the shblocks in
      92             :    the caller's address space.
      93             : 
      94             :    Returns a pointer in the local address space to blocks on success. */
      95             : 
      96             : fd_tower_blocks_t *
      97             : fd_tower_blocks_join( void * shblocks );
      98             : 
      99             : /* fd_tower_blocks_leave blocks a current local join.  Returns a pointer
     100             :    to the underlying shared memory region on success and NULL on failure
     101             :    (logs details).  Reasons for failure include blocks is NULL. */
     102             : 
     103             : void *
     104             : fd_tower_blocks_leave( fd_tower_blocks_t const * blocks );
     105             : 
     106             : /* fd_tower_blocks_delete unformats a memory region used as a blocks.
     107             :    Assumes only the local process is joined to the region.  Returns a
     108             :    pointer to the underlying shared memory region or NULL if used
     109             :    obviously in error (e.g. blocks is obviously not a blocks ...  logs
     110             :    details).  The ownership of the memory region is transferred to the
     111             :    caller. */
     112             : 
     113             : void *
     114             : fd_tower_blocks_delete( void * blocks );
     115             : 
     116             : /* fd_tower_is_slot_{ancestor,descendant} return 1 or 0 depending on
     117             :    whether the specified relationship is true in blocks. */
     118             : 
     119             : int
     120             : fd_tower_blocks_is_slot_ancestor( fd_tower_blocks_t * blocks,
     121             :                                   ulong               descendant_slot,
     122             :                                   ulong               ancestor_slot );
     123             : 
     124             : int
     125             : fd_tower_blocks_is_slot_descendant( fd_tower_blocks_t * blocks,
     126             :                                     ulong               ancestor_slot,
     127             :                                     ulong               descendant_slot );
     128             : 
     129             : /* fd_tower_blocks_lowest_common_ancestor returns the lowest common
     130             :    ancestor of slot1 and slot 2.  There is always an LCA in a valid
     131             :    tower_forks tree (the root). */
     132             : 
     133             : ulong
     134             : fd_tower_blocks_lowest_common_ancestor( fd_tower_blocks_t * blocks,
     135             :                                         ulong               slot1,
     136             :                                         ulong               slot2 );
     137             : 
     138             : /* fd_tower_blocks_canonical_block_id returns what we think to be the
     139             :    correct block id for a given slot, based on what we've observed.
     140             : 
     141             :    We prioritize in-order:
     142             :    1. the duplicate-confirmed block id
     143             :    2. our voted block id
     144             :    3. our first-replayed block id
     145             : 
     146             :    This is the canonical order because it reflects what we think is the
     147             :    "true" block id given the information we have.
     148             : 
     149             :    Agave behaves similarly, except they "purge" their replay bank hash
     150             :    so they're always comparing the confirmed block id */
     151             : 
     152             : fd_hash_t const *
     153             : fd_tower_blocks_canonical_block_id( fd_tower_blocks_t * blocks,
     154             :                                     ulong               slot );
     155             : 
     156             : /* fd_tower_blocks_{query,insert,remove} provide convenient wrappers for
     157             :    {querying,inserting,removing} into the underlying map. */
     158             : 
     159             : fd_tower_blk_t *
     160             : fd_tower_blocks_query( fd_tower_blocks_t * blocks,
     161             :                        ulong               slot );
     162             : 
     163             : fd_tower_blk_t *
     164             : fd_tower_blocks_insert( fd_tower_blocks_t * blocks,
     165             :                         ulong               slot,
     166             :                         ulong               parent_slot );
     167             : 
     168             : void
     169             : fd_tower_blocks_remove( fd_tower_blocks_t * blocks,
     170             :                         ulong               slot );
     171             : 
     172             : FD_PROTOTYPES_END
     173             : 
     174             : #endif /* HEADER_fd_src_choreo_tower_fd_tower_blocks_h */

Generated by: LCOV version 1.14