sc_cli/commands/
generate_node_key.rs1use crate::{build_network_key_dir_or_default, Error, NODE_KEY_ED25519_FILE};
21use clap::{Args, Parser};
22use libp2p_identity::{ed25519, Keypair};
23use sc_service::BasePath;
24use std::{
25 fs,
26 io::{self, Write},
27 path::PathBuf,
28};
29
30#[derive(Debug, Args, Clone)]
32pub struct GenerateKeyCmdCommon {
33 #[arg(long)]
36 file: Option<PathBuf>,
37
38 #[arg(long)]
41 bin: bool,
42}
43
44#[derive(Debug, Clone, Parser)]
46#[command(
47 name = "generate-node-key",
48 about = "Generate a random node key, write it to a file or stdout \
49 and write the corresponding peer-id to stderr"
50)]
51pub struct GenerateNodeKeyCmd {
52 #[clap(flatten)]
53 pub common: GenerateKeyCmdCommon,
54 #[arg(long, value_name = "CHAIN_SPEC")]
58 pub chain: Option<String>,
59 #[arg(long, conflicts_with_all = ["file", "default_base_path"])]
62 base_path: Option<PathBuf>,
63
64 #[arg(long, conflicts_with_all = ["base_path", "file"])]
67 default_base_path: bool,
68}
69
70impl GenerateKeyCmdCommon {
71 pub fn run(&self) -> Result<(), Error> {
73 generate_key(&self.file, self.bin, None, &None, false, None)
74 }
75}
76
77impl GenerateNodeKeyCmd {
78 pub fn run(&self, chain_spec_id: &str, executable_name: &String) -> Result<(), Error> {
80 generate_key(
81 &self.common.file,
82 self.common.bin,
83 Some(chain_spec_id),
84 &self.base_path,
85 self.default_base_path,
86 Some(executable_name),
87 )
88 }
89}
90
91fn generate_key(
96 file: &Option<PathBuf>,
97 bin: bool,
98 chain_spec_id: Option<&str>,
99 base_path: &Option<PathBuf>,
100 default_base_path: bool,
101 executable_name: Option<&String>,
102) -> Result<(), Error> {
103 let keypair = ed25519::Keypair::generate();
104
105 let secret = keypair.secret();
106
107 let file_data = if bin {
108 secret.as_ref().to_owned()
109 } else {
110 array_bytes::bytes2hex("", secret).into_bytes()
111 };
112
113 match (file, base_path, default_base_path) {
114 (Some(file), None, false) => fs::write(file, file_data)?,
115 (None, Some(_), false) | (None, None, true) => {
116 let network_path = build_network_key_dir_or_default(
117 base_path.clone().map(BasePath::new),
118 chain_spec_id.unwrap_or_default(),
119 executable_name.ok_or(Error::Input("Executable name not provided".into()))?,
120 );
121
122 fs::create_dir_all(network_path.as_path())?;
123
124 let key_path = network_path.join(NODE_KEY_ED25519_FILE);
125 if key_path.exists() {
126 eprintln!("Skip generation, a key already exists in {:?}", key_path);
127 return Err(Error::KeyAlreadyExistsInPath(key_path));
128 } else {
129 eprintln!("Generating key in {:?}", key_path);
130 fs::write(key_path, file_data)?
131 }
132 },
133 (None, None, false) => io::stdout().lock().write_all(&file_data)?,
134 (_, _, _) => {
135 return Err(Error::Input("Mutually exclusive arguments provided".into()));
137 },
138 }
139
140 eprintln!("{}", Keypair::from(keypair).public().to_peer_id());
141
142 Ok(())
143}
144
145#[cfg(test)]
146pub mod tests {
147 use crate::DEFAULT_NETWORK_CONFIG_PATH;
148
149 use super::*;
150 use std::io::Read;
151 use tempfile::Builder;
152
153 #[test]
154 fn generate_node_key() {
155 let mut file = Builder::new().prefix("keyfile").tempfile().unwrap();
156 let file_path = file.path().display().to_string();
157 let generate = GenerateNodeKeyCmd::parse_from(&["generate-node-key", "--file", &file_path]);
158 assert!(generate.run("test", &String::from("test")).is_ok());
159 let mut buf = String::new();
160 assert!(file.read_to_string(&mut buf).is_ok());
161 assert!(array_bytes::hex2bytes(&buf).is_ok());
162 }
163
164 #[test]
165 fn generate_node_key_base_path() {
166 let base_dir = Builder::new().prefix("keyfile").tempdir().unwrap();
167 let key_path = base_dir
168 .path()
169 .join("chains/test_id/")
170 .join(DEFAULT_NETWORK_CONFIG_PATH)
171 .join(NODE_KEY_ED25519_FILE);
172 let base_path = base_dir.path().display().to_string();
173 let generate =
174 GenerateNodeKeyCmd::parse_from(&["generate-node-key", "--base-path", &base_path]);
175 assert!(generate.run("test_id", &String::from("test")).is_ok());
176 let buf = fs::read_to_string(key_path.as_path()).unwrap();
177 assert!(array_bytes::hex2bytes(&buf).is_ok());
178
179 assert!(generate.run("test_id", &String::from("test")).is_err());
180 let new_buf = fs::read_to_string(key_path).unwrap();
181 assert_eq!(
182 array_bytes::hex2bytes(&new_buf).unwrap(),
183 array_bytes::hex2bytes(&buf).unwrap()
184 );
185 }
186}