Line data Source code
1 : #ifndef HEADER_fd_src_discof_backup_fd_txncache_writer_h 2 : #define HEADER_fd_src_discof_backup_fd_txncache_writer_h 3 : 4 : /* fd_txncache_writer serializes the rooted part of a txncache into the 5 : snapshots/status_cache file of a Solana snapshot (bincode 6 : Vec<SlotDelta>), shaped the way Agave writes it: one slot delta per 7 : recent rooted slot with a block, holding the transactions that 8 : executed in that slot, grouped by the blockhash they referenced. 9 : Holds txncache read locks while walking the txncache. */ 10 : 11 : #include "../../flamenco/runtime/fd_txncache.h" 12 : #include "../../flamenco/runtime/fd_txncache_shmem.h" 13 : 14 : /* FD_TXNCACHE_WRITER_MAX_SLOT_DELTAS is the number of slot deltas a 15 : status cache holds, mirroring Agave's StatusCache::max_root_entries 16 : (default MAX_ROOT_ENTRIES = MAX_RECENT_BLOCKHASHES = 300; the 17 : constant status_cache::MAX_CACHE_ENTRIES up to v3.x). Snapshot load 18 : walks the SlotHistory sysvar back from the snapshot slot and requires 19 : a slot delta for each of the newest max_root_entries slots that has 20 : a block. No shipped Agave changes it from 300; if it ever becomes 21 : configurable, this and FD_SLOT_DELTA_MAX_ENTRIES must follow. 22 : https://github.com/anza-xyz/agave/blob/v3.1.8/runtime/src/snapshot_bank_utils.rs#L609 */ 23 : 24 17751 : #define FD_TXNCACHE_WRITER_MAX_SLOT_DELTAS (300UL) 25 : 26 653847 : #define FD_TXNCACHE_WRITER_MAX_BLOCKHASHES (FD_TXNCACHE_MAX_SLOT_DELTAS+1UL) 27 : 28 : #define FD_TXNCACHE_WRITER_MAX_GROUPS (FD_TXNCACHE_WRITER_MAX_SLOT_DELTAS*FD_TXNCACHE_WRITER_MAX_BLOCKHASHES) 29 : 30 : /* FD_TXNCACHE_WRITER_BUF_MIN is the smallest output buffer 31 : fd_txncache_writer_serialize accepts (fits any single record). 32 : Serialization emits whole records only and stops when the next one 33 : does not fit; the caller comes back with more room. */ 34 : 35 21 : #define FD_TXNCACHE_WRITER_BUF_MIN (4096UL) 36 : 37 6 : #define FD_TXNCACHE_WRITER_RELOCK_THRESH (1UL<<16) 38 : 39 : struct fd_txncache_writer_blockhash_desc { 40 : ulong blockcache_idx; /* blockcache pool index */ 41 : uint generation; /* blockcache generation at init, detects reuse */ 42 : uchar blockhash[ 32UL ]; 43 : ulong txnhash_offset; 44 : }; 45 : 46 : typedef struct fd_txncache_writer_blockhash_desc fd_txncache_writer_blockhash_desc_t; 47 : 48 : /* A group is one non-empty (execution slot, referenced blockhash) pair 49 : and serializes as one wire group in one slot delta. The dense group 50 : key is a position in slots[] paired with a position in 51 : blockhash_descs[]. */ 52 : struct fd_txncache_writer_group { 53 : ushort slot_i; 54 : ushort blockhash_i; 55 : uint entry_cnt; /* transactions in the group */ 56 : uint arena_entry_off; 57 : uint filled_entry_cnt; /* entries copied so far in the current fill */ 58 : }; 59 : 60 : typedef struct fd_txncache_writer_group fd_txncache_writer_group_t; 61 : 62 : typedef uchar fd_txnhash_t[ 20UL ]; 63 : 64 : struct fd_txncache_writer { 65 : uint state; 66 : fd_txncache_t * tc; 67 : ulong snapshot_slot; 68 : ulong snapshot_root_idx; 69 : uint snapshot_root_generation; 70 : 71 : /* SlotDeltas (ascending). */ 72 : ulong slots[ FD_TXNCACHE_WRITER_MAX_SLOT_DELTAS ]; 73 : ulong slot_cnt; 74 : ulong group_cnt_by_slot_i[ FD_TXNCACHE_WRITER_MAX_SLOT_DELTAS ]; 75 : 76 : /* Referenced blockhash descriptors, oldest rooted blockcache first. */ 77 : fd_txncache_writer_blockhash_desc_t blockhash_descs[ FD_TXNCACHE_WRITER_MAX_BLOCKHASHES ]; 78 : ulong blockhash_cnt; 79 : ushort fork_id_to_slot_i[ USHORT_MAX+1UL ]; 80 : 81 : /* Groups in wire order (slot ascending, then blockhash descriptor ascending). */ 82 : uint entry_cnt_by_key[ FD_TXNCACHE_WRITER_MAX_GROUPS ]; 83 : uint group_i_by_key[ FD_TXNCACHE_WRITER_MAX_GROUPS ]; 84 : fd_txncache_writer_group_t groups[ FD_TXNCACHE_WRITER_MAX_GROUPS ]; 85 : ulong group_cnt; 86 : ulong entry_cnt; /* Transactions across all groups. */ 87 : 88 : /* Scratchpad for collecting transaction hashes in serialization 89 : order. */ 90 : fd_txnhash_t * arena; 91 : ulong arena_entry_cnt_max; 92 : ulong batch_hi; 93 : 94 : /* Serialization cursor. */ 95 : ulong slot_i; /* Next slot delta. */ 96 : ulong group_i; /* Next group. */ 97 : ulong entry_i; /* Next entry of the current group. */ 98 : }; 99 : 100 : typedef struct fd_txncache_writer fd_txncache_writer_t; 101 : 102 : FD_PROTOTYPES_BEGIN 103 : 104 : /* Returns the arena size the writer recommends for a txncache whose 105 : blocks hold at most max_txn_per_slot transactions. 106 : 107 : The writer stages transaction hashes in an arena the caller provides. 108 : The txncache stores transactions by referenced blockhash while the 109 : wire format wants them by execution slot, so the writer counts every 110 : group in one walk of the txncache, then fills the arena with as many 111 : consecutive groups as fit per further walk. A single group that does 112 : not fit the arena is fatal. Caller should use this function to size 113 : the arena. 114 : 115 : The arena must be aligned to fd_txncache_writer_arena_align(). */ 116 : 117 : FD_FN_CONST ulong 118 : fd_txncache_writer_arena_sz( ulong max_txn_per_slot ); 119 : 120 : FD_FN_CONST ulong 121 : fd_txncache_writer_arena_align( void ); 122 : 123 : /* fd_txncache_writer_init prepares writer to serialize the status 124 : cache of tc as of the rooted fork fork_id, which must be the most 125 : recent root. snapshot_slot is the fork's slot. slot_history points 126 : to the slot_history_sz byte bincode encoding of the SlotHistory 127 : sysvar in the bank's sysvar cache, which decides which slots get a 128 : slot delta and names each execution slot. arena is the arena_sz byte 129 : staging area. 130 : 131 : Walks the whole txncache once to count the transactions of every 132 : (slot, blockhash) group, so the serialized size is known on return. 133 : 134 : Returns writer on success. Returns NULL if fork_id is not the most 135 : recent root or if more than one rooted blockhash descriptor lacks a 136 : named execution slot. Replay should not be advancing the root while 137 : a snapshot is in progress, so snapmk treats NULL as fatal. */ 138 : 139 : fd_txncache_writer_t * 140 : fd_txncache_writer_init( fd_txncache_writer_t * writer, 141 : fd_txncache_t * tc, 142 : fd_txncache_fork_id_t fork_id, 143 : ulong snapshot_slot, 144 : uchar const * slot_history, 145 : ulong slot_history_sz, 146 : void * arena, 147 : ulong arena_sz ); 148 : 149 : /* fd_txncache_writer_serialized_sz returns the exact size in bytes of 150 : the status cache an initialized writer will produce. */ 151 : 152 : FD_FN_PURE ulong 153 : fd_txncache_writer_serialized_sz( fd_txncache_writer_t const * writer ); 154 : 155 : /* fd_txncache_writer_serialize writes the next chunk of the status 156 : cache to out_buf (buf_sz>=FD_TXNCACHE_WRITER_BUF_MIN). Returns the 157 : chunk size, or zero once the whole status cache was written. May 158 : walk the txncache to fill the arena with the next batch of groups. */ 159 : 160 : ulong 161 : fd_txncache_writer_serialize( fd_txncache_writer_t * writer, 162 : uchar * out_buf, 163 : ulong buf_sz ); 164 : 165 : FD_PROTOTYPES_END 166 : 167 : #endif /* HEADER_fd_src_discof_backup_fd_txncache_writer_h */