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