tempo_xtask/
main.rs

1//! xtask is a Swiss army knife of tools that help with running and testing tempo.
2use 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/// Generates an ed25519 signing key pair to be used in consensus.
64#[derive(Debug, clap::Args)]
65struct GenerateSigningKey {
66    /// Destination of the generated signing key.
67    #[arg(long, short, value_name = "FILE")]
68    output: PathBuf,
69    /// AVOID IN PRODUCTION.
70    /// Optional seed for the random generator used when generating the key.
71    /// Use this only in environments that require reproducible keys.
72    #[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}