LCOV - code coverage report
Current view: top level - choreo/votor - ag_cert.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 95 105 90.5 %
Date: 2026-09-17 04:28:31 Functions: 9 9 100.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        4284 :            ulong             rank ) {
       8        4284 :   switch( self->kind ) {
       9         531 :   case AG_CERT_KIND_FINAL:          return fd_bls_set_test( self->final.agg.set,               rank );
      10        1455 :   case AG_CERT_KIND_FAST_FINAL:     return fd_bls_set_test( self->fast_final.agg.set,          rank );
      11         795 :   case AG_CERT_KIND_NOTAR:          return fd_bls_set_test( self->notar.agg.set,               rank );
      12         801 :   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         702 :   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        4284 :   }
      16        4284 : }
      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         273 :                  ag_epoch_info_t const * epoch_info ) {
      23         273 :   ag_validator_info_t const * validators = ag_epoch_info_validators( epoch_info );
      24         273 :   ulong                       stake      = 0UL;
      25        4557 :   for( ulong i=0UL; i<epoch_info->validator_cnt; i++ ) if( FD_LIKELY( is_signer( self, validators[i].id ) ) ) stake += validators[i].stake;
      26         273 :   return fd_int_if( self->kind==AG_CERT_KIND_FAST_FINAL,
      27         273 :                     ag_epoch_info_is_strong_quorum( epoch_info, stake ),
      28         273 :                     ag_epoch_info_is_quorum       ( epoch_info, stake ) );
      29         273 : }
      30             : 
      31             : /* TODO sum all the pubkeys on advance_epoch and subtract instead? */
      32             : 
      33             : static int
      34             : pub_sum( fd_bls_pub_t *          pub,
      35             :          fd_bls_set_t const *    set,
      36         339 :          ag_epoch_info_t const * epoch_info ) {
      37         339 :   memset( pub, 0, sizeof(fd_bls_pub_t) ); /* zero is the point at infinity */
      38        4059 :   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        3723 :     if( FD_UNLIKELY( rank>=epoch_info->validator_cnt ) ) return -1;
      40        3720 :     blst_p1_add_or_double( pub, pub, epoch_info->pubkeys+rank );
      41        3720 :   }
      42         336 :   return 0;
      43         339 : }
      44             : 
      45             : static int
      46             : pair_verify( fd_bls_pub_t const * pub,
      47             :              uchar const *        msg,
      48             :              ulong                msg_sz,
      49             :              fd_bls_pub_t const * pub_fb,
      50             :              uchar const *        msg_fb,
      51             :              ulong                msg_fb_sz,
      52          54 :              fd_bls_sig_t const * sig ) {
      53          54 :   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          54 :   blst_p1_affine a[3];
      56          54 :   blst_p2_affine b[3];
      57          54 :   blst_p2        h[1];
      58          54 :   blst_p1_to_affine( a, pub );
      59          54 :   blst_hash_to_g2( h, msg, msg_sz, (uchar const *)FD_BLS_DST, FD_BLS_DST_SZ, NULL, 0UL );
      60          54 :   blst_p2_to_affine( b, h );
      61          54 :   blst_p1_to_affine( a+1, pub_fb );
      62          54 :   blst_hash_to_g2( h, msg_fb, msg_fb_sz, (uchar const *)FD_BLS_DST, FD_BLS_DST_SZ, NULL, 0UL );
      63          54 :   blst_p2_to_affine( b+1, h );
      64          54 :   a[2] = BLS12_381_NEG_G1;
      65          54 :   blst_p2_to_affine( b+2, sig );
      66             : 
      67          54 :   blst_p1_affine const * aptr[3] = { a, a+1, a+2 };
      68          54 :   blst_p2_affine const * bptr[3] = { b, b+1, b+2 };
      69          54 :   blst_fp12 r[1];
      70          54 :   blst_miller_loop_n( r, bptr, aptr, 3UL );
      71          54 :   return !!blst_fp12_finalverify( r, blst_fp12_one() );
      72          54 : }
      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         171 :                ushort                  shred_version ) {
      81         171 :   fd_bls_pub_t pub[1];
      82         171 :   if( FD_UNLIKELY( pub_sum( pub, agg->set, epoch_info ) ) ) return 0;
      83         168 :   uchar buf[ AG_VOTE_SIGNING_SER_MAX ];
      84         168 :   ulong sz = ag_vote_signing_ser( kind, slot, block_hash, shred_version, buf );
      85         168 :   return fd_bls_agg_verify( buf, sz, pub, &agg->sig );
      86         171 : }
      87             : 
      88             : static int
      89             : check_sig_pair( fd_bls_agg_t const *    agg,
      90             :                 uint                    kind,
      91             :                 fd_bls_agg_t const *    agg_fb,
      92             :                 uint                    kind_fb,
      93             :                 ulong                   slot,
      94             :                 uchar const *           block_hash,
      95             :                 ag_epoch_info_t const * epoch_info,
      96          84 :                 ushort                  shred_version ) {
      97          84 :   fd_bls_pub_t pub[1], pub_fb[1];
      98          84 :   if( FD_UNLIKELY( pub_sum( pub, agg->set, epoch_info ) || pub_sum( pub_fb, agg_fb->set, epoch_info ) ) ) return 0;
      99          84 :   fd_bls_sig_t sig[1];
     100          84 :   blst_p2_add_or_double( sig, &agg->sig, &agg_fb->sig );
     101          84 :   uchar buf   [ AG_VOTE_SIGNING_SER_MAX ]; ulong sz    = ag_vote_signing_ser( kind,    slot, block_hash, shred_version, buf    );
     102          84 :   uchar buf_fb[ AG_VOTE_SIGNING_SER_MAX ]; ulong sz_fb = ag_vote_signing_ser( kind_fb, slot, block_hash, shred_version, buf_fb );
     103          84 :   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 */
     104          63 :   if( FD_UNLIKELY( fd_bls_set_is_null( agg->set    ) ) ) return fd_bls_agg_verify( buf_fb, sz_fb, pub_fb, sig );
     105          54 :   return pair_verify( pub, buf, sz, pub_fb, buf_fb, sz_fb, sig );
     106          63 : }
     107             : 
     108             : static int
     109             : check_sig( ag_cert_t const *       self,
     110         255 :            ag_epoch_info_t const * epoch_info ) {
     111         255 :   switch( self->kind ) {
     112          21 :   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      );
     113         105 :   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 );
     114          45 :   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      );
     115          45 :   case AG_CERT_KIND_NOTAR_FALLBACK: {
     116          45 :     ag_cert_notar_fallback_t const * nf = &self->notar_fallback;
     117          45 :     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 );
     118           0 :   }
     119          39 :   case AG_CERT_KIND_SKIP: {
     120          39 :     ag_cert_skip_t const * skip = &self->skip;
     121          39 :     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 );
     122           0 :   }
     123           0 :   default:
     124           0 :     FD_LOG_CRIT(( "unreachable" ));
     125         255 :   }
     126         255 : }
     127             : 
     128             : int
     129             : ag_cert_verify( ag_cert_t const *       self,
     130         273 :                 ag_epoch_info_t const * epoch_info ) {
     131         273 :   return check_threshold( self, epoch_info ) && check_sig( self, epoch_info );
     132         273 : }
     133             : 
     134             : char *
     135             : ag_cert_to_cstr( ag_cert_t const * self,
     136           3 :                  char              cstr[ static AG_CERT_CSTR_MAX ] ) {
     137           3 :   static char const * kind_cstr[] = { "Final", "FastFinal", "Notar", "NotarFallback", "Skip" };
     138           3 :   fd_bls_agg_t const * aggs[2] = { NULL, NULL };
     139           3 :   ulong                stake;
     140           3 :   switch( self->kind ) {
     141           0 :   case AG_CERT_KIND_FINAL:          aggs[0] = &self->final.agg;                                                                  stake = self->final.stake;          break;
     142           0 :   case AG_CERT_KIND_FAST_FINAL:     aggs[0] = &self->fast_final.agg;                                                             stake = self->fast_final.stake;     break;
     143           0 :   case AG_CERT_KIND_NOTAR:          aggs[0] = &self->notar.agg;                                                                  stake = self->notar.stake;          break;
     144           3 :   case AG_CERT_KIND_NOTAR_FALLBACK: aggs[0] = &self->notar_fallback.agg_notar; aggs[1] = &self->notar_fallback.agg_notar_fallback; stake = self->notar_fallback.stake; break;
     145           0 :   case AG_CERT_KIND_SKIP:           aggs[0] = &self->skip.agg_skip;            aggs[1] = &self->skip.agg_skip_fallback;            stake = self->skip.stake;           break;
     146           0 :   default:                          FD_LOG_CRIT(( "unreachable" ));
     147           3 :   }
     148           3 :   uchar const * block_hash = ag_cert_block_hash( self );
     149           3 :   char *        p          = cstr;
     150           3 :   p = fd_cstr_append_printf( p, "%s { slot: %lu", kind_cstr[ self->kind ], ag_cert_slot( self ) );
     151           3 :   if( FD_LIKELY( block_hash ) ) p = fd_cstr_append_printf( p, ", hash: %02x%02x%02x...", block_hash[0], block_hash[1], block_hash[2] );
     152           3 :   p = fd_cstr_append_printf( p, ", sig: %lu", fd_bls_set_cnt( aggs[0]->set ) );
     153           3 :   if( FD_LIKELY( aggs[1] ) ) p = fd_cstr_append_printf( p, ", sig_fallback: %lu", fd_bls_set_cnt( aggs[1]->set ) );
     154           3 :   p = fd_cstr_append_printf( p, ", stake: %lu }", stake );
     155           3 :   *p = '\0';
     156           3 :   return cstr;
     157           3 : }

Generated by: LCOV version 1.14