LCOV - code coverage report
Current view: top level - choreo/votor - ag_cert.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 87 0.0 %
Date: 2026-10-22 18:10:43 Functions: 0 8 0.0 %

          Line data    Source code
       1             : #include "ag_cert.h"
       2             : 
       3             : #include "ag_vote_serde.h" /* ag_vote_signing_ser */
       4             : 
       5             : static int
       6             : is_signer( ag_cert_t const * self,
       7           0 :            ulong             rank ) {
       8           0 :   switch( self->kind ) {
       9           0 :   case AG_CERT_KIND_FINAL:          return fd_bls_set_test( self->final.agg.set,               rank );
      10           0 :   case AG_CERT_KIND_FAST_FINAL:     return fd_bls_set_test( self->fast_final.agg.set,          rank );
      11           0 :   case AG_CERT_KIND_NOTAR:          return fd_bls_set_test( self->notar.agg.set,               rank );
      12           0 :   case AG_CERT_KIND_NOTAR_FALLBACK: return fd_bls_set_test( self->notar_fallback.agg_notar.set, rank ) || fd_bls_set_test( self->notar_fallback.agg_notar_fallback.set, rank );
      13           0 :   case AG_CERT_KIND_SKIP:           return fd_bls_set_test( self->skip.agg_skip.set,            rank ) || fd_bls_set_test( self->skip.agg_skip_fallback.set,            rank );
      14           0 :   default:                          FD_LOG_CRIT(( "unreachable" ));
      15           0 :   }
      16           0 : }
      17             : 
      18             : /* each validator is counted once even if in both partitions */
      19             : 
      20             : static int
      21             : check_threshold( ag_cert_t const *       self,
      22           0 :                  ag_epoch_info_t const * epoch_info ) {
      23           0 :   ag_validator_info_t const * validators = ag_epoch_info_validators( epoch_info );
      24           0 :   ulong                       stake      = 0UL;
      25           0 :   for( ulong i=0UL; i<epoch_info->validator_cnt; i++ ) if( FD_LIKELY( is_signer( self, validators[i].id ) ) ) stake += validators[i].stake;
      26           0 :   return fd_int_if( self->kind==AG_CERT_KIND_FAST_FINAL,
      27           0 :                     ag_epoch_info_is_strong_quorum( epoch_info, stake ),
      28           0 :                     ag_epoch_info_is_quorum       ( epoch_info, stake ) );
      29           0 : }
      30             : 
      31             : /* TODO sum all the pubkeys on advance_epoch and subtract instead? */
      32             : 
      33             : static int
      34             : pub_sum( blst_p1 *               pub,
      35             :          fd_bls_set_t const *    set,
      36           0 :          ag_epoch_info_t const * epoch_info ) {
      37           0 :   memset( pub, 0, sizeof(blst_p1) ); /* zero is the point at infinity */
      38           0 :   for( ulong rank=fd_bls_set_const_iter_init( set ); !fd_bls_set_const_iter_done( rank ); rank=fd_bls_set_const_iter_next( set, rank ) ) {
      39           0 :     if( FD_UNLIKELY( rank>=epoch_info->validator_cnt ) ) return -1;
      40           0 :     blst_p1_add_or_double_affine( pub, pub, epoch_info->pubkeys+rank );
      41           0 :   }
      42           0 :   return 0;
      43           0 : }
      44             : 
      45             : static int
      46             : pair_verify( blst_p1 const * pub,
      47             :              uchar const *   msg,
      48             :              ulong           msg_sz,
      49             :              blst_p1 const * pub_fb,
      50             :              uchar const *   msg_fb,
      51             :              ulong           msg_fb_sz,
      52           0 :              blst_p2 const * sig ) {
      53           0 :   if( FD_UNLIKELY( blst_p1_is_inf( pub ) || blst_p1_is_inf( pub_fb ) || blst_p2_is_inf( sig ) ) ) return 0; /* the miller loop is wrong on an infinity operand */
      54             : 
      55           0 :   blst_p1_affine a[3];
      56           0 :   blst_p2_affine b[3];
      57           0 :   blst_p2        h[1];
      58           0 :   blst_p1_to_affine( a, pub );
      59           0 :   blst_hash_to_g2( h, msg, msg_sz, (uchar const *)FD_BLS_DST, FD_BLS_DST_SZ, NULL, 0UL );
      60           0 :   blst_p2_to_affine( b, h );
      61           0 :   blst_p1_to_affine( a+1, pub_fb );
      62           0 :   blst_hash_to_g2( h, msg_fb, msg_fb_sz, (uchar const *)FD_BLS_DST, FD_BLS_DST_SZ, NULL, 0UL );
      63           0 :   blst_p2_to_affine( b+1, h );
      64           0 :   a[2] = BLS12_381_NEG_G1;
      65           0 :   blst_p2_to_affine( b+2, sig );
      66             : 
      67           0 :   blst_p1_affine const * aptr[3] = { a, a+1, a+2 };
      68           0 :   blst_p2_affine const * bptr[3] = { b, b+1, b+2 };
      69           0 :   blst_fp12 r[1];
      70           0 :   blst_miller_loop_n( r, bptr, aptr, 3UL );
      71           0 :   return !!blst_fp12_finalverify( r, blst_fp12_one() );
      72           0 : }
      73             : 
      74             : static int
      75             : check_sig_one( fd_bls_agg_t const *    agg,
      76             :                uint                    kind,
      77             :                ulong                   slot,
      78             :                uchar const *           block_hash,
      79             :                ag_epoch_info_t const * epoch_info,
      80           0 :                ushort                  shred_version ) {
      81           0 :   blst_p1 pub[1];
      82           0 :   if( FD_UNLIKELY( pub_sum( pub, agg->set, epoch_info ) ) ) return 0;
      83           0 :   uchar buf[ AG_VOTE_SIGNING_SER_MAX ];
      84           0 :   ulong sz = ag_vote_signing_ser( kind, slot, block_hash, shred_version, buf );
      85           0 :   if( FD_UNLIKELY( !blst_p2_in_g2( &agg->sig ) ) ) return 0;
      86           0 :   return fd_bls_agg_verify( buf, sz, pub, &agg->sig );
      87           0 : }
      88             : 
      89             : static int
      90             : check_sig_pair( fd_bls_agg_t const *    agg,
      91             :                 uint                    kind,
      92             :                 fd_bls_agg_t const *    agg_fb,
      93             :                 uint                    kind_fb,
      94             :                 ulong                   slot,
      95             :                 uchar const *           block_hash,
      96             :                 ag_epoch_info_t const * epoch_info,
      97           0 :                 ushort                  shred_version ) {
      98           0 :   blst_p1 pub[1], pub_fb[1];
      99           0 :   if( FD_UNLIKELY( pub_sum( pub, agg->set, epoch_info ) || pub_sum( pub_fb, agg_fb->set, epoch_info ) ) ) return 0;
     100           0 :   blst_p2 sig[1];
     101           0 :   blst_p2_add_or_double( sig, &agg->sig, &agg_fb->sig );
     102           0 :   if( FD_UNLIKELY( !blst_p2_in_g2( sig ) ) ) return 0;
     103           0 :   uchar buf   [ AG_VOTE_SIGNING_SER_MAX ]; ulong sz    = ag_vote_signing_ser( kind,    slot, block_hash, shred_version, buf    );
     104           0 :   uchar buf_fb[ AG_VOTE_SIGNING_SER_MAX ]; ulong sz_fb = ag_vote_signing_ser( kind_fb, slot, block_hash, shred_version, buf_fb );
     105           0 :   if( FD_LIKELY  ( fd_bls_set_is_null( agg_fb->set ) ) ) return fd_bls_agg_verify( buf,    sz,    pub,    sig ); /* one partition is the common case */
     106           0 :   if( FD_UNLIKELY( fd_bls_set_is_null( agg->set    ) ) ) return fd_bls_agg_verify( buf_fb, sz_fb, pub_fb, sig );
     107           0 :   return pair_verify( pub, buf, sz, pub_fb, buf_fb, sz_fb, sig );
     108           0 : }
     109             : 
     110             : static int
     111             : check_sig( ag_cert_t const *       self,
     112           0 :            ag_epoch_info_t const * epoch_info ) {
     113           0 :   switch( self->kind ) {
     114           0 :   case AG_CERT_KIND_FINAL:      return check_sig_one( &self->final.agg,      AG_VOTE_KIND_FINAL, self->final.slot,      NULL,                        epoch_info, self->final.shred_version      );
     115           0 :   case AG_CERT_KIND_FAST_FINAL: return check_sig_one( &self->fast_final.agg, AG_VOTE_KIND_NOTAR, self->fast_final.slot, self->fast_final.block_hash, epoch_info, self->fast_final.shred_version );
     116           0 :   case AG_CERT_KIND_NOTAR:      return check_sig_one( &self->notar.agg,      AG_VOTE_KIND_NOTAR, self->notar.slot,      self->notar.block_hash,      epoch_info, self->notar.shred_version      );
     117           0 :   case AG_CERT_KIND_NOTAR_FALLBACK: {
     118           0 :     ag_cert_notar_fallback_t const * nf = &self->notar_fallback;
     119           0 :     return check_sig_pair( &nf->agg_notar, AG_VOTE_KIND_NOTAR, &nf->agg_notar_fallback, AG_VOTE_KIND_NOTAR_FALLBACK, nf->slot, nf->block_hash, epoch_info, nf->shred_version );
     120           0 :   }
     121           0 :   case AG_CERT_KIND_SKIP: {
     122           0 :     ag_cert_skip_t const * skip = &self->skip;
     123           0 :     return check_sig_pair( &skip->agg_skip, AG_VOTE_KIND_SKIP, &skip->agg_skip_fallback, AG_VOTE_KIND_SKIP_FALLBACK, skip->slot, NULL, epoch_info, skip->shred_version );
     124           0 :   }
     125           0 :   default:
     126           0 :     FD_LOG_CRIT(( "unreachable" ));
     127           0 :   }
     128           0 : }
     129             : 
     130             : int
     131             : ag_cert_verify( ag_cert_t const *       self,
     132           0 :                 ag_epoch_info_t const * epoch_info ) {
     133           0 :   if( FD_UNLIKELY( !check_threshold( self, epoch_info ) ) ) return AG_CERT_VERIFY_ERR_THRESHOLD;
     134           0 :   if( FD_UNLIKELY( !check_sig      ( self, epoch_info ) ) ) return AG_CERT_VERIFY_ERR_SIG;
     135           0 :   return AG_CERT_VERIFY_SUCCESS;
     136           0 : }

Generated by: LCOV version 1.14