Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions bin/tempo/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ serde_json.workspace = true
tempo-alloy.workspace = true
tempo-contracts.workspace = true
tempo-dkg-onchain-artifacts.workspace = true
tempo-precompiles.workspace = true
reth-cli-commands.workspace = true
reth-cli-runner.workspace = true
reth-cli-util.workspace = true
Expand Down
144 changes: 137 additions & 7 deletions bin/tempo/src/tempo_cmd.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
use std::{fs::OpenOptions, path::PathBuf, sync::Arc};
use std::{
fs::OpenOptions,
net::{IpAddr, SocketAddr},
path::PathBuf,
sync::Arc,
};

use alloy_primitives::Address;
use alloy_primitives::{Address, Keccak256};
use alloy_provider::Provider;

use alloy_rpc_types_eth::TransactionRequest;
use alloy_sol_types::SolCall;
use clap::Subcommand;
use commonware_codec::{DecodeExt as _, ReadExt as _};
use commonware_codec::{DecodeExt as _, Encode as _, ReadExt as _};
use commonware_consensus::types::{Epocher as _, FixedEpocher, Height};
use commonware_cryptography::{
Signer as _,
Expand All @@ -21,8 +26,11 @@ use serde::Serialize;
use tempo_alloy::TempoNetwork;
use tempo_chainspec::spec::TempoChainSpec;
use tempo_commonware_node_config::SigningKey;
use tempo_contracts::precompiles::{IValidatorConfig, VALIDATOR_CONFIG_ADDRESS};
use tempo_contracts::precompiles::{
IValidatorConfig, VALIDATOR_CONFIG_ADDRESS, VALIDATOR_CONFIG_V2_ADDRESS,
};
use tempo_dkg_onchain_artifacts::OnchainDkgOutcome;
use tempo_precompiles::validator_config_v2::{VALIDATOR_NS_ADD, VALIDATOR_NS_ROTATE};

/// Tempo-specific subcommands that extend the reth CLI.
#[derive(Debug, Subcommand)]
Expand All @@ -42,24 +50,146 @@ impl ExtendedCommand for TempoSubcommand {

#[derive(Debug, Subcommand)]
pub(crate) enum ConsensusSubcommand {
/// Generates an ed25519 signing key pair to be used in consensus.
GeneratePrivateKey(GeneratePrivateKey),
/// Calculates the public key from an ed25519 signing key.
CalculatePublicKey(CalculatePublicKey),
/// Creating signatures to pass to ValidatorConfigV2.addValidator and
/// ValidatorConfigV2.rotateValidator contract calls.
#[command(subcommand)]
CreateCallSignature(CreateCallSignature),
/// Generates an ed25519 signing key pair to be used in consensus.
GeneratePrivateKey(GeneratePrivateKey),
/// Query validator info from the previous epoch's DKG outcome and current contract state.
ValidatorsInfo(ValidatorsInfo),
}

impl ConsensusSubcommand {
fn run(self) -> eyre::Result<()> {
match self {
Self::GeneratePrivateKey(args) => args.run(),
Self::CalculatePublicKey(args) => args.run(),
Self::CreateCallSignature(args) => args.run(),
Self::GeneratePrivateKey(args) => args.run(),
Self::ValidatorsInfo(args) => args.run(),
}
}
}

#[derive(Debug, Subcommand)]
pub(crate) enum CreateCallSignature {
/// Returns the signature argument to pass to the addValidator call.
AddValidator(AddValidator),
/// Returns the signature argument to pass to the rotateValidator call.
RotateValidator(RotateValidator),
}

impl CreateCallSignature {
fn run(self) -> eyre::Result<()> {
match self {
Self::AddValidator(args) => args.run(),
Self::RotateValidator(args) => args.run(),
}
}
}

#[derive(Debug, clap::Args)]
pub(crate) struct AddValidator {
/// The path to the file holding the ed25519 signing key holding used for
/// creating the signfature.
#[arg(long, value_name = "FILE")]
private_key: PathBuf,
/// The chain ID of the chain that the add validator call will be sent to.
#[arg(long)]
chain_id: u64,
/// The owner of the validator entry in the add validator call.
#[arg(long, value_name = "ETHEREUM ADDRESS")]
address: Address,
/// The socket address that will be used for ingress in the add validator call.
#[arg(long, value_name = "IP:PORT>")]
ingress: SocketAddr,
/// The ip address that will be used for egress in the add validator call.
#[arg(long, value_name = "IP")]
egress: IpAddr,
}

impl AddValidator {
fn run(self) -> eyre::Result<()> {
let Self {
private_key,
chain_id,
address,
ingress,
egress,
} = self;
let private_key = SigningKey::read_from_file(&private_key).wrap_err_with(|| {
format!(
"failed reading private key from `{}`",
private_key.display()
)
})?;
let mut hasher = Keccak256::new();
hasher.update(chain_id.to_be_bytes());
hasher.update(VALIDATOR_CONFIG_V2_ADDRESS.as_slice());
hasher.update(address.as_slice());
hasher.update(ingress.to_string().as_bytes());
hasher.update(egress.to_string().as_bytes());
let msg = hasher.finalize();
let signature = private_key
.into_inner()
.sign(VALIDATOR_NS_ADD, msg.as_slice());
println!("{:x}", signature.encode());
Ok(())
}
}

#[derive(Debug, clap::Args)]
pub(crate) struct RotateValidator {
/// The path to the file holding the ed25519 signing key holding used for
/// creating the signfature.
#[arg(long, value_name = "FILE")]
private_key: PathBuf,
/// The chain ID of the chain that the rotate validator call will be sent to.
#[arg(long)]
chain_id: u64,
/// The owner of the validator entry in the rotate validator call.
#[arg(long, value_name = "ETHEREUM ADDRESS")]
address: Address,
/// The socket address that will be used for ingress in the rotate validator call.
#[arg(long, value_name = "IP:PORT>")]
ingress: SocketAddr,
/// The ip address that will be used for egress in the rotate validator call.
#[arg(long, value_name = "IP")]
egress: IpAddr,
}

impl RotateValidator {
fn run(self) -> eyre::Result<()> {
let Self {
private_key,
chain_id,
address,
ingress,
egress,
} = self;
let private_key = SigningKey::read_from_file(&private_key).wrap_err_with(|| {
format!(
"failed reading private key from `{}`",
private_key.display()
)
})?;
let mut hasher = Keccak256::new();
hasher.update(chain_id.to_be_bytes());
hasher.update(VALIDATOR_CONFIG_V2_ADDRESS.as_slice());
hasher.update(address.as_slice());
hasher.update(ingress.to_string().as_bytes());
hasher.update(egress.to_string().as_bytes());
let msg = hasher.finalize();
let signature = private_key
.into_inner()
.sign(VALIDATOR_NS_ROTATE, msg.as_slice());
println!("{:x}", signature.encode());
Ok(())
}
}

#[derive(Debug, clap::Args)]
pub(crate) struct GeneratePrivateKey {
/// Destination of the generated signing key.
Expand Down
Loading