tempo_xtask/
generate_devnet.rs1use std::{net::SocketAddr, path::PathBuf};
2
3use alloy_primitives::Address;
4use eyre::{Context, OptionExt as _, ensure};
5use rand_08::SeedableRng as _;
6use reth_network_peers::pk2id;
7use secp256k1::SECP256K1;
8use serde::Serialize;
9
10use crate::genesis_args::GenesisArgs;
11
12#[derive(Debug, clap::Parser)]
16pub(crate) struct GenerateDevnet {
17 #[arg(long, short, value_name = "DIR")]
22 output: PathBuf,
23
24 #[arg(long)]
26 force: bool,
27
28 #[arg(long)]
29 image_tag: String,
30
31 #[arg(long)]
33 genesis_url: String,
34
35 #[clap(flatten)]
36 genesis_args: GenesisArgs,
37}
38
39impl GenerateDevnet {
40 pub(crate) async fn run(self) -> eyre::Result<()> {
41 let Self {
42 output,
43 force,
44 image_tag,
45 genesis_url,
46 genesis_args,
47 } = self;
48
49 let seed = genesis_args.seed;
50 let (genesis, consensus_config) = genesis_args
51 .generate_genesis()
52 .await
53 .wrap_err("failed to generate genesis")?;
54
55 let consensus_config = consensus_config
56 .ok_or_eyre("no consensus config generated; did you provide --validators?")?;
57
58 std::fs::create_dir_all(&output).wrap_err_with(|| {
59 format!("failed creating target directory at `{}`", output.display())
60 })?;
61
62 if force {
63 eprintln!(
64 "--force was specified: deleting all files in target directory `{}`",
65 output.display()
66 );
67 std::fs::remove_dir_all(&output)
70 .and_then(|_| std::fs::create_dir(&output))
71 .wrap_err_with(|| {
72 format!("failed clearing target directory at `{}`", output.display())
73 })?;
74 } else {
75 let target_is_empty = std::fs::read_dir(&output)
76 .wrap_err_with(|| {
77 format!(
78 "failed reading target directory `{}` to determine if it is empty",
79 output.display()
80 )
81 })?
82 .next()
83 .is_none();
84 ensure!(
85 target_is_empty,
86 "target directory `{}` is not empty; delete all its contents or rerun command with --force",
87 output.display(),
88 );
89 }
90
91 let mut rng =
92 rand_08::rngs::StdRng::seed_from_u64(seed.unwrap_or_else(rand_08::random::<u64>));
93 let mut execution_peers = vec![];
94
95 let devmode = consensus_config.validators.len() == 1;
96
97 let mut all_configs = vec![];
98 for validator in consensus_config.validators {
99 let (execution_p2p_signing_key, execution_p2p_identity) = {
100 let (sk, pk) = SECP256K1.generate_keypair(&mut rng);
101 (sk, pk2id(&pk))
102 };
103
104 let consensus_p2p_port = validator.addr.port();
105 let execution_p2p_port = consensus_p2p_port + 1;
106 let consensus_metrics_port = consensus_p2p_port + 2;
107
108 execution_peers.push(format!(
109 "enode://{execution_p2p_identity:x}@{}",
110 SocketAddr::new(validator.addr.ip(), execution_p2p_port),
111 ));
112
113 all_configs.push((
114 validator.clone(),
115 ConfigOutput {
116 execution_genesis_url: genesis_url.clone(),
117
118 devmode,
119 node_image_tag: image_tag.clone(),
120
121 consensus_on_disk_signing_key: validator.signing_key.to_string(),
122 consensus_on_disk_signing_share: validator.signing_share.to_string(),
123
124 consensus_fee_recipient: Address::ZERO,
126
127 consensus_p2p_port,
128 consensus_metrics_port,
129 execution_p2p_port,
130
131 execution_p2p_disc_key: execution_p2p_signing_key.display_secret().to_string(),
132
133 execution_peers: vec![],
135 },
136 ));
137
138 println!("created a config for validator `{}`", validator.addr);
139 }
140
141 for (validator, mut config) in all_configs {
142 config.execution_peers = execution_peers.clone();
143 let config_json = serde_json::to_string_pretty(&config)
144 .wrap_err("failed to convert config to json")?;
145 let dst = output.join(format!("{}.json", validator.addr));
147 std::fs::write(&dst, config_json).wrap_err_with(|| {
148 format!("failed to write deployment config to `{}`", dst.display())
149 })?;
150 println!("wrote config to `{}`", dst.display());
151 }
152 eprintln!("config files written");
153
154 let genesis_ser = serde_json::to_string_pretty(&genesis)
155 .wrap_err("failed serializing genesis as json")?;
156 let dst = output.join("genesis.json");
157 std::fs::write(&dst, &genesis_ser)
158 .wrap_err_with(|| format!("failed writing genesis to `{}`", dst.display()))?;
159 println!("wrote genesis to `{}`", dst.display());
160 Ok(())
161 }
162}
163
164#[derive(Debug, Serialize)]
165pub(crate) struct ConfigOutput {
166 devmode: bool,
167 consensus_on_disk_signing_key: String,
168 consensus_on_disk_signing_share: String,
169 consensus_p2p_port: u16,
170 consensus_fee_recipient: Address,
171 consensus_metrics_port: u16,
172 node_image_tag: String,
173 execution_genesis_url: String,
174 execution_p2p_port: u16,
175 execution_peers: Vec<String>,
176 execution_p2p_disc_key: String,
177}