1use std::{
2 error::Error,
3 path::PathBuf,
4 str::{self, FromStr},
5 sync::mpsc,
6 thread,
7};
8
9use base64::prelude::*;
10
11mod config;
12
13use clap::Parser;
14use libp2p_identity as identity;
15use libp2p_identity::PeerId;
16use zeroize::Zeroizing;
17
18#[derive(Debug, Parser)]
19#[command(name = "libp2p key material generator")]
20struct Args {
21 #[arg(long, global = true)]
23 json: bool,
24
25 #[command(subcommand)]
26 cmd: Command,
27}
28
29#[derive(Debug, Parser)]
30enum Command {
31 From {
33 #[arg(value_parser)]
35 config: PathBuf,
36 },
37 Rand {
39 #[arg(long)]
41 prefix: Option<String>,
42 },
43}
44
45const ALLOWED_FIRST_BYTE: &[u8] = b"NPQRSTUVWXYZ";
48
49const ALPHABET: &[u8] = b"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
51
52fn main() -> Result<(), Box<dyn Error>> {
53 let args = Args::parse();
54
55 let (local_peer_id, local_keypair) = match args.cmd {
56 Command::From { config } => {
58 let config = Zeroizing::new(config::Config::from_file(config.as_ref())?);
59
60 let keypair = identity::Keypair::from_protobuf_encoding(&Zeroizing::new(
61 BASE64_STANDARD.decode(config.identity.priv_key.as_bytes())?,
62 ))?;
63
64 let peer_id = keypair.public().into();
65 assert_eq!(
66 PeerId::from_str(&config.identity.peer_id)?,
67 peer_id,
68 "Expect peer id derived from private key and peer id retrieved from config to match."
69 );
70
71 (peer_id, keypair)
72 }
73
74 Command::Rand { prefix } => {
76 if let Some(prefix) = prefix {
77 if prefix.as_bytes().iter().any(|c| !ALPHABET.contains(c)) {
78 eprintln!("Prefix {prefix} is not valid base58");
79 std::process::exit(1);
80 }
81
82 if !prefix.is_empty() && !ALLOWED_FIRST_BYTE.contains(&prefix.as_bytes()[0]) {
84 eprintln!("Prefix {prefix} is not reachable");
85 eprintln!(
86 "Only the following bytes are possible as first byte: {}",
87 str::from_utf8(ALLOWED_FIRST_BYTE).unwrap()
88 );
89 std::process::exit(1);
90 }
91
92 let (tx, rx) = mpsc::channel::<(PeerId, identity::Keypair)>();
93
94 for _ in 0..thread::available_parallelism()?.get() {
96 let prefix = prefix.clone();
97 let tx = tx.clone();
98
99 thread::spawn(move || loop {
100 let keypair = identity::Keypair::generate_ed25519();
101 let peer_id = keypair.public().to_peer_id();
102 let base58 = peer_id.to_base58();
103 if base58[8..].starts_with(&prefix) {
104 tx.send((peer_id, keypair)).expect("to send");
105 }
106 });
107 }
108
109 rx.recv().expect("to recv")
110 } else {
111 let keypair = identity::Keypair::generate_ed25519();
112 (keypair.public().into(), keypair)
113 }
114 }
115 };
116
117 if args.json {
118 let config = config::Config::from_key_material(local_peer_id, &local_keypair)?;
119 println!("{}", serde_json::to_string(&config)?);
120 } else {
121 println!(
122 "PeerId: {:?} Keypair: {:?}",
123 local_peer_id,
124 local_keypair.to_protobuf_encoding()
125 );
126 }
127
128 Ok(())
129}