mod bootstrap;
mod start;
use std::num::NonZeroUsize;
use std::path::PathBuf;
use anyhow::Context;
use clap::Parser;
use miden_node_utils::clap::GrpcOptionsInternal;
use miden_node_utils::logging::OpenTelemetry;
use miden_protocol::crypto::dsa::ecdsa_k256_keccak::SigningKey;
use miden_protocol::utils::serde::Deserializable;
use miden_validator::ValidatorSigner;
const ENV_DATA_DIRECTORY: &str = "MIDEN_VALIDATOR_DATA_DIRECTORY";
const ENV_LISTEN: &str = "MIDEN_VALIDATOR_LISTEN";
const ENV_KEY: &str = "MIDEN_VALIDATOR_KEY";
const ENV_KMS_KEY_ID: &str = "MIDEN_VALIDATOR_KMS_KEY_ID";
const ENV_GENESIS_CONFIG_FILE: &str = "MIDEN_VALIDATOR_GENESIS_CONFIG_FILE";
const ENV_SQLITE_CONNECTION_POOL_SIZE: &str = "MIDEN_VALIDATOR_SQLITE_CONNECTION_POOL_SIZE";
pub(crate) const INSECURE_KEY_HEX: &str =
"0101010101010101010101010101010101010101010101010101010101010101";
#[derive(Parser)]
#[command(version, about, long_about = None)]
pub enum ValidatorCommand {
Bootstrap {
#[arg(long, value_name = "DIR")]
genesis_block_directory: PathBuf,
#[arg(long, value_name = "DIR")]
accounts_directory: PathBuf,
#[arg(long, env = ENV_DATA_DIRECTORY, value_name = "DIR")]
data_directory: PathBuf,
#[arg(
long = "sqlite.connection_pool_size",
env = ENV_SQLITE_CONNECTION_POOL_SIZE,
default_value_t = miden_node_db::default_connection_pool_size(),
value_name = "NUM"
)]
sqlite_connection_pool_size: NonZeroUsize,
#[arg(long, env = ENV_GENESIS_CONFIG_FILE, value_name = "GENESIS_CONFIG")]
genesis_config_file: Option<PathBuf>,
#[command(flatten)]
validator_key: ValidatorKey,
},
Migrate {
#[arg(long, env = ENV_DATA_DIRECTORY, value_name = "DIR")]
data_directory: PathBuf,
},
Start {
#[arg(long = "listen", env = ENV_LISTEN, value_name = "LISTEN")]
listen: std::net::SocketAddr,
#[command(flatten)]
grpc_options: GrpcOptionsInternal,
#[arg(
long = "sqlite.connection_pool_size",
env = ENV_SQLITE_CONNECTION_POOL_SIZE,
default_value_t = miden_node_db::default_connection_pool_size(),
value_name = "NUM"
)]
sqlite_connection_pool_size: NonZeroUsize,
#[arg(long, env = ENV_DATA_DIRECTORY, value_name = "DIR")]
data_directory: PathBuf,
#[arg(
long = "key.hex",
env = ENV_KEY,
value_name = "VALIDATOR_KEY",
default_value = INSECURE_KEY_HEX,
group = "key"
)]
validator_key: String,
#[arg(
long = "key.kms-id",
env = ENV_KMS_KEY_ID,
value_name = "VALIDATOR_KMS_KEY_ID",
group = "key"
)]
kms_key_id: Option<String>,
},
}
impl ValidatorCommand {
pub async fn handle(self) -> anyhow::Result<()> {
match self {
Self::Bootstrap {
genesis_block_directory,
accounts_directory,
data_directory,
sqlite_connection_pool_size,
genesis_config_file,
validator_key,
} => {
bootstrap::bootstrap(
&genesis_block_directory,
&accounts_directory,
&data_directory,
sqlite_connection_pool_size,
genesis_config_file.as_ref(),
validator_key,
)
.await
},
Self::Migrate { data_directory } => {
miden_validator::db::migrate(data_directory.join("validator.sqlite3"))
.context("failed to apply validator database migrations")?;
Ok(())
},
Self::Start {
listen,
grpc_options,
validator_key,
data_directory,
kms_key_id,
sqlite_connection_pool_size,
..
} => {
let address = listen;
if let Some(kms_key_id) = kms_key_id {
let signer = ValidatorSigner::new_kms(kms_key_id).await?;
start::start(
address,
grpc_options,
signer,
data_directory,
sqlite_connection_pool_size,
)
.await
} else {
let signer = SigningKey::read_from_bytes(hex::decode(validator_key)?.as_ref())?;
let signer = ValidatorSigner::new_local(signer);
start::start(
address,
grpc_options,
signer,
data_directory,
sqlite_connection_pool_size,
)
.await
}
},
}
}
pub fn open_telemetry(&self) -> OpenTelemetry {
match self {
Self::Start { .. } => OpenTelemetry::from_env().with_name("validator"),
Self::Bootstrap { .. } | Self::Migrate { .. } => OpenTelemetry::Disabled,
}
}
}
#[derive(clap::Args)]
#[group(required = false, multiple = false)]
pub struct ValidatorKey {
#[arg(
long = "key.hex",
env = ENV_KEY,
value_name = "VALIDATOR_KEY",
default_value = INSECURE_KEY_HEX,
)]
pub validator_key: String,
#[arg(
long = "key.kms-id",
env = ENV_KMS_KEY_ID,
value_name = "VALIDATOR_KMS_KEY_ID",
)]
pub validator_kms_key_id: Option<String>,
}
impl ValidatorKey {
pub async fn into_signer(self) -> anyhow::Result<ValidatorSigner> {
if let Some(kms_key_id) = self.validator_kms_key_id {
Ok(ValidatorSigner::new_kms(kms_key_id).await?)
} else {
let signer = SigningKey::read_from_bytes(hex::decode(self.validator_key)?.as_ref())?;
Ok(ValidatorSigner::new_local(signer))
}
}
}