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 a new consensus root is finalized, 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_store_publish to prune the store of any FEC sets below the new 30 : 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 0 : #define REPLAY_SIG_ROOT_ADVANCED (2) 107 0 : #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 : #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 0 : #define REPLAY_SIG_MISSING_FEC (13) 118 0 : #define REPLAY_SIG_NEXT_LEADER (14) 119 : 120 : /* replay_out mcache seq[i] slots */ 121 : #define REPLAY_SYNC_SEQ (0UL) /* mcache->seq[0]: recently published seq no */ 122 : #define REPLAY_SYNC_SNAP (1UL) /* mcache->seq[1]: last published snap msg (acq-rel) */ 123 : 124 : /* fd_replay_slot_completed promises that it will deliver at most 2 125 : frags for a given slot (at most 2 equivocating blocks). The first 126 : block is the first one we replay to completion. The second version 127 : (if there is) is always the confirmed equivocating block. This 128 : guarantee is provided by fd_reasm. */ 129 : 130 : struct fd_replay_slot_completed { 131 : ulong slot; 132 : ulong root_slot; 133 : ulong storage_slot; 134 : ulong epoch; 135 : ulong slot_in_epoch; 136 : ulong slots_per_epoch; 137 : ulong block_height; 138 : ulong parent_slot; 139 : 140 : fd_hash_t block_id; /* block id (last FEC set's merkle root) of the slot received from replay */ 141 : fd_hash_t parent_block_id; /* parent block id of the slot received from replay */ 142 : fd_hash_t bank_hash; /* bank hash of the slot received from replay */ 143 : fd_hash_t block_hash; /* last microblock header hash of slot received from replay */ 144 : ulong transaction_count; /* since genesis */ 145 : 146 : struct { 147 : double initial; 148 : double terminal; 149 : double taper; 150 : double foundation; 151 : double foundation_term; 152 : } inflation; 153 : 154 : struct { 155 : ulong lamports_per_uint8_year; 156 : double exemption_threshold; 157 : uchar burn_percent; 158 : } rent; 159 : 160 : /* Reference to the bank for this completed slot. */ 161 : ulong bank_idx; 162 : ulong bank_seq; 163 : ulong parent_bank_idx; /* parent bank's pool index (ULONG_MAX if none) */ 164 : ulong parent_bank_seq; /* parent bank's app-wide seq (ULONG_MAX if none) */ 165 : fd_accdb_fork_id_t accdb_fork_id; 166 : 167 : long first_fec_set_received_nanos; /* timestamp when replay received the first fec of the slot from turbine or repair */ 168 : long preparation_begin_nanos; /* timestamp when replay began preparing the state to begin execution of the slot */ 169 : long first_transaction_scheduled_nanos; /* timestamp when replay first sent a transaction to be executed */ 170 : long last_transaction_finished_nanos; /* timestamp when replay received the last execution completion */ 171 : long completion_time_nanos; /* timestamp when replay completed finalizing the slot */ 172 : 173 : int is_leader; /* whether we were leader for this slot */ 174 : ulong identity_balance; 175 : 176 : /* since slot start, default ULONG_MAX */ 177 : ulong vote_success; 178 : ulong vote_failed; 179 : ulong nonvote_success; 180 : ulong nonvote_failed; 181 : 182 : ulong transaction_fee; 183 : ulong priority_fee; 184 : ulong tips; 185 : ulong shred_cnt; 186 : 187 : int voted; /* our vote was in the reward cert this block carried */ 188 : ushort voted_rank; /* our rank in the reward slot's epoch, USHORT_MAX if we are not a voter */ 189 : ushort vote_count; /* distinct signers of the reward certs this block carried (votes for slot-FD_NUM_SLOTS_FOR_REWARD), USHORT_MAX if unknown */ 190 : ulong vote_balance; /* ULONG_MAX if not sampled */ 191 : ushort vote_commission; /* USHORT_MAX if not sampled */ 192 : 193 : struct { 194 : ulong block_cost; 195 : ulong allocated_accounts_data_size; 196 : ulong block_cost_limit; 197 : ulong account_cost_limit; 198 : ulong pool_idx; 199 : } cost_tracker; 200 : 201 : fd_block_footer_t footer; 202 : }; 203 : 204 : typedef struct fd_replay_slot_completed fd_replay_slot_completed_t; 205 : 206 : struct fd_replay_slot_dead { 207 : ulong slot; 208 : fd_hash_t block_id; 209 : 210 : /* Agave can finalize off the certs in a dead block's footer. 211 : 212 : TODO dead blocks short-circuit from parsing out the footer unless 213 : it's a BHM. Always read the footer? */ 214 : 215 : fd_block_footer_t footer; 216 : }; 217 : typedef struct fd_replay_slot_dead fd_replay_slot_dead_t; 218 : 219 : struct fd_replay_oc_advanced { 220 : ulong slot; 221 : ulong bank_idx; 222 : ulong bank_seq; /* fork discriminator of the optimistically-confirmed bank */ 223 : }; 224 : typedef struct fd_replay_oc_advanced fd_replay_oc_advanced_t; 225 : 226 : struct fd_replay_root_advanced { 227 : ulong bank_idx; 228 : ulong bank_seq; /* fork discriminator of the rooted bank */ 229 : ulong slot; 230 : fd_hash_t bank_hash; 231 : fd_hash_t block_id; 232 : }; 233 : typedef struct fd_replay_root_advanced fd_replay_root_advanced_t; 234 : 235 : struct fd_replay_txn_executed { 236 : fd_txn_p_t txn[ 1 ]; 237 : int is_committable; 238 : int is_fees_only; 239 : int is_noop; 240 : int txn_err; 241 : int is_simple_vote; 242 : 243 : /* LONG_MAX if stage was not reached */ 244 : long tick_parsed; 245 : long tick_sigverify_disp; 246 : long tick_sigverify_done; 247 : long tick_exec_disp; 248 : long tick_exec_done; 249 : long tick_load_start; 250 : long tick_check_start; 251 : long tick_exec_start; 252 : long tick_commit_start; 253 : long tick_commit_end; 254 : 255 : ulong slot; 256 : ulong bank_seq; 257 : ulong index_in_slot; 258 : ulong exec_tile_idx; 259 : ulong sigverify_exec_tile_idx; 260 : uint compute_units_consumed; /* possibly zero if is_committable is zero */ 261 : ulong max_compute_units; 262 : ulong transaction_fee; 263 : ulong priority_fee; 264 : ulong tips; 265 : }; 266 : typedef struct fd_replay_txn_executed fd_replay_txn_executed_t; 267 : 268 : struct fd_replay_fec_evicted { 269 : fd_hash_t mr; 270 : ulong slot; 271 : uint fec_set_idx; 272 : ulong bank_idx; 273 : }; 274 : typedef struct fd_replay_fec_evicted fd_replay_fec_evicted_t; 275 : 276 : /* Only rpc needs to consume this message since the resolv tile holds 277 : onto a bank reference based on the root, which will never be forced 278 : to drop its bank reference. */ 279 : struct fd_replay_drop_bank_ref { 280 : ulong bank_idx; 281 : }; 282 : typedef struct fd_replay_drop_bank_ref fd_replay_drop_bank_ref_t; 283 : 284 : /* The replay tile broadcasts fd_replay_snap_start_t 285 : (REPLAY_SIG_SNAP_START) just before starting snapshot creation. 286 : Replay keeps the bank's epoch credits pinned in memory until the 287 : snapshot is done. */ 288 : 289 : struct fd_replay_snap_start { 290 : ulong bank_idx; 291 : ulong base_slot; 292 : ulong slot; /* ==base_slot implies full snapshot, else incremental */ 293 : fd_pubkey_t leader; /* leader of slot, written to the manifest for Agave */ 294 : }; 295 : typedef struct fd_replay_snap_start fd_replay_snap_start_t; 296 : 297 : /* fd_replay_final_cert carries the finalization cert parsed out of an 298 : Alpenglow block footer. */ 299 : struct fd_replay_final_cert { 300 : ulong slot; /* the block whose footer carried the cert */ 301 : uint cert_cnt; 302 : ag_cert_t certs[ 2 ]; 303 : }; 304 : typedef struct fd_replay_final_cert fd_replay_final_cert_t; 305 : 306 : struct fd_replay_leader_footer { 307 : ulong slot; 308 : fd_block_footer_t footer; 309 : }; 310 : typedef struct fd_replay_leader_footer fd_replay_leader_footer_t; 311 : 312 : struct fd_replay_next_leader { 313 : ulong slot; 314 : ulong next_leader_slot; 315 : }; 316 : typedef struct fd_replay_next_leader fd_replay_next_leader_t; 317 : 318 : union fd_replay_message { 319 : fd_replay_slot_completed_t slot_completed; 320 : fd_replay_slot_dead_t slot_dead; 321 : fd_replay_root_advanced_t root_advanced; 322 : fd_replay_oc_advanced_t oc_advanced; 323 : fd_poh_reset_t reset; 324 : fd_became_leader_t became_leader; 325 : fd_replay_txn_executed_t txn_executed; 326 : fd_replay_fec_evicted_t reasm_evicted; 327 : fd_replay_drop_bank_ref_t drop_bank_ref; 328 : fd_replay_leader_footer_t leader_footer; 329 : fd_replay_next_leader_t next_leader; 330 : }; 331 : 332 : typedef union fd_replay_message fd_replay_message_t; 333 : 334 : #endif /* HEADER_fd_src_discof_replay_fd_replay_tile_h */