use std::net::SocketAddr;
use std::num::NonZeroUsize;
use std::path::{Path, PathBuf};
use std::time::Duration;
use anyhow::Context;
use miden_node_block_producer::{
DEFAULT_BATCH_INTERVAL,
DEFAULT_BLOCK_INTERVAL,
DEFAULT_MAX_BATCHES_PER_BLOCK,
DEFAULT_MAX_TXS_PER_BATCH,
};
use miden_node_utils::clap::duration_to_human_readable_string;
use miden_node_validator::ValidatorSigner;
use miden_protocol::crypto::dsa::ecdsa_k256_keccak::SecretKey;
use miden_protocol::utils::serde::Deserializable;
use tokio::net::TcpListener;
use url::Url;
pub mod block_producer;
pub mod bundled;
pub mod rpc;
pub mod store;
pub mod validator;
const INSECURE_VALIDATOR_KEY_HEX: &str =
"0101010101010101010101010101010101010101010101010101010101010101";
const ENV_BLOCK_PRODUCER_URL: &str = "MIDEN_NODE_BLOCK_PRODUCER_URL";
const ENV_VALIDATOR_URL: &str = "MIDEN_NODE_VALIDATOR_URL";
const ENV_BATCH_PROVER_URL: &str = "MIDEN_NODE_BATCH_PROVER_URL";
const ENV_BLOCK_PROVER_URL: &str = "MIDEN_NODE_BLOCK_PROVER_URL";
const ENV_NTX_PROVER_URL: &str = "MIDEN_NODE_NTX_PROVER_URL";
const ENV_RPC_URL: &str = "MIDEN_NODE_RPC_URL";
const ENV_STORE_RPC_URL: &str = "MIDEN_NODE_STORE_RPC_URL";
const ENV_STORE_NTX_BUILDER_URL: &str = "MIDEN_NODE_STORE_NTX_BUILDER_URL";
const ENV_STORE_BLOCK_PRODUCER_URL: &str = "MIDEN_NODE_STORE_BLOCK_PRODUCER_URL";
const ENV_VALIDATOR_BLOCK_PRODUCER_URL: &str = "MIDEN_NODE_VALIDATOR_BLOCK_PRODUCER_URL";
const ENV_DATA_DIRECTORY: &str = "MIDEN_NODE_DATA_DIRECTORY";
const ENV_ENABLE_OTEL: &str = "MIDEN_NODE_ENABLE_OTEL";
const ENV_GENESIS_CONFIG_FILE: &str = "MIDEN_GENESIS_CONFIG_FILE";
const ENV_MAX_TXS_PER_BATCH: &str = "MIDEN_MAX_TXS_PER_BATCH";
const ENV_MAX_BATCHES_PER_BLOCK: &str = "MIDEN_MAX_BATCHES_PER_BLOCK";
const ENV_MEMPOOL_TX_CAPACITY: &str = "MIDEN_NODE_MEMPOOL_TX_CAPACITY";
const ENV_NTX_SCRIPT_CACHE_SIZE: &str = "MIDEN_NTX_DATA_STORE_SCRIPT_CACHE_SIZE";
const ENV_VALIDATOR_KEY: &str = "MIDEN_NODE_VALIDATOR_KEY";
const ENV_VALIDATOR_KMS_KEY_ID: &str = "MIDEN_NODE_VALIDATOR_KMS_KEY_ID";
const ENV_NTX_DATA_DIRECTORY: &str = "MIDEN_NODE_NTX_DATA_DIRECTORY";
const ENV_NTX_BUILDER_URL: &str = "MIDEN_NODE_NTX_BUILDER_URL";
const ENV_NTX_MAX_CYCLES: &str = "MIDEN_NTX_MAX_CYCLES";
const DEFAULT_NTX_TICKER_INTERVAL: Duration = Duration::from_millis(200);
const DEFAULT_NTX_IDLE_TIMEOUT: Duration = Duration::from_secs(5 * 60);
const DEFAULT_NTX_SCRIPT_CACHE_SIZE: NonZeroUsize = NonZeroUsize::new(1000).unwrap();
const DEFAULT_NTX_MAX_CYCLES: u32 = 1 << 18;
#[derive(clap::Args)]
#[group(required = false, multiple = false)]
pub struct ValidatorKey {
#[arg(
long = "validator.key.hex",
env = ENV_VALIDATOR_KEY,
value_name = "VALIDATOR_KEY",
default_value = INSECURE_VALIDATOR_KEY_HEX,
)]
validator_key: String,
#[arg(
long = "validator.key.kms-id",
env = ENV_VALIDATOR_KMS_KEY_ID,
value_name = "VALIDATOR_KMS_KEY_ID",
)]
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 {
let signer = ValidatorSigner::new_kms(kms_key_id).await?;
Ok(signer)
} else {
let signer = SecretKey::read_from_bytes(hex::decode(self.validator_key)?.as_ref())?;
let signer = ValidatorSigner::new_local(signer);
Ok(signer)
}
}
}
#[derive(clap::Args)]
pub struct BundledValidatorConfig {
#[arg(
long = "validator.key",
env = ENV_VALIDATOR_KEY,
value_name = "VALIDATOR_KEY",
default_value = INSECURE_VALIDATOR_KEY_HEX
)]
validator_key: String,
#[arg(long = "validator.url", env = ENV_VALIDATOR_URL, value_name = "URL")]
validator_url: Option<Url>,
}
impl BundledValidatorConfig {
async fn to_addresses(&self) -> anyhow::Result<(Url, Option<SocketAddr>)> {
if let Some(url) = &self.validator_url {
Ok((url.clone(), None))
} else {
let socket_addr = TcpListener::bind("127.0.0.1:0")
.await
.context("Failed to bind to validator gRPC endpoint")?
.local_addr()
.context("Failed to retrieve the validator's gRPC address")?;
let url = Url::parse(&format!("http://{socket_addr}"))
.context("Failed to parse Validator URL")?;
Ok((url, Some(socket_addr)))
}
}
}
#[derive(clap::Args)]
pub struct NtxBuilderConfig {
#[arg(long = "no-ntx-builder", default_value_t = false)]
pub disabled: bool,
#[arg(long = "tx-prover.url", env = ENV_NTX_PROVER_URL, value_name = "URL")]
pub tx_prover_url: Option<Url>,
#[arg(
long = "ntx-builder.interval",
default_value = &duration_to_human_readable_string(DEFAULT_NTX_TICKER_INTERVAL),
value_parser = humantime::parse_duration,
value_name = "DURATION"
)]
pub ticker_interval: Duration,
#[arg(
long = "ntx-builder.script-cache-size",
env = ENV_NTX_SCRIPT_CACHE_SIZE,
value_name = "NUM",
default_value_t = DEFAULT_NTX_SCRIPT_CACHE_SIZE
)]
pub script_cache_size: NonZeroUsize,
#[arg(
long = "ntx-builder.idle-timeout",
default_value = &duration_to_human_readable_string(DEFAULT_NTX_IDLE_TIMEOUT),
value_parser = humantime::parse_duration,
value_name = "DURATION"
)]
pub idle_timeout: Duration,
#[arg(
long = "ntx-builder.max-account-crashes",
default_value_t = 10,
value_name = "NUM"
)]
pub max_account_crashes: usize,
#[arg(
long = "ntx-builder.max-cycles",
env = ENV_NTX_MAX_CYCLES,
default_value_t = DEFAULT_NTX_MAX_CYCLES,
value_name = "NUM",
)]
pub max_tx_cycles: u32,
#[arg(long = "ntx-builder.data-directory", env = ENV_NTX_DATA_DIRECTORY, value_name = "DIR")]
pub ntx_data_directory: Option<PathBuf>,
}
impl NtxBuilderConfig {
pub fn into_builder_config(
self,
store_url: Url,
block_producer_url: Url,
validator_url: Url,
node_data_directory: &Path,
) -> miden_node_ntx_builder::NtxBuilderConfig {
let data_dir = self.ntx_data_directory.unwrap_or_else(|| node_data_directory.to_path_buf());
let database_filepath = data_dir.join("ntx-builder.sqlite3");
miden_node_ntx_builder::NtxBuilderConfig::new(
store_url,
block_producer_url,
validator_url,
database_filepath,
)
.with_tx_prover_url(self.tx_prover_url)
.with_script_cache_size(self.script_cache_size)
.with_idle_timeout(self.idle_timeout)
.with_max_account_crashes(self.max_account_crashes)
.with_max_cycles(self.max_tx_cycles)
}
}
#[derive(clap::Args)]
pub struct BlockProducerConfig {
#[arg(
long = "block.interval",
default_value = &duration_to_human_readable_string(DEFAULT_BLOCK_INTERVAL),
value_parser = humantime::parse_duration,
value_name = "DURATION"
)]
pub block_interval: Duration,
#[arg(
long = "batch.interval",
default_value = &duration_to_human_readable_string(DEFAULT_BATCH_INTERVAL),
value_parser = humantime::parse_duration,
value_name = "DURATION"
)]
pub batch_interval: Duration,
#[arg(long = "batch-prover.url", env = ENV_BATCH_PROVER_URL, value_name = "URL")]
pub batch_prover_url: Option<Url>,
#[arg(
long = "max-txs-per-batch",
env = ENV_MAX_TXS_PER_BATCH,
value_name = "NUM",
default_value_t = DEFAULT_MAX_TXS_PER_BATCH
)]
pub max_txs_per_batch: usize,
#[arg(
long = "max-batches-per-block",
env = ENV_MAX_BATCHES_PER_BLOCK,
value_name = "NUM",
default_value_t = DEFAULT_MAX_BATCHES_PER_BLOCK
)]
pub max_batches_per_block: usize,
#[arg(
long = "mempool.tx-capacity",
default_value_t = miden_node_block_producer::DEFAULT_MEMPOOL_TX_CAPACITY,
env = ENV_MEMPOOL_TX_CAPACITY,
value_name = "NUM"
)]
mempool_tx_capacity: NonZeroUsize,
}