Line data Source code
1 : #ifndef HEADER_fd_src_discof_repair_fd_store_h
2 : #define HEADER_fd_src_discof_repair_fd_store_h
3 :
4 : /* fd_store is a high-performance in-memory storage engine for shreds as
5 : they are received from the network.
6 :
7 : The elements in the store themselves are not shreds, but FEC set
8 : payloads. Briefly, FEC sets are groupings of shreds that encode the
9 : same data but provide redundancy and security. While Firedancer
10 : receives individual shreds over Turbine / Repair (the relevant Solana
11 : protocols), a lot of Firedancer's validation of those shreds can only
12 : be done at the FEC set boundary. Also, there are potential future
13 : protocol changes to encode all shreds in a FEC set so that they can
14 : only be decoded once the entire FEC set is received ("all-coding").
15 :
16 : Therefore, the FEC set becomes the logical unit for the store vs.
17 : individual shreds. Replay will only ever attempt replay of a FEC set
18 : so there is no need to store at the granularity of individual shreds.
19 : The store is essentially a mapping of merkle root->FEC set payload,
20 : in which the merkle root was produced by feeding every shred in a FEC
21 : set into a merkle tree. This uniquely identifies a FEC set, because
22 : if any of the shreds change, the merkle root changes.
23 :
24 : Shreds are coalesced and inserted into the store as bytes. The max
25 : bytes per FEC set is currently variable-length but capped at 63985.
26 : In the future this will be fixed to 31840 bytes, when FEC sets are
27 : enforced to always be 32 shreds.
28 :
29 : The shared memory used by a store instance is within a workspace such
30 : that it is also persistent and remotely inspectable. Store is
31 : designed to be used inter-process (allowing concurrent joins from
32 : multiple tiles), relocated in memory (via wksp operations), and
33 : accessed concurrently (managing conflicts with a lock).
34 :
35 : EQUIVOCATION
36 :
37 : There is a protocol violation called equivocation (also known as
38 : "duplicates") that results in two or more blocks for the same slot.
39 : The actual conflict occurs at the shred level: a leader produces two
40 : or more shreds for the same slot at the same index, with different
41 : data payloads. The result will be two different FEC sets for the
42 : same "logical" FEC set based on (slot, fec_set_idx).
43 :
44 : Unfortunately a naive keying scheme such as (slot, fec_set_idx) is
45 : insufficient as a result of equivocation. As mentioned earlier,
46 : Store instead uses the merkle root as the key for its FEC set
47 : elements, at the cost of some keying performance and complexity.
48 :
49 : ARCHITECTURE
50 :
51 : In the Firedancer topology, Shred tile writes to store and Replay
52 : tile reads from store. Replay tile only reads from the store after
53 : Repair tile (which is downstream of Shred) has notified Replay that a
54 : FEC set is ready. Shred's writes are append-only and Replay is
55 : responsible for publishing once it is done consuming (signaled by a
56 : new Tower root).
57 :
58 : Shred (writes) -> Repair (notif) -> Replay (reads, publishes)
59 :
60 : ORDERING
61 :
62 : In the above architecture, Repair delivers FEC sets to Replay in
63 : partial order. Any given fork will be delivered in-order, but
64 : concurrent forks can be delivered in arbitrary order. Another way to
65 : phrase this is a parent FEC set will always be delivered before the
66 : child (see fd_fec_chainer).
67 :
68 : CONCURRENCY
69 :
70 : It is possible to design Store access in a way that enables parallel
71 : writes and minimizes lock contention between readers and writers.
72 : Store contains a fd_rwlock (read-write lock), but the name is a bit
73 : of a misnomer because writes can actually be concurrent and the only
74 : operation that will actually need the write lock is publish, which
75 : will be done by Replay. It is more appropriate to describe as an
76 : exclusive-shared access lock.
77 :
78 : In this design, writers (Shred tiles) hold the shared lock during
79 : their access. The reader (Replay tile) also holds the shared lock
80 : during its access, and given both Shred and Replay will be taking out
81 : shared locks, they will not contend.
82 :
83 : For parallel writes, the Store's hash function is carefully designed
84 : to partition the keyspace so that the same Shred tile always writes
85 : to the same map slots. This ensures map collisions always happen on
86 : the same Shred tile and cannot happen across tiles. Specifically, if
87 : two different FEC sets hash to the same slot, it is guaranteed that
88 : to be the same Shred tile processing both those FEC sets. This
89 : prevents a data race in which multiple Shred tiles (each with a
90 : handle to the shared lock) write to the same map slot.
91 :
92 : The hash function is defined as follows:
93 : ```
94 : #define MAP_KEY_HASH(key,seed) ((ulong)key->mr.ul[0]%seed + (key)->part*seed)
95 : ```
96 : where `key` is a key type that includes the merkle root (32 bytes)
97 : and the partition index (4 bytes) that is equivalent to the Shred
98 : tile index doing the insertion. seed, on initialization, is the
99 : number of chains/buckets in the map_chain divided by the number of
100 : partitions. In effect, seed is the size of each partition. For
101 : example, if the map_chain is sized to 1024, and there are 4 shred
102 : tiles, then the seed is 1024/4 = 256. Then the map key hash can
103 : bound the chain index of each partition as such: shred tile 0 will
104 : write to chains 0-255, shred tile 1 will write to chains 256-511,
105 : shred tile 2 will write to chains 512-767, and shred tile 3 will
106 : write to chains 768-1023, without overlap. The merkle root is a 32
107 : byte SHA-256 hash, so we can expect a fairly uniform distribution of
108 : hash values even after truncating to the first 8 bytes, without
109 : needing to introduce more randomness. Thus we can repurpose the
110 : `seed` argument to be the number of partitions.
111 :
112 : Essentially, this allows for limited single-producer single-consumer
113 : (SPSC) concurrency, where the producer is a given Shred tile and the
114 : consumer is Replay tile. The SPSC concurrency is limited in that the
115 : Store should 1. only be read by Replay after Repair has notified
116 : Replay it is time to read (ie. Shred has finished writing), and 2. be
117 : written to by Shred(s) in append-only fashion, so Shred never
118 : modifies or removes from the map. Store is backed by fd_map_chain,
119 : which is not thread-safe generally, but does support this particular
120 : SPSC concurrency model in cases where the consumer is guaranteed to
121 : be lagging the producer.
122 :
123 : Analyzing fd_map_chain in gory detail, in the case of a map collision
124 : where Replay tile is reading an element and Shred tile writes a new
125 : element to the same map slot, that new element is prepended to the
126 : hash chain within that slot (which modifies what the head of the
127 : chain points to as well as the now-previous head in the hash chain's
128 : `.next` field, but does not touch application data). With fencing
129 : enabled (FD_MAP_INSERT_FENCING), it is guaranteed the consumer either
130 : reads the head before or after the update. If it reads before, that
131 : is safe, it would just check the key (if no match, iterate down the
132 : chain etc.) If it reads after, it is also safe because the new
133 : element is guaranteed to be before the old element in the chain, so
134 : it would just do one more iteration. Note the consumer should always
135 : use fd_store_query_const to ensure the underlying fd_map_chain is not
136 : modified during querying.
137 :
138 : The exception to the above is publishing. Publishing requires
139 : exclusive access because it involves removing from fd_map_chain,
140 : which is not safe for shared access. So the Replay tile should take
141 : out the exclusive lock. Publishing happens at most once per slot, so
142 : it is a relatively infrequent Store access compared to FEC queries
143 : and inserts (which is good because it is also the most expensive). */
144 :
145 : #include "../../flamenco/fd_rwlock.h"
146 : #include "../../flamenco/types/fd_types_custom.h"
147 :
148 : /* FD_STORE_USE_HANDHOLDING: Define this to non-zero at compile time
149 : to turn on additional runtime checks and logging. */
150 :
151 : #ifndef FD_STORE_USE_HANDHOLDING
152 : #define FD_STORE_USE_HANDHOLDING 1
153 : #endif
154 :
155 : /* FD_STORE_ALIGN specifies the alignment needed for store. ALIGN is
156 : double x86 cache line to mitigate various kinds of false sharing (eg.
157 : ACLPF adjacent cache line prefetch). */
158 :
159 : #define FD_STORE_ALIGN (128UL)
160 :
161 : /* FD_STORE_MAGIC is a magic number for detecting store corruption. */
162 :
163 9 : #define FD_STORE_MAGIC (0xf17eda2ce75702e0UL) /* firedancer store version 0 */
164 :
165 : /* FD_STORE_DATA_MAX defines a constant for the maximum size of a FEC
166 : set payload. The value is computed from the maximum number
167 : of shreds in a FEC set * the payload bytes per shred.
168 :
169 : 67 shreds per FEC set * 955 payloads per shred = 63985 bytes max. */
170 :
171 : #define FD_STORE_DATA_MAX (63985UL) /* TODO fixed-32 */
172 :
173 : /* fd_store_fec describes a store element (FEC set). The pointer fields
174 : implement a left-child, right-sibling n-ary tree. */
175 :
176 : struct __attribute__((packed)) fd_store_key {
177 : fd_hash_t mr;
178 : ulong part; /* partition index of the inserter */
179 : };
180 : typedef struct fd_store_key fd_store_key_t;
181 :
182 : struct __attribute__((aligned(128UL))) fd_store_fec {
183 :
184 : /* Keys */
185 :
186 : fd_store_key_t key; /* map key, merkle root of the FEC set + a partition index*/
187 : fd_hash_t cmr; /* parent's map key, chained merkle root of the FEC set */
188 :
189 : /* Pointers */
190 :
191 : ulong next; /* reserved for internal use by fd_pool, fd_map_chain, orphan list */
192 : ulong parent; /* pool idx of the parent */
193 : ulong child; /* pool idx of the left-child */
194 : ulong sibling; /* pool idx of the right-sibling */
195 :
196 : /* Data */
197 :
198 : ulong data_sz; /* TODO fixed-32. sz of the FEC set payload, guaranteed < FD_STORE_DATA_MAX */
199 : uchar data[FD_STORE_DATA_MAX]; /* FEC set payload = coalesced data shreds (byte array) */
200 : };
201 : typedef struct fd_store_fec fd_store_fec_t;
202 :
203 : #define POOL_NAME fd_store_pool
204 498 : #define POOL_ELE_T fd_store_fec_t
205 : #include "../../util/tmpl/fd_pool_para.c"
206 :
207 : #define MAP_NAME fd_store_map
208 : #define MAP_ELE_T fd_store_fec_t
209 : #define MAP_KEY_T fd_store_key_t
210 51 : #define MAP_KEY key
211 537 : #define MAP_KEY_EQ(k0,k1) (!memcmp((k0),(k1), sizeof(fd_hash_t)))
212 471 : #define MAP_KEY_HASH(key,seed) ((ulong)key->mr.ul[0]%seed + (key)->part*seed) /* See documentation above for the hash function */
213 : #define MAP_INSERT_FENCE 1
214 : #include "../../util/tmpl/fd_map_chain.c"
215 :
216 : struct fd_store {
217 : ulong magic; /* ==FD_STORE_MAGIC */
218 : ulong fec_max; /* max number of FEC sets that can be stored */
219 : ulong part_cnt; /* number of partitions, also the number of writers */
220 : ulong root; /* pool idx of the root */
221 : ulong slot0; /* FIXME this hack is needed until the block_id is in the bank (manifest) */
222 : ulong store_gaddr; /* wksp gaddr of store in the backing wksp, non-zero gaddr */
223 : ulong map_gaddr; /* wksp gaddr of map of fd_store_key->fd_store_fec */
224 : ulong pool_mem_gaddr; /* wksp gaddr of shmem_t object in pool_para */
225 : ulong pool_ele_gaddr; /* wksp gaddr of first ele_t object in pool_para */
226 : fd_rwlock_t lock; /* rwlock for concurrent access */
227 : };
228 : typedef struct fd_store fd_store_t;
229 :
230 : FD_PROTOTYPES_BEGIN
231 :
232 : /* Constructors */
233 :
234 : /* fd_store_{align,footprint} return the required alignment and
235 : footprint of a memory region suitable for use as store with up to
236 : fec_max elements. */
237 :
238 : FD_FN_CONST static inline ulong
239 102 : fd_store_align( void ) {
240 102 : return alignof(fd_store_t);
241 102 : }
242 :
243 : FD_FN_CONST static inline ulong
244 18 : fd_store_footprint( ulong fec_max ) {
245 18 : return FD_LAYOUT_FINI(
246 18 : FD_LAYOUT_APPEND(
247 18 : FD_LAYOUT_APPEND(
248 18 : FD_LAYOUT_APPEND(
249 18 : FD_LAYOUT_APPEND(
250 18 : FD_LAYOUT_INIT,
251 18 : alignof(fd_store_t), sizeof(fd_store_t) ),
252 18 : fd_store_map_align(), fd_store_map_footprint( fec_max ) ),
253 18 : fd_store_pool_align(), fd_store_pool_footprint() ),
254 18 : alignof(fd_store_fec_t), sizeof(fd_store_fec_t)*fec_max ),
255 18 : fd_store_align() );
256 18 : }
257 :
258 : /* fd_store_new formats an unused memory region for use as a store.
259 : mem is a non-NULL pointer to this region in the local address space
260 : with the required footprint and alignment. */
261 :
262 : void *
263 : fd_store_new( void * shmem, ulong fec_max, ulong part_cnt );
264 :
265 : /* fd_store_join joins the caller to the store. store points to the
266 : first byte of the memory region backing the store in the caller's
267 : address space.
268 :
269 : Returns a pointer in the local address space to store on success. */
270 :
271 : fd_store_t *
272 : fd_store_join( void * store );
273 :
274 : /* fd_store_leave leaves a current local join. Returns a pointer to the
275 : underlying shared memory region on success and NULL on failure (logs
276 : details). Reasons for failure include store is NULL. */
277 :
278 : void *
279 : fd_store_leave( fd_store_t const * store );
280 :
281 : /* fd_store_delete unformats a memory region used as a store.
282 : Assumes only the nobody is joined to the region. Returns a
283 : pointer to the underlying shared memory region or NULL if used
284 : obviously in error (e.g. store is obviously not a store ... logs
285 : details). The ownership of the memory region is transferred to the
286 : caller. */
287 :
288 : void *
289 : fd_store_delete( void * store );
290 :
291 : /* Accessors */
292 :
293 : /* fd_store_wksp returns the local join to the wksp backing the store.
294 : The lifetime of the returned pointer is at least as long as the
295 : lifetime of the local join. Assumes store is a current local
296 : join. */
297 :
298 : FD_FN_PURE static inline fd_wksp_t *
299 1698 : fd_store_wksp( fd_store_t const * store ) {
300 1698 : return (fd_wksp_t *)( ( (ulong)store ) - store->store_gaddr );
301 1698 : }
302 :
303 : /* fd_store_pool returns a local join to the pool_t object of the store. */
304 651 : FD_FN_PURE static inline fd_store_pool_t fd_store_pool_const( fd_store_t const * store ) {
305 651 : return (fd_store_pool_t){ .pool = fd_wksp_laddr_fast( fd_store_wksp( store ), store->pool_mem_gaddr ),
306 651 : .ele = fd_wksp_laddr_fast( fd_store_wksp( store ), store->pool_ele_gaddr ),
307 651 : .ele_max = store->fec_max };
308 651 : }
309 :
310 : /* fd_store_{map,map_const,pool,fec0,fec0_const,root,root_const} returns a pointer in the
311 : caller's address space to the corresponding store field. const
312 : versions for each are also provided. */
313 :
314 162 : FD_FN_PURE static inline fd_store_map_t * fd_store_map ( fd_store_t * store ) { return fd_wksp_laddr_fast( fd_store_wksp( store ), store->map_gaddr ); }
315 234 : FD_FN_PURE static inline fd_store_map_t const * fd_store_map_const ( fd_store_t const * store ) { return fd_wksp_laddr_fast( fd_store_wksp( store ), store->map_gaddr ); }
316 372 : FD_FN_PURE static inline fd_store_pool_t fd_store_pool ( fd_store_t * store ) { return fd_store_pool_const( store ); }
317 :
318 159 : FD_FN_PURE static inline fd_store_fec_t * fd_store_fec0 ( fd_store_t * store ) { fd_store_pool_t pool = fd_store_pool ( store ); return pool.ele; }
319 234 : FD_FN_PURE static inline fd_store_fec_t const * fd_store_fec0_const( fd_store_t const * store ) { fd_store_pool_t pool = fd_store_pool_const( store ); return pool.ele; }
320 12 : FD_FN_PURE static inline fd_store_fec_t * fd_store_root ( fd_store_t * store ) { fd_store_pool_t pool = fd_store_pool ( store ); return fd_store_pool_ele ( &pool, store->root); }
321 3 : FD_FN_PURE static inline fd_store_fec_t const * fd_store_root_const( fd_store_t const * store ) { fd_store_pool_t pool = fd_store_pool_const( store ); return fd_store_pool_ele_const( &pool, store->root); }
322 :
323 : /* fd_store_{parent,child,sibling} returns a pointer in the caller's
324 : address space to the corresponding {parent,left-child,right-sibling}
325 : of fec. Assumes store is a current local join and fec is a valid
326 : pointer to a pool element inside store. const versions for each are
327 : also provided. */
328 :
329 0 : FD_FN_PURE static inline fd_store_fec_t * fd_store_parent ( fd_store_t * store, fd_store_fec_t const * fec ) { fd_store_pool_t pool = fd_store_pool ( store ); return fd_store_pool_ele ( &pool, fec->parent ); }
330 0 : FD_FN_PURE static inline fd_store_fec_t const * fd_store_parent_const ( fd_store_t const * store, fd_store_fec_t const * fec ) { fd_store_pool_t pool = fd_store_pool_const( store ); return fd_store_pool_ele_const( &pool, fec->parent ); }
331 0 : FD_FN_PURE static inline fd_store_fec_t * fd_store_child ( fd_store_t * store, fd_store_fec_t const * fec ) { fd_store_pool_t pool = fd_store_pool ( store ); return fd_store_pool_ele ( &pool, fec->child ); }
332 0 : FD_FN_PURE static inline fd_store_fec_t const * fd_store_child_const ( fd_store_t const * store, fd_store_fec_t const * fec ) { fd_store_pool_t pool = fd_store_pool_const( store ); return fd_store_pool_ele_const( &pool, fec->child ); }
333 0 : FD_FN_PURE static inline fd_store_fec_t * fd_store_sibling ( fd_store_t * store, fd_store_fec_t const * fec ) { fd_store_pool_t pool = fd_store_pool ( store ); return fd_store_pool_ele ( &pool, fec->sibling ); }
334 0 : FD_FN_PURE static inline fd_store_fec_t const * fd_store_sibling_const( fd_store_t const * store, fd_store_fec_t const * fec ) { fd_store_pool_t pool = fd_store_pool_const( store ); return fd_store_pool_ele_const( &pool, fec->sibling ); }
335 :
336 : /* fd_store_{shacq, shrel, exacq, exrel} acquires or releases the shared
337 : or exclusive lock. */
338 :
339 0 : FD_FN_PURE static inline void fd_store_shacq( fd_store_t * store ) { fd_rwlock_read ( &store->lock ); }
340 0 : FD_FN_PURE static inline void fd_store_shrel( fd_store_t * store ) { fd_rwlock_unread ( &store->lock ); }
341 0 : FD_FN_PURE static inline void fd_store_exacq( fd_store_t * store ) { fd_rwlock_write ( &store->lock ); }
342 0 : FD_FN_PURE static inline void fd_store_exrel( fd_store_t * store ) { fd_rwlock_unwrite( &store->lock ); }
343 :
344 : /* Wrappers for acquiring and releasing the lock with timing, for lock
345 : contention metrics. */
346 :
347 0 : #define FD_STORE_SHACQ_TIMED(store, shacq_start, shacq_end) shacq_start = fd_tickcount(); fd_store_shacq( store ); shacq_end = fd_tickcount();
348 0 : #define FD_STORE_SHREL_TIMED(store, shrel_end) shrel_end = fd_tickcount(); fd_store_shrel( store );
349 0 : #define FD_STORE_EXACQ_TIMED(store, exacq_start, exacq_end) exacq_start = fd_tickcount(); fd_store_exacq( store ); exacq_end = fd_tickcount();
350 0 : #define FD_STORE_EXREL_TIMED(store, exrel_end) exrel_end = fd_tickcount(); fd_store_exrel( store );
351 :
352 : /* fd_store_{query,query_const} queries the FEC set keyed by merkle.
353 : Returns a pointer to the fd_store_fec_t if found, NULL otherwise.
354 :
355 : Both the const and non-const versions are concurrency safe; as in
356 : they avoid using the non-const map_ele_query that reorders the chain.
357 : fd_store_query gets around this by calling map idx_query_const, which
358 : does not reorder the chain, and then indexes directly into the pool
359 : and returns a non-const pointer to the element of interest.
360 :
361 : Assumes caller has acquired the shared lock via fd_store_shacq.
362 :
363 : IMPORTANT SAFETY TIP! Caller should only call fd_store_shrel when
364 : they no longer retain interest in the returned pointer. */
365 :
366 : FD_FN_PURE static inline fd_store_fec_t *
367 90 : fd_store_query( fd_store_t * store, fd_hash_t * merkle_root ) {
368 90 : fd_store_key_t key = { .mr = *merkle_root, .part = UINT_MAX };
369 90 : fd_store_pool_t pool = fd_store_pool( store );
370 96 : for( uint i = 0; i < store->part_cnt; i++ ) {
371 96 : key.part = i;
372 96 : ulong idx = fd_store_map_idx_query_const( fd_store_map( store ), &key, ULONG_MAX, fd_store_fec0( store ) );
373 96 : if( idx != ULONG_MAX ) return fd_store_pool_ele( &pool, idx );
374 96 : }
375 0 : return NULL;
376 90 : }
377 :
378 : FD_FN_PURE static inline fd_store_fec_t const *
379 177 : fd_store_query_const( fd_store_t const * store, fd_hash_t * merkle_root ) {
380 177 : fd_store_key_t key = { .mr = *merkle_root, .part = UINT_MAX };
381 285 : for( uint i = 0; i < store->part_cnt; i++ ) {
382 234 : key.part = i;
383 234 : fd_store_fec_t const * fec = fd_store_map_ele_query_const( fd_store_map_const( store ), &key, NULL, fd_store_fec0_const( store ) );
384 234 : if( fec ) return fec;
385 234 : }
386 51 : return NULL;
387 177 : }
388 :
389 : /* Operations */
390 :
391 : /* fd_store_insert inserts a new FEC set keyed by merkle. Returns the
392 : newly inserted fd_store_fec_t. Each fd_store_fec_t can hold at most
393 : FD_STORE_DATA_MAX bytes of data, and caller is responsible for
394 : copying into the region.
395 :
396 : Assumes store is a current local join and has space for another
397 : element. Does additional checks when handholding is enabled and
398 : fails insertion (returning NULL) if checks fail. If this is the
399 : first element being inserted into store, the store root will be set
400 : to this newly inserted element.
401 :
402 : Assumes caller has acquired either the shared or exclusive lock via
403 : fd_store_shacq or fd_store_exacq. See top-level documentation for
404 : why this operation may only require a shared lock vs. exclusive.
405 :
406 : IMPORTANT SAFETY TIP! Caller should only call fd_store_shrel or
407 : fd_store_exrel when they no longer retain interest in the returned
408 : pointer. */
409 :
410 : fd_store_fec_t *
411 : fd_store_insert( fd_store_t * store,
412 : ulong part_idx,
413 : fd_hash_t * merkle_root );
414 :
415 : /* fd_store_link queries for and links the child keyed by merkle_root to
416 : parent keyed by chained_merkle_root. Returns a pointer to the child.
417 : Assumes merkle_root and chained_merkle_root are both non-NULL and key
418 : elements currently in the store.
419 :
420 : Assumes caller has acquired the shared lock via fd_store_shacq.
421 :
422 : IMPORTANT SAFETY TIP! Caller should only call fd_store_shrel when
423 : they no longer retain interest in the returned pointer. */
424 :
425 : fd_store_fec_t *
426 : fd_store_link( fd_store_t * store,
427 : fd_hash_t * merkle_root,
428 : fd_hash_t * chained_merkle_root );
429 :
430 : /* fd_store_publish publishes merkle_root as the new store root, pruning
431 : all elements across branches that do not descend from the new root.
432 : Returns a pointer to the new root. Assumes merkle_root is in the
433 : store and connected to the root (if handholding is enabled does
434 : additional checks and returns NULL on error). Note pruning can
435 : result in store elements greater than the new root slot being
436 : removed. These are elements that become orphaned as a result of the
437 : common ancestor with the new root being removed (the entire branch
438 : ie. fork is pruned).
439 :
440 : For example, in the tree (preorder) [0 1 2 4 3 5 6] publishing 2 will
441 : result in [0 1] being removed given they are ancestors of 2, and
442 : removing 1 will leave [3 5 6] orphaned and also removed.
443 :
444 : Assumes caller has acquired the exclusive lock via fd_store_exacq.
445 :
446 : IMPORTANT SAFETY TIP! Caller should only call fd_store_exrel when
447 : they no longer retain interest in the returned pointer. */
448 :
449 : fd_store_fec_t *
450 : fd_store_publish( fd_store_t * store,
451 : fd_hash_t * merkle_root );
452 :
453 : /* fd_store_clear clears the store. All elements are removed from the
454 : map and released back into the pool. Does not zero-out fields. */
455 :
456 : fd_store_t *
457 : fd_store_clear( fd_store_t * store );
458 :
459 : /* TODO fd_store_verify */
460 :
461 : /* fd_store_print pretty-prints a formatted store as a tree structure.
462 : Printing begins from the store root and each node is the FEC set key
463 : (merkle root hash). */
464 :
465 : int
466 : fd_store_verify( fd_store_t * store );
467 :
468 : void
469 : fd_store_print( fd_store_t const * store );
470 :
471 : FD_PROTOTYPES_END
472 :
473 : #endif /* HEADER_fd_src_discof_repair_fd_store_h */
|