Line data Source code
1 : #include "fd_tower_tile.h"
2 : #include "generated/fd_tower_tile_seccomp.h"
3 :
4 : #include "../../choreo/eqvoc/fd_eqvoc.h"
5 : #include "../../choreo/ghost/fd_ghost.h"
6 : #include "../../choreo/hfork/fd_hfork.h"
7 : #include "../../choreo/votes/fd_votes.h"
8 : #include "../../choreo/tower/fd_tower.h"
9 : #include "../../choreo/tower/fd_tower_voters.h"
10 : #include "../../choreo/tower/fd_tower_blocks.h"
11 : #include "../../choreo/tower/fd_tower_serdes.h"
12 : #include "../../choreo/tower/fd_tower_stakes.h"
13 : #include "../../disco/fd_txn_p.h"
14 : #include "../../disco/keyguard/fd_keyload.h"
15 : #include "../../disco/keyguard/fd_keyswitch.h"
16 : #include "../../disco/metrics/fd_metrics.h"
17 : #include "../../disco/topo/fd_topo.h"
18 : #include "../../disco/fd_txn_m.h"
19 : #include "../../discof/fd_accdb_topo.h"
20 : #include "../../discof/replay/fd_replay_tile.h"
21 : #include "../../flamenco/accdb/fd_accdb_sync.h"
22 : #include "../../flamenco/accdb/fd_accdb_pipe.h"
23 : #include "../../flamenco/leaders/fd_multi_epoch_leaders.h"
24 : #include "../../flamenco/runtime/fd_bank.h"
25 : #include "../../flamenco/runtime/program/vote/fd_vote_state_versioned.h"
26 : #include "../../util/pod/fd_pod.h"
27 :
28 : #include <errno.h>
29 : #include <fcntl.h>
30 : #include <unistd.h>
31 :
32 : /* The Tower tile broadly processes three classes of frags, leading to
33 : three distinct kinds of frag processing:
34 :
35 : 1. Processing vote _accounts_ (after replaying a block)
36 :
37 : When Replay finishes executing a block, Tower reads back the vote
38 : account state for every staked validator. This is deterministic:
39 : the vote account state is the result of executing all vote txns in
40 : the block through the vote program, so it is guaranteed to
41 : converge with Agave's view of the same accounts. Tower uses these
42 : accounts to run the fork choice rule (fd_ghost) and TowerBFT
43 : (fd_tower).
44 :
45 : 2. Processing vote _transactions_ (at arbitrary points in time)
46 :
47 : Tower also receives vote txns from Gossip and TPU. These arrive
48 : at arbitrary, nondeterministic times because Gossip and TPU are
49 : both unreliable mediums: there's no guarantee we observe all the
50 : same vote txns as Agave (nor another Firedancer, for that matter).
51 :
52 : Tower is stricter than Agave when validating these vote txns (e.g.
53 : we use is_simple_vote which requires at most two signers, whereas
54 : Agave's Gossip vote parser does not). Being stricter is
55 : acceptable given vote txns from Gossip and TPU are inherently
56 : unreliable, so dropping a small number of votes that Agave allows
57 : but Firedancer does not is not significant to convergence.
58 :
59 : However, these same vote txns are (redundantly) transmitted as
60 : part of a block as well ie. through Replay. The validation of
61 : these Replay-sourced vote txns _is_ one-to-one with Agave (namely
62 : the Vote Program), and critical for convergence. Specifically, we
63 : only process Replay vote txns that have been successfully executed
64 : when counting them towards confirmations.
65 :
66 : This provides an "eventually-consistent" model, specifically that
67 : even though individual Gossip or TPU vote txns may be lost, we are
68 : guaranteed to "eventually" confirm a block and converge with Agave
69 : as long as we replay the block and its descendants, because our
70 : vote programs match 1-1. Gossip / TPU provide an optimization for
71 : earlier confirmations as well as a source of security via
72 : redundancy, in case we are not receiving block data from the rest
73 : of the network.
74 :
75 : The processing of vote txns is important to (as already alluded)
76 : confirmations (fd_notar) and hard fork detection (fd_hfork).
77 :
78 : 3. Processing "other" frags. Vote account and vote transaction
79 : processing (1 and 2 above) is the meat and potatoes, but Tower also
80 : processes several auxiliary frag types:
81 :
82 : a. Shreds (from the shred tile): Tower checks incoming shreds for
83 : equivocation via fd_eqvoc. If two conflicting shreds are
84 : detected for the same FEC set, Tower constructs a duplicate
85 : proof and publishes it (FD_TOWER_SIG_SLOT_DUPLICATE).
86 :
87 : b. Duplicate shred gossip messages (from the gossip tile): Tower
88 : receives duplicate shred proofs from other validators via
89 : gossip. These proofs arrive in chunks (fd_eqvoc_chunk_insert)
90 : and are reassembled and cryptographically verified before being
91 : accepted.
92 :
93 : c. Epoch stake updates (from the replay tile): Tower receives
94 : epoch stake information to maintain the leader schedule via
95 : fd_stake_ci, which is needed by eqvoc for signature
96 : verification of shred proofs.
97 :
98 : d. Shred version (from the ipecho tile): Tower receives the shred
99 : version from ipecho to configure eqvoc's shred version
100 : filtering for proof verification.
101 :
102 : e. Slot dead (from the replay tile): Tower records a NULL bank
103 : hash for dead slots in the hard fork detector (fd_hfork).
104 :
105 : Tower signals to other tiles about events that occur as a result of
106 : those three modes, such as what block to vote on, what block to reset
107 : onto as leader, what block got rooted, what blocks are duplicates,
108 : and what blocks are confirmed.
109 :
110 : In general, Tower uses "block_id" as the identifier for a block. The
111 : block_id is the merkle root of the last FEC set for a block. Unlike
112 : slot numbers, this is guaranteed to be unique for a given block and
113 : is therefore a canonical identifier because slot numbers can identify
114 : multiple blocks, if a leader equivocates (produces multiple blocks
115 : for the same slot), whereas it is not feasible for a leader to
116 : produce block_id collisions.
117 :
118 : However, the block_id was only introduced into the Solana protocol
119 : recently, and TowerBFT still uses the "legacy" identifier of slot
120 : numbers for blocks. So the tile (and relevant modules) will use
121 : block_id when possible to interface with the protocol but otherwise
122 : fallback to slot number when block_id is unsupported due to limits of
123 : the protocol. */
124 :
125 : #define LOGGING 0
126 :
127 0 : #define IN_KIND_DEDUP (0)
128 0 : #define IN_KIND_EPOCH (1)
129 0 : #define IN_KIND_REPLAY (2)
130 0 : #define IN_KIND_GOSSIP (3)
131 0 : #define IN_KIND_IPECHO (4)
132 0 : #define IN_KIND_SHRED (5)
133 :
134 0 : #define OUT_IDX 0 /* only a single out link tower_out */
135 0 : #define AUTH_VTR_LG_MAX (5) /* The Solana Vote Interface supports up to 32 authorized voters. */
136 : FD_STATIC_ASSERT( 1<<AUTH_VTR_LG_MAX==32, AUTH_VTR_LG_MAX );
137 :
138 : /* Tower processes at most 2 equivocating blocks for a given slot: the
139 : first block is the first one we observe for a slot, and the second
140 : block is the one that gets duplicate confirmed. Most of the time,
141 : they are the same (ie. the block we first saw is the block that gets
142 : duplicate confirmed), but we size for the worst case which is every
143 : block in slot_max equivocates and we always see 2 blocks for every
144 : slot. */
145 :
146 0 : #define EQVOC_MAX (2)
147 :
148 : /* The Alpenglow VAT caps the voting set of validators to 2000. Only
149 : the top 2000 voters by stake will be counted towards consensus rules.
150 : Firedancer uses the same bound for TowerBFT.
151 :
152 : https://github.com/solana-foundation/solana-improvement-documents/blob/main/proposals/0357-alpenglow_validator_admission_ticket.md */
153 :
154 0 : #define VTR_MAX (2000) /* the maximum # of unique voters ie. node pubkeys. */
155 :
156 : /* PER_VTR_MAX controls how many "entries" a validator is allowed to
157 : occupy in various vote-tracking structures. This is set somewhat
158 : arbitrarily based on expected worst-case usage by an honest validator
159 : and is set to guard against a malicious spamming validator attempting
160 : to fill up Firedancer structures. */
161 :
162 0 : #define PER_VTR_MAX (512) /* the maximum amount of slot history the sysvar retains */
163 :
164 : struct publish {
165 : ulong sig;
166 : fd_tower_msg_t msg;
167 : };
168 : typedef struct publish publish_t;
169 :
170 : #define DEQUE_NAME publishes
171 0 : #define DEQUE_T publish_t
172 : #include "../../util/tmpl/fd_deque_dynamic.c"
173 :
174 : struct auth_vtr {
175 : fd_pubkey_t addr; /* map key, vote account address */
176 : uint hash; /* reserved for use by fd_map */
177 : ulong paths_idx; /* index in authorized voter paths */
178 : };
179 : typedef struct auth_vtr auth_vtr_t;
180 :
181 : #define MAP_NAME auth_vtr
182 0 : #define MAP_T auth_vtr_t
183 0 : #define MAP_LG_SLOT_CNT AUTH_VTR_LG_MAX
184 0 : #define MAP_KEY addr
185 0 : #define MAP_KEY_T fd_pubkey_t
186 0 : #define MAP_KEY_NULL (fd_pubkey_t){0}
187 0 : #define MAP_KEY_EQUAL(k0,k1) (!(memcmp((k0).key,(k1).key,sizeof(fd_pubkey_t))))
188 0 : #define MAP_KEY_INVAL(k) (MAP_KEY_EQUAL((k),MAP_KEY_NULL))
189 : #define MAP_KEY_EQUAL_IS_SLOW 1
190 0 : #define MAP_KEY_HASH(k) ((uint)fd_ulong_hash( fd_ulong_load_8( (k).uc ) ))
191 : #include "../../util/tmpl/fd_map.c"
192 :
193 : #define AUTH_VOTERS_MAX (16UL)
194 :
195 : typedef struct {
196 : int mcache_only;
197 : fd_wksp_t * mem;
198 : ulong chunk0;
199 : ulong wmark;
200 : ulong mtu;
201 : } in_ctx_t;
202 :
203 : struct fd_tower_tile {
204 : ulong seed; /* map seed */
205 : int checkpt_fd;
206 : int restore_fd;
207 : fd_pubkey_t identity_key[1];
208 : fd_pubkey_t vote_account[1];
209 : ulong auth_vtr_path_cnt; /* number of authorized voter paths passed to tile */
210 : uchar our_vote_acct[FD_VOTE_STATE_DATA_MAX]; /* buffer for reading back our own vote acct data */
211 : ulong our_vote_acct_sz;
212 :
213 : /* owned joins */
214 :
215 : fd_wksp_t * wksp; /* workspace */
216 : fd_keyswitch_t * identity_keyswitch;
217 : auth_vtr_t * auth_vtr;
218 : fd_keyswitch_t * auth_vtr_keyswitch; /* authorized voter keyswitch */
219 :
220 : fd_eqvoc_t * eqvoc;
221 : fd_ghost_t * ghost;
222 : fd_hfork_t * hfork;
223 : fd_votes_t * votes;
224 : fd_tower_t * tower;
225 :
226 : fd_tower_t * scratch_tower; /* spare tower used during processing */
227 : fd_tower_blocks_t * tower_blocks;
228 : fd_tower_lockos_t * tower_lockos;
229 : fd_tower_stakes_t * tower_stakes; /* tracks the stakes for each voter in the epoch per fork */
230 : fd_tower_voters_t * tower_voters;
231 : uchar * voter_towers; /* pre-allocated array of vtr_max towers (each FD_TOWER_FOOTPRINT bytes) */
232 :
233 : publish_t * publishes; /* deque of slot_confirmed msgs queued for publishing */
234 : fd_multi_epoch_leaders_t * mleaders; /* multi-epoch leaders */
235 :
236 : /* borrowed joins */
237 :
238 : fd_banks_t * banks;
239 : fd_accdb_user_t accdb[1];
240 :
241 : /* static structures */
242 :
243 : fd_gossip_duplicate_shred_t chunks[FD_EQVOC_CHUNK_CNT];
244 : fd_compact_tower_sync_serde_t compact_tower_sync_serde;
245 : uchar vote_txn[FD_TPU_PARSED_MTU];
246 :
247 : uchar __attribute__((aligned(FD_MULTI_EPOCH_LEADERS_ALIGN))) mleaders_mem[ FD_MULTI_EPOCH_LEADERS_FOOTPRINT ];
248 :
249 : /* metadata */
250 :
251 : int halt_signing;
252 : int hard_fork_fatal;
253 : ushort shred_version;
254 : ulong init_slot; /* initial slot (either genesis or snapshot slot) */
255 : ulong root_slot; /* monotonically increasing contiguous root slot */
256 :
257 : /* in/out link setup */
258 :
259 : int in_kind[ 64UL ];
260 : in_ctx_t in [ 64UL ];
261 :
262 : fd_wksp_t * out_mem;
263 : ulong out_chunk0;
264 : ulong out_wmark;
265 : ulong out_chunk;
266 : ulong out_seq;
267 :
268 : /* metrics */
269 :
270 : struct {
271 :
272 : ulong ignored_cnt;
273 : ulong ignored_slot;
274 : ulong eqvoc_cnt;
275 : ulong eqvoc_slot;
276 :
277 : ulong replay_slot;
278 : ulong last_vote_slot;
279 : ulong reset_slot;
280 : ulong root_slot;
281 : ulong init_slot;
282 :
283 : ulong ancestor_rollback;
284 : ulong sibling_confirmed;
285 : ulong same_fork;
286 : ulong switch_pass;
287 : ulong switch_fail;
288 : ulong lockout_fail;
289 : ulong threshold_fail;
290 : ulong propagated_fail;
291 :
292 : ulong txn_bad_deser;
293 : ulong txn_bad_tower;
294 : ulong txn_not_tower_sync;
295 : ulong txn_empty_tower;
296 : ulong txn_not_ready;
297 :
298 : ulong votes_too_old;
299 : ulong votes_too_new;
300 : ulong votes_unknown_vtr;
301 : ulong votes_already_voted;
302 : ulong votes_unknown_slot;
303 : ulong votes_unknown_block_id;
304 :
305 : ulong eqvoc_success_merkle;
306 : ulong eqvoc_success_meta;
307 : ulong eqvoc_success_last;
308 : ulong eqvoc_success_overlap;
309 : ulong eqvoc_success_chained;
310 :
311 : ulong eqvoc_err_serde;
312 : ulong eqvoc_err_slot;
313 : ulong eqvoc_err_version;
314 : ulong eqvoc_err_type;
315 : ulong eqvoc_err_merkle;
316 : ulong eqvoc_err_signature;
317 :
318 : ulong eqvoc_err_chunk_cnt;
319 : ulong eqvoc_err_chunk_idx;
320 : ulong eqvoc_err_chunk_len;
321 :
322 : ulong eqvoc_err_ignored_from;
323 : ulong eqvoc_err_ignored_slot;
324 :
325 : ulong eqvoc_proof_constructed;
326 : ulong eqvoc_proof_verified;
327 :
328 : ulong ghost_not_voted;
329 : ulong ghost_too_old;
330 : ulong ghost_already_voted;
331 :
332 : ulong hfork_unknown_vtr;
333 : ulong hfork_already_voted;
334 : ulong hfork_too_old;
335 :
336 : ulong hfork_matched_slot;
337 : ulong hfork_mismatched_slot;
338 : } metrics;
339 : };
340 : typedef struct fd_tower_tile fd_tower_tile_t;
341 :
342 : static void
343 : update_metrics_eqvoc( fd_tower_tile_t * ctx,
344 0 : int err ) {
345 0 : switch( err ) {
346 :
347 0 : case FD_EQVOC_SUCCESS: break;
348 :
349 0 : case FD_EQVOC_SUCCESS_MERKLE: ctx->metrics.eqvoc_success_merkle++; break;
350 0 : case FD_EQVOC_SUCCESS_META: ctx->metrics.eqvoc_success_meta++; break;
351 0 : case FD_EQVOC_SUCCESS_LAST: ctx->metrics.eqvoc_success_last++; break;
352 0 : case FD_EQVOC_SUCCESS_OVERLAP: ctx->metrics.eqvoc_success_overlap++; break;
353 0 : case FD_EQVOC_SUCCESS_CHAINED: ctx->metrics.eqvoc_success_chained++; break;
354 :
355 0 : case FD_EQVOC_ERR_SERDE: ctx->metrics.eqvoc_err_serde++; break;
356 0 : case FD_EQVOC_ERR_SLOT: ctx->metrics.eqvoc_err_slot++; break;
357 0 : case FD_EQVOC_ERR_VERSION: ctx->metrics.eqvoc_err_version++; break;
358 0 : case FD_EQVOC_ERR_TYPE: ctx->metrics.eqvoc_err_type++; break;
359 0 : case FD_EQVOC_ERR_MERKLE: ctx->metrics.eqvoc_err_merkle++; break;
360 0 : case FD_EQVOC_ERR_SIG: ctx->metrics.eqvoc_err_signature++; break;
361 :
362 0 : case FD_EQVOC_ERR_CHUNK_CNT: ctx->metrics.eqvoc_err_chunk_cnt++; break;
363 0 : case FD_EQVOC_ERR_CHUNK_IDX: ctx->metrics.eqvoc_err_chunk_idx++; break;
364 0 : case FD_EQVOC_ERR_CHUNK_LEN: ctx->metrics.eqvoc_err_chunk_len++; break;
365 :
366 0 : case FD_EQVOC_ERR_IGNORED_FROM: ctx->metrics.eqvoc_err_ignored_from++; break;
367 0 : case FD_EQVOC_ERR_IGNORED_SLOT: ctx->metrics.eqvoc_err_ignored_slot++; break;
368 :
369 0 : default: FD_LOG_ERR(( "unhandled eqvoc err %d", err ));
370 0 : }
371 0 : }
372 :
373 : static void
374 : update_metrics_hfork( fd_tower_tile_t * ctx,
375 : int hfork_err,
376 : ulong slot,
377 0 : fd_hash_t const * block_id ) {
378 0 : switch( hfork_err ) {
379 0 : case FD_HFORK_SUCCESS_MATCHED: ctx->metrics.hfork_matched_slot = fd_ulong_max( ctx->metrics.hfork_matched_slot, slot ); break;
380 0 : case FD_HFORK_SUCCESS: break;
381 0 : case FD_HFORK_ERR_MISMATCHED: {
382 0 : ctx->metrics.hfork_mismatched_slot = fd_ulong_max( ctx->metrics.hfork_mismatched_slot, slot );
383 0 : FD_BASE58_ENCODE_32_BYTES( block_id->uc, _block_id );
384 0 : if( FD_UNLIKELY( ctx->hard_fork_fatal ) ) FD_LOG_ERR (( "HARD FORK DETECTED for slot %lu block ID `%s`", slot, _block_id ));
385 0 : else FD_LOG_WARNING(( "HARD FORK DETECTED for slot %lu block ID `%s`", slot, _block_id ));
386 0 : break;
387 0 : }
388 0 : case FD_HFORK_ERR_UNKNOWN_VTR: ctx->metrics.hfork_unknown_vtr++; break;
389 0 : case FD_HFORK_ERR_ALREADY_VOTED: ctx->metrics.hfork_already_voted++; break;
390 0 : case FD_HFORK_ERR_VOTE_TOO_OLD: ctx->metrics.hfork_too_old++; break;
391 0 : default: FD_LOG_ERR(( "unhandled hfork_err %d", hfork_err ));
392 0 : }
393 0 : }
394 :
395 : static void
396 : update_metrics_votes( fd_tower_tile_t * ctx,
397 0 : int err ) {
398 0 : ctx->metrics.votes_too_new += (ulong)(err==FD_VOTES_ERR_VOTE_TOO_NEW);
399 0 : ctx->metrics.votes_unknown_vtr += (ulong)(err==FD_VOTES_ERR_UNKNOWN_VTR);
400 0 : ctx->metrics.votes_already_voted += (ulong)(err==FD_VOTES_ERR_ALREADY_VOTED);
401 0 : }
402 :
403 : static void
404 : update_metrics_ghost( fd_tower_tile_t * ctx,
405 0 : int err ) {
406 0 : ctx->metrics.ghost_not_voted += (ulong)(err==FD_GHOST_ERR_NOT_VOTED);
407 0 : ctx->metrics.ghost_too_old += (ulong)(err==FD_GHOST_ERR_VOTE_TOO_OLD);
408 0 : ctx->metrics.ghost_already_voted += (ulong)(err==FD_GHOST_ERR_ALREADY_VOTED);
409 0 : }
410 :
411 : static void
412 : confirm_block( fd_tower_tile_t * ctx,
413 : ulong slot,
414 : fd_hash_t const * block_id,
415 0 : ulong total_stake ) {
416 :
417 0 : fd_tower_blk_t * tower_blk = fd_tower_blocks_query( ctx->tower_blocks, slot );
418 0 : fd_ghost_blk_t * ghost_blk = fd_ghost_query( ctx->ghost, block_id );
419 0 : fd_votes_blk_t * votes_blk = fd_votes_query( ctx->votes, slot, block_id );
420 0 : if( FD_UNLIKELY( !votes_blk ) ) return;
421 :
422 0 : static double const ratios[FD_TOWER_SLOT_CONFIRMED_LEVEL_CNT] = FD_TOWER_SLOT_CONFIRMED_RATIOS;
423 0 : int const levels[FD_TOWER_SLOT_CONFIRMED_LEVEL_CNT] = FD_TOWER_SLOT_CONFIRMED_LEVELS;
424 0 : for( int i = 0; i < FD_TOWER_SLOT_CONFIRMED_LEVEL_CNT; i++ ) {
425 0 : if( FD_LIKELY( fd_uchar_extract_bit( votes_blk->flags, i ) ) ) continue; /* already contiguously confirmed */
426 0 : double ratio = (double)votes_blk->stake / (double)total_stake;
427 0 : if( FD_LIKELY( ratio < ratios[i] ) ) break; /* threshold not met */
428 :
429 : /* If ghost is missing, then we know this is a forward
430 : confirmation (ie. we haven't replayed the block yet). */
431 :
432 0 : if( FD_UNLIKELY( !ghost_blk ) ) {
433 0 : if( fd_uchar_extract_bit( votes_blk->flags, i+4 ) ) continue; /* already forward confirmed */
434 0 : votes_blk->flags = fd_uchar_set_bit( votes_blk->flags, i+4 );
435 0 : publishes_push_head( ctx->publishes, (publish_t){ .sig = FD_TOWER_SIG_SLOT_CONFIRMED, .msg = { .slot_confirmed = (fd_tower_slot_confirmed_t){ .level = levels[i], .fwd = 1, .slot = votes_blk->key.slot, .block_id = votes_blk->key.block_id } } } );
436 :
437 : /* If we have a tower_blk for the slot, but not a ghost blk, this
438 : implies we replayed one version of the slot that is not the
439 : same as the one getting confirmed. */
440 :
441 0 : if( FD_LIKELY( tower_blk ) ) {
442 0 : FD_TEST( 0!=memcmp( &tower_blk->replayed_block_id, &votes_blk->key.block_id, sizeof(fd_hash_t) ) );
443 0 : tower_blk->confirmed = 1;
444 0 : tower_blk->confirmed_block_id = votes_blk->key.block_id;
445 0 : }
446 0 : continue;
447 0 : }
448 :
449 : /* Otherwise if they are present, then we know this is not a forward
450 : confirmation and thus we have replayed and confirmed the block,
451 : which also implies we have replayed and confirmed its ancestry.
452 : So publish confirmations for all ancestors (short-circuit at the
453 : first ancestor that was already confirmed).
454 :
455 : We use ghost to walk up the ancestry and also mark ghost and
456 : tower blocks as confirmed as we walk if this is the duplicate
457 : confirmation level. */
458 :
459 0 : fd_ghost_blk_t * ghost_anc = ghost_blk;
460 0 : fd_tower_blk_t * tower_anc = tower_blk;
461 0 : fd_votes_blk_t * votes_anc = votes_blk;
462 0 : while( FD_LIKELY( ghost_anc ) ) {
463 :
464 0 : tower_anc = fd_tower_blocks_query( ctx->tower_blocks, ghost_anc->slot );
465 0 : votes_anc = fd_votes_query( ctx->votes, ghost_anc->slot, &ghost_anc->id );
466 0 : if( FD_UNLIKELY( !tower_anc || !votes_anc ) ) break;
467 :
468 : /* Terminate at the first ancestor that has already reached this
469 : confirmation level. */
470 :
471 0 : if( FD_LIKELY( fd_uchar_extract_bit( votes_anc->flags, i ) ) ) break;
472 :
473 : /* Mark the ancestor as confirmed at this level. If this is the
474 : duplicate confirmation level, also mark the ghost and tower
475 : blocks as confirmed. */
476 :
477 0 : votes_anc->flags = fd_uchar_set_bit( votes_anc->flags, i );
478 0 : publishes_push_head( ctx->publishes, (publish_t){ .sig = FD_TOWER_SIG_SLOT_CONFIRMED, .msg = { .slot_confirmed = (fd_tower_slot_confirmed_t){ .level = levels[i], .fwd = 0, .slot = ghost_anc->slot, .block_id = ghost_anc->id } } } );
479 0 : if( FD_UNLIKELY( levels[i]==FD_TOWER_SLOT_CONFIRMED_PROPAGATED ) ) {
480 0 : tower_anc->propagated = 1;
481 0 : }
482 0 : if( FD_UNLIKELY( levels[i]==FD_TOWER_SLOT_CONFIRMED_DUPLICATE ) ) {
483 0 : tower_anc->confirmed = 1;
484 0 : tower_anc->confirmed_block_id = ghost_anc->id;
485 0 : }
486 :
487 : /* Walk up to next ancestor. */
488 :
489 0 : ghost_anc = fd_ghost_parent( ctx->ghost, ghost_anc );
490 0 : }
491 0 : }
492 0 : }
493 :
494 : /* count_vote counts vote txns from Gossip, TPU and Replay. Note these
495 : txns have already been parsed and sigverified before they are sent to
496 : tower. Replay votes have been successfully executed. They are
497 : counted towards votes and hfork (see point 2 in the top-level
498 : documentation). */
499 :
500 : static void
501 : count_vote( fd_tower_tile_t * ctx,
502 : fd_txn_t const * txn,
503 24 : uchar const * payload ) {
504 :
505 24 : if( FD_UNLIKELY( ctx->root_slot==ULONG_MAX ) ) { ctx->metrics.txn_not_ready++; return; }
506 :
507 : /* We are a little stricter than Agave here when validating the vote
508 : because we use the same validation as pack ie. is_simple_vote which
509 : includes a check that there are at most two signers, whereas
510 : Agave's gossip vote parser does not perform that same check (the
511 : only two signers are the identity key and vote authority, which may
512 : optionally be the same).
513 :
514 : Being a little stricter here is ok because even if we drop some
515 : votes with extraneous signers that Agave would consider valid
516 : (unlikely), gossip votes are in general considered unreliable and
517 : ultimately consensus is reached through replaying the vote txns.
518 :
519 : The remaining checks mirror Agave as closely as possible (and are
520 : documented throughout below). */
521 :
522 24 : if( FD_UNLIKELY( !fd_txn_is_simple_vote_transaction( txn, payload ) ) ) return;
523 :
524 : /* TODO check the authorized voter for this vote account (from epoch
525 : stakes) is one of the signers. */
526 :
527 : /* Filter any non-TowerSync vote txns. */
528 :
529 : /* TODO SECURITY ensure SIMD-0138 is activated */
530 :
531 24 : fd_txn_instr_t const * instr = &txn->instr[0];
532 24 : uchar const * instr_data = payload + instr->data_off;
533 24 : if( FD_UNLIKELY( instr->data_sz<4 ) ) { ctx->metrics.txn_bad_deser++; return; }
534 :
535 24 : uint kind = fd_uint_load_4_fast( instr_data );
536 24 : if( FD_UNLIKELY( kind!=FD_VOTE_IX_KIND_TOWER_SYNC && kind!=FD_VOTE_IX_KIND_TOWER_SYNC_SWITCH ) ) { ctx->metrics.txn_not_tower_sync++; return; };
537 :
538 : /* Deserialize the TowerSync out of the vote txn. */
539 :
540 24 : int err = fd_compact_tower_sync_de( &ctx->compact_tower_sync_serde, instr_data + sizeof(uint), instr->data_sz - sizeof(uint) );
541 24 : if( FD_UNLIKELY( err==-1 ) ) { ctx->metrics.txn_bad_deser++; return; }
542 24 : ulong slot = ctx->compact_tower_sync_serde.root;
543 24 : fd_tower_remove_all( ctx->scratch_tower );
544 150 : for( ulong i = 0; i < ctx->compact_tower_sync_serde.lockouts_cnt; i++ ) {
545 126 : slot += ctx->compact_tower_sync_serde.lockouts[i].offset;
546 126 : fd_tower_push_tail( ctx->scratch_tower, (fd_tower_vote_t){ .slot = slot, .conf = ctx->compact_tower_sync_serde.lockouts[i].confirmation_count } );
547 126 : }
548 :
549 : /* Return early if their tower is empty. */
550 :
551 24 : if( FD_UNLIKELY( fd_tower_empty( ctx->scratch_tower ) ) ) { ctx->metrics.txn_empty_tower++; return; };
552 :
553 : /* Validate the tower. */
554 :
555 21 : fd_tower_vote_t const * prev = fd_tower_peek_head_const( ctx->scratch_tower );
556 21 : if( FD_UNLIKELY( prev->conf > FD_TOWER_VOTE_MAX ) ) { ctx->metrics.txn_bad_tower++; return; }
557 :
558 18 : fd_tower_iter_t iter = fd_tower_iter_next( ctx->scratch_tower, fd_tower_iter_init( ctx->scratch_tower ) );
559 117 : for( ; !fd_tower_iter_done( ctx->scratch_tower, iter ); iter = fd_tower_iter_next( ctx->scratch_tower, iter ) ) {
560 105 : fd_tower_vote_t const * vote = fd_tower_iter_ele( ctx->scratch_tower, iter );
561 105 : if( FD_UNLIKELY( vote->slot <= prev->slot ) ) { ctx->metrics.txn_bad_tower++; return; }
562 105 : if( FD_UNLIKELY( vote->conf >= prev->conf ) ) { ctx->metrics.txn_bad_tower++; return; }
563 99 : if( FD_UNLIKELY( vote->conf > FD_TOWER_VOTE_MAX ) ) { ctx->metrics.txn_bad_tower++; return; }
564 99 : prev = vote;
565 99 : }
566 :
567 12 : if( FD_UNLIKELY( 0==memcmp( &ctx->compact_tower_sync_serde.block_id, &hash_null, sizeof(fd_hash_t) ) ) ) { ctx->metrics.votes_unknown_block_id++; return; };
568 :
569 0 : fd_pubkey_t const * accs = (fd_pubkey_t const *)fd_type_pun_const( payload + txn->acct_addr_off );
570 0 : fd_pubkey_t const * vote_acc = NULL;
571 0 : if( FD_UNLIKELY( txn->signature_cnt==1 ) ) vote_acc = (fd_pubkey_t const *)fd_type_pun_const( &accs[1] ); /* identity and authority same, account idx 1 is the vote account address */
572 0 : else vote_acc = (fd_pubkey_t const *)fd_type_pun_const( &accs[2] ); /* identity and authority diff, account idx 2 is the vote account address */
573 :
574 : /* The vote txn contains a block id and bank hash for their last vote
575 : slot in the tower. Agave always counts the last vote.
576 :
577 : https://github.com/anza-xyz/agave/blob/v2.3.7/core/src/cluster_info_vote_listener.rs#L476-L487 */
578 :
579 0 : fd_tower_vote_t const * their_last_vote = fd_tower_peek_tail_const( ctx->scratch_tower );
580 0 : fd_hash_t const * their_block_id = &ctx->compact_tower_sync_serde.block_id;
581 0 : fd_hash_t const * their_bank_hash = &ctx->compact_tower_sync_serde.hash;
582 :
583 : /* Return early if their last vote is too old. */
584 :
585 0 : if( FD_UNLIKELY( their_last_vote->slot <= ctx->root_slot ) ) { ctx->metrics.votes_too_old++; return; }
586 :
587 : /* Similar to what Agave does in cluster_info_vote_listener, we use
588 : the stake associated with a vote account as of our current root's
589 : epoch (which could potentially be a different epoch than the vote
590 : we are counting or when we observe the vote). They default stake
591 : to 0 for voters who are not found. */
592 :
593 0 : fd_tower_stakes_vtr_xid_t xid = { .addr = *vote_acc, .slot = ctx->root_slot };
594 0 : fd_tower_stakes_vtr_t * vtr = fd_tower_stakes_vtr_map_ele_query( ctx->tower_stakes->vtr_map, &xid, NULL, ctx->tower_stakes->vtr_pool );
595 0 : if( FD_UNLIKELY( !vtr ) ) return; /* voter is not staked in current root's epoch */
596 :
597 0 : ulong their_stake = vtr->stake;
598 0 : ulong total_stake = fd_ghost_root( ctx->ghost )->total_stake;
599 :
600 0 : int hfork_err = fd_hfork_count_vote( ctx->hfork, vote_acc, their_block_id, their_bank_hash, their_last_vote->slot, their_stake, total_stake );
601 0 : update_metrics_hfork( ctx, hfork_err, their_last_vote->slot, their_block_id );
602 :
603 0 : int votes_err = fd_votes_count_vote( ctx->votes, vote_acc, their_last_vote->slot, their_block_id );
604 0 : update_metrics_votes( ctx, votes_err );
605 0 : if( FD_LIKELY( votes_err==FD_VOTES_SUCCESS ) ) confirm_block( ctx, their_last_vote->slot, their_block_id, total_stake );
606 :
607 : /* Agave decides to count intermediate vote slots in the tower only if
608 : 1. they've replayed the slot and 2. their replay bank hash matches
609 : the vote's bank hash. We do the same thing, but using block_ids
610 : instead of bank hashes.
611 :
612 : It's possible we haven't yet replayed this slot being voted on
613 : because gossip votes can be ahead of our replay.
614 :
615 : https://github.com/anza-xyz/agave/blob/v2.3.7/core/src/cluster_info_vote_listener.rs#L483-L487 */
616 :
617 0 : if( FD_UNLIKELY( !fd_tower_blk_query( ctx->tower_blocks->blk_map, their_last_vote->slot, NULL ) ) ) { ctx->metrics.votes_unknown_slot++; return; }; /* we haven't replayed this block yet */
618 0 : fd_hash_t const * our_block_id = fd_tower_blocks_canonical_block_id( ctx->tower_blocks, their_last_vote->slot );
619 0 : if( FD_UNLIKELY( 0!=memcmp( our_block_id, their_block_id, sizeof(fd_hash_t) ) ) ) { ctx->metrics.votes_unknown_block_id++; return; } /* we don't recognize this block id */
620 :
621 : /* At this point, we know we have replayed the same slot and also have
622 : a matching block id, so we can count the intermediate votes. */
623 :
624 0 : int skipped_last_vote = 0;
625 0 : for( fd_tower_iter_t iter = fd_tower_iter_init_rev( ctx->scratch_tower );
626 0 : !fd_tower_iter_done_rev( ctx->scratch_tower, iter );
627 0 : iter = fd_tower_iter_prev ( ctx->scratch_tower, iter ) ) {
628 0 : if( FD_UNLIKELY( !skipped_last_vote ) ) { skipped_last_vote = 1; continue; }
629 0 : fd_tower_vote_t const * their_intermediate_vote = fd_tower_iter_ele_const( ctx->scratch_tower, iter );
630 :
631 : /* If we don't recognize an intermediate vote slot in their tower,
632 : it means their tower either:
633 :
634 : 1. Contains intermediate vote slots that are too old (older than
635 : our root) so we already pruned them for tower_forks. Normally
636 : if the descendant (last vote slot) is in tower forks, then all
637 : of its ancestors should be in there too.
638 :
639 : 2. Is invalid. Even though at this point we have successfully
640 : sigverified and deserialized their vote txn, the tower itself
641 : might still be invalid because unlike TPU vote txns, we have
642 : not plumbed through the vote program, but obviously gossip
643 : votes do not so we need to do some light validation here.
644 :
645 : We could throwaway this voter's tower, but we handle it the same
646 : way as Agave which is to just skip this intermediate vote slot:
647 :
648 : https://github.com/anza-xyz/agave/blob/v2.3.7/core/src/cluster_info_vote_listener.rs#L513-L518 */
649 :
650 0 : if( FD_UNLIKELY( their_intermediate_vote->slot <= ctx->root_slot ) ) { ctx->metrics.votes_too_old++; continue; }
651 :
652 0 : fd_tower_blk_t * tower_blk = fd_tower_blocks_query( ctx->tower_blocks, their_intermediate_vote->slot );
653 0 : if( FD_UNLIKELY( !tower_blk ) ) { ctx->metrics.votes_unknown_slot++; continue; }
654 :
655 : /* Otherwise, we count the vote using our own block id for that slot
656 : (again, mirroring what Agave does albeit with bank hashes).
657 :
658 : Agave uses the current root bank's total stake when counting vote
659 : txns from gossip / replay:
660 :
661 : https://github.com/anza-xyz/agave/blob/v2.3.7/core/src/cluster_info_vote_listener.rs#L500 */
662 :
663 0 : fd_hash_t const * intermediate_block_id = fd_tower_blocks_canonical_block_id( ctx->tower_blocks, their_intermediate_vote->slot );
664 0 : int votes_err = fd_votes_count_vote( ctx->votes, vote_acc, their_intermediate_vote->slot, intermediate_block_id );
665 0 : update_metrics_votes( ctx, votes_err );
666 0 : if( FD_LIKELY( votes_err==FD_VOTES_SUCCESS ) ) confirm_block( ctx, their_intermediate_vote->slot, intermediate_block_id, total_stake );
667 0 : }
668 0 : }
669 :
670 : static int
671 : deser_auth_vtr( fd_tower_tile_t * ctx,
672 : ulong epoch,
673 : int vote_acc_found,
674 : fd_pubkey_t * authority_out,
675 0 : ulong * authority_idx_out ) {
676 :
677 0 : if( FD_UNLIKELY( !vote_acc_found ) ) return 0;
678 :
679 0 : fd_bincode_decode_ctx_t decode_ctx = {
680 0 : .data = ctx->our_vote_acct,
681 0 : .dataend = ctx->our_vote_acct + ctx->our_vote_acct_sz,
682 0 : };
683 :
684 0 : uchar __attribute__((aligned(FD_VOTE_STATE_VERSIONED_ALIGN))) vote_state_versioned[ FD_VOTE_STATE_VERSIONED_FOOTPRINT ];
685 :
686 0 : fd_vote_state_versioned_t * vsv = fd_vote_state_versioned_decode( vote_state_versioned, &decode_ctx );
687 0 : FD_CRIT( vsv, "unable to decode vote state versioned" );
688 :
689 0 : fd_pubkey_t const * auth_vtr_addr = NULL;
690 0 : switch( vsv->discriminant ) {
691 0 : case fd_vote_state_versioned_enum_v1_14_11:
692 0 : for( fd_vote_authorized_voters_treap_rev_iter_t iter = fd_vote_authorized_voters_treap_rev_iter_init( vsv->inner.v1_14_11.authorized_voters.treap, vsv->inner.v1_14_11.authorized_voters.pool );
693 0 : !fd_vote_authorized_voters_treap_rev_iter_done( iter );
694 0 : iter = fd_vote_authorized_voters_treap_rev_iter_next( iter, vsv->inner.v1_14_11.authorized_voters.pool ) ) {
695 0 : fd_vote_authorized_voter_t * ele = fd_vote_authorized_voters_treap_rev_iter_ele( iter, vsv->inner.v1_14_11.authorized_voters.pool );
696 0 : if( FD_LIKELY( ele->epoch<=epoch ) ) {
697 0 : auth_vtr_addr = &ele->pubkey;
698 0 : break;
699 0 : }
700 0 : }
701 0 : break;
702 0 : case fd_vote_state_versioned_enum_v3:
703 0 : for( fd_vote_authorized_voters_treap_rev_iter_t iter = fd_vote_authorized_voters_treap_rev_iter_init( vsv->inner.v3.authorized_voters.treap, vsv->inner.v3.authorized_voters.pool );
704 0 : !fd_vote_authorized_voters_treap_rev_iter_done( iter );
705 0 : iter = fd_vote_authorized_voters_treap_rev_iter_next( iter, vsv->inner.v3.authorized_voters.pool ) ) {
706 0 : fd_vote_authorized_voter_t * ele = fd_vote_authorized_voters_treap_rev_iter_ele( iter, vsv->inner.v3.authorized_voters.pool );
707 0 : if( FD_LIKELY( ele->epoch<=epoch ) ) {
708 0 : auth_vtr_addr = &ele->pubkey;
709 0 : break;
710 0 : }
711 0 : }
712 0 : break;
713 0 : case fd_vote_state_versioned_enum_v4:
714 0 : for( fd_vote_authorized_voters_treap_rev_iter_t iter = fd_vote_authorized_voters_treap_rev_iter_init( vsv->inner.v4.authorized_voters.treap, vsv->inner.v4.authorized_voters.pool );
715 0 : !fd_vote_authorized_voters_treap_rev_iter_done( iter );
716 0 : iter = fd_vote_authorized_voters_treap_rev_iter_next( iter, vsv->inner.v4.authorized_voters.pool ) ) {
717 0 : fd_vote_authorized_voter_t * ele = fd_vote_authorized_voters_treap_rev_iter_ele( iter, vsv->inner.v4.authorized_voters.pool );
718 0 : if( FD_LIKELY( ele->epoch<=epoch ) ) {
719 0 : auth_vtr_addr = &ele->pubkey;
720 0 : break;
721 0 : }
722 0 : }
723 0 : break;
724 0 : default:
725 0 : FD_LOG_CRIT(( "unsupported vote state versioned discriminant: %u", vsv->discriminant ));
726 0 : }
727 :
728 0 : FD_CRIT( auth_vtr_addr, "unable to find authorized voter, likely corrupt vote account state" );
729 :
730 0 : if( fd_pubkey_eq( auth_vtr_addr, ctx->identity_key ) ) {
731 0 : *authority_idx_out = ULONG_MAX;
732 0 : *authority_out = *auth_vtr_addr;
733 0 : return 1;
734 0 : }
735 :
736 0 : auth_vtr_t * auth_vtr = auth_vtr_query( ctx->auth_vtr, *auth_vtr_addr, NULL );
737 0 : if( FD_LIKELY( auth_vtr ) ) {
738 0 : *authority_idx_out = auth_vtr->paths_idx;
739 0 : *authority_out = *auth_vtr_addr;
740 0 : return 1;
741 0 : }
742 :
743 0 : return 0;
744 0 : }
745 :
746 : static ulong
747 : update_voters( fd_tower_tile_t * ctx,
748 : ulong bank_idx,
749 0 : ulong slot ) {
750 :
751 0 : fd_bank_t * bank = fd_banks_bank_query( ctx->banks, bank_idx );
752 0 : if( FD_UNLIKELY( !bank ) ) FD_LOG_CRIT(( "invariant violation: bank %lu is missing", bank_idx ));
753 :
754 0 : fd_tower_voters_t * tower_voters = ctx->tower_voters;
755 0 : fd_tower_voters_remove_all( tower_voters );
756 :
757 0 : ulong total_stake = 0UL;
758 0 : ulong prev_voter_idx = ULONG_MAX;
759 :
760 0 : fd_accdb_ro_pipe_t ro_pipe[1];
761 0 : fd_funk_txn_xid_t xid = { .ul = { slot, bank_idx } };
762 0 : fd_accdb_ro_pipe_init( ro_pipe, ctx->accdb, &xid );
763 :
764 0 : fd_top_votes_t const * top_votes_t_2 = fd_bank_top_votes_t_2_query( bank );
765 0 : uchar __attribute__((aligned(FD_TOP_VOTES_ITER_ALIGN))) iter_mem[ FD_TOP_VOTES_ITER_FOOTPRINT ];
766 :
767 0 : ulong pending_cnt = 0UL;
768 0 : fd_top_votes_iter_t * iter = fd_top_votes_iter_init( top_votes_t_2, iter_mem );
769 0 : for(;;) {
770 0 : if( FD_UNLIKELY( fd_top_votes_iter_done( top_votes_t_2, iter ) ) ) {
771 0 : if( !pending_cnt ) break;
772 0 : fd_accdb_ro_pipe_flush( ro_pipe );
773 0 : } else {
774 0 : fd_pubkey_t vote_acc;
775 0 : ulong stake;
776 0 : int is_valid = fd_top_votes_iter_ele( top_votes_t_2, iter, &vote_acc, NULL, &stake, NULL, NULL, NULL );
777 0 : fd_top_votes_iter_next( top_votes_t_2, iter );
778 0 : if( FD_UNLIKELY( !is_valid ) ) continue;
779 :
780 0 : fd_accdb_ro_pipe_enqueue( ro_pipe, vote_acc.key );
781 0 : pending_cnt++;
782 0 : }
783 :
784 0 : fd_accdb_ro_t * ro;
785 0 : while( FD_LIKELY( ro = fd_accdb_ro_pipe_poll( ro_pipe ) ) ) {
786 0 : pending_cnt--;
787 0 : fd_pubkey_t const * vote_acc = fd_accdb_ref_address( ro );
788 :
789 0 : ulong stake;
790 0 : int is_valid = fd_top_votes_query( top_votes_t_2, vote_acc, NULL, &stake, NULL, NULL, NULL );
791 0 : if( FD_UNLIKELY( !is_valid ) ) continue;
792 :
793 0 : FD_TEST( fd_tower_voters_cnt( tower_voters )<=VTR_MAX );
794 0 : FD_TEST( fd_accdb_ref_lamports( ro ) && fd_vsv_is_correct_size_and_initialized( ro->meta ) );
795 :
796 0 : total_stake += stake;
797 :
798 0 : fd_tower_voters_t * acct = fd_tower_voters_push_tail_nocopy( tower_voters );
799 0 : fd_tower_remove_all( acct->tower );
800 0 : fd_tower_from_vote_acc( acct->tower, &acct->root, fd_accdb_ref_data_const( ro ) );
801 0 : acct->id_key = fd_vsv_get_node_account( fd_accdb_ref_data_const( ro ) );
802 0 : acct->vote_acc = *vote_acc;
803 0 : acct->stake = stake;
804 0 : prev_voter_idx = fd_tower_stakes_insert( ctx->tower_stakes, slot, vote_acc, stake, prev_voter_idx );
805 0 : }
806 0 : }
807 :
808 0 : fd_accdb_ro_pipe_fini( ro_pipe );
809 :
810 0 : return total_stake;
811 0 : }
812 :
813 : static void
814 : replay_slot_completed( fd_tower_tile_t * ctx,
815 : fd_replay_slot_completed_t * slot_completed,
816 : ulong tsorig,
817 0 : fd_stem_context_t * stem ) {
818 :
819 : /* Sanity checks. */
820 :
821 0 : FD_TEST( 0!=memcmp( &slot_completed->block_id, &hash_null, sizeof(fd_hash_t) ) );
822 0 : FD_TEST( ctx->init_slot==ULONG_MAX || 0!=memcmp( &slot_completed->block_id, &hash_null, sizeof(fd_hash_t) ) );
823 :
824 : /* Update voters. */
825 :
826 0 : fd_tower_stakes_remove( ctx->tower_stakes, slot_completed->slot ); /* no-op for 99% of cases except for eqvoc */
827 0 : ulong total_stake = update_voters( ctx, slot_completed->bank_idx, slot_completed->slot );
828 :
829 : /* The first replay_slot_completed is always either the snapshot slot
830 : or genesis slot, which we use to initialize our slot and
831 : epoch-related metadata. */
832 :
833 0 : if( FD_UNLIKELY( ctx->init_slot==ULONG_MAX ) ) {
834 0 : ctx->init_slot = slot_completed->slot;
835 0 : ctx->root_slot = slot_completed->slot;
836 0 : fd_votes_publish( ctx->votes, slot_completed->slot );
837 0 : fd_votes_update_voters( ctx->votes, ctx->tower_voters, ctx->tower_stakes, slot_completed->slot );
838 0 : fd_eqvoc_update_voters( ctx->eqvoc, ctx->tower_voters );
839 0 : fd_hfork_update_voters( ctx->hfork, ctx->tower_voters );
840 0 : }
841 :
842 : /* Due to asynchronous frag processing, it's possible this block from
843 : replay_slot_completed is on a minority fork Tower already pruned
844 : after publishing a new root. */
845 :
846 0 : if( FD_UNLIKELY( slot_completed->slot!=ctx->init_slot && !fd_ghost_query( ctx->ghost, &slot_completed->parent_block_id ) ) ) {
847 0 : ctx->metrics.ignored_cnt++;
848 0 : ctx->metrics.ignored_slot = slot_completed->slot;
849 :
850 : /* Still need to return a message to replay so the refcnt on the
851 : bank is decremented. */
852 :
853 0 : fd_tower_slot_ignored_t * msg = fd_chunk_to_laddr( ctx->out_mem, ctx->out_chunk );
854 0 : msg->slot = slot_completed->slot;
855 0 : msg->bank_idx = slot_completed->bank_idx;
856 :
857 0 : fd_stem_publish( stem, OUT_IDX, FD_TOWER_SIG_SLOT_IGNORED, ctx->out_chunk, sizeof(fd_tower_slot_ignored_t), 0UL, tsorig, fd_frag_meta_ts_comp( fd_tickcount() ) );
858 0 : ctx->out_chunk = fd_dcache_compact_next( ctx->out_chunk, sizeof(fd_tower_slot_ignored_t), ctx->out_chunk0, ctx->out_wmark );
859 0 : ctx->out_seq = stem->seqs[ OUT_IDX ];
860 0 : return;
861 0 : }
862 :
863 : /* Reconcile our local tower with the on-chain tower (stored inside
864 : our vote account). */
865 :
866 0 : ulong our_vote_acct_bal = ULONG_MAX;
867 0 : int found = 0;
868 0 : fd_funk_txn_xid_t xid = { .ul = { slot_completed->slot, slot_completed->bank_idx } };
869 0 : fd_accdb_ro_t ro[1];
870 0 : if( FD_LIKELY( fd_accdb_open_ro( ctx->accdb, ro, &xid, ctx->vote_account ) ) ) {
871 0 : found = 1;
872 0 : ctx->our_vote_acct_sz = fd_ulong_min( fd_accdb_ref_data_sz( ro ), FD_VOTE_STATE_DATA_MAX );
873 0 : our_vote_acct_bal = fd_accdb_ref_lamports( ro );
874 0 : fd_memcpy( ctx->our_vote_acct, fd_accdb_ref_data_const( ro ), ctx->our_vote_acct_sz );
875 0 : fd_accdb_close_ro( ctx->accdb, ro );
876 0 : fd_tower_reconcile( ctx->tower, ctx->root_slot, ctx->our_vote_acct, ctx->tower_blocks );
877 0 : }
878 :
879 : /* Check for equivocation (already received a replay_slot_completed
880 : for this slot). */
881 :
882 0 : fd_tower_blk_t * eqvoc_tower_blk = NULL;
883 0 : if( FD_UNLIKELY( eqvoc_tower_blk = fd_tower_blocks_query( ctx->tower_blocks, slot_completed->slot ) ) ) {
884 :
885 : /* As fd_replay_slot_completed guarantees, we process at most 2
886 : equivocating blocks for a given slot. fd_tower_{...} only stores
887 : one version of a block (by design, given the tower protocol's
888 : limitations ... see notes in fd_tower_blocks.h). We also know
889 : when seeing the same slot a second time that second block is
890 : confirmed, also guaranteed by fd_replay_slot_completed ... see
891 : notes in fd_replay_tile.h).
892 :
893 : So we retain the existing tower_block we have (which contains the
894 : first voted_block_id if we did indeed vote for it). We update
895 : the replayed_block_id to the newer replayed version, and also
896 : update the parent if it is different. We clear out the slot from
897 : the other tower adjacent structures, and re-insert into them with
898 : the confirmed version of the slot. */
899 :
900 0 : FD_TEST( eqvoc_tower_blk->confirmed ); /* check the confirmed bit is set (second replay_slot_completed version must be confirmed) */
901 0 : fd_tower_lockos_remove( ctx->tower_lockos, slot_completed->slot );
902 0 : eqvoc_tower_blk->parent_slot = slot_completed->parent_slot;
903 0 : eqvoc_tower_blk->replayed_block_id = slot_completed->block_id;
904 :
905 0 : ctx->metrics.eqvoc_cnt++;
906 0 : ctx->metrics.eqvoc_slot = slot_completed->slot;
907 0 : } else {
908 :
909 : /* Otherwise this is the first replay of this block, so insert a new
910 : tower_blk. */
911 :
912 0 : fd_tower_blk_t * tower_blk = fd_tower_blocks_insert( ctx->tower_blocks, slot_completed->slot, slot_completed->parent_slot );
913 0 : tower_blk->parent_slot = slot_completed->parent_slot;
914 0 : tower_blk->epoch = slot_completed->epoch;
915 0 : tower_blk->replayed = 1;
916 0 : tower_blk->replayed_block_id = slot_completed->block_id;
917 0 : tower_blk->voted = 0;
918 0 : tower_blk->confirmed = 0;
919 0 : tower_blk->leader = slot_completed->is_leader;
920 0 : tower_blk->propagated = 0;
921 :
922 : /* Set the prev_leader_slot. */
923 :
924 0 : if( FD_UNLIKELY( tower_blk->leader ) ) {
925 0 : tower_blk->prev_leader_slot = slot_completed->parent_slot;
926 0 : } else if ( FD_UNLIKELY( ctx->init_slot==slot_completed->slot ) ) {
927 0 : tower_blk->prev_leader_slot = ULONG_MAX;
928 0 : } else {
929 0 : fd_tower_blk_t * parent_tower_blk = fd_tower_blocks_query( ctx->tower_blocks, slot_completed->parent_slot );
930 0 : FD_TEST( parent_tower_blk );
931 0 : tower_blk->prev_leader_slot = parent_tower_blk->prev_leader_slot;
932 0 : }
933 0 : }
934 :
935 : /* Insert into ghost */
936 :
937 0 : fd_ghost_blk_t * ghost_blk = fd_ghost_insert( ctx->ghost, &slot_completed->block_id, fd_ptr_if( slot_completed->slot!=ctx->init_slot, &slot_completed->parent_block_id, NULL ), slot_completed->slot );
938 0 : ghost_blk->total_stake = total_stake;
939 :
940 : /* Insert into hard fork detector. */
941 :
942 0 : fd_hfork_blk_t * hfork_blk = fd_hfork_record_our_bank_hash( ctx->hfork, &slot_completed->block_id, &slot_completed->bank_hash, fd_ghost_root( ctx->ghost )->total_stake );
943 0 : update_metrics_hfork( ctx, hfork_blk->flag, slot_completed->slot, &slot_completed->block_id );
944 :
945 0 : fd_tower_voters_t * tower_voters = ctx->tower_voters;
946 0 : for( fd_tower_voters_iter_t iter = fd_tower_voters_iter_init( tower_voters );
947 0 : !fd_tower_voters_iter_done( tower_voters, iter );
948 0 : iter = fd_tower_voters_iter_next( tower_voters, iter ) ) {
949 0 : fd_tower_voters_t * acct = fd_tower_voters_iter_ele( tower_voters, iter );
950 :
951 : /* 1. Update forks with lockouts. */
952 :
953 0 : fd_tower_lockos_insert( ctx->tower_lockos, slot_completed->slot, &acct->vote_acc, acct );
954 :
955 : /* 2. Count the last vote slot in the vote state towards ghost. */
956 :
957 0 : ulong vote_slot = fd_tower_empty( acct->tower ) ? ULONG_MAX : fd_tower_peek_tail_const( acct->tower )->slot;
958 0 : if( FD_LIKELY( vote_slot!=ULONG_MAX && /* has voted */
959 0 : vote_slot>=fd_ghost_root( ctx->ghost )->slot ) ) { /* vote not too old */
960 :
961 : /* We search up the ghost ancestry to find the ghost block for
962 : this vote slot. In Agave, they look this value up using a
963 : hashmap of slot->bank hash ("fork progress"), but that approach
964 : only works because they dump and repair (so there's only ever
965 : one canonical bank hash). We retain multiple block ids, both
966 : the original and confirmed one. */
967 :
968 0 : fd_ghost_blk_t * ancestor_blk = fd_ghost_slot_ancestor( ctx->ghost, ghost_blk, vote_slot ); /* FIXME potentially slow */
969 :
970 : /* It is impossible for ancestor_blk to be missing, because
971 : these are vote accounts on a given fork, not vote txns across
972 : forks. So we know these towers must contain slots we know
973 : about as long as they are >= root, which we checked above. */
974 :
975 0 : if( FD_UNLIKELY( !ancestor_blk ) ) {
976 0 : FD_BASE58_ENCODE_32_BYTES( acct->vote_acc.key, pubkey_b58 );
977 0 : FD_LOG_CRIT(( "missing ancestor. replay slot %lu vote slot %lu voter %s", slot_completed->slot, vote_slot, pubkey_b58 ));
978 0 : }
979 :
980 0 : int ghost_err = fd_ghost_count_vote( ctx->ghost, ancestor_blk, &acct->vote_acc, acct->stake, vote_slot );
981 0 : update_metrics_ghost( ctx, ghost_err );
982 0 : }
983 0 : }
984 :
985 : /* Determine reset, vote, and root slots. There may not be a vote or
986 : root slot but there is always a reset slot. */
987 :
988 0 : fd_tower_out_t out = fd_tower_vote_and_reset( ctx->tower, ctx->tower_blocks, ctx->tower_lockos, ctx->tower_stakes, ctx->tower_voters, ctx->ghost, ctx->votes );
989 :
990 : /* Update forks if there is a vote slot. */
991 :
992 0 : if( FD_LIKELY( out.vote_slot!=ULONG_MAX ) ) {
993 :
994 : /* We might not be voting for the current replay slot because the
995 : best_blk (according to ghost) could be another fork. */
996 :
997 0 : fd_tower_blk_t * vote_tower_blk = fd_tower_blocks_query( ctx->tower_blocks, out.vote_slot );
998 0 : vote_tower_blk->voted = 1;
999 0 : vote_tower_blk->voted_block_id = out.vote_block_id;
1000 0 : }
1001 :
1002 : /* Publish structures if there is a new root. */
1003 :
1004 0 : if( FD_UNLIKELY( out.root_slot!=ULONG_MAX ) ) {
1005 0 : if( FD_UNLIKELY( 0==memcmp( &out.root_block_id, &hash_null, sizeof(fd_hash_t) ) ) ) {
1006 0 : FD_LOG_CRIT(( "invariant violation: root block id is null at slot %lu", out.root_slot ));
1007 0 : }
1008 :
1009 0 : fd_tower_blk_t * oldr_tower_blk = fd_tower_blocks_query( ctx->tower_blocks, ctx->root_slot );
1010 0 : fd_tower_blk_t * newr_tower_blk = fd_tower_blocks_query( ctx->tower_blocks, out.root_slot );
1011 0 : FD_TEST( oldr_tower_blk );
1012 0 : FD_TEST( newr_tower_blk );
1013 :
1014 : /* It is a Solana consensus protocol invariant that a validator must
1015 : make at least one root in an epoch, so the root's epoch cannot
1016 : advance by more than one. */
1017 :
1018 0 : FD_TEST( oldr_tower_blk->epoch==newr_tower_blk->epoch || oldr_tower_blk->epoch+1==newr_tower_blk->epoch ); /* root can only move forward one epoch */
1019 :
1020 : /* Publish votes: 1. reindex if it's a new epoch. 2. publish the new
1021 : root to votes. */
1022 :
1023 0 : if( FD_UNLIKELY( oldr_tower_blk->epoch+1==newr_tower_blk->epoch ) ) {
1024 0 : FD_TEST( newr_tower_blk->epoch==slot_completed->epoch ); /* new root's epoch must be same as current slot_completed */
1025 0 : fd_votes_update_voters( ctx->votes, ctx->tower_voters, ctx->tower_stakes, out.root_slot );
1026 0 : fd_eqvoc_update_voters( ctx->eqvoc, ctx->tower_voters );
1027 0 : fd_hfork_update_voters( ctx->hfork, ctx->tower_voters );
1028 0 : }
1029 0 : fd_votes_publish( ctx->votes, out.root_slot );
1030 :
1031 : /* Publish tower_blocks and tower_stakes by removing any entries
1032 : older than the new root. */
1033 :
1034 0 : for( ulong slot = ctx->root_slot; slot < out.root_slot; slot++ ) {
1035 0 : fd_tower_blocks_remove( ctx->tower_blocks, slot );
1036 0 : fd_tower_lockos_remove( ctx->tower_lockos, slot );
1037 0 : fd_tower_stakes_remove( ctx->tower_stakes, slot );
1038 0 : }
1039 :
1040 : /* Publish roots by walking up the ghost ancestry to publish new root
1041 : frags for intermediate slots we couldn't vote for. */
1042 :
1043 0 : fd_ghost_blk_t * newr = fd_ghost_query( ctx->ghost, &out.root_block_id );
1044 0 : fd_ghost_blk_t * oldr = fd_ghost_root( ctx->ghost );
1045 :
1046 : /* oldr is not guaranteed to be the immediate parent of newr, but is
1047 : rather an arbitrary ancestor. This can happen if we couldn't
1048 : vote for those intermediate slot(s). We publish those slots as
1049 : intermediate roots. */
1050 :
1051 0 : fd_ghost_blk_t * intr = newr;
1052 0 : while( FD_LIKELY( intr!=oldr ) ) {
1053 0 : publishes_push_head( ctx->publishes, (publish_t){ .sig = FD_TOWER_SIG_SLOT_ROOTED, .msg = { .slot_rooted = { .slot = intr->slot, .block_id = intr->id } } } );
1054 0 : intr = fd_ghost_parent( ctx->ghost, intr );
1055 0 : }
1056 :
1057 : /* Publish ghost. */
1058 :
1059 0 : fd_ghost_publish( ctx->ghost, newr );
1060 :
1061 : /* Update the new root. */
1062 :
1063 0 : ctx->root_slot = out.root_slot;
1064 0 : }
1065 :
1066 : /* Publish a slot_done frag to tower_out. */
1067 :
1068 0 : fd_tower_slot_done_t * msg = fd_chunk_to_laddr( ctx->out_mem, ctx->out_chunk );
1069 0 : msg->replay_slot = slot_completed->slot;
1070 0 : msg->active_fork_cnt = fd_ghost_width( ctx->ghost );
1071 0 : msg->vote_slot = out.vote_slot;
1072 0 : msg->reset_slot = out.reset_slot;
1073 0 : msg->reset_block_id = out.reset_block_id;
1074 0 : msg->root_slot = out.root_slot;
1075 0 : msg->root_block_id = out.root_block_id;
1076 0 : msg->replay_bank_idx = slot_completed->bank_idx;
1077 0 : msg->vote_acct_bal = our_vote_acct_bal;
1078 :
1079 : /* Populate slot_done with a vote txn representing our current tower
1080 : (regardless of whether there was a new vote slot or not).
1081 :
1082 : TODO only do this on refresh_last_vote? */
1083 :
1084 0 : ulong authority_idx = ULONG_MAX;
1085 0 : fd_pubkey_t authority[1];
1086 0 : int found_authority = deser_auth_vtr( ctx, slot_completed->epoch, found, authority, &authority_idx );
1087 :
1088 0 : if( FD_LIKELY( found_authority ) ) {
1089 0 : msg->has_vote_txn = 1;
1090 0 : fd_txn_p_t txn[1];
1091 0 : fd_tower_to_vote_txn( ctx->tower, ctx->root_slot, &slot_completed->bank_hash, &slot_completed->block_id, &slot_completed->block_hash, ctx->identity_key, authority, ctx->vote_account, txn );
1092 0 : FD_TEST( !fd_tower_empty( ctx->tower ) );
1093 0 : FD_TEST( txn->payload_sz && txn->payload_sz<=FD_TPU_MTU );
1094 0 : fd_memcpy( msg->vote_txn, txn->payload, txn->payload_sz );
1095 0 : msg->vote_txn_sz = txn->payload_sz;
1096 0 : msg->authority_idx = authority_idx;
1097 0 : } else {
1098 0 : msg->has_vote_txn = 0;
1099 0 : }
1100 :
1101 0 : msg->tower_cnt = 0UL;
1102 0 : if( FD_LIKELY( found ) ) msg->tower_cnt = fd_tower_with_lat_from_vote_acc( msg->tower, ctx->our_vote_acct );
1103 :
1104 0 : fd_stem_publish( stem, OUT_IDX, FD_TOWER_SIG_SLOT_DONE, ctx->out_chunk, sizeof(fd_tower_slot_done_t), 0UL, tsorig, fd_frag_meta_ts_comp( fd_tickcount() ) );
1105 0 : ctx->out_chunk = fd_dcache_compact_next( ctx->out_chunk, sizeof(fd_tower_slot_done_t), ctx->out_chunk0, ctx->out_wmark );
1106 0 : ctx->out_seq = stem->seqs[ OUT_IDX ];
1107 :
1108 : /* Write out metrics. */
1109 :
1110 0 : ctx->metrics.replay_slot = slot_completed->slot;
1111 0 : if( FD_LIKELY( out.vote_slot!=ULONG_MAX ) ) ctx->metrics.last_vote_slot = out.vote_slot;
1112 0 : ctx->metrics.reset_slot = out.reset_slot; /* always set */
1113 0 : ctx->metrics.root_slot = ctx->root_slot;
1114 0 : ctx->metrics.init_slot = ctx->init_slot;
1115 :
1116 0 : ctx->metrics.ancestor_rollback += (ulong)fd_uchar_extract_bit( out.flags, FD_TOWER_FLAG_ANCESTOR_ROLLBACK );
1117 0 : ctx->metrics.sibling_confirmed += (ulong)fd_uchar_extract_bit( out.flags, FD_TOWER_FLAG_SIBLING_CONFIRMED );
1118 0 : ctx->metrics.same_fork += (ulong)fd_uchar_extract_bit( out.flags, FD_TOWER_FLAG_SAME_FORK );
1119 0 : ctx->metrics.switch_pass += (ulong)fd_uchar_extract_bit( out.flags, FD_TOWER_FLAG_SWITCH_PASS );
1120 0 : ctx->metrics.switch_fail += (ulong)fd_uchar_extract_bit( out.flags, FD_TOWER_FLAG_SWITCH_FAIL );
1121 0 : ctx->metrics.lockout_fail += (ulong)fd_uchar_extract_bit( out.flags, FD_TOWER_FLAG_LOCKOUT_FAIL );
1122 0 : ctx->metrics.threshold_fail += (ulong)fd_uchar_extract_bit( out.flags, FD_TOWER_FLAG_THRESHOLD_FAIL );
1123 0 : ctx->metrics.propagated_fail += (ulong)fd_uchar_extract_bit( out.flags, FD_TOWER_FLAG_PROPAGATED_FAIL );
1124 :
1125 : /* Log out structures. */
1126 :
1127 0 : char cstr[4096]; ulong cstr_sz;
1128 0 : FD_LOG_DEBUG(( "\n\n%s", fd_ghost_to_cstr( ctx->ghost, fd_ghost_root( ctx->ghost ), cstr, sizeof(cstr), &cstr_sz ) ));
1129 0 : FD_LOG_DEBUG(( "\n\n%s", fd_tower_to_cstr( ctx->tower, ctx->root_slot, cstr ) ));
1130 0 : }
1131 :
1132 : FD_FN_CONST static inline ulong
1133 0 : scratch_align( void ) {
1134 0 : return 128UL;
1135 0 : }
1136 :
1137 : FD_FN_PURE static inline ulong
1138 0 : scratch_footprint( fd_topo_tile_t const * tile ) {
1139 0 : ulong slot_max = fd_ulong_pow2_up( tile->tower.max_live_slots );
1140 0 : ulong per_vtr_max = PER_VTR_MAX;
1141 0 : ulong vtr_max = fd_ulong_pow2_up( VTR_MAX );
1142 0 : ulong blk_max = slot_max * EQVOC_MAX;
1143 0 : ulong fec_max = slot_max * FD_SHRED_BLK_MAX / FD_FEC_SHRED_CNT;
1144 0 : ulong pub_max = slot_max * FD_TOWER_SLOT_CONFIRMED_LEVEL_CNT;
1145 :
1146 0 : ulong l = FD_LAYOUT_INIT;
1147 0 : l = FD_LAYOUT_APPEND( l, alignof(fd_tower_tile_t), sizeof(fd_tower_tile_t) );
1148 0 : l = FD_LAYOUT_APPEND( l, auth_vtr_align(), auth_vtr_footprint() );
1149 : /*Â auth_vtr_keyswitch */
1150 0 : l = FD_LAYOUT_APPEND( l, fd_eqvoc_align(), fd_eqvoc_footprint( slot_max, fec_max, per_vtr_max, vtr_max ) );
1151 0 : l = FD_LAYOUT_APPEND( l, fd_ghost_align(), fd_ghost_footprint( blk_max, vtr_max ) );
1152 0 : l = FD_LAYOUT_APPEND( l, fd_hfork_align(), fd_hfork_footprint( per_vtr_max, vtr_max ) );
1153 0 : l = FD_LAYOUT_APPEND( l, fd_votes_align(), fd_votes_footprint( slot_max, vtr_max ) );
1154 0 : l = FD_LAYOUT_APPEND( l, fd_tower_align(), fd_tower_footprint() );
1155 0 : l = FD_LAYOUT_APPEND( l, fd_tower_align(), fd_tower_footprint() );
1156 0 : l = FD_LAYOUT_APPEND( l, fd_tower_blocks_align(), fd_tower_blocks_footprint( slot_max ) );
1157 0 : l = FD_LAYOUT_APPEND( l, fd_tower_lockos_align(), fd_tower_lockos_footprint( slot_max, vtr_max ) );
1158 0 : l = FD_LAYOUT_APPEND( l, fd_tower_stakes_align(), fd_tower_stakes_footprint( slot_max, vtr_max ) );
1159 0 : l = FD_LAYOUT_APPEND( l, fd_tower_voters_align(), fd_tower_voters_footprint( vtr_max ) );
1160 0 : l = FD_LAYOUT_APPEND( l, FD_TOWER_ALIGN, FD_TOWER_FOOTPRINT * vtr_max );
1161 0 : l = FD_LAYOUT_APPEND( l, publishes_align(), publishes_footprint( pub_max ) );
1162 0 : return FD_LAYOUT_FINI( l, scratch_align() );
1163 0 : }
1164 :
1165 : static void
1166 0 : during_housekeeping( fd_tower_tile_t * ctx ) {
1167 0 : if( FD_UNLIKELY( fd_keyswitch_state_query( ctx->auth_vtr_keyswitch )==FD_KEYSWITCH_STATE_UNHALT_PENDING ) ) {
1168 0 : fd_keyswitch_state( ctx->auth_vtr_keyswitch, FD_KEYSWITCH_STATE_UNLOCKED );
1169 0 : }
1170 :
1171 0 : if( FD_UNLIKELY( fd_keyswitch_state_query( ctx->auth_vtr_keyswitch )==FD_KEYSWITCH_STATE_SWITCH_PENDING ) ) {
1172 0 : fd_pubkey_t pubkey = *(fd_pubkey_t const *)fd_type_pun_const( ctx->auth_vtr_keyswitch->bytes );
1173 0 : if( FD_UNLIKELY( auth_vtr_query( ctx->auth_vtr, pubkey, NULL ) ) ) FD_LOG_CRIT(( "keyswitch: duplicate authorized voter key, keys not synced up with sign tile" ));
1174 0 : if( FD_UNLIKELY( ctx->auth_vtr_path_cnt==AUTH_VOTERS_MAX ) ) FD_LOG_CRIT(( "keyswitch: too many authorized voters, keys not synced up with sign tile" ));
1175 :
1176 0 : auth_vtr_t * auth_vtr = auth_vtr_insert( ctx->auth_vtr, pubkey );
1177 0 : auth_vtr->paths_idx = ctx->auth_vtr_path_cnt;
1178 0 : ctx->auth_vtr_path_cnt++;
1179 0 : fd_keyswitch_state( ctx->auth_vtr_keyswitch, FD_KEYSWITCH_STATE_COMPLETED );
1180 :
1181 0 : }
1182 :
1183 : /* FIXME: Currently, the tower tile doesn't support set-identity with
1184 : a tower file. When support for a tower file is added, we need to
1185 : swap the file that is running and sync it to the local state of
1186 : the tower. Because a tower file is not supported, if another
1187 : validator was running with the identity that was switched to, then
1188 : it is possible that the original validator and the fallback (this
1189 : node), may have tower files which are out of sync. This could lead
1190 : to consensus violations such as double voting or duplicate
1191 : confirmations. Currently it is unsafe for a validator operator to
1192 : switch identities without a 512 slot delay: the reason for this
1193 : delay is to account for the worst case number of slots a vote
1194 : account can be locked out for. */
1195 :
1196 0 : if( FD_UNLIKELY( fd_keyswitch_state_query( ctx->identity_keyswitch )==FD_KEYSWITCH_STATE_UNHALT_PENDING ) ) {
1197 0 : FD_LOG_DEBUG(( "keyswitch: unhalting signing" ));
1198 0 : FD_CRIT( ctx->halt_signing, "state machine corruption" );
1199 0 : ctx->halt_signing = 0;
1200 0 : fd_keyswitch_state( ctx->identity_keyswitch, FD_KEYSWITCH_STATE_COMPLETED );
1201 0 : }
1202 :
1203 0 : if( FD_UNLIKELY( fd_keyswitch_state_query( ctx->identity_keyswitch )==FD_KEYSWITCH_STATE_SWITCH_PENDING ) ) {
1204 0 : FD_LOG_DEBUG(( "keyswitch: halting signing" ));
1205 0 : memcpy( ctx->identity_key, ctx->identity_keyswitch->bytes, 32UL );
1206 0 : fd_keyswitch_state( ctx->identity_keyswitch, FD_KEYSWITCH_STATE_COMPLETED );
1207 0 : ctx->halt_signing = 1;
1208 0 : ctx->identity_keyswitch->result = ctx->out_seq;
1209 0 : }
1210 0 : }
1211 :
1212 : static inline void
1213 0 : metrics_write( fd_tower_tile_t * ctx ) {
1214 0 : FD_MCNT_SET ( TOWER, IGNORED_CNT, ctx->metrics.ignored_cnt );
1215 0 : FD_MGAUGE_SET( TOWER, IGNORED_SLOT, ctx->metrics.ignored_slot );
1216 :
1217 0 : FD_MGAUGE_SET( TOWER, REPLAY_SLOT, ctx->metrics.replay_slot );
1218 0 : FD_MGAUGE_SET( TOWER, VOTE_SLOT, ctx->metrics.last_vote_slot );
1219 0 : FD_MGAUGE_SET( TOWER, RESET_SLOT, ctx->metrics.reset_slot );
1220 0 : FD_MGAUGE_SET( TOWER, ROOT_SLOT, ctx->metrics.root_slot );
1221 0 : FD_MGAUGE_SET( TOWER, INIT_SLOT, ctx->metrics.init_slot );
1222 :
1223 0 : FD_MCNT_SET( TOWER, ANCESTOR_ROLLBACK, ctx->metrics.ancestor_rollback );
1224 0 : FD_MCNT_SET( TOWER, SIBLING_CONFIRMED, ctx->metrics.sibling_confirmed );
1225 0 : FD_MCNT_SET( TOWER, SAME_FORK, ctx->metrics.same_fork );
1226 0 : FD_MCNT_SET( TOWER, SWITCH_PASS, ctx->metrics.switch_pass );
1227 0 : FD_MCNT_SET( TOWER, SWITCH_FAIL, ctx->metrics.switch_fail );
1228 0 : FD_MCNT_SET( TOWER, LOCKOUT_FAIL, ctx->metrics.lockout_fail );
1229 0 : FD_MCNT_SET( TOWER, THRESHOLD_FAIL, ctx->metrics.threshold_fail );
1230 0 : FD_MCNT_SET( TOWER, PROPAGATED_FAIL, ctx->metrics.propagated_fail );
1231 :
1232 0 : FD_MCNT_SET( TOWER, TXN_BAD_DESER, ctx->metrics.txn_bad_deser );
1233 0 : FD_MCNT_SET( TOWER, TXN_BAD_TOWER, ctx->metrics.txn_bad_tower );
1234 0 : FD_MCNT_SET( TOWER, TXN_NOT_TOWER_SYNC, ctx->metrics.txn_not_tower_sync );
1235 0 : FD_MCNT_SET( TOWER, TXN_EMPTY_TOWER, ctx->metrics.txn_empty_tower );
1236 0 : FD_MCNT_SET( TOWER, TXN_NOT_READY, ctx->metrics.txn_not_ready );
1237 :
1238 0 : FD_MCNT_SET( TOWER, VOTES_TOO_OLD, ctx->metrics.votes_too_old );
1239 0 : FD_MCNT_SET( TOWER, VOTES_TOO_NEW, ctx->metrics.votes_too_new );
1240 0 : FD_MCNT_SET( TOWER, VOTES_UNKNOWN_VTR, ctx->metrics.votes_unknown_vtr );
1241 0 : FD_MCNT_SET( TOWER, VOTES_ALREADY_VOTED, ctx->metrics.votes_already_voted );
1242 0 : FD_MCNT_SET( TOWER, VOTES_UNKNOWN_SLOT, ctx->metrics.votes_unknown_slot );
1243 0 : FD_MCNT_SET( TOWER, VOTES_UNKNOWN_BLOCK_ID, ctx->metrics.votes_unknown_block_id );
1244 :
1245 0 : FD_MCNT_SET( TOWER, EQVOC_SUCCESS_MERKLE, ctx->metrics.eqvoc_success_merkle );
1246 0 : FD_MCNT_SET( TOWER, EQVOC_SUCCESS_META, ctx->metrics.eqvoc_success_meta );
1247 0 : FD_MCNT_SET( TOWER, EQVOC_SUCCESS_LAST, ctx->metrics.eqvoc_success_last );
1248 0 : FD_MCNT_SET( TOWER, EQVOC_SUCCESS_OVERLAP, ctx->metrics.eqvoc_success_overlap );
1249 0 : FD_MCNT_SET( TOWER, EQVOC_SUCCESS_CHAINED, ctx->metrics.eqvoc_success_chained );
1250 :
1251 0 : FD_MCNT_SET( TOWER, EQVOC_ERR_SERDE, ctx->metrics.eqvoc_err_serde );
1252 0 : FD_MCNT_SET( TOWER, EQVOC_ERR_SLOT, ctx->metrics.eqvoc_err_slot );
1253 0 : FD_MCNT_SET( TOWER, EQVOC_ERR_VERSION, ctx->metrics.eqvoc_err_version );
1254 0 : FD_MCNT_SET( TOWER, EQVOC_ERR_TYPE, ctx->metrics.eqvoc_err_type );
1255 0 : FD_MCNT_SET( TOWER, EQVOC_ERR_MERKLE, ctx->metrics.eqvoc_err_merkle );
1256 0 : FD_MCNT_SET( TOWER, EQVOC_ERR_SIGNATURE, ctx->metrics.eqvoc_err_signature );
1257 :
1258 0 : FD_MCNT_SET( TOWER, EQVOC_ERR_CHUNK_CNT, ctx->metrics.eqvoc_err_chunk_cnt );
1259 0 : FD_MCNT_SET( TOWER, EQVOC_ERR_CHUNK_IDX, ctx->metrics.eqvoc_err_chunk_idx );
1260 0 : FD_MCNT_SET( TOWER, EQVOC_ERR_CHUNK_LEN, ctx->metrics.eqvoc_err_chunk_len );
1261 :
1262 0 : FD_MCNT_SET( TOWER, EQVOC_ERR_IGNORED_FROM, ctx->metrics.eqvoc_err_ignored_from );
1263 0 : FD_MCNT_SET( TOWER, EQVOC_ERR_IGNORED_SLOT, ctx->metrics.eqvoc_err_ignored_slot );
1264 :
1265 0 : FD_MCNT_SET( TOWER, EQVOC_PROOF_CONSTRUCTED, ctx->metrics.eqvoc_proof_constructed );
1266 0 : FD_MCNT_SET( TOWER, EQVOC_PROOF_VERIFIED, ctx->metrics.eqvoc_proof_verified );
1267 :
1268 0 : FD_MCNT_SET( TOWER, GHOST_NOT_VOTED, ctx->metrics.ghost_not_voted );
1269 0 : FD_MCNT_SET( TOWER, GHOST_TOO_OLD, ctx->metrics.ghost_too_old );
1270 0 : FD_MCNT_SET( TOWER, GHOST_ALREADY_VOTED, ctx->metrics.ghost_already_voted );
1271 :
1272 0 : FD_MCNT_SET( TOWER, HFORK_UNKNOWN_VTR, ctx->metrics.hfork_unknown_vtr );
1273 0 : FD_MCNT_SET( TOWER, HFORK_ALREADY_VOTED, ctx->metrics.hfork_already_voted );
1274 0 : FD_MCNT_SET( TOWER, HFORK_TOO_OLD, ctx->metrics.hfork_too_old );
1275 :
1276 0 : FD_MGAUGE_SET( TOWER, HFORK_MATCHED_SLOT, ctx->metrics.hfork_matched_slot );
1277 0 : FD_MGAUGE_SET( TOWER, HFORK_MISMATCHED_SLOT, ctx->metrics.hfork_mismatched_slot );
1278 0 : }
1279 :
1280 : static inline void
1281 : after_credit( fd_tower_tile_t * ctx,
1282 : fd_stem_context_t * stem,
1283 : int * opt_poll_in,
1284 0 : int * charge_busy ) {
1285 0 : if( FD_LIKELY( !publishes_empty( ctx->publishes ) ) ) {
1286 0 : publish_t * pub = publishes_pop_head_nocopy( ctx->publishes );
1287 0 : memcpy( fd_chunk_to_laddr( ctx->out_mem, ctx->out_chunk ), &pub->msg, sizeof(fd_tower_msg_t) );
1288 0 : fd_stem_publish( stem, OUT_IDX, pub->sig, ctx->out_chunk, sizeof(fd_tower_msg_t), 0UL, fd_frag_meta_ts_comp( fd_tickcount() ), fd_frag_meta_ts_comp( fd_tickcount() ) );
1289 0 : ctx->out_chunk = fd_dcache_compact_next( ctx->out_chunk, sizeof(fd_tower_msg_t), ctx->out_chunk0, ctx->out_wmark );
1290 0 : ctx->out_seq = stem->seqs[ OUT_IDX ];
1291 0 : *opt_poll_in = 0; /* drain the publishes */
1292 0 : *charge_busy = 1;
1293 0 : }
1294 0 : }
1295 :
1296 : static inline int
1297 : returnable_frag( fd_tower_tile_t * ctx,
1298 : ulong in_idx,
1299 : ulong seq FD_PARAM_UNUSED,
1300 : ulong sig,
1301 : ulong chunk,
1302 : ulong sz,
1303 : ulong ctl FD_PARAM_UNUSED,
1304 : ulong tsorig,
1305 : ulong tspub FD_PARAM_UNUSED,
1306 0 : fd_stem_context_t * stem ) {
1307 :
1308 0 : if( FD_UNLIKELY( !ctx->in[ in_idx ].mcache_only && ( chunk<ctx->in[ in_idx ].chunk0 || chunk>ctx->in[ in_idx ].wmark || sz>ctx->in[ in_idx ].mtu ) ) )
1309 0 : FD_LOG_ERR(( "chunk %lu %lu from in %d corrupt, not in range [%lu,%lu]", chunk, sz, ctx->in_kind[ in_idx ], ctx->in[ in_idx ].chunk0, ctx->in[ in_idx ].wmark ));
1310 :
1311 0 : switch( ctx->in_kind[ in_idx ] ) {
1312 0 : case IN_KIND_DEDUP: {
1313 0 : if( FD_UNLIKELY( ctx->root_slot==ULONG_MAX ) ) return 1;
1314 0 : fd_txn_m_t * txnm = (fd_txn_m_t *)fd_chunk_to_laddr( ctx->in[in_idx].mem, chunk );
1315 0 : FD_TEST( txnm->payload_sz<=FD_TPU_MTU );
1316 0 : FD_TEST( txnm->txn_t_sz<=FD_TXN_MAX_SZ );
1317 0 : count_vote( ctx, fd_txn_m_txn_t_const( txnm ), fd_txn_m_payload_const( txnm ) );
1318 0 : return 0;
1319 0 : }
1320 0 : case IN_KIND_EPOCH: {
1321 0 : fd_multi_epoch_leaders_epoch_msg_init( ctx->mleaders, fd_chunk_to_laddr_const( ctx->in[ in_idx ].mem, chunk ) );
1322 0 : fd_multi_epoch_leaders_epoch_msg_fini( ctx->mleaders );
1323 0 : return 0;
1324 0 : }
1325 0 : case IN_KIND_GOSSIP: {
1326 0 : if( FD_LIKELY( sig==FD_GOSSIP_UPDATE_TAG_DUPLICATE_SHRED ) ) {
1327 0 : fd_gossip_update_message_t const * msg = (fd_gossip_update_message_t const *)fd_type_pun_const( fd_chunk_to_laddr_const( ctx->in[ in_idx ].mem, chunk ) );
1328 0 : fd_gossip_duplicate_shred_t const * duplicate_shred = msg->duplicate_shred;
1329 0 : fd_pubkey_t const * from = (fd_pubkey_t const *)fd_type_pun_const( msg->origin );
1330 0 : fd_epoch_leaders_t const * lsched = fd_multi_epoch_leaders_get_lsched_for_slot( ctx->mleaders, duplicate_shred->slot );
1331 0 : fd_tower_slot_duplicate_t * out = fd_chunk_to_laddr( ctx->out_mem, ctx->out_chunk );
1332 0 : int eqvoc_err = fd_eqvoc_chunk_insert( ctx->eqvoc, ctx->shred_version, ctx->root_slot, lsched, from, duplicate_shred, out->chunks );
1333 0 : update_metrics_eqvoc( ctx, eqvoc_err );
1334 0 : if( FD_UNLIKELY( eqvoc_err>0 ) ) {
1335 0 : ctx->metrics.eqvoc_proof_verified++;
1336 0 : fd_stem_publish( stem, OUT_IDX, FD_TOWER_SIG_SLOT_DUPLICATE, ctx->out_chunk, sizeof(fd_tower_slot_duplicate_t), 0UL, tsorig, fd_frag_meta_ts_comp( fd_tickcount() ) );
1337 0 : ctx->out_chunk = fd_dcache_compact_next( ctx->out_chunk, sizeof(fd_tower_slot_duplicate_t), ctx->out_chunk0, ctx->out_wmark );
1338 0 : ctx->out_seq = stem->seqs[ OUT_IDX ];
1339 0 : }
1340 0 : }
1341 0 : return 0;
1342 0 : }
1343 0 : case IN_KIND_IPECHO: {
1344 0 : FD_TEST( sig!=0UL && sig<=USHORT_MAX );
1345 0 : ctx->shred_version = (ushort)sig;
1346 0 : return 0;
1347 0 : }
1348 0 : case IN_KIND_REPLAY: {
1349 : /* In the case that the tower tile is halting signing, we don't
1350 : want to process any replay fragments that will cause us to
1351 : produce a vote txn. */
1352 0 : if( FD_UNLIKELY( ctx->halt_signing ) ) return 1;
1353 :
1354 0 : if( FD_LIKELY( sig==REPLAY_SIG_TXN_EXECUTED ) ) {
1355 :
1356 : /* Agave only counts replay vote txns that executed successfully.
1357 : https://github.com/anza-xyz/agave/blob/v3.1.8/runtime/src/bank_utils.rs#L53 */
1358 :
1359 0 : fd_replay_txn_executed_t * txn_executed = fd_type_pun( fd_chunk_to_laddr( ctx->in[in_idx].mem, chunk ) );
1360 0 : if( FD_UNLIKELY( !txn_executed->is_committable || txn_executed->is_fees_only || txn_executed->txn_err ) ) return 0;
1361 0 : count_vote( ctx, TXN(txn_executed->txn), txn_executed->txn->payload );
1362 0 : } else if( FD_LIKELY( sig==REPLAY_SIG_SLOT_COMPLETED ) ) {
1363 0 : fd_replay_slot_completed_t * slot_completed = (fd_replay_slot_completed_t *)fd_type_pun( fd_chunk_to_laddr( ctx->in[ in_idx ].mem, chunk ) );
1364 0 : replay_slot_completed( ctx, slot_completed, tsorig, stem );
1365 0 : } else if( FD_LIKELY( sig==REPLAY_SIG_SLOT_DEAD ) ) {
1366 0 : fd_replay_slot_dead_t * slot_dead = (fd_replay_slot_dead_t *)fd_chunk_to_laddr( ctx->in[ in_idx ].mem, chunk );
1367 0 : fd_hfork_blk_t * hfork_blk = fd_hfork_record_our_bank_hash( ctx->hfork, &slot_dead->block_id, NULL, fd_ghost_root( ctx->ghost )->total_stake );
1368 0 : update_metrics_hfork( ctx, hfork_blk->flag, slot_dead->slot, &slot_dead->block_id );
1369 0 : }
1370 0 : return 0;
1371 0 : }
1372 0 : case IN_KIND_SHRED: {
1373 0 : if( FD_LIKELY( sz==FD_SHRED_MIN_SZ || sz==FD_SHRED_MAX_SZ ) ) { /* TODO depends on pending shred_out changes */
1374 0 : fd_shred_t * shred = (fd_shred_t *)fd_type_pun( fd_chunk_to_laddr( ctx->in[ in_idx ].mem, chunk ) );
1375 0 : fd_tower_slot_duplicate_t * out = fd_chunk_to_laddr( ctx->out_mem, ctx->out_chunk );
1376 0 : fd_epoch_leaders_t const * lsched = fd_multi_epoch_leaders_get_lsched_for_slot( ctx->mleaders, shred->slot );
1377 0 : int eqvoc_err = fd_eqvoc_shred_insert( ctx->eqvoc, ctx->shred_version, ctx->root_slot, lsched, shred, out->chunks );
1378 0 : update_metrics_eqvoc( ctx, eqvoc_err );
1379 0 : if( FD_UNLIKELY( eqvoc_err>0 ) ) {
1380 0 : ctx->metrics.eqvoc_proof_constructed++;
1381 0 : fd_stem_publish( stem, OUT_IDX, FD_TOWER_SIG_SLOT_DUPLICATE, ctx->out_chunk, sizeof(fd_tower_slot_duplicate_t), 0UL, tsorig, fd_frag_meta_ts_comp( fd_tickcount() ) );
1382 0 : ctx->out_chunk = fd_dcache_compact_next( ctx->out_chunk, sizeof(fd_tower_slot_duplicate_t), ctx->out_chunk0, ctx->out_wmark );
1383 0 : ctx->out_seq = stem->seqs[ OUT_IDX ];
1384 0 : }
1385 0 : }
1386 0 : return 0;
1387 0 : }
1388 0 : default: {
1389 0 : FD_LOG_ERR(( "unexpected input kind %d", ctx->in_kind[ in_idx ] ));
1390 0 : }
1391 0 : }
1392 0 : }
1393 :
1394 : static void
1395 : privileged_init( fd_topo_t * topo,
1396 0 : fd_topo_tile_t * tile ) {
1397 0 : void * scratch = fd_topo_obj_laddr( topo, tile->tile_obj_id );
1398 0 : FD_SCRATCH_ALLOC_INIT( l, scratch );
1399 0 : fd_tower_tile_t * ctx = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_tower_tile_t), sizeof(fd_tower_tile_t) );
1400 0 : void * auth_vtr = FD_SCRATCH_ALLOC_APPEND( l, auth_vtr_align(), auth_vtr_footprint() );
1401 0 : FD_SCRATCH_ALLOC_FINI( l, scratch_align() );
1402 :
1403 0 : FD_TEST( fd_rng_secure( &ctx->seed, sizeof(ctx->seed) ) );
1404 :
1405 0 : if( FD_UNLIKELY( !strcmp( tile->tower.identity_key, "" ) ) ) FD_LOG_ERR(( "identity_key_path not set" ));
1406 0 : ctx->identity_key[ 0 ] = *(fd_pubkey_t const *)fd_type_pun_const( fd_keyload_load( tile->tower.identity_key, /* pubkey only: */ 1 ) );
1407 :
1408 : /* The vote key can be specified either directly as a base58 encoded
1409 : pubkey, or as a file path. We first try to decode as a pubkey. */
1410 :
1411 0 : uchar * vote_key = fd_base58_decode_32( tile->tower.vote_account, ctx->vote_account->uc );
1412 0 : if( FD_UNLIKELY( !vote_key ) ) ctx->vote_account[ 0 ] = *(fd_pubkey_t const *)fd_type_pun_const( fd_keyload_load( tile->tower.vote_account, /* pubkey only: */ 1 ) );
1413 :
1414 0 : ctx->auth_vtr = auth_vtr_join( auth_vtr_new( auth_vtr ) );
1415 0 : for( ulong i=0UL; i<tile->tower.authorized_voter_paths_cnt; i++ ) {
1416 0 : fd_pubkey_t pubkey = *(fd_pubkey_t const *)fd_type_pun_const( fd_keyload_load( tile->tower.authorized_voter_paths[ i ], /* pubkey only: */ 1 ) );
1417 0 : if( FD_UNLIKELY( auth_vtr_query( ctx->auth_vtr, pubkey, NULL ) ) ) {
1418 0 : FD_BASE58_ENCODE_32_BYTES( pubkey.uc, pubkey_b58 );
1419 0 : FD_LOG_ERR(( "authorized voter key duplicate %s", pubkey_b58 ));
1420 0 : }
1421 :
1422 0 : auth_vtr_t * auth_vtr = auth_vtr_insert( ctx->auth_vtr, pubkey );
1423 0 : auth_vtr->paths_idx = i;
1424 0 : }
1425 0 : ctx->auth_vtr_path_cnt = tile->tower.authorized_voter_paths_cnt;
1426 :
1427 : /* The tower file is used to checkpt and restore the state of the
1428 : local tower. */
1429 :
1430 0 : char path[ PATH_MAX ];
1431 0 : FD_BASE58_ENCODE_32_BYTES( ctx->identity_key->uc, identity_key_b58 );
1432 0 : FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "%s/tower-1_9-%s.bin.new", tile->tower.base_path, identity_key_b58 ) );
1433 0 : ctx->checkpt_fd = open( path, O_WRONLY|O_CREAT|O_TRUNC, 0600 );
1434 0 : if( FD_UNLIKELY( -1==ctx->checkpt_fd ) ) FD_LOG_ERR(( "open(`%s`) failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
1435 :
1436 0 : FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "%s/tower-1_9-%s.bin", tile->tower.base_path, identity_key_b58 ) );
1437 0 : ctx->restore_fd = open( path, O_RDONLY );
1438 0 : if( FD_UNLIKELY( -1==ctx->restore_fd && errno!=ENOENT ) ) FD_LOG_ERR(( "open(`%s`) failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
1439 0 : }
1440 :
1441 : static void
1442 : unprivileged_init( fd_topo_t * topo,
1443 0 : fd_topo_tile_t * tile ) {
1444 0 : ulong slot_max = fd_ulong_pow2_up( tile->tower.max_live_slots );
1445 0 : ulong per_vtr_max = PER_VTR_MAX;
1446 0 : ulong vtr_max = fd_ulong_pow2_up( VTR_MAX );
1447 0 : ulong blk_max = slot_max * EQVOC_MAX;
1448 0 : ulong fec_max = slot_max * FD_SHRED_BLK_MAX / FD_FEC_SHRED_CNT;
1449 0 : ulong pub_max = slot_max * FD_TOWER_SLOT_CONFIRMED_LEVEL_CNT;
1450 :
1451 0 : void * scratch = fd_topo_obj_laddr( topo, tile->tile_obj_id );
1452 0 : FD_SCRATCH_ALLOC_INIT( l, scratch );
1453 0 : fd_tower_tile_t * ctx = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_tower_tile_t), sizeof(fd_tower_tile_t) );
1454 0 : void * auth_vtr = FD_SCRATCH_ALLOC_APPEND( l, auth_vtr_align(), auth_vtr_footprint() );
1455 0 : void * eqvoc = FD_SCRATCH_ALLOC_APPEND( l, fd_eqvoc_align(), fd_eqvoc_footprint( slot_max, fec_max, per_vtr_max, vtr_max ) );
1456 0 : void * ghost = FD_SCRATCH_ALLOC_APPEND( l, fd_ghost_align(), fd_ghost_footprint( blk_max, vtr_max ) );
1457 0 : void * hfork = FD_SCRATCH_ALLOC_APPEND( l, fd_hfork_align(), fd_hfork_footprint( per_vtr_max, vtr_max ) );
1458 0 : void * votes = FD_SCRATCH_ALLOC_APPEND( l, fd_votes_align(), fd_votes_footprint( slot_max, vtr_max ) );
1459 0 : void * tower = FD_SCRATCH_ALLOC_APPEND( l, fd_tower_align(), fd_tower_footprint() );
1460 0 : void * scratch_tower = FD_SCRATCH_ALLOC_APPEND( l, fd_tower_align(), fd_tower_footprint() );
1461 0 : void * tower_blocks = FD_SCRATCH_ALLOC_APPEND( l, fd_tower_blocks_align(), fd_tower_blocks_footprint( slot_max ) );
1462 0 : void * tower_lockos = FD_SCRATCH_ALLOC_APPEND( l, fd_tower_lockos_align(), fd_tower_lockos_footprint( slot_max, vtr_max ) );
1463 0 : void * tower_stakes = FD_SCRATCH_ALLOC_APPEND( l, fd_tower_stakes_align(), fd_tower_stakes_footprint( slot_max, vtr_max ) );
1464 0 : void * tower_voters = FD_SCRATCH_ALLOC_APPEND( l, fd_tower_voters_align(), fd_tower_voters_footprint( vtr_max ) );
1465 0 : uchar * voter_towers = FD_SCRATCH_ALLOC_APPEND( l, FD_TOWER_ALIGN, FD_TOWER_FOOTPRINT * vtr_max );
1466 0 : void * publishes = FD_SCRATCH_ALLOC_APPEND( l, publishes_align(), publishes_footprint( pub_max ) );
1467 0 : FD_SCRATCH_ALLOC_FINI( l, scratch_align() );
1468 :
1469 0 : ctx->wksp = topo->workspaces[ topo->objs[ tile->tile_obj_id ].wksp_id ].wksp;
1470 0 : ctx->identity_keyswitch = fd_keyswitch_join( fd_topo_obj_laddr( topo, tile->id_keyswitch_obj_id ) );
1471 0 : ctx->auth_vtr_keyswitch = fd_keyswitch_join( fd_topo_obj_laddr( topo, tile->av_keyswitch_obj_id ) );
1472 0 : (void)auth_vtr; /* privileged_init */
1473 0 : ctx->eqvoc = fd_eqvoc_join ( fd_eqvoc_new ( eqvoc, slot_max, fec_max, per_vtr_max, vtr_max, ctx->seed ) );
1474 0 : ctx->ghost = fd_ghost_join ( fd_ghost_new ( ghost, blk_max, vtr_max, ctx->seed ) );
1475 0 : ctx->hfork = fd_hfork_join ( fd_hfork_new ( hfork, per_vtr_max, vtr_max, ctx->seed ) );
1476 0 : ctx->votes = fd_votes_join ( fd_votes_new ( votes, slot_max, vtr_max, ctx->seed ) );
1477 0 : ctx->tower = fd_tower_join ( fd_tower_new ( tower ) );
1478 0 : ctx->scratch_tower = fd_tower_join ( fd_tower_new ( scratch_tower ) );
1479 0 : ctx->tower_blocks = fd_tower_blocks_join ( fd_tower_blocks_new ( tower_blocks, slot_max, ctx->seed ) );
1480 0 : ctx->tower_lockos = fd_tower_lockos_join ( fd_tower_lockos_new ( tower_lockos, slot_max, vtr_max, ctx->seed ) );
1481 0 : ctx->tower_stakes = fd_tower_stakes_join ( fd_tower_stakes_new ( tower_stakes, slot_max, vtr_max, ctx->seed ) );
1482 0 : ctx->tower_voters = fd_tower_voters_join ( fd_tower_voters_new ( tower_voters, vtr_max ) );
1483 0 : ctx->voter_towers = voter_towers;
1484 0 : for( ulong i = 0; i < vtr_max; i++ ) {
1485 0 : fd_tower_voters_t * entry = fd_tower_voters_push_tail_nocopy( ctx->tower_voters );
1486 0 : entry->tower = fd_tower_join( fd_tower_new( voter_towers + i * FD_TOWER_FOOTPRINT ) );
1487 0 : }
1488 0 : fd_tower_voters_remove_all( ctx->tower_voters );
1489 0 : ctx->publishes = publishes_join ( publishes_new ( publishes, pub_max ) );
1490 0 : ctx->mleaders = fd_multi_epoch_leaders_join( fd_multi_epoch_leaders_new( ctx->mleaders_mem ) );
1491 :
1492 0 : FD_TEST( ctx->wksp );
1493 0 : FD_TEST( ctx->identity_keyswitch );
1494 0 : FD_TEST( ctx->auth_vtr_keyswitch );
1495 0 : FD_TEST( ctx->auth_vtr );
1496 0 : FD_TEST( ctx->eqvoc );
1497 0 : FD_TEST( ctx->ghost );
1498 0 : FD_TEST( ctx->hfork );
1499 0 : FD_TEST( ctx->votes );
1500 0 : FD_TEST( ctx->tower );
1501 0 : FD_TEST( ctx->scratch_tower );
1502 0 : FD_TEST( ctx->tower_blocks );
1503 0 : FD_TEST( ctx->tower_lockos );
1504 0 : FD_TEST( ctx->tower_stakes );
1505 0 : FD_TEST( ctx->tower_voters );
1506 0 : FD_TEST( ctx->voter_towers );
1507 0 : FD_TEST( ctx->publishes );
1508 0 : FD_TEST( ctx->mleaders );
1509 :
1510 0 : memset( ctx->chunks, 0, sizeof(ctx->chunks) );
1511 0 : memset( &ctx->compact_tower_sync_serde, 0, sizeof(ctx->compact_tower_sync_serde) );
1512 0 : memset( ctx->vote_txn, 0, sizeof(ctx->vote_txn) );
1513 :
1514 0 : ctx->halt_signing = 0;
1515 0 : ctx->hard_fork_fatal = tile->tower.hard_fork_fatal;
1516 0 : ctx->shred_version = 0;
1517 0 : ctx->init_slot = ULONG_MAX;
1518 0 : ctx->root_slot = ULONG_MAX;
1519 :
1520 0 : ulong banks_obj_id = fd_pod_query_ulong( topo->props, "banks", ULONG_MAX );
1521 0 : FD_TEST( banks_obj_id!=ULONG_MAX );
1522 0 : ctx->banks = fd_banks_join( fd_topo_obj_laddr( topo, banks_obj_id ) );
1523 0 : FD_TEST( ctx->banks );
1524 :
1525 0 : fd_accdb_init_from_topo( ctx->accdb, topo, tile, tile->tower.accdb_max_depth );
1526 :
1527 0 : FD_TEST( tile->in_cnt<sizeof(ctx->in_kind)/sizeof(ctx->in_kind[0]) );
1528 0 : for( ulong i=0UL; i<tile->in_cnt; i++ ) {
1529 0 : fd_topo_link_t * link = &topo->links[ tile->in_link_id[ i ] ];
1530 0 : fd_topo_wksp_t * link_wksp = &topo->workspaces[ topo->objs[ link->dcache_obj_id ].wksp_id ];
1531 :
1532 0 : if ( FD_LIKELY( !strcmp( link->name, "dedup_resolv" ) ) ) ctx->in_kind[ i ] = IN_KIND_DEDUP;
1533 0 : else if( FD_LIKELY( !strcmp( link->name, "replay_epoch" ) ) ) ctx->in_kind[ i ] = IN_KIND_EPOCH;
1534 0 : else if( FD_LIKELY( !strcmp( link->name, "gossip_out" ) ) ) ctx->in_kind[ i ] = IN_KIND_GOSSIP;
1535 0 : else if( FD_LIKELY( !strcmp( link->name, "ipecho_out" ) ) ) ctx->in_kind[ i ] = IN_KIND_IPECHO;
1536 0 : else if( FD_LIKELY( !strcmp( link->name, "replay_out" ) ) ) ctx->in_kind[ i ] = IN_KIND_REPLAY;
1537 0 : else if( FD_LIKELY( !strcmp( link->name, "shred_out" ) ) ) ctx->in_kind[ i ] = IN_KIND_SHRED;
1538 0 : else FD_LOG_ERR(( "tower tile has unexpected input link %lu %s", i, link->name ));
1539 :
1540 0 : ctx->in[ i ].mcache_only = !link->mtu;
1541 0 : if( FD_LIKELY( !ctx->in[ i ].mcache_only ) ) {
1542 0 : ctx->in[ i ].mem = link_wksp->wksp;
1543 0 : ctx->in[ i ].mtu = link->mtu;
1544 0 : ctx->in[ i ].chunk0 = fd_dcache_compact_chunk0( ctx->in[ i ].mem, link->dcache );
1545 0 : ctx->in[ i ].wmark = fd_dcache_compact_wmark ( ctx->in[ i ].mem, link->dcache, link->mtu );
1546 0 : }
1547 0 : }
1548 :
1549 0 : ctx->out_mem = topo->workspaces[ topo->objs[ topo->links[ tile->out_link_id[ 0 ] ].dcache_obj_id ].wksp_id ].wksp;
1550 0 : ctx->out_chunk0 = fd_dcache_compact_chunk0( ctx->out_mem, topo->links[ tile->out_link_id[ 0 ] ].dcache );
1551 0 : ctx->out_wmark = fd_dcache_compact_wmark ( ctx->out_mem, topo->links[ tile->out_link_id[ 0 ] ].dcache, topo->links[ tile->out_link_id[ 0 ] ].mtu );
1552 0 : ctx->out_chunk = ctx->out_chunk0;
1553 0 : ctx->out_seq = 0UL;
1554 :
1555 0 : memset( &ctx->metrics, 0, sizeof(ctx->metrics) );
1556 0 : ctx->metrics.last_vote_slot = ULONG_MAX;
1557 0 : }
1558 :
1559 : static ulong
1560 : populate_allowed_seccomp( fd_topo_t const * topo,
1561 : fd_topo_tile_t const * tile,
1562 : ulong out_cnt,
1563 0 : struct sock_filter * out ) {
1564 0 : void * scratch = fd_topo_obj_laddr( topo, tile->tile_obj_id );
1565 0 : FD_SCRATCH_ALLOC_INIT( l, scratch );
1566 0 : fd_tower_tile_t * ctx = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_tower_tile_t), sizeof(fd_tower_tile_t) );
1567 :
1568 0 : populate_sock_filter_policy_fd_tower_tile( out_cnt, out, (uint)fd_log_private_logfile_fd(), (uint)ctx->checkpt_fd, (uint)ctx->restore_fd );
1569 0 : return sock_filter_policy_fd_tower_tile_instr_cnt;
1570 0 : }
1571 :
1572 : static ulong
1573 : populate_allowed_fds( fd_topo_t const * topo,
1574 : fd_topo_tile_t const * tile,
1575 : ulong out_fds_cnt,
1576 0 : int * out_fds ) {
1577 0 : void * scratch = fd_topo_obj_laddr( topo, tile->tile_obj_id );
1578 0 : FD_SCRATCH_ALLOC_INIT( l, scratch );
1579 0 : fd_tower_tile_t * ctx = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_tower_tile_t), sizeof(fd_tower_tile_t) );
1580 :
1581 0 : if( FD_UNLIKELY( out_fds_cnt<4UL ) ) FD_LOG_ERR(( "out_fds_cnt %lu", out_fds_cnt ));
1582 :
1583 0 : ulong out_cnt = 0UL;
1584 0 : out_fds[ out_cnt++ ] = 2; /* stderr */
1585 0 : if( FD_LIKELY( -1!=fd_log_private_logfile_fd() ) )
1586 0 : out_fds[ out_cnt++ ] = fd_log_private_logfile_fd(); /* logfile */
1587 0 : if( FD_LIKELY( ctx->checkpt_fd!=-1 ) ) out_fds[ out_cnt++ ] = ctx->checkpt_fd;
1588 0 : if( FD_LIKELY( ctx->restore_fd!=-1 ) ) out_fds[ out_cnt++ ] = ctx->restore_fd;
1589 0 : return out_cnt;
1590 0 : }
1591 :
1592 0 : #define STEM_BURST (2UL) /* MAX( slot_confirmed, slot_rooted AND (slot_done OR slot_ignored) ) */
1593 0 : #define STEM_LAZY (128L*3000L) /* see explanation in fd_pack */
1594 :
1595 0 : #define STEM_CALLBACK_CONTEXT_TYPE fd_tower_tile_t
1596 0 : #define STEM_CALLBACK_CONTEXT_ALIGN alignof(fd_tower_tile_t)
1597 0 : #define STEM_CALLBACK_DURING_HOUSEKEEPING during_housekeeping
1598 0 : #define STEM_CALLBACK_METRICS_WRITE metrics_write
1599 0 : #define STEM_CALLBACK_AFTER_CREDIT after_credit
1600 0 : #define STEM_CALLBACK_RETURNABLE_FRAG returnable_frag
1601 :
1602 : #include "../../disco/stem/fd_stem.c"
1603 :
1604 : fd_topo_run_tile_t fd_tile_tower = {
1605 : .name = "tower",
1606 : .populate_allowed_seccomp = populate_allowed_seccomp,
1607 : .populate_allowed_fds = populate_allowed_fds,
1608 : .scratch_align = scratch_align,
1609 : .scratch_footprint = scratch_footprint,
1610 : .unprivileged_init = unprivileged_init,
1611 : .privileged_init = privileged_init,
1612 : .run = stem_run,
1613 : };
|