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