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