Line data Source code
1 : #include "fd_restart.h"
2 :
3 : #include "../../flamenco/stakes/fd_stakes.h"
4 : #include "../../flamenco/snapshot/fd_snapshot_create.h"
5 : #include "../../flamenco/runtime/sysvar/fd_sysvar_epoch_schedule.h"
6 :
7 : #include <sys/types.h>
8 : #include <unistd.h>
9 :
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[FD_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[FD_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<FD_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]>=FD_RESTART_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>=FD_RESTART_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!=FD_RESTART_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+FD_RESTART_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[ FD_RESTART_EPOCHS_MAX ] = {0UL, 0UL};
88 :
89 0 : for( ulong e=0; e<FD_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[ FD_RESTART_EPOCHS_MAX ] = { restart->total_stake_received[0]
135 0 : - restart->total_stake[0]*FD_RESTART_HEAVIEST_FORK_THRESHOLD_DELTA_PERCENT/100UL,
136 0 : restart->total_stake_received[1]
137 0 : - restart->total_stake[1]*FD_RESTART_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<FD_RESTART_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 = FD_RESTART_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 : ulong * is_constipated,
224 : fd_slot_pair_t * hard_forks,
225 : ulong hard_forks_len,
226 : fd_hash_t * genesis_hash,
227 : uchar * out_buf,
228 0 : ulong * out_send ) {
229 0 : *out_send = 0;
230 0 : if( FD_UNLIKELY( restart->stage!=FD_RESTART_STAGE_FIND_HEAVIEST_FORK_BANK_HASH ) ) return;
231 :
232 0 : if( FD_UNLIKELY(( restart->heaviest_fork_ready==1 )) ) {
233 0 : if( FD_UNLIKELY( memcmp( restart->my_pubkey.key,
234 0 : restart->coordinator_pubkey.key,
235 0 : sizeof(fd_pubkey_t) )==0 ) ) {
236 : /* I am the wen-restart coordinator */
237 0 : *out_send = 1;
238 0 : } else if( FD_UNLIKELY( restart->coordinator_heaviest_fork_ready==1 ) ) {
239 : /* I am not the wen-restart coordinator, but the coordinator message was received */
240 0 : if( restart->heaviest_fork_slot!=restart->coordinator_heaviest_fork_slot ) {
241 0 : FD_LOG_ERR(( "Heaviest fork mismatch: my slot=%lu, coordinator slot=%lu",
242 0 : restart->heaviest_fork_slot, restart->coordinator_heaviest_fork_slot ));
243 0 : }
244 0 : if( memcmp( restart->heaviest_fork_bank_hash.hash,
245 0 : restart->coordinator_heaviest_fork_bank_hash.hash,
246 0 : sizeof(fd_hash_t) )!=0 ) {
247 0 : FD_LOG_ERR(( "Heaviest fork mismatch for slot%lu: my hash=%s, coordinator hash=%s",
248 0 : restart->heaviest_fork_slot,
249 0 : FD_BASE58_ENC_32_ALLOCA( &restart->heaviest_fork_bank_hash ),
250 0 : FD_BASE58_ENC_32_ALLOCA( &restart->coordinator_heaviest_fork_bank_hash ) ));
251 0 : }
252 0 : *out_send = 1;
253 0 : }
254 :
255 0 : if( FD_UNLIKELY( *out_send ) ) {
256 0 : fd_gossip_restart_heaviest_fork_t * msg = (fd_gossip_restart_heaviest_fork_t *) fd_type_pun( out_buf );
257 0 : msg->observed_stake = 0;
258 0 : msg->last_slot = restart->heaviest_fork_slot;
259 0 : fd_memcpy( msg->last_slot_hash.hash, restart->heaviest_fork_bank_hash.hash, sizeof(fd_hash_t) );
260 :
261 0 : restart->stage = FD_RESTART_STAGE_GENERATE_SNAPSHOT;
262 :
263 : /* Generate a full snapshot since we started wen-restart with a funk file instead of a snapshot file */
264 0 : ulong updated_fseq = fd_batch_fseq_pack( 1, 0, restart->heaviest_fork_slot );
265 0 : fd_fseq_update( is_constipated, updated_fseq );
266 :
267 : /* Calculate the new shred version after inserting a hard fork */
268 0 : fd_sha256_t _sha[ 1 ]; fd_sha256_t * sha = fd_sha256_join( fd_sha256_new( _sha ) );
269 0 : fd_sha256_init( sha );
270 0 : fd_sha256_append( sha, genesis_hash->hash, sizeof(fd_pubkey_t) );
271 0 : for( ulong i=0; i<hard_forks_len; i++ )
272 0 : fd_sha256_append( sha, hard_forks+i, sizeof(fd_slot_pair_t) );
273 :
274 0 : union {
275 0 : uchar c[ 32 ];
276 0 : ushort s[ 16 ];
277 0 : } hash;
278 0 : fd_sha256_fini( sha, hash.c );
279 0 : fd_sha256_delete( fd_sha256_leave( sha ) );
280 :
281 0 : ushort xor = 0;
282 0 : for( ulong i=0UL; i<16UL; i++ ) xor ^= hash.s[ i ];
283 0 : xor = fd_ushort_bswap( xor );
284 0 : ushort new_shred_version = fd_ushort_if( xor<USHORT_MAX, (ushort)(xor + 1), USHORT_MAX );
285 :
286 0 : FD_LOG_WARNING(( "Wen-restart succeeds with slot=%lu, bank hash=%s, shred version=%u",
287 0 : restart->heaviest_fork_slot, FD_BASE58_ENC_32_ALLOCA( &restart->heaviest_fork_bank_hash ), new_shred_version ));
288 :
289 0 : restart->stage = FD_RESTART_STAGE_DONE;
290 0 : }
291 0 : }
292 0 : }
293 :
294 : void
295 : fd_restart_convert_runlength_to_raw_bitmap( fd_gossip_restart_last_voted_fork_slots_t * msg,
296 : uchar * out_bitmap,
297 0 : ulong * out_bitmap_len ) {
298 0 : ulong bit_cnt = 0;
299 0 : *out_bitmap_len = 0;
300 0 : fd_memset( out_bitmap, 0, FD_RESTART_RAW_BITMAP_BYTES_MAX );
301 :
302 0 : for ( ulong i=0, bit=1; i<msg->offsets.inner.run_length_encoding.offsets_len; i++ ) {
303 0 : ushort cnt = msg->offsets.inner.run_length_encoding.offsets[i].bits;
304 0 : if( bit ) {
305 0 : for ( ulong pos=bit_cnt; pos<bit_cnt+cnt; pos++ ) {
306 0 : if( FD_UNLIKELY( pos/BITS_PER_UCHAR>=FD_RESTART_RAW_BITMAP_BYTES_MAX ) ) {
307 : /* Invalid message triggering a buffer overflow */
308 0 : *out_bitmap_len = FD_RESTART_RAW_BITMAP_BYTES_MAX+1;
309 0 : return;
310 0 : }
311 0 : out_bitmap[pos/BITS_PER_UCHAR] = fd_uchar_set_bit( out_bitmap[pos/BITS_PER_UCHAR], pos%BITS_PER_UCHAR );
312 0 : }
313 0 : }
314 0 : bit ^= 1;
315 0 : bit_cnt += cnt;
316 0 : *out_bitmap_len = (bit_cnt-1)/BITS_PER_UCHAR+1;
317 0 : }
318 0 : msg->offsets.discriminant = fd_restart_slots_offsets_enum_raw_offsets;
319 0 : msg->offsets.inner.raw_offsets.offsets.has_bits = 1;
320 0 : msg->offsets.inner.raw_offsets.offsets.len = bit_cnt;
321 0 : msg->offsets.inner.raw_offsets.offsets.bits.bits_len = *out_bitmap_len;
322 0 : }
323 :
324 : void
325 : fd_restart_convert_raw_bitmap_to_runlength( fd_gossip_restart_last_voted_fork_slots_t * msg,
326 0 : fd_restart_run_length_encoding_inner_t * out_encoding ) {
327 0 : ushort cnt = 0;
328 0 : int last_bit = 1;
329 0 : ulong offsets_len = 0;
330 0 : for( ulong raw_bitmap_iter=0;
331 0 : raw_bitmap_iter<msg->offsets.inner.raw_offsets.offsets.len &&
332 0 : offsets_len<FD_RESTART_PACKET_BITMAP_BYTES_MAX/sizeof(ushort);
333 0 : raw_bitmap_iter++ ) {
334 0 : ulong idx = raw_bitmap_iter/BITS_PER_UCHAR;
335 0 : int off = raw_bitmap_iter%BITS_PER_UCHAR;
336 0 : int bit = fd_uchar_extract_bit( msg->offsets.inner.raw_offsets.offsets.bits.bits[idx], off );
337 0 : if( FD_LIKELY( bit==last_bit ) ) {
338 0 : cnt++;
339 0 : } else {
340 0 : out_encoding[offsets_len++].bits = cnt;
341 0 : cnt = 1;
342 0 : last_bit = bit;
343 0 : }
344 0 : }
345 0 : out_encoding[offsets_len++].bits = cnt;
346 :
347 0 : msg->offsets.discriminant = fd_restart_slots_offsets_enum_run_length_encoding;
348 0 : msg->offsets.inner.run_length_encoding.offsets_len = offsets_len;
349 0 : msg->offsets.inner.run_length_encoding.offsets = out_encoding;
350 0 : }
351 :
352 : void
353 : fd_restart_init( fd_restart_t * restart,
354 : ulong funk_root,
355 : fd_hash_t * root_bank_hash,
356 : fd_vote_accounts_t const ** epoch_stakes,
357 : fd_epoch_schedule_t * epoch_schedule,
358 : int tower_checkpt_fileno,
359 : fd_slot_history_t const * slot_history,
360 : fd_pubkey_t * my_pubkey,
361 : fd_pubkey_t * coordinator_pubkey,
362 : uchar * out_buf,
363 : ulong * out_buf_len,
364 0 : fd_spad_t * runtime_spad ) {
365 0 : restart->funk_root = funk_root;
366 0 : restart->epoch_schedule = epoch_schedule;
367 0 : restart->root_epoch = fd_slot_to_epoch( epoch_schedule, restart->funk_root, NULL ),
368 0 : restart->stage = FD_RESTART_STAGE_FIND_HEAVIEST_FORK_SLOT_NUM;
369 0 : restart->heaviest_fork_ready = 0;
370 0 : restart->coordinator_heaviest_fork_ready = 0;
371 0 : fd_memcpy( restart->root_bank_hash.hash, root_bank_hash, sizeof(fd_pubkey_t) );
372 0 : fd_memcpy( restart->my_pubkey.key, my_pubkey, sizeof(fd_pubkey_t) );
373 0 : fd_memcpy( restart->coordinator_pubkey.key, coordinator_pubkey, sizeof(fd_pubkey_t) );
374 0 : fd_memset( restart->slot_to_stake, 0, sizeof(restart->slot_to_stake) );
375 0 : fd_memset( restart->last_voted_fork_slots_received, 0, sizeof(restart->last_voted_fork_slots_received) );
376 0 : FD_LOG_WARNING(( "[%s]\nfunk root=%lu\nroot epoch=%lu\nroot_bank_hash=%s\ncoordinator pubkey: %s\nMy pubkey: %s",
377 0 : __func__,
378 0 : restart->funk_root,
379 0 : restart->root_epoch,
380 0 : FD_BASE58_ENC_32_ALLOCA( &restart->root_bank_hash ),
381 0 : FD_BASE58_ENC_32_ALLOCA( &restart->coordinator_pubkey ),
382 0 : FD_BASE58_ENC_32_ALLOCA( &restart->my_pubkey ) ));
383 :
384 : /* Save the vote accounts stake information for the MAX_EPOCH epochs */
385 0 : FD_TEST( FD_RESTART_EPOCHS_MAX==2 );
386 0 : for( ulong e=0; e<FD_RESTART_EPOCHS_MAX; e++ ) {
387 0 : if( epoch_stakes[e]->vote_accounts_root==NULL ) FD_LOG_ERR(( "vote account information is missing for epoch#%lu", restart->root_epoch+e ));
388 0 : restart->num_vote_accts[e] = fd_stake_weights_by_node( epoch_stakes[e], restart->stake_weights[e], runtime_spad );
389 0 : restart->total_stake[e] = 0;
390 0 : restart->total_stake_received[e] = 0;
391 0 : restart->total_stake_received_and_voted[e] = 0;
392 0 : FD_TEST( restart->num_vote_accts[e]<=FD_RESTART_MAX_PEERS );
393 :
394 0 : for( ulong i=0; i<restart->num_vote_accts[e]; i++ ) {
395 0 : FD_LOG_DEBUG(( "Epoch#%lu voter %s holds stake amount=%lu",
396 0 : restart->root_epoch+e,
397 0 : FD_BASE58_ENC_32_ALLOCA( &restart->stake_weights[e][i].key ),
398 0 : restart->stake_weights[e][i].stake ));
399 0 : restart->total_stake[e] += restart->stake_weights[e][i].stake;
400 0 : }
401 0 : FD_LOG_NOTICE(( "[%s] There are %lu staked voters in epoch#%lu with total stake %lu",
402 0 : __func__, restart->num_vote_accts[e], restart->root_epoch+e, restart->total_stake[e] ));
403 0 : }
404 :
405 : /* Get the last_voted_slot and its bank hash from the tower checkpoint file */
406 0 : fd_hash_t tower_bank_hash;
407 0 : ulong rsz, tower_height, tower_root, tower_slots[ FD_TOWER_VOTE_MAX+1 ];
408 :
409 0 : FD_TEST( 0==fd_io_read( tower_checkpt_fileno, &tower_bank_hash, sizeof(fd_hash_t), sizeof(fd_hash_t), &rsz ) );
410 0 : FD_TEST( rsz==sizeof(fd_hash_t) );
411 0 : FD_TEST( 0==fd_io_read( tower_checkpt_fileno, &tower_height, sizeof(ulong), sizeof(ulong), &rsz ) );
412 0 : FD_TEST( rsz==sizeof(ulong) );
413 0 : FD_TEST( tower_height<=FD_TOWER_VOTE_MAX );
414 0 : FD_TEST( 0==fd_io_read( tower_checkpt_fileno, &tower_root, sizeof(ulong), sizeof(ulong), &rsz ) );
415 0 : FD_TEST( rsz==sizeof(ulong) );
416 0 : ulong tower_slots_sz = sizeof(ulong)*tower_height;
417 0 : FD_TEST( 0==fd_io_read( tower_checkpt_fileno, tower_slots, tower_slots_sz, tower_slots_sz, &rsz ) );
418 0 : FD_TEST( rsz==tower_slots_sz );
419 :
420 0 : fd_gossip_restart_last_voted_fork_slots_t * msg = (fd_gossip_restart_last_voted_fork_slots_t *) fd_type_pun( out_buf );
421 0 : msg->last_voted_slot = tower_slots[tower_height-1];
422 0 : fd_memcpy( msg->last_voted_hash.hash, tower_bank_hash.hash, sizeof(fd_hash_t) );
423 :
424 : /* Given last_voted_slot, get the bitmap for the last_voted_fork_slots gossip message */
425 0 : ulong end_slot = msg->last_voted_slot;
426 0 : ulong start_slot = ( end_slot>FD_RESTART_LAST_VOTED_FORK_MAX_SLOTS? end_slot-FD_RESTART_LAST_VOTED_FORK_MAX_SLOTS : 0 );
427 0 : ulong num_slots = end_slot-start_slot+1;
428 0 : uchar * bitmap = out_buf+sizeof(fd_gossip_restart_last_voted_fork_slots_t);
429 0 : fd_memset( bitmap, 0, num_slots/BITS_PER_UCHAR+1 );
430 0 : msg->offsets.discriminant = fd_restart_slots_offsets_enum_raw_offsets;
431 0 : msg->offsets.inner.raw_offsets.offsets.has_bits = 1;
432 0 : msg->offsets.inner.raw_offsets.offsets.len = num_slots;
433 0 : msg->offsets.inner.raw_offsets.offsets.bits.bits = bitmap;
434 0 : msg->offsets.inner.raw_offsets.offsets.bits.bits_len = ( num_slots-1 )/BITS_PER_UCHAR+1;
435 0 : *out_buf_len = sizeof(fd_gossip_restart_last_voted_fork_slots_t)+( num_slots-1 )/BITS_PER_UCHAR+1;
436 0 : FD_LOG_NOTICE(( "[%s] last_voted_slot=%lu, bank_hash=%s, encoding %lu bits in bitmap",
437 0 : __func__, msg->last_voted_slot, FD_BASE58_ENC_32_ALLOCA( &tower_bank_hash ), num_slots ));
438 :
439 : /* Encode slots from the tower checkpoint into the bitmap */
440 0 : ulong checkpt_ghost_root=ULONG_MAX;
441 0 : while(1) {
442 0 : ulong slot;
443 0 : FD_TEST( 0==fd_io_read( tower_checkpt_fileno, &slot, sizeof(ulong), sizeof(ulong), &rsz ) );
444 0 : if( FD_UNLIKELY( slot==ULONG_MAX ) ) break;
445 0 : checkpt_ghost_root = slot;
446 :
447 0 : ulong offset_from_end = end_slot-slot;
448 0 : ulong out_idx = offset_from_end/BITS_PER_UCHAR;
449 0 : int out_bit_off = offset_from_end%BITS_PER_UCHAR;
450 0 : bitmap[out_idx] = fd_uchar_set_bit( bitmap[out_idx], out_bit_off );
451 0 : }
452 :
453 : /* Encode slots from the slot_history system program into the bitmap */
454 0 : if( FD_UNLIKELY( checkpt_ghost_root>slot_history->next_slot ) ) {
455 0 : FD_LOG_WARNING(( "You may be loading a wrong snapshot in funk.\n \
456 0 : We expect checkpointed ghost root(%lu) <= slot_history->next_slot(%lu)", checkpt_ghost_root, slot_history->next_slot ));
457 0 : }
458 :
459 0 : for( ulong i=start_slot; i<slot_history->next_slot; i++ ) {
460 0 : ulong in_idx = ( i/BITS_PER_ULONG )%( slot_history->bits.bits->blocks_len );
461 0 : int in_bit_off = i%BITS_PER_ULONG;
462 :
463 0 : ulong offset_from_end = end_slot-i;
464 0 : ulong out_idx = offset_from_end/BITS_PER_UCHAR;
465 0 : int out_bit_off = offset_from_end%BITS_PER_UCHAR;
466 :
467 0 : if( FD_LIKELY( fd_ulong_extract_bit( slot_history->bits.bits->blocks[in_idx], in_bit_off ) ) ) {
468 : /* bit#i in slot_history is 1 */
469 0 : bitmap[out_idx] = fd_uchar_set_bit( bitmap[out_idx], out_bit_off );
470 0 : }
471 0 : }
472 :
473 0 : ulong found = 0;
474 0 : fd_memcpy( msg->from.key, my_pubkey->key, sizeof(fd_pubkey_t) );
475 0 : fd_restart_recv_last_voted_fork_slots( restart, msg, &found );
476 0 : if( FD_UNLIKELY( found ) ) FD_LOG_WARNING(( "[%s] It seems that this single validator alone has >80%% stake", __func__ ));
477 0 : }
478 :
479 : void
480 : fd_restart_tower_checkpt( fd_hash_t const * vote_bank_hash,
481 : fd_tower_t * tower,
482 : fd_ghost_t * ghost,
483 : ulong root,
484 0 : int tower_checkpt_fileno ) {
485 0 : lseek( tower_checkpt_fileno, 0, SEEK_SET );
486 0 : ulong wsz;
487 0 : ulong total_wsz = 0;
488 0 : ulong checkpt_history_len = 0;
489 0 : ulong tower_height = fd_tower_votes_cnt( tower );
490 0 : FD_TEST( tower_height>0 );
491 :
492 : /* Checkpoint the tower */
493 0 : fd_io_write( tower_checkpt_fileno, vote_bank_hash, sizeof(fd_hash_t), sizeof(fd_hash_t), &wsz );
494 0 : if( FD_UNLIKELY( wsz!=sizeof(fd_hash_t) ) ) goto checkpt_finish;
495 0 : total_wsz += wsz;
496 0 : fd_io_write( tower_checkpt_fileno, &tower_height, sizeof(ulong), sizeof(ulong), &wsz );
497 0 : if( FD_UNLIKELY( wsz!=sizeof(ulong) ) ) goto checkpt_finish;
498 0 : total_wsz += wsz;
499 0 : fd_io_write( tower_checkpt_fileno, &root, sizeof(ulong), sizeof(ulong), &wsz );
500 0 : if( FD_UNLIKELY( wsz!=sizeof(ulong) ) ) goto checkpt_finish;
501 0 : total_wsz += wsz;
502 :
503 0 : ulong last_voted_slot = ULONG_MAX;
504 0 : for( fd_tower_votes_iter_t tower_iter = fd_tower_votes_iter_init( tower );
505 0 : !fd_tower_votes_iter_done( tower, tower_iter );
506 0 : tower_iter = fd_tower_votes_iter_next( tower, tower_iter ) ) {
507 0 : ulong slot = fd_tower_votes_iter_ele( tower, tower_iter )->slot;
508 0 : fd_io_write( tower_checkpt_fileno, &slot, sizeof(ulong), sizeof(ulong), &wsz );
509 0 : if( FD_UNLIKELY( wsz!=sizeof(ulong) ) ) goto checkpt_finish;
510 0 : total_wsz += wsz;
511 :
512 0 : last_voted_slot = slot;
513 0 : }
514 :
515 : /* Checkpoint the vote slot history */
516 0 : fd_ghost_node_map_t * node_map = fd_ghost_node_map( ghost );
517 0 : fd_ghost_node_t * node_pool = fd_ghost_node_pool( ghost );
518 0 : fd_ghost_node_t * node = fd_ghost_node_map_ele_query( node_map, &last_voted_slot, NULL, node_pool );
519 0 : ulong ghost_root = fd_ghost_node_pool_ele_const( node_pool, ghost->root_idx )->slot;
520 :
521 0 : FD_TEST( node!=NULL );
522 0 : while( node->slot != ghost_root ) {
523 0 : fd_io_write( tower_checkpt_fileno, &node->slot, sizeof(ulong), sizeof(ulong), &wsz );
524 0 : if( FD_UNLIKELY( wsz!=sizeof(ulong) ) ) goto checkpt_finish;
525 0 : total_wsz += wsz;
526 0 : checkpt_history_len++;
527 :
528 0 : node = fd_ghost_node_pool_ele( node_pool, node->parent_idx );
529 0 : }
530 :
531 : /* Checkpoint the ghost root */
532 0 : fd_io_write( tower_checkpt_fileno, &node->slot, sizeof(ulong), sizeof(ulong), &wsz );
533 0 : if( FD_UNLIKELY( wsz!=sizeof(ulong) ) ) goto checkpt_finish;
534 0 : total_wsz += wsz;
535 0 : checkpt_history_len++;
536 :
537 : /* Mark the end of slot history */
538 0 : ulong end = ULONG_MAX;
539 0 : fd_io_write( tower_checkpt_fileno, &end, sizeof(ulong), sizeof(ulong), &wsz );
540 0 : if( FD_UNLIKELY( wsz!=sizeof(ulong) ) ) goto checkpt_finish;
541 0 : total_wsz += wsz;
542 0 : checkpt_history_len++;
543 :
544 : /* Truncate and flush the checkpoint file */
545 0 : fd_io_truncate( tower_checkpt_fileno, total_wsz );
546 0 : fsync( tower_checkpt_fileno );
547 0 : checkpt_finish:
548 0 : if( FD_UNLIKELY( total_wsz!=sizeof(fd_hash_t)+sizeof(ulong)*( 2+tower_height+checkpt_history_len ) ) ) {
549 0 : FD_LOG_WARNING(( "Failed at checkpointing tower" ));
550 0 : }
551 0 : }
|