From 49bf30cf3e7cf3bdbb3a48ccc7334d8b8f046fbc Mon Sep 17 00:00:00 2001 From: ryardley Date: Sat, 14 Mar 2026 04:16:40 +0000 Subject: [PATCH 01/31] redirect println to console --- Cargo.lock | 8 ++ Cargo.toml | 3 +- crates/Dockerfile | 1 + crates/cli/Cargo.toml | 1 + crates/cli/src/ciphernode/license.rs | 21 +++-- crates/cli/src/ciphernode/lifecycle.rs | 57 ++++++++----- crates/cli/src/ciphernode/mod.rs | 17 ++-- crates/cli/src/ciphernode/setup.rs | 35 +++++--- crates/cli/src/ciphernode/tickets.rs | 15 ++-- crates/cli/src/cli.rs | 27 ++++--- crates/cli/src/main.rs | 5 +- crates/cli/src/net.rs | 5 +- crates/cli/src/net_get_peer_id.rs | 5 +- crates/cli/src/noir.rs | 107 ++++++++++++++----------- crates/cli/src/password.rs | 7 +- crates/cli/src/password_delete.rs | 13 +-- crates/cli/src/password_set.rs | 7 +- crates/cli/src/print_env.rs | 7 +- crates/cli/src/rev.rs | 6 +- crates/cli/src/wallet.rs | 9 ++- crates/cli/src/wallet_get.rs | 5 +- crates/cli/src/wallet_set.rs | 12 ++- crates/console/Cargo.toml | 10 +++ crates/console/src/lib.rs | 37 +++++++++ examples/CRISP/server/Dockerfile | 1 + 25 files changed, 278 insertions(+), 143 deletions(-) create mode 100644 crates/console/Cargo.toml create mode 100644 crates/console/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 830a1a5c43..c4549786b8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3126,6 +3126,7 @@ dependencies = [ "dialoguer", "dirs 5.0.1", "e3-config", + "e3-console", "e3-crypto", "e3-entrypoint", "e3-events", @@ -3191,6 +3192,13 @@ dependencies = [ "url", ] +[[package]] +name = "e3-console" +version = "0.1.15" +dependencies = [ + "tokio", +] + [[package]] name = "e3-crypto" version = "0.1.15" diff --git a/Cargo.toml b/Cargo.toml index d72b7f4da4..6725ea5eab 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,7 +38,7 @@ members = [ "crates/wasm", "crates/parity-matrix", "crates/polynomial", - "crates/zk-helpers", + "crates/zk-helpers", "crates/console", ] exclude = [ "examples/CRISP", @@ -75,6 +75,7 @@ repository = "https://github.com/gnosisguild/enclave" e3-aggregator = { version = "0.1.15", path = "./crates/aggregator" } e3-bfv-client = { version = "0.1.15", path = "./crates/bfv-client" } e3-config = { version = "0.1.15", path = "./crates/config" } +e3-console = { version = "0.1.15", path = "./crates/console" } e3-ciphernode-builder = { version = "0.1.15", path = "./crates/ciphernode-builder" } e3-crypto = { version = "0.1.15", path = "./crates/crypto" } e3-data = { version = "0.1.15", path = "./crates/data" } diff --git a/crates/Dockerfile b/crates/Dockerfile index 467a8082d7..546dbfed2a 100644 --- a/crates/Dockerfile +++ b/crates/Dockerfile @@ -48,6 +48,7 @@ COPY crates/cli/Cargo.toml ./cli/Cargo.toml COPY crates/ciphernode-builder/Cargo.toml ./ciphernode-builder/Cargo.toml COPY crates/compute-provider/Cargo.toml ./compute-provider/Cargo.toml COPY crates/config/Cargo.toml ./config/Cargo.toml +COPY crates/console/Cargo.toml ./console/Cargo.toml COPY crates/crypto/Cargo.toml ./crypto/Cargo.toml COPY crates/data/Cargo.toml ./data/Cargo.toml COPY crates/enclaveup/Cargo.toml ./enclaveup/Cargo.toml diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index 1ed13553a6..0a30690215 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -20,6 +20,7 @@ compile-time = { workspace = true } dialoguer = { workspace = true } dirs = { workspace = true } e3-config = { workspace = true } +e3-console = { workspace = true } e3-crypto = { workspace = true } e3-entrypoint = { workspace = true } e3-events = { workspace = true } diff --git a/crates/cli/src/ciphernode/license.rs b/crates/cli/src/ciphernode/license.rs index ac6761f336..e565b773dc 100644 --- a/crates/cli/src/ciphernode/license.rs +++ b/crates/cli/src/ciphernode/license.rs @@ -6,15 +6,16 @@ use alloy::primitives::U256; use anyhow::Result; +use e3_console::Out; use super::context::ChainContext; use super::utils::{ensure_allowance, parse_amount}; use super::LicenseCommands; -pub(crate) async fn execute(ctx: &ChainContext, command: LicenseCommands) -> Result<()> { +pub(crate) async fn execute(out: Out, ctx: &ChainContext, command: LicenseCommands) -> Result<()> { match command { LicenseCommands::Bond { amount } => { - bond_license(ctx, &amount).await?; + bond_license(out, ctx, &amount).await?; } LicenseCommands::Unbond { amount } => { let license = ctx.license_token_address().await?; @@ -27,9 +28,11 @@ pub(crate) async fn execute(ctx: &ChainContext, command: LicenseCommands) -> Res .await? .get_receipt() .await?; - println!( + e3_console::log!( + out, "Queued {} ENCL for exit (tx: {:#x})", - amount, receipt.transaction_hash + amount, + receipt.transaction_hash ); } LicenseCommands::Claim { @@ -64,14 +67,14 @@ pub(crate) async fn execute(ctx: &ChainContext, command: LicenseCommands) -> Res .await? .get_receipt() .await?; - println!("Claimed exits (tx: {:#x})", receipt.transaction_hash); + e3_console::log!(out, "Claimed exits (tx: {:#x})", receipt.transaction_hash); } } Ok(()) } -async fn bond_license(ctx: &ChainContext, amount: &str) -> Result<()> { +async fn bond_license(out: Out, ctx: &ChainContext, amount: &str) -> Result<()> { let license = ctx.license_token_address().await?; let erc20 = ctx.erc20(license); let decimals = erc20.decimals().call().await?; @@ -84,9 +87,11 @@ async fn bond_license(ctx: &ChainContext, amount: &str) -> Result<()> { .await? .get_receipt() .await?; - println!( + e3_console::log!( + out, "Bonded {} ENCL (tx: {:#x})", - amount, receipt.transaction_hash + amount, + receipt.transaction_hash ); Ok(()) } diff --git a/crates/cli/src/ciphernode/lifecycle.rs b/crates/cli/src/ciphernode/lifecycle.rs index dbfda59305..bd30796d9d 100644 --- a/crates/cli/src/ciphernode/lifecycle.rs +++ b/crates/cli/src/ciphernode/lifecycle.rs @@ -6,11 +6,12 @@ use alloy::primitives::U256; use anyhow::{bail, Result}; +use e3_console::Out; use super::context::ChainContext; use super::utils::{format_amount, parse_amount}; -pub(crate) async fn register(ctx: &ChainContext) -> Result<()> { +pub(crate) async fn register(out: Out, ctx: &ChainContext) -> Result<()> { let receipt = ctx .bonding() .registerOperator() @@ -18,7 +19,8 @@ pub(crate) async fn register(ctx: &ChainContext) -> Result<()> { .await? .get_receipt() .await?; - println!( + e3_console::log!( + out, "Registered ciphernode on {} (tx: {:#x})", ctx.chain_label(), receipt.transaction_hash @@ -26,7 +28,7 @@ pub(crate) async fn register(ctx: &ChainContext) -> Result<()> { Ok(()) } -pub(crate) async fn deregister(ctx: &ChainContext) -> Result<()> { +pub(crate) async fn deregister(out: Out, ctx: &ChainContext) -> Result<()> { let receipt = ctx .bonding() .deregisterOperator() @@ -34,18 +36,20 @@ pub(crate) async fn deregister(ctx: &ChainContext) -> Result<()> { .await? .get_receipt() .await?; - println!( + e3_console::log!( + out, "Deregistration requested (tx: {:#x})", receipt.transaction_hash ); Ok(()) } -pub(crate) async fn activate(ctx: &ChainContext) -> Result<()> { - register(ctx).await +pub(crate) async fn activate(out: Out, ctx: &ChainContext) -> Result<()> { + register(out, ctx).await } pub(crate) async fn deactivate( + out: Out, ctx: &ChainContext, ticket_amount: Option, license_amount: Option, @@ -67,9 +71,11 @@ pub(crate) async fn deactivate( .await? .get_receipt() .await?; - println!( + e3_console::log!( + out, "Removed {} tickets (tx: {:#x})", - amount, receipt.transaction_hash + amount, + receipt.transaction_hash ); } @@ -84,17 +90,22 @@ pub(crate) async fn deactivate( .await? .get_receipt() .await?; - println!( + e3_console::log!( + out, "Queued {} ENCL for exit (tx: {:#x})", - amount, receipt.transaction_hash + amount, + receipt.transaction_hash ); } - println!("Submitted deactivation transactions; monitor exit delays before claiming."); + e3_console::log!( + out, + "Submitted deactivation transactions; monitor exit delays before claiming." + ); Ok(()) } -pub(crate) async fn status(ctx: &ChainContext) -> Result<()> { +pub(crate) async fn status(out: Out, ctx: &ChainContext) -> Result<()> { let contract = ctx.bonding(); let operator = ctx.operator(); let ticket_balance: U256 = contract.getTicketBalance(operator).call().await?; @@ -115,26 +126,30 @@ pub(crate) async fn status(ctx: &ChainContext) -> Result<()> { let ticket_decimals = ctx.erc20(ticket_token).decimals().call().await?; let license_decimals = ctx.erc20(license_token).decimals().call().await?; - println!("Ciphernode status on {}:", ctx.chain_label()); - println!(" Address: {:#x}", operator); - println!(" Registered: {}", is_registered); - println!(" Active: {}", is_active); - println!(" Exit pending: {}", has_exit); - println!( + e3_console::log!(out, "Ciphernode status on {}:", ctx.chain_label()); + e3_console::log!(out, " Address: {:#x}", operator); + e3_console::log!(out, " Registered: {}", is_registered); + e3_console::log!(out, " Active: {}", is_active); + e3_console::log!(out, " Exit pending: {}", has_exit); + e3_console::log!( + out, " Ticket balance: {} ({} available)", format_amount(ticket_balance, ticket_decimals), format_amount(available_tickets, ticket_decimals) ); - println!( + e3_console::log!( + out, " License bond: {}", format_amount(license_bond, license_decimals) ); - println!( + e3_console::log!( + out, " Pending exits: tickets={}, license={}", format_amount(pending_tickets, ticket_decimals), format_amount(pending_license, license_decimals) ); - println!( + e3_console::log!( + out, " Requirements: minTickets={}, ticketPrice={} EKT, licenseBond={} ENCL", format_amount(min_ticket_balance, ticket_decimals), format_amount(ticket_price, ticket_decimals), diff --git a/crates/cli/src/ciphernode/mod.rs b/crates/cli/src/ciphernode/mod.rs index 1f5b2667d1..7cfc836160 100644 --- a/crates/cli/src/ciphernode/mod.rs +++ b/crates/cli/src/ciphernode/mod.rs @@ -16,6 +16,7 @@ mod tickets; mod utils; use context::ChainContext; +use e3_console::Out; use zeroize::Zeroizing; use crate::helpers::{ensure_hex_zeroizing, parse_zeroizing}; @@ -129,27 +130,27 @@ pub enum TicketCommands { }, } -pub async fn execute(command: CiphernodeCommands, config: &AppConfig) -> Result<()> { +pub async fn execute(out: Out, command: CiphernodeCommands, config: &AppConfig) -> Result<()> { match command { CiphernodeCommands::License { chain, command } => { let ctx = ChainContext::new(config, chain.selection()).await?; - license::execute(&ctx, command).await? + license::execute(out, &ctx, command).await? } CiphernodeCommands::Tickets { chain, command } => { let ctx = ChainContext::new(config, chain.selection()).await?; - tickets::execute(&ctx, command).await? + tickets::execute(out, &ctx, command).await? } CiphernodeCommands::Register { chain } => { let ctx = ChainContext::new(config, chain.selection()).await?; - lifecycle::register(&ctx).await? + lifecycle::register(out, &ctx).await? } CiphernodeCommands::Deregister { chain } => { let ctx = ChainContext::new(config, chain.selection()).await?; - lifecycle::deregister(&ctx).await? + lifecycle::deregister(out, &ctx).await? } CiphernodeCommands::Activate { chain } => { let ctx = ChainContext::new(config, chain.selection()).await?; - lifecycle::activate(&ctx).await? + lifecycle::activate(out, &ctx).await? } CiphernodeCommands::Deactivate { chain, @@ -157,11 +158,11 @@ pub async fn execute(command: CiphernodeCommands, config: &AppConfig) -> Result< license_amount, } => { let ctx = ChainContext::new(config, chain.selection()).await?; - lifecycle::deactivate(&ctx, ticket_amount, license_amount).await? + lifecycle::deactivate(out, &ctx, ticket_amount, license_amount).await? } CiphernodeCommands::Status { chain } => { let ctx = ChainContext::new(config, chain.selection()).await?; - lifecycle::status(&ctx).await? + lifecycle::status(out, &ctx).await? } CiphernodeCommands::Setup { .. } => { bail!( diff --git a/crates/cli/src/ciphernode/setup.rs b/crates/cli/src/ciphernode/setup.rs index e95a910807..aef8362eda 100644 --- a/crates/cli/src/ciphernode/setup.rs +++ b/crates/cli/src/ciphernode/setup.rs @@ -8,6 +8,7 @@ use alloy::primitives::Address; use anyhow::Result; use dialoguer::{theme::ColorfulTheme, Input}; use e3_config::AppConfig; +use e3_console::Out; use e3_entrypoint::config::setup; use e3_utils::{colorize, Color}; use std::path::PathBuf; @@ -19,6 +20,7 @@ use crate::wallet_set::ask_for_private_key; #[instrument(name = "app", skip_all)] pub async fn execute( + out: Out, rpc_url: Option, password: Option>, private_key: Option>, @@ -64,26 +66,34 @@ pub async fn execute( e3_entrypoint::password::set::execute(&config, pw).await?; let (address, peer_id) = e3_entrypoint::wallet::set::execute(&config, private_key).await?; - print_info(&config, address, &peer_id.to_string(), &rpc_url)?; + print_info(out, &config, address, &peer_id.to_string(), &rpc_url)?; Ok(()) } -fn print_info(config: &AppConfig, address: Address, peer_id: &str, rpc_url: &str) -> Result<()> { +fn print_info( + out: Out, + config: &AppConfig, + address: Address, + peer_id: &str, + rpc_url: &str, +) -> Result<()> { let abs_config = config.config_file().canonicalize()?; - println!("\nEnclave configuration successfully created!"); - println!( + e3_console::log!(out, "\nEnclave configuration successfully created!"); + e3_console::log!( + out, "Editable configuration has been written to:\n\n {}", colorize(abs_config.to_string_lossy(), Color::Yellow) ); - println!(""); - println!("Data written:"); - println!(" address: {}", colorize(address, Color::Cyan)); - println!(" peer_id: {}", colorize(peer_id, Color::Cyan)); - println!(" rpc_url: {}", colorize(rpc_url, Color::Cyan)); - println!(""); + e3_console::log!(out, ""); + e3_console::log!(out, "Data written:"); + e3_console::log!(out, " address: {}", colorize(address, Color::Cyan)); + e3_console::log!(out, " peer_id: {}", colorize(peer_id, Color::Cyan)); + e3_console::log!(out, " rpc_url: {}", colorize(rpc_url, Color::Cyan)); + e3_console::log!(out, ""); if config.using_custom_config() { - println!( + e3_console::log!( + out, "Run future commands from within this directory tree, or pass\n {}\n", colorize( format!("--config {}", abs_config.to_string_lossy()), @@ -91,7 +101,8 @@ fn print_info(config: &AppConfig, address: Address, peer_id: &str, rpc_url: &str ) ); } - println!( + e3_console::log!( + out, "You can start your node using:\n `{}`\n", colorize("enclave start", Color::Yellow) ); diff --git a/crates/cli/src/ciphernode/tickets.rs b/crates/cli/src/ciphernode/tickets.rs index 6a439af705..20f366ae6d 100644 --- a/crates/cli/src/ciphernode/tickets.rs +++ b/crates/cli/src/ciphernode/tickets.rs @@ -5,12 +5,13 @@ // or FITNESS FOR A PARTICULAR PURPOSE. use anyhow::Result; +use e3_console::Out; use super::context::ChainContext; use super::utils::{ensure_allowance, parse_amount}; use super::TicketCommands; -pub(crate) async fn execute(ctx: &ChainContext, command: TicketCommands) -> Result<()> { +pub(crate) async fn execute(out: Out, ctx: &ChainContext, command: TicketCommands) -> Result<()> { match command { TicketCommands::Buy { amount } => { let ticket_contract = ctx.ticket_token_address().await?; @@ -26,9 +27,11 @@ pub(crate) async fn execute(ctx: &ChainContext, command: TicketCommands) -> Resu .await? .get_receipt() .await?; - println!( + e3_console::log!( + out, "Purchased {} tickets (tx: {:#x})", - amount, receipt.transaction_hash + amount, + receipt.transaction_hash ); } TicketCommands::Burn { amount } => { @@ -42,9 +45,11 @@ pub(crate) async fn execute(ctx: &ChainContext, command: TicketCommands) -> Resu .await? .get_receipt() .await?; - println!( + e3_console::log!( + out, "Removed {} tickets (tx: {:#x})", - amount, receipt.transaction_hash + amount, + receipt.transaction_hash ); } } diff --git a/crates/cli/src/cli.rs b/crates/cli/src/cli.rs index 1a43d45e79..362506bf19 100644 --- a/crates/cli/src/cli.rs +++ b/crates/cli/src/cli.rs @@ -20,6 +20,7 @@ use anyhow::{bail, Result}; use clap::{command, ArgAction, Parser, Subcommand}; use e3_config::validation::ValidUrl; use e3_config::{load_config, AppConfig}; +use e3_console::Out; use e3_entrypoint::helpers::datastore::close_all_connections; use tracing::{info, instrument, Level}; @@ -79,7 +80,7 @@ impl Cli { } #[instrument(skip_all)] - pub async fn execute(self) -> Result<()> { + pub async fn execute(self, out: Out) -> Result<()> { let log_level = self.log_level(); // Attempt to load the config, but only treat "not found" as // the trigger for the init flow. All other errors bubble up. @@ -94,7 +95,7 @@ impl Cli { { // Existing init branch match self.command { - Commands::Rev => rev::execute().await?, + Commands::Rev => rev::execute(out).await?, Commands::Init {path, template, skip_cleanup} => { setup_simple_tracing(log_level); init::execute(path, template, skip_cleanup, self.verbose > 0).await? @@ -107,6 +108,7 @@ impl Cli { } } => { ciphernode::setup::execute( + out, rpc_url, password, private_key, @@ -114,8 +116,9 @@ impl Cli { .await?; } Commands::Start { .. } => { - println!("No configuration found. Setting up enclave configuration..."); + e3_console::log!(out,"No configuration found. Setting up enclave configuration..."); ciphernode::setup::execute( + out, None, None, None, @@ -124,7 +127,7 @@ impl Cli { }, Commands::Noir { command } => { setup_simple_tracing(log_level); - noir::execute_without_config(command).await? + noir::execute_without_config(out, command).await? }, _ => bail!( "Configuration file not found. Run `enclave ciphernode setup` to create a configuration." @@ -155,7 +158,9 @@ impl Cli { Commands::Compile { dev } => { e3_support_scripts::program_compile(config.program().clone(), dev).await? } - Commands::PrintEnv { vite, chain } => print_env::execute(&config, &chain, vite).await?, + Commands::PrintEnv { vite, chain } => { + print_env::execute(out, &config, &chain, vite).await? + } Commands::Program { command } => program::execute(command, &config).await?, Commands::PurgeAll => { purge_all::execute().await?; @@ -170,12 +175,12 @@ impl Cli { ) .await? } - Commands::Password { command } => password::execute(command, &config).await?, - Commands::Wallet { command } => wallet::execute(command, config).await?, - Commands::Ciphernode { command } => ciphernode::execute(command, &config).await?, - Commands::Noir { command } => noir::execute(command, &config).await?, - Commands::Net { command } => net::execute(command, &config).await?, - Commands::Rev => rev::execute().await?, + Commands::Password { command } => password::execute(out, command, &config).await?, + Commands::Wallet { command } => wallet::execute(out, command, config).await?, + Commands::Ciphernode { command } => ciphernode::execute(out, command, &config).await?, + Commands::Noir { command } => noir::execute(out, command, &config).await?, + Commands::Net { command } => net::execute(&out, command, &config).await?, + Commands::Rev => rev::execute(out).await?, } close_all_connections(); diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index ca271a26cd..ab4ec27fcc 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -6,6 +6,7 @@ use clap::Parser; use cli::Cli; +use e3_console::Out; use e3_utils::{colorize, Color}; use tracing::info; @@ -61,9 +62,9 @@ pub fn owo() { #[actix::main] pub async fn main() { info!("COMPILATION ID: '{}'", helpers::compile_id::generate_id()); - + let out = Out::stdout(); // Execute the cli - if let Err(err) = Cli::parse().execute().await { + if let Err(err) = Cli::parse().execute(out).await { eprintln!("{}", colorize(err, Color::Red)); std::process::exit(1); } diff --git a/crates/cli/src/net.rs b/crates/cli/src/net.rs index bcb54d0e6c..1d83ce6450 100644 --- a/crates/cli/src/net.rs +++ b/crates/cli/src/net.rs @@ -7,6 +7,7 @@ use anyhow::*; use clap::Subcommand; use e3_config::AppConfig; +use e3_console::Out; use crate::net_get_peer_id; @@ -16,9 +17,9 @@ pub enum NetCommands { GetPeerId, } -pub async fn execute(command: NetCommands, config: &AppConfig) -> Result<()> { +pub async fn execute(out: &Out, command: NetCommands, config: &AppConfig) -> Result<()> { match command { - NetCommands::GetPeerId => net_get_peer_id::execute(config).await?, + NetCommands::GetPeerId => net_get_peer_id::execute(out, config).await?, }; Ok(()) diff --git a/crates/cli/src/net_get_peer_id.rs b/crates/cli/src/net_get_peer_id.rs index be7a397208..8e9f068979 100644 --- a/crates/cli/src/net_get_peer_id.rs +++ b/crates/cli/src/net_get_peer_id.rs @@ -6,9 +6,10 @@ use anyhow::Result; use e3_config::AppConfig; +use e3_console::Out; -pub async fn execute(config: &AppConfig) -> Result<()> { +pub async fn execute(out: &Out, config: &AppConfig) -> Result<()> { let peer_id = e3_entrypoint::net::get_peer_id::execute(config).await?; - println!("{}", peer_id); + e3_console::log!(out, "{}", peer_id); Ok(()) } diff --git a/crates/cli/src/noir.rs b/crates/cli/src/noir.rs index 2b7e1bad16..9bb41f16e5 100644 --- a/crates/cli/src/noir.rs +++ b/crates/cli/src/noir.rs @@ -7,6 +7,7 @@ use anyhow::*; use clap::Subcommand; use e3_config::AppConfig; +use e3_console::Out; use e3_zk_prover::{SetupStatus, ZkBackend}; #[derive(Subcommand, Debug)] @@ -18,119 +19,123 @@ pub enum NoirCommands { }, } -pub async fn execute(command: NoirCommands, config: &AppConfig) -> Result<()> { +pub async fn execute(out: Out, command: NoirCommands, config: &AppConfig) -> Result<()> { let backend = ZkBackend::new(config.bb_binary(), config.circuits_dir(), config.work_dir()); match command { NoirCommands::Status => { - execute_status(&backend).await?; + execute_status(out, &backend).await?; } NoirCommands::Setup { force } => { - execute_setup(&backend, force).await?; + execute_setup(out, &backend, force).await?; } } Ok(()) } -pub async fn execute_without_config(command: NoirCommands) -> Result<()> { +pub async fn execute_without_config(out: Out, command: NoirCommands) -> Result<()> { let backend = ZkBackend::with_default_dir("default") .map_err(|e| anyhow!("Failed to initialize ZK backend: {}", e))?; match command { NoirCommands::Status => { - execute_status(&backend).await?; + execute_status(out, &backend).await?; } NoirCommands::Setup { force } => { - execute_setup(&backend, force).await?; + execute_setup(out, &backend, force).await?; } } Ok(()) } -async fn execute_status(backend: &ZkBackend) -> Result<()> { +async fn execute_status(out: Out, backend: &ZkBackend) -> Result<()> { let status = backend.check_status().await; let version_info = backend.load_version_info().await; - println!("=== ZK Prover Status ===\n"); + e3_console::log!(out, "=== ZK Prover Status ===\n"); - println!("Barretenberg (bb):"); - println!(" Path: {}", backend.bb_binary.display()); + e3_console::log!(out, "Barretenberg (bb):"); + e3_console::log!(out, " Path: {}", backend.bb_binary.display()); if let Some(ref v) = version_info.bb_version { - println!(" Version: {}", v); + e3_console::log!(out, " Version: {}", v); } if backend.bb_binary.exists() { - println!(" Installed"); + e3_console::log!(out, " Installed"); } else { - println!(" Not installed"); + e3_console::log!(out, " Not installed"); } - println!(); + e3_console::log!(out, ""); - println!("Circuits:"); - println!(" Path: {}", backend.circuits_dir.display()); + e3_console::log!(out, "Circuits:"); + e3_console::log!(out, " Path: {}", backend.circuits_dir.display()); if let Some(ref v) = version_info.circuits_version { - println!(" Version: {}", v); + e3_console::log!(out, " Version: {}", v); } if backend.circuits_dir.exists() { - println!(" Installed"); + e3_console::log!(out, " Installed"); } else { - println!(" Not installed"); + e3_console::log!(out, " Not installed"); } - println!(); + e3_console::log!(out, ""); match status { SetupStatus::Ready => { - println!("Status: Ready"); + e3_console::log!(out, "Status: Ready"); } SetupStatus::BbNeedsUpdate { installed, required, } => { - println!("Status: Barretenberg needs update"); - println!( + e3_console::log!(out, "Status: Barretenberg needs update"); + e3_console::log!( + out, " Installed: {}", installed.as_deref().unwrap_or("not installed") ); - println!(" Required: {}", required); - println!("\nRun `enclave noir setup` to update"); + e3_console::log!(out, " Required: {}", required); + e3_console::log!(out, "\nRun `enclave noir setup` to update"); } SetupStatus::CircuitsNeedUpdate { installed, required, } => { - println!("Status: Circuits need update"); - println!( + e3_console::log!(out, "Status: Circuits need update"); + e3_console::log!( + out, " Installed: {}", installed.as_deref().unwrap_or("not installed") ); - println!(" Required: {}", required); - println!("\nRun `enclave noir setup` to update"); + e3_console::log!(out, " Required: {}", required); + e3_console::log!(out, "\nRun `enclave noir setup` to update"); } SetupStatus::FullSetupNeeded => { - println!("Status: Setup required"); - println!("\nRun `enclave noir setup` to install"); + e3_console::log!(out, "Status: Setup required"); + e3_console::log!(out, "\nRun `enclave noir setup` to install"); } } Ok(()) } -async fn execute_setup(backend: &ZkBackend, force: bool) -> Result<()> { - println!("Setting up ZK prover...\n"); - println!( +async fn execute_setup(out: Out, backend: &ZkBackend, force: bool) -> Result<()> { + e3_console::log!(out, "Setting up ZK prover...\n"); + e3_console::log!( + out, " target bb version: {}", backend.config.required_bb_version ); - println!( + e3_console::log!( + out, " target circuits version: {}\n", backend.config.required_circuits_version ); if force { - println!("Force reinstalling ZK prover components...\n"); + e3_console::log!(out, "Force reinstalling ZK prover components...\n"); // Force reinstall by directly downloading components backend @@ -145,19 +150,21 @@ async fn execute_setup(backend: &ZkBackend, force: bool) -> Result<()> { let status = backend.check_status().await; if matches!(status, SetupStatus::Ready) { let version_info = backend.load_version_info().await; - println!("ZK prover is already set up and up to date."); - println!( + e3_console::log!(out, "ZK prover is already set up and up to date."); + e3_console::log!( + out, " bb version: {}", version_info.bb_version.as_deref().unwrap_or("unknown") ); - println!( + e3_console::log!( + out, " circuits version: {}", version_info .circuits_version .as_deref() .unwrap_or("unknown") ); - println!(" Use --force to reinstall."); + e3_console::log!(out, " Use --force to reinstall."); return Ok(()); } @@ -169,15 +176,21 @@ async fn execute_setup(backend: &ZkBackend, force: bool) -> Result<()> { let version_info = backend.load_version_info().await; - println!("\nZK prover setup complete!"); - println!(); - println!(" bb binary: {}", backend.bb_binary.display()); - println!( + e3_console::log!(out, "\nZK prover setup complete!"); + e3_console::log!(out, ""); + e3_console::log!(out, " bb binary: {}", backend.bb_binary.display()); + e3_console::log!( + out, " bb version: {}", version_info.bb_version.as_deref().unwrap_or("unknown") ); - println!(" circuits dir: {}", backend.circuits_dir.display()); - println!( + e3_console::log!( + out, + " circuits dir: {}", + backend.circuits_dir.display() + ); + e3_console::log!( + out, " circuits version: {}", version_info .circuits_version @@ -185,7 +198,7 @@ async fn execute_setup(backend: &ZkBackend, force: bool) -> Result<()> { .unwrap_or("unknown") ); if let Some(ref ts) = version_info.last_updated { - println!(" last updated: {}", ts); + e3_console::log!(out, " last updated: {}", ts); } Ok(()) diff --git a/crates/cli/src/password.rs b/crates/cli/src/password.rs index 4cc08b9553..d7ed5d284e 100644 --- a/crates/cli/src/password.rs +++ b/crates/cli/src/password.rs @@ -7,6 +7,7 @@ use anyhow::*; use clap::Subcommand; use e3_config::AppConfig; +use e3_console::Out; use zeroize::Zeroizing; use crate::{helpers::parse_zeroizing, password_delete, password_set}; @@ -24,10 +25,10 @@ pub enum PasswordCommands { Delete, } -pub async fn execute(command: PasswordCommands, config: &AppConfig) -> Result<()> { +pub async fn execute(out: Out, command: PasswordCommands, config: &AppConfig) -> Result<()> { match command { - PasswordCommands::Set { password } => password_set::execute(&config, password).await?, - PasswordCommands::Delete => password_delete::execute(&config).await?, + PasswordCommands::Set { password } => password_set::execute(out, &config, password).await?, + PasswordCommands::Delete => password_delete::execute(&out, &config).await?, }; Ok(()) diff --git a/crates/cli/src/password_delete.rs b/crates/cli/src/password_delete.rs index 9c5ed85308..ee7793bd1d 100644 --- a/crates/cli/src/password_delete.rs +++ b/crates/cli/src/password_delete.rs @@ -8,9 +8,10 @@ use crate::helpers::prompt_password::prompt_password; use anyhow::Result; use dialoguer::{theme::ColorfulTheme, Confirm}; use e3_config::AppConfig; +use e3_console::Out; use zeroize::Zeroize; -pub async fn prompt_delete(config: &AppConfig) -> Result { +pub async fn prompt_delete(out: Out, config: &AppConfig) -> Result { if !Confirm::with_theme(&ColorfulTheme::default()) .with_prompt("Are you sure you want to delete the key? This action cannot be undone.") .default(false) @@ -20,7 +21,7 @@ pub async fn prompt_delete(config: &AppConfig) -> Result { } let Ok(mut cur_pw) = e3_entrypoint::password::delete::get_current_password(config).await else { - println!("Password is not set. Nothing to do."); + e3_console::log!(out, "Password is not set. Nothing to do."); return Ok(false); }; @@ -35,12 +36,12 @@ pub async fn prompt_delete(config: &AppConfig) -> Result { Ok(true) } -pub async fn execute(config: &AppConfig) -> Result<()> { - if prompt_delete(config).await? { +pub async fn execute(out: &Out, config: &AppConfig) -> Result<()> { + if prompt_delete(out.clone(), config).await? { e3_entrypoint::password::delete::execute(config).await?; - println!("Password successfully deleted."); + e3_console::log!(out, "Password successfully deleted."); } else { - println!("Operation cancelled."); + e3_console::log!(out, "Operation cancelled."); } Ok(()) } diff --git a/crates/cli/src/password_set.rs b/crates/cli/src/password_set.rs index 5b302098eb..77509356fd 100644 --- a/crates/cli/src/password_set.rs +++ b/crates/cli/src/password_set.rs @@ -6,6 +6,7 @@ use anyhow::{bail, Result}; use e3_config::AppConfig; +use e3_console::Out; use zeroize::{Zeroize, Zeroizing}; use crate::helpers::prompt_password::prompt_password; @@ -43,15 +44,15 @@ pub fn ask_for_password(input: Option>) -> Result>) -> Result<()> { - println!("Setting password..."); +pub async fn execute(out: Out, config: &AppConfig, input: Option>) -> Result<()> { + e3_console::log!(out, "Setting password..."); e3_entrypoint::password::set::preflight(config).await?; let pw = ask_for_password(input)?; e3_entrypoint::password::set::execute(config, pw).await?; - println!("Password successfully set."); + e3_console::log!(out, "Password successfully set."); Ok(()) } diff --git a/crates/cli/src/print_env.rs b/crates/cli/src/print_env.rs index 99ccba221d..05d789e411 100644 --- a/crates/cli/src/print_env.rs +++ b/crates/cli/src/print_env.rs @@ -6,6 +6,7 @@ use anyhow::Result; use e3_config::AppConfig; +use e3_console::Out; pub fn extract_env_vars_vite(config: &AppConfig, chain: &str) -> String { let mut env_vars = Vec::new(); @@ -70,11 +71,11 @@ pub fn extract_env_vars(config: &AppConfig, chain: &str) -> String { env_vars.join(" ") } -pub async fn execute(config: &AppConfig, chain: &str, as_vite: bool) -> Result<()> { +pub async fn execute(out: Out, config: &AppConfig, chain: &str, as_vite: bool) -> Result<()> { if as_vite { - println!("{}", extract_env_vars_vite(config, chain)); + e3_console::log!(out, "{}", extract_env_vars_vite(config, chain)); } else { - println!("{}", extract_env_vars(config, chain)); + e3_console::log!(out, "{}", extract_env_vars(config, chain)); } Ok(()) } diff --git a/crates/cli/src/rev.rs b/crates/cli/src/rev.rs index faca607142..1e6871b996 100644 --- a/crates/cli/src/rev.rs +++ b/crates/cli/src/rev.rs @@ -4,9 +4,11 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. +use e3_console::Out; + pub const GIT_SHA: &str = env!("GIT_SHA"); -pub async fn execute() -> anyhow::Result<()> { - println!("{}", GIT_SHA); +pub async fn execute(out: Out) -> anyhow::Result<()> { + e3_console::log!(out, "{}", GIT_SHA); Ok(()) } diff --git a/crates/cli/src/wallet.rs b/crates/cli/src/wallet.rs index 36225b6ebc..1cdc58f431 100644 --- a/crates/cli/src/wallet.rs +++ b/crates/cli/src/wallet.rs @@ -7,6 +7,7 @@ use anyhow::*; use clap::Subcommand; use e3_config::AppConfig; +use e3_console::Out; use zeroize::Zeroizing; use crate::{helpers::ensure_hex_zeroizing, wallet_get, wallet_set}; @@ -24,10 +25,12 @@ pub enum WalletCommands { Get, } -pub async fn execute(command: WalletCommands, config: AppConfig) -> Result<()> { +pub async fn execute(out: Out, command: WalletCommands, config: AppConfig) -> Result<()> { match command { - WalletCommands::Set { private_key } => wallet_set::execute(&config, private_key).await?, - WalletCommands::Get => wallet_get::execute(&config).await?, + WalletCommands::Set { private_key } => { + wallet_set::execute(out, &config, private_key).await? + } + WalletCommands::Get => wallet_get::execute(out, &config).await?, }; Ok(()) diff --git a/crates/cli/src/wallet_get.rs b/crates/cli/src/wallet_get.rs index 383ae256f1..cda5bb06af 100644 --- a/crates/cli/src/wallet_get.rs +++ b/crates/cli/src/wallet_get.rs @@ -6,10 +6,11 @@ use anyhow::Result; use e3_config::AppConfig; +use e3_console::Out; -pub async fn execute(config: &AppConfig) -> Result<()> { +pub async fn execute(out: Out, config: &AppConfig) -> Result<()> { let address = e3_entrypoint::wallet::get::execute(config).await?; - println!("{}", address); + e3_console::log!(out, "{}", address); Ok(()) } diff --git a/crates/cli/src/wallet_set.rs b/crates/cli/src/wallet_set.rs index 27b38d2964..fd2a77e17f 100644 --- a/crates/cli/src/wallet_set.rs +++ b/crates/cli/src/wallet_set.rs @@ -7,6 +7,7 @@ use anyhow::Result; use dialoguer::{theme::ColorfulTheme, Password}; use e3_config::AppConfig; +use e3_console::Out; use e3_entrypoint::wallet::set::validate_private_key; use zeroize::Zeroizing; @@ -28,10 +29,17 @@ pub fn ask_for_private_key(given_key: Option>) -> Result>) -> Result<()> { +pub async fn execute( + out: Out, + config: &AppConfig, + private_key: Option>, +) -> Result<()> { let input = ask_for_private_key(private_key)?; e3_entrypoint::wallet::set::execute(config, input).await?; - println!("Wallet key has been successfully stored and encrypted."); + e3_console::log!( + out, + "Wallet key has been successfully stored and encrypted." + ); Ok(()) } diff --git a/crates/console/Cargo.toml b/crates/console/Cargo.toml new file mode 100644 index 0000000000..804a00634f --- /dev/null +++ b/crates/console/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "e3-console" +version.workspace = true +edition.workspace = true +license.workspace = true +description.workspace = true +repository.workspace = true + +[dependencies] +tokio.workspace = true diff --git a/crates/console/src/lib.rs b/crates/console/src/lib.rs new file mode 100644 index 0000000000..0e212c230d --- /dev/null +++ b/crates/console/src/lib.rs @@ -0,0 +1,37 @@ +use tokio::sync::mpsc; + +#[derive(Clone)] +pub struct Out { + tx: mpsc::UnboundedSender, +} + +impl Out { + /// Output goes to stdout. + pub fn stdout() -> Self { + let (tx, mut rx) = mpsc::unbounded_channel(); + tokio::spawn(async move { + while let Some(msg) = rx.recv().await { + println!("{msg}"); + } + }); + Self { tx } + } + + /// Output goes to the returned receiver. Caller decides the destination. + pub fn channel() -> (Self, mpsc::UnboundedReceiver) { + let (tx, rx) = mpsc::unbounded_channel(); + (Self { tx }, rx) + } + + /// Emit a message to whatever destination this context is wired to. + pub fn log(&self, msg: String) { + let _ = self.tx.send(msg); + } +} + +#[macro_export] +macro_rules! log { + ($ctx:expr, $($arg:tt)*) => { + $ctx.log(format!($($arg)*)) + }; +} diff --git a/examples/CRISP/server/Dockerfile b/examples/CRISP/server/Dockerfile index 8c6ad43ff6..31c2c3cc55 100644 --- a/examples/CRISP/server/Dockerfile +++ b/examples/CRISP/server/Dockerfile @@ -64,6 +64,7 @@ COPY crates/ciphernode-builder/Cargo.toml crates/ciphernode-builder/Cargo.toml COPY crates/cli/Cargo.toml crates/cli/Cargo.toml COPY crates/compute-provider/Cargo.toml crates/compute-provider/Cargo.toml COPY crates/config/Cargo.toml crates/config/Cargo.toml +COPY crates/console/Cargo.toml crates/console/Cargo.toml COPY crates/crypto/Cargo.toml crates/crypto/Cargo.toml COPY crates/data/Cargo.toml crates/data/Cargo.toml COPY crates/entrypoint/Cargo.toml crates/entrypoint/Cargo.toml From 3eabbb8b2744da9e385d4da2c75a9064c8b401ee Mon Sep 17 00:00:00 2001 From: ryardley Date: Sat, 14 Mar 2026 04:21:06 +0000 Subject: [PATCH 02/31] Out -> Console --- crates/cli/src/ciphernode/license.rs | 10 +++++++--- crates/cli/src/ciphernode/lifecycle.rs | 12 ++++++------ crates/cli/src/ciphernode/mod.rs | 4 ++-- crates/cli/src/ciphernode/setup.rs | 6 +++--- crates/cli/src/ciphernode/tickets.rs | 8 ++++++-- crates/cli/src/cli.rs | 4 ++-- crates/cli/src/main.rs | 4 ++-- crates/cli/src/net.rs | 4 ++-- crates/cli/src/net_get_peer_id.rs | 4 ++-- crates/cli/src/noir.rs | 10 +++++----- crates/cli/src/password.rs | 4 ++-- crates/cli/src/password_delete.rs | 6 +++--- crates/cli/src/password_set.rs | 8 ++++++-- crates/cli/src/print_env.rs | 4 ++-- crates/cli/src/rev.rs | 4 ++-- crates/cli/src/wallet.rs | 4 ++-- crates/cli/src/wallet_get.rs | 4 ++-- crates/cli/src/wallet_set.rs | 4 ++-- crates/console/src/lib.rs | 4 ++-- 19 files changed, 60 insertions(+), 48 deletions(-) diff --git a/crates/cli/src/ciphernode/license.rs b/crates/cli/src/ciphernode/license.rs index e565b773dc..9b5e56f7a2 100644 --- a/crates/cli/src/ciphernode/license.rs +++ b/crates/cli/src/ciphernode/license.rs @@ -6,13 +6,17 @@ use alloy::primitives::U256; use anyhow::Result; -use e3_console::Out; +use e3_console::Console; use super::context::ChainContext; use super::utils::{ensure_allowance, parse_amount}; use super::LicenseCommands; -pub(crate) async fn execute(out: Out, ctx: &ChainContext, command: LicenseCommands) -> Result<()> { +pub(crate) async fn execute( + out: Console, + ctx: &ChainContext, + command: LicenseCommands, +) -> Result<()> { match command { LicenseCommands::Bond { amount } => { bond_license(out, ctx, &amount).await?; @@ -74,7 +78,7 @@ pub(crate) async fn execute(out: Out, ctx: &ChainContext, command: LicenseComman Ok(()) } -async fn bond_license(out: Out, ctx: &ChainContext, amount: &str) -> Result<()> { +async fn bond_license(out: Console, ctx: &ChainContext, amount: &str) -> Result<()> { let license = ctx.license_token_address().await?; let erc20 = ctx.erc20(license); let decimals = erc20.decimals().call().await?; diff --git a/crates/cli/src/ciphernode/lifecycle.rs b/crates/cli/src/ciphernode/lifecycle.rs index bd30796d9d..0b820c54fe 100644 --- a/crates/cli/src/ciphernode/lifecycle.rs +++ b/crates/cli/src/ciphernode/lifecycle.rs @@ -6,12 +6,12 @@ use alloy::primitives::U256; use anyhow::{bail, Result}; -use e3_console::Out; +use e3_console::Console; use super::context::ChainContext; use super::utils::{format_amount, parse_amount}; -pub(crate) async fn register(out: Out, ctx: &ChainContext) -> Result<()> { +pub(crate) async fn register(out: Console, ctx: &ChainContext) -> Result<()> { let receipt = ctx .bonding() .registerOperator() @@ -28,7 +28,7 @@ pub(crate) async fn register(out: Out, ctx: &ChainContext) -> Result<()> { Ok(()) } -pub(crate) async fn deregister(out: Out, ctx: &ChainContext) -> Result<()> { +pub(crate) async fn deregister(out: Console, ctx: &ChainContext) -> Result<()> { let receipt = ctx .bonding() .deregisterOperator() @@ -44,12 +44,12 @@ pub(crate) async fn deregister(out: Out, ctx: &ChainContext) -> Result<()> { Ok(()) } -pub(crate) async fn activate(out: Out, ctx: &ChainContext) -> Result<()> { +pub(crate) async fn activate(out: Console, ctx: &ChainContext) -> Result<()> { register(out, ctx).await } pub(crate) async fn deactivate( - out: Out, + out: Console, ctx: &ChainContext, ticket_amount: Option, license_amount: Option, @@ -105,7 +105,7 @@ pub(crate) async fn deactivate( Ok(()) } -pub(crate) async fn status(out: Out, ctx: &ChainContext) -> Result<()> { +pub(crate) async fn status(out: Console, ctx: &ChainContext) -> Result<()> { let contract = ctx.bonding(); let operator = ctx.operator(); let ticket_balance: U256 = contract.getTicketBalance(operator).call().await?; diff --git a/crates/cli/src/ciphernode/mod.rs b/crates/cli/src/ciphernode/mod.rs index 7cfc836160..ccc04dcbd4 100644 --- a/crates/cli/src/ciphernode/mod.rs +++ b/crates/cli/src/ciphernode/mod.rs @@ -16,7 +16,7 @@ mod tickets; mod utils; use context::ChainContext; -use e3_console::Out; +use e3_console::Console; use zeroize::Zeroizing; use crate::helpers::{ensure_hex_zeroizing, parse_zeroizing}; @@ -130,7 +130,7 @@ pub enum TicketCommands { }, } -pub async fn execute(out: Out, command: CiphernodeCommands, config: &AppConfig) -> Result<()> { +pub async fn execute(out: Console, command: CiphernodeCommands, config: &AppConfig) -> Result<()> { match command { CiphernodeCommands::License { chain, command } => { let ctx = ChainContext::new(config, chain.selection()).await?; diff --git a/crates/cli/src/ciphernode/setup.rs b/crates/cli/src/ciphernode/setup.rs index aef8362eda..8be361ec10 100644 --- a/crates/cli/src/ciphernode/setup.rs +++ b/crates/cli/src/ciphernode/setup.rs @@ -8,7 +8,7 @@ use alloy::primitives::Address; use anyhow::Result; use dialoguer::{theme::ColorfulTheme, Input}; use e3_config::AppConfig; -use e3_console::Out; +use e3_console::Console; use e3_entrypoint::config::setup; use e3_utils::{colorize, Color}; use std::path::PathBuf; @@ -20,7 +20,7 @@ use crate::wallet_set::ask_for_private_key; #[instrument(name = "app", skip_all)] pub async fn execute( - out: Out, + out: Console, rpc_url: Option, password: Option>, private_key: Option>, @@ -71,7 +71,7 @@ pub async fn execute( } fn print_info( - out: Out, + out: Console, config: &AppConfig, address: Address, peer_id: &str, diff --git a/crates/cli/src/ciphernode/tickets.rs b/crates/cli/src/ciphernode/tickets.rs index 20f366ae6d..cc5ea81bc4 100644 --- a/crates/cli/src/ciphernode/tickets.rs +++ b/crates/cli/src/ciphernode/tickets.rs @@ -5,13 +5,17 @@ // or FITNESS FOR A PARTICULAR PURPOSE. use anyhow::Result; -use e3_console::Out; +use e3_console::Console; use super::context::ChainContext; use super::utils::{ensure_allowance, parse_amount}; use super::TicketCommands; -pub(crate) async fn execute(out: Out, ctx: &ChainContext, command: TicketCommands) -> Result<()> { +pub(crate) async fn execute( + out: Console, + ctx: &ChainContext, + command: TicketCommands, +) -> Result<()> { match command { TicketCommands::Buy { amount } => { let ticket_contract = ctx.ticket_token_address().await?; diff --git a/crates/cli/src/cli.rs b/crates/cli/src/cli.rs index 362506bf19..d4820adb63 100644 --- a/crates/cli/src/cli.rs +++ b/crates/cli/src/cli.rs @@ -20,7 +20,7 @@ use anyhow::{bail, Result}; use clap::{command, ArgAction, Parser, Subcommand}; use e3_config::validation::ValidUrl; use e3_config::{load_config, AppConfig}; -use e3_console::Out; +use e3_console::Console; use e3_entrypoint::helpers::datastore::close_all_connections; use tracing::{info, instrument, Level}; @@ -80,7 +80,7 @@ impl Cli { } #[instrument(skip_all)] - pub async fn execute(self, out: Out) -> Result<()> { + pub async fn execute(self, out: Console) -> Result<()> { let log_level = self.log_level(); // Attempt to load the config, but only treat "not found" as // the trigger for the init flow. All other errors bubble up. diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index ab4ec27fcc..f5efffab25 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -6,7 +6,7 @@ use clap::Parser; use cli::Cli; -use e3_console::Out; +use e3_console::Console; use e3_utils::{colorize, Color}; use tracing::info; @@ -62,7 +62,7 @@ pub fn owo() { #[actix::main] pub async fn main() { info!("COMPILATION ID: '{}'", helpers::compile_id::generate_id()); - let out = Out::stdout(); + let out = Console::stdout(); // Execute the cli if let Err(err) = Cli::parse().execute(out).await { eprintln!("{}", colorize(err, Color::Red)); diff --git a/crates/cli/src/net.rs b/crates/cli/src/net.rs index 1d83ce6450..b22b9dba66 100644 --- a/crates/cli/src/net.rs +++ b/crates/cli/src/net.rs @@ -7,7 +7,7 @@ use anyhow::*; use clap::Subcommand; use e3_config::AppConfig; -use e3_console::Out; +use e3_console::Console; use crate::net_get_peer_id; @@ -17,7 +17,7 @@ pub enum NetCommands { GetPeerId, } -pub async fn execute(out: &Out, command: NetCommands, config: &AppConfig) -> Result<()> { +pub async fn execute(out: &Console, command: NetCommands, config: &AppConfig) -> Result<()> { match command { NetCommands::GetPeerId => net_get_peer_id::execute(out, config).await?, }; diff --git a/crates/cli/src/net_get_peer_id.rs b/crates/cli/src/net_get_peer_id.rs index 8e9f068979..71906f9217 100644 --- a/crates/cli/src/net_get_peer_id.rs +++ b/crates/cli/src/net_get_peer_id.rs @@ -6,9 +6,9 @@ use anyhow::Result; use e3_config::AppConfig; -use e3_console::Out; +use e3_console::Console; -pub async fn execute(out: &Out, config: &AppConfig) -> Result<()> { +pub async fn execute(out: &Console, config: &AppConfig) -> Result<()> { let peer_id = e3_entrypoint::net::get_peer_id::execute(config).await?; e3_console::log!(out, "{}", peer_id); Ok(()) diff --git a/crates/cli/src/noir.rs b/crates/cli/src/noir.rs index 9bb41f16e5..7f9b3069d4 100644 --- a/crates/cli/src/noir.rs +++ b/crates/cli/src/noir.rs @@ -7,7 +7,7 @@ use anyhow::*; use clap::Subcommand; use e3_config::AppConfig; -use e3_console::Out; +use e3_console::Console; use e3_zk_prover::{SetupStatus, ZkBackend}; #[derive(Subcommand, Debug)] @@ -19,7 +19,7 @@ pub enum NoirCommands { }, } -pub async fn execute(out: Out, command: NoirCommands, config: &AppConfig) -> Result<()> { +pub async fn execute(out: Console, command: NoirCommands, config: &AppConfig) -> Result<()> { let backend = ZkBackend::new(config.bb_binary(), config.circuits_dir(), config.work_dir()); match command { @@ -34,7 +34,7 @@ pub async fn execute(out: Out, command: NoirCommands, config: &AppConfig) -> Res Ok(()) } -pub async fn execute_without_config(out: Out, command: NoirCommands) -> Result<()> { +pub async fn execute_without_config(out: Console, command: NoirCommands) -> Result<()> { let backend = ZkBackend::with_default_dir("default") .map_err(|e| anyhow!("Failed to initialize ZK backend: {}", e))?; @@ -50,7 +50,7 @@ pub async fn execute_without_config(out: Out, command: NoirCommands) -> Result<( Ok(()) } -async fn execute_status(out: Out, backend: &ZkBackend) -> Result<()> { +async fn execute_status(out: Console, backend: &ZkBackend) -> Result<()> { let status = backend.check_status().await; let version_info = backend.load_version_info().await; @@ -121,7 +121,7 @@ async fn execute_status(out: Out, backend: &ZkBackend) -> Result<()> { Ok(()) } -async fn execute_setup(out: Out, backend: &ZkBackend, force: bool) -> Result<()> { +async fn execute_setup(out: Console, backend: &ZkBackend, force: bool) -> Result<()> { e3_console::log!(out, "Setting up ZK prover...\n"); e3_console::log!( out, diff --git a/crates/cli/src/password.rs b/crates/cli/src/password.rs index d7ed5d284e..eff2a4d82f 100644 --- a/crates/cli/src/password.rs +++ b/crates/cli/src/password.rs @@ -7,7 +7,7 @@ use anyhow::*; use clap::Subcommand; use e3_config::AppConfig; -use e3_console::Out; +use e3_console::Console; use zeroize::Zeroizing; use crate::{helpers::parse_zeroizing, password_delete, password_set}; @@ -25,7 +25,7 @@ pub enum PasswordCommands { Delete, } -pub async fn execute(out: Out, command: PasswordCommands, config: &AppConfig) -> Result<()> { +pub async fn execute(out: Console, command: PasswordCommands, config: &AppConfig) -> Result<()> { match command { PasswordCommands::Set { password } => password_set::execute(out, &config, password).await?, PasswordCommands::Delete => password_delete::execute(&out, &config).await?, diff --git a/crates/cli/src/password_delete.rs b/crates/cli/src/password_delete.rs index ee7793bd1d..865f475975 100644 --- a/crates/cli/src/password_delete.rs +++ b/crates/cli/src/password_delete.rs @@ -8,10 +8,10 @@ use crate::helpers::prompt_password::prompt_password; use anyhow::Result; use dialoguer::{theme::ColorfulTheme, Confirm}; use e3_config::AppConfig; -use e3_console::Out; +use e3_console::Console; use zeroize::Zeroize; -pub async fn prompt_delete(out: Out, config: &AppConfig) -> Result { +pub async fn prompt_delete(out: Console, config: &AppConfig) -> Result { if !Confirm::with_theme(&ColorfulTheme::default()) .with_prompt("Are you sure you want to delete the key? This action cannot be undone.") .default(false) @@ -36,7 +36,7 @@ pub async fn prompt_delete(out: Out, config: &AppConfig) -> Result { Ok(true) } -pub async fn execute(out: &Out, config: &AppConfig) -> Result<()> { +pub async fn execute(out: &Console, config: &AppConfig) -> Result<()> { if prompt_delete(out.clone(), config).await? { e3_entrypoint::password::delete::execute(config).await?; e3_console::log!(out, "Password successfully deleted."); diff --git a/crates/cli/src/password_set.rs b/crates/cli/src/password_set.rs index 77509356fd..b05854d4cf 100644 --- a/crates/cli/src/password_set.rs +++ b/crates/cli/src/password_set.rs @@ -6,7 +6,7 @@ use anyhow::{bail, Result}; use e3_config::AppConfig; -use e3_console::Out; +use e3_console::Console; use zeroize::{Zeroize, Zeroizing}; use crate::helpers::prompt_password::prompt_password; @@ -44,7 +44,11 @@ pub fn ask_for_password(input: Option>) -> Result>) -> Result<()> { +pub async fn execute( + out: Console, + config: &AppConfig, + input: Option>, +) -> Result<()> { e3_console::log!(out, "Setting password..."); e3_entrypoint::password::set::preflight(config).await?; diff --git a/crates/cli/src/print_env.rs b/crates/cli/src/print_env.rs index 05d789e411..befe046311 100644 --- a/crates/cli/src/print_env.rs +++ b/crates/cli/src/print_env.rs @@ -6,7 +6,7 @@ use anyhow::Result; use e3_config::AppConfig; -use e3_console::Out; +use e3_console::Console; pub fn extract_env_vars_vite(config: &AppConfig, chain: &str) -> String { let mut env_vars = Vec::new(); @@ -71,7 +71,7 @@ pub fn extract_env_vars(config: &AppConfig, chain: &str) -> String { env_vars.join(" ") } -pub async fn execute(out: Out, config: &AppConfig, chain: &str, as_vite: bool) -> Result<()> { +pub async fn execute(out: Console, config: &AppConfig, chain: &str, as_vite: bool) -> Result<()> { if as_vite { e3_console::log!(out, "{}", extract_env_vars_vite(config, chain)); } else { diff --git a/crates/cli/src/rev.rs b/crates/cli/src/rev.rs index 1e6871b996..11c553bda6 100644 --- a/crates/cli/src/rev.rs +++ b/crates/cli/src/rev.rs @@ -4,11 +4,11 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -use e3_console::Out; +use e3_console::Console; pub const GIT_SHA: &str = env!("GIT_SHA"); -pub async fn execute(out: Out) -> anyhow::Result<()> { +pub async fn execute(out: Console) -> anyhow::Result<()> { e3_console::log!(out, "{}", GIT_SHA); Ok(()) } diff --git a/crates/cli/src/wallet.rs b/crates/cli/src/wallet.rs index 1cdc58f431..4a40b005aa 100644 --- a/crates/cli/src/wallet.rs +++ b/crates/cli/src/wallet.rs @@ -7,7 +7,7 @@ use anyhow::*; use clap::Subcommand; use e3_config::AppConfig; -use e3_console::Out; +use e3_console::Console; use zeroize::Zeroizing; use crate::{helpers::ensure_hex_zeroizing, wallet_get, wallet_set}; @@ -25,7 +25,7 @@ pub enum WalletCommands { Get, } -pub async fn execute(out: Out, command: WalletCommands, config: AppConfig) -> Result<()> { +pub async fn execute(out: Console, command: WalletCommands, config: AppConfig) -> Result<()> { match command { WalletCommands::Set { private_key } => { wallet_set::execute(out, &config, private_key).await? diff --git a/crates/cli/src/wallet_get.rs b/crates/cli/src/wallet_get.rs index cda5bb06af..dd456bc939 100644 --- a/crates/cli/src/wallet_get.rs +++ b/crates/cli/src/wallet_get.rs @@ -6,9 +6,9 @@ use anyhow::Result; use e3_config::AppConfig; -use e3_console::Out; +use e3_console::Console; -pub async fn execute(out: Out, config: &AppConfig) -> Result<()> { +pub async fn execute(out: Console, config: &AppConfig) -> Result<()> { let address = e3_entrypoint::wallet::get::execute(config).await?; e3_console::log!(out, "{}", address); diff --git a/crates/cli/src/wallet_set.rs b/crates/cli/src/wallet_set.rs index fd2a77e17f..fbedc5e1f2 100644 --- a/crates/cli/src/wallet_set.rs +++ b/crates/cli/src/wallet_set.rs @@ -7,7 +7,7 @@ use anyhow::Result; use dialoguer::{theme::ColorfulTheme, Password}; use e3_config::AppConfig; -use e3_console::Out; +use e3_console::Console; use e3_entrypoint::wallet::set::validate_private_key; use zeroize::Zeroizing; @@ -30,7 +30,7 @@ pub fn ask_for_private_key(given_key: Option>) -> Result>, ) -> Result<()> { diff --git a/crates/console/src/lib.rs b/crates/console/src/lib.rs index 0e212c230d..86c19f0d67 100644 --- a/crates/console/src/lib.rs +++ b/crates/console/src/lib.rs @@ -1,11 +1,11 @@ use tokio::sync::mpsc; #[derive(Clone)] -pub struct Out { +pub struct Console { tx: mpsc::UnboundedSender, } -impl Out { +impl Console { /// Output goes to stdout. pub fn stdout() -> Self { let (tx, mut rx) = mpsc::unbounded_channel(); From d55ff1bf7b3789cf16edb28288bc5ba3b0ad2713 Mon Sep 17 00:00:00 2001 From: ryardley Date: Sat, 14 Mar 2026 04:36:12 +0000 Subject: [PATCH 03/31] import dependency --- crates/cli/src/ciphernode/license.rs | 8 +-- crates/cli/src/ciphernode/lifecycle.rs | 30 +++++----- crates/cli/src/ciphernode/setup.rs | 22 +++---- crates/cli/src/ciphernode/tickets.rs | 6 +- crates/cli/src/cli.rs | 4 +- crates/cli/src/net_get_peer_id.rs | 4 +- crates/cli/src/noir.rs | 80 +++++++++++++------------- crates/cli/src/password_delete.rs | 8 +-- crates/cli/src/password_set.rs | 6 +- crates/cli/src/print_env.rs | 6 +- crates/cli/src/rev.rs | 4 +- crates/cli/src/wallet_get.rs | 4 +- crates/cli/src/wallet_set.rs | 4 +- 13 files changed, 93 insertions(+), 93 deletions(-) diff --git a/crates/cli/src/ciphernode/license.rs b/crates/cli/src/ciphernode/license.rs index 9b5e56f7a2..f7d8c48153 100644 --- a/crates/cli/src/ciphernode/license.rs +++ b/crates/cli/src/ciphernode/license.rs @@ -6,7 +6,7 @@ use alloy::primitives::U256; use anyhow::Result; -use e3_console::Console; +use e3_console::{log, Console}; use super::context::ChainContext; use super::utils::{ensure_allowance, parse_amount}; @@ -32,7 +32,7 @@ pub(crate) async fn execute( .await? .get_receipt() .await?; - e3_console::log!( + log!( out, "Queued {} ENCL for exit (tx: {:#x})", amount, @@ -71,7 +71,7 @@ pub(crate) async fn execute( .await? .get_receipt() .await?; - e3_console::log!(out, "Claimed exits (tx: {:#x})", receipt.transaction_hash); + log!(out, "Claimed exits (tx: {:#x})", receipt.transaction_hash); } } @@ -91,7 +91,7 @@ async fn bond_license(out: Console, ctx: &ChainContext, amount: &str) -> Result< .await? .get_receipt() .await?; - e3_console::log!( + log!( out, "Bonded {} ENCL (tx: {:#x})", amount, diff --git a/crates/cli/src/ciphernode/lifecycle.rs b/crates/cli/src/ciphernode/lifecycle.rs index 0b820c54fe..a7ab02d405 100644 --- a/crates/cli/src/ciphernode/lifecycle.rs +++ b/crates/cli/src/ciphernode/lifecycle.rs @@ -6,7 +6,7 @@ use alloy::primitives::U256; use anyhow::{bail, Result}; -use e3_console::Console; +use e3_console::{log, Console}; use super::context::ChainContext; use super::utils::{format_amount, parse_amount}; @@ -19,7 +19,7 @@ pub(crate) async fn register(out: Console, ctx: &ChainContext) -> Result<()> { .await? .get_receipt() .await?; - e3_console::log!( + log!( out, "Registered ciphernode on {} (tx: {:#x})", ctx.chain_label(), @@ -36,7 +36,7 @@ pub(crate) async fn deregister(out: Console, ctx: &ChainContext) -> Result<()> { .await? .get_receipt() .await?; - e3_console::log!( + log!( out, "Deregistration requested (tx: {:#x})", receipt.transaction_hash @@ -71,7 +71,7 @@ pub(crate) async fn deactivate( .await? .get_receipt() .await?; - e3_console::log!( + log!( out, "Removed {} tickets (tx: {:#x})", amount, @@ -90,7 +90,7 @@ pub(crate) async fn deactivate( .await? .get_receipt() .await?; - e3_console::log!( + log!( out, "Queued {} ENCL for exit (tx: {:#x})", amount, @@ -98,7 +98,7 @@ pub(crate) async fn deactivate( ); } - e3_console::log!( + log!( out, "Submitted deactivation transactions; monitor exit delays before claiming." ); @@ -126,29 +126,29 @@ pub(crate) async fn status(out: Console, ctx: &ChainContext) -> Result<()> { let ticket_decimals = ctx.erc20(ticket_token).decimals().call().await?; let license_decimals = ctx.erc20(license_token).decimals().call().await?; - e3_console::log!(out, "Ciphernode status on {}:", ctx.chain_label()); - e3_console::log!(out, " Address: {:#x}", operator); - e3_console::log!(out, " Registered: {}", is_registered); - e3_console::log!(out, " Active: {}", is_active); - e3_console::log!(out, " Exit pending: {}", has_exit); - e3_console::log!( + log!(out, "Ciphernode status on {}:", ctx.chain_label()); + log!(out, " Address: {:#x}", operator); + log!(out, " Registered: {}", is_registered); + log!(out, " Active: {}", is_active); + log!(out, " Exit pending: {}", has_exit); + log!( out, " Ticket balance: {} ({} available)", format_amount(ticket_balance, ticket_decimals), format_amount(available_tickets, ticket_decimals) ); - e3_console::log!( + log!( out, " License bond: {}", format_amount(license_bond, license_decimals) ); - e3_console::log!( + log!( out, " Pending exits: tickets={}, license={}", format_amount(pending_tickets, ticket_decimals), format_amount(pending_license, license_decimals) ); - e3_console::log!( + log!( out, " Requirements: minTickets={}, ticketPrice={} EKT, licenseBond={} ENCL", format_amount(min_ticket_balance, ticket_decimals), diff --git a/crates/cli/src/ciphernode/setup.rs b/crates/cli/src/ciphernode/setup.rs index 8be361ec10..a35f893af3 100644 --- a/crates/cli/src/ciphernode/setup.rs +++ b/crates/cli/src/ciphernode/setup.rs @@ -8,7 +8,7 @@ use alloy::primitives::Address; use anyhow::Result; use dialoguer::{theme::ColorfulTheme, Input}; use e3_config::AppConfig; -use e3_console::Console; +use e3_console::{log, Console}; use e3_entrypoint::config::setup; use e3_utils::{colorize, Color}; use std::path::PathBuf; @@ -79,20 +79,20 @@ fn print_info( ) -> Result<()> { let abs_config = config.config_file().canonicalize()?; - e3_console::log!(out, "\nEnclave configuration successfully created!"); - e3_console::log!( + log!(out, "\nEnclave configuration successfully created!"); + log!( out, "Editable configuration has been written to:\n\n {}", colorize(abs_config.to_string_lossy(), Color::Yellow) ); - e3_console::log!(out, ""); - e3_console::log!(out, "Data written:"); - e3_console::log!(out, " address: {}", colorize(address, Color::Cyan)); - e3_console::log!(out, " peer_id: {}", colorize(peer_id, Color::Cyan)); - e3_console::log!(out, " rpc_url: {}", colorize(rpc_url, Color::Cyan)); - e3_console::log!(out, ""); + log!(out, ""); + log!(out, "Data written:"); + log!(out, " address: {}", colorize(address, Color::Cyan)); + log!(out, " peer_id: {}", colorize(peer_id, Color::Cyan)); + log!(out, " rpc_url: {}", colorize(rpc_url, Color::Cyan)); + log!(out, ""); if config.using_custom_config() { - e3_console::log!( + log!( out, "Run future commands from within this directory tree, or pass\n {}\n", colorize( @@ -101,7 +101,7 @@ fn print_info( ) ); } - e3_console::log!( + log!( out, "You can start your node using:\n `{}`\n", colorize("enclave start", Color::Yellow) diff --git a/crates/cli/src/ciphernode/tickets.rs b/crates/cli/src/ciphernode/tickets.rs index cc5ea81bc4..55fbbd2fef 100644 --- a/crates/cli/src/ciphernode/tickets.rs +++ b/crates/cli/src/ciphernode/tickets.rs @@ -5,7 +5,7 @@ // or FITNESS FOR A PARTICULAR PURPOSE. use anyhow::Result; -use e3_console::Console; +use e3_console::{log, Console}; use super::context::ChainContext; use super::utils::{ensure_allowance, parse_amount}; @@ -31,7 +31,7 @@ pub(crate) async fn execute( .await? .get_receipt() .await?; - e3_console::log!( + log!( out, "Purchased {} tickets (tx: {:#x})", amount, @@ -49,7 +49,7 @@ pub(crate) async fn execute( .await? .get_receipt() .await?; - e3_console::log!( + log!( out, "Removed {} tickets (tx: {:#x})", amount, diff --git a/crates/cli/src/cli.rs b/crates/cli/src/cli.rs index d4820adb63..bfdb6c4222 100644 --- a/crates/cli/src/cli.rs +++ b/crates/cli/src/cli.rs @@ -20,7 +20,7 @@ use anyhow::{bail, Result}; use clap::{command, ArgAction, Parser, Subcommand}; use e3_config::validation::ValidUrl; use e3_config::{load_config, AppConfig}; -use e3_console::Console; +use e3_console::{log, Console}; use e3_entrypoint::helpers::datastore::close_all_connections; use tracing::{info, instrument, Level}; @@ -116,7 +116,7 @@ impl Cli { .await?; } Commands::Start { .. } => { - e3_console::log!(out,"No configuration found. Setting up enclave configuration..."); + log!(out,"No configuration found. Setting up enclave configuration..."); ciphernode::setup::execute( out, None, diff --git a/crates/cli/src/net_get_peer_id.rs b/crates/cli/src/net_get_peer_id.rs index 71906f9217..e486e1d122 100644 --- a/crates/cli/src/net_get_peer_id.rs +++ b/crates/cli/src/net_get_peer_id.rs @@ -6,10 +6,10 @@ use anyhow::Result; use e3_config::AppConfig; -use e3_console::Console; +use e3_console::{log, Console}; pub async fn execute(out: &Console, config: &AppConfig) -> Result<()> { let peer_id = e3_entrypoint::net::get_peer_id::execute(config).await?; - e3_console::log!(out, "{}", peer_id); + log!(out, "{}", peer_id); Ok(()) } diff --git a/crates/cli/src/noir.rs b/crates/cli/src/noir.rs index 7f9b3069d4..f0dfa80f73 100644 --- a/crates/cli/src/noir.rs +++ b/crates/cli/src/noir.rs @@ -7,7 +7,7 @@ use anyhow::*; use clap::Subcommand; use e3_config::AppConfig; -use e3_console::Console; +use e3_console::{log, Console}; use e3_zk_prover::{SetupStatus, ZkBackend}; #[derive(Subcommand, Debug)] @@ -54,67 +54,67 @@ async fn execute_status(out: Console, backend: &ZkBackend) -> Result<()> { let status = backend.check_status().await; let version_info = backend.load_version_info().await; - e3_console::log!(out, "=== ZK Prover Status ===\n"); + log!(out, "=== ZK Prover Status ===\n"); - e3_console::log!(out, "Barretenberg (bb):"); - e3_console::log!(out, " Path: {}", backend.bb_binary.display()); + log!(out, "Barretenberg (bb):"); + log!(out, " Path: {}", backend.bb_binary.display()); if let Some(ref v) = version_info.bb_version { - e3_console::log!(out, " Version: {}", v); + log!(out, " Version: {}", v); } if backend.bb_binary.exists() { - e3_console::log!(out, " Installed"); + log!(out, " Installed"); } else { - e3_console::log!(out, " Not installed"); + log!(out, " Not installed"); } - e3_console::log!(out, ""); + log!(out, ""); - e3_console::log!(out, "Circuits:"); - e3_console::log!(out, " Path: {}", backend.circuits_dir.display()); + log!(out, "Circuits:"); + log!(out, " Path: {}", backend.circuits_dir.display()); if let Some(ref v) = version_info.circuits_version { - e3_console::log!(out, " Version: {}", v); + log!(out, " Version: {}", v); } if backend.circuits_dir.exists() { - e3_console::log!(out, " Installed"); + log!(out, " Installed"); } else { - e3_console::log!(out, " Not installed"); + log!(out, " Not installed"); } - e3_console::log!(out, ""); + log!(out, ""); match status { SetupStatus::Ready => { - e3_console::log!(out, "Status: Ready"); + log!(out, "Status: Ready"); } SetupStatus::BbNeedsUpdate { installed, required, } => { - e3_console::log!(out, "Status: Barretenberg needs update"); - e3_console::log!( + log!(out, "Status: Barretenberg needs update"); + log!( out, " Installed: {}", installed.as_deref().unwrap_or("not installed") ); - e3_console::log!(out, " Required: {}", required); - e3_console::log!(out, "\nRun `enclave noir setup` to update"); + log!(out, " Required: {}", required); + log!(out, "\nRun `enclave noir setup` to update"); } SetupStatus::CircuitsNeedUpdate { installed, required, } => { - e3_console::log!(out, "Status: Circuits need update"); - e3_console::log!( + log!(out, "Status: Circuits need update"); + log!( out, " Installed: {}", installed.as_deref().unwrap_or("not installed") ); - e3_console::log!(out, " Required: {}", required); - e3_console::log!(out, "\nRun `enclave noir setup` to update"); + log!(out, " Required: {}", required); + log!(out, "\nRun `enclave noir setup` to update"); } SetupStatus::FullSetupNeeded => { - e3_console::log!(out, "Status: Setup required"); - e3_console::log!(out, "\nRun `enclave noir setup` to install"); + log!(out, "Status: Setup required"); + log!(out, "\nRun `enclave noir setup` to install"); } } @@ -122,20 +122,20 @@ async fn execute_status(out: Console, backend: &ZkBackend) -> Result<()> { } async fn execute_setup(out: Console, backend: &ZkBackend, force: bool) -> Result<()> { - e3_console::log!(out, "Setting up ZK prover...\n"); - e3_console::log!( + log!(out, "Setting up ZK prover...\n"); + log!( out, " target bb version: {}", backend.config.required_bb_version ); - e3_console::log!( + log!( out, " target circuits version: {}\n", backend.config.required_circuits_version ); if force { - e3_console::log!(out, "Force reinstalling ZK prover components...\n"); + log!(out, "Force reinstalling ZK prover components...\n"); // Force reinstall by directly downloading components backend @@ -150,13 +150,13 @@ async fn execute_setup(out: Console, backend: &ZkBackend, force: bool) -> Result let status = backend.check_status().await; if matches!(status, SetupStatus::Ready) { let version_info = backend.load_version_info().await; - e3_console::log!(out, "ZK prover is already set up and up to date."); - e3_console::log!( + log!(out, "ZK prover is already set up and up to date."); + log!( out, " bb version: {}", version_info.bb_version.as_deref().unwrap_or("unknown") ); - e3_console::log!( + log!( out, " circuits version: {}", version_info @@ -164,7 +164,7 @@ async fn execute_setup(out: Console, backend: &ZkBackend, force: bool) -> Result .as_deref() .unwrap_or("unknown") ); - e3_console::log!(out, " Use --force to reinstall."); + log!(out, " Use --force to reinstall."); return Ok(()); } @@ -176,20 +176,20 @@ async fn execute_setup(out: Console, backend: &ZkBackend, force: bool) -> Result let version_info = backend.load_version_info().await; - e3_console::log!(out, "\nZK prover setup complete!"); - e3_console::log!(out, ""); - e3_console::log!(out, " bb binary: {}", backend.bb_binary.display()); - e3_console::log!( + log!(out, "\nZK prover setup complete!"); + log!(out, ""); + log!(out, " bb binary: {}", backend.bb_binary.display()); + log!( out, " bb version: {}", version_info.bb_version.as_deref().unwrap_or("unknown") ); - e3_console::log!( + log!( out, " circuits dir: {}", backend.circuits_dir.display() ); - e3_console::log!( + log!( out, " circuits version: {}", version_info @@ -198,7 +198,7 @@ async fn execute_setup(out: Console, backend: &ZkBackend, force: bool) -> Result .unwrap_or("unknown") ); if let Some(ref ts) = version_info.last_updated { - e3_console::log!(out, " last updated: {}", ts); + log!(out, " last updated: {}", ts); } Ok(()) diff --git a/crates/cli/src/password_delete.rs b/crates/cli/src/password_delete.rs index 865f475975..d6792d73d3 100644 --- a/crates/cli/src/password_delete.rs +++ b/crates/cli/src/password_delete.rs @@ -8,7 +8,7 @@ use crate::helpers::prompt_password::prompt_password; use anyhow::Result; use dialoguer::{theme::ColorfulTheme, Confirm}; use e3_config::AppConfig; -use e3_console::Console; +use e3_console::{log, Console}; use zeroize::Zeroize; pub async fn prompt_delete(out: Console, config: &AppConfig) -> Result { @@ -21,7 +21,7 @@ pub async fn prompt_delete(out: Console, config: &AppConfig) -> Result { } let Ok(mut cur_pw) = e3_entrypoint::password::delete::get_current_password(config).await else { - e3_console::log!(out, "Password is not set. Nothing to do."); + log!(out, "Password is not set. Nothing to do."); return Ok(false); }; @@ -39,9 +39,9 @@ pub async fn prompt_delete(out: Console, config: &AppConfig) -> Result { pub async fn execute(out: &Console, config: &AppConfig) -> Result<()> { if prompt_delete(out.clone(), config).await? { e3_entrypoint::password::delete::execute(config).await?; - e3_console::log!(out, "Password successfully deleted."); + log!(out, "Password successfully deleted."); } else { - e3_console::log!(out, "Operation cancelled."); + log!(out, "Operation cancelled."); } Ok(()) } diff --git a/crates/cli/src/password_set.rs b/crates/cli/src/password_set.rs index b05854d4cf..755d402441 100644 --- a/crates/cli/src/password_set.rs +++ b/crates/cli/src/password_set.rs @@ -6,7 +6,7 @@ use anyhow::{bail, Result}; use e3_config::AppConfig; -use e3_console::Console; +use e3_console::{log, Console}; use zeroize::{Zeroize, Zeroizing}; use crate::helpers::prompt_password::prompt_password; @@ -49,14 +49,14 @@ pub async fn execute( config: &AppConfig, input: Option>, ) -> Result<()> { - e3_console::log!(out, "Setting password..."); + log!(out, "Setting password..."); e3_entrypoint::password::set::preflight(config).await?; let pw = ask_for_password(input)?; e3_entrypoint::password::set::execute(config, pw).await?; - e3_console::log!(out, "Password successfully set."); + log!(out, "Password successfully set."); Ok(()) } diff --git a/crates/cli/src/print_env.rs b/crates/cli/src/print_env.rs index befe046311..3bd3f95633 100644 --- a/crates/cli/src/print_env.rs +++ b/crates/cli/src/print_env.rs @@ -6,7 +6,7 @@ use anyhow::Result; use e3_config::AppConfig; -use e3_console::Console; +use e3_console::{log, Console}; pub fn extract_env_vars_vite(config: &AppConfig, chain: &str) -> String { let mut env_vars = Vec::new(); @@ -73,9 +73,9 @@ pub fn extract_env_vars(config: &AppConfig, chain: &str) -> String { } pub async fn execute(out: Console, config: &AppConfig, chain: &str, as_vite: bool) -> Result<()> { if as_vite { - e3_console::log!(out, "{}", extract_env_vars_vite(config, chain)); + log!(out, "{}", extract_env_vars_vite(config, chain)); } else { - e3_console::log!(out, "{}", extract_env_vars(config, chain)); + log!(out, "{}", extract_env_vars(config, chain)); } Ok(()) } diff --git a/crates/cli/src/rev.rs b/crates/cli/src/rev.rs index 11c553bda6..b44d302958 100644 --- a/crates/cli/src/rev.rs +++ b/crates/cli/src/rev.rs @@ -4,11 +4,11 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -use e3_console::Console; +use e3_console::{log, Console}; pub const GIT_SHA: &str = env!("GIT_SHA"); pub async fn execute(out: Console) -> anyhow::Result<()> { - e3_console::log!(out, "{}", GIT_SHA); + log!(out, "{}", GIT_SHA); Ok(()) } diff --git a/crates/cli/src/wallet_get.rs b/crates/cli/src/wallet_get.rs index dd456bc939..7b3d3fc218 100644 --- a/crates/cli/src/wallet_get.rs +++ b/crates/cli/src/wallet_get.rs @@ -6,11 +6,11 @@ use anyhow::Result; use e3_config::AppConfig; -use e3_console::Console; +use e3_console::{log, Console}; pub async fn execute(out: Console, config: &AppConfig) -> Result<()> { let address = e3_entrypoint::wallet::get::execute(config).await?; - e3_console::log!(out, "{}", address); + log!(out, "{}", address); Ok(()) } diff --git a/crates/cli/src/wallet_set.rs b/crates/cli/src/wallet_set.rs index fbedc5e1f2..6973b98dd3 100644 --- a/crates/cli/src/wallet_set.rs +++ b/crates/cli/src/wallet_set.rs @@ -7,7 +7,7 @@ use anyhow::Result; use dialoguer::{theme::ColorfulTheme, Password}; use e3_config::AppConfig; -use e3_console::Console; +use e3_console::{log, Console}; use e3_entrypoint::wallet::set::validate_private_key; use zeroize::Zeroizing; @@ -36,7 +36,7 @@ pub async fn execute( ) -> Result<()> { let input = ask_for_private_key(private_key)?; e3_entrypoint::wallet::set::execute(config, input).await?; - e3_console::log!( + log!( out, "Wallet key has been successfully stored and encrypted." ); From c77e22d9a38f988cb62f7491e31c387ddd44be5e Mon Sep 17 00:00:00 2001 From: ryardley Date: Sat, 14 Mar 2026 04:37:42 +0000 Subject: [PATCH 04/31] add header --- crates/console/src/lib.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/crates/console/src/lib.rs b/crates/console/src/lib.rs index 86c19f0d67..9996e939de 100644 --- a/crates/console/src/lib.rs +++ b/crates/console/src/lib.rs @@ -1,3 +1,9 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + use tokio::sync::mpsc; #[derive(Clone)] From e524cfed34168a0b6f47bcbed10ea0211be3d43a Mon Sep 17 00:00:00 2001 From: ryardley Date: Sat, 14 Mar 2026 05:26:33 +0000 Subject: [PATCH 05/31] add type conversion --- crates/cli/src/cli.rs | 94 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 93 insertions(+), 1 deletion(-) diff --git a/crates/cli/src/cli.rs b/crates/cli/src/cli.rs index bfdb6c4222..9b9bf1756c 100644 --- a/crates/cli/src/cli.rs +++ b/crates/cli/src/cli.rs @@ -6,7 +6,7 @@ use std::path::PathBuf; -use crate::ciphernode::{self, CiphernodeCommands}; +use crate::ciphernode::{self, ChainArgs, CiphernodeCommands}; use crate::helpers::telemetry::{setup_simple_tracing, setup_tracing}; use crate::net::{self, NetCommands}; use crate::nodes::{self, NodeCommands}; @@ -297,3 +297,95 @@ pub enum Commands { command: NetCommands, }, } + +pub struct SocketCli { + name: Option, + otel: Option, + quiet: bool, + config: Option, + verbose: u8, + command: SocketCommand, +} + +pub enum SocketCommand { + NetGetPeerId, + NodePs, + NodeStatus { id: String }, + CiphernodeStatus { chain: ChainArgs }, + NoirStatus, + WalletGet, + Rev, + PrintEnv { vite: bool, chain: String }, +} + +impl TryFrom for SocketCommand { + type Error = anyhow::Error; + + fn try_from(value: Commands) -> std::result::Result { + match value { + Commands::Rev => Ok(SocketCommand::Rev), + Commands::Net { + command: NetCommands::GetPeerId, + } => Ok(SocketCommand::NetGetPeerId), + Commands::Noir { + command: NoirCommands::Status, + } => Ok(SocketCommand::NoirStatus), + Commands::Nodes { + command: NodeCommands::Ps, + } => Ok(SocketCommand::NodePs), + Commands::Nodes { + command: NodeCommands::Status { id }, + } => Ok(SocketCommand::NodeStatus { id }), + Commands::Ciphernode { + command: CiphernodeCommands::Status { chain }, + } => Ok(SocketCommand::CiphernodeStatus { chain }), + Commands::PrintEnv { chain, vite } => Ok(SocketCommand::PrintEnv { vite, chain }), + Commands::Wallet { + command: WalletCommands::Get, + } => Ok(SocketCommand::WalletGet), + _ => bail!("Command not allowed while node is running."), + } + } +} + +impl TryFrom for Cli { + type Error = anyhow::Error; + fn try_from(value: SocketCli) -> std::result::Result { + Ok(Cli { + verbose: value.verbose, + config: value.config, + quiet: value.quiet, + otel: value.otel, + command: value.command.try_into()?, + name: value.name, + }) + } +} + +impl TryFrom for Commands { + type Error = anyhow::Error; + fn try_from(value: SocketCommand) -> std::result::Result { + let command = match value { + SocketCommand::Rev => Commands::Rev, + SocketCommand::WalletGet => Commands::Wallet { + command: WalletCommands::Get, + }, + SocketCommand::PrintEnv { vite, chain } => Commands::PrintEnv { vite, chain }, + SocketCommand::CiphernodeStatus { chain } => Commands::Ciphernode { + command: CiphernodeCommands::Status { chain }, + }, + SocketCommand::NodeStatus { id } => Commands::Nodes { + command: NodeCommands::Status { id }, + }, + SocketCommand::NodePs => Commands::Nodes { + command: NodeCommands::Ps, + }, + SocketCommand::NetGetPeerId => Commands::Net { + command: NetCommands::GetPeerId, + }, + _ => bail!("Command not allowed while node is running"), + }; + // We might have to hold this stuff on SocketCommand + Ok(command) + } +} From d920b3da4a7fe18311a620e8d2c53e53f44de426 Mon Sep 17 00:00:00 2001 From: ryardley Date: Sun, 15 Mar 2026 01:45:31 +0000 Subject: [PATCH 06/31] Socket -> Remote --- crates/cli/src/cli.rs | 48 +++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/crates/cli/src/cli.rs b/crates/cli/src/cli.rs index 9b9bf1756c..9ec147d6a5 100644 --- a/crates/cli/src/cli.rs +++ b/crates/cli/src/cli.rs @@ -298,16 +298,16 @@ pub enum Commands { }, } -pub struct SocketCli { +pub struct RemoteCli { name: Option, otel: Option, quiet: bool, config: Option, verbose: u8, - command: SocketCommand, + command: RemoteCommand, } -pub enum SocketCommand { +pub enum RemoteCommand { NetGetPeerId, NodePs, NodeStatus { id: String }, @@ -318,39 +318,39 @@ pub enum SocketCommand { PrintEnv { vite: bool, chain: String }, } -impl TryFrom for SocketCommand { +impl TryFrom for RemoteCommand { type Error = anyhow::Error; fn try_from(value: Commands) -> std::result::Result { match value { - Commands::Rev => Ok(SocketCommand::Rev), + Commands::Rev => Ok(RemoteCommand::Rev), Commands::Net { command: NetCommands::GetPeerId, - } => Ok(SocketCommand::NetGetPeerId), + } => Ok(RemoteCommand::NetGetPeerId), Commands::Noir { command: NoirCommands::Status, - } => Ok(SocketCommand::NoirStatus), + } => Ok(RemoteCommand::NoirStatus), Commands::Nodes { command: NodeCommands::Ps, - } => Ok(SocketCommand::NodePs), + } => Ok(RemoteCommand::NodePs), Commands::Nodes { command: NodeCommands::Status { id }, - } => Ok(SocketCommand::NodeStatus { id }), + } => Ok(RemoteCommand::NodeStatus { id }), Commands::Ciphernode { command: CiphernodeCommands::Status { chain }, - } => Ok(SocketCommand::CiphernodeStatus { chain }), - Commands::PrintEnv { chain, vite } => Ok(SocketCommand::PrintEnv { vite, chain }), + } => Ok(RemoteCommand::CiphernodeStatus { chain }), + Commands::PrintEnv { chain, vite } => Ok(RemoteCommand::PrintEnv { vite, chain }), Commands::Wallet { command: WalletCommands::Get, - } => Ok(SocketCommand::WalletGet), + } => Ok(RemoteCommand::WalletGet), _ => bail!("Command not allowed while node is running."), } } } -impl TryFrom for Cli { +impl TryFrom for Cli { type Error = anyhow::Error; - fn try_from(value: SocketCli) -> std::result::Result { + fn try_from(value: RemoteCli) -> std::result::Result { Ok(Cli { verbose: value.verbose, config: value.config, @@ -362,30 +362,30 @@ impl TryFrom for Cli { } } -impl TryFrom for Commands { +impl TryFrom for Commands { type Error = anyhow::Error; - fn try_from(value: SocketCommand) -> std::result::Result { + fn try_from(value: RemoteCommand) -> std::result::Result { let command = match value { - SocketCommand::Rev => Commands::Rev, - SocketCommand::WalletGet => Commands::Wallet { + RemoteCommand::Rev => Commands::Rev, + RemoteCommand::WalletGet => Commands::Wallet { command: WalletCommands::Get, }, - SocketCommand::PrintEnv { vite, chain } => Commands::PrintEnv { vite, chain }, - SocketCommand::CiphernodeStatus { chain } => Commands::Ciphernode { + RemoteCommand::PrintEnv { vite, chain } => Commands::PrintEnv { vite, chain }, + RemoteCommand::CiphernodeStatus { chain } => Commands::Ciphernode { command: CiphernodeCommands::Status { chain }, }, - SocketCommand::NodeStatus { id } => Commands::Nodes { + RemoteCommand::NodeStatus { id } => Commands::Nodes { command: NodeCommands::Status { id }, }, - SocketCommand::NodePs => Commands::Nodes { + RemoteCommand::NodePs => Commands::Nodes { command: NodeCommands::Ps, }, - SocketCommand::NetGetPeerId => Commands::Net { + RemoteCommand::NetGetPeerId => Commands::Net { command: NetCommands::GetPeerId, }, _ => bail!("Command not allowed while node is running"), }; - // We might have to hold this stuff on SocketCommand + // We might have to hold this stuff on RemoteCommand Ok(command) } } From a3f5609fdb27ce05480197a18841c86441ec613a Mon Sep 17 00:00:00 2001 From: ryardley Date: Tue, 17 Mar 2026 10:10:35 +0000 Subject: [PATCH 07/31] run on socket server if available --- Cargo.lock | 12 +++++++++++ Cargo.toml | 11 +++++++---- crates/cli/Cargo.toml | 1 + crates/cli/src/ciphernode/mod.rs | 3 ++- crates/cli/src/cli.rs | 25 +++++++++++++++++++---- crates/cli/src/main.rs | 20 +++++++++++++++---- crates/config/src/validation.rs | 6 ++++++ crates/socket-server/Cargo.toml | 14 +++++++++++++ crates/socket-server/src/lib.rs | 34 ++++++++++++++++++++++++++++++++ 9 files changed, 113 insertions(+), 13 deletions(-) create mode 100644 crates/socket-server/Cargo.toml create mode 100644 crates/socket-server/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index c4549786b8..f7d71fc9c4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3132,6 +3132,7 @@ dependencies = [ "e3-events", "e3-evm", "e3-init", + "e3-socket-server", "e3-support-scripts", "e3-utils", "e3-zk-prover", @@ -3641,6 +3642,17 @@ dependencies = [ "e3-indexer", ] +[[package]] +name = "e3-socket-server" +version = "0.1.15" +dependencies = [ + "anyhow", + "e3-console", + "serde", + "serde_json", + "tokio", +] + [[package]] name = "e3-sortition" version = "0.1.15" diff --git a/Cargo.toml b/Cargo.toml index 6725ea5eab..f32c173941 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ members = [ "crates/cli", "crates/compute-provider", "crates/config", + "crates/console", "crates/crypto", "crates/data", "crates/enclaveup", @@ -23,11 +24,13 @@ members = [ "crates/logger", "crates/multithread", "crates/net", - "crates/zk-prover", + "crates/parity-matrix", + "crates/polynomial", "crates/program-server", "crates/request", "crates/safe", "crates/sdk", + "crates/socket-server", "crates/sortition", "crates/support-scripts", "crates/sync", @@ -36,9 +39,8 @@ members = [ "crates/trbfv", "crates/utils", "crates/wasm", - "crates/parity-matrix", - "crates/polynomial", - "crates/zk-helpers", "crates/console", + "crates/zk-helpers", + "crates/zk-prover", ] exclude = [ "examples/CRISP", @@ -97,6 +99,7 @@ e3-logger = { version = "0.1.15", path = "./crates/logger" } e3-net = { version = "0.1.15", path = "./crates/net" } e3-compute-provider = { version = "0.1.15", path = "./crates/compute-provider" } e3-sortition = { version = "0.1.15", path = "./crates/sortition" } +e3-socket-server = { version = "0.1.15", path = "./crates/socket-server" } e3-program-server = { version = "0.1.15", path = "./crates/program-server" } e3-polynomial = { version = "0.1.15", path = "./crates/polynomial" } e3-support-scripts = { version = "0.1.15", path = "./crates/support-scripts" } diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index 0a30690215..a821bcc34a 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -27,6 +27,7 @@ e3-events = { workspace = true } e3-evm = { workspace = true } e3-init = { workspace = true } e3-support-scripts = { workspace = true } +e3-socket-server = { workspace = true } e3-utils = { workspace = true } e3-zk-prover = { workspace = true } hex = { workspace = true } diff --git a/crates/cli/src/ciphernode/mod.rs b/crates/cli/src/ciphernode/mod.rs index ccc04dcbd4..262964c6cc 100644 --- a/crates/cli/src/ciphernode/mod.rs +++ b/crates/cli/src/ciphernode/mod.rs @@ -17,11 +17,12 @@ mod utils; use context::ChainContext; use e3_console::Console; +use serde::{Deserialize, Serialize}; use zeroize::Zeroizing; use crate::helpers::{ensure_hex_zeroizing, parse_zeroizing}; -#[derive(Debug, Args, Clone, Default)] +#[derive(Debug, Args, Clone, Default, Serialize, Deserialize)] pub struct ChainArgs { /// Chain name as defined in the enclave config (defaults to the first entry) #[arg(long = "chain")] diff --git a/crates/cli/src/cli.rs b/crates/cli/src/cli.rs index 9ec147d6a5..50e3445ec0 100644 --- a/crates/cli/src/cli.rs +++ b/crates/cli/src/cli.rs @@ -4,8 +4,6 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -use std::path::PathBuf; - use crate::ciphernode::{self, ChainArgs, CiphernodeCommands}; use crate::helpers::telemetry::{setup_simple_tracing, setup_tracing}; use crate::net::{self, NetCommands}; @@ -22,6 +20,9 @@ use e3_config::validation::ValidUrl; use e3_config::{load_config, AppConfig}; use e3_console::{log, Console}; use e3_entrypoint::helpers::datastore::close_all_connections; +use serde::{Deserialize, Serialize}; +use std::path::PathBuf; +use std::str::FromStr; use tracing::{info, instrument, Level}; #[derive(Parser, Debug)] @@ -298,15 +299,17 @@ pub enum Commands { }, } +#[derive(Serialize, Deserialize)] pub struct RemoteCli { name: Option, - otel: Option, + otel: Option, quiet: bool, config: Option, verbose: u8, command: RemoteCommand, } +#[derive(Serialize, Deserialize)] pub enum RemoteCommand { NetGetPeerId, NodePs, @@ -348,6 +351,20 @@ impl TryFrom for RemoteCommand { } } +impl TryFrom for RemoteCli { + type Error = anyhow::Error; + fn try_from(value: Cli) -> Result { + Ok(RemoteCli { + otel: value.otel.map(|o| o.to_string()), + verbose: value.verbose, + config: value.config, + name: value.name, + quiet: value.quiet, + command: value.command.try_into()?, + }) + } +} + impl TryFrom for Cli { type Error = anyhow::Error; fn try_from(value: RemoteCli) -> std::result::Result { @@ -355,7 +372,7 @@ impl TryFrom for Cli { verbose: value.verbose, config: value.config, quiet: value.quiet, - otel: value.otel, + otel: value.otel.and_then(|o| ValidUrl::from_str(&o).ok()), command: value.command.try_into()?, name: value.name, }) diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index f5efffab25..b0e4b4c4b5 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -4,9 +4,11 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. +use anyhow::Result; use clap::Parser; -use cli::Cli; +use cli::{Cli, RemoteCli}; use e3_console::Console; +use e3_socket_server::{connect_socket, run_on_socket}; use e3_utils::{colorize, Color}; use tracing::info; @@ -60,12 +62,22 @@ pub fn owo() { } #[actix::main] -pub async fn main() { +pub async fn main() -> Result<()> { info!("COMPILATION ID: '{}'", helpers::compile_id::generate_id()); let out = Console::stdout(); - // Execute the cli - if let Err(err) = Cli::parse().execute(out).await { + let cli = Cli::parse(); + + // If the socket exists + if let Err(err) = if let Some(stream) = connect_socket().await { + let cli: RemoteCli = cli.try_into()?; + // Run the command over the socket + run_on_socket(out, stream, cli).await + } else { + // Run the command locally + cli.execute(out).await + } { eprintln!("{}", colorize(err, Color::Red)); std::process::exit(1); } + Ok(()) } diff --git a/crates/config/src/validation.rs b/crates/config/src/validation.rs index c6a4f0369b..8c6246881a 100644 --- a/crates/config/src/validation.rs +++ b/crates/config/src/validation.rs @@ -18,6 +18,12 @@ impl FromStr for ValidUrl { } } +impl ToString for ValidUrl { + fn to_string(&self) -> String { + self.0.to_string() + } +} + impl From for String { fn from(value: ValidUrl) -> Self { value.0.to_string() diff --git a/crates/socket-server/Cargo.toml b/crates/socket-server/Cargo.toml new file mode 100644 index 0000000000..f622848104 --- /dev/null +++ b/crates/socket-server/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "e3-socket-server" +version.workspace = true +edition.workspace = true +license.workspace = true +description.workspace = true +repository.workspace = true + +[dependencies] +tokio.workspace = true +anyhow.workspace = true +serde_json.workspace = true +serde.workspace = true +e3-console.workspace = true diff --git a/crates/socket-server/src/lib.rs b/crates/socket-server/src/lib.rs new file mode 100644 index 0000000000..09c281142b --- /dev/null +++ b/crates/socket-server/src/lib.rs @@ -0,0 +1,34 @@ +use e3_console::{log, Console}; +use std::path::Path; +use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader}; + +use serde::Serialize; +use tokio::net::UnixStream; + +const SOCKET_PATH: &str = "/tmp/enclave.sock"; + +pub async fn connect_socket() -> Option { + if !Path::new(SOCKET_PATH).exists() { + return None; + } + UnixStream::connect(SOCKET_PATH).await.ok() +} + +pub async fn run_on_socket( + out: Console, + stream: UnixStream, + cli: T, +) -> anyhow::Result<()> { + let (reader, mut writer) = stream.into_split(); + let payload = serde_json::to_string(&cli)?; + writer.write_all(payload.as_bytes()).await?; + writer.write_all(b"\n").await?; + writer.shutdown().await?; + + let mut lines = BufReader::new(reader).lines(); + while let Some(line) = lines.next_line().await? { + log!(out, "{}", line); + } + + Ok(()) +} From e442b5b5b7ad26b7cc660b19735c15ba3d25ae7f Mon Sep 17 00:00:00 2001 From: ryardley Date: Tue, 17 Mar 2026 10:11:57 +0000 Subject: [PATCH 08/31] add header --- crates/socket-server/src/lib.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/crates/socket-server/src/lib.rs b/crates/socket-server/src/lib.rs index 09c281142b..2eb4fe06e9 100644 --- a/crates/socket-server/src/lib.rs +++ b/crates/socket-server/src/lib.rs @@ -1,3 +1,9 @@ +// SPDX-License-Identifier: LGPL-2.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + use e3_console::{log, Console}; use std::path::Path; use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader}; From 35a6bd58ca1bc5fe6b08d3808bbe0ebf3959f406 Mon Sep 17 00:00:00 2001 From: ryardley Date: Wed, 18 Mar 2026 05:35:01 +0000 Subject: [PATCH 09/31] socket server working --- Cargo.lock | 4 + .../src/ciphernode_builder.rs | 13 ++- crates/ciphernode-builder/src/event_system.rs | 16 +++ .../src/global_store_cache.rs | 18 +++ crates/ciphernode-builder/src/lib.rs | 1 + crates/cli/Cargo.toml | 2 + crates/cli/src/ciphernode/mod.rs | 4 +- crates/cli/src/cli.rs | 34 +++--- crates/cli/src/helpers/telemetry.rs | 9 +- crates/cli/src/main.rs | 5 +- crates/cli/src/start.rs | 108 ++++++++++++++++-- crates/console/src/lib.rs | 48 +++++++- crates/entrypoint/Cargo.toml | 1 + crates/entrypoint/src/helpers/datastore.rs | 10 +- crates/entrypoint/src/helpers/mod.rs | 2 - crates/entrypoint/src/helpers/shutdown.rs | 26 ----- .../entrypoint/src/start/aggregator_start.rs | 2 +- crates/entrypoint/src/start/start.rs | 2 +- crates/socket-server/Cargo.toml | 1 + crates/socket-server/src/lib.rs | 40 ++++++- 20 files changed, 273 insertions(+), 73 deletions(-) create mode 100644 crates/ciphernode-builder/src/global_store_cache.rs delete mode 100644 crates/entrypoint/src/helpers/shutdown.rs diff --git a/Cargo.lock b/Cargo.lock index f7d71fc9c4..7517148159 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3125,6 +3125,7 @@ dependencies = [ "compile-time", "dialoguer", "dirs 5.0.1", + "e3-ciphernode-builder", "e3-config", "e3-console", "e3-crypto", @@ -3144,6 +3145,7 @@ dependencies = [ "petname", "rand 0.8.5", "serde", + "serde_json", "tokio", "toml 0.8.23", "tracing", @@ -3261,6 +3263,7 @@ dependencies = [ "e3-logger", "e3-net", "e3-request", + "e3-socket-server", "e3-sortition", "e3-test-helpers", "e3-zk-prover", @@ -3651,6 +3654,7 @@ dependencies = [ "serde", "serde_json", "tokio", + "tracing", ] [[package]] diff --git a/crates/ciphernode-builder/src/ciphernode_builder.rs b/crates/ciphernode-builder/src/ciphernode_builder.rs index 2fb75df292..3dc8061bf9 100644 --- a/crates/ciphernode-builder/src/ciphernode_builder.rs +++ b/crates/ciphernode-builder/src/ciphernode_builder.rs @@ -81,6 +81,7 @@ pub struct CiphernodeBuilder { zk_backend: Option, net_config: Option, ignore_address_check: bool, + global_shared_store: bool, } // Simple Net Configuration @@ -148,6 +149,7 @@ impl CiphernodeBuilder { net_config: None, zk_backend: None, ignore_address_check: false, + global_shared_store: false, } } @@ -311,6 +313,11 @@ impl CiphernodeBuilder { self } + pub fn with_shared_store(mut self) -> Self { + self.global_shared_store = true; + self + } + /// Setup net package components. pub fn with_net(mut self, peers: Vec, quic_port: u16) -> Self { self.net_config = Some(NetConfig::new(peers, quic_port)); @@ -388,18 +395,20 @@ impl CiphernodeBuilder { EventSystem::persisted(log_path, kv_path) .with_event_bus(local_bus) .with_aggregate_config(aggregate_config.clone()) + .with_global_shared_store(self.global_shared_store) } else { if let Some(ref store) = self.in_mem_store { EventSystem::in_mem_from_store(store) .with_event_bus(local_bus) .with_aggregate_config(aggregate_config.clone()) + .with_global_shared_store(self.global_shared_store) } else { EventSystem::in_mem() .with_event_bus(local_bus) .with_aggregate_config(aggregate_config.clone()) + .with_global_shared_store(self.global_shared_store) } }; - let store = event_system.store()?; let eventstore_ts = event_system.eventstore_getter_ts()?; let eventstore_seq = event_system.eventstore_getter_seq()?; @@ -456,6 +465,8 @@ impl CiphernodeBuilder { .as_ref() .ok_or_else(|| anyhow::anyhow!("ZK backend is required for threshold keyshare"))?; + backend.ensure_installed().await?; + // Ensure signer is available before setting up extensions that need it let signer = provider_cache.ensure_signer().await?; diff --git a/crates/ciphernode-builder/src/event_system.rs b/crates/ciphernode-builder/src/event_system.rs index 055030f417..4f7244236a 100644 --- a/crates/ciphernode-builder/src/event_system.rs +++ b/crates/ciphernode-builder/src/event_system.rs @@ -5,6 +5,7 @@ // or FITNESS FOR A PARTICULAR PURPOSE. use crate::get_enclave_event_bus; +use crate::global_store_cache::share_store; use actix::{Actor, Addr, Handler, Recipient}; use anyhow::{anyhow, Result}; use e3_data::{ @@ -87,6 +88,8 @@ pub struct EventSystem { aggregate_config: OnceCell, /// Cached EventStoreAddrs for idempotency eventstore_addrs: OnceCell, + /// Global shared store + global_shared_store: bool, } impl EventSystem { @@ -108,6 +111,7 @@ impl EventSystem { handle: OnceCell::new(), aggregate_config: OnceCell::new(), eventstore_addrs: OnceCell::new(), + global_shared_store: false, } } @@ -124,6 +128,7 @@ impl EventSystem { handle: OnceCell::new(), aggregate_config: OnceCell::new(), eventstore_addrs: OnceCell::new(), + global_shared_store: false, } } @@ -142,6 +147,7 @@ impl EventSystem { handle: OnceCell::new(), aggregate_config: OnceCell::new(), eventstore_addrs: OnceCell::new(), + global_shared_store: false, } } @@ -165,6 +171,12 @@ impl EventSystem { self } + /// Share the store with other processes + pub fn with_global_shared_store(mut self, value: bool) -> Self { + self.global_shared_store = value; + self + } + /// Get the eventbus address pub fn eventbus(&self) -> Addr> { self.eventbus.get_or_init(get_enclave_event_bus).clone() @@ -337,6 +349,10 @@ impl EventSystem { } }; + if self.global_shared_store { + share_store(&store) + } + Ok(store) } } diff --git a/crates/ciphernode-builder/src/global_store_cache.rs b/crates/ciphernode-builder/src/global_store_cache.rs new file mode 100644 index 0000000000..b9b4ce9256 --- /dev/null +++ b/crates/ciphernode-builder/src/global_store_cache.rs @@ -0,0 +1,18 @@ +use std::sync::OnceLock; + +use e3_data::DataStore; + +// Hold shared data store - this is for production only - for testing we can create new stores per +// routine +static CACHED_STORE: OnceLock = OnceLock::new(); + +/// Save the store to a cache for use by socket commands. This solves the problem of reusing a +/// database connection while the node is running in start mode. We can use this during node start. +/// Only the first call to this is satisfied. +pub fn share_store(store: &DataStore) { + CACHED_STORE.get_or_init(|| store.clone()); +} + +pub fn get_cached_store() -> Option { + CACHED_STORE.get().cloned() +} diff --git a/crates/ciphernode-builder/src/lib.rs b/crates/ciphernode-builder/src/lib.rs index 70a5a3079d..0e89c2c77b 100644 --- a/crates/ciphernode-builder/src/lib.rs +++ b/crates/ciphernode-builder/src/lib.rs @@ -9,6 +9,7 @@ mod ciphernode_builder; mod event_system; mod eventbus_factory; mod evm_system; +pub mod global_store_cache; mod provider_caches; pub use ciphernode::*; pub use ciphernode_builder::*; diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index a821bcc34a..8a4492cab1 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -21,6 +21,7 @@ dialoguer = { workspace = true } dirs = { workspace = true } e3-config = { workspace = true } e3-console = { workspace = true } +e3-ciphernode-builder = { workspace = true } e3-crypto = { workspace = true } e3-entrypoint = { workspace = true } e3-events = { workspace = true } @@ -38,6 +39,7 @@ opentelemetry_sdk = { workspace = true } petname = { workspace = true } rand = { workspace = true } serde = { workspace = true } +serde_json = { workspace = true } tokio = { workspace = true } toml = { workspace = true } tracing = { workspace = true } diff --git a/crates/cli/src/ciphernode/mod.rs b/crates/cli/src/ciphernode/mod.rs index 262964c6cc..7c1a7348cb 100644 --- a/crates/cli/src/ciphernode/mod.rs +++ b/crates/cli/src/ciphernode/mod.rs @@ -44,11 +44,11 @@ pub enum CiphernodeCommands { rpc_url: Option, /// The password - #[arg(short, long, value_parser = parse_zeroizing)] + #[arg(short='p', long, value_parser = parse_zeroizing)] password: Option>, /// Wallet Private Key - #[arg(short, long, value_parser = ensure_hex_zeroizing)] + #[arg(short='k', long, value_parser = ensure_hex_zeroizing)] private_key: Option>, }, /// Manage ENCL license tokens and bonding state diff --git a/crates/cli/src/cli.rs b/crates/cli/src/cli.rs index 50e3445ec0..7d9c491e60 100644 --- a/crates/cli/src/cli.rs +++ b/crates/cli/src/cli.rs @@ -299,7 +299,7 @@ pub enum Commands { }, } -#[derive(Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] pub struct RemoteCli { name: Option, otel: Option, @@ -309,11 +309,11 @@ pub struct RemoteCli { command: RemoteCommand, } -#[derive(Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] pub enum RemoteCommand { NetGetPeerId, - NodePs, - NodeStatus { id: String }, + // NodePs, + // NodeStatus { id: String }, CiphernodeStatus { chain: ChainArgs }, NoirStatus, WalletGet, @@ -333,12 +333,12 @@ impl TryFrom for RemoteCommand { Commands::Noir { command: NoirCommands::Status, } => Ok(RemoteCommand::NoirStatus), - Commands::Nodes { - command: NodeCommands::Ps, - } => Ok(RemoteCommand::NodePs), - Commands::Nodes { - command: NodeCommands::Status { id }, - } => Ok(RemoteCommand::NodeStatus { id }), + // Commands::Nodes { + // command: NodeCommands::Ps, + // } => Ok(RemoteCommand::NodePs), + // Commands::Nodes { + // command: NodeCommands::Status { id }, + // } => Ok(RemoteCommand::NodeStatus { id }), Commands::Ciphernode { command: CiphernodeCommands::Status { chain }, } => Ok(RemoteCommand::CiphernodeStatus { chain }), @@ -391,16 +391,18 @@ impl TryFrom for Commands { RemoteCommand::CiphernodeStatus { chain } => Commands::Ciphernode { command: CiphernodeCommands::Status { chain }, }, - RemoteCommand::NodeStatus { id } => Commands::Nodes { - command: NodeCommands::Status { id }, - }, - RemoteCommand::NodePs => Commands::Nodes { - command: NodeCommands::Ps, + // RemoteCommand::NodeStatus { id } => Commands::Nodes { + // command: NodeCommands::Status { id }, + // }, + // RemoteCommand::NodePs => Commands::Nodes { + // command: NodeCommands::Ps, + // }, + RemoteCommand::NoirStatus => Commands::Noir { + command: NoirCommands::Status, }, RemoteCommand::NetGetPeerId => Commands::Net { command: NetCommands::GetPeerId, }, - _ => bail!("Command not allowed while node is running"), }; // We might have to hold this stuff on RemoteCommand Ok(command) diff --git a/crates/cli/src/helpers/telemetry.rs b/crates/cli/src/helpers/telemetry.rs index 8525db2dc0..d7e7d3ce4c 100644 --- a/crates/cli/src/helpers/telemetry.rs +++ b/crates/cli/src/helpers/telemetry.rs @@ -20,7 +20,8 @@ pub fn setup_simple_tracing(log_level: Level) { .with(tracing_subscriber::filter::LevelFilter::from_level( log_level, )) - .init(); + .try_init() + .ok(); } pub fn setup_tracing(config: &AppConfig, log_level: Level) -> Result<()> { @@ -51,7 +52,8 @@ pub fn setup_tracing(config: &AppConfig, log_level: Level) -> Result<()> { .with(tracing_subscriber::filter::LevelFilter::from_level( log_level, )) - .init(); + .try_init() + .ok(); } None => { // TODO: we might be able to dedupe this with above but there were @@ -61,7 +63,8 @@ pub fn setup_tracing(config: &AppConfig, log_level: Level) -> Result<()> { .with(tracing_subscriber::filter::LevelFilter::from_level( log_level, )) - .init(); + .try_init() + .ok(); } } diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index b0e4b4c4b5..44a25ccce2 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -64,7 +64,8 @@ pub fn owo() { #[actix::main] pub async fn main() -> Result<()> { info!("COMPILATION ID: '{}'", helpers::compile_id::generate_id()); - let out = Console::stdout(); + let handle = Console::stdout(); + let out = handle.writer(); let cli = Cli::parse(); // If the socket exists @@ -79,5 +80,7 @@ pub async fn main() -> Result<()> { eprintln!("{}", colorize(err, Color::Red)); std::process::exit(1); } + + handle.flush().await; Ok(()) } diff --git a/crates/cli/src/start.rs b/crates/cli/src/start.rs index 94d3e59410..086e5a953b 100644 --- a/crates/cli/src/start.rs +++ b/crates/cli/src/start.rs @@ -4,16 +4,80 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -use crate::owo; +use std::time::Duration; + +use crate::{ + cli::{Cli, RemoteCli}, + owo, +}; use anyhow::Result; +use e3_ciphernode_builder::CiphernodeHandle; use e3_config::{AppConfig, NodeRole}; -use e3_entrypoint::helpers::listen_for_shutdown; -use tracing::{info, instrument}; +use e3_console::Console; +use e3_events::{prelude::*, Shutdown}; +use e3_socket_server::{remove_socket, start_socket_server}; +use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader}; +use tokio::signal::unix::{signal, SignalKind}; +use tracing::{error, info, instrument}; #[instrument(skip_all)] pub async fn execute(mut config: AppConfig, peers: Vec) -> Result<()> { + // Register signal listeners immediately at startup + let shutdown = shutdown_signal(); + tokio::pin!(shutdown); + owo(); + launch_socket_server(); + + let node = tokio::select! { + // build the ciphernode and if it completes first return the result + result = build_ciphernode(&mut config, peers) => result, + // if the shutdown signal completes first then do shutdown without the node + _ = &mut shutdown => { + graceful_shutdown(None).await; + return Ok(()); + } + }?; + + info!( + "LAUNCHING CIPHERNODE: ({}/{}/{})", + config.name(), + node.address, + node.peer_id + ); + + shutdown.await; + graceful_shutdown(Some(node)).await; + + Ok(()) +} + +/// Launch a socket server to read RemoteCli commands +pub fn launch_socket_server() { + // Setup socket server for daemon + tokio::task::spawn_local(start_socket_server(|stream| async move { + let (reader, mut writer) = stream.into_split(); + let mut lines = BufReader::new(reader).lines(); + + if let Some(line) = lines.next_line().await? { + let (out, mut rx) = Console::channel(); + let remote_cli: RemoteCli = serde_json::from_str(&line)?; + let cli: Cli = remote_cli.try_into()?; + cli.execute(out).await?; + while let Some(msg) = rx.recv().await { + writer.write_all(format!("{msg}\n").as_bytes()).await?; + } + } + writer.shutdown().await?; + Ok(()) + })); +} + +pub async fn build_ciphernode( + config: &mut AppConfig, + peers: Vec, +) -> Result { // add cli peers to the config config.add_peers(peers); @@ -35,14 +99,36 @@ pub async fn execute(mut config: AppConfig, peers: Vec) -> Result<()> { NodeRole::Ciphernode => e3_entrypoint::start::start::execute(&config).await?, }; - info!( - "LAUNCHING CIPHERNODE: ({}/{}/{})", - config.name(), - node.address, - node.peer_id - ); + Ok(node) +} - tokio::spawn(listen_for_shutdown(node)).await?; +pub fn shutdown_signal() -> impl std::future::Future { + let mut sigint = + signal(SignalKind::interrupt()).expect("Failed to create SIGINT signal stream"); + let mut sigterm = + signal(SignalKind::terminate()).expect("Failed to create SIGTERM signal stream"); - Ok(()) + async move { + tokio::select! { + _ = sigint.recv() => info!("SIGINT received"), + _ = sigterm.recv() => info!("SIGTERM received"), + } + } +} + +pub async fn graceful_shutdown(node: Option) { + info!("initiating graceful shutdown..."); + + if let Some(node) = node { + if let Err(e) = node.bus.publish_without_context(Shutdown) { + error!("Shutdown failed to publish! {e}"); + } + } + + if let Err(e) = remove_socket() { + error!("FAILED TO REMOVE SOCKET! {e}"); + } + + tokio::time::sleep(Duration::from_secs(2)).await; + info!("Graceful shutdown complete"); } diff --git a/crates/console/src/lib.rs b/crates/console/src/lib.rs index 9996e939de..4cb39ac66e 100644 --- a/crates/console/src/lib.rs +++ b/crates/console/src/lib.rs @@ -4,23 +4,50 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. +use tokio::io::{AsyncWrite, AsyncWriteExt}; use tokio::sync::mpsc; +use tokio::task::JoinHandle; #[derive(Clone)] pub struct Console { tx: mpsc::UnboundedSender, } - +pub struct ConsoleHandle { + console: Console, + join: JoinHandle<()>, +} impl Console { /// Output goes to stdout. - pub fn stdout() -> Self { + pub fn stdout() -> ConsoleHandle { let (tx, mut rx) = mpsc::unbounded_channel(); - tokio::spawn(async move { + let join = tokio::spawn(async move { while let Some(msg) = rx.recv().await { println!("{msg}"); } }); - Self { tx } + ConsoleHandle { + console: Console { tx }, + join, + } + } + + pub fn writer(mut w: impl AsyncWrite + Unpin + Send + 'static) -> ConsoleHandle { + let (tx, mut rx) = mpsc::unbounded_channel::(); + let join = tokio::spawn(async move { + while let Some(msg) = rx.recv().await { + if w.write_all(msg.as_bytes()).await.is_err() { + break; + } + if w.write_all(b"\n").await.is_err() { + break; + } + } + let _ = w.flush().await; + }); + ConsoleHandle { + console: Console { tx }, + join, + } } /// Output goes to the returned receiver. Caller decides the destination. @@ -35,6 +62,19 @@ impl Console { } } +impl ConsoleHandle { + /// Get a cheap cloneable reference to pass around. + pub fn writer(&self) -> Console { + self.console.clone() + } + + /// Drop the sender and wait for the printer task to drain. + pub async fn flush(self) { + drop(self.console); + let _ = self.join.await; + } +} + #[macro_export] macro_rules! log { ($ctx:expr, $($arg:tt)*) => { diff --git a/crates/entrypoint/Cargo.toml b/crates/entrypoint/Cargo.toml index 947ecdba46..a7e090a404 100644 --- a/crates/entrypoint/Cargo.toml +++ b/crates/entrypoint/Cargo.toml @@ -23,6 +23,7 @@ dirs = { workspace = true } e3-events = { workspace = true } e3-evm = { workspace = true } e3-fhe = { workspace = true } +e3-socket-server = { workspace = true } hex = { workspace = true } e3-keyshare = { workspace = true } e3-logger = { workspace = true } diff --git a/crates/entrypoint/src/helpers/datastore.rs b/crates/entrypoint/src/helpers/datastore.rs index eea5ee5a09..6811ef7423 100644 --- a/crates/entrypoint/src/helpers/datastore.rs +++ b/crates/entrypoint/src/helpers/datastore.rs @@ -4,15 +4,15 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -use std::path::PathBuf; - use actix::Actor; use anyhow::Result; use e3_ciphernode_builder::get_enclave_bus_handle; +use e3_ciphernode_builder::global_store_cache::get_cached_store; use e3_config::AppConfig; use e3_data::{DataStore, InMemStore, SledDb, SledStore}; use e3_data::{Repositories, RepositoriesFactory}; use e3_events::{BusHandle, Disabled}; +use std::path::PathBuf; pub fn get_sled_store(bus: &BusHandle, db_file: &PathBuf) -> Result { Ok((&SledStore::new(bus, db_file)?).into()) @@ -32,6 +32,12 @@ pub fn setup_datastore(config: &AppConfig, bus: &BusHandle) -> Result< } pub fn get_repositories(config: &AppConfig) -> Result { + // If there is a cache set get it and use it + if let Some(store) = get_cached_store() { + return Ok(store.repositories()); + } + + // Setup a fresh data store let bus = get_enclave_bus_handle()?; let store = setup_datastore(config, &bus)?; Ok(store.repositories()) diff --git a/crates/entrypoint/src/helpers/mod.rs b/crates/entrypoint/src/helpers/mod.rs index ea837e2a25..30d12518ce 100644 --- a/crates/entrypoint/src/helpers/mod.rs +++ b/crates/entrypoint/src/helpers/mod.rs @@ -6,6 +6,4 @@ pub mod datastore; pub mod rand; -pub mod shutdown; pub mod termtable; -pub use shutdown::*; diff --git a/crates/entrypoint/src/helpers/shutdown.rs b/crates/entrypoint/src/helpers/shutdown.rs deleted file mode 100644 index b81f126092..0000000000 --- a/crates/entrypoint/src/helpers/shutdown.rs +++ /dev/null @@ -1,26 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -// -// This file is provided WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY -// or FITNESS FOR A PARTICULAR PURPOSE. - -use e3_ciphernode_builder::CiphernodeHandle; -use e3_events::{prelude::*, Shutdown}; -use std::time::Duration; -use tokio::signal::unix::{signal, SignalKind}; -use tracing::{error, info}; - -pub async fn listen_for_shutdown(node: CiphernodeHandle) { - let bus = node.bus; - let mut sigterm = - signal(SignalKind::terminate()).expect("Failed to create SIGTERM signal stream"); - sigterm.recv().await; - info!("SIGTERM received, initiating graceful shutdown..."); - - if let Err(e) = bus.publish_without_context(Shutdown) { - error!("Shutdown failed to publish! {e}"); - } - - tokio::time::sleep(Duration::from_secs(2)).await; - info!("Graceful shutdown complete"); -} diff --git a/crates/entrypoint/src/start/aggregator_start.rs b/crates/entrypoint/src/start/aggregator_start.rs index b628a5d810..28852e2d06 100644 --- a/crates/entrypoint/src/start/aggregator_start.rs +++ b/crates/entrypoint/src/start/aggregator_start.rs @@ -25,7 +25,6 @@ pub async fn execute( let rng = Arc::new(Mutex::new(ChaCha20Rng::from_rng(OsRng)?)); let cipher = Arc::new(Cipher::from_file(config.key_file()).await?); let backend = ZkBackend::new(config.bb_binary(), config.circuits_dir(), config.work_dir()); - backend.ensure_installed().await?; let node = CiphernodeBuilder::new(rng.clone(), cipher.clone()) .with_persistence(&config.log_file(), &config.db_file()) @@ -40,6 +39,7 @@ pub async fn execute( .with_pubkey_aggregation() .with_threshold_plaintext_aggregation() .with_net(config.peers(), config.quic_port()) + .with_shared_store() .build() .await?; diff --git a/crates/entrypoint/src/start/start.rs b/crates/entrypoint/src/start/start.rs index 497022f90d..13288e2b65 100644 --- a/crates/entrypoint/src/start/start.rs +++ b/crates/entrypoint/src/start/start.rs @@ -19,7 +19,6 @@ pub async fn execute(config: &AppConfig) -> Result { let rng = Arc::new(Mutex::new(rand_chacha::ChaCha20Rng::from_rng(OsRng)?)); let cipher = Arc::new(Cipher::from_file(&config.key_file()).await?); let backend = ZkBackend::new(config.bb_binary(), config.circuits_dir(), config.work_dir()); - backend.ensure_installed().await?; let node = CiphernodeBuilder::new(rng.clone(), cipher.clone()) .with_persistence(&config.log_file(), &config.db_file()) @@ -33,6 +32,7 @@ pub async fn execute(config: &AppConfig) -> Result { .with_trbfv() .with_zkproof(backend) .with_net(config.peers(), config.quic_port()) + .with_shared_store() .build() .await?; diff --git a/crates/socket-server/Cargo.toml b/crates/socket-server/Cargo.toml index f622848104..fda76a6df8 100644 --- a/crates/socket-server/Cargo.toml +++ b/crates/socket-server/Cargo.toml @@ -12,3 +12,4 @@ anyhow.workspace = true serde_json.workspace = true serde.workspace = true e3-console.workspace = true +tracing.workspace = true diff --git a/crates/socket-server/src/lib.rs b/crates/socket-server/src/lib.rs index 2eb4fe06e9..9c9f702ffa 100644 --- a/crates/socket-server/src/lib.rs +++ b/crates/socket-server/src/lib.rs @@ -4,14 +4,17 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. +use anyhow::Result; use e3_console::{log, Console}; +use serde::Serialize; +use std::future::Future; use std::path::Path; +use std::sync::Arc; use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader}; - -use serde::Serialize; use tokio::net::UnixStream; +use tracing::error; -const SOCKET_PATH: &str = "/tmp/enclave.sock"; +pub const SOCKET_PATH: &str = "/tmp/enclave.sock"; pub async fn connect_socket() -> Option { if !Path::new(SOCKET_PATH).exists() { @@ -38,3 +41,34 @@ pub async fn run_on_socket( Ok(()) } + +pub fn remove_socket() -> Result<()> { + std::fs::remove_file(SOCKET_PATH)?; + Ok(()) +} + +pub async fn start_socket_server(handler: F) +where + F: Fn(UnixStream) -> Fut + Send + Sync + 'static, + Fut: Future> + 'static, +{ + let _ = std::fs::remove_file(SOCKET_PATH); + let listener = match tokio::net::UnixListener::bind(SOCKET_PATH) { + Ok(l) => l, + Err(e) => { + error!("Failed to bind socket: {e}"); + return; + } + }; + + let handler = Arc::new(handler); + + while let Ok((stream, _)) = listener.accept().await { + let handler = Arc::clone(&handler); + tokio::task::spawn_local(async move { + if let Err(e) = handler(stream).await { + error!("Connection error: {e}"); + } + }); + } +} From 1cb99565d615ef2b49a15e24a1dc972b078fb26c Mon Sep 17 00:00:00 2001 From: ryardley Date: Wed, 18 Mar 2026 05:59:24 +0000 Subject: [PATCH 10/31] update license and get dockerfiles building --- crates/Dockerfile | 1 + crates/ciphernode-builder/src/global_store_cache.rs | 6 ++++++ examples/CRISP/server/Dockerfile | 1 + 3 files changed, 8 insertions(+) diff --git a/crates/Dockerfile b/crates/Dockerfile index 546dbfed2a..54f81f8187 100644 --- a/crates/Dockerfile +++ b/crates/Dockerfile @@ -75,6 +75,7 @@ COPY crates/request/Cargo.toml ./request/Cargo.toml COPY crates/safe/Cargo.toml ./safe/Cargo.toml COPY crates/sdk/Cargo.toml ./sdk/Cargo.toml COPY crates/sortition/Cargo.toml ./sortition/Cargo.toml +COPY crates/socket-server/Cargo.toml ./socket-server/Cargo.toml COPY crates/support-scripts/Cargo.toml ./support-scripts/Cargo.toml COPY crates/sync/Cargo.toml ./sync/Cargo.toml COPY crates/test-helpers/Cargo.toml ./test-helpers/Cargo.toml diff --git a/crates/ciphernode-builder/src/global_store_cache.rs b/crates/ciphernode-builder/src/global_store_cache.rs index b9b4ce9256..8fcd038260 100644 --- a/crates/ciphernode-builder/src/global_store_cache.rs +++ b/crates/ciphernode-builder/src/global_store_cache.rs @@ -1,3 +1,9 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + use std::sync::OnceLock; use e3_data::DataStore; diff --git a/examples/CRISP/server/Dockerfile b/examples/CRISP/server/Dockerfile index 31c2c3cc55..888feee3f5 100644 --- a/examples/CRISP/server/Dockerfile +++ b/examples/CRISP/server/Dockerfile @@ -85,6 +85,7 @@ COPY crates/program-server/Cargo.toml crates/program-server/Cargo.toml COPY crates/request/Cargo.toml crates/request/Cargo.toml COPY crates/sdk/Cargo.toml crates/sdk/Cargo.toml COPY crates/sortition/Cargo.toml crates/sortition/Cargo.toml +COPY crates/socket-server/Cargo.toml crates/socket-server/Cargo.toml COPY crates/support-scripts/Cargo.toml crates/support-scripts/Cargo.toml COPY crates/sync/Cargo.toml crates/sync/Cargo.toml COPY crates/test-helpers/Cargo.toml crates/test-helpers/Cargo.toml From f86e3e570046e24428ae82bfc3698590718d31f7 Mon Sep 17 00:00:00 2001 From: ryardley Date: Wed, 18 Mar 2026 09:14:43 +0000 Subject: [PATCH 11/31] add event store reader --- .../src/ciphernode_builder.rs | 16 ++++-- crates/ciphernode-builder/src/event_system.rs | 41 ++++++++++----- .../src/global_eventstore_cache.rs | 50 +++++++++++++++++++ crates/ciphernode-builder/src/lib.rs | 1 + crates/entrypoint/src/helpers/datastore.rs | 23 +++++++-- .../entrypoint/src/start/aggregator_start.rs | 1 + crates/entrypoint/src/start/start.rs | 1 + 7 files changed, 112 insertions(+), 21 deletions(-) create mode 100644 crates/ciphernode-builder/src/global_eventstore_cache.rs diff --git a/crates/ciphernode-builder/src/ciphernode_builder.rs b/crates/ciphernode-builder/src/ciphernode_builder.rs index 3dc8061bf9..210f827e71 100644 --- a/crates/ciphernode-builder/src/ciphernode_builder.rs +++ b/crates/ciphernode-builder/src/ciphernode_builder.rs @@ -82,6 +82,7 @@ pub struct CiphernodeBuilder { net_config: Option, ignore_address_check: bool, global_shared_store: bool, + global_shared_eventstore: bool, } // Simple Net Configuration @@ -150,6 +151,7 @@ impl CiphernodeBuilder { zk_backend: None, ignore_address_check: false, global_shared_store: false, + global_shared_eventstore: false, } } @@ -313,11 +315,18 @@ impl CiphernodeBuilder { self } + /// Share the store this ciphernode uses with socket server commands pub fn with_shared_store(mut self) -> Self { self.global_shared_store = true; self } + /// Share the eventstore this ciphernode uses with socket server commands + pub fn with_shared_eventstore(mut self) -> Self { + self.global_shared_eventstore = true; + self + } + /// Setup net package components. pub fn with_net(mut self, peers: Vec, quic_port: u16) -> Self { self.net_config = Some(NetConfig::new(peers, quic_port)); @@ -410,8 +419,7 @@ impl CiphernodeBuilder { } }; let store = event_system.store()?; - let eventstore_ts = event_system.eventstore_getter_ts()?; - let eventstore_seq = event_system.eventstore_getter_seq()?; + let eventstore = event_system.eventstore_reader()?; let cipher = &self.cipher; let repositories = Arc::new(store.repositories()); let mut provider_cache = @@ -550,7 +558,7 @@ impl CiphernodeBuilder { (peer_id, interface, channel_bridge) }; - setup_net(topic, bus.clone(), eventstore_ts, interface)?; + setup_net(topic, bus.clone(), eventstore.ts(), interface)?; // Run the sync routine sync( @@ -558,7 +566,7 @@ impl CiphernodeBuilder { &evm_config, &repositories, &aggregate_config, - &eventstore_seq, + &eventstore.seq(), ) .await?; diff --git a/crates/ciphernode-builder/src/event_system.rs b/crates/ciphernode-builder/src/event_system.rs index 4f7244236a..3e1eb01489 100644 --- a/crates/ciphernode-builder/src/event_system.rs +++ b/crates/ciphernode-builder/src/event_system.rs @@ -5,6 +5,7 @@ // or FITNESS FOR A PARTICULAR PURPOSE. use crate::get_enclave_event_bus; +use crate::global_eventstore_cache::{share_eventstore_reader, EventStoreReader}; use crate::global_store_cache::share_store; use actix::{Actor, Addr, Handler, Recipient}; use anyhow::{anyhow, Result}; @@ -88,8 +89,11 @@ pub struct EventSystem { aggregate_config: OnceCell, /// Cached EventStoreAddrs for idempotency eventstore_addrs: OnceCell, - /// Global shared store + /// Global shared store. This can allow commands to access the database while a node is running. global_shared_store: bool, + /// Global shared eventstore. This can allow commands to access the eventstore while a node is + /// running. + global_shared_eventstore: bool, } impl EventSystem { @@ -112,6 +116,7 @@ impl EventSystem { aggregate_config: OnceCell::new(), eventstore_addrs: OnceCell::new(), global_shared_store: false, + global_shared_eventstore: false, } } @@ -129,6 +134,7 @@ impl EventSystem { aggregate_config: OnceCell::new(), eventstore_addrs: OnceCell::new(), global_shared_store: false, + global_shared_eventstore: false, } } @@ -148,6 +154,7 @@ impl EventSystem { aggregate_config: OnceCell::new(), eventstore_addrs: OnceCell::new(), global_shared_store: false, + global_shared_eventstore: false, } } @@ -177,6 +184,12 @@ impl EventSystem { self } + /// Share the store with other processes + pub fn with_global_shared_eventstore(mut self, value: bool) -> Self { + self.global_shared_eventstore = value; + self + } + /// Get the eventbus address pub fn eventbus(&self) -> Addr> { self.eventbus.get_or_init(get_enclave_event_bus).clone() @@ -299,20 +312,24 @@ impl EventSystem { } } - pub fn eventstore_getter_seq(&self) -> Result>> { + pub fn eventstore_reader(&self) -> Result { let eventstores = self.eventstore_addrs()?; - match &eventstores { - EventStoreAddrs::InMem(_) => Ok(self.in_mem_eventstore_router()?.recipient()), - EventStoreAddrs::Persisted(_) => Ok(self.persisted_eventstore_router()?.recipient()), - } - } + let reader = match &eventstores { + EventStoreAddrs::InMem(_) => { + let router = self.in_mem_eventstore_router()?; + EventStoreReader::new(router.clone().recipient(), router.recipient()) + } + EventStoreAddrs::Persisted(_) => { + let router = self.persisted_eventstore_router()?; + EventStoreReader::new(router.clone().recipient(), router.recipient()) + } + }; - pub fn eventstore_getter_ts(&self) -> Result>> { - let eventstores = self.eventstore_addrs()?; - match &eventstores { - EventStoreAddrs::InMem(_) => Ok(self.in_mem_eventstore_router()?.recipient()), - EventStoreAddrs::Persisted(_) => Ok(self.persisted_eventstore_router()?.recipient()), + if self.global_shared_eventstore { + share_eventstore_reader(&reader); } + + Ok(reader) } /// Get the BusHandle diff --git a/crates/ciphernode-builder/src/global_eventstore_cache.rs b/crates/ciphernode-builder/src/global_eventstore_cache.rs new file mode 100644 index 0000000000..4f60f4e936 --- /dev/null +++ b/crates/ciphernode-builder/src/global_eventstore_cache.rs @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + +use std::sync::OnceLock; + +use actix::Recipient; +use e3_events::{EventStoreQueryBy, SeqAgg, TsAgg}; + +#[derive(Clone)] +pub struct EventStoreReader { + query_by_seq: Recipient>, + query_by_ts: Recipient>, +} + +impl EventStoreReader { + pub fn new( + ts: Recipient>, + seq: Recipient>, + ) -> Self { + Self { + query_by_ts: ts, + query_by_seq: seq, + } + } + + pub fn seq(&self) -> Recipient> { + self.query_by_seq.clone() + } + + pub fn ts(&self) -> Recipient> { + self.query_by_ts.clone() + } +} + +// Hold shared eventstore seq - this is a singleton for production only +static CACHED_EVENTSTORE_READER: OnceLock = OnceLock::new(); + +/// Save the eventstore to a cache for use by socket commands. This solves the problem of reusing a +/// commitlog connection while the node is running in start mode. We can use this during node start. +/// Only the first call to this is shared. +pub fn share_eventstore_reader(store: &EventStoreReader) { + CACHED_EVENTSTORE_READER.get_or_init(|| store.clone()); +} + +pub fn get_shared_eventstore() -> Option { + CACHED_EVENTSTORE_READER.get().cloned() +} diff --git a/crates/ciphernode-builder/src/lib.rs b/crates/ciphernode-builder/src/lib.rs index 0e89c2c77b..5d83b23719 100644 --- a/crates/ciphernode-builder/src/lib.rs +++ b/crates/ciphernode-builder/src/lib.rs @@ -9,6 +9,7 @@ mod ciphernode_builder; mod event_system; mod eventbus_factory; mod evm_system; +pub mod global_eventstore_cache; pub mod global_store_cache; mod provider_caches; pub use ciphernode::*; diff --git a/crates/entrypoint/src/helpers/datastore.rs b/crates/entrypoint/src/helpers/datastore.rs index 6811ef7423..03a8908ecf 100644 --- a/crates/entrypoint/src/helpers/datastore.rs +++ b/crates/entrypoint/src/helpers/datastore.rs @@ -4,14 +4,15 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -use actix::Actor; +use actix::{Actor, Recipient}; use anyhow::Result; -use e3_ciphernode_builder::get_enclave_bus_handle; +use e3_ciphernode_builder::global_eventstore_cache::{get_shared_eventstore, EventStoreReader}; use e3_ciphernode_builder::global_store_cache::get_cached_store; +use e3_ciphernode_builder::{get_enclave_bus_handle, EventSystem}; use e3_config::AppConfig; use e3_data::{DataStore, InMemStore, SledDb, SledStore}; use e3_data::{Repositories, RepositoriesFactory}; -use e3_events::{BusHandle, Disabled}; +use e3_events::{BusHandle, Disabled, EventStoreQueryBy, SeqAgg, TsAgg}; use std::path::PathBuf; pub fn get_sled_store(bus: &BusHandle, db_file: &PathBuf) -> Result { @@ -32,17 +33,29 @@ pub fn setup_datastore(config: &AppConfig, bus: &BusHandle) -> Result< } pub fn get_repositories(config: &AppConfig) -> Result { - // If there is a cache set get it and use it + // We are probably in a socket command so get the shared store if let Some(store) = get_cached_store() { return Ok(store.repositories()); } - // Setup a fresh data store + // We are probably in a standalone command so setup a fresh data store let bus = get_enclave_bus_handle()?; let store = setup_datastore(config, &bus)?; Ok(store.repositories()) } +pub fn get_eventstore_reader(config: &AppConfig) -> Result { + // We are probably in a socket command so get the shared eventstore reader + if let Some(es) = get_shared_eventstore() { + return Ok(es); + } + + // We are probably in a standalone command so get a new reader + let system = EventSystem::persisted(config.log_file(), config.db_file()); + let es = system.eventstore_reader()?; + Ok(es) +} + pub fn close_all_connections() { SledDb::close_all_connections(); } diff --git a/crates/entrypoint/src/start/aggregator_start.rs b/crates/entrypoint/src/start/aggregator_start.rs index 28852e2d06..32b428aa85 100644 --- a/crates/entrypoint/src/start/aggregator_start.rs +++ b/crates/entrypoint/src/start/aggregator_start.rs @@ -40,6 +40,7 @@ pub async fn execute( .with_threshold_plaintext_aggregation() .with_net(config.peers(), config.quic_port()) .with_shared_store() + .with_shared_eventstore() .build() .await?; diff --git a/crates/entrypoint/src/start/start.rs b/crates/entrypoint/src/start/start.rs index 13288e2b65..f1db8bd827 100644 --- a/crates/entrypoint/src/start/start.rs +++ b/crates/entrypoint/src/start/start.rs @@ -33,6 +33,7 @@ pub async fn execute(config: &AppConfig) -> Result { .with_zkproof(backend) .with_net(config.peers(), config.quic_port()) .with_shared_store() + .with_shared_eventstore() .build() .await?; From 7c6de3a388751888066d1c1adc62e66764689eeb Mon Sep 17 00:00:00 2001 From: ryardley Date: Wed, 18 Mar 2026 09:16:01 +0000 Subject: [PATCH 12/31] remove unused imports --- crates/entrypoint/src/helpers/datastore.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/entrypoint/src/helpers/datastore.rs b/crates/entrypoint/src/helpers/datastore.rs index 03a8908ecf..33dea18df7 100644 --- a/crates/entrypoint/src/helpers/datastore.rs +++ b/crates/entrypoint/src/helpers/datastore.rs @@ -4,7 +4,7 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -use actix::{Actor, Recipient}; +use actix::Actor; use anyhow::Result; use e3_ciphernode_builder::global_eventstore_cache::{get_shared_eventstore, EventStoreReader}; use e3_ciphernode_builder::global_store_cache::get_cached_store; @@ -12,7 +12,7 @@ use e3_ciphernode_builder::{get_enclave_bus_handle, EventSystem}; use e3_config::AppConfig; use e3_data::{DataStore, InMemStore, SledDb, SledStore}; use e3_data::{Repositories, RepositoriesFactory}; -use e3_events::{BusHandle, Disabled, EventStoreQueryBy, SeqAgg, TsAgg}; +use e3_events::{BusHandle, Disabled}; use std::path::PathBuf; pub fn get_sled_store(bus: &BusHandle, db_file: &PathBuf) -> Result { From aa4680c2beaeab02baee88faebcb9a7511767a9a Mon Sep 17 00:00:00 2001 From: ryardley Date: Wed, 18 Mar 2026 09:17:05 +0000 Subject: [PATCH 13/31] add comments --- crates/entrypoint/src/helpers/datastore.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/entrypoint/src/helpers/datastore.rs b/crates/entrypoint/src/helpers/datastore.rs index 33dea18df7..ab965b339c 100644 --- a/crates/entrypoint/src/helpers/datastore.rs +++ b/crates/entrypoint/src/helpers/datastore.rs @@ -32,6 +32,7 @@ pub fn setup_datastore(config: &AppConfig, bus: &BusHandle) -> Result< Ok(store) } +/// Command helper to get a store pub fn get_repositories(config: &AppConfig) -> Result { // We are probably in a socket command so get the shared store if let Some(store) = get_cached_store() { @@ -44,6 +45,7 @@ pub fn get_repositories(config: &AppConfig) -> Result { Ok(store.repositories()) } +/// Command helper to get an eventstore reader for reading events from the event store pub fn get_eventstore_reader(config: &AppConfig) -> Result { // We are probably in a socket command so get the shared eventstore reader if let Some(es) = get_shared_eventstore() { From 48a48e9a52ba34592a64bc862e56fb83493484bd Mon Sep 17 00:00:00 2001 From: ryardley Date: Wed, 18 Mar 2026 09:35:46 +0000 Subject: [PATCH 14/31] add comments --- crates/cli/src/start.rs | 6 +---- crates/socket-server/src/lib.rs | 29 +++++++++------------ docs/pages/ciphernode-operators/running.mdx | 3 +++ 3 files changed, 16 insertions(+), 22 deletions(-) diff --git a/crates/cli/src/start.rs b/crates/cli/src/start.rs index 086e5a953b..6f6633fe31 100644 --- a/crates/cli/src/start.rs +++ b/crates/cli/src/start.rs @@ -15,7 +15,7 @@ use e3_ciphernode_builder::CiphernodeHandle; use e3_config::{AppConfig, NodeRole}; use e3_console::Console; use e3_events::{prelude::*, Shutdown}; -use e3_socket_server::{remove_socket, start_socket_server}; +use e3_socket_server::start_socket_server; use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader}; use tokio::signal::unix::{signal, SignalKind}; use tracing::{error, info, instrument}; @@ -125,10 +125,6 @@ pub async fn graceful_shutdown(node: Option) { } } - if let Err(e) = remove_socket() { - error!("FAILED TO REMOVE SOCKET! {e}"); - } - tokio::time::sleep(Duration::from_secs(2)).await; info!("Graceful shutdown complete"); } diff --git a/crates/socket-server/src/lib.rs b/crates/socket-server/src/lib.rs index 9c9f702ffa..2c022fe76e 100644 --- a/crates/socket-server/src/lib.rs +++ b/crates/socket-server/src/lib.rs @@ -8,24 +8,24 @@ use anyhow::Result; use e3_console::{log, Console}; use serde::Serialize; use std::future::Future; -use std::path::Path; use std::sync::Arc; use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader}; -use tokio::net::UnixStream; +use tokio::net::TcpStream; use tracing::error; -pub const SOCKET_PATH: &str = "/tmp/enclave.sock"; +pub const TCP_PORT: u16 = 50505; +const TCP_ADDRESS: &str = "127.0.0.1"; // using localhost specifically so that it is not mounted + // externally. We might change this if we need to control + // externally and add authentication or TLS -pub async fn connect_socket() -> Option { - if !Path::new(SOCKET_PATH).exists() { - return None; - } - UnixStream::connect(SOCKET_PATH).await.ok() +pub async fn connect_socket() -> Option { + let addr = format!("{}:{}", TCP_ADDRESS, TCP_PORT); + TcpStream::connect(addr).await.ok() } pub async fn run_on_socket( out: Console, - stream: UnixStream, + stream: TcpStream, cli: T, ) -> anyhow::Result<()> { let (reader, mut writer) = stream.into_split(); @@ -42,18 +42,13 @@ pub async fn run_on_socket( Ok(()) } -pub fn remove_socket() -> Result<()> { - std::fs::remove_file(SOCKET_PATH)?; - Ok(()) -} - pub async fn start_socket_server(handler: F) where - F: Fn(UnixStream) -> Fut + Send + Sync + 'static, + F: Fn(TcpStream) -> Fut + Send + Sync + 'static, Fut: Future> + 'static, { - let _ = std::fs::remove_file(SOCKET_PATH); - let listener = match tokio::net::UnixListener::bind(SOCKET_PATH) { + let addr = format!("{}:{}", TCP_ADDRESS, TCP_PORT); + let listener = match tokio::net::TcpListener::bind(addr).await { Ok(l) => l, Err(e) => { error!("Failed to bind socket: {e}"); diff --git a/docs/pages/ciphernode-operators/running.mdx b/docs/pages/ciphernode-operators/running.mdx index 267c551d6f..157ce2720a 100644 --- a/docs/pages/ciphernode-operators/running.mdx +++ b/docs/pages/ciphernode-operators/running.mdx @@ -289,6 +289,9 @@ Open the following ports: | ------ | -------- | -------------------------- | | `9091` | UDP | QUIC/libp2p P2P networking | +> **Important:** Port `50505` (TCP) is used for local CLI commands and binds to `localhost` only. +> **Do not expose this port externally** - it provides control plane access to your node. + ### Bootstrap Peers Connect to the Interfold bootstrap network: From 69dd648f05599f32dc5831ec92b7013e550f8040 Mon Sep 17 00:00:00 2001 From: ryardley Date: Wed, 18 Mar 2026 09:52:57 +0000 Subject: [PATCH 15/31] add colorization --- crates/cli/src/start.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/cli/src/start.rs b/crates/cli/src/start.rs index 6f6633fe31..f475323b0b 100644 --- a/crates/cli/src/start.rs +++ b/crates/cli/src/start.rs @@ -16,6 +16,7 @@ use e3_config::{AppConfig, NodeRole}; use e3_console::Console; use e3_events::{prelude::*, Shutdown}; use e3_socket_server::start_socket_server; +use e3_utils::{colorize, Color}; use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader}; use tokio::signal::unix::{signal, SignalKind}; use tracing::{error, info, instrument}; @@ -61,6 +62,7 @@ pub fn launch_socket_server() { if let Some(line) = lines.next_line().await? { let (out, mut rx) = Console::channel(); + info!("CMD: {}", &colorize(&line, Color::Blue)); let remote_cli: RemoteCli = serde_json::from_str(&line)?; let cli: Cli = remote_cli.try_into()?; cli.execute(out).await?; From 208ccf700b77b06159a443cf6843e949d0686244 Mon Sep 17 00:00:00 2001 From: ryardley Date: Wed, 18 Mar 2026 10:09:14 +0000 Subject: [PATCH 16/31] add error loop for socket server --- crates/socket-server/src/lib.rs | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/crates/socket-server/src/lib.rs b/crates/socket-server/src/lib.rs index 2c022fe76e..376d193008 100644 --- a/crates/socket-server/src/lib.rs +++ b/crates/socket-server/src/lib.rs @@ -57,13 +57,21 @@ where }; let handler = Arc::new(handler); + loop { + match listener.accept().await { + Ok((stream, _)) => { + let handler = Arc::clone(&handler); - while let Ok((stream, _)) = listener.accept().await { - let handler = Arc::clone(&handler); - tokio::task::spawn_local(async move { - if let Err(e) = handler(stream).await { - error!("Connection error: {e}"); + tokio::task::spawn_local(async move { + if let Err(e) = handler(stream).await { + error!("Connection error: {e}"); + } + }); } - }); + Err(e) => { + error!("Accept error: {e}"); + tokio::time::sleep(std::time::Duration::from_secs(1)).await; + } + } } } From ba007de50c4915d3d51bdebc2285d76107c49317 Mon Sep 17 00:00:00 2001 From: ryardley Date: Wed, 18 Mar 2026 13:50:48 +0000 Subject: [PATCH 17/31] dont run remote unless parseable --- crates/cli/src/ciphernode/mod.rs | 6 +++--- crates/cli/src/cli.rs | 18 ++---------------- crates/cli/src/main.rs | 10 +++++----- crates/cli/src/net.rs | 2 +- crates/cli/src/nodes.rs | 2 +- crates/cli/src/noir.rs | 2 +- crates/cli/src/password.rs | 2 +- crates/cli/src/program.rs | 4 ++-- crates/cli/src/wallet.rs | 2 +- 9 files changed, 17 insertions(+), 31 deletions(-) diff --git a/crates/cli/src/ciphernode/mod.rs b/crates/cli/src/ciphernode/mod.rs index 7c1a7348cb..c654fd8824 100644 --- a/crates/cli/src/ciphernode/mod.rs +++ b/crates/cli/src/ciphernode/mod.rs @@ -35,7 +35,7 @@ impl ChainArgs { } } -#[derive(Subcommand, Debug)] +#[derive(Subcommand, Clone, Debug)] pub enum CiphernodeCommands { /// Setup local ciphernode configuration Setup { @@ -96,7 +96,7 @@ pub enum CiphernodeCommands { }, } -#[derive(Subcommand, Debug)] +#[derive(Subcommand, Clone, Debug)] pub enum LicenseCommands { /// Bond ENCL into the bonding registry Bond { @@ -117,7 +117,7 @@ pub enum LicenseCommands { }, } -#[derive(Subcommand, Debug)] +#[derive(Subcommand, Clone, Debug)] pub enum TicketCommands { /// Deposit stablecoins to mint tickets Buy { diff --git a/crates/cli/src/cli.rs b/crates/cli/src/cli.rs index 7d9c491e60..285f1d28fb 100644 --- a/crates/cli/src/cli.rs +++ b/crates/cli/src/cli.rs @@ -25,7 +25,7 @@ use std::path::PathBuf; use std::str::FromStr; use tracing::{info, instrument, Level}; -#[derive(Parser, Debug)] +#[derive(Parser, Clone, Debug)] #[command(name = "enclave")] #[command(about = "A CLI for interacting with Enclave the open-source protocol for Encrypted Execution Environments (E3)", long_about = None)] #[command(version = env!("CARGO_PKG_VERSION"))] @@ -204,7 +204,7 @@ impl Cli { } } -#[derive(Subcommand, Debug)] +#[derive(Subcommand, Clone, Debug)] pub enum Commands { /// Start the application Start { @@ -312,8 +312,6 @@ pub struct RemoteCli { #[derive(Clone, Debug, Serialize, Deserialize)] pub enum RemoteCommand { NetGetPeerId, - // NodePs, - // NodeStatus { id: String }, CiphernodeStatus { chain: ChainArgs }, NoirStatus, WalletGet, @@ -333,12 +331,6 @@ impl TryFrom for RemoteCommand { Commands::Noir { command: NoirCommands::Status, } => Ok(RemoteCommand::NoirStatus), - // Commands::Nodes { - // command: NodeCommands::Ps, - // } => Ok(RemoteCommand::NodePs), - // Commands::Nodes { - // command: NodeCommands::Status { id }, - // } => Ok(RemoteCommand::NodeStatus { id }), Commands::Ciphernode { command: CiphernodeCommands::Status { chain }, } => Ok(RemoteCommand::CiphernodeStatus { chain }), @@ -391,12 +383,6 @@ impl TryFrom for Commands { RemoteCommand::CiphernodeStatus { chain } => Commands::Ciphernode { command: CiphernodeCommands::Status { chain }, }, - // RemoteCommand::NodeStatus { id } => Commands::Nodes { - // command: NodeCommands::Status { id }, - // }, - // RemoteCommand::NodePs => Commands::Nodes { - // command: NodeCommands::Ps, - // }, RemoteCommand::NoirStatus => Commands::Noir { command: NoirCommands::Status, }, diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index 44a25ccce2..5718359140 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -68,11 +68,12 @@ pub async fn main() -> Result<()> { let out = handle.writer(); let cli = Cli::parse(); - // If the socket exists - if let Err(err) = if let Some(stream) = connect_socket().await { - let cli: RemoteCli = cli.try_into()?; + // If the socket exists and the command can be parsed as remote + let maybe_stream = connect_socket().await; + let maybe_remote_command = TryInto::::try_into(cli.clone()).ok(); + if let Err(err) = if let (Some(stream), Some(command)) = (maybe_stream, maybe_remote_command) { // Run the command over the socket - run_on_socket(out, stream, cli).await + run_on_socket(out, stream, command).await } else { // Run the command locally cli.execute(out).await @@ -80,7 +81,6 @@ pub async fn main() -> Result<()> { eprintln!("{}", colorize(err, Color::Red)); std::process::exit(1); } - handle.flush().await; Ok(()) } diff --git a/crates/cli/src/net.rs b/crates/cli/src/net.rs index b22b9dba66..3160dc7b1d 100644 --- a/crates/cli/src/net.rs +++ b/crates/cli/src/net.rs @@ -11,7 +11,7 @@ use e3_console::Console; use crate::net_get_peer_id; -#[derive(Subcommand, Debug)] +#[derive(Subcommand, Clone, Debug)] pub enum NetCommands { /// Get the ciphernode's libp2p PeerId GetPeerId, diff --git a/crates/cli/src/nodes.rs b/crates/cli/src/nodes.rs index bcd4f0866b..dd4d8403f5 100644 --- a/crates/cli/src/nodes.rs +++ b/crates/cli/src/nodes.rs @@ -13,7 +13,7 @@ use crate::{ nodes_stop, nodes_up, }; -#[derive(Subcommand, Debug)] +#[derive(Subcommand, Clone, Debug)] pub enum NodeCommands { /// Launch all nodes Up { diff --git a/crates/cli/src/noir.rs b/crates/cli/src/noir.rs index f0dfa80f73..081689d7c0 100644 --- a/crates/cli/src/noir.rs +++ b/crates/cli/src/noir.rs @@ -10,7 +10,7 @@ use e3_config::AppConfig; use e3_console::{log, Console}; use e3_zk_prover::{SetupStatus, ZkBackend}; -#[derive(Subcommand, Debug)] +#[derive(Subcommand, Clone, Debug)] pub enum NoirCommands { Status, Setup { diff --git a/crates/cli/src/password.rs b/crates/cli/src/password.rs index eff2a4d82f..338f7501ee 100644 --- a/crates/cli/src/password.rs +++ b/crates/cli/src/password.rs @@ -12,7 +12,7 @@ use zeroize::Zeroizing; use crate::{helpers::parse_zeroizing, password_delete, password_set}; -#[derive(Subcommand, Debug)] +#[derive(Subcommand, Clone, Debug)] pub enum PasswordCommands { /// Set (or overwrite) a password Set { diff --git a/crates/cli/src/program.rs b/crates/cli/src/program.rs index 9f4578465a..6cc9bfe009 100644 --- a/crates/cli/src/program.rs +++ b/crates/cli/src/program.rs @@ -8,7 +8,7 @@ use anyhow::Result; use clap::Subcommand; use e3_config::AppConfig; -#[derive(Subcommand, Debug)] +#[derive(Subcommand, Clone, Debug)] pub enum ProgramCommands { /// Start the program Start { @@ -38,7 +38,7 @@ pub enum ProgramCommands { }, } -#[derive(Subcommand, Debug)] +#[derive(Subcommand, Clone, Debug)] pub enum ProgramCacheCommands { /// Purge program compilation caches. Will make program compilation take longer. Purge, diff --git a/crates/cli/src/wallet.rs b/crates/cli/src/wallet.rs index 4a40b005aa..63f9bd15aa 100644 --- a/crates/cli/src/wallet.rs +++ b/crates/cli/src/wallet.rs @@ -12,7 +12,7 @@ use zeroize::Zeroizing; use crate::{helpers::ensure_hex_zeroizing, wallet_get, wallet_set}; -#[derive(Subcommand, Debug)] +#[derive(Subcommand, Clone, Debug)] pub enum WalletCommands { /// Set wallet private key Set { From c33fba7b18c3b1b0763dd876c43e6c02a46f74d9 Mon Sep 17 00:00:00 2001 From: ryardley Date: Thu, 19 Mar 2026 07:54:00 +0000 Subject: [PATCH 18/31] supply base test --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ead13f3975..a6943d2e88 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -473,7 +473,7 @@ jobs: strategy: matrix: # TODO removed base test for now - test-suite: [persist] + test-suite: [base, persist] fail-fast: false steps: - name: 'Check out the repo' From f21e4270b5a826413f34f593f2612b863b639ade Mon Sep 17 00:00:00 2001 From: ryardley Date: Thu, 19 Mar 2026 12:07:18 +0000 Subject: [PATCH 19/31] configure ctrl port --- Cargo.lock | 1 + crates/cli/src/cli.rs | 4 ++-- crates/cli/src/main.rs | 6 +++--- crates/cli/src/start.rs | 9 +++++---- crates/config/src/app_config.rs | 8 ++++++++ crates/socket-server/Cargo.toml | 7 ++++--- crates/socket-server/src/lib.rs | 11 ++++++----- tests/integration/enclave.config.yaml | 6 ++++++ 8 files changed, 35 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7517148159..f5d239bbc6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3650,6 +3650,7 @@ name = "e3-socket-server" version = "0.1.15" dependencies = [ "anyhow", + "e3-config", "e3-console", "serde", "serde_json", diff --git a/crates/cli/src/cli.rs b/crates/cli/src/cli.rs index 285f1d28fb..d42eed453c 100644 --- a/crates/cli/src/cli.rs +++ b/crates/cli/src/cli.rs @@ -81,11 +81,11 @@ impl Cli { } #[instrument(skip_all)] - pub async fn execute(self, out: Console) -> Result<()> { + pub async fn execute(self, out: Console, config_result: Result) -> Result<()> { let log_level = self.log_level(); // Attempt to load the config, but only treat "not found" as // the trigger for the init flow. All other errors bubble up. - let config = match self.load_config() { + let config = match config_result { Ok(cfg) => cfg, // If the file truly doesn't exist, fall back to init Err(e) diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index 5718359140..32b6f0974d 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -67,16 +67,16 @@ pub async fn main() -> Result<()> { let handle = Console::stdout(); let out = handle.writer(); let cli = Cli::parse(); - + let config_result = cli.load_config(); // If the socket exists and the command can be parsed as remote - let maybe_stream = connect_socket().await; + let maybe_stream = connect_socket(config_result.as_ref().ok()).await; let maybe_remote_command = TryInto::::try_into(cli.clone()).ok(); if let Err(err) = if let (Some(stream), Some(command)) = (maybe_stream, maybe_remote_command) { // Run the command over the socket run_on_socket(out, stream, command).await } else { // Run the command locally - cli.execute(out).await + cli.execute(out, config_result).await } { eprintln!("{}", colorize(err, Color::Red)); std::process::exit(1); diff --git a/crates/cli/src/start.rs b/crates/cli/src/start.rs index f475323b0b..3f5c54416e 100644 --- a/crates/cli/src/start.rs +++ b/crates/cli/src/start.rs @@ -28,7 +28,7 @@ pub async fn execute(mut config: AppConfig, peers: Vec) -> Result<()> { tokio::pin!(shutdown); owo(); - launch_socket_server(); + launch_socket_server(config.ctrl_port()); let node = tokio::select! { // build the ciphernode and if it completes first return the result @@ -54,9 +54,9 @@ pub async fn execute(mut config: AppConfig, peers: Vec) -> Result<()> { } /// Launch a socket server to read RemoteCli commands -pub fn launch_socket_server() { +pub fn launch_socket_server(ctrl_port: u16) { // Setup socket server for daemon - tokio::task::spawn_local(start_socket_server(|stream| async move { + tokio::task::spawn_local(start_socket_server(ctrl_port, |stream| async move { let (reader, mut writer) = stream.into_split(); let mut lines = BufReader::new(reader).lines(); @@ -65,7 +65,8 @@ pub fn launch_socket_server() { info!("CMD: {}", &colorize(&line, Color::Blue)); let remote_cli: RemoteCli = serde_json::from_str(&line)?; let cli: Cli = remote_cli.try_into()?; - cli.execute(out).await?; + let config_result = cli.load_config(); + cli.execute(out, config_result).await?; while let Some(msg) = rx.recv().await { writer.write_all(format!("{msg}\n").as_bytes()).await?; } diff --git a/crates/config/src/app_config.rs b/crates/config/src/app_config.rs index a5f7f27c8f..7e027c5a0c 100644 --- a/crates/config/src/app_config.rs +++ b/crates/config/src/app_config.rs @@ -53,6 +53,8 @@ pub struct NodeDefinition { pub peers: Vec, /// The port to use for the quic listener pub quic_port: u16, + /// The port to use for the ctrl socket listener + pub ctrl_port: u16, /// The name for the database pub db_file: PathBuf, /// The name for the keyfile @@ -80,6 +82,7 @@ impl Default for NodeDefinition { peers: vec![], // NOTE: We should look at generation via ipns fetch for the latest nodes address: None, quic_port: 9091, + ctrl_port: 50505, key_file: PathBuf::from("key"), // ~/.config/enclave/key db_file: PathBuf::from("db"), // ~/.config/enclave/db log_file: PathBuf::from("log"), // ~/.config/enclave/log @@ -335,6 +338,11 @@ impl AppConfig { self.node_def().quic_port } + /// get the ctrl port + pub fn ctrl_port(&self) -> u16 { + self.node_def().ctrl_port + } + /// Get the config file path pub fn config_file(&self) -> PathBuf { self.paths.config_file() diff --git a/crates/socket-server/Cargo.toml b/crates/socket-server/Cargo.toml index fda76a6df8..c21bbc1420 100644 --- a/crates/socket-server/Cargo.toml +++ b/crates/socket-server/Cargo.toml @@ -7,9 +7,10 @@ description.workspace = true repository.workspace = true [dependencies] -tokio.workspace = true anyhow.workspace = true -serde_json.workspace = true -serde.workspace = true e3-console.workspace = true +e3-config.workspace = true +serde.workspace = true +serde_json.workspace = true +tokio.workspace = true tracing.workspace = true diff --git a/crates/socket-server/src/lib.rs b/crates/socket-server/src/lib.rs index 376d193008..c3ff33ae64 100644 --- a/crates/socket-server/src/lib.rs +++ b/crates/socket-server/src/lib.rs @@ -5,6 +5,7 @@ // or FITNESS FOR A PARTICULAR PURPOSE. use anyhow::Result; +use e3_config::AppConfig; use e3_console::{log, Console}; use serde::Serialize; use std::future::Future; @@ -13,13 +14,13 @@ use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader}; use tokio::net::TcpStream; use tracing::error; -pub const TCP_PORT: u16 = 50505; const TCP_ADDRESS: &str = "127.0.0.1"; // using localhost specifically so that it is not mounted // externally. We might change this if we need to control // externally and add authentication or TLS -pub async fn connect_socket() -> Option { - let addr = format!("{}:{}", TCP_ADDRESS, TCP_PORT); +pub async fn connect_socket(maybe_config: Option<&AppConfig>) -> Option { + let config = maybe_config?; + let addr = format!("{}:{}", TCP_ADDRESS, config.ctrl_port()); TcpStream::connect(addr).await.ok() } @@ -42,12 +43,12 @@ pub async fn run_on_socket( Ok(()) } -pub async fn start_socket_server(handler: F) +pub async fn start_socket_server(tcp_port: u16, handler: F) where F: Fn(TcpStream) -> Fut + Send + Sync + 'static, Fut: Future> + 'static, { - let addr = format!("{}:{}", TCP_ADDRESS, TCP_PORT); + let addr = format!("{}:{}", TCP_ADDRESS, tcp_port); let listener = match tokio::net::TcpListener::bind(addr).await { Ok(l) => l, Err(e) => { diff --git a/tests/integration/enclave.config.yaml b/tests/integration/enclave.config.yaml index 6e50b6d7c4..865419596a 100644 --- a/tests/integration/enclave.config.yaml +++ b/tests/integration/enclave.config.yaml @@ -36,31 +36,37 @@ nodes: cn1: address: "0x70997970C51812dc3A010C7d01b50e0d17dc79C8" quic_port: 9201 + ctrl_socket: 50501 autonetkey: true autopassword: true cn2: address: "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC" quic_port: 9202 + ctrl_socket: 50502 autonetkey: true autopassword: true cn3: address: "0x90F79bf6EB2c4f870365E785982E1f101E93b906" quic_port: 9203 + ctrl_socket: 50503 autonetkey: true autopassword: true cn4: address: "0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65" quic_port: 9204 + ctrl_socket: 50504 autonetkey: true autopassword: true cn5: address: "0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc" quic_port: 9205 + ctrl_socket: 50505 autonetkey: true autopassword: true ag: address: "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" quic_port: 9206 + ctrl_socket: 50506 autonetkey: true autopassword: true role: From ab4ad5ce5d3e393704492f181ab8164b720df053 Mon Sep 17 00:00:00 2001 From: ryardley Date: Fri, 20 Mar 2026 02:53:29 +0000 Subject: [PATCH 20/31] fix misname --- tests/integration/enclave.config.yaml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/integration/enclave.config.yaml b/tests/integration/enclave.config.yaml index 865419596a..3dc7463498 100644 --- a/tests/integration/enclave.config.yaml +++ b/tests/integration/enclave.config.yaml @@ -36,37 +36,37 @@ nodes: cn1: address: "0x70997970C51812dc3A010C7d01b50e0d17dc79C8" quic_port: 9201 - ctrl_socket: 50501 + ctrl_port: 50501 autonetkey: true autopassword: true cn2: address: "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC" quic_port: 9202 - ctrl_socket: 50502 + ctrl_port: 50502 autonetkey: true autopassword: true cn3: address: "0x90F79bf6EB2c4f870365E785982E1f101E93b906" quic_port: 9203 - ctrl_socket: 50503 + ctrl_port: 50503 autonetkey: true autopassword: true cn4: address: "0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65" quic_port: 9204 - ctrl_socket: 50504 + ctrl_port: 50504 autonetkey: true autopassword: true cn5: address: "0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc" quic_port: 9205 - ctrl_socket: 50505 + ctrl_port: 50505 autonetkey: true autopassword: true ag: address: "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" quic_port: 9206 - ctrl_socket: 50506 + ctrl_port: 50506 autonetkey: true autopassword: true role: From 08cb29ec9e0e7d69f2cb67d8730d823771b2e4c6 Mon Sep 17 00:00:00 2001 From: ryardley Date: Fri, 20 Mar 2026 02:56:14 +0000 Subject: [PATCH 21/31] add ctrl port config --- examples/CRISP/enclave.config.yaml | 8 +++++++- templates/default/enclave.config.yaml | 7 ++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/examples/CRISP/enclave.config.yaml b/examples/CRISP/enclave.config.yaml index 8925b9d4f9..86fd2a8fd9 100644 --- a/examples/CRISP/enclave.config.yaml +++ b/examples/CRISP/enclave.config.yaml @@ -34,33 +34,39 @@ nodes: cn1: address: "0x70997970C51812dc3A010C7d01b50e0d17dc79C8" quic_port: 9201 + ctrl_port: 50501 autonetkey: true autopassword: true cn2: address: "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC" quic_port: 9202 + ctrl_port: 50502 + autonetkey: true autonetkey: true autopassword: true cn3: address: "0x90F79bf6EB2c4f870365E785982E1f101E93b906" quic_port: 9203 + ctrl_port: 50503 autonetkey: true autopassword: true cn4: address: "0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65" quic_port: 9204 + ctrl_port: 50504 autonetkey: true autopassword: true cn5: address: "0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc" quic_port: 9205 + ctrl_port: 50505 autonetkey: true autopassword: true ag: address: "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" quic_port: 9206 + ctrl_port: 50506 autonetkey: true autopassword: true role: type: "aggregator" - diff --git a/templates/default/enclave.config.yaml b/templates/default/enclave.config.yaml index 0a7d832812..d78ae3240a 100644 --- a/templates/default/enclave.config.yaml +++ b/templates/default/enclave.config.yaml @@ -26,33 +26,38 @@ nodes: cn1: address: "0x70997970C51812dc3A010C7d01b50e0d17dc79C8" quic_port: 9201 + ctrl_port: 50501 autonetkey: true autopassword: true cn2: address: "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC" quic_port: 9202 + ctrl_port: 50502 autonetkey: true autopassword: true cn3: address: "0x90F79bf6EB2c4f870365E785982E1f101E93b906" quic_port: 9203 + ctrl_port: 50503 autonetkey: true autopassword: true cn4: address: "0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65" quic_port: 9204 + ctrl_port: 50504 autonetkey: true autopassword: true cn5: address: "0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc" quic_port: 9205 + ctrl_port: 50505 autonetkey: true autopassword: true ag: address: "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" quic_port: 9206 + ctrl_port: 50506 autonetkey: true autopassword: true role: type: "aggregator" - From 579739f11f6f089d3023007c78c14abe637e650e Mon Sep 17 00:00:00 2001 From: ryardley Date: Fri, 20 Mar 2026 05:36:40 +0000 Subject: [PATCH 22/31] debug --- crates/cli/src/main.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index 32b6f0974d..e4e6435c57 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -67,10 +67,13 @@ pub async fn main() -> Result<()> { let handle = Console::stdout(); let out = handle.writer(); let cli = Cli::parse(); + let config_result = cli.load_config(); + // let maybe_stream = connect_socket(config_result.as_ref().ok()).await; + // let maybe_remote_command = TryInto::::try_into(cli.clone()).ok(); + let maybe_stream = None; + let maybe_remote_command: Option = None; // If the socket exists and the command can be parsed as remote - let maybe_stream = connect_socket(config_result.as_ref().ok()).await; - let maybe_remote_command = TryInto::::try_into(cli.clone()).ok(); if let Err(err) = if let (Some(stream), Some(command)) = (maybe_stream, maybe_remote_command) { // Run the command over the socket run_on_socket(out, stream, command).await From 8fe52d9d987542642b538c90d9224adf73137576 Mon Sep 17 00:00:00 2001 From: ryardley Date: Fri, 20 Mar 2026 06:23:00 +0000 Subject: [PATCH 23/31] revert disabling ctrl server --- crates/cli/src/main.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index e4e6435c57..56e50674e8 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -69,10 +69,9 @@ pub async fn main() -> Result<()> { let cli = Cli::parse(); let config_result = cli.load_config(); - // let maybe_stream = connect_socket(config_result.as_ref().ok()).await; - // let maybe_remote_command = TryInto::::try_into(cli.clone()).ok(); - let maybe_stream = None; - let maybe_remote_command: Option = None; + let maybe_stream = connect_socket(config_result.as_ref().ok()).await; + let maybe_remote_command = TryInto::::try_into(cli.clone()).ok(); + // If the socket exists and the command can be parsed as remote if let Err(err) = if let (Some(stream), Some(command)) = (maybe_stream, maybe_remote_command) { // Run the command over the socket From 149e226784c3462b0e7e2fc0a47ade09a71f527b Mon Sep 17 00:00:00 2001 From: ryardley Date: Sat, 21 Mar 2026 05:41:50 +0000 Subject: [PATCH 24/31] remove redundant config --- examples/CRISP/enclave.config.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/CRISP/enclave.config.yaml b/examples/CRISP/enclave.config.yaml index 2910b2b766..bbb720bc06 100644 --- a/examples/CRISP/enclave.config.yaml +++ b/examples/CRISP/enclave.config.yaml @@ -42,7 +42,6 @@ nodes: quic_port: 9202 ctrl_port: 50502 autonetkey: true - autonetkey: true autopassword: true cn3: address: "0x90F79bf6EB2c4f870365E785982E1f101E93b906" From 06c40db55d44c6a6cd7c434c986f658f44dfdfac Mon Sep 17 00:00:00 2001 From: ryardley Date: Sat, 21 Mar 2026 07:34:58 +0000 Subject: [PATCH 25/31] add extra timeout and retry --- crates/net/src/net_sync_manager.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/crates/net/src/net_sync_manager.rs b/crates/net/src/net_sync_manager.rs index 01bd433a36..1e7a4d93b6 100644 --- a/crates/net/src/net_sync_manager.rs +++ b/crates/net/src/net_sync_manager.rs @@ -367,7 +367,10 @@ async fn handle_sync_request_event( "Requesting batched events for aggregate_id={} since={}", aggregate_id, since ); - let requester = DirectRequester::builder(net_cmds.clone(), net_events.clone()).build(); + let requester = DirectRequester::builder(net_cmds.clone(), net_events.clone()) + .max_retries(10) + .retry_timeout(Duration::from_secs(30)) + .build(); match fetch_all_batched_events::>( requester, PeerTarget::Random, From 3d42079f7653276aa220fd6ecb0267ef29724be3 Mon Sep 17 00:00:00 2001 From: ryardley Date: Sat, 21 Mar 2026 07:35:39 +0000 Subject: [PATCH 26/31] update fetch retry logic to take longer --- crates/net/src/net_sync_manager.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/net/src/net_sync_manager.rs b/crates/net/src/net_sync_manager.rs index 1e7a4d93b6..bde95be1cf 100644 --- a/crates/net/src/net_sync_manager.rs +++ b/crates/net/src/net_sync_manager.rs @@ -369,7 +369,7 @@ async fn handle_sync_request_event( ); let requester = DirectRequester::builder(net_cmds.clone(), net_events.clone()) .max_retries(10) - .retry_timeout(Duration::from_secs(30)) + .retry_timeout(Duration::from_secs(20)) .build(); match fetch_all_batched_events::>( requester, From 9fcf21eafbff1e8949b7ee9408723540aeb4434a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=B3=CE=BB?= Date: Sat, 21 Mar 2026 08:01:52 +0000 Subject: [PATCH 27/31] Update crates/net/src/net_sync_manager.rs Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- crates/net/src/net_sync_manager.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/net/src/net_sync_manager.rs b/crates/net/src/net_sync_manager.rs index bde95be1cf..872038528c 100644 --- a/crates/net/src/net_sync_manager.rs +++ b/crates/net/src/net_sync_manager.rs @@ -369,7 +369,7 @@ async fn handle_sync_request_event( ); let requester = DirectRequester::builder(net_cmds.clone(), net_events.clone()) .max_retries(10) - .retry_timeout(Duration::from_secs(20)) + .retry_timeout(Duration::from_secs(5)) .build(); match fetch_all_batched_events::>( requester, From a5e25bf2e7a6ec05b9fc45f964827f8ca2776652 Mon Sep 17 00:00:00 2001 From: ryardley Date: Sat, 21 Mar 2026 10:23:45 +0000 Subject: [PATCH 28/31] update wait for event in sync --- crates/net/src/net_sync_manager.rs | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/crates/net/src/net_sync_manager.rs b/crates/net/src/net_sync_manager.rs index 872038528c..333b235bb5 100644 --- a/crates/net/src/net_sync_manager.rs +++ b/crates/net/src/net_sync_manager.rs @@ -30,7 +30,7 @@ use crate::{ const NET_READY_CONNECT_TIMEOUT: Duration = Duration::from_secs(60); /// Maximum time to wait for the `AllPeersDialed` event before giving up. -const ALL_PEERS_DIALED_TIMEOUT: Duration = Duration::from_secs(30); +const ALL_PEERS_DIALED_TIMEOUT: Duration = Duration::from_secs(120); #[derive(Debug, Clone, Serialize, Deserialize)] pub struct SyncResponseValue { @@ -342,19 +342,24 @@ async fn handle_sync_request_event( let (event, ctx) = event.into_components(); info!("Checking for AllPeersDialed..."); if wait_for_event { + info!("Waiting for peer connection..."); await_event( &net_events, - |e| { - if matches!(e, &NetEvent::AllPeersDialed { .. }) { - info!("AllPeersDialed matched!"); - Some(e.clone()) - } else { - None + |e| match e { + NetEvent::ConnectionEstablished { .. } => { + info!("Peer connection established"); + Some(()) } + NetEvent::AllPeersDialed { total: 0, .. } => { + info!("No peers configured, proceeding without sync"); + Some(()) + } + _ => None, }, - ALL_PEERS_DIALED_TIMEOUT, + NET_READY_CONNECT_TIMEOUT, ) - .await?; + .await + .context("No peer connections established within timeout")?; } info!("handle_sync_request_event: AllPeersDialed"); From bee743cb6a0703adfe5838c62db825648704ba5a Mon Sep 17 00:00:00 2001 From: ryardley Date: Sat, 21 Mar 2026 10:32:06 +0000 Subject: [PATCH 29/31] add some logic for handling when there are no peers --- crates/net/src/net_sync_manager.rs | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/crates/net/src/net_sync_manager.rs b/crates/net/src/net_sync_manager.rs index 333b235bb5..7f3397fe5f 100644 --- a/crates/net/src/net_sync_manager.rs +++ b/crates/net/src/net_sync_manager.rs @@ -343,16 +343,16 @@ async fn handle_sync_request_event( info!("Checking for AllPeersDialed..."); if wait_for_event { info!("Waiting for peer connection..."); - await_event( + let has_peers = await_event( &net_events, |e| match e { NetEvent::ConnectionEstablished { .. } => { info!("Peer connection established"); - Some(()) + Some(true) } NetEvent::AllPeersDialed { total: 0, .. } => { info!("No peers configured, proceeding without sync"); - Some(()) + Some(false) } _ => None, }, @@ -360,6 +360,18 @@ async fn handle_sync_request_event( ) .await .context("No peer connections established within timeout")?; + + if !has_peers { + let value = SyncRequestSucceeded { + response: SyncResponseValue { + events: vec![], + ts: 0, + }, + }; + + address.into().try_send(TypedEvent::new(value, ctx))?; + return Ok(()); + } } info!("handle_sync_request_event: AllPeersDialed"); From 29b20b0b215f6ea8d81a4ad5f6425aeceea1894c Mon Sep 17 00:00:00 2001 From: ryardley Date: Sat, 21 Mar 2026 23:03:48 +0000 Subject: [PATCH 30/31] debug increasing sleep --- tests/integration/persist.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/integration/persist.sh b/tests/integration/persist.sh index 9deaed3b60..c31f5f8503 100755 --- a/tests/integration/persist.sh +++ b/tests/integration/persist.sh @@ -75,12 +75,12 @@ waiton "$SCRIPT_DIR/output/pubkey.bin" # kill aggregator enclave_nodes_stop ag -sleep 2 +sleep 8 # relaunch the aggregator enclave_nodes_start ag -sleep 4 +sleep 8 heading "Mock encrypted plaintext" $SCRIPT_DIR/lib/fake_encrypt.sh --input "$SCRIPT_DIR/output/pubkey.bin" --output "$SCRIPT_DIR/output/output.bin" --plaintext $PLAINTEXT --params "$ENCODED_PARAMS" From d93253aeab0b259755c3d88cf09f1506371bed13 Mon Sep 17 00:00:00 2001 From: ryardley Date: Sat, 21 Mar 2026 23:26:13 +0000 Subject: [PATCH 31/31] try better shutdown handling --- crates/net/src/net_interface.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/crates/net/src/net_interface.rs b/crates/net/src/net_interface.rs index 54f378c0e4..5508a7b645 100644 --- a/crates/net/src/net_interface.rs +++ b/crates/net/src/net_interface.rs @@ -210,7 +210,7 @@ impl Libp2pNetInterface { // Process commands Some(command) = cmd_rx.recv() => { if let NetCommand::Shutdown = command { - if let Err(e) = handle_shutdown(&mut self.swarm) { + if let Err(e) = handle_shutdown(&mut self.swarm).await { error!("Error processing NetCommand: {e}"); } break; @@ -907,16 +907,20 @@ fn handle_get_record( Ok(()) } -fn handle_shutdown(swarm: &mut Swarm) -> Result<()> { +async fn handle_shutdown(swarm: &mut Swarm) -> Result<()> { info!("Starting graceful shutdown"); - - // Disconnect all peers let peers: Vec<_> = swarm.connected_peers().copied().collect(); for peer in peers { - info!("Disconnecting from peer: {}", peer); let _ = swarm.disconnect_peer_id(peer); } - + // Drive the swarm briefly to flush QUIC CONNECTION_CLOSE frames + let drain_deadline = Instant::now() + Duration::from_secs(2); + while Instant::now() < drain_deadline { + match tokio::time::timeout(Duration::from_millis(100), swarm.select_next_some()).await { + Ok(_event) => continue, + Err(_timeout) => break, // No more events, frames flushed + } + } info!("Graceful shutdown complete"); Ok(()) }