LCOV - code coverage report
Current view: top level - choreo/eqvoc - fd_eqvoc.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 280 0.0 %
Date: 2025-01-08 12:08:44 Functions: 0 16 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             : 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 : }

Generated by: LCOV version 1.14