Line data Source code
1 : #include "fd_notar.h"
2 : #include "../../util/bits/fd_bits.h"
3 :
4 : void *
5 : fd_notar_new( void * shmem,
6 3 : ulong slot_max ) {
7 :
8 3 : if( FD_UNLIKELY( !shmem ) ) {
9 0 : FD_LOG_WARNING(( "NULL mem" ));
10 0 : return NULL;
11 0 : }
12 :
13 3 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shmem, fd_notar_align() ) ) ) {
14 0 : FD_LOG_WARNING(( "misaligned mem" ));
15 0 : return NULL;
16 0 : }
17 :
18 3 : ulong footprint = fd_notar_footprint( slot_max );
19 3 : if( FD_UNLIKELY( !footprint ) ) {
20 0 : FD_LOG_WARNING(( "bad slot_max (%lu)", slot_max ));
21 0 : return NULL;
22 0 : }
23 :
24 3 : fd_memset( shmem, 0, footprint );
25 :
26 3 : int lg_slot_max = fd_ulong_find_msb( fd_ulong_pow2_up( slot_max ) ) + 1;
27 3 : int lg_blk_max = fd_ulong_find_msb( fd_ulong_pow2_up( slot_max * FD_VOTER_MAX ) ) + 1;
28 3 : int lg_vtr_max = fd_ulong_find_msb( fd_ulong_pow2_up( FD_VOTER_MAX ) ) + 1;
29 :
30 3 : FD_SCRATCH_ALLOC_INIT( l, shmem );
31 3 : fd_notar_t * notar = FD_SCRATCH_ALLOC_APPEND( l, fd_notar_align(), sizeof(fd_notar_t) );
32 3 : void * slot_map = FD_SCRATCH_ALLOC_APPEND( l, fd_notar_slot_align(), fd_notar_slot_footprint( lg_slot_max ) );
33 3 : void * blk_map = FD_SCRATCH_ALLOC_APPEND( l, fd_notar_blk_align(), fd_notar_blk_footprint( lg_blk_max ) );
34 3 : void * vtr_map = FD_SCRATCH_ALLOC_APPEND( l, fd_notar_vtr_align(), fd_notar_vtr_footprint( lg_vtr_max ) );
35 3 : FD_TEST( FD_SCRATCH_ALLOC_FINI( l, fd_notar_align() ) == (ulong)shmem + footprint );
36 :
37 3 : notar->slot_max = slot_max;
38 3 : notar->slot_map = fd_notar_slot_new( slot_map, lg_slot_max, 0UL );
39 3 : notar->blk_map = fd_notar_blk_new( blk_map, lg_blk_max, 0UL );
40 3 : notar->vtr_map = fd_notar_vtr_new( vtr_map, lg_vtr_max, 0UL );
41 :
42 3 : notar->epoch = ULONG_MAX;
43 3 : notar->lo_wmark = ULONG_MAX;
44 3 : notar->hi_wmark = ULONG_MAX;
45 :
46 3 : return shmem;
47 3 : }
48 :
49 : fd_notar_t *
50 3 : fd_notar_join( void * shnotar ) {
51 3 : fd_notar_t * notar = (fd_notar_t *)shnotar;
52 :
53 3 : if( FD_UNLIKELY( !notar ) ) {
54 0 : FD_LOG_WARNING(( "NULL notar" ));
55 0 : return NULL;
56 0 : }
57 :
58 3 : if( FD_UNLIKELY( !fd_ulong_is_aligned((ulong)notar, fd_notar_align() ) ) ) {
59 0 : FD_LOG_WARNING(( "misaligned notar" ));
60 0 : return NULL;
61 0 : }
62 :
63 3 : notar->slot_map = fd_notar_slot_join( notar->slot_map );
64 3 : notar->blk_map = fd_notar_blk_join( notar->blk_map );
65 3 : notar->vtr_map = fd_notar_vtr_join( notar->vtr_map );
66 :
67 3 : return notar;
68 3 : }
69 :
70 : void *
71 3 : fd_notar_leave( fd_notar_t const * notar ) {
72 :
73 3 : if( FD_UNLIKELY( !notar ) ) {
74 0 : FD_LOG_WARNING(( "NULL notar" ));
75 0 : return NULL;
76 0 : }
77 :
78 3 : return (void *)notar;
79 3 : }
80 :
81 : void *
82 3 : fd_notar_delete( void * notar ) {
83 :
84 3 : if( FD_UNLIKELY( !notar ) ) {
85 0 : FD_LOG_WARNING(( "NULL notar" ));
86 0 : return NULL;
87 0 : }
88 :
89 3 : if( FD_UNLIKELY( !fd_ulong_is_aligned((ulong)notar, fd_notar_align() ) ) ) {
90 0 : FD_LOG_WARNING(( "misaligned notar" ));
91 0 : return NULL;
92 0 : }
93 :
94 3 : return notar;
95 3 : }
96 :
97 : fd_notar_blk_t *
98 : fd_notar_count_vote( fd_notar_t * notar,
99 : ulong total_stake,
100 : fd_pubkey_t const * addr,
101 : ulong vote_slot,
102 6 : fd_hash_t const * vote_block_id ) {
103 :
104 : /* Ignore if this vote slot isn't in range. */
105 :
106 6 : if( FD_UNLIKELY( vote_slot < notar->lo_wmark || vote_slot > notar->hi_wmark ) ) return NULL;
107 :
108 : /* Ignore if this vote account isn't in the voter set. */
109 :
110 6 : fd_notar_vtr_t const * vtr = fd_notar_vtr_query( notar->vtr_map, *addr, NULL );
111 6 : if( FD_UNLIKELY( !vtr ) ) return NULL;
112 :
113 : /* Check we haven't already counted the voter's stake for this slot.
114 : If a voter voted for multiple block ids for the same slot, we only
115 : count their first one. Honest voters never vote more than once for
116 : the same slot so the percentage of stake doing this should be small
117 : per honest majority assumption. */
118 :
119 6 : fd_notar_slot_t * notar_slot = fd_notar_slot_query( notar->slot_map, vote_slot, NULL );
120 6 : if( FD_UNLIKELY( !notar_slot ) ) {
121 6 : notar_slot = fd_notar_slot_insert( notar->slot_map, vote_slot );
122 6 : notar_slot->parent_slot = ULONG_MAX;
123 6 : notar_slot->prev_leader_slot = ULONG_MAX;
124 6 : notar_slot->stake = 0;
125 6 : notar_slot->is_leader = 0;
126 6 : notar_slot->is_propagated = 0;
127 6 : notar_slot->block_ids_cnt = 0;
128 6 : fd_notar_slot_vtrs_null( notar_slot->prev_vtrs );
129 6 : fd_notar_slot_vtrs_null( notar_slot->vtrs );
130 6 : }
131 6 : if( FD_UNLIKELY( vtr->bit==ULONG_MAX ) ) return NULL;
132 3 : if( FD_UNLIKELY( fd_notar_slot_vtrs_test( notar_slot->vtrs, vtr->bit ) ) ) return NULL;
133 3 : fd_notar_slot_vtrs_insert( notar_slot->vtrs, vtr->bit );
134 3 : notar_slot->stake += vtr->stake;
135 :
136 : /* Get the actual block with the block_id. */
137 :
138 3 : fd_notar_blk_t * notar_blk = fd_notar_blk_query( notar->blk_map, *vote_block_id, NULL );
139 3 : if( FD_UNLIKELY( !notar_blk ) ) {
140 3 : FD_TEST( notar_slot->block_ids_cnt < FD_VOTER_MAX ); /* at most one unique block id per voter in a slot */
141 3 : notar_blk = fd_notar_blk_insert( notar->blk_map, *vote_block_id );
142 3 : notar_blk->slot = vote_slot;
143 3 : notar_blk->stake = 0;
144 3 : notar_blk->dup_conf = 0;
145 3 : notar_blk->opt_conf = 0;
146 3 : notar_blk->dup_notif = 0;
147 3 : notar_blk->opt_notif = 0;
148 3 : notar_slot->block_ids[notar_slot->block_ids_cnt++] = *vote_block_id;
149 3 : }
150 3 : notar_blk->stake += vtr->stake;
151 3 : notar_blk->dup_conf = ((double)notar_blk->stake / (double)total_stake) > 0.52;
152 3 : notar_blk->opt_conf = ((double)notar_blk->stake / (double)total_stake) > (2.0/3.0);
153 3 : return notar_blk;
154 3 : }
155 :
156 : void
157 : fd_notar_advance_epoch( fd_notar_t * notar,
158 : fd_tower_accts_t * accts,
159 6 : ulong epoch ) {
160 6 : notar->epoch = epoch;
161 49152 : for( ulong i = 0; i < fd_notar_vtr_key_max( notar->vtr_map ); i++ ) {
162 49146 : fd_notar_vtr_t * vtr = ¬ar->vtr_map[i];
163 49146 : if( fd_notar_vtr_key_inval( vtr->vote_acc ) ) continue;
164 3 : vtr->prev_stake = vtr->stake;
165 3 : vtr->stake = 0;
166 3 : vtr->prev_bit = vtr->bit;
167 3 : vtr->bit = ULONG_MAX;
168 3 : }
169 :
170 6 : ulong vtr_bit = 0;
171 6 : for( fd_tower_accts_iter_t iter = fd_tower_accts_iter_init( accts );
172 9 : !fd_tower_accts_iter_done( accts, iter );
173 6 : iter = fd_tower_accts_iter_next( accts, iter ) ) {
174 3 : fd_tower_accts_t const * acct = fd_tower_accts_iter_ele( accts, iter );
175 3 : fd_notar_vtr_t * vtr = fd_notar_vtr_query( notar->vtr_map, acct->addr, NULL );
176 3 : if( FD_UNLIKELY( !vtr ) ) vtr = fd_notar_vtr_insert( notar->vtr_map, acct->addr );
177 3 : vtr->stake = acct->stake;
178 3 : vtr->bit = vtr_bit++;
179 3 : }
180 6 : }
181 :
182 : void
183 : fd_notar_advance_wmark( fd_notar_t * notar,
184 3 : ulong wmark ) {
185 3 : for(ulong slot = notar->lo_wmark; slot < wmark; slot++ ) {
186 0 : fd_notar_slot_t * notar_slot = fd_notar_slot_query( notar->slot_map, slot, NULL );
187 0 : if( FD_LIKELY( notar_slot ) ) {
188 0 : for( ulong i=0; i<notar_slot->block_ids_cnt; i++ ) {
189 0 : fd_hash_t const * block_id = ¬ar_slot->block_ids[i];
190 0 : fd_notar_blk_t * notar_blk = fd_notar_blk_query( notar->blk_map, *block_id, NULL );
191 0 : if( FD_UNLIKELY( !notar_blk ) ) { FD_BASE58_ENCODE_32_BYTES( block_id->uc, crit ); FD_LOG_CRIT(( "missing %lu %s %lu %lu", slot, crit, i, notar_slot->block_ids_cnt )); };
192 0 : fd_notar_blk_remove( notar->blk_map, notar_blk );
193 0 : }
194 0 : fd_notar_slot_remove( notar->slot_map, notar_slot );
195 0 : }
196 0 : }
197 3 : notar->lo_wmark = wmark;
198 3 : notar->hi_wmark = wmark + notar->slot_max;
199 3 : }
|