Line data Source code
1 : #ifndef HEADER_fd_src_flamenco_runtime_fd_blockstore_h
2 : #define HEADER_fd_src_flamenco_runtime_fd_blockstore_h
3 :
4 : /* Blockstore is a high-performance database for storing, building, and
5 : tracking blocks.
6 :
7 : `fd_blockstore` defines a number of useful types e.g. `fd_block_t`,
8 : `fd_block_shred`, etc.
9 :
10 : The blockstore alloc is used for allocating wksp resources for shred
11 : headers, microblock headers, and blocks. This is an fd_alloc.
12 : Allocations from this allocator will be tagged with wksp_tag and
13 : operations on this allocator will use concurrency group 0. */
14 :
15 : #include "../../ballet/block/fd_microblock.h"
16 : #include "../../ballet/shred/fd_deshredder.h"
17 : #include "../../ballet/shred/fd_shred.h"
18 : #include "../fd_flamenco_base.h"
19 : #include "../types/fd_types.h"
20 : #include "fd_rwseq_lock.h"
21 : #include "stdbool.h"
22 : #include <fcntl.h>
23 :
24 : /* FD_BLOCKSTORE_{ALIGN,FOOTPRINT} describe the alignment and footprint needed
25 : for a blockstore. ALIGN should be a positive integer power of 2.
26 : FOOTPRINT is multiple of ALIGN. These are provided to facilitate
27 : compile time declarations. */
28 :
29 : /* clang-format off */
30 : #define FD_BLOCKSTORE_ALIGN (128UL)
31 : #define FD_BLOCKSTORE_FOOTPRINT (256UL)
32 0 : #define FD_BLOCKSTORE_MAGIC (0xf17eda2ce7b10c00UL) /* firedancer bloc version 0 */
33 :
34 : /* DO NOT MODIFY. */
35 : // #define FD_BUF_SHRED_MAP_MAX (1UL << 24UL) /* 16 million shreds can be buffered */
36 : // #define FD_TXN_MAP_LG_MAX (24) /* 16 million txns can be stored in the txn map */
37 :
38 : /* TODO this can be removed if we explicitly manage a memory pool for
39 : the fd_block_map_t entries */
40 0 : #define FD_BLOCKSTORE_CHILD_SLOT_MAX (32UL) /* the maximum # of children a slot can have */
41 0 : #define FD_BLOCKSTORE_ARCHIVE_MIN_SIZE (1UL << 26UL) /* 64MB := ceil(MAX_DATA_SHREDS_PER_SLOT*1228) */
42 :
43 : // TODO centralize these
44 : // https://github.com/firedancer-io/solana/blob/v1.17.5/sdk/program/src/clock.rs#L34
45 : #define FD_MS_PER_TICK 6
46 :
47 : // https://github.com/firedancer-io/solana/blob/v1.17.5/core/src/repair/repair_service.rs#L55
48 : #define FD_REPAIR_TIMEOUT (200 / FD_MS_PER_TICK)
49 :
50 0 : #define FD_BLOCKSTORE_OK 0
51 0 : #define FD_BLOCKSTORE_OK_SLOT_COMPLETE 1
52 : #define FD_BLOCKSTORE_ERR_SHRED_FULL -1 /* no space left for shreds */
53 0 : #define FD_BLOCKSTORE_ERR_SLOT_FULL -2 /* no space left for slots */
54 : #define FD_BLOCKSTORE_ERR_TXN_FULL -3 /* no space left for txns */
55 : #define FD_BLOCKSTORE_ERR_SHRED_MISSING -4
56 0 : #define FD_BLOCKSTORE_ERR_SLOT_MISSING -5
57 0 : #define FD_BLOCKSTORE_ERR_TXN_MISSING -6
58 0 : #define FD_BLOCKSTORE_ERR_SHRED_INVALID -7 /* shred was invalid */
59 0 : #define FD_BLOCKSTORE_ERR_DESHRED_INVALID -8 /* deshredded block was invalid */
60 0 : #define FD_BLOCKSTORE_ERR_NO_MEM -9 /* no mem */
61 0 : #define FD_BLOCKSTORE_ERR_UNKNOWN -99
62 : /* clang-format on */
63 :
64 : struct fd_shred_key {
65 : ulong slot;
66 : uint idx;
67 : };
68 : typedef struct fd_shred_key fd_shred_key_t;
69 :
70 : /* clang-format off */
71 : static const fd_shred_key_t fd_shred_key_null = { 0 };
72 : #define FD_SHRED_KEY_NULL fd_shred_key_null
73 : #define FD_SHRED_KEY_INVAL(key) (!((key).slot) & !((key).idx))
74 0 : #define FD_SHRED_KEY_EQ(k0,k1) (!(((k0).slot) ^ ((k1).slot))) & !(((k0).idx) ^ (((k1).idx)))
75 0 : #define FD_SHRED_KEY_HASH(key) ((uint)(((key).slot)<<15UL) | (((key).idx))) /* current max shred idx is 32KB = 2 << 15*/
76 : /* clang-format on */
77 :
78 : /* fd_buf_shred is a thin wrapper around fd_shred_t that facilitates
79 : buffering data shreds before all the shreds for a slot have been
80 : received. After all shreds are received, these buffered shreds are
81 : released back into memory pool and future queries for the shreds are
82 : offset into the block data directly.
83 :
84 : The blockstore is only aware of data shreds and all APIs involving
85 : shreds refers to data shreds.
86 :
87 : Shreds are buffered into a map as they are received:
88 :
89 : | 0 | 1 | 2 | x | x | 5 | x |
90 : ^ ^
91 : c r
92 :
93 : c = "consumed" = contiguous window starting from index 0
94 : r = "received" = highest index received so far
95 :
96 : Shred memory layout while stored in the map:
97 :
98 : | shred hdr | shred payload |
99 : */
100 : struct fd_buf_shred {
101 : fd_shred_key_t key;
102 : ulong next;
103 : union {
104 : fd_shred_t hdr; /* data shred header */
105 : uchar raw[FD_SHRED_MAX_SZ]; /* the data shred as raw bytes, both header and payload. */
106 : };
107 : };
108 : typedef struct fd_buf_shred fd_buf_shred_t;
109 :
110 : #define POOL_NAME fd_buf_shred_pool
111 0 : #define POOL_T fd_buf_shred_t
112 : #include "../../util/tmpl/fd_pool.c"
113 :
114 : /* clang-format off */
115 : #define MAP_NAME fd_buf_shred_map
116 : #define MAP_ELE_T fd_buf_shred_t
117 : #define MAP_KEY_T fd_shred_key_t
118 0 : #define MAP_KEY_EQ(k0,k1) (FD_SHRED_KEY_EQ(*k0,*k1))
119 0 : #define MAP_KEY_HASH(key,seed) (FD_SHRED_KEY_HASH(*key)^seed)
120 : #include "../../util/tmpl/fd_map_chain.c"
121 : /* clang-format on */
122 :
123 : #define DEQUE_NAME fd_slot_deque
124 0 : #define DEQUE_T ulong
125 : #include "../../util/tmpl/fd_deque_dynamic.c"
126 :
127 : /* fd_block_shred_t is a shred that has been assembled into a block. The
128 : shred begins at `off` relative to the start of the block's data
129 : region. */
130 : struct fd_block_shred {
131 : fd_shred_t hdr; /* ptr to the data shred header */
132 : uchar merkle[FD_SHRED_MERKLE_ROOT_SZ + FD_SHRED_MERKLE_NODE_SZ*9U /* FD_FEC_SET_MAX_BMTREE_DEPTH */];
133 : ulong merkle_sz;
134 : ulong off; /* offset to the payload relative to the start of the block's data region */
135 : };
136 : typedef struct fd_block_shred fd_block_shred_t;
137 :
138 : /*
139 : * fd_block_entry_batch_t is a microblock/entry batch within a block.
140 : * The offset is relative to the start of the block's data region,
141 : * and indicates where the batch ends. The (exclusive) end offset of
142 : * batch i is the (inclusive) start offset of batch i+1. The 0th batch
143 : * always starts at offset 0.
144 : * On the wire, the presence of one of the COMPLETE flags in a data
145 : * shred marks the end of a batch.
146 : * In other words, batch ends are aligned with shred ends, and batch
147 : * starts are aligned with shred starts. Usually a batch comprises
148 : * multiple shreds, and a block comprises multiple batches.
149 : * This information is useful because bincode deserialization needs to
150 : * be performed on a per-batch basis. Precisely a single array of
151 : * microblocks/entries is expected to be deserialized from a batch.
152 : * Trailing bytes in each batch are ignored by default.
153 : */
154 : struct fd_block_entry_batch {
155 : ulong end_off; /* exclusive */
156 : };
157 : typedef struct fd_block_entry_batch fd_block_entry_batch_t;
158 :
159 : /* fd_block_micro_t is a microblock ("entry" in Solana parlance) within
160 : a block. The microblock begins at `off` relative to the start of the
161 : block's data region. */
162 : struct fd_block_micro {
163 : ulong off; /* offset into block data */
164 : };
165 : typedef struct fd_block_micro fd_block_micro_t;
166 :
167 : /* fd_block_txn_t is a transaction that has been parsed and is part of a
168 : block. The transaction begins at `off` relative to the start of the
169 : block's data region. */
170 : struct fd_block_txn {
171 : ulong txn_off; /* offset into block data of transaction */
172 : ulong id_off; /* offset into block data of transaction identifiers */
173 : ulong sz;
174 : };
175 : typedef struct fd_block_txn fd_block_txn_t;
176 :
177 : /* If the 0th bit is set, this indicates the block is preparing, which
178 : means it might be partially executed e.g. a subset of the microblocks
179 : have been executed. It is not safe to remove, relocate, or modify
180 : the block in any way at this time.
181 :
182 : Callers holding a pointer to a block should always make sure to
183 : inspect this flag.
184 :
185 : Other flags mainly provide useful metadata for read-only callers, eg.
186 : RPC. */
187 :
188 0 : #define FD_BLOCK_FLAG_RECEIVING 0 /* xxxxxxx1 still receiving shreds */
189 0 : #define FD_BLOCK_FLAG_COMPLETED 1 /* xxxxxx1x received the block ie. all shreds (SLOT_COMPLETE) */
190 0 : #define FD_BLOCK_FLAG_REPLAYING 2 /* xxxxx1xx replay in progress (DO NOT REMOVE) */
191 0 : #define FD_BLOCK_FLAG_PROCESSED 3 /* xxxx1xxx successfully replayed the block */
192 0 : #define FD_BLOCK_FLAG_EQVOCSAFE 4 /* xxxx1xxx 52% of cluster has voted on this (slot, bank hash) */
193 0 : #define FD_BLOCK_FLAG_CONFIRMED 5 /* xxx1xxxx 2/3 of cluster has voted on this (slot, bank hash) */
194 0 : #define FD_BLOCK_FLAG_FINALIZED 6 /* xx1xxxxx 2/3 of cluster has rooted this slot */
195 : #define FD_BLOCK_FLAG_DEADBLOCK 7 /* x1xxxxxx failed to replay the block */
196 :
197 : /* Rewards assigned after block is executed */
198 :
199 : struct fd_block_rewards {
200 : ulong collected_fees;
201 : fd_hash_t leader;
202 : ulong post_balance;
203 : };
204 : typedef struct fd_block_rewards fd_block_rewards_t;
205 :
206 : /* Remaining bits [4, 8) are reserved.
207 :
208 : To avoid confusion, please use `fd_bits.h` API
209 : ie. `fd_uchar_set_bit`, `fd_uchar_extract_bit`. */
210 :
211 : struct fd_block {
212 :
213 : /* Computed rewards */
214 :
215 : fd_block_rewards_t rewards;
216 :
217 : /* data region
218 :
219 : A block's data region is indexed to support iterating by shred,
220 : microblock/entry batch, microblock/entry, or transaction.
221 : This is done by iterating the headers for each, stored in allocated
222 : memory.
223 : To iterate shred payloads, for example, a caller should iterate the headers in tandem with the data region
224 : (offsetting by the bytes indicated in the shred header).
225 :
226 : Note random access of individual shred indices is not performant, due to the variable-length
227 : nature of shreds. */
228 :
229 : ulong data_gaddr; /* ptr to the beginning of the block's allocated data region */
230 : ulong data_sz; /* block size */
231 : ulong shreds_gaddr; /* ptr to the first fd_block_shred_t */
232 : ulong shreds_cnt;
233 : ulong batch_gaddr; /* list of fd_block_entry_batch_t */
234 : ulong batch_cnt;
235 : ulong micros_gaddr; /* ptr to the list of fd_blockstore_micro_t */
236 : ulong micros_cnt;
237 : ulong txns_gaddr; /* ptr to the list of fd_block_txn_t */
238 : ulong txns_cnt;
239 : ulong txns_meta_gaddr; /* ptr to the allocation for txn meta data */
240 : ulong txns_meta_sz;
241 : };
242 : typedef struct fd_block fd_block_t;
243 :
244 : struct fd_block_map {
245 : ulong slot; /* map key */
246 : ulong next; /* reserved for use by fd_map_giant.c */
247 :
248 : /* Ancestry */
249 :
250 : ulong parent_slot;
251 : ulong child_slots[FD_BLOCKSTORE_CHILD_SLOT_MAX];
252 : ulong child_slot_cnt;
253 :
254 : /* Metadata */
255 :
256 : ulong height;
257 : fd_hash_t block_hash;
258 : fd_hash_t bank_hash;
259 : fd_hash_t merkle_hash; /* the last FEC set's merkle hash */
260 : ulong fec_cnt; /* the number of FEC sets in the slot */
261 : uchar flags;
262 : uchar reference_tick; /* the tick when the leader prepared the block. */
263 : long ts; /* the wallclock time when we finished receiving the block. */
264 :
265 : /* Windowing */
266 :
267 : uint consumed_idx; /* the highest shred idx of the contiguous window from idx 0 (inclusive). */
268 : uint received_idx; /* the highest shred idx we've received (exclusive). */
269 : uint complete_idx; /* the shred idx with the FD_SHRED_DATA_FLAG_SLOT_COMPLETE flag set. */
270 :
271 : /* Block */
272 :
273 : ulong block_gaddr; /* global address to the start of the allocated fd_block_t */
274 : };
275 : typedef struct fd_block_map fd_block_map_t;
276 :
277 : /* clang-format off */
278 : #define MAP_NAME fd_block_map
279 0 : #define MAP_T fd_block_map_t
280 0 : #define MAP_KEY slot
281 : #include "../../util/tmpl/fd_map_giant.c"
282 : /* clang-format on */
283 :
284 : /* fd_block_idx is an in-memory index of finalized blocks that have been
285 : archived to disk. It records the slot together with the byte offset
286 : relative to the start of the file. */
287 :
288 : struct fd_block_idx {
289 : ulong slot;
290 : ulong next;
291 : uint hash;
292 : ulong off;
293 : fd_hash_t block_hash;
294 : fd_hash_t bank_hash;
295 : };
296 : typedef struct fd_block_idx fd_block_idx_t;
297 :
298 : #define MAP_NAME fd_block_idx
299 0 : #define MAP_T fd_block_idx_t
300 0 : #define MAP_KEY slot
301 0 : #define MAP_KEY_HASH(key) ((uint)(key)) /* finalized slots are guaranteed to be unique so perfect hashing */
302 : #include "../../util/tmpl/fd_map_dynamic.c"
303 :
304 : struct fd_txn_key {
305 : ulong v[FD_ED25519_SIG_SZ / sizeof( ulong )];
306 : };
307 : typedef struct fd_txn_key fd_txn_key_t;
308 :
309 : struct fd_txn_map {
310 : fd_txn_key_t sig;
311 : ulong next;
312 : ulong slot;
313 : ulong offset;
314 : ulong sz;
315 : ulong meta_gaddr; /* ptr to the transaction metadata */
316 : ulong meta_sz; /* metadata size */
317 : };
318 : typedef struct fd_txn_map fd_txn_map_t;
319 :
320 : /* clang-format off */
321 : int fd_txn_key_equal(fd_txn_key_t const * k0, fd_txn_key_t const * k1);
322 : ulong fd_txn_key_hash(fd_txn_key_t const * k, ulong seed);
323 :
324 : #define MAP_NAME fd_txn_map
325 0 : #define MAP_T fd_txn_map_t
326 0 : #define MAP_KEY sig
327 : #define MAP_KEY_T fd_txn_key_t
328 0 : #define MAP_KEY_EQ(k0,k1) fd_txn_key_equal(k0,k1)
329 0 : #define MAP_KEY_HASH(k,seed) fd_txn_key_hash(k, seed)
330 : #include "../../util/tmpl/fd_map_giant.c"
331 :
332 : /* fd_blockstore_archiver outlines the format of metadata
333 : at the start of an archive file - needed so that archive
334 : files can be read back on initialization. */
335 :
336 : struct fd_blockstore_archiver {
337 : ulong magic;
338 : ulong fd_size_max; /* maximum size of the archival file */
339 : ulong num_blocks; /* number of blocks in the archival file. needed for reading back */
340 : ulong head; /* location of least recently written block */
341 : ulong tail; /* location after most recently written block */
342 : };
343 : typedef struct fd_blockstore_archiver fd_blockstore_archiver_t;
344 0 : #define FD_BLOCKSTORE_ARCHIVE_START sizeof(fd_blockstore_archiver_t)
345 :
346 : struct __attribute__((aligned(FD_BLOCKSTORE_ALIGN))) fd_blockstore {
347 : /* clang-format on */
348 :
349 : /* Metadata */
350 :
351 : ulong magic;
352 : ulong blockstore_gaddr;
353 : ulong wksp_tag;
354 : ulong seed;
355 :
356 : /* Concurrency */
357 :
358 : fd_rwseq_lock_t lock;
359 :
360 : /* Persistence */
361 :
362 : fd_blockstore_archiver_t archiver;
363 : ulong mrw_slot; /* most recently written slot */
364 :
365 : /* Slot metadata */
366 :
367 : ulong lps; /* latest processed slot */
368 : ulong hcs; /* highest confirmed slot */
369 : ulong smr; /* supermajority root. DO NOT MODIFY DIRECTLY. */
370 : ulong wmk; /* watermark. DO NOT MODIFY DIRECTLY. */
371 :
372 : /* Config limits */
373 :
374 : ulong shred_max; /* maximum # of shreds that can be held in memory */
375 : ulong block_max; /* maximum # of blocks that can be held in memory */
376 : ulong idx_max; /* maximum # of blocks that can be indexed from the archival file */
377 : ulong txn_max; /* maximum # of transactions that can be indexed from blocks */
378 : ulong alloc_max; /* maximum bytes that can be allocated */
379 :
380 : /* Owned */
381 :
382 : ulong shred_pool_gaddr; /* memory pool for buffering shreds before block assembly */
383 : ulong shred_map_gaddr; /* map of (slot, shred_idx)->shred */
384 : ulong block_map_gaddr; /* map of slot->(slot_meta, block) */
385 : ulong block_idx_gaddr; /* map of slot->byte offset in archival file */
386 : ulong slot_deque_gaddr; /* deque of slot numbers */
387 : ulong txn_map_gaddr;
388 : ulong alloc_gaddr;
389 : };
390 : typedef struct fd_blockstore fd_blockstore_t;
391 :
392 : FD_PROTOTYPES_BEGIN
393 :
394 : /* Construction API */
395 :
396 : /* TODO document lifecycle methods */
397 :
398 : FD_FN_CONST static inline ulong
399 0 : fd_blockstore_align( void ) {
400 0 : return alignof(fd_blockstore_t);
401 0 : }
402 :
403 : FD_FN_CONST static inline ulong
404 0 : fd_blockstore_footprint( ulong shred_max, ulong block_max, ulong idx_max, ulong txn_max ) {
405 0 : int lg_idx_max = fd_ulong_find_msb( fd_ulong_pow2_up( idx_max ) );
406 0 : return FD_LAYOUT_FINI(
407 0 : FD_LAYOUT_APPEND(
408 0 : FD_LAYOUT_APPEND(
409 0 : FD_LAYOUT_APPEND(
410 0 : FD_LAYOUT_APPEND(
411 0 : FD_LAYOUT_APPEND(
412 0 : FD_LAYOUT_APPEND(
413 0 : FD_LAYOUT_APPEND(
414 0 : FD_LAYOUT_APPEND(
415 0 : FD_LAYOUT_INIT,
416 0 : alignof(fd_blockstore_t), sizeof(fd_blockstore_t) ),
417 0 : fd_buf_shred_pool_align(), fd_buf_shred_pool_footprint( shred_max ) ),
418 0 : fd_buf_shred_map_align(), fd_buf_shred_map_footprint( shred_max ) ),
419 0 : fd_block_map_align(), fd_block_map_footprint( block_max ) ),
420 0 : fd_block_idx_align(), fd_block_idx_footprint( lg_idx_max ) ),
421 0 : fd_slot_deque_align(), fd_slot_deque_footprint( block_max ) ),
422 0 : fd_txn_map_align(), fd_txn_map_footprint( txn_max ) ),
423 0 : fd_alloc_align(), fd_alloc_footprint() ),
424 0 : fd_blockstore_align() );
425 0 : }
426 :
427 : void *
428 : fd_blockstore_new( void * shmem,
429 : ulong wksp_tag,
430 : ulong seed,
431 : ulong shred_max,
432 : ulong block_max,
433 : ulong idx_max,
434 : ulong txn_max );
435 :
436 : fd_blockstore_t *
437 : fd_blockstore_join( void * shblockstore );
438 :
439 : void *
440 : fd_blockstore_leave( fd_blockstore_t * blockstore );
441 :
442 : void *
443 : fd_blockstore_delete( void * shblockstore );
444 :
445 : /* fd_blockstore_init initializes a blockstore with the given
446 : `slot_bank`. This bank is used for initializing fields (SMR, etc.),
447 : and should be the bank upon finishing a snapshot load if booting from
448 : a snapshot, genesis bank otherwise. It is also used to "fake" the
449 : snapshot block as if that block's data were available. The metadata
450 : for this block's slot will be populated (fd_block_map_t) but the
451 : actual block data (fd_block_t) won't exist. This is done to bootstrap
452 : the various components for live replay (turbine, repair, etc.)
453 :
454 : `fd` is a file descriptor for the blockstore archival file. As part
455 : of `init`, blockstore rebuilds an in-memory index of the archival
456 : file. */
457 :
458 : fd_blockstore_t *
459 : fd_blockstore_init( fd_blockstore_t * blockstore, int fd, ulong fd_size_max, fd_slot_bank_t const * slot_bank );
460 :
461 : /* fd_blockstore_fini finalizes a blockstore.
462 :
463 : IMPORTANT! Caller MUST hold the read lock when calling this
464 : function. */
465 :
466 : void
467 : fd_blockstore_fini( fd_blockstore_t * blockstore );
468 :
469 : /* Accessors */
470 :
471 : /* fd_blockstore_wksp returns the local join to the wksp backing the
472 : blockstore. The lifetime of the returned pointer is at least as long
473 : as the lifetime of the local join. Assumes blockstore is a current
474 : local join. */
475 :
476 : FD_FN_PURE static inline fd_wksp_t *
477 0 : fd_blockstore_wksp( fd_blockstore_t * blockstore ) {
478 0 : return (fd_wksp_t *)( ( (ulong)blockstore ) - blockstore->blockstore_gaddr );
479 0 : }
480 :
481 : /* fd_blockstore_wksp_tag returns the workspace allocation tag used by
482 : the blockstore for its wksp allocations. Will be positive. Assumes
483 : blockstore is a current local join. */
484 :
485 : FD_FN_PURE static inline ulong
486 0 : fd_blockstore_wksp_tag( fd_blockstore_t const * blockstore ) {
487 0 : return blockstore->wksp_tag;
488 0 : }
489 :
490 : /* fd_blockstore_seed returns the hash seed used by the blockstore for various hash
491 : functions. Arbitrary value. Assumes blockstore is a current local join.
492 : TODO: consider renaming hash_seed? */
493 : FD_FN_PURE static inline ulong
494 0 : fd_blockstore_seed( fd_blockstore_t const * blockstore ) {
495 0 : return blockstore->seed;
496 0 : }
497 :
498 : /* fd_blockstore_buf_shred_pool returns a pointer in the caller's
499 : address space to the pool pointer fd_buf_shred_t * in the blockstore
500 : wksp. Assumes blockstore is local join. Lifetime of the returned
501 : pointer is that of the local join. */
502 :
503 : FD_FN_PURE static inline fd_buf_shred_t *
504 0 : fd_blockstore_shred_pool( fd_blockstore_t * blockstore ) {
505 0 : return fd_wksp_laddr_fast( fd_blockstore_wksp( blockstore ), blockstore->shred_pool_gaddr );
506 0 : }
507 :
508 : /* fd_blockstore_buf_shred_map returns a pointer in the caller's address
509 : space to the fd_buf_shred_map_t * in the blockstore wksp. Assumes
510 : blockstore is local join. Lifetime of the returned pointer is that
511 : of the local join. */
512 :
513 : FD_FN_PURE static inline fd_buf_shred_map_t *
514 0 : fd_blockstore_shred_map( fd_blockstore_t * blockstore ) {
515 0 : return fd_wksp_laddr_fast( fd_blockstore_wksp( blockstore ), blockstore->shred_map_gaddr );
516 0 : }
517 :
518 : /* fd_block_map returns a pointer in the caller's address space to the
519 : fd_block_map_t in the blockstore wksp. Assumes blockstore is local
520 : join. Lifetime of the returned pointer is that of the local join. */
521 :
522 : FD_FN_PURE static inline fd_block_map_t *
523 0 : fd_blockstore_block_map( fd_blockstore_t * blockstore ) {
524 0 : return fd_wksp_laddr_fast( fd_blockstore_wksp( blockstore ), blockstore->block_map_gaddr );
525 0 : }
526 :
527 : /* fd_block_idx returns a pointer in the caller's address space to the
528 : fd_block_idx_t in the blockstore wksp. Assumes blockstore is local
529 : join. Lifetime of the returned pointer is that of the local join. */
530 :
531 : FD_FN_PURE static inline fd_block_idx_t *
532 0 : fd_blockstore_block_idx( fd_blockstore_t * blockstore ) {
533 0 : return fd_wksp_laddr_fast( fd_blockstore_wksp( blockstore ), blockstore->block_idx_gaddr );
534 0 : }
535 :
536 : /* fd_slot_deque returns a pointer in the caller's address space to the
537 : fd_slot_deque_t in the blockstore wksp. Assumes blockstore is local
538 : join. Lifetime of the returned pointer is that of the local join. */
539 :
540 : FD_FN_PURE static inline ulong *
541 0 : fd_blockstore_slot_deque( fd_blockstore_t * blockstore ) {
542 0 : return fd_wksp_laddr_fast( fd_blockstore_wksp( blockstore), blockstore->slot_deque_gaddr );
543 0 : }
544 :
545 : /* fd_txn_map returns a pointer in the caller's address space to the blockstore's
546 : block map. Assumes blockstore is local join. Lifetime of the returned pointer is that of the
547 : local join. */
548 :
549 : FD_FN_PURE static inline fd_txn_map_t *
550 0 : fd_blockstore_txn_map( fd_blockstore_t * blockstore ) {
551 0 : return fd_wksp_laddr_fast( fd_blockstore_wksp( blockstore), blockstore->txn_map_gaddr );
552 0 : }
553 :
554 : /* fd_blockstore_alloc returns a pointer in the caller's address space to
555 : the blockstore's allocator. */
556 :
557 : FD_FN_PURE static inline fd_alloc_t * /* Lifetime is that of the local join */
558 0 : fd_blockstore_alloc( fd_blockstore_t * blockstore ) {
559 0 : return fd_wksp_laddr_fast( fd_blockstore_wksp( blockstore), blockstore->alloc_gaddr );
560 0 : }
561 :
562 : /* fd_blockstore_block_data_laddr returns a local pointer to the block's
563 : data. The returned pointer lifetime is until the block is removed. */
564 :
565 : FD_FN_PURE static inline uchar *
566 0 : fd_blockstore_block_data_laddr( fd_blockstore_t * blockstore, fd_block_t * block ) {
567 0 : return fd_wksp_laddr_fast( fd_blockstore_wksp( blockstore ), block->data_gaddr );
568 0 : }
569 :
570 : FD_FN_PURE static inline fd_block_entry_batch_t *
571 0 : fd_blockstore_block_batch_laddr( fd_blockstore_t * blockstore, fd_block_t * block ) {
572 0 : return fd_wksp_laddr_fast( fd_blockstore_wksp( blockstore ), block->batch_gaddr );
573 0 : }
574 :
575 : /* fd_buf_shred_query queries the blockstore for shred at slot,
576 : shred_idx. Returns a pointer to the shred or NULL if not in
577 : blockstore. The returned pointer lifetime is until the shred is
578 : removed. Check return value for error info. This API only works for
579 : shreds from incomplete blocks.
580 :
581 : Callers should hold the read lock during the entirety of its read to
582 : ensure the pointer remains valid. */
583 : fd_shred_t *
584 : fd_buf_shred_query( fd_blockstore_t * blockstore, ulong slot, uint shred_idx );
585 :
586 : /* fd_buf_shred_query_copy_data queries the blockstore for shred at
587 : slot, shred_idx. Copies the shred data to the given buffer and
588 : returns the data size. Returns -1 on failure.
589 :
590 : IMPORTANT! Caller MUST hold the read lock when calling this
591 : function. */
592 : long
593 : fd_buf_shred_query_copy_data( fd_blockstore_t * blockstore,
594 : ulong slot,
595 : uint shred_idx,
596 : void * buf,
597 : ulong buf_max );
598 :
599 : /* fd_blockstore_block_query queries blockstore for block at slot.
600 : Returns a pointer to the block or NULL if not in blockstore. The
601 : returned pointer lifetime is until the block is removed. Check
602 : return value for error info.
603 :
604 : IMPORTANT! Caller MUST hold the read lock when calling this
605 : function. */
606 : fd_block_t *
607 : fd_blockstore_block_query( fd_blockstore_t * blockstore, ulong slot );
608 :
609 : /* fd_blockstore_block_hash_query queries blockstore for the block hash
610 : at slot. This is the final poh hash for a slot.
611 :
612 : IMPORTANT! Caller MUST hold the read lock when calling this
613 : function. */
614 : fd_hash_t const *
615 : fd_blockstore_block_hash_query( fd_blockstore_t * blockstore, ulong slot );
616 :
617 : /* fd_blockstore_bank_hash_query query blockstore for the bank hash for
618 : a given slot.
619 :
620 : IMPORTANT! Caller MUST hold the read lock when calling this
621 : function. */
622 : fd_hash_t const *
623 : fd_blockstore_bank_hash_query( fd_blockstore_t * blockstore, ulong slot );
624 :
625 : /* fd_blockstore_block_map_query queries the blockstore for the block
626 : map entry at slot. Returns a pointer to the slot meta or NULL if not
627 : in blockstore. The returned pointer lifetime is until the slot meta
628 : is removed.
629 :
630 : IMPORTANT! Caller MUST hold the read lock when calling this
631 : function. */
632 : fd_block_map_t *
633 : fd_blockstore_block_map_query( fd_blockstore_t * blockstore, ulong slot );
634 :
635 : /* fd_blockstore_parent_slot_query queries the parent slot of slot.
636 :
637 : IMPORTANT! Caller MUST hold the read lock when calling this
638 : function. */
639 : ulong
640 : fd_blockstore_parent_slot_query( fd_blockstore_t * blockstore, ulong slot );
641 :
642 : /* fd_blockstore_child_slots_query queries slot's child slots. Return
643 : values are saved in slots_out and slot_cnt. Returns FD_BLOCKSTORE_OK
644 : on success, FD_BLOCKSTORE_ERR_SLOT_MISSING if slot is not in the
645 : blockstore. The returned slot array is always <= the max size
646 : FD_BLOCKSTORE_CHILD_SLOT_MAX and contiguous. Empty slots in the
647 : array are set to FD_SLOT_NULL.
648 :
649 : IMPORTANT! Caller MUST hold the read lock when calling this
650 : function. */
651 :
652 : int
653 : fd_blockstore_child_slots_query( fd_blockstore_t * blockstore, ulong slot, ulong ** slots_out, ulong * slot_cnt );
654 :
655 : /* fd_blockstore_block_frontier_query query the frontier i.e. all the
656 : blocks that need to be replayed that haven't been. These are the
657 : slot children of the current frontier that are shred complete.
658 :
659 : IMPORTANT! Caller MUST hold the read lock when calling this
660 : function. */
661 : fd_block_t *
662 : fd_blockstore_block_frontier_query( fd_blockstore_t * blockstore,
663 : ulong * parents,
664 : ulong parents_sz );
665 :
666 : /* fd_blockstore_block_data_query_volatile queries the block map entry
667 : (metadata and block data) in a lock-free thread-safe manner that does
668 : not block writes. Copies the metadata (fd_block_map_t) into
669 : block_map_entry_out. Allocates a new block data (uchar *) using
670 : alloc, copies the block data into it, and sets the block_data_out
671 : pointer. Caller provides the allocator via alloc for the copied
672 : block data (an allocator is needed because the block data sz is not
673 : known apriori). Returns FD_BLOCKSTORE_SLOT_MISSING if slot is
674 : missing: caller MUST ignore out pointers in this case. Otherwise this
675 : call cannot fail and returns FD_BLOCKSTORE_OK. */
676 :
677 : int
678 : fd_blockstore_block_data_query_volatile( fd_blockstore_t * blockstore,
679 : int fd,
680 : ulong slot,
681 : fd_valloc_t alloc,
682 : fd_hash_t * parent_block_hash_out,
683 : fd_block_map_t * block_map_entry_out,
684 : fd_block_rewards_t * block_rewards_out,
685 : uchar ** block_data_out,
686 : ulong * block_data_sz_out );
687 :
688 : /* fd_blockstore_block_map_query_volatile is the same as above except it
689 : only copies out the metadata (fd_block_map_t). Returns
690 : FD_BLOCKSTORE_SLOT_MISSING if slot is missing, otherwise
691 : FD_BLOCKSTORE_OK. */
692 :
693 : int
694 : fd_blockstore_block_map_query_volatile( fd_blockstore_t * blockstore,
695 : int fd,
696 : ulong slot,
697 : fd_block_map_t * block_map_entry_out );
698 :
699 : /* fd_blockstore_txn_query queries the transaction data for the given
700 : signature.
701 :
702 : IMPORTANT! Caller MUST hold the read lock when calling this
703 : function. */
704 : fd_txn_map_t *
705 : fd_blockstore_txn_query( fd_blockstore_t * blockstore, uchar const sig[static FD_ED25519_SIG_SZ] );
706 :
707 : /* Query the transaction data for the given signature in a thread
708 : safe manner. The transaction data is copied out. txn_data_out can
709 : be NULL if you are only interested in the transaction metadata. */
710 : int
711 : fd_blockstore_txn_query_volatile( fd_blockstore_t * blockstore,
712 : int fd,
713 : uchar const sig[static FD_ED25519_SIG_SZ],
714 : fd_txn_map_t * txn_out,
715 : long * blk_ts,
716 : uchar * blk_flags,
717 : uchar txn_data_out[FD_TXN_MTU] );
718 :
719 : /* fd_blockstore_slot_remove removes slot from blockstore, including all
720 : relevant internal structures.
721 :
722 : IMPORTANT! Caller MUST hold the write lock when calling this
723 : function. */
724 : void
725 : fd_blockstore_slot_remove( fd_blockstore_t * blockstore, ulong slot );
726 :
727 : /* Operations */
728 :
729 : /* fd_buf_shred_insert inserts shred into the blockstore, fast O(1).
730 : Fail if this shred is already in the blockstore or the blockstore is
731 : full. Returns an error code indicating success or failure.
732 : TODO eventually this will need to support "upsert" duplicate shred handling.
733 :
734 : IMPORTANT! Caller MUST hold the write lock when calling this
735 : function. */
736 : int
737 : fd_buf_shred_insert( fd_blockstore_t * blockstore, fd_shred_t const * shred );
738 :
739 : /* fd_blockstore_buffered_shreds_remove removes all the unassembled shreds
740 : for a slot
741 :
742 : IMPORTANT! Caller MUST hold the write lock when calling this
743 : function. */
744 : int
745 : fd_blockstore_buffered_shreds_remove( fd_blockstore_t * blockstore, ulong slot );
746 :
747 : /* fd_blockstore_block_height_update sets the block height.
748 :
749 : IMPORTANT! Caller MUST hold the write lock when calling this
750 : function. */
751 : void
752 : fd_blockstore_block_height_update( fd_blockstore_t * blockstore, ulong slot, ulong block_height );
753 :
754 : /* fd_blockstore_publish publishes all blocks until the current
755 : blockstore smr (`blockstore->smr`). Publishing entails 1. pruning
756 : and 2. archiving. Pruning removes any blocks that are not part of
757 : the same fork as the smr (hence the name pruning, like pruning the
758 : branches of a tree). Archiving removes from memory any slots < smr
759 : that are on the same fork, but writes those blocks out to disk using
760 : the provided file descriptor to the archival file `fd`.
761 :
762 : Note that slots < smr are ancestors of the smr, and are therefore
763 : finalized slots which is why they are archived. Blocks removed as a
764 : result of pruning are not finalized, and therefore not archived.
765 :
766 : IMPORTANT! Caller MUST hold the write lock when calling this
767 : function. */
768 :
769 : void
770 : fd_blockstore_publish( fd_blockstore_t * blockstore, int fd );
771 :
772 : /* fd_blockstore_start_read acquires the read lock */
773 : static inline void
774 0 : fd_blockstore_start_read( fd_blockstore_t * blockstore ) {
775 0 : fd_rwseq_start_read( &blockstore->lock );
776 0 : }
777 :
778 : /* fd_blockstore_end_read releases the read lock */
779 : static inline void
780 0 : fd_blockstore_end_read( fd_blockstore_t * blockstore ) {
781 0 : fd_rwseq_end_read( &blockstore->lock );
782 0 : }
783 :
784 : /* fd_blockstore_start_write acquire the write lock */
785 : static inline void
786 0 : fd_blockstore_start_write( fd_blockstore_t * blockstore ) {
787 0 : fd_rwseq_start_write( &blockstore->lock );
788 0 : }
789 :
790 : /* fd_blockstore_end_write releases the write lock */
791 : static inline void
792 0 : fd_blockstore_end_write( fd_blockstore_t * blockstore ) {
793 0 : fd_rwseq_end_write( &blockstore->lock );
794 0 : }
795 :
796 : void
797 : fd_blockstore_log_block_status( fd_blockstore_t * blockstore, ulong around_slot );
798 :
799 : /* fd_blockstore_log_mem_usage logs the memory usage of blockstore in a
800 : human-readable format. Caller MUST hold the read lock. */
801 :
802 : void
803 : fd_blockstore_log_mem_usage( fd_blockstore_t * blockstore );
804 :
805 : FD_PROTOTYPES_END
806 :
807 : /* fd_blockstore_ser is a serialization context for archiving a block to
808 : disk. */
809 :
810 : struct fd_blockstore_ser {
811 : fd_block_map_t * block_map;
812 : fd_block_t * block;
813 : uchar * data;
814 : };
815 : typedef struct fd_blockstore_ser fd_blockstore_ser_t;
816 :
817 : /* Archives a block and block map entry to fd at blockstore->off, and does
818 : any necessary bookkeeping.
819 : If fd is -1, no write is attempted. Returns written size */
820 : ulong
821 : fd_blockstore_block_checkpt( fd_blockstore_t * blockstore,
822 : fd_blockstore_ser_t * ser,
823 : int fd,
824 : ulong slot );
825 :
826 : /* Restores a block and block map entry from fd at given offset. As this used by
827 : rpcserver, it must return an error code instead of throwing an error on failure. */
828 : int
829 : fd_blockstore_block_meta_restore( fd_blockstore_archiver_t * archvr,
830 : int fd,
831 : fd_block_idx_t * block_idx_entry,
832 : fd_block_map_t * block_map_entry_out,
833 : fd_block_t * block_out );
834 :
835 : /* Reads block data from fd into a given buf. Modifies data_off similarly to
836 : meta_restore */
837 : int
838 : fd_blockstore_block_data_restore( fd_blockstore_archiver_t * archvr,
839 : int fd,
840 : fd_block_idx_t * block_idx_entry,
841 : uchar * buf_out,
842 : ulong buf_max,
843 : ulong data_sz );
844 :
845 : /* Returns 0 if the archive metadata is valid */
846 : bool
847 : fd_blockstore_archiver_verify( fd_blockstore_t * blockstore, fd_blockstore_archiver_t * archiver );
848 :
849 : ulong
850 : fd_blockstore_archiver_lrw_slot( fd_blockstore_t * blockstore, int fd, fd_block_map_t * lrw_block_map, fd_block_t * lrw_block );
851 :
852 : #endif /* HEADER_fd_src_flamenco_runtime_fd_blockstore_h */
|