mod block_loop; use std::sync::Arc; use anyhow::Result; use clap::Parser; use tokio::sync::Mutex; use tracing_subscriber::EnvFilter; use nu_mempool::Mempool; use nu_rpc::server::RpcServer; use nu_state::StateDb; #[derive(Parser)] #[command(name = "nu-node", version)] struct Cli { /// Single-validator dev mode — no consensus, produces blocks every slot #[arg(long)] dev: bool, /// Act as block-producing validator #[arg(long)] validator: bool, /// Validator address (required when --validator is set) #[arg(long, default_value = "0xDEV0000000000000000000000000000000000000")] validator_addr: String, /// JSON-RPC HTTP bind address #[arg(long, default_value = "0.0.0.0:9545")] rpc_addr: String, /// RocksDB data directory #[arg(long, default_value = "./data/state")] db_path: String, /// Chain identifier #[arg(long, default_value = "nu-devnet-1")] chain_id: String, } #[tokio::main] async fn main() -> Result<()> { tracing_subscriber::fmt() .with_env_filter(EnvFilter::from_default_env()) .init(); let cli = Cli::parse(); let db = Arc::new(Mutex::new(StateDb::open(&cli.db_path)?)); tracing::info!("State DB opened at {}", cli.db_path); let mempool = Arc::new(Mutex::new(Mempool::new())); // Spawn block production loop in dev mode if cli.dev && cli.validator { let cfg = block_loop::BlockLoopConfig { validator_addr: cli.validator_addr.clone(), chain_id: cli.chain_id.clone(), }; tokio::spawn(block_loop::run(cfg, Arc::clone(&db), Arc::clone(&mempool))); } let rpc = RpcServer::new( cli.rpc_addr.clone(), Arc::clone(&db), Arc::clone(&mempool), cli.chain_id.clone(), ); tracing::info!( chain_id = %cli.chain_id, rpc_addr = %cli.rpc_addr, dev = cli.dev, validator = cli.validator, "nu-node ready" ); rpc.run().await?; Ok(()) }