Line data Source code
1 : #include "fd_cert.h"
2 :
3 : #include "fd_vote_serde.h" /* fd_vote_signing_ser */
4 :
5 : static int
6 : is_signer( fd_cert_t const * self,
7 0 : ulong rank ) {
8 0 : switch( self->kind ) {
9 0 : case FD_CERT_KIND_FINAL: return fd_bls_set_test( self->final.agg.set, rank );
10 0 : case FD_CERT_KIND_FAST_FINAL: return fd_bls_set_test( self->fast_final.agg.set, rank );
11 0 : case FD_CERT_KIND_NOTAR: return fd_bls_set_test( self->notar.agg.set, rank );
12 0 : case FD_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 FD_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( fd_cert_t const * self,
22 0 : fd_epoch_info_t const * epoch_info ) {
23 0 : fd_validator_info_t const * validators = fd_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==FD_CERT_KIND_FAST_FINAL,
27 0 : fd_epoch_info_is_strong_quorum( epoch_info, stake ),
28 0 : fd_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( fd_bls_pub_t * pub,
35 : fd_bls_set_t const * set,
36 0 : fd_epoch_info_t const * epoch_info ) {
37 0 : memset( pub, 0, sizeof(fd_bls_pub_t) ); /* zero is the point at infinity */
38 0 : if( FD_UNLIKELY( fd_bls_set_cnt( set ) &&
39 0 : fd_bls_set_last( set )>=epoch_info->validator_cnt ) ) return -1;
40 0 : fd_bls_pub_sum( pub, epoch_info->pubkeys, set );
41 0 : return 0;
42 0 : }
43 :
44 : static int
45 : check_sig_one( fd_bls_agg_t const * agg,
46 : uint kind,
47 : ulong slot,
48 : fd_block_hash_t const * block_hash,
49 : fd_epoch_info_t const * epoch_info,
50 0 : ushort shred_version ) {
51 0 : fd_bls_pub_t pub[1];
52 0 : if( FD_UNLIKELY( pub_sum( pub, agg->set, epoch_info ) ) ) return 0;
53 0 : uchar buf[ FD_VOTE_SIGNING_SER_MAX ];
54 0 : ulong sz = fd_vote_signing_ser( kind, slot, block_hash, shred_version, buf );
55 0 : if( FD_UNLIKELY( !fd_bls_sig_validate( &agg->sig ) ) ) return 0; /* deferred g2 subgroup check */
56 0 : return fd_bls_agg_verify( buf, sz, pub, &agg->sig );
57 0 : }
58 :
59 : static int
60 : check_sig_pair( fd_bls_agg_t const * agg,
61 : uint kind,
62 : fd_bls_agg_t const * agg_fb,
63 : uint kind_fb,
64 : ulong slot,
65 : fd_block_hash_t const * block_hash,
66 : fd_epoch_info_t const * epoch_info,
67 0 : ushort shred_version ) {
68 0 : fd_bls_pub_t pub[1], pub_fb[1];
69 0 : if( FD_UNLIKELY( pub_sum( pub, agg->set, epoch_info ) || pub_sum( pub_fb, agg_fb->set, epoch_info ) ) ) return 0;
70 0 : fd_bls_sig_t sig[1];
71 0 : fd_bls_sig_add( sig, &agg->sig, &agg_fb->sig );
72 0 : if( FD_UNLIKELY( !fd_bls_sig_validate( sig ) ) ) return 0; /* deferred g2 subgroup check */
73 0 : uchar buf [ FD_VOTE_SIGNING_SER_MAX ]; ulong sz = fd_vote_signing_ser( kind, slot, block_hash, shred_version, buf );
74 0 : uchar buf_fb[ FD_VOTE_SIGNING_SER_MAX ]; ulong sz_fb = fd_vote_signing_ser( kind_fb, slot, block_hash, shred_version, buf_fb );
75 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 */
76 0 : if( FD_UNLIKELY( fd_bls_set_is_null( agg->set ) ) ) return fd_bls_agg_verify( buf_fb, sz_fb, pub_fb, sig );
77 0 : return fd_bls_agg_verify_pair( buf, sz, pub, buf_fb, sz_fb, pub_fb, sig );
78 0 : }
79 :
80 : static int
81 : check_sig( fd_cert_t const * self,
82 0 : fd_epoch_info_t const * epoch_info ) {
83 0 : switch( self->kind ) {
84 0 : case FD_CERT_KIND_FINAL: return check_sig_one( &self->final.agg, FD_VOTE_KIND_FINAL, self->final.slot, NULL, epoch_info, self->final.shred_version );
85 0 : case FD_CERT_KIND_FAST_FINAL: return check_sig_one( &self->fast_final.agg, FD_VOTE_KIND_NOTAR, self->fast_final.slot, &self->fast_final.block_hash, epoch_info, self->fast_final.shred_version );
86 0 : case FD_CERT_KIND_NOTAR: return check_sig_one( &self->notar.agg, FD_VOTE_KIND_NOTAR, self->notar.slot, &self->notar.block_hash, epoch_info, self->notar.shred_version );
87 0 : case FD_CERT_KIND_NOTAR_FALLBACK: {
88 0 : fd_cert_notar_fallback_t const * nf = &self->notar_fallback;
89 0 : return check_sig_pair( &nf->agg_notar, FD_VOTE_KIND_NOTAR, &nf->agg_notar_fallback, FD_VOTE_KIND_NOTAR_FALLBACK, nf->slot, &nf->block_hash, epoch_info, nf->shred_version );
90 0 : }
91 0 : case FD_CERT_KIND_SKIP: {
92 0 : fd_cert_skip_t const * skip = &self->skip;
93 0 : return check_sig_pair( &skip->agg_skip, FD_VOTE_KIND_SKIP, &skip->agg_skip_fallback, FD_VOTE_KIND_SKIP_FALLBACK, skip->slot, NULL, epoch_info, skip->shred_version );
94 0 : }
95 0 : default:
96 0 : FD_LOG_CRIT(( "unreachable" ));
97 0 : }
98 0 : }
99 :
100 : int
101 : fd_cert_verify( fd_cert_t const * self,
102 0 : fd_epoch_info_t const * epoch_info ) {
103 0 : if( FD_UNLIKELY( !check_threshold( self, epoch_info ) ) ) return FD_CERT_VERIFY_ERR_THRESHOLD;
104 0 : if( FD_UNLIKELY( !check_sig ( self, epoch_info ) ) ) return FD_CERT_VERIFY_ERR_SIG;
105 0 : return FD_CERT_VERIFY_SUCCESS;
106 0 : }
|