Line data Source code
1 : #include "fd_eqvoc.h"
2 : #include "../../ballet/shred/fd_shred.h"
3 :
4 : void *
5 0 : fd_eqvoc_new( void * shmem, ulong fec_max, ulong proof_max, ulong seed ) {
6 :
7 0 : if( FD_UNLIKELY( !shmem ) ) {
8 0 : FD_LOG_WARNING(( "NULL mem" ));
9 0 : return NULL;
10 0 : }
11 :
12 0 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shmem, fd_eqvoc_align() ) ) ) {
13 0 : FD_LOG_WARNING(( "misaligned mem" ));
14 0 : return NULL;
15 0 : }
16 :
17 0 : FD_SCRATCH_ALLOC_INIT( l, shmem );
18 0 : fd_eqvoc_t * eqvoc = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_eqvoc_t), sizeof(fd_eqvoc_t) );
19 0 : void * fec_pool = FD_SCRATCH_ALLOC_APPEND( l, fd_eqvoc_fec_pool_align(), fd_eqvoc_fec_pool_footprint( fec_max ) );
20 0 : void * fec_map = FD_SCRATCH_ALLOC_APPEND( l, fd_eqvoc_fec_map_align(), fd_eqvoc_fec_map_footprint( fec_max ) );
21 0 : void * proof_pool = FD_SCRATCH_ALLOC_APPEND( l, fd_eqvoc_proof_pool_align(), fd_eqvoc_proof_pool_footprint( proof_max ) );
22 0 : void * proof_map = FD_SCRATCH_ALLOC_APPEND( l, fd_eqvoc_proof_map_align(), fd_eqvoc_proof_map_footprint( proof_max ) );
23 0 : void * sha512 = FD_SCRATCH_ALLOC_APPEND( l, fd_sha512_align(), fd_sha512_footprint() );
24 0 : void * bmtree_mem = FD_SCRATCH_ALLOC_APPEND( l, fd_bmtree_commit_align(), fd_bmtree_commit_footprint( FD_SHRED_MERKLE_LAYER_CNT ) );
25 0 : FD_SCRATCH_ALLOC_FINI( l, fd_eqvoc_align() );
26 :
27 0 : eqvoc->fec_max = fec_max;
28 0 : eqvoc->proof_max = proof_max;
29 0 : eqvoc->shred_version = 0;
30 0 : fd_eqvoc_fec_pool_new( fec_pool, fec_max );
31 0 : fd_eqvoc_fec_map_new( fec_map, fec_max, seed );
32 0 : fd_eqvoc_proof_pool_new( proof_pool, proof_max );
33 0 : fd_eqvoc_proof_map_new( proof_map, proof_max, seed );
34 0 : fd_sha512_new( sha512 );
35 0 : (void)bmtree_mem; /* does not require new */
36 :
37 0 : return shmem;
38 0 : }
39 :
40 : fd_eqvoc_t *
41 0 : fd_eqvoc_join( void * sheqvoc ) {
42 :
43 0 : if( FD_UNLIKELY( !sheqvoc ) ) {
44 0 : FD_LOG_WARNING(( "NULL eqvoc" ));
45 0 : return NULL;
46 0 : }
47 :
48 0 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)sheqvoc, fd_eqvoc_align() ) ) ) {
49 0 : FD_LOG_WARNING(( "misaligned eqvoc" ));
50 0 : return NULL;
51 0 : }
52 :
53 0 : FD_SCRATCH_ALLOC_INIT( l, sheqvoc );
54 0 : fd_eqvoc_t * eqvoc = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_eqvoc_t), sizeof(fd_eqvoc_t) );
55 0 : void * fec_pool = FD_SCRATCH_ALLOC_APPEND( l, fd_eqvoc_fec_pool_align(), fd_eqvoc_fec_pool_footprint( eqvoc->fec_max ) );
56 0 : void * fec_map = FD_SCRATCH_ALLOC_APPEND( l, fd_eqvoc_fec_map_align(), fd_eqvoc_fec_map_footprint( eqvoc->fec_max ) );
57 0 : void * proof_pool = FD_SCRATCH_ALLOC_APPEND( l, fd_eqvoc_proof_pool_align(), fd_eqvoc_proof_pool_footprint( eqvoc->proof_max ) );
58 0 : void * proof_map = FD_SCRATCH_ALLOC_APPEND( l, fd_eqvoc_proof_map_align(), fd_eqvoc_proof_map_footprint( eqvoc->proof_max ) );
59 0 : void * sha512 = FD_SCRATCH_ALLOC_APPEND( l, fd_sha512_align(), fd_sha512_footprint() );
60 0 : void * bmtree_mem = FD_SCRATCH_ALLOC_APPEND( l, fd_bmtree_commit_align(), fd_bmtree_commit_footprint( FD_SHRED_MERKLE_LAYER_CNT ) );
61 0 : FD_SCRATCH_ALLOC_FINI( l, fd_eqvoc_align() );
62 :
63 0 : eqvoc->fec_pool = fd_eqvoc_fec_pool_join( fec_pool );
64 0 : eqvoc->fec_map = fd_eqvoc_fec_map_join( fec_map );
65 0 : eqvoc->proof_pool = fd_eqvoc_proof_pool_join( proof_pool );
66 0 : eqvoc->proof_map = fd_eqvoc_proof_map_join( proof_map );
67 0 : eqvoc->sha512 = fd_sha512_join( sha512 );
68 0 : eqvoc->bmtree_mem = bmtree_mem; /* does not require join */
69 :
70 0 : return (fd_eqvoc_t *)sheqvoc;
71 0 : }
72 :
73 : void *
74 0 : fd_eqvoc_leave( fd_eqvoc_t const * eqvoc ) {
75 :
76 0 : if( FD_UNLIKELY( !eqvoc ) ) {
77 0 : FD_LOG_WARNING(( "NULL eqvoc" ));
78 0 : return NULL;
79 0 : }
80 :
81 0 : return (void *)eqvoc;
82 0 : }
83 :
84 : void *
85 0 : fd_eqvoc_delete( void * eqvoc ) {
86 :
87 0 : if( FD_UNLIKELY( !eqvoc ) ) {
88 0 : FD_LOG_WARNING(( "NULL eqvoc" ));
89 0 : return NULL;
90 0 : }
91 :
92 0 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)eqvoc, fd_eqvoc_align() ) ) ) {
93 0 : FD_LOG_WARNING(( "misaligned eqvoc" ));
94 0 : return NULL;
95 0 : }
96 :
97 0 : return eqvoc;
98 0 : }
99 :
100 : void
101 0 : fd_eqvoc_init( fd_eqvoc_t * eqvoc, ulong shred_version ) {
102 0 : eqvoc->shred_version = shred_version;
103 0 : }
104 :
105 : fd_eqvoc_fec_t *
106 0 : fd_eqvoc_fec_insert( fd_eqvoc_t * eqvoc, ulong slot, uint fec_set_idx ) {
107 0 : fd_slot_fec_t key = { slot, fec_set_idx };
108 :
109 0 : #if FD_EQVOC_USE_HANDHOLDING
110 0 : if( FD_UNLIKELY( fd_eqvoc_fec_map_ele_query( eqvoc->fec_map, &key, NULL, eqvoc->fec_pool ) ) ) FD_LOG_ERR(( "[%s] key (%lu, %u) already in map.", __func__, slot, fec_set_idx ));
111 0 : #endif
112 :
113 : /* FIXME eviction */
114 :
115 0 : if( FD_UNLIKELY( !fd_eqvoc_fec_pool_free( eqvoc->fec_pool ) ) ) FD_LOG_ERR(( "[%s] map full.", __func__ ));
116 :
117 0 : fd_eqvoc_fec_t * fec = fd_eqvoc_fec_pool_ele_acquire( eqvoc->fec_pool );
118 0 : fec->key.slot = slot;
119 0 : fec->key.fec_set_idx = fec_set_idx;
120 0 : fec->code_cnt = 0;
121 0 : fec->data_cnt = 0;
122 0 : fec->last_idx = FD_SHRED_IDX_NULL;
123 0 : fd_eqvoc_fec_map_ele_insert( eqvoc->fec_map, fec, eqvoc->fec_pool);
124 0 : return fec;
125 0 : }
126 :
127 : fd_eqvoc_fec_t const *
128 0 : fd_eqvoc_fec_search( fd_eqvoc_t const * eqvoc, fd_shred_t const * shred ) {
129 0 : fd_eqvoc_fec_t const * entry = fd_eqvoc_fec_query( eqvoc, shred->slot, shred->fec_set_idx );
130 :
131 : /* If we've already seen a shred in this FEC set */
132 :
133 0 : if( FD_LIKELY( entry ) ) {
134 :
135 : /* Make sure the signature matches. Every merkle shred in the FEC
136 : set must have the same signature. */
137 :
138 0 : if( FD_UNLIKELY( 0 != memcmp( entry->sig, shred->signature, FD_ED25519_SIG_SZ ) ) ) {
139 0 : return entry;
140 0 : }
141 :
142 : /* Check if this shred's idx is higher than another shred that claimed
143 : to be the last_idx. This indicates equivocation. */
144 :
145 0 : if( FD_UNLIKELY( shred->idx > entry->last_idx ) ) {
146 0 : return entry;
147 0 : }
148 0 : }
149 :
150 : /* Look backward FEC_MAX idxs for overlap. */
151 :
152 0 : for( uint i = 1; shred->fec_set_idx >= i && i < FD_EQVOC_FEC_MAX; i++ ) {
153 0 : fd_eqvoc_fec_t const * conflict = fd_eqvoc_fec_query( eqvoc, shred->slot, shred->fec_set_idx - i );
154 0 : if( FD_UNLIKELY( conflict &&
155 0 : conflict->data_cnt > 0 &&
156 0 : conflict->key.fec_set_idx + conflict->data_cnt > shred->fec_set_idx ) ) {
157 0 : return conflict;
158 0 : }
159 0 : }
160 :
161 : /* Look forward data_cnt idxs for overlap. */
162 :
163 0 : for( uint i = 1; entry && i < entry->data_cnt; i++ ) {
164 0 : fd_eqvoc_fec_t const * conflict = fd_eqvoc_fec_query( eqvoc, shred->slot, shred->fec_set_idx + i );
165 0 : if( FD_UNLIKELY( conflict ) ) return conflict;
166 0 : }
167 :
168 0 : return NULL; /* No conflicts */
169 0 : }
170 :
171 : int
172 : fd_eqvoc_fec_verify( FD_PARAM_UNUSED fd_eqvoc_t const * eqvoc,
173 : fd_blockstore_t * blockstore,
174 : ulong slot,
175 : uint fec_set_idx,
176 0 : fd_hash_t * chained_hash ) {
177 :
178 0 : fd_shred_t * shred = NULL;
179 0 : uint idx = fec_set_idx;
180 0 : do {
181 0 : shred = fd_buf_shred_query( blockstore, slot, idx );
182 :
183 0 : #if FD_EQVOC_USE_HANDHOLDING
184 0 : if( FD_UNLIKELY( !shred ) ) {
185 0 : FD_LOG_WARNING(( "[%s] couldn't find shred %lu %u", __func__, slot, fec_set_idx ));
186 0 : return 0;
187 0 : }
188 0 : #endif
189 :
190 0 : #if FD_EQVOC_USE_HANDHOLDING
191 0 : FD_TEST( fd_shred_is_chained( fd_shred_type( shred->variant ) ) );
192 0 : #endif
193 :
194 0 : if( FD_UNLIKELY( 0 != memcmp( chained_hash, shred + fd_shred_chain_off( shred->variant ), FD_SHRED_MERKLE_ROOT_SZ ) ) ) {
195 0 : return 0;
196 0 : }
197 :
198 0 : } while( shred->fec_set_idx == fec_set_idx );
199 :
200 0 : return 1;
201 0 : }
202 :
203 : fd_eqvoc_proof_t *
204 0 : fd_eqvoc_proof_insert( fd_eqvoc_t * eqvoc, ulong slot, fd_pubkey_t const * from ) {
205 0 : fd_slot_pubkey_t key = { slot, *from };
206 :
207 0 : #if FD_EQVOC_USE_HANDHOLDING
208 0 : if( FD_UNLIKELY( fd_eqvoc_proof_map_ele_query( eqvoc->proof_map, &key, NULL, eqvoc->proof_pool ) ) ) FD_LOG_ERR(( "[%s] key (%lu, %s) already in map.", __func__, slot, FD_BASE58_ENC_32_ALLOCA( from->uc ) ));
209 0 : #endif
210 :
211 : /* FIXME eviction */
212 :
213 0 : fd_eqvoc_proof_t * proof = fd_eqvoc_proof_pool_ele_acquire( eqvoc->proof_pool );
214 0 : memset( proof, 0, sizeof(fd_eqvoc_proof_t) );
215 0 : proof->key.slot = slot;
216 0 : proof->key.hash = *from;
217 0 : fd_eqvoc_proof_map_ele_insert( eqvoc->proof_map, proof, eqvoc->proof_pool );
218 0 : return proof;
219 0 : }
220 :
221 : void
222 0 : fd_eqvoc_proof_chunk_insert( fd_eqvoc_proof_t * proof, fd_gossip_duplicate_shred_t const * chunk ) {
223 0 : if( FD_UNLIKELY( chunk->wallclock > proof->wallclock ) ) {
224 0 : FD_LOG_WARNING(( "[%s] received newer chunk (slot: %lu from: %s). overwriting.", __func__, proof->key.slot, FD_BASE58_ENC_32_ALLOCA( proof->key.hash.uc ) ));
225 0 : proof->wallclock = chunk->wallclock;
226 0 : proof->chunk_cnt = chunk->num_chunks;
227 0 : memset( proof->set, 0, 4 * sizeof(ulong) );
228 : // fd_eqvoc_proof_set_null( proof->set );
229 0 : }
230 :
231 0 : if ( FD_UNLIKELY( chunk->wallclock < proof->wallclock ) ) {
232 0 : FD_LOG_WARNING(( "[%s] received older chunk (slot: %lu from: %s). ignoring.", __func__, proof->key.slot, FD_BASE58_ENC_32_ALLOCA( proof->key.hash.uc ) ));
233 0 : return;
234 0 : }
235 :
236 0 : if( FD_UNLIKELY( proof->chunk_cnt != chunk->num_chunks ) ) {
237 0 : FD_LOG_WARNING(( "[%s] received incompatible chunk (slot: %lu from: %s). ignoring.", __func__, proof->key.slot, FD_BASE58_ENC_32_ALLOCA( proof->key.hash.uc ) ));
238 0 : return;
239 0 : }
240 :
241 :
242 0 : if( FD_UNLIKELY( fd_eqvoc_proof_set_test( proof->set, chunk->chunk_index ) ) ) {
243 0 : FD_LOG_WARNING(( "[%s] already received chunk %u. slot: %lu from: %s. ignoring.", __func__, chunk->chunk_index, proof->key.slot, FD_BASE58_ENC_32_ALLOCA( proof->key.hash.uc ) ));
244 0 : return;
245 0 : }
246 :
247 0 : fd_memcpy( &proof->shreds[proof->chunk_sz * chunk->chunk_index], chunk->chunk, chunk->chunk_len );
248 0 : fd_eqvoc_proof_set_insert( proof->set, chunk->chunk_index );
249 0 : }
250 :
251 : /* fd_eqvoc_proof_init initializes a new proof entry. */
252 :
253 : void
254 0 : fd_eqvoc_proof_init( fd_eqvoc_proof_t * proof, fd_pubkey_t const * producer, ulong wallclock, ulong chunk_cnt, ulong chunk_sz, void * bmtree_mem ) {
255 0 : proof->producer = *producer;
256 0 : proof->bmtree_mem = bmtree_mem;
257 0 : proof->wallclock = wallclock;
258 0 : proof->chunk_cnt = chunk_cnt;
259 0 : proof->chunk_sz = chunk_sz;
260 0 : memset( proof->set, 0, 4 * sizeof(ulong) );
261 0 : memset( proof->shreds, 0, 2472 );
262 0 : }
263 :
264 :
265 : void
266 0 : fd_eqvoc_proof_remove( fd_eqvoc_t * eqvoc, fd_slot_pubkey_t const * key ) {
267 0 : fd_eqvoc_proof_t * proof = fd_eqvoc_proof_map_ele_remove( eqvoc->proof_map, key, NULL, eqvoc->proof_pool );
268 0 : if( FD_UNLIKELY( !proof ) ) {
269 0 : FD_LOG_WARNING(( "[%s] key (%lu, %s) not in map.", __func__, key->slot, FD_BASE58_ENC_32_ALLOCA( key->hash.uc ) ));
270 0 : return;
271 0 : }
272 0 : fd_eqvoc_proof_pool_ele_release( eqvoc->proof_pool, proof );
273 0 : }
274 :
275 : int
276 0 : fd_eqvoc_proof_verify( fd_eqvoc_proof_t const * proof ) {
277 0 : return fd_eqvoc_shreds_verify( fd_eqvoc_proof_shred1_const( proof ), fd_eqvoc_proof_shred2_const( proof ), &proof->producer, proof->bmtree_mem );
278 0 : }
279 :
280 : int
281 0 : fd_eqvoc_shreds_verify( fd_shred_t const * shred1, fd_shred_t const * shred2, fd_pubkey_t const * producer, void * bmtree_mem ) {
282 0 : if( FD_UNLIKELY( shred1->slot != shred2->slot ) ) {
283 0 : return FD_EQVOC_PROOF_VERIFY_ERR_SLOT;
284 0 : }
285 :
286 0 : if( FD_UNLIKELY( shred1->version != shred2->version ) ) {
287 0 : return FD_EQVOC_PROOF_VERIFY_ERR_VERSION;
288 0 : }
289 :
290 0 : if( FD_UNLIKELY( !fd_shred_is_chained ( fd_shred_type( shred1->variant) ) &&
291 0 : !fd_shred_is_resigned( fd_shred_type( shred2->variant ) ) ) ) {
292 0 : return FD_EQVOC_PROOF_VERIFY_ERR_TYPE;
293 0 : }
294 :
295 : /* Check both shreds contain valid signatures from the assigned leader
296 : to that slot. This requires deriving the merkle root and
297 : sig-verifying it, because the leader signs the merkle root for
298 : merkle shreds.
299 :
300 : TODO remove? */
301 :
302 0 : fd_bmtree_node_t root1 = { 0 };
303 0 : if( FD_UNLIKELY( !fd_shred_merkle_root( shred1, bmtree_mem, &root1 ) ) ) {
304 0 : return FD_EQVOC_PROOF_VERIFY_ERR_MERKLE;
305 0 : }
306 0 : fd_bmtree_node_t root2;
307 0 : if( FD_UNLIKELY( !fd_shred_merkle_root( shred2, bmtree_mem, &root2 ) ) ) {
308 0 : return FD_EQVOC_PROOF_VERIFY_ERR_MERKLE;
309 0 : }
310 0 : fd_sha512_t _sha512[1];
311 0 : fd_sha512_t * sha512 = fd_sha512_join( fd_sha512_new( _sha512 ) );
312 0 : if( FD_UNLIKELY( FD_ED25519_SUCCESS != fd_ed25519_verify( root1.hash,
313 0 : 32UL,
314 0 : shred1->signature,
315 0 : producer->uc,
316 0 : sha512 ) ||
317 0 : FD_ED25519_SUCCESS != fd_ed25519_verify( root2.hash,
318 0 : 32UL,
319 0 : shred2->signature,
320 0 : producer->uc,
321 0 : sha512 ) ) ) {
322 0 : return FD_EQVOC_PROOF_VERIFY_ERR_SIGNATURE;
323 0 : }
324 :
325 : /* Same FEC set index checks */
326 :
327 0 : if( FD_LIKELY( shred1->fec_set_idx == shred2->fec_set_idx ) ) {
328 :
329 : /* Test if two shreds have different signatures when they are in the
330 : same FEC set. */
331 :
332 0 : if( FD_LIKELY( 0 != memcmp( shred1->signature, shred2->signature, FD_ED25519_SIG_SZ ) ) ) {
333 0 : return FD_EQVOC_PROOF_VERIFY_SUCCESS_SIGNATURE;
334 0 : }
335 :
336 : /* Test if the shreds have different coding metadata when they're
337 : both coding shreds in the same FEC set. */
338 :
339 0 : if( FD_UNLIKELY( fd_shred_is_code( fd_shred_type( shred1->variant ) ) &&
340 0 : fd_shred_is_code( fd_shred_type( shred2->variant ) ) &&
341 0 : ( shred1->code.code_cnt != shred2->code.code_cnt ||
342 0 : shred1->code.data_cnt != shred2->code.data_cnt ||
343 0 : shred1->idx - shred1->code.idx == shred2->idx - shred2->code.idx ) ) ) {
344 0 : return FD_EQVOC_PROOF_VERIFY_SUCCESS_META;
345 0 : }
346 :
347 : /* Test if one shred is marked the last shred in the slot, but the
348 : other shred has a higher index when both shreds are data
349 : shreds. */
350 :
351 0 : if( FD_UNLIKELY( fd_shred_is_data( fd_shred_type( shred1->variant ) ) &&
352 0 : fd_shred_is_data( fd_shred_type( shred2->variant ) ) &&
353 0 : ( ( shred1->data.flags & FD_SHRED_DATA_FLAG_SLOT_COMPLETE && shred2->idx > shred1->idx ) ||
354 0 : ( shred2->data.flags & FD_SHRED_DATA_FLAG_SLOT_COMPLETE && shred1->idx > shred2->idx ) ) ) ) {
355 0 : return FD_EQVOC_PROOF_VERIFY_SUCCESS_LAST;
356 0 : }
357 0 : }
358 :
359 : /* Different FEC set index checks. Lower FEC set index shred must be a
360 : coding shred. */
361 :
362 0 : fd_shred_t const * lo = fd_ptr_if( shred1->fec_set_idx < shred2->fec_set_idx, shred1, shred2 );
363 0 : fd_shred_t const * hi = fd_ptr_if( shred1->fec_set_idx > shred2->fec_set_idx, shred1, shred2 );
364 :
365 0 : if ( FD_UNLIKELY( fd_shred_is_code( fd_shred_type( lo->variant ) ) ) ) {
366 :
367 : /* Test for overlap. The FEC sets overlap if the lower fec_set_idx +
368 : data_cnt > higher fec_set_idx. We must have received at least one
369 : coding shred in the FEC set with the lower fec_set_idx to perform
370 : this check. */
371 :
372 0 : if( FD_UNLIKELY( lo->fec_set_idx + lo->code.data_cnt > hi->fec_set_idx ) ) {
373 0 : return FD_EQVOC_PROOF_VERIFY_SUCCESS_OVERLAP;
374 0 : }
375 :
376 : /* Test for conflicting chained merkle roots when shred1 and shred2
377 : are in adjacent FEC sets. We know the FEC sets are adjacent if the
378 : last data shred index in the lower FEC set is one less than the
379 : first data shred index in the higher FEC set. */
380 :
381 0 : if( FD_UNLIKELY( lo->fec_set_idx + lo->code.data_cnt == hi->fec_set_idx ) ) {
382 0 : uchar * merkle_hash = fd_ptr_if( shred1->fec_set_idx < shred2->fec_set_idx,
383 0 : (uchar *)shred1 + fd_shred_merkle_off( shred1 ),
384 0 : (uchar *)shred2 + fd_shred_merkle_off( shred2 ) );
385 0 : uchar * chained_hash = fd_ptr_if( shred1->fec_set_idx > shred2->fec_set_idx,
386 0 : (uchar *)shred1 + fd_shred_chain_off( shred1->variant ),
387 0 : (uchar *)shred2 + fd_shred_chain_off( shred2->variant ) );
388 0 : if ( FD_LIKELY( 0 != memcmp( merkle_hash, chained_hash, FD_SHRED_MERKLE_ROOT_SZ ) ) ) {
389 0 : return FD_EQVOC_PROOF_VERIFY_SUCCESS_CHAINED;
390 0 : };
391 0 : }
392 0 : }
393 :
394 : /* None of the equivocation tests passed, so this equivocation proof
395 : failed to verify. */
396 :
397 0 : return FD_EQVOC_PROOF_VERIFY_FAILURE;
398 0 : }
399 :
400 : void
401 : fd_eqvoc_proof_from_chunks( fd_gossip_duplicate_shred_t const * chunks,
402 0 : fd_eqvoc_proof_t * proof_out ) {
403 0 : ulong chunk_cnt = chunks[0].num_chunks;
404 0 : for ( ulong i = 0; i < chunk_cnt; i++ ) {
405 0 : fd_eqvoc_proof_chunk_insert( proof_out, chunks + i );
406 0 : }
407 0 : }
408 :
409 : void
410 0 : fd_eqvoc_proof_to_chunks( fd_eqvoc_proof_t * proof, fd_gossip_duplicate_shred_t * chunks_out ) {
411 0 : for (uchar i = 0; i < FD_EQVOC_PROOF_CHUNK_CNT; i++ ) {
412 0 : fd_gossip_duplicate_shred_t * chunk = &chunks_out[i];
413 0 : chunk->duplicate_shred_index = i;
414 0 : chunk->from = proof->key.hash;
415 0 : chunk->wallclock = (ulong)fd_log_wallclock();
416 0 : chunk->slot = proof->key.slot;
417 0 : chunk->num_chunks = FD_EQVOC_PROOF_CHUNK_CNT;
418 0 : chunk->chunk_len = FD_EQVOC_PROOF_CHUNK_MAX;
419 0 : ulong off = i * FD_EQVOC_PROOF_CHUNK_MAX;
420 0 : ulong sz = fd_ulong_min( FD_EQVOC_PROOF_CHUNK_MAX, FD_EQVOC_PROOF_MAX - off );
421 0 : fd_memcpy( chunks_out[i].chunk, proof->shreds + off, sz );
422 0 : }
423 0 : }
|