Line data Source code
1 : #include "fd_tower_tile.h"
2 : #include "generated/fd_tower_tile_seccomp.h"
3 :
4 : #include "../genesis/fd_genesi_tile.h"
5 : #include "../replay/fd_replay_tile.h"
6 : #include "../../choreo/ghost/fd_ghost.h"
7 : #include "../../choreo/tower/fd_tower.h"
8 : #include "../../choreo/voter/fd_voter.h"
9 : #include "../../disco/keyguard/fd_keyload.h"
10 : #include "../../disco/topo/fd_topo.h"
11 : #include "../../discof/restore/utils/fd_ssmsg.h"
12 : #include "../../ballet/lthash/fd_lthash.h"
13 : #include "../../flamenco/fd_flamenco_base.h"
14 : #include "../../flamenco/runtime/fd_system_ids.h"
15 :
16 : #include <errno.h>
17 : #include <fcntl.h>
18 :
19 : #define LOGGING 0
20 :
21 0 : #define IN_KIND_GENESIS (0)
22 0 : #define IN_KIND_SNAP (1)
23 0 : #define IN_KIND_REPLAY (2)
24 :
25 : struct fd_tower_tile_in {
26 : fd_wksp_t * mem;
27 : ulong chunk0;
28 : ulong wmark;
29 : ulong mtu;
30 : };
31 :
32 : typedef struct fd_tower_tile_in fd_tower_tile_in_t;
33 :
34 : struct fd_tower_tile {
35 : fd_pubkey_t identity_key[1];
36 : fd_pubkey_t vote_acc[1];
37 :
38 : int initialized;
39 :
40 : int checkpt_fd;
41 : int restore_fd;
42 :
43 : fd_epoch_t * epoch;
44 : fd_ghost_t * ghost;
45 : fd_tower_t * scratch;
46 : fd_tower_t * tower;
47 : uchar * voters;
48 :
49 : long ts; /* tower timestamp */
50 :
51 : fd_snapshot_manifest_t manifest;
52 : fd_replay_slot_completed_t replay_slot_info;
53 :
54 : ulong replay_towers_cnt;
55 : fd_replay_tower_t replay_towers[ FD_REPLAY_TOWER_VOTE_ACC_MAX ];
56 :
57 : fd_tower_t * vote_towers[ FD_REPLAY_TOWER_VOTE_ACC_MAX ];
58 : fd_pubkey_t vote_keys[ FD_REPLAY_TOWER_VOTE_ACC_MAX ];
59 :
60 : uchar vote_state[ FD_REPLAY_TOWER_VOTE_ACC_MAX ]; /* our vote state */
61 :
62 : int in_kind[ 64UL ];
63 : fd_tower_tile_in_t in[ 64UL ];
64 :
65 : fd_wksp_t * out_mem;
66 : ulong out_chunk0;
67 : ulong out_wmark;
68 : ulong out_chunk;
69 : };
70 :
71 : typedef struct fd_tower_tile fd_tower_tile_t;
72 :
73 : FD_FN_CONST static inline ulong
74 0 : scratch_align( void ) {
75 0 : return 128UL;
76 0 : }
77 :
78 : FD_FN_PURE static inline ulong
79 0 : scratch_footprint( fd_topo_tile_t const * tile ) {
80 0 : (void)tile;
81 :
82 0 : ulong l = FD_LAYOUT_INIT;
83 0 : l = FD_LAYOUT_APPEND( l, alignof(fd_tower_tile_t), sizeof(fd_tower_tile_t) );
84 0 : l = FD_LAYOUT_APPEND( l, fd_epoch_align(), fd_epoch_footprint( FD_REPLAY_TOWER_VOTE_ACC_MAX ) );
85 0 : l = FD_LAYOUT_APPEND( l, fd_ghost_align(), fd_ghost_footprint( FD_BLOCK_MAX ) );
86 0 : l = FD_LAYOUT_APPEND( l, fd_tower_align(), fd_tower_footprint() );
87 0 : l = FD_LAYOUT_APPEND( l, fd_tower_align(), fd_tower_footprint()*FD_REPLAY_TOWER_VOTE_ACC_MAX );
88 0 : return FD_LAYOUT_FINI( l, scratch_align() );
89 0 : }
90 :
91 : static void
92 0 : update_ghost( fd_tower_tile_t * ctx ) {
93 0 : fd_voter_t * epoch_voters = fd_epoch_voters( ctx->epoch );
94 0 : for( ulong i=0UL; i<ctx->replay_towers_cnt; i++ ) {
95 0 : fd_replay_tower_t const * replay_tower = &ctx->replay_towers[ i ];
96 0 : fd_pubkey_t const * pubkey = &replay_tower->key;
97 0 : fd_voter_state_t const * voter_state = (fd_voter_state_t const *)fd_type_pun_const( replay_tower->acc );
98 0 : fd_tower_t * tower = ctx->vote_towers[ i ];
99 :
100 : /* Look up the voter for this vote account */
101 0 : fd_voter_t * voter = fd_epoch_voters_query( epoch_voters, *pubkey, NULL );
102 0 : if( FD_UNLIKELY( !voter ) ) {
103 : /* This means that the cached list of epoch voters is not in sync
104 : with the list passed through from replay. This likely means
105 : that we have crossed an epoch boundary and the epoch_voter list
106 : has not been updated.
107 :
108 : TODO: update the set of account in epoch_voter's to match the
109 : list received from replay, so that epoch_voters is
110 : correct across epoch boundaries. */
111 0 : FD_LOG_CRIT(( "voter %s was not in epoch voters", FD_BASE58_ENC_32_ALLOCA( pubkey ) ));
112 0 : }
113 :
114 0 : voter->stake = replay_tower->stake; /* update the voters stake */
115 :
116 0 : if( FD_UNLIKELY( !fd_voter_state_cnt( voter_state ) ) ) continue; /* skip voters with no votes */
117 :
118 0 : ulong vote = fd_tower_votes_peek_tail( tower )->slot; /* peek last vote from the tower */
119 0 : ulong root = fd_voter_root_slot( voter_state );
120 :
121 : /* Only process votes for slots >= root. */
122 0 : if( FD_LIKELY( vote != FD_SLOT_NULL && vote >= fd_ghost_root( ctx->ghost )->slot ) ) {
123 0 : fd_ghost_ele_t const * ele = fd_ghost_query_const( ctx->ghost, fd_ghost_hash( ctx->ghost, vote ) );
124 :
125 : /* It is an invariant violation if the vote slot is not in ghost.
126 : These votes come from replay ie. on-chain towers stored in vote
127 : accounts which implies every vote slot must have been processed
128 : by the vote program (ie. replayed) and therefore in ghost. */
129 :
130 0 : if( FD_UNLIKELY( !ele ) ) FD_LOG_CRIT(( "voter %s's vote slot %lu was not in ghost", FD_BASE58_ENC_32_ALLOCA( &voter->key ), vote ));
131 0 : fd_ghost_replay_vote( ctx->ghost, voter, &ele->key );
132 0 : }
133 :
134 : /* Check if this voter's root >= ghost root. We can't process roots
135 : before our own root because it was already pruned. */
136 :
137 0 : if( FD_LIKELY( root!=FD_SLOT_NULL && root>=fd_ghost_root( ctx->ghost )->slot ) ) {
138 0 : fd_ghost_ele_t const * ele = fd_ghost_query( ctx->ghost, fd_ghost_hash( ctx->ghost, root ) );
139 :
140 : /* Error if the node's root slot is not in ghost. This is an
141 : invariant violation, because we know their tower must be on the
142 : same fork as this current one that we're processing, and so by
143 : definition their root slot must be in our ghost (ie. we can't
144 : have rooted past it or be on a different fork). */
145 :
146 0 : if( FD_UNLIKELY( !ele ) ) FD_LOG_CRIT(( "voter %s's root slot %lu was not in ghost", FD_BASE58_ENC_32_ALLOCA( &voter->key ), root ));
147 :
148 0 : fd_ghost_rooted_vote( ctx->ghost, voter, root );
149 0 : }
150 0 : }
151 0 : }
152 :
153 : static void
154 : replay_slot_completed( fd_tower_tile_t * ctx,
155 : fd_replay_slot_completed_t * slot_info,
156 : ulong tsorig,
157 0 : fd_stem_context_t * stem ) {
158 : /* If we have not received any votes, something is wrong. */
159 0 : if( FD_UNLIKELY( !ctx->replay_towers_cnt ) ) {
160 : /* TODO: This is not correct. It is fine and valid to receive a
161 : block with no votes, we don't want to return here as we still
162 : want to vote on the block. */
163 0 : FD_LOG_WARNING(( "No vote states received from replay. No votes will be sent"));
164 0 : return;
165 0 : }
166 :
167 : /* Parse the replay vote towers */
168 0 : for( ulong i=0UL; i<ctx->replay_towers_cnt; i++ ) {
169 0 : fd_tower_votes_remove_all( ctx->vote_towers[ i ] );
170 0 : fd_tower_from_vote_acc_data( ctx->replay_towers[ i ].acc, ctx->vote_towers[ i ] );
171 0 : ctx->vote_keys[ i ] = ctx->replay_towers[ i ].key;
172 :
173 0 : if( FD_UNLIKELY( 0==memcmp( &ctx->vote_keys[ i ], ctx->vote_acc, sizeof(fd_pubkey_t) ) ) ) {
174 :
175 : /* If this is our vote account, and our tower has not been
176 : initialized, initialize it with our vote state */
177 :
178 0 : if( FD_UNLIKELY( fd_tower_votes_empty( ctx->tower ) ) ) {
179 0 : fd_tower_from_vote_acc_data( ctx->replay_towers[i].acc, ctx->tower );
180 0 : }
181 :
182 : /* Copy in our voter state */
183 0 : memcpy( &ctx->vote_state, ctx->replay_towers[ i ].acc, ctx->replay_towers[ i ].acc_sz );
184 0 : }
185 0 : }
186 :
187 : /* Update ghost with the vote account states received from replay. */
188 :
189 0 : fd_ghost_ele_t const * ghost_ele = fd_ghost_insert( ctx->ghost, &slot_info->parent_block_id, slot_info->slot, &slot_info->block_id, ctx->epoch->total_stake );
190 0 : FD_TEST( ghost_ele );
191 0 : update_ghost( ctx );
192 :
193 : /* Populate the out frag. */
194 :
195 0 : fd_tower_slot_done_t * msg = fd_chunk_to_laddr( ctx->out_mem, ctx->out_chunk );
196 :
197 : /* 1. Determine next slot to vote for, if one exists. */
198 :
199 0 : msg->vote_slot = fd_tower_vote_slot( ctx->tower, ctx->epoch, ctx->vote_keys, ctx->vote_towers, ctx->replay_towers_cnt, ctx->ghost );
200 0 : msg->new_root = 0UL;
201 :
202 : /* 2. Determine new root, if there is one. A new vote slot can result
203 : in a new root but not always. */
204 :
205 0 : if( FD_LIKELY( msg->vote_slot!=FD_SLOT_NULL ) ) {
206 0 : msg->root_slot = fd_tower_vote( ctx->tower, msg->vote_slot );
207 0 : fd_hash_t const * root_block_id = fd_ghost_hash( ctx->ghost, msg->root_slot );
208 0 : if( FD_LIKELY( root_block_id ) ) {
209 0 : msg->root_block_id = *root_block_id;
210 0 : msg->new_root = 1;
211 0 : fd_ghost_publish( ctx->ghost, &msg->root_block_id );
212 0 : }
213 0 : }
214 :
215 : /* 3. Populate vote_txn with the current tower (regardless of whether
216 : there was a new vote slot or not). */
217 :
218 0 : fd_lockout_offset_t lockouts[ FD_TOWER_VOTE_MAX ];
219 0 : fd_txn_p_t txn[1];
220 0 : fd_tower_to_vote_txn( ctx->tower, msg->root_slot, lockouts, &slot_info->bank_hash, &slot_info->block_hash, ctx->identity_key, ctx->identity_key, ctx->vote_acc, txn );
221 0 : FD_TEST( !fd_tower_votes_empty( ctx->tower ) );
222 0 : FD_TEST( txn->payload_sz && txn->payload_sz<=FD_TPU_MTU );
223 0 : fd_memcpy( msg->vote_txn, txn->payload, txn->payload_sz );
224 0 : msg->vote_txn_sz = txn->payload_sz;
225 :
226 : /* 4. Determine next slot to reset leader pipeline to. */
227 :
228 0 : msg->reset_slot = fd_tower_reset_slot( ctx->tower, ctx->epoch, ctx->ghost );
229 0 : msg->reset_block_id = *fd_ghost_hash( ctx->ghost, msg->reset_slot ); /* FIXME fd_ghost_hash is a naive lookup but reset_slot only ever refers to the confirmed duplicate */
230 :
231 : /* Publish the frag */
232 :
233 0 : fd_stem_publish( stem, 0UL, 0UL, ctx->out_chunk, sizeof(fd_tower_slot_done_t), 0UL, tsorig, fd_frag_meta_ts_comp( fd_tickcount() ) );
234 0 : ctx->out_chunk = fd_dcache_compact_next( ctx->out_chunk, sizeof(fd_tower_slot_done_t), ctx->out_chunk0, ctx->out_wmark );
235 :
236 : # if LOGGING
237 : fd_ghost_print( ctx->ghost, ctx->epoch->total_stake, fd_ghost_root( ctx->ghost ) );
238 : fd_tower_print( ctx->tower, msg->root_slot );
239 : # endif
240 0 : }
241 :
242 : static void
243 : init_genesis( fd_tower_tile_t * ctx,
244 0 : fd_genesis_solana_global_t const * genesis ) {
245 0 : fd_hash_t manifest_block_id = { .ul = { 0xf17eda2ce7b1d } }; /* FIXME manifest_block_id */
246 0 : fd_ghost_init( ctx->ghost, 0UL, &manifest_block_id );
247 :
248 0 : fd_voter_t * epoch_voters = fd_epoch_voters( ctx->epoch );
249 :
250 0 : fd_pubkey_account_pair_global_t const * accounts = fd_genesis_solana_accounts_join( genesis );
251 0 : for( ulong i=0UL; i<genesis->accounts_len; i++ ) {
252 0 : fd_solana_account_global_t const * account = &accounts[ i ].account;
253 0 : if( FD_LIKELY( memcmp( account->owner.key, fd_solana_stake_program_id.key, sizeof(fd_pubkey_t) ) ) ) continue;
254 :
255 0 : uchar const * acc_data = fd_solana_account_data_join( account );
256 :
257 0 : fd_stake_state_v2_t stake_state;
258 0 : if( FD_UNLIKELY( !fd_bincode_decode_static( stake_state_v2, &stake_state, acc_data, account->data_len, NULL ) ) ) {
259 0 : FD_LOG_ERR(( "Failed to deserialize genesis stake account %s", FD_BASE58_ENC_32_ALLOCA( accounts[ i ].key.uc ) ));
260 0 : }
261 :
262 0 : if( FD_UNLIKELY( !fd_stake_state_v2_is_stake( &stake_state ) ) ) continue;
263 0 : if( FD_UNLIKELY( !stake_state.inner.stake.stake.delegation.stake ) ) continue;
264 :
265 0 : fd_pubkey_t const * pubkey = &stake_state.inner.stake.stake.delegation.voter_pubkey;
266 :
267 0 : fd_voter_t * voter = fd_epoch_voters_insert( epoch_voters, *pubkey );
268 :
269 0 : voter->stake = stake_state.inner.stake.stake.delegation.stake;
270 0 : ctx->epoch->total_stake += voter->stake;
271 0 : }
272 0 : }
273 :
274 : static void
275 : snapshot_done( fd_tower_tile_t * ctx,
276 0 : fd_snapshot_manifest_t const * manifest ) {
277 0 : fd_hash_t manifest_block_id = { .ul = { 0xf17eda2ce7b1d } }; /* FIXME manifest_block_id */
278 0 : fd_ghost_init( ctx->ghost, manifest->slot, &manifest_block_id );
279 :
280 : /* As per the documentation in fd_ssmsg.h, we use
281 : manifest->epoch_stakes[1] to initialize the epoch voters. These
282 : correspond to the amount staked to each vote account at the
283 : beginning of the current epoch:
284 :
285 : manifest->epoch_stakes[0] represents the stakes used to generate
286 : the leader schedule at the end of the current epoch. These are the
287 : stakes as of the end of two epochs ago.
288 :
289 : manifest->epoch_stakes[1] represents the stakes used to generate
290 : the leader schedule at the end of the next epoch. These are the
291 : stakes as of the end of the previous epoch, so these will be the
292 : stakes throughout the current epoch. */
293 0 : fd_voter_t * epoch_voters = fd_epoch_voters( ctx->epoch );
294 0 : fd_snapshot_manifest_epoch_stakes_t const * epoch_stakes = &manifest->epoch_stakes[ 1 ];
295 0 : for( ulong i=0UL; i<epoch_stakes->vote_stakes_len; i++ ) {
296 0 : if( FD_UNLIKELY( !epoch_stakes->vote_stakes[ i ].stake ) ) continue;
297 :
298 0 : fd_pubkey_t const * pubkey = (fd_pubkey_t const *)epoch_stakes->vote_stakes[ i ].vote;
299 :
300 0 : #if FD_EPOCH_USE_HANDHOLDING
301 0 : FD_TEST( !fd_epoch_voters_query( epoch_voters, *pubkey, NULL ) );
302 0 : FD_TEST( fd_epoch_voters_key_cnt( epoch_voters ) < fd_epoch_voters_key_max( epoch_voters ) );
303 0 : #endif
304 :
305 0 : fd_voter_t * voter = fd_epoch_voters_insert( epoch_voters, *pubkey );
306 :
307 0 : #if FD_EPOCH_USE_HANDHOLDING
308 0 : FD_TEST( 0==memcmp( voter->key.uc, pubkey->uc, sizeof(fd_pubkey_t) ) );
309 0 : FD_TEST( fd_epoch_voters_query( epoch_voters, voter->key, NULL ) );
310 0 : #endif
311 :
312 0 : voter->stake = epoch_stakes->vote_stakes[ i ].stake;
313 0 : voter->replay_vote.slot = FD_SLOT_NULL;
314 0 : voter->gossip_vote.slot = FD_SLOT_NULL;
315 0 : voter->rooted_vote.slot = FD_SLOT_NULL;
316 0 : ctx->epoch->total_stake += voter->stake;
317 0 : }
318 0 : }
319 :
320 : static inline int
321 : returnable_frag( fd_tower_tile_t * ctx,
322 : ulong in_idx,
323 : ulong seq,
324 : ulong sig,
325 : ulong chunk,
326 : ulong sz,
327 : ulong ctl,
328 : ulong tsorig,
329 : ulong tspub,
330 0 : fd_stem_context_t * stem ) {
331 0 : (void)seq;
332 0 : (void)tspub;
333 :
334 0 : if( FD_LIKELY( ctx->in_kind[ in_idx ]==IN_KIND_GENESIS ) ) {
335 0 : if( FD_LIKELY( sig==GENESI_SIG_BOOTSTRAP_COMPLETED ) ) {
336 0 : init_genesis( ctx, fd_type_pun( (uchar*)fd_chunk_to_laddr( ctx->in[ in_idx ].mem, chunk )+sizeof(fd_lthash_value_t)+sizeof(fd_hash_t) ) );
337 0 : ctx->initialized = 1;
338 0 : }
339 0 : } else if( FD_LIKELY( ctx->in_kind[ in_idx ]==IN_KIND_SNAP ) ) {
340 0 : if( FD_UNLIKELY( fd_ssmsg_sig_message( sig )==FD_SSMSG_DONE ) ) {
341 0 : snapshot_done( ctx, &ctx->manifest );
342 0 : ctx->initialized = 1;
343 0 : } else {
344 0 : if( FD_UNLIKELY( chunk<ctx->in[ in_idx ].chunk0 || chunk>ctx->in[ in_idx ].wmark || sz>ctx->in[ in_idx ].mtu ) )
345 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 ));
346 :
347 0 : fd_memcpy( &ctx->manifest, fd_chunk_to_laddr( ctx->in[ in_idx ].mem, chunk ), sizeof(fd_snapshot_manifest_t) );
348 0 : }
349 0 : } else if( FD_LIKELY( ctx->in_kind[ in_idx ]==IN_KIND_REPLAY ) ) {
350 0 : if( FD_UNLIKELY( chunk<ctx->in[ in_idx ].chunk0 || chunk>ctx->in[ in_idx ].wmark || sz>ctx->in[ in_idx ].mtu ) )
351 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 ));
352 :
353 : /* If we haven't initialized from either genesis or a snapshot yet,
354 : we cannot process any frags from replay as it's a race condition,
355 : just wait until we initialize and then process. */
356 0 : if( FD_UNLIKELY( !ctx->initialized ) ) return 1;
357 :
358 0 : if( FD_LIKELY( sig==REPLAY_SIG_SLOT_COMPLETED ) ) {
359 0 : fd_memcpy( &ctx->replay_slot_info, fd_chunk_to_laddr( ctx->in[ in_idx ].mem, chunk ), sizeof(fd_replay_slot_completed_t) );
360 0 : } else if( FD_LIKELY( sig==REPLAY_SIG_VOTE_STATE ) ) {
361 0 : if( FD_UNLIKELY( fd_frag_meta_ctl_som( ctl ) ) ) ctx->replay_towers_cnt = 0;
362 :
363 0 : if( FD_UNLIKELY( ctx->replay_towers_cnt>=FD_REPLAY_TOWER_VOTE_ACC_MAX ) ) FD_LOG_ERR(( "tower received more vote states than expected" ));
364 0 : memcpy( &ctx->replay_towers[ ctx->replay_towers_cnt ], fd_chunk_to_laddr( ctx->in[ in_idx ].mem, chunk ), sizeof(fd_replay_tower_t) );
365 0 : ctx->replay_towers_cnt++;
366 :
367 0 : if( FD_UNLIKELY( fd_frag_meta_ctl_eom( ctl ) ) ) replay_slot_completed( ctx, &ctx->replay_slot_info, tsorig, stem );
368 0 : }
369 0 : } else {
370 0 : FD_LOG_ERR(( "unexpected input kind %d", ctx->in_kind[ in_idx ] ));
371 0 : }
372 :
373 0 : return 0;
374 0 : }
375 :
376 : static void
377 : privileged_init( fd_topo_t * topo,
378 0 : fd_topo_tile_t * tile ) {
379 0 : void * scratch = fd_topo_obj_laddr( topo, tile->tile_obj_id );
380 0 : FD_SCRATCH_ALLOC_INIT( l, scratch );
381 0 : fd_tower_tile_t * ctx = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_tower_tile_t), sizeof(fd_tower_tile_t) );
382 :
383 0 : if( FD_UNLIKELY( !strcmp( tile->tower.identity_key_path, "" ) ) )
384 0 : FD_LOG_ERR(( "identity_key_path not set" ));
385 :
386 0 : ctx->identity_key[ 0 ] = *(fd_pubkey_t const *)fd_type_pun_const( fd_keyload_load( tile->tower.identity_key_path, /* pubkey only: */ 1 ) );
387 :
388 : /* The vote key can be specified either directly as a base58 encoded
389 : pubkey, or as a file path. We first try to decode as a pubkey.
390 :
391 : TODO: The "vote_acc_path" should be renamed, as it might not be
392 : a path. */
393 0 : uchar * vote_key = fd_base58_decode_32( tile->tower.vote_acc_path, ctx->vote_acc->uc );
394 0 : if( FD_UNLIKELY( !vote_key ) ) {
395 0 : ctx->vote_acc[ 0 ] = *(fd_pubkey_t const *)fd_type_pun_const( fd_keyload_load( tile->tower.vote_acc_path, /* pubkey only: */ 1 ) );
396 0 : }
397 :
398 0 : char path[ PATH_MAX ];
399 0 : FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "%s/tower-1_9-%s.bin.new", tile->tower.ledger_path, FD_BASE58_ENC_32_ALLOCA( ctx->identity_key->uc ) ) );
400 0 : ctx->checkpt_fd = open( path, O_WRONLY|O_CREAT|O_TRUNC, 0600 );
401 0 : if( FD_UNLIKELY( -1==ctx->checkpt_fd ) ) FD_LOG_ERR(( "open(`%s`) failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
402 :
403 0 : FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "%s/tower-1_9-%s.bin", tile->tower.ledger_path, FD_BASE58_ENC_32_ALLOCA( ctx->identity_key->uc ) ) );
404 0 : ctx->restore_fd = open( path, O_RDONLY );
405 0 : if( FD_UNLIKELY( -1==ctx->restore_fd && errno!=ENOENT ) ) FD_LOG_WARNING(( "open(`%s`) failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
406 0 : }
407 :
408 : static void
409 : unprivileged_init( fd_topo_t * topo,
410 0 : fd_topo_tile_t * tile ) {
411 0 : void * scratch = fd_topo_obj_laddr( topo, tile->tile_obj_id );
412 :
413 0 : FD_SCRATCH_ALLOC_INIT( l, scratch );
414 0 : fd_tower_tile_t * ctx = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_tower_tile_t), sizeof(fd_tower_tile_t) );
415 0 : void * _epoch = FD_SCRATCH_ALLOC_APPEND( l, fd_epoch_align(), fd_epoch_footprint( FD_REPLAY_TOWER_VOTE_ACC_MAX ) );
416 0 : void * _ghost = FD_SCRATCH_ALLOC_APPEND( l, fd_ghost_align(), fd_ghost_footprint( FD_BLOCK_MAX ) );
417 0 : void * _tower = FD_SCRATCH_ALLOC_APPEND( l, fd_tower_align(), fd_tower_footprint() );
418 0 : void * _vote_towers = FD_SCRATCH_ALLOC_APPEND( l, fd_tower_align(), fd_tower_footprint()*FD_REPLAY_TOWER_VOTE_ACC_MAX );
419 :
420 0 : ctx->epoch = fd_epoch_join( fd_epoch_new( _epoch, FD_REPLAY_TOWER_VOTE_ACC_MAX ) );
421 0 : FD_TEST( ctx->epoch );
422 :
423 0 : ctx->ghost = fd_ghost_join( fd_ghost_new( _ghost, FD_BLOCK_MAX, 42UL ) );
424 0 : FD_TEST( ctx->ghost );
425 :
426 0 : ctx->tower = fd_tower_join( fd_tower_new( _tower ) );
427 0 : FD_TEST( ctx->tower );
428 :
429 0 : for( ulong i=0UL; i<FD_REPLAY_TOWER_VOTE_ACC_MAX; i++ ) {
430 0 : ctx->vote_towers[ i ] = fd_tower_join( fd_tower_new( (uchar*)_vote_towers+(i*fd_tower_footprint() ) ) );
431 0 : FD_TEST( ctx->vote_towers[ i ] );
432 0 : }
433 :
434 0 : ctx->initialized = 0;
435 :
436 0 : ctx->replay_towers_cnt = 0UL;
437 :
438 0 : FD_TEST( tile->in_cnt<sizeof(ctx->in_kind)/sizeof(ctx->in_kind[0]) );
439 0 : for( ulong i=0UL; i<tile->in_cnt; i++ ) {
440 0 : fd_topo_link_t * link = &topo->links[ tile->in_link_id[ i ] ];
441 0 : fd_topo_wksp_t * link_wksp = &topo->workspaces[ topo->objs[ link->dcache_obj_id ].wksp_id ];
442 :
443 0 : if( FD_LIKELY( !strcmp( link->name, "genesi_out" ) ) ) ctx->in_kind[ i ] = IN_KIND_GENESIS;
444 0 : else if( FD_LIKELY( !strcmp( link->name, "snapin_manif" ) ) ) ctx->in_kind[ i ] = IN_KIND_SNAP;
445 0 : else if( FD_LIKELY( !strcmp( link->name, "replay_out" ) ) ) ctx->in_kind[ i ] = IN_KIND_REPLAY;
446 0 : else FD_LOG_ERR(( "tower tile has unexpected input link %lu %s", i, link->name ));
447 :
448 0 : ctx->in[ i ].mem = link_wksp->wksp;
449 0 : ctx->in[ i ].mtu = link->mtu;
450 0 : ctx->in[ i ].chunk0 = fd_dcache_compact_chunk0( ctx->in[ i ].mem, link->dcache );
451 0 : ctx->in[ i ].wmark = fd_dcache_compact_wmark ( ctx->in[ i ].mem, link->dcache, link->mtu );
452 0 : }
453 :
454 0 : ctx->out_mem = topo->workspaces[ topo->objs[ topo->links[ tile->out_link_id[ 0 ] ].dcache_obj_id ].wksp_id ].wksp;
455 0 : ctx->out_chunk0 = fd_dcache_compact_chunk0( ctx->out_mem, topo->links[ tile->out_link_id[ 0 ] ].dcache );
456 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 );
457 0 : ctx->out_chunk = ctx->out_chunk0;
458 0 : }
459 :
460 : static ulong
461 : populate_allowed_seccomp( fd_topo_t const * topo,
462 : fd_topo_tile_t const * tile,
463 : ulong out_cnt,
464 0 : struct sock_filter * out ) {
465 0 : void * scratch = fd_topo_obj_laddr( topo, tile->tile_obj_id );
466 0 : FD_SCRATCH_ALLOC_INIT( l, scratch );
467 0 : fd_tower_tile_t * ctx = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_tower_tile_t), sizeof(fd_tower_tile_t) );
468 :
469 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 );
470 0 : return sock_filter_policy_fd_tower_tile_instr_cnt;
471 0 : }
472 :
473 : static ulong
474 : populate_allowed_fds( fd_topo_t const * topo,
475 : fd_topo_tile_t const * tile,
476 : ulong out_fds_cnt,
477 0 : int * out_fds ) {
478 0 : void * scratch = fd_topo_obj_laddr( topo, tile->tile_obj_id );
479 0 : FD_SCRATCH_ALLOC_INIT( l, scratch );
480 0 : fd_tower_tile_t * ctx = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_tower_tile_t), sizeof(fd_tower_tile_t) );
481 :
482 0 : if( FD_UNLIKELY( out_fds_cnt<4UL ) ) FD_LOG_ERR(( "out_fds_cnt %lu", out_fds_cnt ));
483 :
484 0 : ulong out_cnt = 0UL;
485 0 : out_fds[ out_cnt++ ] = 2; /* stderr */
486 0 : if( FD_LIKELY( -1!=fd_log_private_logfile_fd() ) )
487 0 : out_fds[ out_cnt++ ] = fd_log_private_logfile_fd(); /* logfile */
488 0 : if( FD_LIKELY( ctx->checkpt_fd!=-1 ) ) out_fds[ out_cnt++ ] = ctx->checkpt_fd;
489 0 : if( FD_LIKELY( ctx->restore_fd!=-1 ) ) out_fds[ out_cnt++ ] = ctx->restore_fd;
490 0 : return out_cnt;
491 0 : }
492 :
493 0 : #define STEM_BURST (1UL)
494 :
495 0 : #define STEM_CALLBACK_CONTEXT_TYPE fd_tower_tile_t
496 0 : #define STEM_CALLBACK_CONTEXT_ALIGN alignof(fd_tower_tile_t)
497 0 : #define STEM_CALLBACK_RETURNABLE_FRAG returnable_frag
498 :
499 : #include "../../disco/stem/fd_stem.c"
500 :
501 : fd_topo_run_tile_t fd_tile_tower = {
502 : .name = "tower",
503 : .populate_allowed_seccomp = populate_allowed_seccomp,
504 : .populate_allowed_fds = populate_allowed_fds,
505 : .scratch_align = scratch_align,
506 : .scratch_footprint = scratch_footprint,
507 : .unprivileged_init = unprivileged_init,
508 : .privileged_init = privileged_init,
509 : .run = stem_run,
510 : };
|