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 :
200 : /* 2. Determine new root, if there is one. A new vote slot can result
201 : in a new root but not always. */
202 :
203 0 : if( FD_LIKELY( msg->vote_slot!=FD_SLOT_NULL ) ) {
204 0 : msg->root_slot = fd_tower_vote( ctx->tower, msg->vote_slot );
205 0 : fd_hash_t const * root_block_id = fd_ghost_hash( ctx->ghost, msg->root_slot );
206 0 : if( FD_LIKELY( root_block_id ) ) {
207 0 : msg->root_block_id = *root_block_id;
208 0 : msg->new_root = 1;
209 0 : fd_ghost_publish( ctx->ghost, &msg->root_block_id );
210 0 : } else {
211 0 : msg->root_block_id = (fd_hash_t){ 0 };
212 0 : msg->new_root = 0;
213 0 : }
214 0 : }
215 :
216 : /* 3. Populate vote_txn with the current tower (regardless of whether
217 : there was a new vote slot or not). */
218 :
219 0 : fd_lockout_offset_t lockouts[ FD_TOWER_VOTE_MAX ];
220 0 : fd_txn_p_t txn[1];
221 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 );
222 0 : FD_TEST( !fd_tower_votes_empty( ctx->tower ) );
223 0 : FD_TEST( txn->payload_sz && txn->payload_sz<=FD_TPU_MTU );
224 0 : fd_memcpy( msg->vote_txn, txn->payload, txn->payload_sz );
225 0 : msg->vote_txn_sz = txn->payload_sz;
226 :
227 : /* 4. Determine next slot to reset leader pipeline to. */
228 :
229 0 : msg->reset_slot = fd_tower_reset_slot( ctx->tower, ctx->epoch, ctx->ghost );
230 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 */
231 :
232 : /* Publish the frag */
233 :
234 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() ) );
235 0 : ctx->out_chunk = fd_dcache_compact_next( ctx->out_chunk, sizeof(fd_tower_slot_done_t), ctx->out_chunk0, ctx->out_wmark );
236 :
237 : # if LOGGING
238 : fd_ghost_print( ctx->ghost, ctx->epoch->total_stake, fd_ghost_root( ctx->ghost ) );
239 : fd_tower_print( ctx->tower, msg->root_slot );
240 : # endif
241 0 : }
242 :
243 : static void
244 : init_genesis( fd_tower_tile_t * ctx,
245 0 : fd_genesis_solana_global_t const * genesis ) {
246 0 : fd_hash_t manifest_block_id = { .ul = { 0xf17eda2ce7b1d } }; /* FIXME manifest_block_id */
247 0 : fd_ghost_init( ctx->ghost, 0UL, &manifest_block_id );
248 :
249 0 : fd_voter_t * epoch_voters = fd_epoch_voters( ctx->epoch );
250 :
251 0 : fd_pubkey_account_pair_global_t const * accounts = fd_genesis_solana_accounts_join( genesis );
252 0 : for( ulong i=0UL; i<genesis->accounts_len; i++ ) {
253 0 : fd_solana_account_global_t const * account = &accounts[ i ].account;
254 0 : if( FD_LIKELY( memcmp( account->owner.key, fd_solana_stake_program_id.key, sizeof(fd_pubkey_t) ) ) ) continue;
255 :
256 0 : uchar const * acc_data = fd_solana_account_data_join( account );
257 :
258 0 : fd_stake_state_v2_t stake_state;
259 0 : if( FD_UNLIKELY( !fd_bincode_decode_static( stake_state_v2, &stake_state, acc_data, account->data_len, NULL ) ) ) {
260 0 : FD_LOG_ERR(( "Failed to deserialize genesis stake account %s", FD_BASE58_ENC_32_ALLOCA( accounts[ i ].key.uc ) ));
261 0 : }
262 :
263 0 : if( FD_UNLIKELY( !fd_stake_state_v2_is_stake( &stake_state ) ) ) continue;
264 0 : if( FD_UNLIKELY( !stake_state.inner.stake.stake.delegation.stake ) ) continue;
265 :
266 0 : fd_pubkey_t const * pubkey = &stake_state.inner.stake.stake.delegation.voter_pubkey;
267 :
268 0 : fd_voter_t * voter = fd_epoch_voters_insert( epoch_voters, *pubkey );
269 :
270 0 : voter->stake = stake_state.inner.stake.stake.delegation.stake;
271 0 : ctx->epoch->total_stake += voter->stake;
272 0 : }
273 0 : }
274 :
275 : static void
276 : snapshot_done( fd_tower_tile_t * ctx,
277 0 : fd_snapshot_manifest_t const * manifest ) {
278 0 : fd_hash_t manifest_block_id = { .ul = { 0xf17eda2ce7b1d } }; /* FIXME manifest_block_id */
279 0 : fd_ghost_init( ctx->ghost, manifest->slot, &manifest_block_id );
280 :
281 : /* As per the documentation in fd_ssmsg.h, we use
282 : manifest->epoch_stakes[1] to initialize the epoch voters. These
283 : correspond to the amount staked to each vote account at the
284 : beginning of the current epoch:
285 :
286 : manifest->epoch_stakes[0] represents the stakes used to generate
287 : the leader schedule at the end of the current epoch. These are the
288 : stakes as of the end of two epochs ago.
289 :
290 : manifest->epoch_stakes[1] represents the stakes used to generate
291 : the leader schedule at the end of the next epoch. These are the
292 : stakes as of the end of the previous epoch, so these will be the
293 : stakes throughout the current epoch. */
294 0 : fd_voter_t * epoch_voters = fd_epoch_voters( ctx->epoch );
295 0 : fd_snapshot_manifest_epoch_stakes_t const * epoch_stakes = &manifest->epoch_stakes[ 1 ];
296 0 : for( ulong i=0UL; i<epoch_stakes->vote_stakes_len; i++ ) {
297 0 : if( FD_UNLIKELY( !epoch_stakes->vote_stakes[ i ].stake ) ) continue;
298 :
299 0 : fd_pubkey_t const * pubkey = (fd_pubkey_t const *)epoch_stakes->vote_stakes[ i ].vote;
300 :
301 0 : #if FD_EPOCH_USE_HANDHOLDING
302 0 : FD_TEST( !fd_epoch_voters_query( epoch_voters, *pubkey, NULL ) );
303 0 : FD_TEST( fd_epoch_voters_key_cnt( epoch_voters ) < fd_epoch_voters_key_max( epoch_voters ) );
304 0 : #endif
305 :
306 0 : fd_voter_t * voter = fd_epoch_voters_insert( epoch_voters, *pubkey );
307 :
308 0 : #if FD_EPOCH_USE_HANDHOLDING
309 0 : FD_TEST( 0==memcmp( voter->key.uc, pubkey->uc, sizeof(fd_pubkey_t) ) );
310 0 : FD_TEST( fd_epoch_voters_query( epoch_voters, voter->key, NULL ) );
311 0 : #endif
312 :
313 0 : voter->stake = epoch_stakes->vote_stakes[ i ].stake;
314 0 : voter->replay_vote.slot = FD_SLOT_NULL;
315 0 : voter->gossip_vote.slot = FD_SLOT_NULL;
316 0 : voter->rooted_vote.slot = FD_SLOT_NULL;
317 0 : ctx->epoch->total_stake += voter->stake;
318 0 : }
319 0 : }
320 :
321 : static inline int
322 : returnable_frag( fd_tower_tile_t * ctx,
323 : ulong in_idx,
324 : ulong seq,
325 : ulong sig,
326 : ulong chunk,
327 : ulong sz,
328 : ulong ctl,
329 : ulong tsorig,
330 : ulong tspub,
331 0 : fd_stem_context_t * stem ) {
332 0 : (void)seq;
333 0 : (void)tspub;
334 :
335 0 : if( FD_LIKELY( ctx->in_kind[ in_idx ]==IN_KIND_GENESIS ) ) {
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 : } else if( FD_LIKELY( ctx->in_kind[ in_idx ]==IN_KIND_SNAP ) ) {
339 0 : if( FD_UNLIKELY( fd_ssmsg_sig_message( sig )==FD_SSMSG_DONE ) ) {
340 0 : snapshot_done( ctx, &ctx->manifest );
341 0 : ctx->initialized = 1;
342 0 : } else {
343 0 : if( FD_UNLIKELY( chunk<ctx->in[ in_idx ].chunk0 || chunk>ctx->in[ in_idx ].wmark || sz>ctx->in[ in_idx ].mtu ) )
344 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 ));
345 :
346 0 : fd_memcpy( &ctx->manifest, fd_chunk_to_laddr( ctx->in[ in_idx ].mem, chunk ), sizeof(fd_snapshot_manifest_t) );
347 0 : }
348 0 : } else if( FD_LIKELY( ctx->in_kind[ in_idx ]==IN_KIND_REPLAY ) ) {
349 0 : if( FD_UNLIKELY( chunk<ctx->in[ in_idx ].chunk0 || chunk>ctx->in[ in_idx ].wmark || sz>ctx->in[ in_idx ].mtu ) )
350 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 ));
351 :
352 : /* If we haven't initialized from either genesis or a snapshot yet,
353 : we cannot process any frags from replay as it's a race condition,
354 : just wait until we initialize and then process. */
355 0 : if( FD_UNLIKELY( !ctx->initialized ) ) return 1;
356 :
357 0 : if( FD_LIKELY( sig==REPLAY_SIG_SLOT_COMPLETED ) ) {
358 0 : fd_memcpy( &ctx->replay_slot_info, fd_chunk_to_laddr( ctx->in[ in_idx ].mem, chunk ), sizeof(fd_replay_slot_completed_t) );
359 0 : } else if( FD_LIKELY( sig==REPLAY_SIG_VOTE_STATE ) ) {
360 0 : if( FD_UNLIKELY( fd_frag_meta_ctl_som( ctl ) ) ) ctx->replay_towers_cnt = 0;
361 :
362 0 : if( FD_UNLIKELY( ctx->replay_towers_cnt>=FD_REPLAY_TOWER_VOTE_ACC_MAX ) ) FD_LOG_ERR(( "tower received more vote states than expected" ));
363 0 : memcpy( &ctx->replay_towers[ ctx->replay_towers_cnt ], fd_chunk_to_laddr( ctx->in[ in_idx ].mem, chunk ), sizeof(fd_replay_tower_t) );
364 0 : ctx->replay_towers_cnt++;
365 :
366 0 : if( FD_UNLIKELY( fd_frag_meta_ctl_eom( ctl ) ) ) replay_slot_completed( ctx, &ctx->replay_slot_info, tsorig, stem );
367 0 : } else if( FD_UNLIKELY( sig==REPLAY_SIG_ROOT_ADVANCED ) ) {
368 : /* Ignore root advanced messages, we don't need them */
369 0 : } else {
370 0 : FD_LOG_ERR(( "unexpected replay message sig %lu", sig ));
371 0 : }
372 0 : } else {
373 0 : FD_LOG_ERR(( "unexpected input kind %d", ctx->in_kind[ in_idx ] ));
374 0 : }
375 :
376 0 : return 0;
377 0 : }
378 :
379 : static void
380 : privileged_init( fd_topo_t * topo,
381 0 : fd_topo_tile_t * tile ) {
382 0 : void * scratch = fd_topo_obj_laddr( topo, tile->tile_obj_id );
383 0 : FD_SCRATCH_ALLOC_INIT( l, scratch );
384 0 : fd_tower_tile_t * ctx = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_tower_tile_t), sizeof(fd_tower_tile_t) );
385 :
386 0 : if( FD_UNLIKELY( !strcmp( tile->tower.identity_key_path, "" ) ) )
387 0 : FD_LOG_ERR(( "identity_key_path not set" ));
388 :
389 0 : ctx->identity_key[ 0 ] = *(fd_pubkey_t const *)fd_type_pun_const( fd_keyload_load( tile->tower.identity_key_path, /* pubkey only: */ 1 ) );
390 :
391 : /* The vote key can be specified either directly as a base58 encoded
392 : pubkey, or as a file path. We first try to decode as a pubkey.
393 :
394 : TODO: The "vote_acc_path" should be renamed, as it might not be
395 : a path. */
396 0 : uchar * vote_key = fd_base58_decode_32( tile->tower.vote_acc_path, ctx->vote_acc->uc );
397 0 : if( FD_UNLIKELY( !vote_key ) ) {
398 0 : ctx->vote_acc[ 0 ] = *(fd_pubkey_t const *)fd_type_pun_const( fd_keyload_load( tile->tower.vote_acc_path, /* pubkey only: */ 1 ) );
399 0 : }
400 :
401 0 : char path[ PATH_MAX ];
402 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 ) ) );
403 0 : ctx->checkpt_fd = open( path, O_WRONLY|O_CREAT|O_TRUNC, 0600 );
404 0 : if( FD_UNLIKELY( -1==ctx->checkpt_fd ) ) FD_LOG_ERR(( "open(`%s`) failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
405 :
406 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 ) ) );
407 0 : ctx->restore_fd = open( path, O_RDONLY );
408 0 : if( FD_UNLIKELY( -1==ctx->restore_fd && errno!=ENOENT ) ) FD_LOG_WARNING(( "open(`%s`) failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
409 0 : }
410 :
411 : static void
412 : unprivileged_init( fd_topo_t * topo,
413 0 : fd_topo_tile_t * tile ) {
414 0 : void * scratch = fd_topo_obj_laddr( topo, tile->tile_obj_id );
415 :
416 0 : FD_SCRATCH_ALLOC_INIT( l, scratch );
417 0 : fd_tower_tile_t * ctx = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_tower_tile_t), sizeof(fd_tower_tile_t) );
418 0 : void * _epoch = FD_SCRATCH_ALLOC_APPEND( l, fd_epoch_align(), fd_epoch_footprint( FD_REPLAY_TOWER_VOTE_ACC_MAX ) );
419 0 : void * _ghost = FD_SCRATCH_ALLOC_APPEND( l, fd_ghost_align(), fd_ghost_footprint( FD_BLOCK_MAX ) );
420 0 : void * _tower = FD_SCRATCH_ALLOC_APPEND( l, fd_tower_align(), fd_tower_footprint() );
421 0 : void * _vote_towers = FD_SCRATCH_ALLOC_APPEND( l, fd_tower_align(), fd_tower_footprint()*FD_REPLAY_TOWER_VOTE_ACC_MAX );
422 :
423 0 : ctx->epoch = fd_epoch_join( fd_epoch_new( _epoch, FD_REPLAY_TOWER_VOTE_ACC_MAX ) );
424 0 : FD_TEST( ctx->epoch );
425 :
426 0 : ctx->ghost = fd_ghost_join( fd_ghost_new( _ghost, FD_BLOCK_MAX, 42UL ) );
427 0 : FD_TEST( ctx->ghost );
428 :
429 0 : ctx->tower = fd_tower_join( fd_tower_new( _tower ) );
430 0 : FD_TEST( ctx->tower );
431 :
432 0 : for( ulong i=0UL; i<FD_REPLAY_TOWER_VOTE_ACC_MAX; i++ ) {
433 0 : ctx->vote_towers[ i ] = fd_tower_join( fd_tower_new( (uchar*)_vote_towers+(i*fd_tower_footprint() ) ) );
434 0 : FD_TEST( ctx->vote_towers[ i ] );
435 0 : }
436 :
437 0 : ctx->initialized = 0;
438 :
439 0 : ctx->replay_towers_cnt = 0UL;
440 :
441 0 : FD_TEST( tile->in_cnt<sizeof(ctx->in_kind)/sizeof(ctx->in_kind[0]) );
442 0 : for( ulong i=0UL; i<tile->in_cnt; i++ ) {
443 0 : fd_topo_link_t * link = &topo->links[ tile->in_link_id[ i ] ];
444 0 : fd_topo_wksp_t * link_wksp = &topo->workspaces[ topo->objs[ link->dcache_obj_id ].wksp_id ];
445 :
446 0 : if( FD_LIKELY( !strcmp( link->name, "genesi_out" ) ) ) ctx->in_kind[ i ] = IN_KIND_GENESIS;
447 0 : else if( FD_LIKELY( !strcmp( link->name, "snap_out" ) ) ) ctx->in_kind[ i ] = IN_KIND_SNAP;
448 0 : else if( FD_LIKELY( !strcmp( link->name, "replay_out" ) ) ) ctx->in_kind[ i ] = IN_KIND_REPLAY;
449 0 : else FD_LOG_ERR(( "tower tile has unexpected input link %lu %s", i, link->name ));
450 :
451 0 : ctx->in[ i ].mem = link_wksp->wksp;
452 0 : ctx->in[ i ].mtu = link->mtu;
453 0 : ctx->in[ i ].chunk0 = fd_dcache_compact_chunk0( ctx->in[ i ].mem, link->dcache );
454 0 : ctx->in[ i ].wmark = fd_dcache_compact_wmark ( ctx->in[ i ].mem, link->dcache, link->mtu );
455 0 : }
456 :
457 0 : ctx->out_mem = topo->workspaces[ topo->objs[ topo->links[ tile->out_link_id[ 0 ] ].dcache_obj_id ].wksp_id ].wksp;
458 0 : ctx->out_chunk0 = fd_dcache_compact_chunk0( ctx->out_mem, topo->links[ tile->out_link_id[ 0 ] ].dcache );
459 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 );
460 0 : ctx->out_chunk = ctx->out_chunk0;
461 0 : }
462 :
463 : static ulong
464 : populate_allowed_seccomp( fd_topo_t const * topo,
465 : fd_topo_tile_t const * tile,
466 : ulong out_cnt,
467 0 : struct sock_filter * out ) {
468 0 : void * scratch = fd_topo_obj_laddr( topo, tile->tile_obj_id );
469 0 : FD_SCRATCH_ALLOC_INIT( l, scratch );
470 0 : fd_tower_tile_t * ctx = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_tower_tile_t), sizeof(fd_tower_tile_t) );
471 :
472 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 );
473 0 : return sock_filter_policy_fd_tower_tile_instr_cnt;
474 0 : }
475 :
476 : static ulong
477 : populate_allowed_fds( fd_topo_t const * topo,
478 : fd_topo_tile_t const * tile,
479 : ulong out_fds_cnt,
480 0 : int * out_fds ) {
481 0 : void * scratch = fd_topo_obj_laddr( topo, tile->tile_obj_id );
482 0 : FD_SCRATCH_ALLOC_INIT( l, scratch );
483 0 : fd_tower_tile_t * ctx = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_tower_tile_t), sizeof(fd_tower_tile_t) );
484 :
485 0 : if( FD_UNLIKELY( out_fds_cnt<4UL ) ) FD_LOG_ERR(( "out_fds_cnt %lu", out_fds_cnt ));
486 :
487 0 : ulong out_cnt = 0UL;
488 0 : out_fds[ out_cnt++ ] = 2; /* stderr */
489 0 : if( FD_LIKELY( -1!=fd_log_private_logfile_fd() ) )
490 0 : out_fds[ out_cnt++ ] = fd_log_private_logfile_fd(); /* logfile */
491 0 : out_fds[ out_cnt++ ] = ctx->checkpt_fd;
492 0 : out_fds[ out_cnt++ ] = ctx->restore_fd;
493 0 : return out_cnt;
494 0 : }
495 :
496 0 : #define STEM_BURST (1UL)
497 :
498 0 : #define STEM_CALLBACK_CONTEXT_TYPE fd_tower_tile_t
499 0 : #define STEM_CALLBACK_CONTEXT_ALIGN alignof(fd_tower_tile_t)
500 0 : #define STEM_CALLBACK_RETURNABLE_FRAG returnable_frag
501 :
502 : #include "../../disco/stem/fd_stem.c"
503 :
504 : fd_topo_run_tile_t fd_tile_tower = {
505 : .name = "tower",
506 : .populate_allowed_seccomp = populate_allowed_seccomp,
507 : .populate_allowed_fds = populate_allowed_fds,
508 : .scratch_align = scratch_align,
509 : .scratch_footprint = scratch_footprint,
510 : .unprivileged_init = unprivileged_init,
511 : .privileged_init = privileged_init,
512 : .run = stem_run,
513 : };
|