LCOV - code coverage report
Current view: top level - discof/admin - fd_admin_tile.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 834 0.0 %
Date: 2026-10-22 18:10:43 Functions: 0 46 0.0 %

          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             : };

Generated by: LCOV version 1.14