LCOV - code coverage report
Current view: top level - choreo/eqvoc - fd_eqvoc.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 267 0.0 %
Date: 2025-12-20 04:58:29 Functions: 0 15 0.0 %

          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             : fd_eqvoc_proof_t *
     172           0 : fd_eqvoc_proof_insert( fd_eqvoc_t * eqvoc, ulong slot, fd_pubkey_t const * from ) {
     173           0 :   fd_slot_pubkey_t key = { slot, *from };
     174             : 
     175           0 :   #if FD_EQVOC_USE_HANDHOLDING
     176           0 :   if( FD_UNLIKELY( fd_eqvoc_proof_map_ele_query( eqvoc->proof_map, &key, NULL, eqvoc->proof_pool ) ) ) {
     177           0 :     FD_BASE58_ENCODE_32_BYTES( from->key, from_b58 );
     178           0 :     FD_LOG_ERR(( "[%s] key (%lu, %s) already in map.", __func__, slot, from_b58 ));
     179           0 :   }
     180           0 :   #endif
     181             : 
     182             :   /* FIXME eviction */
     183             : 
     184           0 :   fd_eqvoc_proof_t * proof = fd_eqvoc_proof_pool_ele_acquire( eqvoc->proof_pool );
     185           0 :   memset( proof, 0, sizeof(fd_eqvoc_proof_t) );
     186           0 :   proof->key.slot = slot;
     187           0 :   proof->key.hash = *from;
     188           0 :   fd_eqvoc_proof_map_ele_insert( eqvoc->proof_map, proof, eqvoc->proof_pool );
     189           0 :   return proof;
     190           0 : }
     191             : 
     192             : void
     193           0 : fd_eqvoc_proof_chunk_insert( fd_eqvoc_proof_t * proof, fd_gossip_duplicate_shred_t const * chunk ) {
     194           0 :   if( FD_UNLIKELY( chunk->wallclock > proof->wallclock ) ) {
     195           0 :     FD_BASE58_ENCODE_32_BYTES( proof->key.hash.key, hash_b58 );
     196           0 :     FD_LOG_WARNING(( "[%s] received newer chunk (slot: %lu from: %s). overwriting.", __func__, proof->key.slot, hash_b58 ));
     197           0 :     proof->wallclock = chunk->wallclock;
     198           0 :     proof->chunk_cnt = chunk->num_chunks;
     199           0 :     memset( proof->set, 0, 4 * sizeof(ulong) );
     200             :     // fd_eqvoc_proof_set_null( proof->set );
     201           0 :   }
     202             : 
     203           0 :   if ( FD_UNLIKELY( chunk->wallclock < proof->wallclock ) ) {
     204           0 :     FD_BASE58_ENCODE_32_BYTES( proof->key.hash.key, hash_b58 );
     205           0 :     FD_LOG_WARNING(( "[%s] received older chunk (slot: %lu from: %s). ignoring.", __func__, proof->key.slot, hash_b58 ));
     206           0 :     return;
     207           0 :   }
     208             : 
     209           0 :   if( FD_UNLIKELY( proof->chunk_cnt != chunk->num_chunks ) ) {
     210           0 :     FD_BASE58_ENCODE_32_BYTES( proof->key.hash.key, hash_b58 );
     211           0 :     FD_LOG_WARNING(( "[%s] received incompatible chunk (slot: %lu from: %s). ignoring.", __func__, proof->key.slot, hash_b58 ));
     212           0 :     return;
     213           0 :   }
     214             : 
     215             : 
     216           0 :   if( FD_UNLIKELY( fd_eqvoc_proof_set_test( proof->set, chunk->chunk_index ) ) ) {
     217           0 :     FD_BASE58_ENCODE_32_BYTES( proof->key.hash.key, hash_b58 );
     218           0 :     FD_LOG_WARNING(( "[%s] already received chunk %u. slot: %lu from: %s. ignoring.", __func__, chunk->chunk_index, proof->key.slot, hash_b58 ));
     219           0 :     return;
     220           0 :   }
     221             : 
     222           0 :   fd_memcpy( &proof->shreds[proof->chunk_sz * chunk->chunk_index], chunk->chunk, chunk->chunk_len );
     223           0 :   fd_eqvoc_proof_set_insert( proof->set, chunk->chunk_index );
     224           0 : }
     225             : 
     226             : /* fd_eqvoc_proof_init initializes a new proof entry. */
     227             : 
     228             : void
     229           0 : 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 ) {
     230           0 :   proof->producer   = *producer;
     231           0 :   proof->bmtree_mem = bmtree_mem;
     232           0 :   proof->wallclock  = wallclock;
     233           0 :   proof->chunk_cnt  = chunk_cnt;
     234           0 :   proof->chunk_sz   = chunk_sz;
     235           0 :   memset( proof->set, 0, 4 * sizeof(ulong) );
     236           0 :   memset( proof->shreds, 0, 2472 );
     237           0 : }
     238             : 
     239             : 
     240             : void
     241           0 : fd_eqvoc_proof_remove( fd_eqvoc_t * eqvoc, fd_slot_pubkey_t const * key ) {
     242           0 :   fd_eqvoc_proof_t * proof = fd_eqvoc_proof_map_ele_remove( eqvoc->proof_map, key, NULL, eqvoc->proof_pool );
     243           0 :   if( FD_UNLIKELY( !proof ) ) {
     244           0 :     FD_BASE58_ENCODE_32_BYTES( key->hash.key, hash_b58 );
     245           0 :     FD_LOG_WARNING(( "[%s] key (%lu, %s) not in map.", __func__, key->slot, hash_b58 ));
     246           0 :     return;
     247           0 :   }
     248           0 :   fd_eqvoc_proof_pool_ele_release( eqvoc->proof_pool, proof );
     249           0 : }
     250             : 
     251             : int
     252           0 : fd_eqvoc_proof_verify( fd_eqvoc_proof_t const * proof ) {
     253           0 :   return fd_eqvoc_shreds_verify( fd_eqvoc_proof_shred1_const( proof ), fd_eqvoc_proof_shred2_const( proof ), &proof->producer, proof->bmtree_mem );
     254           0 : }
     255             : 
     256             : int
     257           0 : fd_eqvoc_shreds_verify( fd_shred_t const * shred1, fd_shred_t const * shred2, fd_pubkey_t const * producer, void * bmtree_mem ) {
     258           0 :   if( FD_UNLIKELY( shred1->slot != shred2->slot ) ) {
     259           0 :     return FD_EQVOC_PROOF_VERIFY_ERR_SLOT;
     260           0 :   }
     261             : 
     262           0 :   if( FD_UNLIKELY( shred1->version != shred2->version ) ) {
     263           0 :     return FD_EQVOC_PROOF_VERIFY_ERR_VERSION;
     264           0 :   }
     265             : 
     266           0 :   if( FD_UNLIKELY( !fd_shred_is_chained ( fd_shred_type( shred1->variant) ) &&
     267           0 :                    !fd_shred_is_resigned( fd_shred_type( shred2->variant ) ) ) ) {
     268           0 :     return FD_EQVOC_PROOF_VERIFY_ERR_TYPE;
     269           0 :   }
     270             : 
     271             :   /* Check both shreds contain valid signatures from the assigned leader
     272             :      to that slot. This requires deriving the merkle root and
     273             :      sig-verifying it, because the leader signs the merkle root for
     274             :      merkle shreds.
     275             : 
     276             :      TODO remove? */
     277             : 
     278           0 :   fd_bmtree_node_t root1 = { 0 };
     279           0 :   if( FD_UNLIKELY( !fd_shred_merkle_root( shred1, bmtree_mem, &root1 ) ) ) {
     280           0 :     return FD_EQVOC_PROOF_VERIFY_ERR_MERKLE;
     281           0 :   }
     282           0 :   fd_bmtree_node_t root2;
     283           0 :   if( FD_UNLIKELY( !fd_shred_merkle_root( shred2, bmtree_mem, &root2 ) ) ) {
     284           0 :     return FD_EQVOC_PROOF_VERIFY_ERR_MERKLE;
     285           0 :   }
     286           0 :   fd_sha512_t _sha512[1];
     287           0 :   fd_sha512_t * sha512 = fd_sha512_join( fd_sha512_new( _sha512 ) );
     288           0 :   if( FD_UNLIKELY( FD_ED25519_SUCCESS != fd_ed25519_verify( root1.hash,
     289           0 :                                                             32UL,
     290           0 :                                                             shred1->signature,
     291           0 :                                                             producer->uc,
     292           0 :                                                             sha512 ) ||
     293           0 :                    FD_ED25519_SUCCESS != fd_ed25519_verify( root2.hash,
     294           0 :                                                             32UL,
     295           0 :                                                             shred2->signature,
     296           0 :                                                             producer->uc,
     297           0 :                                                             sha512 ) ) ) {
     298           0 :     return FD_EQVOC_PROOF_VERIFY_ERR_SIGNATURE;
     299           0 :   }
     300             : 
     301             :   /* Same FEC set index checks */
     302             : 
     303           0 :   if( FD_LIKELY( shred1->fec_set_idx == shred2->fec_set_idx ) ) {
     304             : 
     305             :     /* Test if two shreds have different signatures when they are in the
     306             :       same FEC set. */
     307             : 
     308           0 :     if( FD_LIKELY( 0 != memcmp( shred1->signature, shred2->signature, FD_ED25519_SIG_SZ ) ) ) {
     309           0 :       return FD_EQVOC_PROOF_VERIFY_SUCCESS_SIGNATURE;
     310           0 :     }
     311             : 
     312             :     /* Test if the shreds have different coding metadata when they're
     313             :        both coding shreds in the same FEC set. */
     314             : 
     315           0 :     if( FD_UNLIKELY( fd_shred_is_code( fd_shred_type( shred1->variant ) ) &&
     316           0 :                      fd_shred_is_code( fd_shred_type( shred2->variant ) ) &&
     317           0 :                      ( shred1->code.code_cnt != shred2->code.code_cnt ||
     318           0 :                        shred1->code.data_cnt != shred2->code.data_cnt ||
     319           0 :                        shred1->idx - shred1->code.idx == shred2->idx - shred2->code.idx ) ) ) {
     320           0 :       return FD_EQVOC_PROOF_VERIFY_SUCCESS_META;
     321           0 :     }
     322             : 
     323             :     /* Test if one shred is marked the last shred in the slot, but the
     324             :        other shred has a higher index when both shreds are data
     325             :        shreds. */
     326             : 
     327           0 :     if( FD_UNLIKELY( fd_shred_is_data( fd_shred_type( shred1->variant ) ) &&
     328           0 :                      fd_shred_is_data( fd_shred_type( shred2->variant ) ) &&
     329           0 :                      ( ( shred1->data.flags & FD_SHRED_DATA_FLAG_SLOT_COMPLETE && shred2->idx > shred1->idx )  ||
     330           0 :                        ( shred2->data.flags & FD_SHRED_DATA_FLAG_SLOT_COMPLETE && shred1->idx > shred2->idx ) ) ) ) {
     331           0 :       return FD_EQVOC_PROOF_VERIFY_SUCCESS_LAST;
     332           0 :     }
     333           0 :   }
     334             : 
     335             :   /* Different FEC set index checks. Lower FEC set index shred must be a
     336             :      coding shred. */
     337             : 
     338           0 :   fd_shred_t const * lo = fd_ptr_if( shred1->fec_set_idx < shred2->fec_set_idx, shred1, shred2 );
     339           0 :   fd_shred_t const * hi = fd_ptr_if( shred1->fec_set_idx > shred2->fec_set_idx, shred1, shred2 );
     340             : 
     341           0 :   if ( FD_UNLIKELY( fd_shred_is_code( fd_shred_type( lo->variant ) ) ) ) {
     342             : 
     343             :     /* Test for overlap. The FEC sets overlap if the lower fec_set_idx +
     344             :        data_cnt > higher fec_set_idx. We must have received at least one
     345             :        coding shred in the FEC set with the lower fec_set_idx to perform
     346             :        this check. */
     347             : 
     348           0 :     if( FD_UNLIKELY( lo->fec_set_idx + lo->code.data_cnt > hi->fec_set_idx ) ) {
     349           0 :       return FD_EQVOC_PROOF_VERIFY_SUCCESS_OVERLAP;
     350           0 :     }
     351             : 
     352             :     /* Test for conflicting chained merkle roots when shred1 and shred2
     353             :       are in adjacent FEC sets. We know the FEC sets are adjacent if the
     354             :       last data shred index in the lower FEC set is one less than the
     355             :       first data shred index in the higher FEC set. */
     356             : 
     357           0 :     if( FD_UNLIKELY( lo->fec_set_idx + lo->code.data_cnt == hi->fec_set_idx ) ) {
     358           0 :       uchar * merkle_hash  = fd_ptr_if( shred1->fec_set_idx < shred2->fec_set_idx,
     359           0 :                                         (uchar *)shred1 + fd_shred_merkle_off( shred1 ),
     360           0 :                                         (uchar *)shred2 + fd_shred_merkle_off( shred2 ) );
     361           0 :       uchar * chained_hash = fd_ptr_if( shred1->fec_set_idx > shred2->fec_set_idx,
     362           0 :                                         (uchar *)shred1 + fd_shred_chain_off( shred1->variant ),
     363           0 :                                         (uchar *)shred2 + fd_shred_chain_off( shred2->variant ) );
     364           0 :       if ( FD_LIKELY( 0 != memcmp( merkle_hash, chained_hash, FD_SHRED_MERKLE_ROOT_SZ ) ) ) {
     365           0 :         return FD_EQVOC_PROOF_VERIFY_SUCCESS_CHAINED;
     366           0 :       };
     367           0 :     }
     368           0 :   }
     369             : 
     370             :   /* None of the equivocation tests passed, so this equivocation proof
     371             :      failed to verify. */
     372             : 
     373           0 :   return FD_EQVOC_PROOF_VERIFY_FAILURE;
     374           0 : }
     375             : 
     376             : void
     377             : fd_eqvoc_proof_from_chunks( fd_gossip_duplicate_shred_t const * chunks,
     378           0 :                             fd_eqvoc_proof_t * proof_out ) {
     379           0 :   ulong chunk_cnt = chunks[0].num_chunks;
     380           0 :   for ( ulong i = 0; i < chunk_cnt; i++ ) {
     381           0 :     fd_eqvoc_proof_chunk_insert( proof_out, chunks + i );
     382           0 :   }
     383           0 : }
     384             : 
     385             : void
     386           0 : fd_eqvoc_proof_to_chunks( fd_eqvoc_proof_t * proof, fd_gossip_duplicate_shred_t * chunks_out ) {
     387           0 :   for (uchar i = 0; i < FD_EQVOC_PROOF_CHUNK_CNT; i++ ) {
     388           0 :     fd_gossip_duplicate_shred_t * chunk = &chunks_out[i];
     389           0 :     chunk->index = i;
     390           0 :     chunk->wallclock = fd_log_wallclock();
     391           0 :     chunk->slot = proof->key.slot;
     392           0 :     chunk->num_chunks = FD_EQVOC_PROOF_CHUNK_CNT;
     393           0 :     chunk->chunk_len = FD_EQVOC_PROOF_CHUNK_SZ;
     394           0 :     ulong off = i * FD_EQVOC_PROOF_CHUNK_SZ;
     395           0 :     ulong sz  = fd_ulong_min( FD_EQVOC_PROOF_CHUNK_SZ, FD_EQVOC_PROOF_SZ - off );
     396           0 :     fd_memcpy( chunks_out[i].chunk, proof->shreds + off, sz );
     397           0 :   }
     398           0 : }

Generated by: LCOV version 1.14