Skip to main content

agave_validator/commands/set_identity/
mod.rs

1use {
2    crate::{
3        admin_rpc_service,
4        commands::{FromClapArgMatches, Result},
5    },
6    clap::{App, Arg, ArgMatches, SubCommand, value_t},
7    solana_clap_utils::input_validators::is_keypair,
8    solana_keypair::read_keypair,
9    solana_signer::Signer,
10    std::{fs, path::Path},
11};
12
13const COMMAND: &str = "set-identity";
14
15#[derive(Debug, PartialEq)]
16pub struct SetIdentityArgs {
17    pub identity: Option<String>,
18    pub require_tower: bool,
19    pub require_vote_history: bool,
20}
21
22impl Default for SetIdentityArgs {
23    fn default() -> Self {
24        Self {
25            identity: None,
26            require_tower: false,
27            require_vote_history: true,
28        }
29    }
30}
31
32impl FromClapArgMatches for SetIdentityArgs {
33    fn from_clap_arg_match(matches: &ArgMatches) -> Result<Self> {
34        Ok(SetIdentityArgs {
35            identity: value_t!(matches, "identity", String).ok(),
36            require_tower: matches.is_present("require_tower"),
37            require_vote_history: !matches.is_present("do_not_require_vote_history"),
38        })
39    }
40}
41pub fn command<'a>() -> App<'a, 'a> {
42    SubCommand::with_name(COMMAND)
43        .about("Set the validator identity")
44        .arg(
45            Arg::with_name("identity")
46                .index(1)
47                .value_name("KEYPAIR")
48                .required(false)
49                .takes_value(true)
50                .validator(is_keypair)
51                .help("Path to validator identity keypair [default: read JSON keypair from stdin]"),
52        )
53        .arg(
54            clap::Arg::with_name("require_tower")
55                .long("require-tower")
56                .takes_value(false)
57                .help("Refuse to set the validator identity if saved tower state is not found"),
58        )
59        .arg(
60            clap::Arg::with_name("do_not_require_vote_history")
61                .long("do-not-require-vote-history")
62                .takes_value(false)
63                .help("Do not require saved vote history state for identity change"),
64        )
65        .after_help(
66            "Note: the new identity only applies to the currently running validator instance",
67        )
68}
69
70pub fn execute(matches: &ArgMatches, ledger_path: &Path) -> Result<()> {
71    let SetIdentityArgs {
72        identity,
73        require_tower,
74        require_vote_history,
75    } = SetIdentityArgs::from_clap_arg_match(matches)?;
76
77    if let Some(identity_keypair) = identity {
78        let identity_keypair = fs::canonicalize(&identity_keypair)?;
79
80        println!(
81            "New validator identity path: {}",
82            identity_keypair.display()
83        );
84
85        let admin_client = admin_rpc_service::connect(ledger_path);
86        admin_rpc_service::runtime().block_on(async move {
87            admin_client
88                .await?
89                .set_identity(
90                    identity_keypair.display().to_string(),
91                    require_tower,
92                    require_vote_history,
93                )
94                .await
95        })?;
96    } else {
97        let mut stdin = std::io::stdin();
98        let identity_keypair = read_keypair(&mut stdin)?;
99
100        println!("New validator identity: {}", identity_keypair.pubkey());
101
102        let admin_client = admin_rpc_service::connect(ledger_path);
103        admin_rpc_service::runtime().block_on(async move {
104            admin_client
105                .await?
106                .set_identity_from_bytes(
107                    Vec::from(identity_keypair.to_bytes()),
108                    require_tower,
109                    require_vote_history,
110                )
111                .await
112        })?;
113    }
114
115    Ok(())
116}
117
118#[cfg(test)]
119mod tests {
120    use {
121        super::*, crate::commands::tests::verify_args_struct_by_command, solana_keypair::Keypair,
122    };
123
124    #[test]
125    fn verify_args_struct_by_command_set_identity_default() {
126        verify_args_struct_by_command(command(), vec![COMMAND], SetIdentityArgs::default());
127    }
128
129    #[test]
130    fn verify_args_struct_by_command_set_identity_with_identity_file() {
131        // generate a keypair
132        let tmp_dir = tempfile::tempdir().unwrap();
133        let file = tmp_dir.path().join("id.json");
134        let keypair = Keypair::new();
135        solana_keypair::write_keypair_file(&keypair, &file).unwrap();
136
137        verify_args_struct_by_command(
138            command(),
139            vec![COMMAND, file.to_str().unwrap()],
140            SetIdentityArgs {
141                identity: Some(file.to_str().unwrap().to_string()),
142                ..SetIdentityArgs::default()
143            },
144        );
145    }
146
147    #[test]
148    fn verify_args_struct_by_command_set_identity_with_require_tower() {
149        verify_args_struct_by_command(
150            command(),
151            vec![COMMAND, "--require-tower"],
152            SetIdentityArgs {
153                require_tower: true,
154                ..SetIdentityArgs::default()
155            },
156        );
157    }
158
159    #[test]
160    fn verify_args_struct_by_command_set_identity_do_not_require_vote_history() {
161        verify_args_struct_by_command(
162            command(),
163            vec![COMMAND, "--do-not-require-vote-history"],
164            SetIdentityArgs {
165                require_vote_history: false,
166                ..SetIdentityArgs::default()
167            },
168        );
169    }
170}