Line data Source code
1 : #ifndef HEADER_fd_src_choreo_eqvoc_fd_eqvoc_h
2 : #define HEADER_fd_src_choreo_eqvoc_fd_eqvoc_h
3 :
4 : #include "../../ballet/shred/fd_shred.h"
5 : #include "../../flamenco/leaders/fd_leaders.h"
6 : #include "../../flamenco/gossip/fd_gossip_types.h"
7 : #include "../fd_choreo_base.h"
8 :
9 : /* fd_eqvoc presents an API for detecting and sending / receiving proofs
10 : of equivocation.
11 :
12 : APIs prefixed with `fd_eqvoc_proof` relate to constructing and
13 : verifying equivocation proofs from shreds.
14 :
15 : APIs prefixed with `fd_eqvoc_fec` relate to shred and FEC set
16 : metadata indexing to detect equivocating shreds.
17 :
18 : Equivocation is when a shred producer produces two or more versions
19 : of a shred for the same (slot, idx). An equivocation proof comprises
20 : two shreds that conflict in a way that imply the shreds' producer
21 : equivocated.
22 :
23 : The proof can be both direct and indirect (implied). A direct proof,
24 : for example, contains two shreds with the same shred index but
25 : different data payloads. An indirect proof contains two shreds with
26 : different shred indices, and the metadata on the shreds implies there
27 : must be two or more versions of a block for that slot. See
28 : `fd_eqvoc_proof_verify` for more details.
29 :
30 : Every FEC set must have the same signature for every shred in the
31 : set, so a different signature would indicate equivocation. Note in
32 : the case of merkle shreds, the shred signature is signed on the FEC
33 : set's merkle root, so every shred in the same FEC set must have the
34 : same signature. */
35 :
36 : /* FD_EQVOC_USE_HANDHOLDING: Define this to non-zero at compile time
37 : to turn on additional runtime checks and logging. */
38 :
39 : #ifndef FD_EQVOC_USE_HANDHOLDING
40 : #define FD_EQVOC_USE_HANDHOLDING 1
41 : #endif
42 :
43 0 : #define FD_EQVOC_FEC_MAX ( 67UL )
44 :
45 : struct fd_slot_fec {
46 : ulong slot;
47 : uint fec_set_idx;
48 : };
49 : typedef struct fd_slot_fec fd_slot_fec_t;
50 :
51 : /* clang-format off */
52 : static const fd_slot_fec_t fd_slot_fec_null = { 0 };
53 : #define FD_SLOT_FEC_NULL fd_slot_fec_null
54 : #define FD_SLOT_FEC_INVAL(key) (!((key).slot) & !((key).fec_set_idx))
55 0 : #define FD_SLOT_FEC_EQ(k0,k1) (!(((k0).slot) ^ ((k1).slot))) & !(((k0).fec_set_idx) ^ (((k1).fec_set_idx)))
56 0 : #define FD_SLOT_FEC_HASH(key) ((uint)(((key).slot)<<15UL) | (((key).fec_set_idx)))
57 : /* clang-format on */
58 :
59 : struct fd_eqvoc_fec {
60 : fd_slot_fec_t key;
61 : ulong next;
62 : ulong code_cnt;
63 : ulong data_cnt;
64 : uint last_idx;
65 : fd_ed25519_sig_t sig;
66 : };
67 : typedef struct fd_eqvoc_fec fd_eqvoc_fec_t;
68 :
69 : #define POOL_NAME fd_eqvoc_fec_pool
70 0 : #define POOL_T fd_eqvoc_fec_t
71 : #include "../../util/tmpl/fd_pool.c"
72 :
73 : /* clang-format off */
74 : #define MAP_NAME fd_eqvoc_fec_map
75 : #define MAP_ELE_T fd_eqvoc_fec_t
76 : #define MAP_KEY_T fd_slot_fec_t
77 0 : #define MAP_KEY_EQ(k0,k1) (FD_SLOT_FEC_EQ(*k0,*k1))
78 0 : #define MAP_KEY_HASH(key,seed) (FD_SLOT_FEC_HASH(*key)^seed)
79 : #include "../../util/tmpl/fd_map_chain.c"
80 : /* clang-format on */
81 :
82 : /* This is the standard MTU
83 :
84 : IPv6 MTU - IP / UDP headers = 1232
85 : DuplicateShredMaxPayloadSize = 1232 - 115
86 : DuplicateShred headers = 63
87 :
88 : https://github.com/anza-xyz/agave/blob/v2.0.3/gossip/src/cluster_info.rs#L113 */
89 :
90 0 : #define FD_EQVOC_PROOF_CHUNK_SZ (1232UL - 115UL - 63UL)
91 0 : #define FD_EQVOC_PROOF_CHUNK_CNT (( FD_EQVOC_PROOF_SZ / FD_EQVOC_PROOF_CHUNK_SZ ) + 1) /* 3 */
92 0 : #define FD_EQVOC_PROOF_SZ (2*FD_SHRED_MAX_SZ + 2*sizeof(ulong)) /* 2 shreds prefixed with sz, encoded in 3 chunks */
93 :
94 : FD_STATIC_ASSERT( FD_EQVOC_PROOF_CHUNK_SZ==FD_GOSSIP_DUPLICATE_SHRED_MAX_CHUNKS, "Update duplicate shred max chunks size" );
95 :
96 : /* The chunk_cnt is encoded in a UCHAR_MAX, so you can have at most
97 : UCHAR_MAX chunks */
98 :
99 : #define FD_EQVOC_PROOF_CHUNK_MIN ( ( FD_EQVOC_PROOF_SZ / UCHAR_MAX ) + 1 ) /* 20 */
100 :
101 0 : #define FD_EQVOC_PROOF_VERIFY_FAILURE (0)
102 0 : #define FD_EQVOC_PROOF_VERIFY_SUCCESS_SIGNATURE (1)
103 0 : #define FD_EQVOC_PROOF_VERIFY_SUCCESS_META (2)
104 0 : #define FD_EQVOC_PROOF_VERIFY_SUCCESS_LAST (3)
105 0 : #define FD_EQVOC_PROOF_VERIFY_SUCCESS_OVERLAP (4)
106 0 : #define FD_EQVOC_PROOF_VERIFY_SUCCESS_CHAINED (5)
107 :
108 0 : #define FD_EQVOC_PROOF_VERIFY_ERR_SLOT (-1) /* different slot */
109 0 : #define FD_EQVOC_PROOF_VERIFY_ERR_VERSION (-2) /* different shred version */
110 0 : #define FD_EQVOC_PROOF_VERIFY_ERR_TYPE (-3) /* wrong shred type (must be chained {resigned} merkle) */
111 0 : #define FD_EQVOC_PROOF_VERIFY_ERR_MERKLE (-4) /* merkle root failed */
112 0 : #define FD_EQVOC_PROOF_VERIFY_ERR_SIGNATURE (-5) /* sig verify of shred producer failed */
113 :
114 : #define SET_NAME fd_eqvoc_proof_set
115 : #define SET_MAX 256
116 : #include "../../util/tmpl/fd_set.c"
117 :
118 : struct fd_eqvoc_proof {
119 : fd_slot_pubkey_t key;
120 : ulong prev; /* reserved for data structure use */
121 : ulong next; /* reserved for data structure use*/
122 :
123 : fd_pubkey_t producer; /* producer of shreds' pubkey */
124 : void * bmtree_mem; /* scratch space for reconstructing
125 : the merkle root */
126 : long wallclock; /* `wallclock` (nanos) */
127 : ulong chunk_cnt; /* `num_chunks` */
128 : ulong chunk_sz; /* `chunk_len` */
129 :
130 : /* static declaration of an fd_set that occupies 4 words ie. 256 bits
131 : that tracks which proof chunks have been received. */
132 :
133 : fd_eqvoc_proof_set_t set[UCHAR_MAX / sizeof( ulong )];
134 :
135 : /* DuplicateShred messages are serialized in the following format:
136 :
137 : ---------
138 : shred1_sz
139 : ---------
140 : shred1
141 : ---------
142 : shred2_sz
143 : ---------
144 : shred2
145 : ---------
146 :
147 : Each shred is prepended with its size in bytes, before being
148 : chunked.
149 : */
150 :
151 : uchar shreds[2 * FD_SHRED_MAX_SZ + 2 * sizeof(ulong)];
152 : };
153 : typedef struct fd_eqvoc_proof fd_eqvoc_proof_t;
154 :
155 : #define POOL_NAME fd_eqvoc_proof_pool
156 0 : #define POOL_T fd_eqvoc_proof_t
157 : #include "../../util/tmpl/fd_pool.c"
158 :
159 : /* clang-format off */
160 : #define MAP_NAME fd_eqvoc_proof_map
161 : #define MAP_ELE_T fd_eqvoc_proof_t
162 : #define MAP_KEY_T fd_slot_pubkey_t
163 0 : #define MAP_KEY_EQ(k0,k1) (FD_SLOT_PUBKEY_EQ(k0,k1))
164 0 : #define MAP_KEY_HASH(key,seed) (FD_SLOT_PUBKEY_HASH(key,seed))
165 : #include "../../util/tmpl/fd_map_chain.c"
166 : /* clang-format on */
167 :
168 : struct fd_eqvoc {
169 :
170 : /* primitives */
171 :
172 : fd_pubkey_t me; /* our pubkey */
173 : ulong fec_max;
174 : ulong proof_max;
175 : ulong shred_version; /* shred version we expect in all shreds in eqvoc-related msgs. */
176 :
177 : /* owned */
178 :
179 : fd_eqvoc_fec_t * fec_pool;
180 : fd_eqvoc_fec_map_t * fec_map;
181 : // fd_eqvoc_fec_dlist_t * fec_dlist;
182 : fd_eqvoc_proof_t * proof_pool;
183 : fd_eqvoc_proof_map_t * proof_map;
184 : fd_sha512_t * sha512;
185 : void * bmtree_mem;
186 :
187 : /* borrowed */
188 :
189 : fd_epoch_leaders_t const * leaders;
190 : };
191 : typedef struct fd_eqvoc fd_eqvoc_t;
192 :
193 : /* clang-format off */
194 :
195 : /* fd_eqvoc_{align,footprint} return the required alignment and
196 : footprint of a memory region suitable for use as eqvoc with up to
197 : node_max nodes and vote_max votes. */
198 :
199 : FD_FN_CONST static inline ulong
200 0 : fd_eqvoc_align( void ) {
201 0 : return alignof(fd_eqvoc_t);
202 0 : }
203 :
204 : FD_FN_CONST static inline ulong
205 0 : fd_eqvoc_footprint( ulong fec_max, ulong proof_max ) {
206 0 : return FD_LAYOUT_FINI(
207 0 : FD_LAYOUT_APPEND(
208 0 : FD_LAYOUT_APPEND(
209 0 : FD_LAYOUT_APPEND(
210 0 : FD_LAYOUT_APPEND(
211 0 : FD_LAYOUT_APPEND(
212 0 : FD_LAYOUT_APPEND(
213 0 : FD_LAYOUT_APPEND(
214 0 : FD_LAYOUT_INIT,
215 0 : alignof(fd_eqvoc_t), sizeof(fd_eqvoc_t) ),
216 0 : fd_eqvoc_fec_pool_align(), fd_eqvoc_fec_pool_footprint( fec_max ) ),
217 0 : fd_eqvoc_fec_map_align(), fd_eqvoc_fec_map_footprint( fec_max ) ),
218 0 : fd_eqvoc_proof_pool_align(), fd_eqvoc_proof_pool_footprint( proof_max ) ),
219 0 : fd_eqvoc_proof_map_align(), fd_eqvoc_proof_map_footprint( proof_max ) ),
220 0 : fd_sha512_align(), fd_sha512_footprint() ),
221 0 : fd_bmtree_commit_align(), fd_bmtree_commit_footprint( FD_SHRED_MERKLE_LAYER_CNT ) ),
222 0 : fd_eqvoc_align() );
223 0 : }
224 : /* clang-format on */
225 :
226 : /* fd_eqvoc_new formats an unused memory region for use as a eqvoc.
227 : mem is a non-NULL pointer to this region in the local address space
228 : with the required footprint and alignment. */
229 :
230 : void *
231 : fd_eqvoc_new( void * shmem, ulong fec_max, ulong proof_max, ulong seed );
232 :
233 : /* fd_eqvoc_join joins the caller to the eqvoc. eqvoc points to the
234 : first byte of the memory region backing the eqvoc in the caller's
235 : address space.
236 :
237 : Returns a pointer in the local address space to eqvoc on success. */
238 :
239 : fd_eqvoc_t *
240 : fd_eqvoc_join( void * sheqvoc );
241 :
242 : /* fd_eqvoc_leave leaves a current local join. Returns a pointer to the
243 : underlying shared memory region on success and NULL on failure (logs
244 : details). Reasons for failure include eqvoc is NULL. */
245 :
246 : void *
247 : fd_eqvoc_leave( fd_eqvoc_t const * eqvoc );
248 :
249 : /* fd_eqvoc_delete unformats a memory region used as a eqvoc.
250 : Assumes only the nobody is joined to the region. Returns a
251 : pointer to the underlying shared memory region or NULL if used
252 : obviously in error (e.g. eqvoc is obviously not a eqvoc ... logs
253 : details). The ownership of the memory region is transferred to the
254 : caller. */
255 :
256 : void *
257 : fd_eqvoc_delete( void * sheqvoc );
258 :
259 : /* fd_eqvoc_init initializes eqvoc with the expected shred version. */
260 :
261 : void
262 : fd_eqvoc_init( fd_eqvoc_t * eqvoc, ulong shred_version );
263 :
264 : /* fd_eqvoc_fec_query queries for FEC set metadata on (slot,
265 : fec_set_idx). At least one coding shred most be inserted to populate
266 : code_cnt, data_cnt, and the last data shred in the slot to populate
267 : last_idx. Otherwise fields are defaulted to 0, 0, FD_SHRED_IDX_NULL
268 : respectively. Callers should check whether fields are the default
269 : values before using them. */
270 :
271 : FD_FN_PURE static inline fd_eqvoc_fec_t const *
272 0 : fd_eqvoc_fec_query( fd_eqvoc_t const * eqvoc, ulong slot, uint fec_set_idx ) {
273 0 : fd_slot_fec_t key = { slot, fec_set_idx };
274 0 : return fd_eqvoc_fec_map_ele_query_const( eqvoc->fec_map, &key, NULL, eqvoc->fec_pool );
275 0 : }
276 :
277 : /* fd_eqvoc_fec_insert inserts a new FEC entry into eqvoc, indexed by
278 : (slot, fec_set_idx). */
279 :
280 : fd_eqvoc_fec_t *
281 : fd_eqvoc_fec_insert( fd_eqvoc_t * eqvoc, ulong slot, uint fec_set_idx );
282 :
283 : /* fd_eqvoc_fec_search searches for whether `shred` implies equivocation
284 : by checking for a conflict in the currently indexed FEC sets. Returns
285 : the conflicting entry if there is one, NULL otherwise.
286 :
287 : A FEC set "overlaps" with another if they both contain a data shred
288 : at the samed idx. For example, say we have a FEC set containing data
289 : shreds in the idx interval [13, 15] and another containing idxs [15,
290 : 20]. The first FEC set has fec_set_idx 13 and data_cnt 3. The second
291 : FEC set has fec_set_idx 15 and data_cnt 6. They overlap because they
292 : both contain a data shred at idx 15. Therefore, these two FEC sets
293 : imply equivocation.
294 :
295 : This overlap can be detected arithmetically by adding the data_cnt to
296 : the fec_set_idx that starts earlier. If the result is greater than
297 : the fec_set_idx that starts later, we know at least one data shred
298 : idx must overlap. In this example, 13 + 3 > 15, which indicates
299 : equivocation.
300 :
301 : We can check for this overlap both backwards and forwards. We know
302 : the max number of data shred idxs in a valid FEC set is 67. So we
303 : need to look back at most 67 FEC set idxs to find the previous FEC
304 : set. Similarly, we look forward at most data_cnt idxs to find the
305 : next FEC set. */
306 :
307 : fd_eqvoc_fec_t const *
308 : fd_eqvoc_fec_search( fd_eqvoc_t const * eqvoc, fd_shred_t const * shred );
309 :
310 : /* fd_eqvoc_proof_query queries for the proof at (slot, from). */
311 :
312 : FD_FN_PURE static inline fd_eqvoc_proof_t *
313 0 : fd_eqvoc_proof_query( fd_eqvoc_t * eqvoc, ulong slot, fd_pubkey_t const * from ) {
314 0 : fd_slot_pubkey_t key = { slot, *from };
315 0 : return fd_eqvoc_proof_map_ele_query( eqvoc->proof_map, &key, NULL, eqvoc->proof_pool );
316 0 : }
317 :
318 : /* fd_eqvoc_proof_query_const is the const version of the above. */
319 :
320 : FD_FN_PURE static inline fd_eqvoc_proof_t const *
321 0 : fd_eqvoc_proof_query_const( fd_eqvoc_t const * eqvoc, ulong slot, fd_pubkey_t const * from ) {
322 0 : fd_slot_pubkey_t key = { slot, *from };
323 0 : return fd_eqvoc_proof_map_ele_query_const( eqvoc->proof_map, &key, NULL, eqvoc->proof_pool );
324 0 : }
325 :
326 : /* fd_eqvoc_proof_insert inserts a proof entry into eqvoc, keyed by
327 : (slot, from) where from is the pubkey that generated the proof. */
328 :
329 : fd_eqvoc_proof_t *
330 : fd_eqvoc_proof_insert( fd_eqvoc_t * eqvoc, ulong slot, fd_pubkey_t const * from );
331 :
332 : void
333 : fd_eqvoc_proof_init( fd_eqvoc_proof_t * proof, fd_pubkey_t const * producer, long wallclock, ulong chunk_cnt, ulong chunk_sz, void * bmtree_mem );
334 :
335 : /* fd_eqvoc_proof_chunk_insert inserts a proof chunk into the proof.
336 : Proofs are divided into chunks before they are transmitted via
337 : gossip, so this function is necessary for reconstruction. */
338 :
339 : void
340 : fd_eqvoc_proof_chunk_insert( fd_eqvoc_proof_t * proof, fd_gossip_duplicate_shred_t const * chunk );
341 :
342 : /* fd_eqvoc_shreds_chunk_insert is a lower-level API for the above. */
343 :
344 : void
345 : fd_eqvoc_shreds_chunk_insert( fd_shred_t * shred1, fd_shred_t * shred2, fd_gossip_duplicate_shred_t const * chunk );
346 :
347 : /* fd_eqvoc_proof_remove removes the proof entry associated with key. */
348 :
349 : void
350 : fd_eqvoc_proof_remove( fd_eqvoc_t * eqvoc, fd_slot_pubkey_t const * key );
351 :
352 : /* fd_eqvoc_proof_complete checks whether the proof has received all
353 : chunks ie. is complete. Returns 1 if so, 0 otherwise. */
354 :
355 : static inline int
356 0 : fd_eqvoc_proof_complete( fd_eqvoc_proof_t const * proof ) {
357 0 : for( uchar i = 0; i < proof->chunk_cnt; i++ ) {
358 0 : if( !fd_eqvoc_proof_set_test( proof->set, i ) ) return 0;
359 0 : }
360 0 : return 1;
361 0 : }
362 :
363 : /* fd_eqvoc_proof_verify verifies that the two shreds contained in
364 : `proof` do in fact equivocate.
365 :
366 : Returns: FD_EQVOC_VERIFY_FAILURE if they do not
367 : FD_EQVOC_VERIFY_SUCCESS_{REASON} if they do
368 : FD_EQVOC_VERIFY_ERR_{REASON} if the shreds were not valid inputs
369 :
370 : Two shreds equivocate if they satisfy any of the following:
371 :
372 : 1. They are in the same FEC set but have different signatures.
373 : 2. They are in the same FEC set and are both coding shreds, but have
374 : different coding metadata ie. code_cnt, data_cnt, first_code_idx.
375 : 3. They are in the same FEC set and are both data shreds. One shred
376 : is marked as the last data shred in the slot
377 : (FD_SHRED_DATA_FLAG_SLOT_COMPLETE), but the other shred has a
378 : higher data shred index.
379 : 4. They are in different FEC sets and the shred with a lower FEC set
380 : index is a coding shred, whereas the shred with the higher FEC set
381 : index is either a coding or data shred. The lower coding shred's
382 : `data_cnt` implies the lower FEC set intersects with the higher
383 : FEC set ie. the FEC sets are overlapping.
384 : 5. They are in different FEC sets and the shred with a lower FEC set
385 : index is a coding shred, and the FEC sets are adjacent ie. the
386 : last data shred index in the lower FEC set is one less than the
387 : first data shred index in the higher FEC set. The merkle root of
388 : the lower FEC set is different from the chained merkle root of the
389 : higher FEC set.
390 :
391 : Note: two shreds are in the same FEC set if they have the same slot
392 : and same FEC set index.
393 :
394 : To prevent false positives, this function also performs the following
395 : input validation on the shreds:
396 :
397 : 1. shred1 and shred2 are both the expected shred_version.
398 : 2. shred1 and shred2 are for the same slot.
399 : 3. shred1 and shred2 are either chained merkle or chained resigned
400 : merkle variants.
401 : 4. shred1 and shred2 contain valid signatures signed by the same
402 : producer pubkey.
403 :
404 : If any of the above input validation fail, this function returns
405 : FD_EQVOC_VERIFY_ERR_{REASON} for the appropriate reason. */
406 :
407 : int
408 : fd_eqvoc_proof_verify( fd_eqvoc_proof_t const * proof );
409 :
410 : /* fd_eqvoc_proof_shreds_verify is a lower-level API for
411 : fd_eqvoc_proof_verify. Refer above for documentation. */
412 :
413 : int
414 : fd_eqvoc_shreds_verify( fd_shred_t const * shred1, fd_shred_t const * shred2, fd_pubkey_t const * producer, void * bmtree_mem );
415 :
416 : /* fd_eqvoc_proof_shred1 returns a pointer to shred1 in `proof`. */
417 :
418 : static inline fd_shred_t *
419 0 : fd_eqvoc_proof_shred1( fd_eqvoc_proof_t * proof ) {
420 0 : return (fd_shred_t *)fd_type_pun_const( proof->shreds + sizeof(ulong) );
421 0 : }
422 :
423 : /* fd_eqvoc_proof_shred1_const returns a const pointer to shred1 in
424 : `proof`. */
425 :
426 : static inline fd_shred_t const *
427 0 : fd_eqvoc_proof_shred1_const( fd_eqvoc_proof_t const * proof ) {
428 0 : return (fd_shred_t const *)fd_type_pun_const( proof->shreds + sizeof(ulong) );
429 0 : }
430 :
431 : /* fd_eqvoc_proof_shred2 returns a pointer to shred2 in `proof`. */
432 :
433 : static inline fd_shred_t *
434 0 : fd_eqvoc_proof_shred2( fd_eqvoc_proof_t * proof ) {
435 0 : ulong shred1_sz = *(ulong *)fd_type_pun( proof->shreds );
436 0 : return (fd_shred_t *)fd_type_pun( proof->shreds + shred1_sz + 2*sizeof(ulong) );
437 0 : }
438 :
439 : /* fd_eqvoc_proof_shred2_const returns a const pointer to shred2 in `proof`. */
440 :
441 : static inline fd_shred_t const *
442 0 : fd_eqvoc_proof_shred2_const( fd_eqvoc_proof_t const * proof ) {
443 0 : ulong shred1_sz = *(ulong const *)fd_type_pun_const( proof->shreds );
444 0 : return (fd_shred_t const *)fd_type_pun_const( proof->shreds + shred1_sz + 2*sizeof(ulong) );
445 0 : }
446 :
447 : /* fd_eqvoc_verify verifies `slot` has FEC sets with merkle roots that
448 : correctly chain, including that the first FEC set in slot's merkle
449 : hash chains from the last FEC set in parent slot's merkle hash. */
450 :
451 : int
452 : fd_eqvoc_slot_verify( fd_eqvoc_t const * eqvoc, ulong slot );
453 :
454 : /* fd_eqvoc_from_chunks reconstructs shred1_out and shred2_out from
455 : `chunks` which is an array of "duplicate shred" gossip msgs. Shred1
456 : and shred2 comprise a "duplicate shred proof", ie. proof of two
457 : shreds that conflict and therefore demonstrate the shreds' producer
458 : has equivocated.
459 :
460 : Assumes `chunks` is non-NULL and contains at least one valid array
461 : member chunks[0] to extract header information. Caller's
462 : responsibility to guarantee this. Also assumes the `chunk` field in
463 : `fd_gossip_duplicate_shred_t` is a pointer to valid memory and
464 : consistent with the metadata presented in the header of the first
465 : array member, eg. if the header says there are 4 chunks then this
466 : implementation assumes this is true. These assumptions should be
467 : already upheld by caller if using deserializers in `fd_types.h`.
468 : Behavior is undefined otherwise.
469 :
470 : Does additional sanity-check validation eg. checking chunk_len <=
471 : FD_EQVOC_PROOF_SZ.
472 :
473 : This function is expected to be deprecated once chunks are specified
474 : to be fixed-length in the gossip protocol. */
475 :
476 : void
477 : fd_eqvoc_proof_from_chunks( fd_gossip_duplicate_shred_t const * chunks,
478 : fd_eqvoc_proof_t * proof_out );
479 :
480 : /* fd_eqvoc_to_chunks constructs an array of DuplicateShred gossip msgs
481 : (`chunks_out`) from shred1 and shred2.
482 :
483 : Shred1 and shred2 are concatenated (the concatenation is implemented
484 : virtually) and then spliced into chunks of FD_EQVOC_PROOF_CHUNK_SZ
485 : size. These chunks are embedded in the body of each DuplicateShred
486 : msg, along with a common header across all msgs.
487 :
488 : Caller supplies `chunks_out`, which is an array that MUST contain
489 : `ceil(shred1_payload_sz + shred2_payload_sz /
490 : FD_EQVOC_PROOF_CHUNK_SZ)` elements. Each chunk in `chunks_out` MUST
491 : have a buffer of at least `chunk_len` size available in its `chunk`
492 : pointer field. Behavior is undefined otherwise.
493 :
494 : IMPORTANT SAFETY TIP! The lifetime of each chunk in `chunks_out`
495 : must be at least as long as the lifetime of the array of
496 : duplicate_shreds. Caller is responsible for ensuring this memory
497 : safety guarantee. */
498 :
499 : void
500 : fd_eqvoc_proof_to_chunks( fd_eqvoc_proof_t * proof, fd_gossip_duplicate_shred_t * chunks_out );
501 :
502 : #endif /* HEADER_fd_src_choreo_eqvoc_fd_eqvoc_h */
|