Line data Source code
1 : #include "adminctl_client.h"
2 : #include "../../shared/fd_config.h"
3 : #include "../../shared/fd_action.h"
4 :
5 : #include "../../../disco/keyguard/fd_keyload.h"
6 : #include "../../../ballet/base58/fd_base58.h"
7 : #include "../../../ballet/ed25519/fd_ed25519.h"
8 :
9 : #include <unistd.h>
10 :
11 : void
12 : add_authorized_voter_cmd_args( int * pargc,
13 : char *** pargv,
14 0 : args_t * args ) {
15 :
16 0 : char const * name = fd_env_strip_cmdline_cstr( pargc, pargv, "--name", NULL, NULL );
17 0 : if( FD_UNLIKELY( name ) ) fd_cstr_ncpy( args->add_authorized_voter.name, name, sizeof(args->add_authorized_voter.name) );
18 :
19 0 : if( FD_UNLIKELY( *pargc<1 ) ) {
20 0 : FD_LOG_ERR(( "Usage: %s add-authorized-voter <keypair>", FD_BINARY_NAME ));
21 0 : }
22 :
23 0 : char const * path = *pargv[0];
24 0 : (*pargc)--;
25 0 : (*pargv)++;
26 :
27 0 : if( FD_UNLIKELY( !strcmp( path, "-" ) ) ) {
28 0 : uchar * keypair_wr = fd_keyload_alloc_protected_pages( 1UL, 2UL );
29 0 : FD_LOG_STDOUT(( "Reading authorized voter keypair from stdin. Press Ctrl-D when done.\n" ));
30 0 : fd_keyload_read( STDIN_FILENO, "stdin", keypair_wr );
31 0 : args->add_authorized_voter.keypair = fd_keyload_mprotect_ro( keypair_wr, 0 );
32 0 : } else {
33 0 : args->add_authorized_voter.keypair = fd_keyload_load( path, 0 );
34 0 : }
35 0 : }
36 :
37 : static void FD_FN_SENSITIVE
38 : add_authorized_voter( args_t * args,
39 0 : config_t * config ) {
40 :
41 0 : uchar public_key[ 32 ];
42 0 : fd_sha512_t sha512[ 1 ];
43 0 : FD_TEST( fd_sha512_join( fd_sha512_new( sha512 ) ) );
44 :
45 0 : fd_ed25519_public_from_private( public_key, args->add_authorized_voter.keypair, sha512 );
46 0 : if( FD_UNLIKELY( memcmp( public_key, args->add_authorized_voter.keypair+32UL, 32UL ) ) ) {
47 0 : FD_LOG_ERR(( "The public key in the key file does not match the public key derived from the private key."
48 0 : "Firedancer will not use the key pair to sign as it might leak the private key." ));
49 0 : }
50 :
51 0 : fd_adminctl_t * adminctl = adminctl_client_attach( config, args->add_authorized_voter.name );
52 :
53 0 : void * payload = NULL;
54 0 : ulong payload_max = 0UL;
55 0 : ulong slot_idx = fd_adminctl_reserve( adminctl, &payload, &payload_max );
56 0 : if( FD_UNLIKELY( slot_idx==ULONG_MAX ) ) {
57 0 : FD_LOG_ERR(( "Failed to process `add-authorized-voter` command as there are other pending "
58 0 : "commands that are being processed. Please wait for other commands to complete "
59 0 : "or forcefully terminate the other processes and retry the command." ));
60 0 : }
61 :
62 0 : fd_adminctl_add_auth_voter_t * req = (fd_adminctl_add_auth_voter_t *)payload;
63 0 : req->version = FD_ADMINCTL_ADD_AUTH_VOTER_PAYLOAD_VERSION;
64 0 : memcpy( req->keypair, args->add_authorized_voter.keypair, 64UL );
65 :
66 0 : uchar * keypair_wr = fd_keyload_mprotect_wr( args->add_authorized_voter.keypair, 0 );
67 0 : fd_memzero_explicit( keypair_wr, 64UL );
68 0 : fd_keyload_mprotect_ro( keypair_wr, 0 );
69 :
70 0 : fd_adminctl_publish( adminctl, slot_idx, FD_ADMINCTL_CMD_ADD_AUTH_VOTER, sizeof(fd_adminctl_add_auth_voter_t) );
71 :
72 0 : ulong result = fd_adminctl_wait( adminctl, slot_idx );
73 0 : switch( result ) {
74 0 : case FD_ADMINCTL_RESULT_SUCCESS:
75 0 : {
76 0 : char voter_key_base58[ FD_BASE58_ENCODED_32_SZ ];
77 0 : fd_base58_encode_32( public_key, NULL, voter_key_base58 );
78 0 : FD_LOG_NOTICE(( "authorized voter key %s%s%s added successfully", fd_log_style_bold(), voter_key_base58, fd_log_style_normal() ));
79 0 : break;
80 0 : }
81 0 : case FD_ADMINCTL_RESULT_ABI_VERSION_MISMATCH:
82 0 : case FD_ADMINCTL_RESULT_ABI_SIZE_MISMATCH:
83 0 : case FD_ADD_AUTHORIZED_VOTER_RESULT_KEYPAIR_MISMATCH:
84 0 : FD_LOG_ERR(( "Failed to add authorized voter key: the command was not able to "
85 0 : "successfully communicate with the running Firedancer process. It "
86 0 : "is possible that you are running the command from an older or "
87 0 : "newer version of Firedancer that is no longer compatible." ));
88 0 : case FD_ADD_AUTHORIZED_VOTER_RESULT_MAX_AUTH_VOTERS:
89 0 : FD_LOG_ERR(( "Failed to add authorized voter key: maximum number of authorized voters "
90 0 : "supported by the validator has been reached" ));
91 0 : case FD_ADD_AUTHORIZED_VOTER_RESULT_DUPLICATE_AUTH_VOTER:
92 0 : FD_LOG_ERR(( "Failed to add authorized voter key: the authorized voter key exists in "
93 0 : "the validator's authorized voter list" ));
94 0 : default:
95 0 : FD_LOG_ERR(( "Unexpected add-authorized-voter result %lu. This can be a result "
96 0 : "of a version mismatch between the command and the running Firedancer "
97 0 : "process. Please report this to the Firedancer team for investigation.", result ));
98 0 : }
99 0 : }
100 :
101 : void
102 : add_authorized_voter_cmd_fn( args_t * args,
103 0 : config_t * config ) {
104 0 : add_authorized_voter( args, config );
105 0 : }
106 :
107 : static void
108 0 : add_authorized_voter_args_help( fd_action_help_t * help ) {
109 : fd_action_help_arg( help, "<keypair>", NULL, "Path to the authorized voter keypair to add, in the standard Solana\n"
110 0 : "keypair file format (the 64-byte JSON array). The full keypair is\n"
111 0 : "required, not just the public key, because the validator must sign\n"
112 0 : "votes with it. Pass `-` to read the same JSON array from stdin\n"
113 0 : "instead of from a file" );
114 0 : fd_action_help_arg( help, "--name", "<name>", "Name of the validator instance to attach to, if more than one is\n"
115 0 : "running on this host" );
116 0 : }
117 :
118 : action_t fd_action_add_authorized_voter = {
119 : .name = "add-authorized-voter",
120 : .args = add_authorized_voter_cmd_args,
121 : .fn = add_authorized_voter_cmd_fn,
122 : .require_config = 0,
123 : .perm = NULL,
124 : .description = "Add an authorized voter to the validator",
125 : .detail = "Registers an additional authorized voter key with an already running\n"
126 : "validator so it can sign votes with that key, in addition to the identity\n"
127 : "key and any voters already configured. On success it prints `Authorized\n"
128 : "voter key added successfully` and exits 0. It fails (non-zero, with no change)\n"
129 : "if the key is already an authorized voter, or if the validator already has\n"
130 : "the maximum of 16 authorized voters.\n"
131 : "\n"
132 : "This command does not start a validator; it attaches to one that is already\n"
133 : "running. With no arguments it discovers the running validator automatically.\n"
134 : "If multiple validators are running, pass --name to select one. If --config is\n"
135 : "given, the validator is instead located from the configuration file; only the\n"
136 : "name and [hugetlbfs.mount_path] values are used, and they must match the\n"
137 : "running validator.\n"
138 : "\n"
139 : "The change is live only: it is not written back to the config file, so the\n"
140 : "voter is dropped on the validator's next restart. To keep it across restarts,\n"
141 : "also add the keypair path to [paths.authorized_voter_paths] in the config.",
142 : .usage = "add-authorized-voter <keypair> [--name <name>]",
143 : .args_help = add_authorized_voter_args_help,
144 : };
|