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 */