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 : }
|