tempo_xtask/
generate_devnet.rs1use std::{net::SocketAddr, path::PathBuf};
2
3use alloy_primitives::Address;
4use eyre::{Context, OptionExt as _, ensure};
5use rand::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 = rand::rngs::StdRng::seed_from_u64(seed.unwrap_or_else(rand::random::<u64>));
92 let mut execution_peers = vec![];
93
94 let devmode = consensus_config.validators.len() == 1;
95
96 let mut all_configs = vec![];
97 for validator in consensus_config.validators {
98 let (execution_p2p_signing_key, execution_p2p_identity) = {
99 let (sk, pk) = SECP256K1.generate_keypair(&mut rng);
100 (sk, pk2id(&pk))
101 };
102
103 let consensus_p2p_port = validator.addr.port();
104 let execution_p2p_port = consensus_p2p_port + 1;
105 let consensus_metrics_port = consensus_p2p_port + 2;
106
107 execution_peers.push(format!(
108 "enode://{execution_p2p_identity:x}@{}",
109 SocketAddr::new(validator.addr.ip(), execution_p2p_port),
110 ));
111
112 all_configs.push((
113 validator.clone(),
114 ConfigOutput {
115 execution_genesis_url: genesis_url.clone(),
116
117 devmode,
118 node_image_tag: image_tag.clone(),
119
120 consensus_on_disk_signing_key: validator.signing_key.to_string(),
121 consensus_on_disk_signing_share: validator.signing_share.to_string(),
122
123 consensus_fee_recipient: Address::ZERO,
125
126 consensus_p2p_port,
127 consensus_metrics_port,
128 execution_p2p_port,
129
130 execution_p2p_disc_key: execution_p2p_signing_key.display_secret().to_string(),
131
132 execution_peers: vec![],
134 },
135 ));
136
137 println!("created a config for validator `{}`", validator.addr);
138 }
139
140 for (validator, mut config) in all_configs {
141 config.execution_peers = execution_peers.clone();
142 let config_json = serde_json::to_string_pretty(&config)
143 .wrap_err("failed to convert config to json")?;
144 let dst = output.join(format!("{}.json", validator.addr));
146 std::fs::write(&dst, config_json).wrap_err_with(|| {
147 format!("failed to write deployment config to `{}`", dst.display())
148 })?;
149 println!("wrote config to `{}`", dst.display());
150 }
151 eprintln!("config files written");
152
153 let genesis_ser = serde_json::to_string_pretty(&genesis)
154 .wrap_err("failed serializing genesis as json")?;
155 let dst = output.join("genesis.json");
156 std::fs::write(&dst, &genesis_ser)
157 .wrap_err_with(|| format!("failed writing genesis to `{}`", dst.display()))?;
158 println!("wrote genesis to `{}`", dst.display());
159 Ok(())
160 }
161}
162
163#[derive(Debug, Serialize)]
164pub(crate) struct ConfigOutput {
165 devmode: bool,
166 consensus_on_disk_signing_key: String,
167 consensus_on_disk_signing_share: String,
168 consensus_p2p_port: u16,
169 consensus_fee_recipient: Address,
170 consensus_metrics_port: u16,
171 node_image_tag: String,
172 execution_genesis_url: String,
173 execution_p2p_port: u16,
174 execution_peers: Vec<String>,
175 execution_p2p_disc_key: String,
176}