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 "../../choreo/eqvoc/fd_eqvoc.h" 5 : #include "../../choreo/tower/fd_tower.h" 6 : #include "../../choreo/voter/fd_voter.h" 7 : #include "../../disco/topo/fd_topo.h" 8 : 9 0 : #define FD_TOWER_SIG_SLOT_DONE (0) 10 0 : #define FD_TOWER_SIG_SLOT_CONFIRMED (1) 11 0 : #define FD_TOWER_SIG_SLOT_IGNORED (2) 12 0 : #define FD_TOWER_SIG_SLOT_DUPLICATE (3) 13 : 14 : /* fd_tower_slot_confirmed provides confirmed notifications of different 15 : Solana confirmation levels. The levels are: 16 : 17 : - duplicate: a block is duplicate confirmed if it has received votes 18 : from at least 52% of stake in the cluster. 19 : 20 : - optimistic: a block is optimistically confirmed if it has received 21 : votes from at least 2/3 of stake in the cluster and we have already 22 : replayed it (bank is available). 23 : 24 : - cluster: same as optimistic, but may not have replayed / can be 25 : delivered out of order. 26 : 27 : - super: same as optimistic, but the stake threshold is 4/5 28 : of stake. 29 : 30 : - rooted: a block is rooted if it or any of its descendants reach max 31 : lockout per TowerBFT rules. 32 : 33 : For optimistic, super, and rooted confirmations, the tower tile 34 : guarantees that we have already replayed the block. This is not the 35 : case for duplicate and cluster confirmations (a block can get 36 : duplicate or cluster confirmed before it has been replayed). 37 : Optimistic, super, and rooted confirmations are also 38 : guaranteed to be delivered in-order with no gaps from tower. That 39 : is, if we receive a rooted frag for slot N, we will have already 40 : received rooted frags for any ancestor slots N - 1, N - 2, ... (if 41 : they are not skipped / on a different fork) and likewise for 42 : optimistic. 43 : 44 : Note even if tower never actually voted on a slot (and therefore the 45 : slot never became a tower root), tower will still send a rooted 46 : confirmation for that slot if a descendant is voted on and eventually 47 : rooted. 48 : 49 : The reason both optimistic and cluster confirmed exist is "cluster" 50 : is intended to be consumed by the Solana RPC protocol, whereas 51 : optimistic is intended for Firedancer-specific APIs (hence in-order 52 : and no gap guarantees) */ 53 : 54 0 : #define FD_TOWER_SLOT_CONFIRMED_DUPLICATE 0 55 0 : #define FD_TOWER_SLOT_CONFIRMED_OPTIMISTIC 1 56 0 : #define FD_TOWER_SLOT_CONFIRMED_CLUSTER 2 57 0 : #define FD_TOWER_SLOT_CONFIRMED_SUPER 3 58 0 : #define FD_TOWER_SLOT_CONFIRMED_ROOTED 4 59 : 60 : struct fd_tower_slot_confirmed { 61 : ulong slot; 62 : fd_hash_t block_id; 63 : ulong bank_idx; /* only valid for OPTIMISTIC or ROOTED kind (otherwise ULONG_MAX) */ 64 : int kind; 65 : }; 66 : typedef struct fd_tower_slot_confirmed fd_tower_slot_confirmed_t; 67 : 68 : /* In response to finishing replay of a slot, the tower tile will 69 : produce both a block to vote for and block to reset to, and 70 : potentially advance the root. */ 71 : 72 : struct fd_tower_slot_done { 73 : 74 : /* This tower_slot_done message is 1-to-1 with the completion of a 75 : replayed slot. When that slot was done, the bank_idx was sent to 76 : tower, which tower used to query the bank and populate the vote 77 : accounts. Tower needs to send back the bank_idx to replay so it 78 : can decrement the reference count on the bank. */ 79 : 80 : ulong replay_slot; 81 : ulong replay_bank_idx; 82 : 83 : /* The slot being voted on. There is not always a vote slot (locked 84 : out, failed switch threshhold, etc.) and will be set to ULONG_MAX 85 : when there is no slot to vote on. When set, the vote slot is used 86 : by the vote sending tile to do some internal book-keeping related 87 : to leader targeting. */ 88 : 89 : ulong vote_slot; 90 : 91 : /* The slot to reset leader pipeline to. Unlike vote slot, the reset 92 : slot is always set and represents the consensus fork to build on. 93 : It may be unchanged since the last slot done. reset_block_id is 94 : a unique identifier in case there are multiple blocks for the reset 95 : slot due to equivocation. */ 96 : 97 : ulong reset_slot; 98 : fd_hash_t reset_block_id; 99 : 100 : /* Sometimes, finishing replay of a slot may cause a new slot to be 101 : rooted. If this happens, new root will be 1 and both root_slot and 102 : root_block_id will be set to the new root values accordingly. 103 : Otherwise, new_root will be 0 and root_slot and root_block_id will 104 : be undefined. Note it is possible tower emits a new root slot but 105 : the new root slot's block_id is unavailable (eg. it is an old tower 106 : vote that precedes the snapshot slot). In this case new_root will 107 : _not_ be set to 1. */ 108 : 109 : ulong root_slot; 110 : fd_hash_t root_block_id; 111 : 112 : /* The number of leaves in the forks tree. */ 113 : 114 : ulong active_fork_cnt; 115 : 116 : /* This always contains a vote transaction with our current tower, 117 : regardless of whether there is a new vote slot or not (ie. vote 118 : slot can be ULONG_MAX and vote_txn will contain a txn of our 119 : current tower). The vote is not yet signed. This is necessary to 120 : support refreshing our last vote, ie. we retransmit our vote even 121 : when we are locked out / can't switch vote forks. If the vote 122 : account's authorized voter is either the identity or one of the 123 : authorized voters, then is_valid_vote will be 1; otherwise it will 124 : be 0. 125 : 126 : The authority_idx is the index of the authorized voter that needs 127 : to sign the vote transaction. If the authorized voter is the 128 : identity, the authority_idx will be ULONG_MAX. 129 : 130 : TODO: Need to implement "refresh last vote" logic. */ 131 : 132 : int has_vote_txn; 133 : ulong authority_idx; 134 : ulong vote_txn_sz; 135 : uchar vote_txn[ FD_TPU_MTU ]; 136 : 137 : /* The latest balance in lamports of our vote account, or ULONG_MAX if 138 : our account is not found. */ 139 : 140 : ulong vote_acct_bal; 141 : 142 : /* Our current on-chain tower with latencies optionally included. */ 143 : 144 : ulong tower_cnt; 145 : fd_voter_vote_t tower[ FD_TOWER_VOTE_MAX ]; 146 : }; 147 : typedef struct fd_tower_slot_done fd_tower_slot_done_t; 148 : 149 : struct fd_tower_slot_duplicate { 150 : fd_gossip_duplicate_shred_t chunks[ FD_EQVOC_CHUNK_CNT ]; 151 : }; 152 : typedef struct fd_tower_slot_duplicate fd_tower_slot_duplicate_t; 153 : 154 : struct fd_tower_slot_ignored { 155 : ulong slot; 156 : ulong bank_idx; 157 : }; 158 : typedef struct fd_tower_slot_ignored fd_tower_slot_ignored_t; 159 : 160 : union fd_tower_msg { 161 : fd_tower_slot_confirmed_t slot_confirmed; 162 : fd_tower_slot_done_t slot_done; 163 : fd_tower_slot_duplicate_t slot_duplicate; 164 : fd_tower_slot_ignored_t slot_ignored; 165 : }; 166 : typedef union fd_tower_msg fd_tower_msg_t; 167 : 168 : extern fd_topo_run_tile_t fd_tile_tower; 169 : 170 : /* The danger of the circular reliable link between tower and replay is 171 : that if tower backpressures replay, and happens to have backed-up 172 : confirmations to publish in after_credit, then tower_out link will 173 : become full. If tower doesn't drain from replay_exec in the next 174 : returnable_frag call, this will cause a credit starvation loop 175 : between tower and replay, which causes both tiles to stall 176 : completely. 177 : 178 : Since there's no way to guarantee tower read from a specific link, 179 : (and no way to guarantee replay will read from a specific link), so 180 : we just make sure tower_out is large enough that the likelihood that 181 : the link is close to full and the above scenario happens is low. */ 182 : 183 : #endif /* HEADER_fd_src_discof_tower_fd_tower_tile_h */