Line data Source code
1 : #include "fd_bank_hash_cmp.h"
2 : #include <unistd.h>
3 :
4 : void *
5 0 : fd_bank_hash_cmp_new( void * mem ) {
6 :
7 0 : if( FD_UNLIKELY( !mem ) ) {
8 0 : FD_LOG_WARNING( ( "NULL mem" ) );
9 0 : return NULL;
10 0 : }
11 :
12 0 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)mem, fd_bank_hash_cmp_align() ) ) ) {
13 0 : FD_LOG_WARNING( ( "misaligned mem" ) );
14 0 : return NULL;
15 0 : }
16 :
17 0 : ulong footprint = fd_bank_hash_cmp_footprint();
18 :
19 0 : fd_memset( mem, 0, footprint );
20 :
21 0 : ulong laddr = (ulong)mem;
22 0 : laddr += sizeof( fd_bank_hash_cmp_t );
23 :
24 0 : laddr = fd_ulong_align_up( laddr, fd_bank_hash_cmp_map_align() );
25 0 : fd_bank_hash_cmp_map_new( (void *)laddr );
26 0 : fd_bank_hash_cmp_set_map_offset( (fd_bank_hash_cmp_t *)mem, (uchar *)laddr );
27 0 : laddr += fd_bank_hash_cmp_map_footprint();
28 :
29 0 : laddr = fd_ulong_align_up( laddr, fd_bank_hash_cmp_align() );
30 0 : FD_TEST( laddr == (ulong)mem + fd_bank_hash_cmp_footprint() );
31 :
32 0 : return mem;
33 0 : }
34 :
35 : fd_bank_hash_cmp_t *
36 0 : fd_bank_hash_cmp_join( void * bank_hash_cmp ) {
37 0 : if( FD_UNLIKELY( !bank_hash_cmp ) ) {
38 0 : FD_LOG_WARNING(( "NULL bank_hash_cmp" ));
39 0 : return NULL;
40 0 : }
41 :
42 0 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)bank_hash_cmp, fd_bank_hash_cmp_align() ) ) ) {
43 0 : FD_LOG_WARNING(( "misaligned bank_hash_cmp" ));
44 0 : return NULL;
45 0 : }
46 :
47 0 : ulong laddr = (ulong)bank_hash_cmp;
48 0 : laddr += sizeof( fd_bank_hash_cmp_t );
49 :
50 0 : fd_bank_hash_cmp_entry_t * map_entry = fd_bank_hash_cmp_map_join( (void *)laddr );
51 0 : if( FD_UNLIKELY( !map_entry ) ) {
52 0 : FD_LOG_WARNING(( "invalid entry map" ));
53 0 : return NULL;
54 0 : }
55 :
56 0 : return bank_hash_cmp;
57 0 : }
58 :
59 : void *
60 0 : fd_bank_hash_cmp_leave( fd_bank_hash_cmp_t const * bank_hash_cmp ) {
61 :
62 0 : if( FD_UNLIKELY( !bank_hash_cmp ) ) {
63 0 : FD_LOG_WARNING( ( "NULL bank_hash_cmp" ) );
64 0 : return NULL;
65 0 : }
66 :
67 0 : return (void *)bank_hash_cmp;
68 0 : }
69 :
70 : void *
71 0 : fd_bank_hash_cmp_delete( void * bank_hash_cmp ) {
72 :
73 0 : if( FD_UNLIKELY( !bank_hash_cmp ) ) {
74 0 : FD_LOG_WARNING( ( "NULL bank_hash_cmp" ) );
75 0 : return NULL;
76 0 : }
77 :
78 0 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)bank_hash_cmp, fd_bank_hash_cmp_align() ) ) ) {
79 0 : FD_LOG_WARNING( ( "misaligned bank_hash_cmp" ) );
80 0 : return NULL;
81 0 : }
82 :
83 0 : return bank_hash_cmp;
84 0 : }
85 :
86 : void
87 0 : fd_bank_hash_cmp_lock( fd_bank_hash_cmp_t * bank_hash_cmp ) {
88 0 : volatile int * lock = &bank_hash_cmp->lock;
89 0 : # if FD_HAS_THREADS
90 0 : for( ;; ) {
91 0 : if( FD_LIKELY( !FD_ATOMIC_CAS( lock, 0UL, 1UL ) ) ) break;
92 0 : FD_SPIN_PAUSE();
93 0 : }
94 : # else
95 : *lock = 1;
96 : # endif
97 0 : FD_COMPILER_MFENCE();
98 0 : }
99 :
100 : void
101 0 : fd_bank_hash_cmp_unlock( fd_bank_hash_cmp_t * bank_hash_cmp ) {
102 0 : volatile int * lock = &bank_hash_cmp->lock;
103 0 : FD_COMPILER_MFENCE();
104 0 : FD_VOLATILE( *lock ) = 0UL;
105 0 : }
106 :
107 : void
108 : fd_bank_hash_cmp_insert( fd_bank_hash_cmp_t * bank_hash_cmp,
109 : ulong slot,
110 : fd_hash_t const * hash,
111 : int ours,
112 0 : ulong stake ) {
113 0 : fd_bank_hash_cmp_entry_t * map = fd_bank_hash_cmp_get_map( bank_hash_cmp );
114 :
115 0 : if( FD_UNLIKELY( slot <= bank_hash_cmp->watermark ) ) { return; }
116 0 : fd_bank_hash_cmp_entry_t * cmp = fd_bank_hash_cmp_map_query( map, slot, NULL );
117 :
118 0 : if( !cmp ) {
119 :
120 : /* If full, make room for new bank hashes */
121 :
122 0 : if( FD_UNLIKELY( bank_hash_cmp->cnt == fd_bank_hash_cmp_map_key_max() ) ) {
123 0 : FD_LOG_WARNING( ( "Bank matches unexpectedly full. Clearing. " ) );
124 0 : for( ulong i = 0; i < fd_bank_hash_cmp_map_slot_cnt(); i++ ) {
125 0 : fd_bank_hash_cmp_entry_t * entry = &map[i];
126 0 : if( FD_LIKELY( !fd_bank_hash_cmp_map_key_inval( entry->slot ) &&
127 0 : entry->slot < bank_hash_cmp->watermark ) ) {
128 0 : fd_bank_hash_cmp_map_remove( map, entry );
129 0 : bank_hash_cmp->cnt--;
130 0 : }
131 0 : }
132 0 : }
133 :
134 0 : cmp = fd_bank_hash_cmp_map_insert( map, slot );
135 0 : cmp->cnt = 0;
136 0 : bank_hash_cmp->cnt++;
137 0 : }
138 :
139 0 : if( FD_UNLIKELY( ours ) ) {
140 0 : cmp->ours = *hash;
141 0 : return;
142 0 : }
143 :
144 0 : for( ulong i = 0; i < cmp->cnt; i++ ) {
145 0 : if( FD_LIKELY( 0 == memcmp( &cmp->theirs[i], hash, sizeof( fd_hash_t ) ) ) ) {
146 0 : cmp->stakes[i] += stake;
147 0 : return;
148 0 : }
149 0 : }
150 :
151 0 : ulong max = sizeof( cmp->stakes ) / sizeof( ulong );
152 0 : if( FD_UNLIKELY( cmp->cnt == max ) ) {
153 0 : if( !cmp->overflow ) {
154 0 : FD_LOG_WARNING(( "[Bank Hash Comparison] more than %lu equivocating hashes for slot %lu. "
155 0 : "new hash: %s. ignoring.",
156 0 : max,
157 0 : slot,
158 0 : FD_BASE58_ENC_32_ALLOCA( hash ) ));
159 0 : cmp->overflow = 1;
160 0 : }
161 0 : return;
162 0 : }
163 0 : cmp->cnt++;
164 0 : cmp->theirs[cmp->cnt - 1] = *hash;
165 0 : cmp->stakes[cmp->cnt - 1] = stake;
166 0 : if( FD_UNLIKELY( cmp->cnt > 1 ) ) {
167 0 : for( ulong i = 0; i < cmp->cnt; i++ ) {
168 0 : FD_LOG_WARNING(( "slot: %lu. equivocating hash (#%lu): %s. stake: %lu",
169 0 : cmp->slot,
170 0 : i,
171 0 : FD_BASE58_ENC_32_ALLOCA( cmp->theirs[i].hash ),
172 0 : cmp->stakes[i] ));
173 0 : }
174 0 : }
175 0 : }
176 :
177 : int
178 0 : fd_bank_hash_cmp_check( fd_bank_hash_cmp_t * bank_hash_cmp, ulong slot ) {
179 0 : fd_bank_hash_cmp_entry_t * map = fd_bank_hash_cmp_get_map( bank_hash_cmp );
180 :
181 0 : fd_bank_hash_cmp_entry_t * cmp = fd_bank_hash_cmp_map_query( map, slot, NULL );
182 :
183 0 : if( FD_UNLIKELY( !cmp ) ) return 0;
184 :
185 0 : fd_hash_t null_hash = { 0 };
186 0 : if( FD_LIKELY( 0 == memcmp( &cmp->ours, &null_hash, sizeof( fd_hash_t ) ) ) ) return 0;
187 :
188 0 : if( FD_UNLIKELY( cmp->cnt == 0 ) ) return 0;
189 :
190 0 : fd_hash_t * theirs = &cmp->theirs[0];
191 0 : ulong stake = cmp->stakes[0];
192 0 : for( ulong i = 1; i < cmp->cnt; i++ ) {
193 0 : if( FD_UNLIKELY( cmp->stakes[i] > stake ) ) {
194 0 : theirs = &cmp->theirs[i];
195 0 : stake = cmp->stakes[i];
196 0 : }
197 0 : }
198 :
199 0 : double pct = (double)stake / (double)bank_hash_cmp->total_stake;
200 0 : if( FD_LIKELY( pct > 0.52 ) ) {
201 0 : if( FD_UNLIKELY( 0 != memcmp( &cmp->ours, theirs, sizeof( fd_hash_t ) ) ) ) {
202 0 : FD_LOG_WARNING(( "\n\n[Bank Hash Comparison]\n"
203 0 : "slot: %lu\n"
204 0 : "ours: %s\n"
205 0 : "theirs: %s\n"
206 0 : "stake: %.0lf%%\n"
207 0 : "result: mismatch!\n",
208 0 : cmp->slot,
209 0 : FD_BASE58_ENC_32_ALLOCA( cmp->ours.hash ),
210 0 : FD_BASE58_ENC_32_ALLOCA( theirs->hash ),
211 0 : pct * 100 ));
212 0 : if( FD_UNLIKELY( cmp->cnt > 1 ) ) {
213 0 : for( ulong i = 0; i < cmp->cnt; i++ ) {
214 0 : FD_LOG_WARNING(( "slot: %lu. hash (#%lu): %s. stake: %lu",
215 0 : cmp->slot,
216 0 : i,
217 0 : FD_BASE58_ENC_32_ALLOCA( cmp->theirs[i].hash ),
218 0 : cmp->stakes[i] ));
219 0 : }
220 0 : }
221 0 : return -1;
222 0 : } else {
223 0 : FD_LOG_INFO(( "\n\n[Bank Hash Comparison]\n"
224 0 : "slot: %lu\n"
225 0 : "ours: %s\n"
226 0 : "theirs: %s\n"
227 0 : "stake: %.0lf%%\n"
228 0 : "result: match!\n",
229 0 : cmp->slot,
230 0 : FD_BASE58_ENC_32_ALLOCA( cmp->ours.hash ),
231 0 : FD_BASE58_ENC_32_ALLOCA( theirs->hash ),
232 0 : pct * 100 ));
233 0 : }
234 0 : fd_bank_hash_cmp_map_remove( map, cmp );
235 0 : bank_hash_cmp->cnt--;
236 0 : return 1;
237 0 : }
238 0 : return 0;
239 0 : }
|