1use std::{net::SocketAddr, path::PathBuf};
3
4use crate::{
5 generate_devnet::GenerateDevnet, generate_genesis::GenerateGenesis,
6 generate_localnet::GenerateLocalnet,
7};
8
9use alloy::signers::{local::MnemonicBuilder, utils::secret_key_to_address};
10use clap::Parser as _;
11use commonware_codec::DecodeExt;
12use commonware_cryptography::{PrivateKeyExt as _, Signer, ed25519::PrivateKey};
13use eyre::Context;
14use rand::SeedableRng as _;
15use tempo_commonware_node_config::SigningKey;
16
17mod generate_devnet;
18mod generate_genesis;
19mod generate_localnet;
20mod genesis_args;
21
22#[tokio::main]
23async fn main() -> eyre::Result<()> {
24 let args = Args::parse();
25 match args.action {
26 Action::GenerateGenesis(args) => args.run().await.wrap_err("failed generating genesis"),
27 Action::GenerateDevnet(args) => args
28 .run()
29 .await
30 .wrap_err("failed to generate devnet configs"),
31 Action::GenerateLocalnet(args) => args
32 .run()
33 .await
34 .wrap_err("failed to generate localnet configs"),
35 Action::GenerateAddPeer(cfg) => generate_config_to_add_peer(cfg),
36 Action::GenerateSigningKey(args) => args.run(),
37 }
38}
39
40#[derive(Debug, clap::Parser)]
41#[command(author)]
42#[command(version)]
43#[command(about)]
44#[command(long_about = None)]
45struct Args {
46 #[command(subcommand)]
47 action: Action,
48}
49
50#[derive(Debug, clap::Subcommand)]
51#[expect(
52 clippy::enum_variant_names,
53 reason = "the variant names map to actual cli inputs and are desired"
54)]
55enum Action {
56 GenerateGenesis(GenerateGenesis),
57 GenerateDevnet(GenerateDevnet),
58 GenerateLocalnet(GenerateLocalnet),
59 GenerateAddPeer(GenerateAddPeer),
60 GenerateSigningKey(GenerateSigningKey),
61}
62
63#[derive(Debug, clap::Args)]
65struct GenerateSigningKey {
66 #[arg(long, short, value_name = "FILE")]
68 output: PathBuf,
69 #[arg(long, value_name = "NUMBER")]
73 seed: Option<u64>,
74}
75
76impl GenerateSigningKey {
77 fn run(self) -> eyre::Result<()> {
78 let Self { output, seed } = self;
79 let mut rng = rand::rngs::StdRng::seed_from_u64(seed.unwrap_or_else(rand::random::<u64>));
80 let signing_key = PrivateKey::from_rng(&mut rng);
81 let validating_key = signing_key.public_key();
82 let signing_key = SigningKey::from(signing_key);
83 signing_key
84 .write_to_file(&output)
85 .wrap_err_with(|| format!("failed writing signing key to `{}`", output.display()))?;
86 println!(
87 "wrote signing key to: {}\nvalidating/public key: {validating_key}",
88 output.display()
89 );
90 Ok(())
91 }
92}
93
94#[derive(Debug, clap::Args)]
95struct GenerateAddPeer {
96 #[arg(long)]
97 public_key: String,
98
99 #[arg(long)]
100 inbound_address: SocketAddr,
101
102 #[arg(long)]
103 rpc_endpoint: String,
104
105 #[arg(long, default_value_t = 0)]
106 admin_index: u32,
107
108 #[arg(long, default_value_t = 20)]
109 validator_index: u32,
110
111 #[arg(
112 short,
113 long,
114 default_value = "test test test test test test test test test test test junk"
115 )]
116 pub mnemonic: String,
117}
118
119fn generate_config_to_add_peer(
120 GenerateAddPeer {
121 public_key,
122 inbound_address,
123 admin_index,
124 validator_index,
125 rpc_endpoint,
126 mnemonic,
127 }: GenerateAddPeer,
128) -> eyre::Result<()> {
129 use tempo_precompiles::VALIDATOR_CONFIG_ADDRESS;
130 let public_key_bytes = const_hex::decode(&public_key)?;
131 let public_key = commonware_cryptography::ed25519::PublicKey::decode(&public_key_bytes[..])?;
132
133 let admin_key = const_hex::encode(
134 MnemonicBuilder::from_phrase_nth(&mnemonic, admin_index)
135 .credential()
136 .to_bytes(),
137 );
138
139 let validator_address = {
140 secret_key_to_address(
141 MnemonicBuilder::from_phrase_nth(mnemonic, validator_index).credential(),
142 )
143 };
144 let inbound = inbound_address.to_string();
145 let outbound = inbound_address.to_string();
146 println!(
147 "\
148 cast send {VALIDATOR_CONFIG_ADDRESS} \
149 \\\n\"addValidator(address newValidatorAddress, bytes32 publicKey, bool active, string calldata inboundAddress, string calldata outboundAddress)\" \
150 \\\n\"{validator_address}\" \
151 \\\n\"{public_key}\" \
152 \\\n\"true\" \
153 \\\n\"{inbound}\" \
154 \\\n\"{outbound}\" \
155 \\\n--private-key {admin_key} \
156 \\\n-r {rpc_endpoint}"
157 );
158 Ok(())
159}