Line data Source code
1 : #ifndef HEADER_fd_src_discof_replay_fd_replay_tile_h 2 : #define HEADER_fd_src_discof_replay_fd_replay_tile_h 3 : 4 : /* Banks and Reasm 5 : ================= 6 : 7 : OVERVIEW 8 : 9 : Reasm maintains a tree of FEC sets organized as a main tree (rooted 10 : at the published root) plus orphan trees. Each FEC set in the 11 : connected tree may be associated with a bank via bank_idx, or be 12 : still unreplayed. In general, reasm tries to approximate the state 13 : of banks as closely as possible. It's inexact, because reasm is 14 : stored at the FEC unit, while banks are stored at the slot unit. 15 : 16 : When reasm delivers a FEC set (via fd_reasm_pop), the replay tile 17 : processes it by assigning it a bank. If it's the first FEC in a 18 : slot (fec_set_idx==0), a new bank is provisioned from the parent's 19 : bank. Subsequent FECs in the same slot inherit the bank_idx from 20 : the preceding FEC. This means all FEC sets within a single slot 21 : share the same bank_idx, with the exception of equivocating FECs. 22 : 23 : PUBLISHING (ROOT ADVANCEMENT) 24 : 25 : When tower sends a new consensus root, replay advances the 26 : published root along the rooted fork as far as possible. A block 27 : on the rooted fork is safe to prune when it and all minority fork 28 : subtrees branching from it have refcnt 0. Publishing calls 29 : fd_reasm_publish to prune the reasm tree (and the store) of any 30 : FEC sets that do not descend from the new root. 31 : 32 : REASM EVICTION (POOL-PRESSURE EVICTION) 33 : 34 : When the reasm pool is nearly full (1 free element remaining) and a 35 : new FEC needs to be inserted, reasm runs its eviction policy to free 36 : space. The eviction in general prioritizes orphans first, and then 37 : frontier slots that are incomplete. 38 : 39 : If eviction succeeds, the evicted chain is returned as a linked 40 : list of pool elements (removed from maps but still acquired in 41 : the pool). The replay tile is responsible for: 42 : 1. Publishing each evicted FEC to repair (REPLAY_SIG_REASM_EVICTED) 43 : so repair can re-request the data. 44 : 2. Releasing each evicted element back to the reasm pool before 45 : the next insert. 46 : 47 : It's important to note that replay bank eviction is NOT coupled with 48 : reasm FEC eviction. Reasm FEC eviction is triggered by the reasm pool 49 : being full, and is independent of the replay bank eviction. Reasm 50 : FEC eviction is triggered by the reasm pool being full while banks 51 : eviction is triggered by the banks being full and the scheduler 52 : being drained. 53 : 54 : By evicting and publishing evicted FECs to repair, replay is 55 : attempting a "go-around" strategy to ensure progress is made even 56 : when memory pressure is high. An evicted FEC - if valid - will be 57 : requested by repair and eventually re-delivered to replay, where 58 : hopefully by then there will be pool capacity to insert and replay 59 : the FEC. 60 : 61 : SNAPSHOT PRODUCTION 62 : 63 : Snapshot production is either periodically scheduled (driven by 64 : replay tile) or externally requested (through admin tile). The 65 : replay tile stops compaction (via snapshot_sync) and rooting until 66 : the snapshot is created. 67 : 68 : ALPENGLOW 69 : 70 : Under alpenglow, the replay tile doesn't use the reasm, but rather 71 : relies on rotor to deliver FECs. Similar to reasm, rotor delivers 72 : FECs in replayable order, with no restrictions on interleaving 73 : between forks. Every delivered FEC is already in store. Alpenglow 74 : also introduces the double merkle root, which uniquely identifies 75 : each FEC set as part of one slot version. For equivocating slots, the 76 : block_id is known before delivery, and so replay can identify how to 77 : allocate banks in the equivocation case. 78 : 79 : Replay can rely on the fact that any new version of the slot 80 : (equivocations or not) is delivered from FEC 0 — blocks are never 81 : delivered starting mid-block. There is also at most one live delivery 82 : stream per logical block. When the turbine version is being streamed 83 : in, the block_id is still unknown, but if a votor-driven version of 84 : the block arrives before the turbine copy is complete, the turbine 85 : copy is abandoned, and will never finish delivering to replay. 86 : 87 : Alpenglow simplifies eviction logic by removing the notion of reasm 88 : evictions. Rotor is sized to protocol limits and will not have 89 : evictions; and thus can be relied on to always have all data since 90 : the root. Banks can still evict. On an eviction, some context in the 91 : replayable chain of FECs is lost, and newer incoming FECs may be 92 : unlinked. When this happens, replay sends a signal to rotor to send 93 : the next FEC with the full replayable path from root. Replay will 94 : need to drain the rotor dcache until it receives a FEC that is 95 : connected, and then resume normal replay. It may need to drop FECs 96 : that have already been replayed, or send multiple signals to rotor to 97 : re-deliver. */ 98 : 99 : #include "../poh/fd_poh_tile.h" 100 : #include "../../disco/tiles.h" 101 : #include "../../choreo/votor/ag_cert.h" 102 : #include "../../flamenco/alpenglow/fd_block_marker.h" 103 : 104 0 : #define REPLAY_SIG_SLOT_COMPLETED (0) 105 0 : #define REPLAY_SIG_SLOT_DEAD (1) 106 48 : #define REPLAY_SIG_ROOT_ADVANCED (2) 107 42 : #define REPLAY_SIG_RESET (3) 108 0 : #define REPLAY_SIG_BECAME_LEADER (4) 109 0 : #define REPLAY_SIG_OC_ADVANCED (5) 110 0 : #define REPLAY_SIG_TXN_EXECUTED (6) 111 0 : #define REPLAY_SIG_REASM_EVICTED (7) 112 0 : #define REPLAY_SIG_WFS_DONE (8) 113 0 : #define REPLAY_SIG_DROP_BANK_REF (9) 114 0 : #define REPLAY_SIG_SNAP_START (10) 115 : #define REPLAY_SIG_FINAL_CERT (11) 116 0 : #define REPLAY_SIG_LEADER_FOOTER (12) 117 72 : #define REPLAY_SIG_MISSING_FEC (13) 118 : 119 : /* replay_out mcache seq[i] slots */ 120 : #define REPLAY_SYNC_SEQ (0UL) /* mcache->seq[0]: recently published seq no */ 121 : #define REPLAY_SYNC_SNAP (1UL) /* mcache->seq[1]: last published snap msg (acq-rel) */ 122 : 123 : /* fd_replay_slot_completed promises that it will deliver at most 2 124 : frags for a given slot (at most 2 equivocating blocks). The first 125 : block is the first one we replay to completion. The second version 126 : (if there is) is always the confirmed equivocating block. This 127 : guarantee is provided by fd_reasm. */ 128 : 129 : struct fd_replay_slot_completed { 130 : ulong slot; 131 : ulong root_slot; 132 : ulong storage_slot; 133 : ulong epoch; 134 : ulong slot_in_epoch; 135 : ulong slots_per_epoch; 136 : ulong block_height; 137 : ulong parent_slot; 138 : 139 : fd_hash_t block_id; /* block id (last FEC set's merkle root) of the slot received from replay */ 140 : fd_hash_t parent_block_id; /* parent block id of the slot received from replay */ 141 : fd_hash_t bank_hash; /* bank hash of the slot received from replay */ 142 : fd_hash_t block_hash; /* last microblock header hash of slot received from replay */ 143 : ulong transaction_count; /* since genesis */ 144 : 145 : struct { 146 : double initial; 147 : double terminal; 148 : double taper; 149 : double foundation; 150 : double foundation_term; 151 : } inflation; 152 : 153 : struct { 154 : ulong lamports_per_uint8_year; 155 : double exemption_threshold; 156 : uchar burn_percent; 157 : } rent; 158 : 159 : /* Reference to the bank for this completed slot. */ 160 : ulong bank_idx; 161 : ulong bank_seq; 162 : ulong parent_bank_idx; /* parent bank's pool index (ULONG_MAX if none) */ 163 : ulong parent_bank_seq; /* parent bank's app-wide seq (ULONG_MAX if none) */ 164 : fd_accdb_fork_id_t accdb_fork_id; 165 : 166 : long first_fec_set_received_nanos; /* timestamp when replay received the first fec of the slot from turbine or repair */ 167 : long preparation_begin_nanos; /* timestamp when replay began preparing the state to begin execution of the slot */ 168 : long first_transaction_scheduled_nanos; /* timestamp when replay first sent a transaction to be executed */ 169 : long last_transaction_finished_nanos; /* timestamp when replay received the last execution completion */ 170 : long completion_time_nanos; /* timestamp when replay completed finalizing the slot and notified tower */ 171 : 172 : int is_leader; /* whether we were leader for this slot */ 173 : ulong identity_balance; 174 : 175 : /* since slot start, default ULONG_MAX */ 176 : ulong vote_success; 177 : ulong vote_failed; 178 : ulong nonvote_success; 179 : ulong nonvote_failed; 180 : 181 : ulong transaction_fee; 182 : ulong priority_fee; 183 : ulong tips; 184 : ulong shred_cnt; 185 : 186 : int voted; /* our vote was in the reward cert this block carried */ 187 : ushort voted_rank; /* our rank in the reward slot's epoch, USHORT_MAX if we are not a voter */ 188 : ulong vote_balance; /* ULONG_MAX if not sampled */ 189 : ushort vote_commission; /* USHORT_MAX if not sampled */ 190 : 191 : struct { 192 : ulong block_cost; 193 : ulong allocated_accounts_data_size; 194 : ulong block_cost_limit; 195 : ulong account_cost_limit; 196 : ulong pool_idx; 197 : } cost_tracker; 198 : }; 199 : 200 : typedef struct fd_replay_slot_completed fd_replay_slot_completed_t; 201 : 202 : struct fd_replay_slot_dead { 203 : ulong slot; 204 : fd_hash_t block_id; 205 : }; 206 : typedef struct fd_replay_slot_dead fd_replay_slot_dead_t; 207 : 208 : struct fd_replay_oc_advanced { 209 : ulong slot; 210 : ulong bank_idx; 211 : ulong bank_seq; /* fork discriminator of the optimistically-confirmed bank */ 212 : }; 213 : typedef struct fd_replay_oc_advanced fd_replay_oc_advanced_t; 214 : 215 : struct fd_replay_root_advanced { 216 : ulong bank_idx; 217 : ulong bank_seq; /* fork discriminator of the rooted bank */ 218 : ulong slot; 219 : fd_hash_t bank_hash; 220 : fd_hash_t block_id; 221 : }; 222 : typedef struct fd_replay_root_advanced fd_replay_root_advanced_t; 223 : 224 : struct fd_replay_txn_executed { 225 : fd_txn_p_t txn[ 1 ]; 226 : int is_committable; 227 : int is_fees_only; 228 : int is_noop; 229 : int txn_err; 230 : int is_simple_vote; 231 : 232 : /* LONG_MAX if stage was not reached */ 233 : long tick_parsed; 234 : long tick_sigverify_disp; 235 : long tick_sigverify_done; 236 : long tick_exec_disp; 237 : long tick_exec_done; 238 : long tick_load_start; 239 : long tick_check_start; 240 : long tick_exec_start; 241 : long tick_commit_start; 242 : long tick_commit_end; 243 : 244 : ulong slot; 245 : ulong bank_seq; 246 : ulong index_in_slot; 247 : ulong exec_tile_idx; 248 : ulong sigverify_exec_tile_idx; 249 : uint compute_units_consumed; /* possibly zero if is_committable is zero */ 250 : ulong max_compute_units; 251 : ulong transaction_fee; 252 : ulong priority_fee; 253 : ulong tips; 254 : }; 255 : typedef struct fd_replay_txn_executed fd_replay_txn_executed_t; 256 : 257 : struct fd_replay_fec_evicted { 258 : fd_hash_t mr; 259 : ulong slot; 260 : uint fec_set_idx; 261 : ulong bank_idx; 262 : }; 263 : typedef struct fd_replay_fec_evicted fd_replay_fec_evicted_t; 264 : 265 : /* Only rpc needs to consume this message since tower holds refcnts 266 : transiently and will drop them without a further trigger from the 267 : replay tile and the resolv tile holds onto a bank reference based on 268 : the root, which will never be forced to drop its bank reference. */ 269 : struct fd_replay_drop_bank_ref { 270 : ulong bank_idx; 271 : }; 272 : typedef struct fd_replay_drop_bank_ref fd_replay_drop_bank_ref_t; 273 : 274 : /* The replay tile broadcasts fd_replay_snap_start_t 275 : (REPLAY_SIG_SNAP_START) just before starting snapshot creation. */ 276 : 277 : struct fd_replay_snap_start { 278 : ulong bank_idx; 279 : ulong base_slot; 280 : ulong slot; /* ==base_slot implies full snapshot, else incremental */ 281 : }; 282 : typedef struct fd_replay_snap_start fd_replay_snap_start_t; 283 : 284 : /* fd_replay_final_cert carries the finalization cert parsed out of an 285 : Alpenglow block footer. */ 286 : struct fd_replay_final_cert { 287 : ulong slot; /* the block whose footer carried the cert */ 288 : uint cert_cnt; 289 : ag_cert_t certs[ 2 ]; 290 : }; 291 : typedef struct fd_replay_final_cert fd_replay_final_cert_t; 292 : 293 : struct fd_replay_leader_footer { 294 : ulong slot; 295 : fd_block_footer_t footer; 296 : }; 297 : typedef struct fd_replay_leader_footer fd_replay_leader_footer_t; 298 : 299 : union fd_replay_message { 300 : fd_replay_slot_completed_t slot_completed; 301 : fd_replay_slot_dead_t slot_dead; 302 : fd_replay_root_advanced_t root_advanced; 303 : fd_replay_oc_advanced_t oc_advanced; 304 : fd_poh_reset_t reset; 305 : fd_became_leader_t became_leader; 306 : fd_replay_txn_executed_t txn_executed; 307 : fd_replay_fec_evicted_t reasm_evicted; 308 : fd_replay_drop_bank_ref_t drop_bank_ref; 309 : fd_replay_leader_footer_t leader_footer; 310 : }; 311 : 312 : typedef union fd_replay_message fd_replay_message_t; 313 : 314 : #endif /* HEADER_fd_src_discof_replay_fd_replay_tile_h */