Line data Source code
1 : #include "../../disco/topo/fd_topo.h"
2 : #include "../../disco/events/generated/fd_event_gen.h"
3 : #include "../../disco/keyguard/fd_keyswitch.h"
4 : #include "../../disco/keyguard/fd_keyload.h"
5 :
6 : #include "fd_adminctl.h"
7 : #include <linux/futex.h>
8 : #include "generated/fd_admin_tile_seccomp.h"
9 :
10 : struct fd_admin_tile_ctx {
11 : fd_topo_t const * topo;
12 : fd_adminctl_t * adminctl;
13 : uchar identity_pubkey[ 32UL ];
14 : fd_keyswitch_t * voter_av_keyswitch;
15 : fd_keyswitch_t * sign_av_keyswitch[ FD_TOPO_MAX_TILES ];
16 : ulong sign_av_keyswitch_cnt;
17 : fd_sha512_t sha512[ 1 ];
18 :
19 : ulong replay_out_idx; /* admin_replay stem out index */
20 : ulong snap_create_slot_idx; /* adminctl slot of snapshot-create command */
21 : ulong snap_create_target_slot; /* requested slot retained until Replay responds */
22 : ulong snap_create_start_time; /* command start retained until Replay responds */
23 :
24 : long poll_ticks;
25 : long idle_poll_ticks;
26 : long deadline_ticks; /* next adminctl poll */
27 : };
28 :
29 : typedef struct fd_admin_tile_ctx fd_admin_tile_ctx_t;
30 :
31 : static inline fd_event_admin_command_t
32 : prepare_admin_command( int type,
33 : void const * payload,
34 0 : ulong payload_sz ) {
35 0 : fd_event_admin_command_t event = {
36 0 : .type = type,
37 0 : .args_json = { '{', '}' },
38 0 : .args_json_len = 2UL,
39 0 : .start_time = (ulong)fd_log_wallclock(),
40 0 : .payload_size = payload_sz,
41 0 : .has_payload_version = 0,
42 0 : };
43 0 : if( FD_LIKELY( payload_sz>=sizeof(ulong) ) ) {
44 0 : event.payload_version = FD_LOAD( ulong, payload );
45 0 : event.has_payload_version = 1;
46 0 : }
47 0 : return event;
48 0 : }
49 :
50 : static inline void
51 : report_admin_command( fd_event_admin_command_t * event,
52 0 : int result ) {
53 0 : FD_TEST( result>=0 && result<=FD_EVENT_ADMIN_COMMAND_RESULT_CUSTOM );
54 0 : event->result = result;
55 0 : event->end_time = (ulong)fd_log_wallclock();
56 0 : fd_event_report_admin_command( event );
57 0 : }
58 :
59 : static inline void
60 : report_admin_command_custom_result( fd_event_admin_command_t * event,
61 0 : char const * custom_result ) {
62 0 : FD_TEST( fd_cstr_printf_check( (char *)event->custom_result,
63 0 : sizeof(event->custom_result),
64 0 : &event->custom_result_len,
65 0 : "%s",
66 0 : custom_result ) );
67 0 : report_admin_command( event, FD_EVENT_ADMIN_COMMAND_RESULT_CUSTOM );
68 0 : }
69 :
70 : FD_FN_CONST static inline ulong
71 0 : scratch_align( void ) {
72 0 : return alignof(fd_admin_tile_ctx_t);
73 0 : }
74 :
75 : FD_FN_PURE static inline ulong
76 0 : scratch_footprint( fd_topo_tile_t const * tile FD_PARAM_UNUSED ) {
77 0 : return sizeof(fd_admin_tile_ctx_t);
78 0 : }
79 :
80 : static void
81 : privileged_init( fd_topo_t const * topo,
82 0 : fd_topo_tile_t const * tile ) {
83 0 : void * scratch = fd_topo_obj_laddr( topo, tile->tile_obj_id );
84 0 : fd_admin_tile_ctx_t * ctx = (fd_admin_tile_ctx_t *)scratch;
85 0 : fd_memset( ctx, 0, sizeof(fd_admin_tile_ctx_t) );
86 :
87 0 : if( FD_UNLIKELY( !strcmp( tile->admin.identity_key_path, "" ) ) )
88 0 : FD_LOG_ERR(( "identity_key_path not set" ));
89 :
90 0 : fd_memcpy( ctx->identity_pubkey, fd_keyload_load( tile->admin.identity_key_path, /* pubkey only: */ 1 ), 32UL );
91 0 : }
92 :
93 : static void
94 : unprivileged_init( fd_topo_t const * topo,
95 0 : fd_topo_tile_t const * tile ) {
96 0 : void * scratch = fd_topo_obj_laddr( topo, tile->tile_obj_id );
97 0 : fd_admin_tile_ctx_t * ctx = (fd_admin_tile_ctx_t *)scratch;
98 0 : ctx->replay_out_idx = ULONG_MAX;
99 0 : ctx->snap_create_slot_idx = ULONG_MAX;
100 0 : ctx->poll_ticks = (long)(1e6*fd_tempo_tick_per_ns( NULL )); /* 1ms */
101 0 : ctx->idle_poll_ticks = (long)((double)FD_SLEEP_PARK_CAP_NS*fd_tempo_tick_per_ns( NULL ));
102 0 : ctx->deadline_ticks = 0L;
103 0 : ctx->topo = topo;
104 :
105 0 : fd_topo_obj_t const * adminctl_obj = fd_topo_find_tile_obj( topo, tile, "adminctl" );
106 0 : FD_TEST( adminctl_obj );
107 :
108 0 : ctx->adminctl = fd_adminctl_join( fd_topo_obj_laddr( topo, adminctl_obj->id ) );
109 0 : FD_TEST( ctx->adminctl );
110 :
111 0 : ctx->replay_out_idx = fd_topo_find_tile_out_link( topo, tile, "admin_replay", 0UL );
112 :
113 0 : for( ulong i=0UL; i<tile->in_cnt; i++ ) {
114 0 : fd_topo_link_t const * link = &topo->links[ tile->in_link_id[ i ] ];
115 0 : if( FD_UNLIKELY( strcmp( link->name, "replay_admin" ) ) ) {
116 0 : FD_LOG_ERR(( "unexpected input link name %s", link->name ));
117 0 : }
118 0 : }
119 :
120 0 : ulong voter_idx = fd_topo_find_tile( topo, "votor", 0UL );
121 0 : FD_TEST( voter_idx!=ULONG_MAX );
122 0 : FD_TEST( topo->tiles[ voter_idx ].av_keyswitch_obj_id!=ULONG_MAX );
123 0 : ctx->voter_av_keyswitch = fd_keyswitch_join( fd_topo_obj_laddr( topo, topo->tiles[ voter_idx ].av_keyswitch_obj_id ) );
124 0 : FD_TEST( ctx->voter_av_keyswitch );
125 :
126 0 : for( ulong i=0UL; i<topo->tile_cnt; i++ ) {
127 0 : fd_topo_tile_t const * sign_tile = &topo->tiles[ i ];
128 0 : if( FD_LIKELY( strcmp( sign_tile->name, "sign" ) ) ) continue;
129 0 : FD_TEST( sign_tile->av_keyswitch_obj_id!=ULONG_MAX );
130 0 : ctx->sign_av_keyswitch[ ctx->sign_av_keyswitch_cnt ] = fd_keyswitch_join( fd_topo_obj_laddr( topo, sign_tile->av_keyswitch_obj_id ) );
131 0 : FD_TEST( ctx->sign_av_keyswitch[ ctx->sign_av_keyswitch_cnt ] );
132 0 : ctx->sign_av_keyswitch_cnt++;
133 0 : }
134 0 : FD_TEST( ctx->sign_av_keyswitch_cnt );
135 :
136 0 : FD_TEST( fd_sha512_join( fd_sha512_new( ctx->sha512 ) ) );
137 0 : }
138 :
139 : /* The process of switching identity of the validator is somewhat
140 : involved, to prevent it from producing torn data (for example,
141 : a block where half the shreds are signed by one private key, and half
142 : are signed by another).
143 :
144 : The process of switching is a state machine that progresses linearly
145 : through each of the states. Generally, no transitions are allowed
146 : except direct forward steps, except in emergency recovery cases an
147 : operator can force the state past the initial lock.
148 :
149 : Votor is referred to as the Voter tile and Rotor takes the place of
150 : Repair for the purposes of the identity switch.
151 :
152 : The states follow, in order. */
153 :
154 : /* State 0: UNLOCKED.
155 : The validator is not currently in the process of switching keys. */
156 0 : #define FD_SET_IDENTITY_STATE_UNLOCKED (0UL)
157 :
158 : /* State 1: LOCKED
159 : Some client to the validator has requested a key switch. To do so,
160 : it acquired an exclusive lock on the validator to prevent the
161 : switch potentially being interleaved with another client. */
162 0 : #define FD_SET_IDENTITY_STATE_LOCKED (1UL)
163 :
164 : /* State 2: REPLAY_HALT_REQUESTED
165 : The first step in the key switch process is to pause Replay,
166 : preventing us from becoming leader or completing additional slots,
167 : but finishing any currently in progress leader slot if there is
168 : one. Replay continues polling incoming links while paused so
169 : downstream tiles can drain without a reliable-link cycle.
170 :
171 : In Firedancer, this halt request goes to the Replay tile, which
172 : causes the tile to switch the identity key it uses to determine the
173 : identity's balance as well as when the validator is the leader.
174 : Replay reports the producer sequence of replay_slot, the link votor
175 : reads, as it switches. */
176 0 : #define FD_SET_IDENTITY_STATE_REPLAY_HALT_REQUESTED (2UL)
177 :
178 : /* State 3: REPLAY_HALTED
179 : Replay has paused and the validator is no longer leader. No more
180 : slots will complete until Replay is unhalted. Replay has switched
181 : its own identity key and reported the voter's replay link sequence.
182 :
183 : At this point, we also have the guarantee that there are no more
184 : outstanding shreds that have to be signed with the old key. Any
185 : tiles related to the leader pipeline that rely on the identity key
186 : will not be used. */
187 0 : #define FD_SET_IDENTITY_STATE_REPLAY_HALTED (3UL)
188 :
189 : /* State 4: VOTER_HALT_REQUESTED
190 : Voter has been requested to consume its replay link through the
191 : sequence Replay reported, stop producing new vote transactions by
192 : backpressuring SLOT_COMPLETED, drain its local publish queue, and
193 : switch identity. */
194 0 : #define FD_SET_IDENTITY_STATE_VOTER_HALT_REQUESTED (4UL)
195 :
196 : /* State 5: VOTER_HALTED
197 : Voter has drained its pre-switch publish queue, switched identity,
198 : and reported the sequence after the final drained message. */
199 0 : #define FD_SET_IDENTITY_STATE_VOTER_HALTED (5UL)
200 :
201 : /* State 6: SIGNERS_HALT_REQUESTED
202 : Repair, Gossip, Bundle, and Rserve will stop sending requests
203 : downstream to the sign tile. This is done to avoid any mismatches
204 : with the identity key. Their identity keys will be switched during
205 : this step, except for Gossip, which switches during
206 : SIGNERS_UNHALT_REQUESTED.
207 :
208 : These tiles use the identity key to populate messages signed by the
209 : sign tile:
210 : (a) Repair. The repair tile uses the identity key as part of the
211 : repair protocol. The identity key is included in and used
212 : for signing requests. Because Repair uses an asynchronous
213 : signing mechanism, Repair will first wait until all
214 : outstanding sign requests have been received back from the
215 : sign tile before halting any new signing requests.
216 : (b) Gossip. The gossip tile sends out ContactInfo messages with
217 : our identity key, and also uses the identity key to sign
218 : outgoing gossip messages.
219 : (c) Bundle. The bundle tile uses the identity key to sign an
220 : authentication challenge from the bundle server.
221 : (d) Rserve. The rserve tile uses the identity key to sign
222 : outgoing pings.
223 : (e) Shred. The shred tile has the sign tile sign FEC sets
224 : asynchronously; it waits for its outstanding requests to be
225 : answered under the old key before switching. */
226 0 : #define FD_SET_IDENTITY_STATE_SIGNERS_HALT_REQUESTED (6UL)
227 :
228 : /* State 7: SIGNERS_HALTED
229 : Repair, Gossip, Bundle, and Rserve are no longer sending requests
230 : to the sign tile. */
231 0 : #define FD_SET_IDENTITY_STATE_SIGNERS_HALTED (7UL)
232 :
233 : /* State 8: ALL_SWITCH_REQUESTED
234 : The client now requests that all other tiles which consume the
235 : identity key in some way switch to the new key. The leader
236 : pipeline is still halted, although it doesn't strictly need to be,
237 : since outgoing shreds have been flushed. This is done to keep the
238 : control flow simpler. The sign tile's switch is requested first to
239 : avoid any potential mismatches with the identity key.
240 :
241 : The other tiles using the identity key are:
242 : (a) Sign. The sign tile is responsible for holding the private
243 : key and servicing signing requests from other tiles.
244 : (b) GUI. The GUI shows the validator identity key to the user,
245 : and uses the key to determine which blocks are ours for
246 : highlighting on the frontend.
247 : (c) Gossvf. The gossvf tile uses the identity key to detect
248 : duplicate running instances of the same validator node as
249 : well as other message handling.
250 : (d) Shred. The shred tile uses the identity key to determine the
251 : position of the validator in the Turbine tree and to sign
252 : outgoing shreds.
253 : (e) Event. Outgoing events to the event server are signed with
254 : the identity key to authenticate the sender. */
255 0 : #define FD_SET_IDENTITY_STATE_ALL_SWITCH_REQUESTED (8UL)
256 :
257 : /* State 9: ALL_SWITCHED
258 : All remaining tiles that use the identity key have confirmed that
259 : they have switched to the new key. Gossip has not yet updated its
260 : identity key. Repair, Gossip, Bundle, and Rserve remain halted. */
261 0 : #define FD_SET_IDENTITY_STATE_ALL_SWITCHED (9UL)
262 :
263 : /* State 10: SIGNERS_UNHALT_REQUESTED
264 : Now that the sign tile is using the switched identity key, the
265 : halted signers can be unhalted. Gossip switches its identity key
266 : during this step. These are the same tiles from
267 : SIGNERS_HALT_REQUESTED. */
268 0 : #define FD_SET_IDENTITY_STATE_SIGNERS_UNHALT_REQUESTED (10UL)
269 :
270 : /* State 11: SIGNERS_UNHALTED
271 : All tiles that rely on the sign tile have been unhalted. Replay
272 : remains paused. */
273 0 : #define FD_SET_IDENTITY_STATE_SIGNERS_UNHALTED (11UL)
274 :
275 : /* State 12: REPLAY_UNHALT_REQUESTED
276 : The final state, now that all tiles have switched, Replay can be
277 : unpaused and the validator can resume processing and producing
278 : blocks. The next state once Replay confirms it is unpaused is
279 : UNLOCKED. */
280 0 : #define FD_SET_IDENTITY_STATE_REPLAY_UNHALT_REQUESTED (12UL)
281 :
282 : static fd_keyswitch_t *
283 : find_identity_keyswitch( fd_admin_tile_ctx_t * ctx,
284 0 : char const * tile_name ) {
285 0 : fd_topo_t const * topo = ctx->topo;
286 0 : ulong tile_idx = fd_topo_find_tile( topo, tile_name, 0UL );
287 0 : FD_TEST( tile_idx!=ULONG_MAX );
288 0 : FD_TEST( topo->tiles[ tile_idx ].id_keyswitch_obj_id!=ULONG_MAX );
289 :
290 0 : fd_keyswitch_t * keyswitch = fd_topo_obj_laddr( topo, topo->tiles[ tile_idx ].id_keyswitch_obj_id );
291 0 : FD_TEST( keyswitch );
292 0 : return keyswitch;
293 0 : }
294 :
295 : static int FD_FN_SENSITIVE
296 : poll_set_identity( fd_admin_tile_ctx_t * ctx,
297 : ulong * state,
298 : ulong identity_outset,
299 : uchar * keypair,
300 0 : ulong vote_history_slot ) {
301 0 : fd_topo_t const * topo = ctx->topo;
302 :
303 0 : switch( *state ) {
304 0 : case FD_SET_IDENTITY_STATE_UNLOCKED: {
305 0 : fd_keyswitch_t * replay = find_identity_keyswitch( ctx, "replay" );
306 0 : if( FD_LIKELY( FD_KEYSWITCH_STATE_UNLOCKED==FD_ATOMIC_CAS( &replay->state, FD_KEYSWITCH_STATE_UNLOCKED, FD_KEYSWITCH_STATE_LOCKED ) ) ) {
307 0 : *state = FD_SET_IDENTITY_STATE_LOCKED;
308 0 : FD_LOG_INFO(( "Locking validator identity for key switch..." ));
309 0 : } else {
310 0 : FD_LOG_CRIT(( "identity keyswitch is in a locked state but should be unlocked" ));
311 0 : }
312 0 : break;
313 0 : }
314 0 : case FD_SET_IDENTITY_STATE_LOCKED: {
315 0 : fd_keyswitch_t * replay = find_identity_keyswitch( ctx, "replay" );
316 0 : memcpy( replay->bytes, keypair+32UL, 32UL );
317 :
318 0 : FD_COMPILER_MFENCE();
319 0 : replay->state = FD_KEYSWITCH_STATE_SWITCH_PENDING;
320 0 : FD_COMPILER_MFENCE();
321 0 : *state = FD_SET_IDENTITY_STATE_REPLAY_HALT_REQUESTED;
322 0 : FD_LOG_INFO(( "Pausing Replay for key switch..." ));
323 0 : break;
324 0 : }
325 0 : case FD_SET_IDENTITY_STATE_REPLAY_HALT_REQUESTED: {
326 0 : fd_keyswitch_t * replay = find_identity_keyswitch( ctx, "replay" );
327 0 : if( FD_LIKELY( replay->state==FD_KEYSWITCH_STATE_COMPLETED ) ) {
328 0 : fd_memzero_explicit( replay->bytes, 64UL );
329 0 : FD_COMPILER_MFENCE();
330 0 : *state = FD_SET_IDENTITY_STATE_REPLAY_HALTED;
331 0 : FD_LOG_INFO(( "Replay successfully paused..." ));
332 0 : } else if( FD_UNLIKELY( replay->state==FD_KEYSWITCH_STATE_SWITCH_PENDING ) ) {
333 0 : FD_SPIN_PAUSE();
334 0 : } else {
335 0 : FD_LOG_ERR(( "Unexpected replay keyswitch state %lu", replay->state ));
336 0 : }
337 0 : break;
338 0 : }
339 0 : case FD_SET_IDENTITY_STATE_REPLAY_HALTED: {
340 0 : fd_keyswitch_t * replay = find_identity_keyswitch( ctx, "replay" );
341 0 : fd_keyswitch_t * voter = find_identity_keyswitch( ctx, "votor" );
342 0 : voter->param = replay->result;
343 0 : memcpy( voter->bytes, keypair+32UL, 32UL );
344 : /* Votor takes the slot to wait to vote past. */
345 0 : FD_STORE( ulong, voter->bytes+32UL, vote_history_slot );
346 0 : FD_COMPILER_MFENCE();
347 0 : voter->state = FD_KEYSWITCH_STATE_SWITCH_PENDING;
348 0 : FD_COMPILER_MFENCE();
349 :
350 0 : *state = FD_SET_IDENTITY_STATE_VOTER_HALT_REQUESTED;
351 0 : FD_LOG_INFO(( "Pausing votor and draining queued messages..." ));
352 0 : break;
353 0 : }
354 0 : case FD_SET_IDENTITY_STATE_VOTER_HALT_REQUESTED: {
355 0 : fd_keyswitch_t * voter = find_identity_keyswitch( ctx, "votor" );
356 0 : if( FD_LIKELY( voter->state==FD_KEYSWITCH_STATE_COMPLETED ) ) {
357 0 : fd_memzero_explicit( voter->bytes, 64UL );
358 0 : FD_COMPILER_MFENCE();
359 0 : *state = FD_SET_IDENTITY_STATE_VOTER_HALTED;
360 0 : FD_LOG_INFO(( "votor successfully paused..." ));
361 0 : } else if( FD_UNLIKELY( voter->state==FD_KEYSWITCH_STATE_SWITCH_PENDING ) ) {
362 0 : FD_SPIN_PAUSE();
363 0 : } else {
364 0 : FD_LOG_ERR(( "Unexpected votor keyswitch state %lu", voter->state ));
365 0 : }
366 0 : break;
367 0 : }
368 0 : case FD_SET_IDENTITY_STATE_VOTER_HALTED: {
369 0 : for( ulong i=0UL; i<topo->tile_cnt; i++ ) {
370 0 : fd_topo_tile_t const * tile = &topo->tiles[ i ];
371 0 : if( FD_LIKELY( tile->id_keyswitch_obj_id==ULONG_MAX ) ) continue;
372 0 : if( strcmp( tile->name, "rotor" ) &&
373 0 : strcmp( tile->name, "gossip" ) &&
374 0 : strcmp( tile->name, "bundle" ) &&
375 0 : strcmp( tile->name, "rserve" ) &&
376 0 : strcmp( tile->name, "shred" ) ) {
377 0 : continue;
378 0 : }
379 :
380 0 : fd_keyswitch_t * tile_ks = fd_topo_obj_laddr( topo, tile->id_keyswitch_obj_id );
381 0 : tile_ks->param = 0UL;
382 0 : memcpy( tile_ks->bytes, keypair+32UL, 32UL );
383 0 : FD_COMPILER_MFENCE();
384 0 : tile_ks->state = FD_KEYSWITCH_STATE_SWITCH_PENDING;
385 0 : FD_COMPILER_MFENCE();
386 0 : }
387 0 : *state = FD_SET_IDENTITY_STATE_SIGNERS_HALT_REQUESTED;
388 0 : FD_LOG_INFO(( "Requesting to halt remaining signers..." ));
389 0 : break;
390 0 : }
391 0 : case FD_SET_IDENTITY_STATE_SIGNERS_HALT_REQUESTED: {
392 0 : int all_switched = 1;
393 0 : for( ulong i=0UL; i<topo->tile_cnt; i++ ) {
394 0 : fd_topo_tile_t const * tile = &topo->tiles[ i ];
395 0 : if( FD_LIKELY( tile->id_keyswitch_obj_id==ULONG_MAX ) ) continue;
396 0 : if( strcmp( tile->name, "rotor" ) &&
397 0 : strcmp( tile->name, "gossip" ) &&
398 0 : strcmp( tile->name, "bundle" ) &&
399 0 : strcmp( tile->name, "rserve" ) &&
400 0 : strcmp( tile->name, "shred" ) ) {
401 0 : continue;
402 0 : }
403 :
404 0 : fd_keyswitch_t * tile_ks = fd_topo_obj_laddr( topo, tile->id_keyswitch_obj_id );
405 0 : if( FD_LIKELY( tile_ks->state==FD_KEYSWITCH_STATE_SWITCH_PENDING ) ) {
406 0 : all_switched = 0;
407 0 : break;
408 0 : }
409 0 : }
410 0 : if( FD_LIKELY( all_switched ) ) {
411 0 : FD_LOG_INFO(( "All remaining signers successfully halted..." ));
412 0 : *state = FD_SET_IDENTITY_STATE_SIGNERS_HALTED;
413 0 : } else {
414 0 : FD_SPIN_PAUSE();
415 0 : }
416 0 : break;
417 0 : }
418 0 : case FD_SET_IDENTITY_STATE_SIGNERS_HALTED: {
419 0 : for( ulong i=0UL; i<topo->tile_cnt; i++ ) {
420 0 : fd_topo_tile_t const * tile = &topo->tiles[ i ];
421 0 : if( strcmp( tile->name, "sign" ) ) continue;
422 0 : fd_keyswitch_t * sign = fd_topo_obj_laddr( topo, tile->id_keyswitch_obj_id );
423 0 : memcpy( sign->bytes, keypair, 64UL );
424 0 : FD_COMPILER_MFENCE();
425 0 : sign->state = FD_KEYSWITCH_STATE_SWITCH_PENDING;
426 0 : FD_COMPILER_MFENCE();
427 0 : }
428 :
429 0 : fd_memzero_explicit( keypair, 32UL ); /* Private key no longer needed by the admin tile. */
430 :
431 0 : for( ulong i=0UL; i<topo->tile_cnt; i++ ) {
432 0 : fd_topo_tile_t const * tile = &topo->tiles[ i ];
433 0 : if( FD_LIKELY( tile->id_keyswitch_obj_id==ULONG_MAX ) ) continue;
434 0 : if( FD_LIKELY( !strcmp( tile->name, "sign" ) ||
435 0 : !strcmp( tile->name, "replay" ) ||
436 0 : !strcmp( tile->name, "rotor" ) ||
437 0 : !strcmp( tile->name, "gossip" ) ||
438 0 : !strcmp( tile->name, "votor" ) ||
439 0 : !strcmp( tile->name, "bundle" ) ||
440 0 : !strcmp( tile->name, "rserve" ) ||
441 0 : !strcmp( tile->name, "shred" ) ) ) continue;
442 :
443 0 : fd_keyswitch_t * tile_ks = fd_topo_obj_laddr( topo, tile->id_keyswitch_obj_id );
444 0 : if( !strcmp( tile->name, "gossvf" ) ) tile_ks->param = identity_outset;
445 0 : memcpy( tile_ks->bytes, keypair+32UL, 32UL );
446 0 : FD_COMPILER_MFENCE();
447 0 : tile_ks->state = FD_KEYSWITCH_STATE_SWITCH_PENDING;
448 0 : FD_COMPILER_MFENCE();
449 0 : }
450 :
451 0 : FD_LOG_INFO(( "Requesting all remaining tiles switch identity key..." ));
452 0 : *state = FD_SET_IDENTITY_STATE_ALL_SWITCH_REQUESTED;
453 0 : break;
454 0 : }
455 0 : case FD_SET_IDENTITY_STATE_ALL_SWITCH_REQUESTED: {
456 0 : ulong all_switched = 1UL;
457 0 : for( ulong i=0UL; i<topo->tile_cnt; i++ ) {
458 0 : fd_topo_tile_t const * tile = &topo->tiles[ i ];
459 0 : if( FD_LIKELY( tile->id_keyswitch_obj_id==ULONG_MAX ) ) continue;
460 0 : if( FD_LIKELY( !strcmp( tile->name, "replay" ) ||
461 0 : !strcmp( tile->name, "rotor" ) ||
462 0 : !strcmp( tile->name, "gossip" ) ||
463 0 : !strcmp( tile->name, "votor" ) ||
464 0 : !strcmp( tile->name, "bundle" ) ||
465 0 : !strcmp( tile->name, "rserve" ) ||
466 0 : !strcmp( tile->name, "shred" ) ) ) continue;
467 :
468 0 : fd_keyswitch_t * tile_ks = fd_topo_obj_laddr( topo, tile->id_keyswitch_obj_id );
469 0 : if( FD_LIKELY( tile_ks->state==FD_KEYSWITCH_STATE_SWITCH_PENDING ) ) {
470 0 : all_switched = 0UL;
471 0 : break;
472 0 : } else if( FD_UNLIKELY( tile_ks->state==FD_KEYSWITCH_STATE_COMPLETED ) ) {
473 0 : if( FD_LIKELY( !strcmp( tile->name, "sign" ) ) ) {
474 0 : FD_COMPILER_MFENCE();
475 0 : fd_memzero_explicit( tile_ks->bytes, 64UL );
476 0 : FD_COMPILER_MFENCE();
477 0 : }
478 0 : continue;
479 0 : } else {
480 0 : FD_LOG_ERR(( "Unexpected %s keyswitch state %lu", tile->name, tile_ks->state ));
481 0 : }
482 0 : }
483 :
484 0 : if( FD_LIKELY( all_switched ) ) {
485 0 : FD_LOG_INFO(( "All tiles successfully switched identity key..." ));
486 0 : *state = FD_SET_IDENTITY_STATE_ALL_SWITCHED;
487 0 : } else {
488 0 : FD_SPIN_PAUSE();
489 0 : }
490 0 : break;
491 0 : }
492 0 : case FD_SET_IDENTITY_STATE_ALL_SWITCHED: {
493 0 : for( ulong i=0UL; i<topo->tile_cnt; i++ ) {
494 0 : fd_topo_tile_t const * tile = &topo->tiles[ i ];
495 0 : if( FD_LIKELY( tile->id_keyswitch_obj_id==ULONG_MAX ) ) continue;
496 0 : if( strcmp( tile->name, "rotor" ) &&
497 0 : strcmp( tile->name, "gossip" ) &&
498 0 : strcmp( tile->name, "votor" ) &&
499 0 : strcmp( tile->name, "bundle" ) &&
500 0 : strcmp( tile->name, "rserve" ) ) {
501 0 : continue;
502 0 : }
503 :
504 0 : fd_keyswitch_t * tile_ks = fd_topo_obj_laddr( topo, tile->id_keyswitch_obj_id );
505 0 : if( !strcmp( tile->name, "gossip" ) ) tile_ks->param = identity_outset;
506 0 : FD_COMPILER_MFENCE();
507 0 : tile_ks->state = FD_KEYSWITCH_STATE_UNHALT_PENDING;
508 0 : FD_COMPILER_MFENCE();
509 0 : }
510 :
511 0 : FD_LOG_INFO(( "Requesting to unpause signers..." ));
512 0 : *state = FD_SET_IDENTITY_STATE_SIGNERS_UNHALT_REQUESTED;
513 0 : break;
514 0 : }
515 0 : case FD_SET_IDENTITY_STATE_SIGNERS_UNHALT_REQUESTED: {
516 0 : int all_switched = 1;
517 0 : for( ulong i=0UL; i<topo->tile_cnt; i++ ) {
518 0 : fd_topo_tile_t const * tile = &topo->tiles[ i ];
519 0 : if( FD_LIKELY( tile->id_keyswitch_obj_id==ULONG_MAX ) ) continue;
520 0 : if( strcmp( tile->name, "rotor" ) &&
521 0 : strcmp( tile->name, "gossip" ) &&
522 0 : strcmp( tile->name, "votor" ) &&
523 0 : strcmp( tile->name, "bundle" ) &&
524 0 : strcmp( tile->name, "rserve" ) ) {
525 0 : continue;
526 0 : }
527 :
528 0 : fd_keyswitch_t * tile_ks = fd_topo_obj_laddr( topo, tile->id_keyswitch_obj_id );
529 0 : if( FD_LIKELY( tile_ks->state==FD_KEYSWITCH_STATE_UNHALT_PENDING ) ) {
530 0 : all_switched = 0;
531 0 : break;
532 0 : }
533 0 : }
534 0 : if( FD_LIKELY( all_switched ) ) {
535 0 : FD_LOG_INFO(( "Successfully unpaused all non-leader signers..." ));
536 0 : *state = FD_SET_IDENTITY_STATE_SIGNERS_UNHALTED;
537 0 : } else {
538 0 : FD_SPIN_PAUSE();
539 0 : }
540 0 : break;
541 0 : }
542 0 : case FD_SET_IDENTITY_STATE_SIGNERS_UNHALTED: {
543 0 : fd_keyswitch_t * replay = find_identity_keyswitch( ctx, "replay" );
544 0 : replay->state = FD_KEYSWITCH_STATE_UNHALT_PENDING;
545 0 : FD_LOG_INFO(( "Requesting to unpause Replay..." ));
546 0 : *state = FD_SET_IDENTITY_STATE_REPLAY_UNHALT_REQUESTED;
547 0 : break;
548 0 : }
549 0 : case FD_SET_IDENTITY_STATE_REPLAY_UNHALT_REQUESTED: {
550 0 : fd_keyswitch_t * replay = find_identity_keyswitch( ctx, "replay" );
551 0 : if( FD_LIKELY( replay->state==FD_KEYSWITCH_STATE_COMPLETED ) ) {
552 0 : FD_LOG_INFO(( "Replay unpaused..." ));
553 0 : replay->state = FD_KEYSWITCH_STATE_UNLOCKED;
554 0 : *state = FD_SET_IDENTITY_STATE_UNLOCKED;
555 0 : } else if( FD_UNLIKELY( replay->state==FD_KEYSWITCH_STATE_UNHALT_PENDING ) ) {
556 0 : FD_SPIN_PAUSE();
557 0 : } else {
558 0 : FD_LOG_ERR(( "Unexpected replay keyswitch state %lu", replay->state ));
559 0 : }
560 0 : break;
561 0 : }
562 0 : default:
563 0 : FD_LOG_ERR(( "Unexpected set-identity state %lu", *state ));
564 0 : }
565 :
566 0 : return *state==FD_SET_IDENTITY_STATE_UNLOCKED;
567 0 : }
568 :
569 : static void FD_FN_SENSITIVE
570 : set_identity( fd_admin_tile_ctx_t * ctx,
571 : ulong slot_idx,
572 : void * data,
573 0 : ulong data_sz ) {
574 :
575 0 : fd_adminctl_t * adminctl = ctx->adminctl;
576 0 : fd_event_admin_command_t event = prepare_admin_command( FD_EVENT_ADMIN_COMMAND_TYPE_SET_IDENTITY, data, data_sz );
577 0 : FD_BASE58_ENCODE_32_BYTES( ctx->identity_pubkey, old_identity );
578 0 : FD_TEST( fd_cstr_printf_check( (char *)event.args_json, sizeof(event.args_json), &event.args_json_len, "{\"old_identity\":\"%s\"}", old_identity ) );
579 :
580 0 : if( FD_UNLIKELY( data_sz<sizeof(ulong) ) ) {
581 0 : FD_LOG_WARNING(( "adminctl set-identity payload too small: %lu", data_sz ));
582 0 : report_admin_command( &event, FD_EVENT_ADMIN_COMMAND_RESULT_ABI_SIZE_MISMATCH );
583 0 : fd_adminctl_complete( adminctl, slot_idx, FD_ADMINCTL_RESULT_ABI_SIZE_MISMATCH );
584 0 : return;
585 0 : }
586 :
587 0 : ulong version = FD_LOAD( ulong, data );
588 0 : if( FD_UNLIKELY( version!=FD_ADMINCTL_SET_IDENTITY_PAYLOAD_VERSION ) ) {
589 0 : FD_LOG_WARNING(( "unsupported adminctl set-identity payload version %lu", version ));
590 0 : report_admin_command( &event, FD_EVENT_ADMIN_COMMAND_RESULT_ABI_VERSION_MISMATCH );
591 0 : fd_adminctl_complete( adminctl, slot_idx, FD_ADMINCTL_RESULT_ABI_VERSION_MISMATCH );
592 0 : return;
593 0 : }
594 :
595 0 : if( FD_UNLIKELY( data_sz!=sizeof(fd_adminctl_set_identity_t) ) ) {
596 0 : FD_LOG_WARNING(( "unexpected adminctl set-identity payload_sz %lu", data_sz ));
597 0 : report_admin_command( &event, FD_EVENT_ADMIN_COMMAND_RESULT_ABI_SIZE_MISMATCH );
598 0 : fd_adminctl_complete( adminctl, slot_idx, FD_ADMINCTL_RESULT_ABI_SIZE_MISMATCH );
599 0 : return;
600 0 : }
601 :
602 0 : fd_adminctl_set_identity_t * req = fd_type_pun( data );
603 :
604 0 : fd_pubkey_t public_key;
605 0 : fd_ed25519_public_from_private( public_key.uc, req->keypair, ctx->sha512 );
606 0 : if( FD_UNLIKELY( memcmp( public_key.uc, req->keypair+32UL, 32UL ) ) ) {
607 0 : FD_LOG_WARNING(( "set-identity failed: public key in key file does not match private key" ));
608 0 : report_admin_command_custom_result( &event, "keypair_mismatch" );
609 0 : fd_adminctl_complete( adminctl, slot_idx, FD_SET_IDENTITY_RESULT_KEYPAIR_MISMATCH );
610 0 : return;
611 0 : }
612 :
613 0 : if( FD_UNLIKELY( !memcmp( public_key.uc, ctx->identity_pubkey, 32UL ) ) ) {
614 0 : FD_LOG_WARNING(( "set-identity failed: the validator is already running as the identity" ));
615 0 : report_admin_command_custom_result( &event, "same_identity" );
616 0 : fd_adminctl_complete( adminctl, slot_idx, FD_SET_IDENTITY_RESULT_SAME_IDENTITY );
617 0 : return;
618 0 : }
619 :
620 0 : ulong state = FD_SET_IDENTITY_STATE_UNLOCKED;
621 0 : ulong identity_outset = (ulong)fd_log_wallclock();
622 0 : for(;;) {
623 0 : if( FD_UNLIKELY( poll_set_identity( ctx, &state, identity_outset, req->keypair, req->vote_history_slot ) ) ) break;
624 0 : }
625 :
626 0 : memcpy( ctx->identity_pubkey, req->keypair+32UL, 32UL );
627 :
628 0 : report_admin_command( &event, FD_EVENT_ADMIN_COMMAND_RESULT_SUCCESS );
629 0 : fd_adminctl_complete( adminctl, slot_idx, FD_ADMINCTL_RESULT_SUCCESS );
630 0 : }
631 :
632 : static void
633 : get_identity( fd_admin_tile_ctx_t * ctx,
634 : ulong slot_idx,
635 : void * data,
636 0 : ulong data_sz ) {
637 :
638 0 : fd_adminctl_t * adminctl = ctx->adminctl;
639 0 : fd_event_admin_command_t event = prepare_admin_command( FD_EVENT_ADMIN_COMMAND_TYPE_GET_IDENTITY, data, data_sz );
640 0 : FD_BASE58_ENCODE_32_BYTES( ctx->identity_pubkey, identity );
641 0 : FD_TEST( fd_cstr_printf_check( (char *)event.args_json, sizeof(event.args_json), &event.args_json_len, "{\"identity\":\"%s\"}", identity ) );
642 :
643 0 : if( FD_UNLIKELY( data_sz<sizeof(ulong) ) ) {
644 0 : FD_LOG_WARNING(( "adminctl get-identity payload too small: %lu", data_sz ));
645 0 : report_admin_command( &event, FD_EVENT_ADMIN_COMMAND_RESULT_ABI_SIZE_MISMATCH );
646 0 : fd_adminctl_complete( adminctl, slot_idx, FD_ADMINCTL_RESULT_ABI_SIZE_MISMATCH );
647 0 : return;
648 0 : }
649 :
650 0 : ulong version = FD_LOAD( ulong, data );
651 0 : if( FD_UNLIKELY( version!=FD_ADMINCTL_GET_IDENTITY_PAYLOAD_VERSION ) ) {
652 0 : FD_LOG_WARNING(( "unsupported adminctl get-identity payload version %lu", version ));
653 0 : report_admin_command( &event, FD_EVENT_ADMIN_COMMAND_RESULT_ABI_VERSION_MISMATCH );
654 0 : fd_adminctl_complete( adminctl, slot_idx, FD_ADMINCTL_RESULT_ABI_VERSION_MISMATCH );
655 0 : return;
656 0 : }
657 :
658 0 : if( FD_UNLIKELY( data_sz!=sizeof(fd_adminctl_get_identity_req_t) ) ) {
659 0 : FD_LOG_WARNING(( "unexpected adminctl get-identity payload_sz %lu", data_sz ));
660 0 : report_admin_command( &event, FD_EVENT_ADMIN_COMMAND_RESULT_ABI_SIZE_MISMATCH );
661 0 : fd_adminctl_complete( adminctl, slot_idx, FD_ADMINCTL_RESULT_ABI_SIZE_MISMATCH );
662 0 : return;
663 0 : }
664 :
665 : /* Adminctl commands are serviced one at a time by this tile, which is
666 : the only driver of identity switches, so the tracked identity
667 : cannot be mid-switch here. */
668 0 : fd_adminctl_get_identity_resp_t resp;
669 0 : resp.version = FD_ADMINCTL_GET_IDENTITY_PAYLOAD_VERSION;
670 0 : memcpy( resp.identity_pubkey, ctx->identity_pubkey, 32UL );
671 :
672 0 : report_admin_command( &event, FD_EVENT_ADMIN_COMMAND_RESULT_SUCCESS );
673 0 : fd_adminctl_complete_response( adminctl, slot_idx, FD_ADMINCTL_RESULT_SUCCESS, &resp, sizeof(resp) );
674 0 : }
675 :
676 : /* The process of adding an authorized voter to the validator must be
677 : done carefully in order to prevent votes being generated with an
678 : authorized voter that the sign tile is not yet aware of. The
679 : authorized voter must be added to the sign tile before it is added
680 : to the voter tile (the tower/votor tile). All transitions must be
681 : linear and in forward order. */
682 :
683 : /* State 0: UNLOCKED
684 : The validator is not currently in the process of switching keys. */
685 0 : #define FD_ADD_AUTH_VOTER_STATE_UNLOCKED (0UL)
686 :
687 : /* State 1: LOCKED
688 : Some client to the validator has requested to add an authorized
689 : voter. To do so, it acquired an exclusive lock on the validator to
690 : prevent the switch potentially being interleaved with another
691 : client. */
692 0 : #define FD_ADD_AUTH_VOTER_STATE_LOCKED (1UL)
693 :
694 : /* State 2: SIGN_TILE_REQUESTED
695 : The first step to add an authorized voter is to notify the sign
696 : tile that an authorized voter is being added. */
697 0 : #define FD_ADD_AUTH_VOTER_STATE_SIGN_TILE_REQUESTED (2UL)
698 :
699 : /* State 3: SIGN_TILE_UPDATED
700 : The Sign tile has confirmed that it has updated its internal
701 : mapping for the set of supported authorized voters. At this point
702 : the sign tile is aware of the new authorized voter but the voter
703 : tile will not vote with the new authorized voter yet. */
704 0 : #define FD_ADD_AUTH_VOTER_STATE_SIGN_TILE_UPDATED (3UL)
705 :
706 : /* State 4: VOTER_TILE_REQUESTED
707 : Once the Sign tile is updated, now the voter tile must be notified
708 : that an authorized voter is being added so it can start voting with
709 : the new authorized voter. */
710 0 : #define FD_ADD_AUTH_VOTER_STATE_VOTER_TILE_REQUESTED (4UL)
711 :
712 : /* State 5: VOTER_TILE_UPDATED
713 : The voter tile has confirmed that it has updated its internal
714 : mapping for the set of supported authorized voters. */
715 0 : #define FD_ADD_AUTH_VOTER_STATE_VOTER_TILE_UPDATED (5UL)
716 :
717 : /* State 6: UNLOCK_REQUESTED
718 : The client now requests that the voter tile unpause the pipeline
719 : so the validator can start producing votes with the new authorized
720 : voter. */
721 0 : #define FD_ADD_AUTH_VOTER_STATE_UNLOCK_REQUESTED (6UL)
722 :
723 : static void FD_FN_SENSITIVE
724 : poll_add_authorized_voter( fd_admin_tile_ctx_t * ctx,
725 : ulong * state,
726 : uchar * keypair,
727 0 : ulong * result ) {
728 0 : fd_keyswitch_t * voter = ctx->voter_av_keyswitch;
729 :
730 0 : switch( *state ) {
731 0 : case FD_ADD_AUTH_VOTER_STATE_UNLOCKED: {
732 0 : if( FD_LIKELY( FD_KEYSWITCH_STATE_UNLOCKED==FD_ATOMIC_CAS( &voter->state, FD_KEYSWITCH_STATE_UNLOCKED, FD_KEYSWITCH_STATE_LOCKED ) ) ) {
733 0 : *state = FD_ADD_AUTH_VOTER_STATE_LOCKED;
734 0 : FD_LOG_INFO(( "Locking authorized voter set for authorized voter update..." ));
735 0 : } else {
736 : /* keyswitch changes should be guarded and ordered by adminctl.
737 : If the keyswitch is in a locked state means there is
738 : unexpected process state and the validator should crash. */
739 0 : FD_LOG_CRIT(( "keyswitch is in a locked state but should be unlocked" ));
740 0 : }
741 0 : break;
742 0 : }
743 0 : case FD_ADD_AUTH_VOTER_STATE_LOCKED: {
744 0 : for( ulong i=0UL; i<ctx->sign_av_keyswitch_cnt; i++ ) {
745 0 : fd_keyswitch_t * sign = ctx->sign_av_keyswitch[ i ];
746 0 : memcpy( sign->bytes, keypair, 64UL );
747 0 : sign->param = FD_KEYSWITCH_PARAM_AV_ADD;
748 0 : FD_COMPILER_MFENCE();
749 0 : sign->state = FD_KEYSWITCH_STATE_SWITCH_PENDING;
750 0 : FD_COMPILER_MFENCE();
751 0 : }
752 0 : fd_memzero_explicit( keypair, 32UL );
753 0 : *state = FD_ADD_AUTH_VOTER_STATE_SIGN_TILE_REQUESTED;
754 0 : FD_LOG_INFO(( "Requesting all sign tiles to update authorized voter key set..." ));
755 0 : break;
756 0 : }
757 0 : case FD_ADD_AUTH_VOTER_STATE_SIGN_TILE_REQUESTED: {
758 0 : int all_updated = 1;
759 0 : for( ulong i=0UL; i<ctx->sign_av_keyswitch_cnt; i++ ) {
760 0 : fd_keyswitch_t * sign = ctx->sign_av_keyswitch[ i ];
761 0 : if( FD_UNLIKELY( sign->state==FD_KEYSWITCH_STATE_SWITCH_PENDING ) ) {
762 0 : all_updated = 0;
763 0 : } else if( FD_UNLIKELY( sign->state==FD_KEYSWITCH_STATE_FAILED ) ) {
764 : /* Recoverable error: the sign tile failed to update the set
765 : of authorized voters is a result of bad caller input. All
766 : the sign tiles should be in sync, which means that if one
767 : sign tile failed, we expect all of them to. */
768 0 : fd_memzero_explicit( sign->bytes, 64UL );
769 0 : if( FD_LIKELY( !*result ) ) *result = sign->result;
770 0 : } else { /* sign->state==FD_KEYSWITCH_STATE_COMPLETED */
771 0 : fd_memzero_explicit( sign->bytes, 64UL );
772 0 : }
773 0 : }
774 :
775 0 : if( FD_LIKELY( all_updated ) ) {
776 0 : if( FD_UNLIKELY( *result ) ) *state = FD_ADD_AUTH_VOTER_STATE_VOTER_TILE_UPDATED;
777 0 : else *state = FD_ADD_AUTH_VOTER_STATE_SIGN_TILE_UPDATED;
778 0 : } else {
779 0 : FD_SPIN_PAUSE();
780 0 : }
781 0 : break;
782 0 : }
783 0 : case FD_ADD_AUTH_VOTER_STATE_SIGN_TILE_UPDATED: {
784 0 : memcpy( voter->bytes, keypair+32UL, 32UL );
785 0 : voter->param = FD_KEYSWITCH_PARAM_AV_ADD;
786 0 : FD_COMPILER_MFENCE();
787 0 : voter->state = FD_KEYSWITCH_STATE_SWITCH_PENDING;
788 0 : FD_COMPILER_MFENCE();
789 0 : *state = FD_ADD_AUTH_VOTER_STATE_VOTER_TILE_REQUESTED;
790 0 : FD_LOG_INFO(( "Requesting votor tile to update authorized voter key set..." ));
791 0 : break;
792 0 : }
793 0 : case FD_ADD_AUTH_VOTER_STATE_VOTER_TILE_REQUESTED: {
794 : /* There is a guarantee that the voter tile will be in sync with
795 : the set of authorized voters in the sign tile. At this point
796 : that means that the command should succeed because invariants
797 : such as not having duplicate authorized voter keys and too many
798 : authorized voters are upheld. If this doesn't hold true, the
799 : voter tile will detect any corruption and gracefully crash the
800 : validator. */
801 0 : if( FD_LIKELY( voter->state==FD_KEYSWITCH_STATE_COMPLETED ) ) {
802 0 : *state = FD_ADD_AUTH_VOTER_STATE_VOTER_TILE_UPDATED;
803 0 : FD_LOG_INFO(( "votor tile key set successfully updated..." ));
804 0 : } else {
805 0 : FD_SPIN_PAUSE();
806 0 : }
807 0 : break;
808 0 : }
809 0 : case FD_ADD_AUTH_VOTER_STATE_VOTER_TILE_UPDATED: {
810 0 : voter->state = FD_KEYSWITCH_STATE_UNHALT_PENDING;
811 0 : *state = FD_ADD_AUTH_VOTER_STATE_UNLOCK_REQUESTED;
812 0 : FD_LOG_INFO(( "Requesting an unlock of the authorized voter key set..." ));
813 0 : break;
814 0 : }
815 0 : case FD_ADD_AUTH_VOTER_STATE_UNLOCK_REQUESTED: {
816 0 : if( FD_LIKELY( voter->state==FD_KEYSWITCH_STATE_UNLOCKED ) ) {
817 0 : *state = FD_ADD_AUTH_VOTER_STATE_UNLOCKED;
818 0 : FD_LOG_INFO(( "Authorized voter key set unlocked..." ));
819 0 : } else {
820 0 : FD_SPIN_PAUSE();
821 0 : }
822 0 : break;
823 0 : }
824 0 : default: {
825 0 : FD_LOG_CRIT(( "Unexpected add-authorized-voter state %lu", *state ));
826 0 : }
827 0 : }
828 0 : }
829 :
830 : static void FD_FN_SENSITIVE
831 : add_authorized_voter( fd_admin_tile_ctx_t * ctx,
832 : ulong slot_idx,
833 : void * data,
834 0 : ulong data_sz ) {
835 :
836 0 : fd_adminctl_t * adminctl = ctx->adminctl;
837 0 : fd_event_admin_command_t event = prepare_admin_command( FD_EVENT_ADMIN_COMMAND_TYPE_ADD_AUTHORIZED_VOTER, data, data_sz );
838 :
839 0 : if( FD_UNLIKELY( data_sz<sizeof(ulong) ) ) {
840 0 : FD_LOG_WARNING(( "adminctl add-authorized-voter payload too small: %lu", data_sz ));
841 0 : report_admin_command( &event, FD_EVENT_ADMIN_COMMAND_RESULT_ABI_SIZE_MISMATCH );
842 0 : fd_adminctl_complete( adminctl, slot_idx, FD_ADMINCTL_RESULT_ABI_SIZE_MISMATCH );
843 0 : return;
844 0 : }
845 :
846 0 : ulong version = FD_LOAD( ulong, data );
847 0 : if( FD_UNLIKELY( version!=FD_ADMINCTL_ADD_AUTH_VOTER_PAYLOAD_VERSION ) ) {
848 0 : FD_LOG_WARNING(( "unsupported adminctl add-authorized-voter payload version %lu", version ));
849 0 : report_admin_command( &event, FD_EVENT_ADMIN_COMMAND_RESULT_ABI_VERSION_MISMATCH );
850 0 : fd_adminctl_complete( adminctl, slot_idx, FD_ADMINCTL_RESULT_ABI_VERSION_MISMATCH );
851 0 : return;
852 0 : }
853 :
854 0 : if( FD_UNLIKELY( data_sz!=sizeof(fd_adminctl_add_auth_voter_t) ) ) {
855 0 : FD_LOG_WARNING(( "unexpected adminctl add-authorized-voter payload_sz %lu", data_sz ));
856 0 : report_admin_command( &event, FD_EVENT_ADMIN_COMMAND_RESULT_ABI_SIZE_MISMATCH );
857 0 : fd_adminctl_complete( adminctl, slot_idx, FD_ADMINCTL_RESULT_ABI_SIZE_MISMATCH );
858 0 : return;
859 0 : }
860 :
861 0 : fd_adminctl_add_auth_voter_t * req = fd_type_pun( data );
862 0 : FD_BASE58_ENCODE_32_BYTES( req->keypair+32UL, authorized_voter );
863 0 : FD_TEST( fd_cstr_printf_check( (char *)event.args_json, sizeof(event.args_json), &event.args_json_len, "{\"authorized_voter\":\"%s\"}", authorized_voter ) );
864 :
865 0 : uchar public_key[ 32UL ];
866 0 : fd_ed25519_public_from_private( public_key, req->keypair, ctx->sha512 );
867 0 : if( FD_UNLIKELY( memcmp( public_key, req->keypair+32UL, 32UL ) ) ) {
868 0 : FD_LOG_WARNING(( "add-authorized-voter failed: public key in key file does not match private key" ));
869 0 : report_admin_command_custom_result( &event, "keypair_mismatch" );
870 0 : fd_adminctl_complete( adminctl, slot_idx, FD_ADD_AUTHORIZED_VOTER_RESULT_KEYPAIR_MISMATCH );
871 0 : return;
872 0 : }
873 :
874 0 : ulong result = FD_ADMINCTL_RESULT_SUCCESS;
875 0 : ulong state = FD_ADD_AUTH_VOTER_STATE_UNLOCKED;
876 0 : for(;;) {
877 0 : poll_add_authorized_voter( ctx, &state, req->keypair, &result );
878 0 : if( FD_UNLIKELY( state==FD_ADD_AUTH_VOTER_STATE_UNLOCKED ) ) break;
879 0 : }
880 :
881 0 : switch( result ) {
882 0 : case FD_ADMINCTL_RESULT_SUCCESS:
883 0 : report_admin_command( &event, FD_EVENT_ADMIN_COMMAND_RESULT_SUCCESS );
884 0 : break;
885 0 : case FD_ADD_AUTHORIZED_VOTER_RESULT_MAX_AUTH_VOTERS:
886 0 : report_admin_command_custom_result( &event, "max_authorized_voters" );
887 0 : break;
888 0 : case FD_ADD_AUTHORIZED_VOTER_RESULT_DUPLICATE_AUTH_VOTER:
889 0 : report_admin_command_custom_result( &event, "duplicate_authorized_voter" );
890 0 : break;
891 0 : default:
892 0 : FD_LOG_ERR(( "unexpected add-authorized-voter result %lu", result ));
893 0 : }
894 0 : fd_adminctl_complete( adminctl, slot_idx, result );
895 0 : }
896 :
897 : static void
898 : snapshot_create( fd_admin_tile_ctx_t * ctx,
899 : fd_stem_context_t * stem,
900 : ulong slot_idx,
901 : void const * payload,
902 0 : ulong payload_sz ) {
903 :
904 0 : fd_adminctl_t * adminctl = ctx->adminctl;
905 0 : fd_event_admin_command_t event = prepare_admin_command( FD_EVENT_ADMIN_COMMAND_TYPE_SNAPSHOT_CREATE, payload, payload_sz );
906 :
907 0 : if( FD_UNLIKELY( payload_sz!=sizeof(fd_adminctl_snap_create_t) ) ) {
908 0 : FD_LOG_WARNING(( "unexpected adminctl snapshot-create payload_sz %lu", payload_sz ));
909 0 : report_admin_command( &event, FD_EVENT_ADMIN_COMMAND_RESULT_ABI_SIZE_MISMATCH );
910 0 : fd_adminctl_complete( adminctl, slot_idx, FD_ADMINCTL_RESULT_ABI_SIZE_MISMATCH );
911 0 : return;
912 0 : }
913 0 : fd_adminctl_snap_create_t const * req = fd_type_pun_const( payload );
914 0 : if( FD_UNLIKELY( req->version!=FD_ADMINCTL_SNAP_CREATE_PAYLOAD_VERSION ) ) {
915 0 : FD_LOG_WARNING(( "unsupported adminctl snapshot-create payload version %lu", req->version ));
916 0 : report_admin_command( &event, FD_EVENT_ADMIN_COMMAND_RESULT_ABI_VERSION_MISMATCH );
917 0 : fd_adminctl_complete( adminctl, slot_idx, FD_ADMINCTL_RESULT_ABI_VERSION_MISMATCH );
918 0 : return;
919 0 : }
920 0 : ulong target_slot = req->slot;
921 0 : FD_TEST( fd_cstr_printf_check( (char *)event.args_json, sizeof(event.args_json), &event.args_json_len, "{\"target_slot\":%lu}", target_slot ) );
922 :
923 0 : if( FD_UNLIKELY( ctx->replay_out_idx==ULONG_MAX ) ) {
924 0 : FD_LOG_WARNING(( "admin requested snapshot creation, but admin tile has no replay command link" ));
925 0 : report_admin_command( &event, FD_EVENT_ADMIN_COMMAND_RESULT_UNSUPPORTED );
926 0 : fd_adminctl_complete( adminctl, slot_idx, FD_ADMINCTL_RESULT_UNSUPPORTED );
927 0 : return;
928 0 : }
929 :
930 0 : if( FD_UNLIKELY( ctx->snap_create_slot_idx!=ULONG_MAX ) ) {
931 0 : FD_LOG_WARNING(( "admin requested snapshot creation, but another snapshot-create command is pending replay response" ));
932 0 : report_admin_command_custom_result( &event, "busy" );
933 0 : fd_adminctl_complete( adminctl, slot_idx, FD_SNAPSHOT_CREATE_RESULT_BUSY );
934 0 : return;
935 0 : }
936 :
937 0 : ulong ctl = fd_frag_meta_ctl( FD_ADMINCTL_CMD_SNAP_CREATE, 0, 0, 0 );
938 0 : ulong tspub = fd_frag_meta_ts_comp( fd_tickcount() );
939 0 : fd_stem_publish( stem, ctx->replay_out_idx, target_slot, 0UL, 0UL, ctl, 0UL, tspub );
940 0 : ctx->snap_create_slot_idx = slot_idx;
941 0 : ctx->snap_create_target_slot = target_slot;
942 0 : ctx->snap_create_start_time = event.start_time;
943 0 : }
944 :
945 : static void
946 : snapshot_create_response( fd_admin_tile_ctx_t * ctx,
947 : ulong sig,
948 0 : ulong ctl ) {
949 :
950 0 : if( FD_UNLIKELY( fd_frag_meta_ctl_orig( ctl )!=FD_ADMINCTL_CMD_SNAP_CREATE ) ) {
951 0 : FD_LOG_ERR(( "unexpected replay admin response orig %lu", fd_frag_meta_ctl_orig( ctl ) ));
952 0 : }
953 :
954 0 : fd_event_admin_command_t event = {
955 0 : .type = FD_EVENT_ADMIN_COMMAND_TYPE_SNAPSHOT_CREATE,
956 0 : .start_time = ctx->snap_create_start_time,
957 0 : .payload_version = FD_ADMINCTL_SNAP_CREATE_PAYLOAD_VERSION,
958 0 : .has_payload_version = 1,
959 0 : .payload_size = sizeof(fd_adminctl_snap_create_t),
960 0 : };
961 0 : FD_TEST( fd_cstr_printf_check( (char *)event.args_json, sizeof(event.args_json), &event.args_json_len, "{\"target_slot\":%lu}", ctx->snap_create_target_slot ) );
962 0 : switch( sig ) {
963 0 : case FD_ADMINCTL_RESULT_SUCCESS:
964 0 : report_admin_command( &event, FD_EVENT_ADMIN_COMMAND_RESULT_SUCCESS );
965 0 : break;
966 0 : case FD_SNAPSHOT_CREATE_RESULT_BUSY:
967 0 : report_admin_command_custom_result( &event, "busy" );
968 0 : break;
969 0 : case FD_ADMINCTL_RESULT_UNSUPPORTED:
970 0 : report_admin_command( &event, FD_EVENT_ADMIN_COMMAND_RESULT_UNSUPPORTED );
971 0 : break;
972 0 : case FD_SNAPSHOT_CREATE_RESULT_NOT_READY:
973 0 : report_admin_command_custom_result( &event, "not_ready" );
974 0 : break;
975 0 : case FD_SNAPSHOT_CREATE_RESULT_SLOT_IN_PAST:
976 0 : report_admin_command_custom_result( &event, "slot_in_past" );
977 0 : break;
978 0 : default:
979 0 : FD_LOG_ERR(( "unexpected snapshot-create result %lu", sig ));
980 0 : }
981 0 : fd_adminctl_complete( ctx->adminctl, ctx->snap_create_slot_idx, sig );
982 0 : ctx->snap_create_slot_idx = ULONG_MAX;
983 0 : ctx->snap_create_target_slot = 0UL;
984 0 : ctx->snap_create_start_time = 0UL;
985 0 : }
986 :
987 : /* Removing all authorized voters from the validator is the inverse of
988 : add-authorized-voter, and must be done in the opposite order. When
989 : adding, the sign tile is updated before the voter tile (the tower
990 : tile, or the votor tile under Alpenglow) so that the voter never asks
991 : the sign tile to sign a vote with an authority index the sign tile
992 : does not yet know about. When removing, the voter tile must be
993 : cleared before the sign tiles, so that the voter stops referencing an
994 : authorized voter index before the sign tile drops the corresponding
995 : key.
996 :
997 : All transitions are linear and in forward order.
998 :
999 : Unlike add-authorized-voter, removal cannot fail on the tile side: it
1000 : is unconditional and idempotent (clearing an empty set succeeds).
1001 :
1002 : The votor signs synchronously, so it has no signing requests in
1003 : flight once it confirms the clear. */
1004 :
1005 : /* State 0: UNLOCKED
1006 : The validator is not currently in the process of switching keys. */
1007 0 : #define FD_REMOVE_ALL_AUTH_VOTERS_STATE_UNLOCKED (0UL)
1008 :
1009 : /* State 1: LOCKED
1010 : Some client to the validator has requested to remove all authorized
1011 : voters. To do so, it acquired an exclusive lock on the validator to
1012 : prevent the removal potentially being interleaved with another
1013 : client. */
1014 0 : #define FD_REMOVE_ALL_AUTH_VOTERS_STATE_LOCKED (1UL)
1015 :
1016 : /* State 2: VOTER_TILE_REQUESTED
1017 : The voter tile has been notified to clear its authorized voter set.
1018 : It is cleared first so it stops voting with any authorized voter
1019 : before the sign tiles drop the keys. */
1020 0 : #define FD_REMOVE_ALL_AUTH_VOTERS_STATE_VOTER_TILE_REQUESTED (2UL)
1021 :
1022 : /* State 3: VOTER_TILE_CLEARED
1023 : The voter tile confirmed it cleared its authorized voter map. At
1024 : this point the validator will only prepare votes signed by the
1025 : identity key. */
1026 0 : #define FD_REMOVE_ALL_AUTH_VOTERS_STATE_VOTER_TILE_CLEARED (3UL)
1027 :
1028 : /* State 4: SIGN_TILE_REQUESTED
1029 : All sign tiles have been notified to clear their authorized voter
1030 : keys. */
1031 0 : #define FD_REMOVE_ALL_AUTH_VOTERS_STATE_SIGN_TILE_REQUESTED (4UL)
1032 :
1033 : /* State 5: SIGN_TILE_CLEARED
1034 : All sign tiles confirmed they cleared (and securely zeroed) their
1035 : authorized voter keys. */
1036 0 : #define FD_REMOVE_ALL_AUTH_VOTERS_STATE_SIGN_TILE_CLEARED (5UL)
1037 :
1038 : /* State 6: UNLOCK_REQUESTED
1039 : The client requests that the voter tile release the lock. */
1040 0 : #define FD_REMOVE_ALL_AUTH_VOTERS_STATE_UNLOCK_REQUESTED (6UL)
1041 :
1042 : static void
1043 : poll_remove_all_authorized_voters( fd_admin_tile_ctx_t * ctx,
1044 0 : ulong * state ) {
1045 0 : fd_keyswitch_t * voter = ctx->voter_av_keyswitch;
1046 :
1047 0 : switch( *state ) {
1048 0 : case FD_REMOVE_ALL_AUTH_VOTERS_STATE_UNLOCKED: {
1049 0 : if( FD_LIKELY( FD_KEYSWITCH_STATE_UNLOCKED==FD_ATOMIC_CAS( &voter->state, FD_KEYSWITCH_STATE_UNLOCKED, FD_KEYSWITCH_STATE_LOCKED ) ) ) {
1050 0 : *state = FD_REMOVE_ALL_AUTH_VOTERS_STATE_LOCKED;
1051 0 : FD_LOG_INFO(( "Locking authorized voter set for authorized voter update..." ));
1052 0 : } else {
1053 : /* keyswitch changes should be guarded and ordered by adminctl.
1054 : If the keyswitch is in a locked state means there is
1055 : unexpected process state and the validator should crash. */
1056 0 : FD_LOG_CRIT(( "keyswitch is in a locked state but should be unlocked" ));
1057 0 : }
1058 0 : break;
1059 0 : }
1060 0 : case FD_REMOVE_ALL_AUTH_VOTERS_STATE_LOCKED: {
1061 0 : voter->param = FD_KEYSWITCH_PARAM_AV_CLEAR;
1062 0 : FD_COMPILER_MFENCE();
1063 0 : voter->state = FD_KEYSWITCH_STATE_SWITCH_PENDING;
1064 0 : FD_COMPILER_MFENCE();
1065 0 : *state = FD_REMOVE_ALL_AUTH_VOTERS_STATE_VOTER_TILE_REQUESTED;
1066 0 : FD_LOG_INFO(( "Requesting votor tile to clear authorized voter key set..." ));
1067 0 : break;
1068 0 : }
1069 0 : case FD_REMOVE_ALL_AUTH_VOTERS_STATE_VOTER_TILE_REQUESTED: {
1070 0 : if( FD_LIKELY( voter->state==FD_KEYSWITCH_STATE_COMPLETED ) ) {
1071 0 : *state = FD_REMOVE_ALL_AUTH_VOTERS_STATE_VOTER_TILE_CLEARED;
1072 0 : FD_LOG_INFO(( "votor tile authorized voter key set cleared..." ));
1073 0 : } else {
1074 0 : FD_SPIN_PAUSE();
1075 0 : }
1076 0 : break;
1077 0 : }
1078 0 : case FD_REMOVE_ALL_AUTH_VOTERS_STATE_VOTER_TILE_CLEARED: {
1079 0 : for( ulong i=0UL; i<ctx->sign_av_keyswitch_cnt; i++ ) {
1080 0 : fd_keyswitch_t * sign = ctx->sign_av_keyswitch[ i ];
1081 0 : sign->param = FD_KEYSWITCH_PARAM_AV_CLEAR;
1082 0 : FD_COMPILER_MFENCE();
1083 0 : sign->state = FD_KEYSWITCH_STATE_SWITCH_PENDING;
1084 0 : FD_COMPILER_MFENCE();
1085 0 : }
1086 0 : *state = FD_REMOVE_ALL_AUTH_VOTERS_STATE_SIGN_TILE_REQUESTED;
1087 0 : FD_LOG_INFO(( "Requesting all sign tiles to clear authorized voter key set..." ));
1088 0 : break;
1089 0 : }
1090 0 : case FD_REMOVE_ALL_AUTH_VOTERS_STATE_SIGN_TILE_REQUESTED: {
1091 0 : int all_cleared = 1;
1092 0 : for( ulong i=0UL; i<ctx->sign_av_keyswitch_cnt; i++ ) {
1093 0 : fd_keyswitch_t * sign = ctx->sign_av_keyswitch[ i ];
1094 0 : if( FD_UNLIKELY( sign->state!=FD_KEYSWITCH_STATE_COMPLETED ) ) {
1095 0 : all_cleared = 0;
1096 0 : break;
1097 0 : }
1098 0 : }
1099 :
1100 0 : if( FD_LIKELY( all_cleared ) ) *state = FD_REMOVE_ALL_AUTH_VOTERS_STATE_SIGN_TILE_CLEARED;
1101 0 : else FD_SPIN_PAUSE();
1102 0 : break;
1103 0 : }
1104 0 : case FD_REMOVE_ALL_AUTH_VOTERS_STATE_SIGN_TILE_CLEARED: {
1105 0 : voter->state = FD_KEYSWITCH_STATE_UNHALT_PENDING;
1106 0 : *state = FD_REMOVE_ALL_AUTH_VOTERS_STATE_UNLOCK_REQUESTED;
1107 0 : FD_LOG_INFO(( "Requesting an unlock of the authorized voter key set..." ));
1108 0 : break;
1109 0 : }
1110 0 : case FD_REMOVE_ALL_AUTH_VOTERS_STATE_UNLOCK_REQUESTED: {
1111 0 : if( FD_LIKELY( voter->state==FD_KEYSWITCH_STATE_UNLOCKED ) ) {
1112 0 : *state = FD_REMOVE_ALL_AUTH_VOTERS_STATE_UNLOCKED;
1113 0 : FD_LOG_INFO(( "Authorized voter key set unlocked..." ));
1114 0 : } else {
1115 0 : FD_SPIN_PAUSE();
1116 0 : }
1117 0 : break;
1118 0 : }
1119 0 : default: {
1120 0 : FD_LOG_CRIT(( "Unexpected remove-all-authorized-voters state %lu", *state ));
1121 0 : }
1122 0 : }
1123 0 : }
1124 :
1125 : static void
1126 : remove_all_authorized_voters( fd_admin_tile_ctx_t * ctx,
1127 : ulong slot_idx,
1128 : void * data,
1129 0 : ulong data_sz ) {
1130 :
1131 0 : fd_adminctl_t * adminctl = ctx->adminctl;
1132 0 : fd_event_admin_command_t event = prepare_admin_command( FD_EVENT_ADMIN_COMMAND_TYPE_REMOVE_ALL_AUTHORIZED_VOTERS, data, data_sz );
1133 :
1134 0 : if( FD_UNLIKELY( data_sz<sizeof(ulong) ) ) {
1135 0 : FD_LOG_WARNING(( "adminctl remove-all-authorized-voters payload too small: %lu", data_sz ));
1136 0 : report_admin_command( &event, FD_EVENT_ADMIN_COMMAND_RESULT_ABI_SIZE_MISMATCH );
1137 0 : fd_adminctl_complete( adminctl, slot_idx, FD_ADMINCTL_RESULT_ABI_SIZE_MISMATCH );
1138 0 : return;
1139 0 : }
1140 :
1141 0 : ulong version = FD_LOAD( ulong, data );
1142 0 : if( FD_UNLIKELY( version!=FD_ADMINCTL_REMOVE_ALL_AUTH_VOTERS_PAYLOAD_VERSION ) ) {
1143 0 : FD_LOG_WARNING(( "unsupported adminctl remove-all-authorized-voters payload version %lu", version ));
1144 0 : report_admin_command( &event, FD_EVENT_ADMIN_COMMAND_RESULT_ABI_VERSION_MISMATCH );
1145 0 : fd_adminctl_complete( adminctl, slot_idx, FD_ADMINCTL_RESULT_ABI_VERSION_MISMATCH );
1146 0 : return;
1147 0 : }
1148 :
1149 0 : if( FD_UNLIKELY( data_sz!=sizeof(fd_adminctl_remove_all_auth_voters_t) ) ) {
1150 0 : FD_LOG_WARNING(( "unexpected adminctl remove-all-authorized-voters payload_sz %lu", data_sz ));
1151 0 : report_admin_command( &event, FD_EVENT_ADMIN_COMMAND_RESULT_ABI_SIZE_MISMATCH );
1152 0 : fd_adminctl_complete( adminctl, slot_idx, FD_ADMINCTL_RESULT_ABI_SIZE_MISMATCH );
1153 0 : return;
1154 0 : }
1155 :
1156 0 : ulong state = FD_REMOVE_ALL_AUTH_VOTERS_STATE_UNLOCKED;
1157 0 : for(;;) {
1158 0 : poll_remove_all_authorized_voters( ctx, &state );
1159 0 : if( FD_UNLIKELY( state==FD_REMOVE_ALL_AUTH_VOTERS_STATE_UNLOCKED ) ) break;
1160 0 : }
1161 :
1162 0 : report_admin_command( &event, FD_EVENT_ADMIN_COMMAND_RESULT_SUCCESS );
1163 0 : fd_adminctl_complete( adminctl, slot_idx, FD_ADMINCTL_RESULT_SUCCESS );
1164 0 : }
1165 :
1166 : static inline long
1167 0 : next_deadline( fd_admin_tile_ctx_t const * ctx ) {
1168 0 : return ctx->deadline_ticks;
1169 0 : }
1170 :
1171 : static inline void FD_FN_SENSITIVE
1172 : after_credit( fd_admin_tile_ctx_t * ctx,
1173 : fd_stem_context_t * stem,
1174 : int * opt_poll_in,
1175 0 : int * charge_busy ) {
1176 :
1177 0 : fd_adminctl_t * adminctl = ctx->adminctl;
1178 0 : ulong slot_idx = ULONG_MAX;
1179 0 : void * payload = NULL;
1180 0 : ulong payload_sz = 0UL;
1181 :
1182 0 : if( FD_LIKELY( stem->now<ctx->deadline_ticks ) ) return;
1183 0 : ctx->deadline_ticks = stem->now + fd_long_if( ctx->snap_create_slot_idx!=ULONG_MAX, ctx->poll_ticks, ctx->idle_poll_ticks );
1184 :
1185 0 : ulong cmd_id = FD_ADMINCTL_CMD_IDLE;
1186 0 : for( ulong i=0UL; i<FD_ADMINCTL_SLOT_CNT && cmd_id==FD_ADMINCTL_CMD_IDLE; i++ ) cmd_id = fd_adminctl_poll( adminctl, &slot_idx, &payload, &payload_sz );
1187 0 : switch( cmd_id ) {
1188 0 : case FD_ADMINCTL_CMD_IDLE:
1189 0 : break;
1190 0 : case FD_ADMINCTL_CMD_ADD_AUTH_VOTER:
1191 0 : add_authorized_voter( ctx, slot_idx, payload, payload_sz );
1192 0 : *charge_busy = 1;
1193 0 : break;
1194 0 : case FD_ADMINCTL_CMD_SET_IDENTITY:
1195 0 : set_identity( ctx, slot_idx, payload, payload_sz );
1196 0 : *charge_busy = 1;
1197 0 : break;
1198 0 : case FD_ADMINCTL_CMD_REMOVE_ALL_AUTH_VOTERS:
1199 0 : remove_all_authorized_voters( ctx, slot_idx, payload, payload_sz );
1200 0 : *charge_busy = 1;
1201 0 : break;
1202 0 : case FD_ADMINCTL_CMD_GET_IDENTITY:
1203 0 : get_identity( ctx, slot_idx, payload, payload_sz );
1204 0 : *charge_busy = 1;
1205 0 : break;
1206 0 : case FD_ADMINCTL_CMD_SNAP_CREATE:
1207 0 : snapshot_create( ctx, stem, slot_idx, payload, payload_sz );
1208 0 : if( FD_LIKELY( ctx->snap_create_slot_idx!=ULONG_MAX ) ) ctx->deadline_ticks = stem->now + ctx->poll_ticks; /* poll fast while replay responds */
1209 0 : *charge_busy = 1;
1210 0 : *opt_poll_in = 0;
1211 0 : break;
1212 0 : default:
1213 0 : FD_LOG_WARNING(( "unexpected adminctl cmd %lu", cmd_id ));
1214 0 : fd_adminctl_complete( adminctl, slot_idx, FD_ADMINCTL_RESULT_UNKNOWN_COMMAND );
1215 0 : }
1216 0 : }
1217 :
1218 : static void
1219 : during_frag( fd_admin_tile_ctx_t * ctx,
1220 : ulong in_idx FD_PARAM_UNUSED,
1221 : ulong seq FD_PARAM_UNUSED,
1222 : ulong sig,
1223 : ulong chunk FD_PARAM_UNUSED,
1224 : ulong sz FD_PARAM_UNUSED,
1225 0 : ulong ctl ) {
1226 0 : if( FD_UNLIKELY( ctx->snap_create_slot_idx==ULONG_MAX ) ) {
1227 0 : FD_LOG_ERR(( "unexpected replay snapshot-create response with no pending adminctl command" ));
1228 0 : return;
1229 0 : }
1230 0 : snapshot_create_response( ctx, sig, ctl );
1231 0 : }
1232 :
1233 : static ulong
1234 : populate_allowed_seccomp( fd_topo_t const * topo FD_PARAM_UNUSED,
1235 : fd_topo_tile_t const * tile FD_PARAM_UNUSED,
1236 : ulong out_cnt,
1237 0 : struct sock_filter * out ) {
1238 :
1239 0 : populate_sock_filter_policy_fd_admin_tile( out_cnt, out, (uint)fd_log_private_logfile_fd() );
1240 0 : return sock_filter_policy_fd_admin_tile_instr_cnt;
1241 0 : }
1242 :
1243 : static ulong
1244 : populate_allowed_fds( fd_topo_t const * topo FD_PARAM_UNUSED,
1245 : fd_topo_tile_t const * tile FD_PARAM_UNUSED,
1246 : ulong out_fds_cnt,
1247 0 : int * out_fds ) {
1248 :
1249 0 : if( FD_UNLIKELY( out_fds_cnt<2UL ) ) FD_LOG_ERR(( "out_fds_cnt %lu", out_fds_cnt ));
1250 :
1251 0 : ulong out_cnt = 0UL;
1252 0 : out_fds[ out_cnt++ ] = 2; /* stderr */
1253 0 : if( FD_LIKELY( -1!=fd_log_private_logfile_fd() ) )
1254 0 : out_fds[ out_cnt++ ] = fd_log_private_logfile_fd(); /* logfile */
1255 0 : return out_cnt;
1256 0 : }
1257 :
1258 0 : #define STEM_BURST (1UL)
1259 0 : #define STEM_LAZY ((long)1e6) /* 1ms */
1260 : #define STEM_ALWAYS_PARK 1
1261 :
1262 0 : #define STEM_CALLBACK_CONTEXT_TYPE fd_admin_tile_ctx_t
1263 0 : #define STEM_CALLBACK_CONTEXT_ALIGN alignof(fd_admin_tile_ctx_t)
1264 :
1265 0 : #define STEM_CALLBACK_NEXT_DEADLINE next_deadline
1266 0 : #define STEM_CALLBACK_AFTER_CREDIT after_credit
1267 0 : #define STEM_CALLBACK_DURING_FRAG during_frag
1268 :
1269 : #include "../../disco/stem/fd_stem.c"
1270 :
1271 : static ulong
1272 0 : max_event_sz( fd_topo_tile_t const * tile FD_PARAM_UNUSED ) {
1273 0 : return sizeof(fd_event_admin_command_t);
1274 0 : }
1275 :
1276 : fd_topo_run_tile_t fd_tile_admin = {
1277 : .name = "admin",
1278 : .max_event_sz = max_event_sz,
1279 : .populate_allowed_seccomp = populate_allowed_seccomp,
1280 : .populate_allowed_fds = populate_allowed_fds,
1281 : .scratch_align = scratch_align,
1282 : .scratch_footprint = scratch_footprint,
1283 : .privileged_init = privileged_init,
1284 : .unprivileged_init = unprivileged_init,
1285 : .run = stem_run,
1286 : };
|