Line data Source code
1 : #include <sys/types.h>
2 : #include <unistd.h>
3 : #include "fd_restart.h"
4 : #include "../../util/fd_util.h"
5 : #include "../../flamenco/stakes/fd_stakes.h"
6 : #include "../../flamenco/runtime/sysvar/fd_sysvar_epoch_schedule.h"
7 :
8 : #pragma GCC diagnostic ignored "-Wformat"
9 : #pragma GCC diagnostic ignored "-Wformat-extra-args"
10 0 : #define BITS_PER_UCHAR ( 8*sizeof(uchar) )
11 0 : #define BITS_PER_ULONG ( 8*sizeof(ulong) )
12 :
13 : void *
14 0 : fd_restart_new( void * mem ) {
15 0 : if( FD_UNLIKELY( !mem ) ) {
16 0 : FD_LOG_WARNING(( "NULL mem" ));
17 0 : return NULL;
18 0 : }
19 :
20 0 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)mem, fd_restart_align() ) ) ) {
21 0 : FD_LOG_WARNING(( "misaligned mem" ));
22 0 : return NULL;
23 0 : }
24 :
25 0 : fd_memset( mem, 0, fd_restart_footprint() );
26 0 : return mem;
27 0 : }
28 :
29 : fd_restart_t *
30 0 : fd_restart_join( void * restart ) {
31 0 : if( FD_UNLIKELY( !restart ) ) {
32 0 : FD_LOG_WARNING(( "NULL mem" ));
33 0 : return NULL;
34 0 : }
35 :
36 0 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)restart, fd_restart_align() ) ) ) {
37 0 : FD_LOG_WARNING(( "misaligned mem" ));
38 0 : return NULL;
39 0 : }
40 :
41 0 : fd_restart_t * restart_ = (fd_restart_t *)restart;
42 0 : return restart_;
43 0 : }
44 :
45 : static int
46 0 : fd_restart_recv_enough_stake( fd_restart_t * restart ) {
47 0 : ulong received[RESTART_EPOCHS_MAX] = { restart->total_stake_received[0]*100/restart->total_stake[0],
48 0 : restart->total_stake_received[1]*100/restart->total_stake[1] };
49 0 : ulong voted[RESTART_EPOCHS_MAX] = { restart->total_stake_received_and_voted[0]*100/restart->total_stake[0],
50 0 : restart->total_stake_received_and_voted[1]*100/restart->total_stake[1] };
51 :
52 0 : for( ulong e=0; e<RESTART_EPOCHS_MAX; e++ ) {
53 0 : FD_LOG_NOTICE(( "Epoch%lu: %lu/%lu = %lu%c stake received\n",
54 0 : restart->root_epoch+e, restart->total_stake_received[e], restart->total_stake[e], received[e], '%' ));
55 0 : FD_LOG_NOTICE(( "Epoch%lu: %lu/%lu = %lu%c stake voted\n",
56 0 : restart->root_epoch+e, restart->total_stake_received_and_voted[e], restart->total_stake[e], voted[e], '%' ));
57 0 : }
58 :
59 0 : ulong min_active_stake = received[0];
60 0 : if( FD_UNLIKELY( voted[1]>=WAIT_FOR_NEXT_EPOCH_THRESHOLD_PERCENT ) ) {
61 0 : min_active_stake = fd_ulong_min( min_active_stake, received[1] );
62 0 : }
63 0 : return min_active_stake>=WAIT_FOR_SUPERMAJORITY_THRESHOLD_PERCENT;
64 0 : }
65 :
66 : static void
67 : fd_restart_recv_last_voted_fork_slots( fd_restart_t * restart,
68 : fd_gossip_restart_last_voted_fork_slots_t * msg,
69 0 : ulong * out_heaviest_fork_found ) {
70 0 : if( FD_UNLIKELY( restart->stage!=WR_STAGE_FIND_HEAVIEST_FORK_SLOT_NUM ) ) return;
71 :
72 : /* Check that funk is not too stale for aggregating this message */
73 0 : ulong voted_epoch = fd_slot_to_epoch( restart->epoch_schedule, msg->last_voted_slot, NULL );
74 0 : if( FD_UNLIKELY( voted_epoch!=restart->root_epoch && voted_epoch!=restart->root_epoch+1 ) ) {
75 0 : FD_LOG_WARNING(( "Ignore last_voted_fork_slots message from validator %s for epoch%lu (because root_epoch=%lu is stale)",
76 0 : FD_BASE58_ENC_32_ALLOCA( &msg->from ), voted_epoch, restart->root_epoch ));
77 0 : return;
78 0 : }
79 0 : if( FD_UNLIKELY( msg->last_voted_slot>=restart->funk_root+LAST_VOTED_FORK_MAX_SLOTS ) ) {
80 0 : FD_LOG_WARNING(( "Ignore last_voted_fork_slots message for slot=%lu (because funk_root=%lu is stale) from validator %s",
81 0 : msg->last_voted_slot, restart->funk_root, FD_BASE58_ENC_32_ALLOCA( &msg->from ) ));
82 0 : return;
83 0 : }
84 :
85 : /* Find the message sender from restart->stake_weights */
86 0 : fd_pubkey_t * pubkey = &msg->from;
87 0 : ulong stake_received[ RESTART_EPOCHS_MAX ] = {0UL, 0UL};
88 :
89 0 : for( ulong e=0; e<RESTART_EPOCHS_MAX; e++ ) {
90 0 : for( ulong i=0; i<restart->num_vote_accts[e]; i++ ) {
91 0 : if( FD_UNLIKELY( memcmp( pubkey->key, restart->stake_weights[e][i].key.key, sizeof(fd_pubkey_t) )==0 ) ) {
92 0 : if( FD_UNLIKELY( restart->last_voted_fork_slots_received[e][i] ) ) {
93 0 : FD_LOG_NOTICE(( "Duplicate last_voted_fork_slots message from validator %s", FD_BASE58_ENC_32_ALLOCA( pubkey ) ));
94 0 : return;
95 0 : }
96 0 : stake_received[e] = restart->stake_weights[e][i].stake;
97 0 : restart->last_voted_fork_slots_received[e][i] = 1;
98 0 : break;
99 0 : }
100 0 : }
101 0 : restart->total_stake_received[e] += stake_received[e];
102 0 : if( FD_LIKELY( restart->root_epoch+e<=voted_epoch ) ) {
103 0 : restart->total_stake_received_and_voted[e] += stake_received[e];
104 0 : }
105 0 : }
106 :
107 0 : if( FD_UNLIKELY( stake_received[0]==0 && stake_received[1]==0 ) ) {
108 0 : FD_LOG_WARNING(( "Get last_voted_fork_slots message from validator %s with 0 stake", FD_BASE58_ENC_32_ALLOCA( pubkey ) ));
109 0 : return;
110 0 : }
111 :
112 : /* Decode the bitmap in the message and aggregate validator stake into slot_to_stake */
113 : /* The gossip tile should have already converted the bitmap into raw format */
114 0 : if( FD_UNLIKELY( msg->last_voted_slot+1<msg->offsets.inner.raw_offsets.offsets.len ) ) {
115 0 : FD_LOG_WARNING(( "Received invalid last_voted_fork_slot message from validator %s because %lu<%lu",
116 0 : FD_BASE58_ENC_32_ALLOCA( pubkey ), msg->last_voted_slot+1, msg->offsets.inner.raw_offsets.offsets.len ));
117 0 : }
118 0 : for( ulong i=0; i<msg->offsets.inner.raw_offsets.offsets.len; i++ ) {
119 0 : if( FD_UNLIKELY( msg->last_voted_slot<restart->funk_root+i ) ) break;
120 :
121 0 : ulong slot = msg->last_voted_slot-i;
122 0 : ulong byte_off = i/BITS_PER_UCHAR;
123 0 : int bit_off = i%BITS_PER_UCHAR;
124 0 : int bit = fd_uchar_extract_bit( msg->offsets.inner.raw_offsets.offsets.bits.bits[byte_off], bit_off );
125 0 : if( FD_LIKELY( bit ) ) {
126 0 : ulong offset = slot-restart->funk_root;
127 0 : ulong slot_epoch = fd_slot_to_epoch( restart->epoch_schedule, slot, NULL );
128 0 : FD_TEST( slot_epoch==restart->root_epoch || slot_epoch==restart->root_epoch+1 );
129 0 : restart->slot_to_stake[offset] += stake_received[slot_epoch-restart->root_epoch];
130 0 : }
131 0 : }
132 :
133 0 : if( FD_UNLIKELY( fd_restart_recv_enough_stake( restart ) ) ) {
134 0 : ulong stake_threshold[ RESTART_EPOCHS_MAX ] = { restart->total_stake_received[0]
135 0 : - restart->total_stake[0]*HEAVIEST_FORK_THRESHOLD_DELTA_PERCENT/100UL,
136 0 : restart->total_stake_received[1]
137 0 : - restart->total_stake[1]*HEAVIEST_FORK_THRESHOLD_DELTA_PERCENT/100UL };
138 : /* The subtraction is safe because restart->total_stake_received[0/1] should be at least >(80-9)%==71% at this point */
139 :
140 0 : restart->heaviest_fork_slot = restart->funk_root;
141 0 : for( ulong offset=0; offset<LAST_VOTED_FORK_MAX_SLOTS; offset++ ) {
142 0 : ulong slot = restart->funk_root+offset;
143 0 : ulong slot_epoch = fd_slot_to_epoch( restart->epoch_schedule, slot, NULL );
144 0 : if( slot_epoch>restart->root_epoch+1 ) break;
145 0 : if( FD_LIKELY( restart->slot_to_stake[offset]>=stake_threshold[slot_epoch-restart->root_epoch] ) ) {
146 0 : restart->heaviest_fork_slot = restart->funk_root+offset;
147 0 : }
148 0 : }
149 0 : FD_LOG_NOTICE(( "[%s] Found heaviest fork slot=%lu", __func__, restart->heaviest_fork_slot ));
150 :
151 0 : *out_heaviest_fork_found = 1;
152 0 : restart->stage = WR_STAGE_FIND_HEAVIEST_FORK_BANK_HASH;
153 0 : }
154 0 : }
155 :
156 : static void
157 : fd_restart_recv_heaviest_fork( fd_restart_t * restart,
158 0 : fd_gossip_restart_heaviest_fork_t * msg ) {
159 0 : if( FD_LIKELY( memcmp( restart->coordinator_pubkey.key,
160 0 : msg->from.key, sizeof(fd_pubkey_t) )==0 ) ) {
161 0 : FD_LOG_WARNING(( "Received a restart_heaviest_fork message: slot=%lu, hash=%s",
162 0 : msg->last_slot, FD_BASE58_ENC_32_ALLOCA( &msg->last_slot_hash ) ));
163 0 : fd_memcpy( &restart->coordinator_heaviest_fork_bank_hash, &msg->last_slot_hash, sizeof(fd_hash_t) );
164 0 : restart->coordinator_heaviest_fork_slot = msg->last_slot;
165 0 : restart->coordinator_heaviest_fork_ready = 1;
166 0 : } else {
167 0 : FD_LOG_WARNING(( "Received and ignored a restart_heaviest_fork message from non-coordinator %s",
168 0 : FD_BASE58_ENC_32_ALLOCA( &msg->from ) ));
169 0 : }
170 0 : }
171 :
172 : void
173 : fd_restart_recv_gossip_msg( fd_restart_t * restart,
174 : void * gossip_msg,
175 0 : ulong * out_heaviest_fork_found ) {
176 0 : uchar * src = (uchar *) fd_type_pun( gossip_msg );
177 0 : uint discriminant = FD_LOAD( uint, src );
178 0 : src += sizeof(uint);
179 :
180 0 : if( discriminant==fd_crds_data_enum_restart_heaviest_fork ) {
181 : /* Incoming packet from gossip tile. Format:
182 : Wen-restart gossip message for heaviest_fork (fd_gossip_restart_heaviest_fork_t)
183 : */
184 0 : fd_gossip_restart_heaviest_fork_t * msg = (fd_gossip_restart_heaviest_fork_t * ) fd_type_pun( src );
185 0 : fd_restart_recv_heaviest_fork( restart, msg );
186 0 : } else if( discriminant==fd_crds_data_enum_restart_last_voted_fork_slots ) {
187 : /* Incoming packet from gossip tile. Format:
188 : Wen-restart gossip message for last voted fork slots (fd_gossip_restart_last_voted_fork_slots_t)
189 : Bitmap in raw format (uchar* - bitmap size is specified in the gossip message)
190 : */
191 0 : fd_gossip_restart_last_voted_fork_slots_t * msg = (fd_gossip_restart_last_voted_fork_slots_t * ) fd_type_pun( src );
192 0 : msg->offsets.inner.raw_offsets.offsets.bits.bits = src + sizeof(fd_gossip_restart_last_voted_fork_slots_t);
193 0 : fd_restart_recv_last_voted_fork_slots( restart, msg, out_heaviest_fork_found );
194 0 : }
195 0 : }
196 :
197 : void
198 : fd_restart_find_heaviest_fork_bank_hash( fd_restart_t * restart,
199 : fd_funk_t * funk,
200 0 : ulong * out_need_repair ) {
201 0 : if( FD_UNLIKELY( restart->heaviest_fork_slot<restart->funk_root ) ) {
202 0 : FD_LOG_ERR(( "Halting wen-restart because heaviest_fork_slot(%lu) < funk_root(%lu)",
203 0 : restart->heaviest_fork_slot, restart->funk_root ));
204 0 : } else if( FD_UNLIKELY( restart->heaviest_fork_slot==restart->funk_root ) ) {
205 0 : FD_LOG_NOTICE(( "Found bank hash of slot%lu in funk: %s",
206 0 : restart->funk_root, FD_BASE58_ENC_32_ALLOCA( &restart->root_bank_hash ) ));
207 0 : fd_memcpy( &restart->heaviest_fork_bank_hash, &restart->root_bank_hash, sizeof(fd_hash_t) );
208 0 : restart->heaviest_fork_ready = 1;
209 :
210 0 : *out_need_repair = 0;
211 0 : } else {
212 : /* Cancel any leftover in-preparation transactions from funk */
213 0 : fd_funk_start_write( funk );
214 0 : fd_funk_txn_cancel_all( funk, 1 );
215 0 : fd_funk_end_write( funk );
216 :
217 0 : *out_need_repair = 1;
218 0 : }
219 0 : }
220 :
221 : void
222 : fd_restart_verify_heaviest_fork( fd_restart_t * restart,
223 : uchar * out_buf,
224 0 : ulong * out_send ) {
225 0 : *out_send = 0;
226 0 : if( FD_UNLIKELY( restart->stage!=WR_STAGE_FIND_HEAVIEST_FORK_BANK_HASH ) ) return;
227 :
228 0 : if( FD_UNLIKELY(( restart->heaviest_fork_ready==1 )) ) {
229 0 : if( FD_UNLIKELY( memcmp( restart->my_pubkey.key,
230 0 : restart->coordinator_pubkey.key,
231 0 : sizeof(fd_pubkey_t) )==0 ) ) {
232 : /* I am the wen-restart coordinator */
233 0 : *out_send = 1;
234 0 : } else if( FD_UNLIKELY( restart->coordinator_heaviest_fork_ready==1 ) ) {
235 : /* I am not the wen-restart coordinator, but the coordinator message was received */
236 0 : if( restart->heaviest_fork_slot!=restart->coordinator_heaviest_fork_slot ) {
237 0 : FD_LOG_ERR(( "Heaviest fork mismatch: my slot=%lu, coordinator slot=%lu",
238 0 : restart->heaviest_fork_slot, restart->coordinator_heaviest_fork_slot ));
239 0 : }
240 0 : if( memcmp( restart->heaviest_fork_bank_hash.hash,
241 0 : restart->coordinator_heaviest_fork_bank_hash.hash,
242 0 : sizeof(fd_hash_t) )!=0 ) {
243 0 : FD_LOG_ERR(( "Heaviest fork mismatch for slot%lu: my hash=%s, coordinator hash=%s",
244 0 : restart->heaviest_fork_slot,
245 0 : FD_BASE58_ENC_32_ALLOCA( &restart->heaviest_fork_bank_hash ),
246 0 : FD_BASE58_ENC_32_ALLOCA( &restart->coordinator_heaviest_fork_bank_hash ) ));
247 0 : }
248 0 : *out_send = 1;
249 0 : }
250 :
251 0 : if( FD_UNLIKELY( *out_send ) ) {
252 0 : fd_gossip_restart_heaviest_fork_t * msg = (fd_gossip_restart_heaviest_fork_t *) fd_type_pun( out_buf );
253 0 : msg->observed_stake = 0;
254 0 : msg->last_slot = restart->heaviest_fork_slot;
255 0 : fd_memcpy( msg->last_slot_hash.hash, restart->heaviest_fork_bank_hash.hash, sizeof(fd_hash_t) );
256 :
257 0 : restart->stage = WR_STAGE_GENERATE_SNAPSHOT;
258 0 : FD_LOG_WARNING(( "Wen-restart succeeds with slot=%lu, bank hash=%s",
259 0 : restart->heaviest_fork_slot, FD_BASE58_ENC_32_ALLOCA( &restart->heaviest_fork_bank_hash ) ));
260 : /* TODO: insert a hard fork and generate an incremental snapshot */
261 : /* The incremental snapshot should contain everything from restart->funk_root to restart->heaviest_fork_slot. */
262 :
263 0 : restart->stage = WR_STAGE_DONE;
264 0 : }
265 0 : }
266 0 : }
267 :
268 : void
269 : fd_restart_convert_runlength_to_raw_bitmap( fd_gossip_restart_last_voted_fork_slots_t * msg,
270 : uchar * out_bitmap,
271 0 : ulong * out_bitmap_len ) {
272 0 : ulong bit_cnt = 0;
273 0 : *out_bitmap_len = 0;
274 0 : fd_memset( out_bitmap, 0, FD_RESTART_RAW_BITMAP_BYTES_MAX );
275 :
276 0 : for ( ulong i=0, bit=1; i<msg->offsets.inner.run_length_encoding.offsets_len; i++ ) {
277 0 : ushort cnt = msg->offsets.inner.run_length_encoding.offsets[i].bits;
278 0 : if( bit ) {
279 0 : for ( ulong pos=bit_cnt; pos<bit_cnt+cnt; pos++ ) {
280 0 : if( FD_UNLIKELY( pos/BITS_PER_UCHAR>=FD_RESTART_RAW_BITMAP_BYTES_MAX ) ) {
281 : /* Invalid message triggering a buffer overflow */
282 0 : *out_bitmap_len = FD_RESTART_RAW_BITMAP_BYTES_MAX+1;
283 0 : return;
284 0 : }
285 0 : out_bitmap[pos/BITS_PER_UCHAR] = fd_uchar_set_bit( out_bitmap[pos/BITS_PER_UCHAR], pos%BITS_PER_UCHAR );
286 0 : }
287 0 : }
288 0 : bit ^= 1;
289 0 : bit_cnt += cnt;
290 0 : *out_bitmap_len = (bit_cnt-1)/BITS_PER_UCHAR+1;
291 0 : }
292 0 : msg->offsets.discriminant = fd_restart_slots_offsets_enum_raw_offsets;
293 0 : msg->offsets.inner.raw_offsets.offsets.has_bits = 1;
294 0 : msg->offsets.inner.raw_offsets.offsets.len = bit_cnt;
295 0 : msg->offsets.inner.raw_offsets.offsets.bits.bits_len = *out_bitmap_len;
296 0 : }
297 :
298 : void
299 : fd_restart_convert_raw_bitmap_to_runlength( fd_gossip_restart_last_voted_fork_slots_t * msg,
300 0 : fd_restart_run_length_encoding_inner_t * out_encoding ) {
301 0 : ushort cnt = 0;
302 0 : int last_bit = 1;
303 0 : ulong offsets_len = 0;
304 0 : for( ulong raw_bitmap_iter=0;
305 0 : raw_bitmap_iter<msg->offsets.inner.raw_offsets.offsets.len &&
306 0 : offsets_len<FD_RESTART_PACKET_BITMAP_BYTES_MAX/sizeof(ushort);
307 0 : raw_bitmap_iter++ ) {
308 0 : ulong idx = raw_bitmap_iter/BITS_PER_UCHAR;
309 0 : int off = raw_bitmap_iter%BITS_PER_UCHAR;
310 0 : int bit = fd_uchar_extract_bit( msg->offsets.inner.raw_offsets.offsets.bits.bits[idx], off );
311 0 : if( FD_LIKELY( bit==last_bit ) ) {
312 0 : cnt++;
313 0 : } else {
314 0 : out_encoding[offsets_len++].bits = cnt;
315 0 : cnt = 1;
316 0 : last_bit = bit;
317 0 : }
318 0 : }
319 0 : out_encoding[offsets_len++].bits = cnt;
320 :
321 0 : msg->offsets.discriminant = fd_restart_slots_offsets_enum_run_length_encoding;
322 0 : msg->offsets.inner.run_length_encoding.offsets_len = offsets_len;
323 0 : msg->offsets.inner.run_length_encoding.offsets = out_encoding;
324 0 : }
325 :
326 : void
327 : fd_restart_init( fd_restart_t * restart,
328 : ulong funk_root,
329 : fd_hash_t * root_bank_hash,
330 : fd_vote_accounts_t const ** epoch_stakes,
331 : fd_epoch_schedule_t * epoch_schedule,
332 : int tower_checkpt_fileno,
333 : fd_slot_history_t const * slot_history,
334 : fd_pubkey_t * my_pubkey,
335 : fd_pubkey_t * coordinator_pubkey,
336 : uchar * out_buf,
337 0 : ulong * out_buf_len ) {
338 0 : restart->funk_root = funk_root;
339 0 : restart->epoch_schedule = epoch_schedule;
340 0 : restart->root_epoch = fd_slot_to_epoch( epoch_schedule, restart->funk_root, NULL ),
341 0 : restart->stage = WR_STAGE_FIND_HEAVIEST_FORK_SLOT_NUM;
342 0 : restart->heaviest_fork_ready = 0;
343 0 : restart->coordinator_heaviest_fork_ready = 0;
344 0 : fd_memcpy( restart->root_bank_hash.hash, root_bank_hash, sizeof(fd_pubkey_t) );
345 0 : fd_memcpy( restart->my_pubkey.key, my_pubkey, sizeof(fd_pubkey_t) );
346 0 : fd_memcpy( restart->coordinator_pubkey.key, coordinator_pubkey, sizeof(fd_pubkey_t) );
347 0 : fd_memset( restart->slot_to_stake, 0, sizeof(restart->slot_to_stake) );
348 0 : fd_memset( restart->last_voted_fork_slots_received, 0, sizeof(restart->last_voted_fork_slots_received) );
349 0 : FD_LOG_WARNING(( "[%s]\nfunk root=%lu\nroot epoch=%lu\nroot_bank_hash=%s\ncoordinator pubkey: %s\nMy pubkey: %s",
350 0 : __func__,
351 0 : restart->funk_root,
352 0 : restart->root_epoch,
353 0 : FD_BASE58_ENC_32_ALLOCA( &restart->root_bank_hash ),
354 0 : FD_BASE58_ENC_32_ALLOCA( &restart->coordinator_pubkey ),
355 0 : FD_BASE58_ENC_32_ALLOCA( &restart->my_pubkey ) ));
356 :
357 : /* Save the vote accounts stake information for the MAX_EPOCH epochs */
358 0 : FD_TEST( RESTART_EPOCHS_MAX==2 );
359 0 : for( ulong e=0; e<RESTART_EPOCHS_MAX; e++ ) {
360 0 : if( epoch_stakes[e]->vote_accounts_root==NULL ) FD_LOG_ERR(( "vote account information is missing for epoch#%lu", restart->root_epoch+e ));
361 0 : restart->num_vote_accts[e] = fd_stake_weights_by_node( epoch_stakes[e], restart->stake_weights[e] );
362 0 : restart->total_stake[e] = 0;
363 0 : restart->total_stake_received[e] = 0;
364 0 : restart->total_stake_received_and_voted[e] = 0;
365 0 : FD_TEST( restart->num_vote_accts[e]<=FD_RESTART_MAX_PEERS );
366 :
367 0 : for( ulong i=0; i<restart->num_vote_accts[e]; i++ ) {
368 0 : FD_LOG_DEBUG(( "Epoch#%lu voter %s holds stake amount=%lu",
369 0 : restart->root_epoch+e,
370 0 : FD_BASE58_ENC_32_ALLOCA( &restart->stake_weights[e][i].key ),
371 0 : restart->stake_weights[e][i].stake ));
372 0 : restart->total_stake[e] += restart->stake_weights[e][i].stake;
373 0 : }
374 0 : FD_LOG_NOTICE(( "[%s] There are %lu staked voters in epoch#%lu with total stake %lu",
375 0 : __func__, restart->num_vote_accts[e], restart->root_epoch+e, restart->total_stake[e] ));
376 0 : }
377 :
378 : /* Get the last_voted_slot and its bank hash from the tower checkpoint file */
379 0 : fd_hash_t tower_bank_hash;
380 0 : ulong tower_height, tower_slots[ FD_TOWER_VOTE_MAX+1 ];
381 0 : fd_restart_tower_restore( &tower_bank_hash, tower_slots, &tower_height, tower_checkpt_fileno );
382 :
383 0 : fd_gossip_restart_last_voted_fork_slots_t * msg = (fd_gossip_restart_last_voted_fork_slots_t *) fd_type_pun( out_buf );
384 0 : msg->last_voted_slot = tower_slots[tower_height-1];
385 0 : fd_memcpy( msg->last_voted_hash.hash, tower_bank_hash.hash, sizeof(fd_hash_t) );
386 :
387 : /* Given last_voted_slot, get the bitmap for the last_voted_fork_slots gossip message */
388 0 : ulong end_slot = msg->last_voted_slot;
389 0 : uchar * bitmap = out_buf+sizeof(fd_gossip_restart_last_voted_fork_slots_t);
390 0 : ulong start_slot = ( end_slot>LAST_VOTED_FORK_MAX_SLOTS? end_slot-LAST_VOTED_FORK_MAX_SLOTS : 0 );
391 0 : ulong num_slots = end_slot-start_slot+1;
392 0 : msg->offsets.discriminant = fd_restart_slots_offsets_enum_raw_offsets;
393 0 : msg->offsets.inner.raw_offsets.offsets.has_bits = 1;
394 0 : msg->offsets.inner.raw_offsets.offsets.len = num_slots;
395 0 : msg->offsets.inner.raw_offsets.offsets.bits.bits = bitmap;
396 0 : msg->offsets.inner.raw_offsets.offsets.bits.bits_len = ( num_slots-1 )/BITS_PER_UCHAR+1;
397 0 : *out_buf_len = sizeof(fd_gossip_restart_last_voted_fork_slots_t)+( num_slots-1 )/BITS_PER_UCHAR+1;
398 0 : FD_LOG_NOTICE(( "[%s] last_voted_slot=%lu, bank_hash=%s, encoding %lu bits in bitmap",
399 0 : __func__, msg->last_voted_slot, FD_BASE58_ENC_32_ALLOCA( &tower_bank_hash ), num_slots ));
400 :
401 : /* Encode slots from the tower checkpoint into the bitmap */
402 0 : for( ulong i=0; i<tower_height; i++ ) {
403 0 : ulong offset_from_end = end_slot-tower_slots[i];
404 0 : ulong out_idx = offset_from_end/BITS_PER_UCHAR;
405 0 : int out_bit_off = offset_from_end%BITS_PER_UCHAR;
406 0 : bitmap[out_idx] = fd_uchar_set_bit( bitmap[out_idx], out_bit_off );
407 0 : }
408 :
409 : /* Encode slots from the slot_history system program into the bitmap */
410 0 : if( FD_UNLIKELY( tower_slots[0]!=slot_history->next_slot ) ) {
411 0 : FD_LOG_WARNING(( "You may be loading a wrong snapshot in funk.\n \
412 0 : We expect tower root(%lu) to be the same as slot_history->next_slot(%lu)", tower_slots[0], slot_history->next_slot ));
413 0 : }
414 :
415 0 : for( ulong i=start_slot; i<slot_history->next_slot; i++ ) {
416 0 : ulong in_idx = ( i/BITS_PER_ULONG )%( slot_history->bits.bits->blocks_len );
417 0 : int in_bit_off = i%BITS_PER_ULONG;
418 :
419 0 : ulong offset_from_end = end_slot-i;
420 0 : ulong out_idx = offset_from_end/BITS_PER_UCHAR;
421 0 : int out_bit_off = offset_from_end%BITS_PER_UCHAR;
422 :
423 0 : if( FD_LIKELY( fd_ulong_extract_bit( slot_history->bits.bits->blocks[in_idx], in_bit_off ) ) ) {
424 : /* bit#i in slot_history is 1 */
425 0 : bitmap[out_idx] = fd_uchar_set_bit( bitmap[out_idx], out_bit_off );
426 0 : } else {
427 : /* bit#i in slot_history is 0 */
428 0 : bitmap[out_idx] = fd_uchar_clear_bit( bitmap[out_idx], out_bit_off );
429 0 : }
430 0 : }
431 :
432 0 : ulong found;
433 0 : fd_memcpy( msg->from.key, my_pubkey->key, sizeof(fd_pubkey_t) );
434 0 : fd_restart_recv_last_voted_fork_slots( restart, msg, &found );
435 0 : if( FD_UNLIKELY( found ) ) FD_LOG_WARNING(( "[%s] It seems that this single validator alone has >80% stake", __func__ ));
436 0 : }
437 :
438 : void
439 : fd_restart_tower_checkpt( fd_hash_t const * vote_bank_hash,
440 : fd_tower_t * tower,
441 0 : int tower_checkpt_fileno ) {
442 0 : lseek( tower_checkpt_fileno, 0, SEEK_SET );
443 0 : ulong wsz, total_wsz = 0;
444 0 : ulong slots_cnt = fd_tower_votes_cnt( tower->votes )+1;
445 :
446 0 : fd_io_write( tower_checkpt_fileno, vote_bank_hash, sizeof(fd_hash_t), sizeof(fd_hash_t), &wsz );
447 0 : if( FD_UNLIKELY( wsz!=sizeof(fd_hash_t) ) ) goto checkpt_finish;
448 0 : total_wsz += wsz;
449 0 : fd_io_write( tower_checkpt_fileno, &slots_cnt, sizeof(ulong), sizeof(ulong), &wsz );
450 0 : if( FD_UNLIKELY( wsz!=sizeof(ulong) ) ) goto checkpt_finish;
451 0 : total_wsz += wsz;
452 0 : fd_io_write( tower_checkpt_fileno, &tower->root, sizeof(ulong), sizeof(ulong), &wsz );
453 0 : if( FD_UNLIKELY( wsz!=sizeof(ulong) ) ) goto checkpt_finish;
454 0 : total_wsz += wsz;
455 :
456 0 : for( fd_tower_votes_iter_t tower_iter = fd_tower_votes_iter_init( tower->votes );
457 0 : !fd_tower_votes_iter_done( tower->votes, tower_iter );
458 0 : tower_iter = fd_tower_votes_iter_next( tower->votes, tower_iter ) ) {
459 0 : ulong slot = fd_tower_votes_iter_ele( tower->votes, tower_iter )->slot;
460 0 : fd_io_write( tower_checkpt_fileno, &slot, sizeof(ulong), sizeof(ulong), &wsz );
461 0 : if( FD_UNLIKELY( wsz!=sizeof(ulong) ) ) goto checkpt_finish;
462 0 : total_wsz += wsz;
463 0 : }
464 :
465 0 : fsync( tower_checkpt_fileno );
466 0 : checkpt_finish:
467 0 : if( FD_UNLIKELY( total_wsz!=sizeof(fd_hash_t)+sizeof(ulong)*( slots_cnt+1 ) ) ) FD_LOG_WARNING(( "Failed at checkpointing tower" ));
468 0 : }
469 :
470 : void
471 : fd_restart_tower_restore( fd_hash_t * vote_bank_hash,
472 : ulong * tower_slots,
473 : ulong * tower_height,
474 0 : int tower_checkpt_fileno ) {
475 0 : ulong rsz;
476 0 : FD_TEST( 0==fd_io_read( tower_checkpt_fileno, vote_bank_hash, sizeof(fd_hash_t), sizeof(fd_hash_t), &rsz ) );
477 0 : FD_TEST( rsz==sizeof(fd_hash_t) );
478 0 : FD_TEST( 0==fd_io_read( tower_checkpt_fileno, tower_height, sizeof(ulong), sizeof(ulong), &rsz ) );
479 0 : FD_TEST( rsz==sizeof(ulong) );
480 0 : FD_TEST( *tower_height<=FD_TOWER_VOTE_MAX+1 );
481 0 : for( ulong i=0; i<*tower_height; i++ ) {
482 0 : FD_TEST( 0==fd_io_read( tower_checkpt_fileno, tower_slots+i, sizeof(ulong), sizeof(ulong), &rsz ) );
483 0 : FD_TEST( rsz==sizeof(ulong) );
484 0 : }
485 0 : }
|