From 441e41d1b1789315ce52d227639f25909af49326 Mon Sep 17 00:00:00 2001 From: ryardley Date: Tue, 17 Feb 2026 09:09:11 +0000 Subject: [PATCH 01/45] feat: prompt user to autogenerate net keypair when not specified --- crates/cli/src/config_set.rs | 40 +++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/crates/cli/src/config_set.rs b/crates/cli/src/config_set.rs index cff843e1bb..c94862cef5 100644 --- a/crates/cli/src/config_set.rs +++ b/crates/cli/src/config_set.rs @@ -5,7 +5,7 @@ // or FITNESS FOR A PARTICULAR PURPOSE. use anyhow::Result; -use dialoguer::{theme::ColorfulTheme, Input}; +use dialoguer::{theme::ColorfulTheme, Confirm, Input}; use e3_entrypoint::config_set; use tracing::instrument; @@ -60,23 +60,29 @@ pub async fn execute( password::execute(PasswordCommands::Set { password }, &config).await?; - if generate_net_keypair { - net::execute( - NetCommands::Keypair { - command: NetKeypairCommands::Generate, - }, - &config, - ) - .await?; + let net_keypair_command = if generate_net_keypair { + NetKeypairCommands::Generate + } else if net_keypair.is_none() { + let should_generate = Confirm::with_theme(&ColorfulTheme::default()) + .with_prompt("No net keypair specified. Would you like to generate one automatically?") + .default(true) + .interact()?; + if should_generate { + NetKeypairCommands::Generate + } else { + NetKeypairCommands::Set { net_keypair } + } } else { - net::execute( - NetCommands::Keypair { - command: NetKeypairCommands::Set { net_keypair }, - }, - &config, - ) - .await?; - } + NetKeypairCommands::Set { net_keypair } + }; + + net::execute( + NetCommands::Keypair { + command: net_keypair_command, + }, + &config, + ) + .await?; println!("Enclave configuration successfully created!"); From 2520ef2f09172a94d3e299ca06e492672a2a72f0 Mon Sep 17 00:00:00 2001 From: ryardley Date: Tue, 17 Feb 2026 09:45:18 +0000 Subject: [PATCH 02/45] add config dir prompt --- Cargo.lock | 1 + crates/cli/Cargo.toml | 1 + crates/cli/src/config_set.rs | 14 +++++++++++++- crates/entrypoint/src/config_set/mod.rs | 11 ++++++----- 4 files changed, 21 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6cc1615332..5a1e823115 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3122,6 +3122,7 @@ dependencies = [ "clap", "compile-time", "dialoguer", + "dirs 5.0.1", "e3-config", "e3-crypto", "e3-entrypoint", diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index 1d1a05355e..8532b54a95 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -26,6 +26,7 @@ e3-events = { workspace = true } e3-init = { workspace = true } e3-zk-prover = { workspace = true } e3-support-scripts = { workspace = true } +dirs = { workspace = true } hex = { workspace = true } opentelemetry = { workspace = true } opentelemetry-otlp = { workspace = true } diff --git a/crates/cli/src/config_set.rs b/crates/cli/src/config_set.rs index c94862cef5..6dd419cc47 100644 --- a/crates/cli/src/config_set.rs +++ b/crates/cli/src/config_set.rs @@ -4,6 +4,8 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. +use std::path::PathBuf; + use anyhow::Result; use dialoguer::{theme::ColorfulTheme, Confirm, Input}; use e3_entrypoint::config_set; @@ -56,7 +58,17 @@ pub async fn execute( } }; - let config = config_set::execute(rpc_url, eth_address).await?; + let default_config_dir = dirs::config_dir() + .ok_or_else(|| anyhow::anyhow!("Could not determine home directory"))? + .join("enclave"); + + let config_dir: PathBuf = Input::with_theme(&ColorfulTheme::default()) + .with_prompt("Enter config directory") + .default(default_config_dir.display().to_string()) + .interact_text()? + .into(); + + let config = config_set::execute(rpc_url, eth_address, &config_dir)?; password::execute(PasswordCommands::Set { password }, &config).await?; diff --git a/crates/entrypoint/src/config_set/mod.rs b/crates/entrypoint/src/config_set/mod.rs index a2ba685379..27184d5f4b 100644 --- a/crates/entrypoint/src/config_set/mod.rs +++ b/crates/entrypoint/src/config_set/mod.rs @@ -10,6 +10,7 @@ use e3_config::load_config; use e3_config::AppConfig; use e3_config::RPC; use std::fs; +use std::path::PathBuf; use tracing::instrument; // Import a built file: @@ -37,11 +38,11 @@ pub fn validate_eth_address(address: &String) -> Result<()> { } #[instrument(name = "app", skip_all)] -pub async fn execute(rpc_url: String, eth_address: Option) -> Result { - let config_dir = dirs::config_dir() - .ok_or_else(|| anyhow!("Could not determine home directory"))? - .join("enclave"); - +pub fn execute( + rpc_url: String, + eth_address: Option, + config_dir: &PathBuf, +) -> Result { fs::create_dir_all(&config_dir)?; let config_path = config_dir.join("enclave.config.yaml"); From 79b0fdbb04ba542a7a50e53ead58a586b997028c Mon Sep 17 00:00:00 2001 From: ryardley Date: Tue, 17 Feb 2026 10:40:38 +0000 Subject: [PATCH 03/45] fix up ordering in config-set --- crates/cli/src/config_set.rs | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/crates/cli/src/config_set.rs b/crates/cli/src/config_set.rs index 6dd419cc47..8293813d74 100644 --- a/crates/cli/src/config_set.rs +++ b/crates/cli/src/config_set.rs @@ -65,13 +65,19 @@ pub async fn execute( let config_dir: PathBuf = Input::with_theme(&ColorfulTheme::default()) .with_prompt("Enter config directory") .default(default_config_dir.display().to_string()) + .validate_with(|input: &String| -> Result<(), &str> { + let path = PathBuf::from(input); + if input.is_empty() { + Err("Path cannot be empty") + } else if path.is_file() { + Err("Path is a file, not a directory") + } else { + Ok(()) + } + }) .interact_text()? .into(); - let config = config_set::execute(rpc_url, eth_address, &config_dir)?; - - password::execute(PasswordCommands::Set { password }, &config).await?; - let net_keypair_command = if generate_net_keypair { NetKeypairCommands::Generate } else if net_keypair.is_none() { @@ -88,6 +94,27 @@ pub async fn execute( NetKeypairCommands::Set { net_keypair } }; + // Execute + + let config = config_set::execute(rpc_url, eth_address, &config_dir)?; + + for i in 0..3 { + if password::execute( + PasswordCommands::Set { + password: password.clone(), + }, + &config, + ) + .await + .is_ok() + { + break; + } + if i == 2 { + return Err(anyhow::anyhow!("Failed after 3 attempts")); + } + } + net::execute( NetCommands::Keypair { command: net_keypair_command, From 2c2ad828572dc2e5f1eb63dc32c5bcae2db30814 Mon Sep 17 00:00:00 2001 From: ryardley Date: Tue, 17 Feb 2026 13:26:03 +0000 Subject: [PATCH 04/45] refactor: consolidate config-set into ciphernode setup command Rename and move the config-set functionality under enclave ciphernode setup for better command organization. This includes: - Add private key validation to ensure address matches stored account - Improve password handling with Zeroizing for better security - Derive address from private key instead of requiring separate input - Update all CLI references and documentation --- .../src/ciphernode_builder.rs | 12 +++- crates/ciphernode-builder/src/event_system.rs | 5 +- crates/cli/src/ciphernode/context.rs | 2 +- crates/cli/src/ciphernode/mod.rs | 29 ++++++++- .../{config_set.rs => ciphernode/setup.rs} | 65 ++++++------------- crates/cli/src/cli.rs | 57 ++++------------ crates/cli/src/main.rs | 1 - crates/cli/src/password.rs | 3 +- crates/cli/src/password_set.rs | 19 +++--- crates/cli/src/start.rs | 1 - crates/cli/src/wallet.rs | 3 +- crates/cli/src/wallet_set.rs | 29 ++++++--- crates/entrypoint/src/config_set/mod.rs | 15 ++--- crates/entrypoint/src/password/set.rs | 15 +++-- crates/entrypoint/src/wallet/set.rs | 8 ++- crates/evm/src/helpers.rs | 4 +- docs/pages/ciphernode-operators/running.mdx | 4 +- 17 files changed, 132 insertions(+), 140 deletions(-) rename crates/cli/src/{config_set.rs => ciphernode/setup.rs} (66%) diff --git a/crates/ciphernode-builder/src/ciphernode_builder.rs b/crates/ciphernode-builder/src/ciphernode_builder.rs index 34aa511e09..fa4a3e9a41 100644 --- a/crates/ciphernode-builder/src/ciphernode_builder.rs +++ b/crates/ciphernode-builder/src/ciphernode_builder.rs @@ -7,7 +7,7 @@ use crate::{CiphernodeHandle, EventSystem, EvmSystemChainBuilder, ProviderCache, WriteEnabled}; use actix::{Actor, Addr}; use alloy::signers::local::PrivateKeySigner; -use anyhow::Result; +use anyhow::{bail, Result}; use derivative::Derivative; use e3_aggregator::ext::{PublicKeyAggregatorExtension, ThresholdPlaintextAggregatorExtension}; use e3_aggregator::CommitteeFinalizer; @@ -414,6 +414,16 @@ impl CiphernodeBuilder { let mut provider_cache = provider_cache.with_write_support(Arc::clone(cipher), Arc::clone(&repositories)); + // Ensure that the private key matches the given address in the config + let signer = provider_cache.ensure_signer().await?; + if signer.address().to_string() != addr { + bail!( + "config address {:?} does not match stored account {:?}!", + addr, + signer.address() + ); + } + // Use the configured backend directly let default_backend = self.sortition_backend.clone(); diff --git a/crates/ciphernode-builder/src/event_system.rs b/crates/ciphernode-builder/src/event_system.rs index 26c28fadcb..8f58d121ea 100644 --- a/crates/ciphernode-builder/src/event_system.rs +++ b/crates/ciphernode-builder/src/event_system.rs @@ -7,9 +7,10 @@ use crate::get_enclave_event_bus; use actix::{Actor, Addr, Handler, Recipient}; use anyhow::{anyhow, Result}; +use e3_aggregator::PublicKeyRepositoryFactory; use e3_data::{ - CommitLogEventLog, DataStore, InMemEventLog, InMemSequenceIndex, InMemStore, SledSequenceIndex, - SledStore, + CommitLogEventLog, DataStore, InMemEventLog, InMemSequenceIndex, InMemStore, + RepositoriesFactory, SledSequenceIndex, SledStore, }; use e3_events::hlc::Hlc; use e3_events::{ diff --git a/crates/cli/src/ciphernode/context.rs b/crates/cli/src/ciphernode/context.rs index 5d01af5bbb..853ac1389d 100644 --- a/crates/cli/src/ciphernode/context.rs +++ b/crates/cli/src/ciphernode/context.rs @@ -145,7 +145,7 @@ fn select_chain<'a>(config: &'a AppConfig, name: Option<&str>) -> Result<&'a Cha None => config .chains() .first() - .ok_or_else(|| anyhow!("No chains configured. Run `enclave config-set` first.")), + .ok_or_else(|| anyhow!("No chains configured. Run `enclave ciphernode setup` first.")), } } diff --git a/crates/cli/src/ciphernode/mod.rs b/crates/cli/src/ciphernode/mod.rs index bb4fcf8474..42e04dd79f 100644 --- a/crates/cli/src/ciphernode/mod.rs +++ b/crates/cli/src/ciphernode/mod.rs @@ -4,17 +4,19 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -use anyhow::Result; +use anyhow::{bail, Result}; use clap::{Args, Subcommand}; use e3_config::AppConfig; mod context; mod license; mod lifecycle; +pub mod setup; mod tickets; mod utils; use context::ChainContext; +use zeroize::Zeroizing; #[derive(Debug, Args, Clone, Default)] pub struct ChainArgs { @@ -31,6 +33,28 @@ impl ChainArgs { #[derive(Subcommand, Debug)] pub enum CiphernodeCommands { + /// Setup local ciphernode configuration + Setup { + /// An rpc url for enclave to connect to + #[arg(long = "rpc-url", short = 'r')] + rpc_url: Option, + + /// The password + #[arg(short, long)] + password: Option>, + + /// Wallet Private Key + #[arg(short, long)] + private_key: Option>, + + /// The network private key (ed25519) + #[arg(long = "net-keypair", short = 'n')] + net_keypair: Option, + + /// Autogenerate a new network keypair + #[arg(long = "generate-net-keypair", short = 'g')] + generate_net_keypair: bool, + }, /// Manage ENCL license tokens and bonding state License { #[command(subcommand)] @@ -150,6 +174,9 @@ pub async fn execute(command: CiphernodeCommands, config: &AppConfig) -> Result< let ctx = ChainContext::new(config, chain.selection()).await?; lifecycle::status(&ctx).await? } + CiphernodeCommands::Setup { .. } => { + bail!("Cannot run `enclave ciphernode setup` when a configuration already exists."); + } } Ok(()) diff --git a/crates/cli/src/config_set.rs b/crates/cli/src/ciphernode/setup.rs similarity index 66% rename from crates/cli/src/config_set.rs rename to crates/cli/src/ciphernode/setup.rs index 8293813d74..bebbfbe8ed 100644 --- a/crates/cli/src/config_set.rs +++ b/crates/cli/src/ciphernode/setup.rs @@ -5,26 +5,36 @@ // or FITNESS FOR A PARTICULAR PURPOSE. use std::path::PathBuf; +use std::str::FromStr; +use alloy::primitives::Address; +use alloy::signers::local::PrivateKeySigner; +use anyhow::Context; use anyhow::Result; use dialoguer::{theme::ColorfulTheme, Confirm, Input}; use e3_entrypoint::config_set; use tracing::instrument; +use zeroize::Zeroizing; -use crate::net; use crate::net::{NetCommands, NetKeypairCommands}; -use crate::password; -use crate::password::PasswordCommands; +use crate::password_set::ask_for_password; +use crate::wallet_set::ask_for_private_key; +use crate::{net, password_set}; + +fn eth_address_from_private_key(private_key: &str) -> Result
{ + let signer = PrivateKeySigner::from_str(private_key).context("invalid private key")?; + Ok(signer.address()) +} #[instrument(name = "app", skip_all)] pub async fn execute( rpc_url: Option, - eth_address: Option, - password: Option, - skip_eth: bool, + password: Option>, + private_key: Option>, net_keypair: Option, generate_net_keypair: bool, ) -> Result<()> { + let pw = ask_for_password(password)?; let rpc_url = match rpc_url { Some(url) => { config_set::validate_rpc_url(&url)?; @@ -37,27 +47,8 @@ pub async fn execute( .interact_text()?, }; - let eth_address: Option = match eth_address { - Some(address) => { - config_set::validate_eth_address(&address)?; - Some(address) - } - None => { - if skip_eth { - None - } else { - Input::with_theme(&ColorfulTheme::default()) - .with_prompt("Enter your Ethereum address (press Enter to skip)") - .allow_empty(true) - .validate_with(config_set::validate_eth_address) - .interact() - .ok() - .map(|s| if s.is_empty() { None } else { Some(s) }) - .flatten() - } - } - }; - + let private_key = ask_for_private_key(private_key)?; + let address = eth_address_from_private_key(&private_key)?; let default_config_dir = dirs::config_dir() .ok_or_else(|| anyhow::anyhow!("Could not determine home directory"))? .join("enclave"); @@ -95,25 +86,11 @@ pub async fn execute( }; // Execute + let config = config_set::execute(rpc_url, &config_dir, &address)?; - let config = config_set::execute(rpc_url, eth_address, &config_dir)?; + password_set::execute(&config, Some(pw)).await?; - for i in 0..3 { - if password::execute( - PasswordCommands::Set { - password: password.clone(), - }, - &config, - ) - .await - .is_ok() - { - break; - } - if i == 2 { - return Err(anyhow::anyhow!("Failed after 3 attempts")); - } - } + e3_entrypoint::wallet::set::execute(&config, private_key).await?; net::execute( NetCommands::Keypair { diff --git a/crates/cli/src/cli.rs b/crates/cli/src/cli.rs index e9d25c0dee..c3d0263456 100644 --- a/crates/cli/src/cli.rs +++ b/crates/cli/src/cli.rs @@ -14,7 +14,7 @@ use crate::noir::NoirCommands; use crate::password::PasswordCommands; use crate::program::{self, ProgramCommands}; use crate::wallet::WalletCommands; -use crate::{config_set, init, net, noir, password, purge_all, rev, wallet}; +use crate::{init, net, noir, password, purge_all, rev, wallet}; use crate::{print_env, start}; use anyhow::{bail, Result}; use clap::{command, ArgAction, Parser, Subcommand}; @@ -99,19 +99,19 @@ impl Cli { setup_simple_tracing(log_level); init::execute(path, template, skip_cleanup, self.verbose > 0).await? }, - Commands::ConfigSet { - rpc_url, - eth_address, - password, - skip_eth, - net_keypair, - generate_net_keypair, + Commands::Ciphernode { + command: CiphernodeCommands::Setup { + rpc_url, + password, + private_key, + net_keypair, + generate_net_keypair, + } } => { - config_set::execute( + ciphernode::setup::execute( rpc_url, - eth_address, password, - skip_eth, + private_key, net_keypair, generate_net_keypair, ) @@ -120,11 +120,10 @@ impl Cli { } Commands::Start { .. } => { println!("No configuration found. Setting up enclave configuration..."); - config_set::execute( + ciphernode::setup::execute( None, None, None, - false, None, false, ) @@ -135,7 +134,7 @@ impl Cli { noir::execute_without_config(command).await? }, _ => bail!( - "Configuration file not found. Run `enclave config-set` to create a configuration." + "Configuration file not found. Run `enclave ciphernode setup` to create a configuration." ), }; return Ok(()); @@ -172,9 +171,6 @@ impl Cli { Commands::PurgeAll => { purge_all::execute().await?; } - Commands::ConfigSet { .. } => { - bail!("Cannot run `enclave config-set` when a configuration already exists."); - } Commands::Nodes { command } => { nodes::execute( command, @@ -301,33 +297,6 @@ pub enum Commands { command: CiphernodeCommands, }, - /// Set configuration values (similar to solana config set) - ConfigSet { - /// An rpc url for enclave to connect to - #[arg(long = "rpc-url", short = 'r')] - rpc_url: Option, - - /// An Ethereum address that enclave should use to identify the node - #[arg(long = "eth-address", short = 'e')] - eth_address: Option, - - /// The password - #[arg(short, long)] - password: Option, - - /// Skip asking for eth - #[arg(long = "skip-eth", short = 's')] - skip_eth: bool, - - /// The network private key (ed25519) - #[arg(long = "net-keypair", short = 'n')] - net_keypair: Option, - - /// Generate a new network keypair - #[arg(long = "generate-net-keypair", short = 'g')] - generate_net_keypair: bool, - }, - /// Manage multiple node processes together as a set Nodes { #[command(subcommand)] diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index e5a09ba751..86b08d4187 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -10,7 +10,6 @@ use tracing::info; mod ciphernode; mod cli; -mod config_set; pub mod helpers; mod init; pub mod net; diff --git a/crates/cli/src/password.rs b/crates/cli/src/password.rs index 4587d77973..af6fa88f9e 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 zeroize::Zeroizing; use crate::{password_delete, password_set}; @@ -16,7 +17,7 @@ pub enum PasswordCommands { Set { /// The new password #[arg(short, long)] - password: Option, + password: Option>, }, /// Delete the current password diff --git a/crates/cli/src/password_set.rs b/crates/cli/src/password_set.rs index 092f8ce66f..5b302098eb 100644 --- a/crates/cli/src/password_set.rs +++ b/crates/cli/src/password_set.rs @@ -10,14 +10,12 @@ use zeroize::{Zeroize, Zeroizing}; use crate::helpers::prompt_password::prompt_password; -fn get_zeroizing_pw_vec(input: Option) -> Result>> { - if let Some(mut pw_str) = input { +pub fn ask_for_password(input: Option>) -> Result> { + if let Some(pw_str) = input { if pw_str.trim().is_empty() { bail!("Password must not be blank") } - let pw = Zeroizing::new(pw_str.trim().as_bytes().to_owned()); - pw_str.zeroize(); - return Ok(pw); + return Ok(pw_str); } // First password entry @@ -37,20 +35,19 @@ fn get_zeroizing_pw_vec(input: Option) -> Result>> { bail!("Passwords do not match") } - let pw = Zeroizing::new(pw_str.trim().as_bytes().to_owned()); - // Clean up sensitive data - pw_str.zeroize(); confirm_pw_str.zeroize(); + let trimmed = Zeroizing::new(pw_str.trim().to_owned()); + pw_str.zeroize(); - Ok(pw) + Ok(trimmed) } -pub async fn execute(config: &AppConfig, input: Option) -> Result<()> { +pub async fn execute(config: &AppConfig, input: Option>) -> Result<()> { println!("Setting password..."); e3_entrypoint::password::set::preflight(config).await?; - let pw = get_zeroizing_pw_vec(input)?; + let pw = ask_for_password(input)?; e3_entrypoint::password::set::execute(config, pw).await?; diff --git a/crates/cli/src/start.rs b/crates/cli/src/start.rs index 2150cd4e2c..a317588d71 100644 --- a/crates/cli/src/start.rs +++ b/crates/cli/src/start.rs @@ -13,7 +13,6 @@ use tracing::{info, instrument}; #[instrument(skip_all)] pub async fn execute(mut config: AppConfig, peers: Vec) -> Result<()> { owo(); - let Some(address) = config.address() else { return Err(anyhow!("You must provide an address")); }; diff --git a/crates/cli/src/wallet.rs b/crates/cli/src/wallet.rs index 3df58d37e7..caa560f2fd 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 zeroize::Zeroizing; use crate::wallet_set; @@ -17,7 +18,7 @@ pub enum WalletCommands { /// The private key - note we are leaving as hex string as it is easier to manage with /// the allow Signer coercion #[arg(long = "private-key", value_parser = ensure_hex)] - private_key: Option, + private_key: Option>, }, } diff --git a/crates/cli/src/wallet_set.rs b/crates/cli/src/wallet_set.rs index 5d4b64158e..9c78ad38a5 100644 --- a/crates/cli/src/wallet_set.rs +++ b/crates/cli/src/wallet_set.rs @@ -8,20 +8,29 @@ use anyhow::Result; use dialoguer::{theme::ColorfulTheme, Password}; use e3_config::AppConfig; use e3_entrypoint::wallet::set::validate_private_key; +use zeroize::Zeroizing; -pub async fn execute(config: &AppConfig, private_key: Option) -> Result<()> { - let input = if let Some(private_key) = private_key { - validate_private_key(&private_key)?; - private_key +pub fn ask_for_private_key(given_key: Option>) -> Result> { + let key = if let Some(given_key) = given_key { + validate_private_key(&given_key)?; + given_key } else { - Password::with_theme(&ColorfulTheme::default()) - .with_prompt("Enter your Ethereum private key") - .validate_with(validate_private_key) - .interact()? - .trim() - .to_string() + Zeroizing::new( + Password::with_theme(&ColorfulTheme::default()) + .with_prompt("Enter your Ethereum private key") + .validate_with(validate_private_key) + .interact()? + .trim() + .to_string(), + ) }; + Ok(key) +} + +pub async fn execute(config: &AppConfig, private_key: Option>) -> Result<()> { + let input = ask_for_private_key(private_key)?; + e3_entrypoint::wallet::set::execute(config, input).await?; println!("WalletKey key has been successfully stored and encrypted."); diff --git a/crates/entrypoint/src/config_set/mod.rs b/crates/entrypoint/src/config_set/mod.rs index 27184d5f4b..4fd48ed288 100644 --- a/crates/entrypoint/src/config_set/mod.rs +++ b/crates/entrypoint/src/config_set/mod.rs @@ -38,19 +38,17 @@ pub fn validate_eth_address(address: &String) -> Result<()> { } #[instrument(name = "app", skip_all)] -pub fn execute( - rpc_url: String, - eth_address: Option, - config_dir: &PathBuf, -) -> Result { +pub fn execute(rpc_url: String, config_dir: &PathBuf, address: &Address) -> Result { fs::create_dir_all(&config_dir)?; let config_path = config_dir.join("enclave.config.yaml"); let config_content = format!( r#"--- +node: + address: "{}" + # Enclave Configuration File -{} chains: - name: "devnet" rpc_url: "{}" @@ -65,10 +63,7 @@ chains: address: "{}" deploy_block: {} "#, - eth_address.map_or(String::new(), |addr| format!( - "# Ethereum Account Configuration\naddress: \"{}\"", - addr - )), + address, rpc_url, get_contract_info("Enclave")?.address, get_contract_info("Enclave")?.deploy_block, diff --git a/crates/entrypoint/src/password/set.rs b/crates/entrypoint/src/password/set.rs index eee50cc4d7..e40d38548b 100644 --- a/crates/entrypoint/src/password/set.rs +++ b/crates/entrypoint/src/password/set.rs @@ -22,7 +22,15 @@ pub async fn preflight(config: &AppConfig) -> Result<()> { Ok(()) } -pub async fn execute(config: &AppConfig, pw: Zeroizing>) -> Result<()> { +pub async fn execute(config: &AppConfig, input: Zeroizing) -> Result<()> { + let pw = Zeroizing::new(input.as_bytes().to_owned()); + + execute_bytes(config, pw).await?; + + Ok(()) +} + +pub async fn execute_bytes(config: &AppConfig, input: Zeroizing>) -> Result<()> { let key_file = config.key_file(); let mut pm = FilePasswordManager::new(key_file); @@ -31,8 +39,7 @@ pub async fn execute(config: &AppConfig, pw: Zeroizing>) -> Result<()> { pm.delete_key().await?; } - pm.set_key(pw).await?; - + pm.set_key(input).await?; Ok(()) } @@ -41,7 +48,7 @@ pub async fn autopassword(config: &AppConfig) -> Result<()> { let pm = FilePasswordManager::new(key_file); if !pm.is_set() { let pw = generate_random_bytes(128); - execute(config, pw.into()).await?; + execute_bytes(config, pw.into()).await?; } Ok(()) } diff --git a/crates/entrypoint/src/wallet/set.rs b/crates/entrypoint/src/wallet/set.rs index 14b6a2e21f..e82e311296 100644 --- a/crates/entrypoint/src/wallet/set.rs +++ b/crates/entrypoint/src/wallet/set.rs @@ -9,6 +9,7 @@ use anyhow::{anyhow, Result}; use e3_config::AppConfig; use e3_crypto::Cipher; use e3_evm::EthPrivateKeyRepositoryFactory; +use zeroize::{Zeroize, Zeroizing}; use crate::helpers::{datastore::get_repositories, rand::generate_random_bytes}; @@ -20,7 +21,7 @@ pub fn validate_private_key(input: &String) -> Result<()> { Ok(()) } -pub async fn execute(config: &AppConfig, input: String) -> Result<()> { +pub async fn execute(config: &AppConfig, input: Zeroizing) -> Result<()> { let cipher = Cipher::from_file(config.key_file()).await?; let encrypted = cipher.encrypt_data(&mut input.as_bytes().to_vec())?; let repositories = get_repositories(config)?; @@ -32,8 +33,9 @@ pub async fn execute(config: &AppConfig, input: String) -> Result<()> { } pub async fn autowallet(config: &AppConfig) -> Result<()> { - let bytes = generate_random_bytes(32); - let input = hex::encode(&bytes); + let mut bytes = generate_random_bytes(32); + let input = Zeroizing::new(hex::encode(&bytes)); + bytes.zeroize(); execute(config, input).await?; Ok(()) } diff --git a/crates/evm/src/helpers.rs b/crates/evm/src/helpers.rs index f43bd295f0..927b92fd66 100644 --- a/crates/evm/src/helpers.rs +++ b/crates/evm/src/helpers.rs @@ -35,6 +35,7 @@ use e3_data::Repository; use e3_utils::{retry_with_backoff, RetryError}; use std::{env, future::Future, sync::Arc}; use tracing::info; +use zeroize::{Zeroize, Zeroizing}; pub trait AuthConversions { fn to_header_value(&self) -> Option; @@ -200,8 +201,7 @@ pub async fn load_signer_from_repository( .ok_or_else(|| anyhow::anyhow!("No private key found in repository"))?; let decrypted = cipher.decrypt_data(&encrypted_key)?; - let private_key = String::from_utf8(decrypted)?; - + let private_key = Zeroizing::new(String::from_utf8(decrypted)?); private_key.parse().map_err(Into::into) } diff --git a/docs/pages/ciphernode-operators/running.mdx b/docs/pages/ciphernode-operators/running.mdx index efeeaade39..9384645e2e 100644 --- a/docs/pages/ciphernode-operators/running.mdx +++ b/docs/pages/ciphernode-operators/running.mdx @@ -59,9 +59,7 @@ enclaveup install ### Initialize Configuration ```bash -enclave config-set \ - --rpc-url wss://ethereum-sepolia-rpc.publicnode.com \ - --eth-address 0xYourAddress +enclave ciphernode start ``` This creates `~/.config/enclave/enclave.config.yaml`. You'll be prompted for a password to encrypt From c1dbbd28f62b63c75c0c231ab6576f9aca283009 Mon Sep 17 00:00:00 2001 From: ryardley Date: Tue, 17 Feb 2026 13:43:34 +0000 Subject: [PATCH 05/45] add ignore switch for address check --- .../src/ciphernode_builder.rs | 33 ++++++++++--------- crates/tests/tests/integration.rs | 4 +-- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/crates/ciphernode-builder/src/ciphernode_builder.rs b/crates/ciphernode-builder/src/ciphernode_builder.rs index fa4a3e9a41..0d147696cd 100644 --- a/crates/ciphernode-builder/src/ciphernode_builder.rs +++ b/crates/ciphernode-builder/src/ciphernode_builder.rs @@ -74,7 +74,7 @@ pub struct CiphernodeBuilder { threshold_plaintext_agg: bool, zk_backend: Option, net_config: Option, - start_buffer: bool, + ignore_address_check: bool, } // Simple Net Configuration @@ -140,8 +140,8 @@ impl CiphernodeBuilder { testmode_signer: None, threshold_plaintext_agg: false, net_config: None, - start_buffer: false, zk_backend: None, + ignore_address_check: false, } } @@ -193,12 +193,6 @@ impl CiphernodeBuilder { self.testmode_errors = true; self } - /// Ensure SnapshotBuffer starts immediately instead of waiting for SyncEnded. This is important - /// for tests that don't specifically - pub fn testmode_start_buffer_immediately(mut self) -> Self { - self.start_buffer = true; - self - } /// Use the node configuration on these specific chains. This will overwrite any previously /// given chains. @@ -316,6 +310,11 @@ impl CiphernodeBuilder { self } + pub fn testmode_ignore_address_check(mut self) -> Self { + self.ignore_address_check = true; + self + } + fn create_local_bus() -> Addr> { EventBus::::new(EventBusConfig { deduplicate: true }).start() } @@ -414,14 +413,16 @@ impl CiphernodeBuilder { let mut provider_cache = provider_cache.with_write_support(Arc::clone(cipher), Arc::clone(&repositories)); - // Ensure that the private key matches the given address in the config - let signer = provider_cache.ensure_signer().await?; - if signer.address().to_string() != addr { - bail!( - "config address {:?} does not match stored account {:?}!", - addr, - signer.address() - ); + if !self.ignore_address_check { + // Ensure that the private key matches the given address in the config + let signer = provider_cache.ensure_signer().await?; + if signer.address().to_string() != addr { + bail!( + "config address {:?} does not match stored account {:?}!", + addr, + signer.address() + ); + } } // Use the configured backend directly diff --git a/crates/tests/tests/integration.rs b/crates/tests/tests/integration.rs index 4414c02fb5..b519fce0d3 100644 --- a/crates/tests/tests/integration.rs +++ b/crates/tests/tests/integration.rs @@ -376,8 +376,8 @@ async fn test_trbfv_actor() -> Result<()> { .with_pubkey_aggregation() .with_sortition_score() .with_threshold_plaintext_aggregation() - .testmode_start_buffer_immediately() .testmode_with_forked_bus(bus.event_bus()) + .testmode_ignore_address_check() .with_logging() .build() .await @@ -394,8 +394,8 @@ async fn test_trbfv_actor() -> Result<()> { .with_zkproof(zk_backend.clone()) .testmode_with_signer(PrivateKeySigner::random()) .with_sortition_score() - .testmode_start_buffer_immediately() .testmode_with_forked_bus(bus.event_bus()) + .testmode_ignore_address_check() .with_logging() .build() .await From bcf851b5974787e30c32051784aac99e4f266c9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=B3=CE=BB?= Date: Tue, 17 Feb 2026 18:16:51 +0000 Subject: [PATCH 06/45] Update crates/cli/src/wallet_set.rs Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- crates/cli/src/wallet_set.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/cli/src/wallet_set.rs b/crates/cli/src/wallet_set.rs index 9c78ad38a5..e379174347 100644 --- a/crates/cli/src/wallet_set.rs +++ b/crates/cli/src/wallet_set.rs @@ -33,7 +33,7 @@ pub async fn execute(config: &AppConfig, private_key: Option>) e3_entrypoint::wallet::set::execute(config, input).await?; - println!("WalletKey key has been successfully stored and encrypted."); + println!("Wallet key has been successfully stored and encrypted."); Ok(()) } From d661b2f875c6d857991c017f026d06a7eab652e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=B3=CE=BB?= Date: Tue, 17 Feb 2026 18:17:42 +0000 Subject: [PATCH 07/45] Update crates/ciphernode-builder/src/ciphernode_builder.rs Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- crates/ciphernode-builder/src/ciphernode_builder.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ciphernode-builder/src/ciphernode_builder.rs b/crates/ciphernode-builder/src/ciphernode_builder.rs index 0d147696cd..5bc4de5ec6 100644 --- a/crates/ciphernode-builder/src/ciphernode_builder.rs +++ b/crates/ciphernode-builder/src/ciphernode_builder.rs @@ -416,7 +416,7 @@ impl CiphernodeBuilder { if !self.ignore_address_check { // Ensure that the private key matches the given address in the config let signer = provider_cache.ensure_signer().await?; - if signer.address().to_string() != addr { + if signer.address().to_string().to_lowercase() != addr.to_lowercase() { bail!( "config address {:?} does not match stored account {:?}!", addr, From 5080799d24e97ed28a55e4522c60c0b20e73f986 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=B3=CE=BB?= Date: Tue, 17 Feb 2026 18:18:04 +0000 Subject: [PATCH 08/45] Update docs/pages/ciphernode-operators/running.mdx Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- docs/pages/ciphernode-operators/running.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/pages/ciphernode-operators/running.mdx b/docs/pages/ciphernode-operators/running.mdx index 9384645e2e..33f973af7d 100644 --- a/docs/pages/ciphernode-operators/running.mdx +++ b/docs/pages/ciphernode-operators/running.mdx @@ -59,7 +59,7 @@ enclaveup install ### Initialize Configuration ```bash -enclave ciphernode start +enclave ciphernode setup ``` This creates `~/.config/enclave/enclave.config.yaml`. You'll be prompted for a password to encrypt From 8237b76cde2a7e880d792c29fbc1bcc0ad9ad852 Mon Sep 17 00:00:00 2001 From: ryardley Date: Tue, 17 Feb 2026 18:55:06 +0000 Subject: [PATCH 09/45] fix parsers --- crates/cli/src/ciphernode/mod.rs | 6 ++++-- crates/cli/src/helpers/mod.rs | 25 +++++++++++++++++++++++++ crates/cli/src/password.rs | 4 ++-- crates/cli/src/wallet.rs | 12 ++---------- 4 files changed, 33 insertions(+), 14 deletions(-) diff --git a/crates/cli/src/ciphernode/mod.rs b/crates/cli/src/ciphernode/mod.rs index 42e04dd79f..b978ae2a89 100644 --- a/crates/cli/src/ciphernode/mod.rs +++ b/crates/cli/src/ciphernode/mod.rs @@ -18,6 +18,8 @@ mod utils; use context::ChainContext; use zeroize::Zeroizing; +use crate::helpers::{ensure_hex_zeroizing, parse_zeroizing}; + #[derive(Debug, Args, Clone, Default)] pub struct ChainArgs { /// Chain name as defined in the enclave config (defaults to the first entry) @@ -40,11 +42,11 @@ pub enum CiphernodeCommands { rpc_url: Option, /// The password - #[arg(short, long)] + #[arg(short, long, value_parser = parse_zeroizing)] password: Option>, /// Wallet Private Key - #[arg(short, long)] + #[arg(short, long, value_parser = ensure_hex_zeroizing)] private_key: Option>, /// The network private key (ed25519) diff --git a/crates/cli/src/helpers/mod.rs b/crates/cli/src/helpers/mod.rs index 89a9cce809..7d005d35b0 100644 --- a/crates/cli/src/helpers/mod.rs +++ b/crates/cli/src/helpers/mod.rs @@ -4,6 +4,31 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. +use anyhow::{bail, Result}; +use zeroize::Zeroizing; + pub mod compile_id; pub mod prompt_password; pub mod telemetry; + +/// Parse to a Zeroizing String +pub fn parse_zeroizing(s: &str) -> Result> { + Ok(Zeroizing::new(s.to_string())) +} + +/// Ensure hex is of the form 0x12435687abcdef... +pub fn ensure_hex_zeroizing(s: &str) -> Result> { + Ok(parse_zeroizing(ensure_hex(s)?)?) +} + +/// Ensure a hexadecimal number +fn ensure_hex(s: &str) -> Result<&str> { + if !s.starts_with("0x") { + bail!("hex value must start with '0x'") + } + if !s[2..].chars().all(|c| c.is_ascii_hexdigit()) { + bail!("private key must only contain hex characters [0-9a-fA-F]"); + } + hex::decode(&s[2..])?; + Ok(s) +} diff --git a/crates/cli/src/password.rs b/crates/cli/src/password.rs index af6fa88f9e..4cc08b9553 100644 --- a/crates/cli/src/password.rs +++ b/crates/cli/src/password.rs @@ -9,14 +9,14 @@ use clap::Subcommand; use e3_config::AppConfig; use zeroize::Zeroizing; -use crate::{password_delete, password_set}; +use crate::{helpers::parse_zeroizing, password_delete, password_set}; #[derive(Subcommand, Debug)] pub enum PasswordCommands { /// Set (or overwrite) a password Set { /// The new password - #[arg(short, long)] + #[arg(short, long, value_parser = parse_zeroizing)] password: Option>, }, diff --git a/crates/cli/src/wallet.rs b/crates/cli/src/wallet.rs index caa560f2fd..a8648d6e43 100644 --- a/crates/cli/src/wallet.rs +++ b/crates/cli/src/wallet.rs @@ -9,7 +9,7 @@ use clap::Subcommand; use e3_config::AppConfig; use zeroize::Zeroizing; -use crate::wallet_set; +use crate::{helpers::ensure_hex_zeroizing, wallet_set}; #[derive(Subcommand, Debug)] pub enum WalletCommands { @@ -17,19 +17,11 @@ pub enum WalletCommands { Set { /// The private key - note we are leaving as hex string as it is easier to manage with /// the allow Signer coercion - #[arg(long = "private-key", value_parser = ensure_hex)] + #[arg(long = "private-key", value_parser = ensure_hex_zeroizing)] private_key: Option>, }, } -fn ensure_hex(s: &str) -> Result { - if !s.starts_with("0x") { - bail!("hex value must start with '0x'") - } - hex::decode(&s[2..])?; - Ok(s.to_string()) -} - pub async fn execute(command: WalletCommands, config: AppConfig) -> Result<()> { match command { WalletCommands::Set { private_key } => wallet_set::execute(&config, private_key).await?, From 3757c55a1a53182ba1d1cd8bfaa1287d3d4caa90 Mon Sep 17 00:00:00 2001 From: ryardley Date: Wed, 18 Feb 2026 05:53:30 +0000 Subject: [PATCH 10/45] get test working in virtual environments --- crates/tests/tests/integration.rs | 3 ++- crates/zk-prover/src/backend/tests.rs | 15 +++++++-------- crates/zk-prover/src/config.rs | 2 +- crates/zk-prover/src/lib.rs | 1 + crates/zk-prover/src/prover.rs | 2 +- crates/zk-prover/src/test_utils.rs | 9 +++++++++ crates/zk-prover/tests/backend_tests.rs | 11 +++++------ crates/zk-prover/tests/integration_tests.rs | 4 ++-- 8 files changed, 28 insertions(+), 19 deletions(-) create mode 100644 crates/zk-prover/src/test_utils.rs diff --git a/crates/tests/tests/integration.rs b/crates/tests/tests/integration.rs index b519fce0d3..95d271a878 100644 --- a/crates/tests/tests/integration.rs +++ b/crates/tests/tests/integration.rs @@ -30,6 +30,7 @@ use e3_test_helpers::{ use e3_trbfv::helpers::calculate_error_size; use e3_utils::rand_eth_addr; use e3_utils::utility_types::ArcBytes; +use e3_zk_prover::test_utils::get_tempdir; use e3_zk_prover::{ZkBackend, ZkConfig}; use fhe::bfv::PublicKey; use fhe_traits::{DeserializeParametrized, Serialize}; @@ -75,7 +76,7 @@ async fn find_bb() -> Option { /// If a local bb binary is found, uses it with fixture files (fast path). /// Otherwise, calls `ensure_installed()` to download bb + circuits (CI path). async fn setup_test_zk_backend() -> (ZkBackend, tempfile::TempDir) { - let temp = tempfile::tempdir().unwrap(); + let temp = get_tempdir().unwrap(); let temp_path = temp.path(); let noir_dir = temp_path.join("noir"); let bb_binary = noir_dir.join("bin").join("bb"); diff --git a/crates/zk-prover/src/backend/tests.rs b/crates/zk-prover/src/backend/tests.rs index daa66af83a..70373af33e 100644 --- a/crates/zk-prover/src/backend/tests.rs +++ b/crates/zk-prover/src/backend/tests.rs @@ -5,8 +5,7 @@ // or FITNESS FOR A PARTICULAR PURPOSE. use super::*; -use crate::config::VersionInfo; -use tempfile::tempdir; +use crate::{config::VersionInfo, get_tempdir}; use tokio::fs; fn test_backend(temp_path: &std::path::Path, config: ZkConfig) -> ZkBackend { @@ -19,7 +18,7 @@ fn test_backend(temp_path: &std::path::Path, config: ZkConfig) -> ZkBackend { #[tokio::test] async fn test_backend_creates_directories() { - let temp = tempdir().unwrap(); + let temp = get_tempdir().unwrap(); let backend = test_backend(temp.path(), ZkConfig::default()); fs::create_dir_all(&backend.base_dir).await.unwrap(); @@ -37,7 +36,7 @@ async fn test_backend_creates_directories() { #[tokio::test] async fn test_version_info_roundtrip() { - let temp = tempdir().unwrap(); + let temp = get_tempdir().unwrap(); let path = temp.path().join("version.json"); let info = VersionInfo { @@ -59,7 +58,7 @@ async fn test_version_info_roundtrip() { #[tokio::test] async fn test_check_status_full_setup_needed() { - let temp = tempdir().unwrap(); + let temp = get_tempdir().unwrap(); let backend = test_backend(temp.path(), ZkConfig::default()); let status = backend.check_status().await; @@ -72,7 +71,7 @@ async fn test_check_status_full_setup_needed() { #[tokio::test] async fn test_check_status_ready_when_installed() { - let temp = tempdir().unwrap(); + let temp = get_tempdir().unwrap(); let config = ZkConfig::default(); let backend = test_backend(temp.path(), config.clone()); @@ -101,7 +100,7 @@ async fn test_check_status_ready_when_installed() { #[tokio::test] async fn test_check_status_bb_needs_update() { - let temp = tempdir().unwrap(); + let temp = get_tempdir().unwrap(); let config = ZkConfig::default(); let backend = test_backend(temp.path(), config.clone()); @@ -130,7 +129,7 @@ async fn test_check_status_bb_needs_update() { #[tokio::test] async fn test_work_dir_cleanup() { - let temp = tempdir().unwrap(); + let temp = get_tempdir().unwrap(); let backend = test_backend(temp.path(), ZkConfig::default()); fs::create_dir_all(&backend.work_dir).await.unwrap(); diff --git a/crates/zk-prover/src/config.rs b/crates/zk-prover/src/config.rs index 87efa2bb38..f99f56f48b 100644 --- a/crates/zk-prover/src/config.rs +++ b/crates/zk-prover/src/config.rs @@ -466,7 +466,7 @@ mod tests { println!("checksum verified for {}", target); // Test saving and loading through VersionInfo - let temp = tempdir().expect("failed to create temp dir"); + let temp = get_tempdir().expect("failed to create temp dir"); let tarball_path = temp.path().join("bb.tar.gz"); fs::write(&tarball_path, &bytes) diff --git a/crates/zk-prover/src/lib.rs b/crates/zk-prover/src/lib.rs index f3ee939522..3958210e2b 100644 --- a/crates/zk-prover/src/lib.rs +++ b/crates/zk-prover/src/lib.rs @@ -10,6 +10,7 @@ mod circuits; mod config; mod error; mod prover; +pub mod test_utils; mod traits; mod witness; diff --git a/crates/zk-prover/src/prover.rs b/crates/zk-prover/src/prover.rs index 14ceb9d778..8001cc3991 100644 --- a/crates/zk-prover/src/prover.rs +++ b/crates/zk-prover/src/prover.rs @@ -225,7 +225,7 @@ mod tests { #[test] fn test_prover_requires_bb() { - let temp = tempdir().unwrap(); + let temp = get_tempdir().unwrap(); let temp_path = temp.path(); let noir_dir = temp_path.join("noir"); let bb_binary = noir_dir.join("bin").join("bb"); diff --git a/crates/zk-prover/src/test_utils.rs b/crates/zk-prover/src/test_utils.rs new file mode 100644 index 0000000000..ff4a1b5129 --- /dev/null +++ b/crates/zk-prover/src/test_utils.rs @@ -0,0 +1,9 @@ +use std::path::Path; + +use tempfile::TempDir; + +pub fn get_tempdir() -> anyhow::Result { + Ok(TempDir::new_in( + Path::new(env!("CARGO_MANIFEST_DIR")).join("../../target/tmp"), + )?) +} diff --git a/crates/zk-prover/tests/backend_tests.rs b/crates/zk-prover/tests/backend_tests.rs index ecc69f5053..b3cfc3b2df 100644 --- a/crates/zk-prover/tests/backend_tests.rs +++ b/crates/zk-prover/tests/backend_tests.rs @@ -7,13 +7,12 @@ mod common; use common::test_backend; -use e3_zk_prover::{ZkConfig, ZkProver}; -use tempfile::tempdir; +use e3_zk_prover::{test_utils::get_tempdir, ZkConfig, ZkProver}; use tokio::fs; #[tokio::test] async fn test_backend_creates_directories() { - let temp = tempdir().unwrap(); + let temp = get_tempdir().unwrap(); let backend = test_backend(temp.path(), ZkConfig::default()); fs::create_dir_all(&backend.base_dir).await.unwrap(); @@ -31,7 +30,7 @@ async fn test_backend_creates_directories() { #[tokio::test] async fn test_work_dir_cleanup() { - let temp = tempdir().unwrap(); + let temp = get_tempdir().unwrap(); let backend = test_backend(temp.path(), ZkConfig::default()); fs::create_dir_all(&backend.work_dir).await.unwrap(); @@ -58,7 +57,7 @@ async fn test_work_dir_cleanup() { #[tokio::test] async fn test_work_dir_path_traversal_protection() { - let temp = tempdir().unwrap(); + let temp = get_tempdir().unwrap(); let backend = test_backend(temp.path(), ZkConfig::default()); // Test path traversal attempts @@ -85,7 +84,7 @@ async fn test_work_dir_path_traversal_protection() { #[test] fn test_prover_requires_bb() { - let temp = tempdir().unwrap(); + let temp = get_tempdir().unwrap(); let backend = test_backend(temp.path(), ZkConfig::default()); let prover = ZkProver::new(&backend); diff --git a/crates/zk-prover/tests/integration_tests.rs b/crates/zk-prover/tests/integration_tests.rs index 86921c30f1..2fd3dfee6c 100644 --- a/crates/zk-prover/tests/integration_tests.rs +++ b/crates/zk-prover/tests/integration_tests.rs @@ -37,7 +37,7 @@ async fn test_full_flow_download_circuits_prove_and_verify() { .expect("versions.json should exist"); eprintln!(">>> Config loaded"); - let temp = tempdir().unwrap(); + let temp = get_tempdir().unwrap(); let backend = test_backend(temp.path(), config); // --- Step 1: Fresh state should need full setup --- @@ -172,7 +172,7 @@ async fn test_download_bb_rejects_wrong_checksum() { *checksum = "0".repeat(64); } - let temp = tempdir().unwrap(); + let temp = get_tempdir().unwrap(); let backend = test_backend(temp.path(), config); let result = backend.download_bb().await; From e4fbfe812ada952b5f85637b67d870af4eb2421e Mon Sep 17 00:00:00 2001 From: ryardley Date: Wed, 18 Feb 2026 05:55:28 +0000 Subject: [PATCH 11/45] header --- crates/zk-prover/src/test_utils.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/crates/zk-prover/src/test_utils.rs b/crates/zk-prover/src/test_utils.rs index ff4a1b5129..1366e96fac 100644 --- a/crates/zk-prover/src/test_utils.rs +++ b/crates/zk-prover/src/test_utils.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::path::Path; use tempfile::TempDir; From e366ba9fda1dfa03e892bd027ad3a8b67447015c Mon Sep 17 00:00:00 2001 From: ryardley Date: Wed, 18 Feb 2026 06:02:11 +0000 Subject: [PATCH 12/45] import get_tmpdir fn --- crates/zk-prover/tests/integration_tests.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/zk-prover/tests/integration_tests.rs b/crates/zk-prover/tests/integration_tests.rs index 2fd3dfee6c..95f2f91596 100644 --- a/crates/zk-prover/tests/integration_tests.rs +++ b/crates/zk-prover/tests/integration_tests.rs @@ -21,9 +21,8 @@ mod common; use common::test_backend; use e3_fhe_params::BfvPreset; use e3_zk_helpers::circuits::dkg::pk::circuit::{PkCircuit, PkCircuitData}; -use e3_zk_prover::{BbTarget, Provable, SetupStatus, ZkConfig, ZkProver}; +use e3_zk_prover::{test_utils::get_tempdir, BbTarget, Provable, SetupStatus, ZkConfig, ZkProver}; use std::path::PathBuf; -use tempfile::tempdir; fn versions_json_path() -> PathBuf { PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("versions.json") From b752dc7f14d13a89f0c223f755bfc0501767d139 Mon Sep 17 00:00:00 2001 From: ryardley Date: Wed, 18 Feb 2026 06:06:48 +0000 Subject: [PATCH 13/45] fix bad imports --- crates/zk-prover/src/backend/tests.rs | 2 +- crates/zk-prover/src/config.rs | 2 +- crates/zk-prover/src/prover.rs | 3 +-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/crates/zk-prover/src/backend/tests.rs b/crates/zk-prover/src/backend/tests.rs index 70373af33e..5f478141f2 100644 --- a/crates/zk-prover/src/backend/tests.rs +++ b/crates/zk-prover/src/backend/tests.rs @@ -5,7 +5,7 @@ // or FITNESS FOR A PARTICULAR PURPOSE. use super::*; -use crate::{config::VersionInfo, get_tempdir}; +use crate::{config::VersionInfo, test_utils::get_tempdir}; use tokio::fs; fn test_backend(temp_path: &std::path::Path, config: ZkConfig) -> ZkBackend { diff --git a/crates/zk-prover/src/config.rs b/crates/zk-prover/src/config.rs index f99f56f48b..6411168589 100644 --- a/crates/zk-prover/src/config.rs +++ b/crates/zk-prover/src/config.rs @@ -220,7 +220,7 @@ pub fn verify_checksum(file: &str, data: &[u8], expected: Option<&str>) -> Resul #[cfg(test)] mod tests { - use tempfile::tempdir; + use crate::test_utils::get_tempdir; use super::*; diff --git a/crates/zk-prover/src/prover.rs b/crates/zk-prover/src/prover.rs index 8001cc3991..03a3fb53fd 100644 --- a/crates/zk-prover/src/prover.rs +++ b/crates/zk-prover/src/prover.rs @@ -219,9 +219,8 @@ impl ZkProver { #[cfg(test)] mod tests { use super::*; - use crate::config::ZkConfig; + use crate::{config::ZkConfig, test_utils::get_tempdir}; use e3_config::BBPath; - use tempfile::tempdir; #[test] fn test_prover_requires_bb() { From e3ae32dbcddecf7d0bc2195b33683345074d2502 Mon Sep 17 00:00:00 2001 From: ryardley Date: Wed, 18 Feb 2026 06:28:27 +0000 Subject: [PATCH 14/45] make tmp folder if not exists --- crates/zk-prover/src/test_utils.rs | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/crates/zk-prover/src/test_utils.rs b/crates/zk-prover/src/test_utils.rs index 1366e96fac..dd49742848 100644 --- a/crates/zk-prover/src/test_utils.rs +++ b/crates/zk-prover/src/test_utils.rs @@ -4,12 +4,19 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -use std::path::Path; +use std::{fs, path::Path}; +use anyhow::Result; use tempfile::TempDir; -pub fn get_tempdir() -> anyhow::Result { - Ok(TempDir::new_in( - Path::new(env!("CARGO_MANIFEST_DIR")).join("../../target/tmp"), - )?) +pub fn get_tempdir() -> Result { + let tmp = Path::new(env!("CARGO_MANIFEST_DIR")) + .parent() + .unwrap() + .parent() + .unwrap() + .join("target") + .join("tmp"); + fs::create_dir_all(tmp.clone())?; + Ok(TempDir::new_in(tmp)?) } From 8cc413f194bdccda27bfb6c731d9022fe7a5d14c Mon Sep 17 00:00:00 2001 From: ryardley Date: Wed, 18 Feb 2026 06:48:44 +0000 Subject: [PATCH 15/45] explain why we need to use get_tmpdir() --- crates/zk-prover/src/test_utils.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/crates/zk-prover/src/test_utils.rs b/crates/zk-prover/src/test_utils.rs index dd49742848..dd3d9b14b0 100644 --- a/crates/zk-prover/src/test_utils.rs +++ b/crates/zk-prover/src/test_utils.rs @@ -9,6 +9,9 @@ use std::{fs, path::Path}; use anyhow::Result; use tempfile::TempDir; +/// Get the tempdir within ./target/tmp. This is important since some virtual environments such as nix +/// won't necessarily have access to bb globaly. Not all tmp operations need to use this path only +/// operations that require tools to exist within a shell at that location. pub fn get_tempdir() -> Result { let tmp = Path::new(env!("CARGO_MANIFEST_DIR")) .parent() From 575b9675b594dbdb7e37f71ba9e2b2a6199b7f87 Mon Sep 17 00:00:00 2001 From: ryardley Date: Wed, 18 Feb 2026 13:16:25 +0000 Subject: [PATCH 16/45] set address in config when changing wallet private key --- Cargo.lock | 2 + crates/cli/Cargo.toml | 9 +- crates/cli/src/ciphernode/setup.rs | 21 +-- crates/cli/src/wallet_set.rs | 5 +- crates/config/src/app_config.rs | 10 + crates/entrypoint/Cargo.toml | 1 + crates/entrypoint/src/config/mod.rs | 2 + crates/entrypoint/src/config/set_address.rs | 175 ++++++++++++++++++ .../{config_set/mod.rs => config/setup.rs} | 2 +- crates/entrypoint/src/lib.rs | 2 +- crates/utils/src/alloy.rs | 10 +- 11 files changed, 214 insertions(+), 25 deletions(-) create mode 100644 crates/entrypoint/src/config/mod.rs create mode 100644 crates/entrypoint/src/config/set_address.rs rename crates/entrypoint/src/{config_set/mod.rs => config/setup.rs} (97%) diff --git a/Cargo.lock b/Cargo.lock index 5a1e823115..6a9cce4ec8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3130,6 +3130,7 @@ dependencies = [ "e3-evm", "e3-init", "e3-support-scripts", + "e3-utils", "e3-zk-prover", "hex", "opentelemetry", @@ -3260,6 +3261,7 @@ dependencies = [ "reqwest", "serde", "serde_json", + "serde_yaml", "tokio", "tracing", "zeroize", diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index 8532b54a95..1ed13553a6 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -12,21 +12,22 @@ name = "enclave" path = "src/main.rs" [dependencies] -alloy = { workspace = true } actix = { workspace = true } +alloy = { workspace = true } anyhow = { workspace = true } clap = { workspace = true } compile-time = { workspace = true } dialoguer = { workspace = true } +dirs = { workspace = true } e3-config = { workspace = true } e3-crypto = { workspace = true } e3-entrypoint = { workspace = true } -e3-evm = { workspace = true } e3-events = { workspace = true } +e3-evm = { workspace = true } e3-init = { workspace = true } -e3-zk-prover = { workspace = true } e3-support-scripts = { workspace = true } -dirs = { workspace = true } +e3-utils = { workspace = true } +e3-zk-prover = { workspace = true } hex = { workspace = true } opentelemetry = { workspace = true } opentelemetry-otlp = { workspace = true } diff --git a/crates/cli/src/ciphernode/setup.rs b/crates/cli/src/ciphernode/setup.rs index bebbfbe8ed..63be4ae519 100644 --- a/crates/cli/src/ciphernode/setup.rs +++ b/crates/cli/src/ciphernode/setup.rs @@ -4,15 +4,11 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -use std::path::PathBuf; -use std::str::FromStr; - -use alloy::primitives::Address; -use alloy::signers::local::PrivateKeySigner; -use anyhow::Context; use anyhow::Result; use dialoguer::{theme::ColorfulTheme, Confirm, Input}; -use e3_entrypoint::config_set; +use e3_entrypoint::config::setup; +use e3_utils::eth_address_from_private_key; +use std::path::PathBuf; use tracing::instrument; use zeroize::Zeroizing; @@ -21,11 +17,6 @@ use crate::password_set::ask_for_password; use crate::wallet_set::ask_for_private_key; use crate::{net, password_set}; -fn eth_address_from_private_key(private_key: &str) -> Result
{ - let signer = PrivateKeySigner::from_str(private_key).context("invalid private key")?; - Ok(signer.address()) -} - #[instrument(name = "app", skip_all)] pub async fn execute( rpc_url: Option, @@ -37,13 +28,13 @@ pub async fn execute( let pw = ask_for_password(password)?; let rpc_url = match rpc_url { Some(url) => { - config_set::validate_rpc_url(&url)?; + setup::validate_rpc_url(&url)?; url } None => Input::::new() .with_prompt("Enter WebSocket devnet RPC URL") .default("wss://ethereum-sepolia-rpc.publicnode.com".to_string()) - .validate_with(config_set::validate_rpc_url) + .validate_with(setup::validate_rpc_url) .interact_text()?, }; @@ -86,7 +77,7 @@ pub async fn execute( }; // Execute - let config = config_set::execute(rpc_url, &config_dir, &address)?; + let config = setup::execute(rpc_url, &config_dir, &address)?; password_set::execute(&config, Some(pw)).await?; diff --git a/crates/cli/src/wallet_set.rs b/crates/cli/src/wallet_set.rs index e379174347..1c2c5e2579 100644 --- a/crates/cli/src/wallet_set.rs +++ b/crates/cli/src/wallet_set.rs @@ -8,6 +8,7 @@ use anyhow::Result; use dialoguer::{theme::ColorfulTheme, Password}; use e3_config::AppConfig; use e3_entrypoint::wallet::set::validate_private_key; +use e3_utils::eth_address_from_private_key; use zeroize::Zeroizing; pub fn ask_for_private_key(given_key: Option>) -> Result> { @@ -30,9 +31,9 @@ pub fn ask_for_private_key(given_key: Option>) -> Result>) -> Result<()> { let input = ask_for_private_key(private_key)?; - + let address = eth_address_from_private_key(&input)?; e3_entrypoint::wallet::set::execute(config, input).await?; - + e3_entrypoint::config::set_address::execute(config, address)?; println!("Wallet key has been successfully stored and encrypted."); Ok(()) diff --git a/crates/config/src/app_config.rs b/crates/config/src/app_config.rs index 9b254c73da..35cd725f52 100644 --- a/crates/config/src/app_config.rs +++ b/crates/config/src/app_config.rs @@ -166,6 +166,8 @@ pub struct AppConfig { peers: Vec, /// Store all paths in the paths engine paths: PathsEngine, + /// The config yaml path + config_yaml: PathBuf, /// Set the Open Telemetry collector grpc endpoint. Eg. 127.0.0.1:4317 otel: Option, /// If a net key has not been set autogenerate one on start @@ -250,6 +252,9 @@ impl AppConfig { chains: config.chains, peers: vec![], paths, + config_yaml: config + .found_config_file + .expect("config_yaml should always be set after loading config"), otel: config.otel, autopassword: node.autopassword, autowallet: node.autowallet, @@ -332,6 +337,11 @@ impl AppConfig { self.paths.config_file() } + /// Get the config yaml path + pub fn config_yaml(&self) -> PathBuf { + self.config_yaml.clone() + } + /// Get the chains config pub fn chains(&self) -> &Vec { &self.chains diff --git a/crates/entrypoint/Cargo.toml b/crates/entrypoint/Cargo.toml index 9770304087..947ecdba46 100644 --- a/crates/entrypoint/Cargo.toml +++ b/crates/entrypoint/Cargo.toml @@ -35,6 +35,7 @@ e3-request = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } e3-sortition = { workspace = true } +serde_yaml = { workspace = true } e3-test-helpers = { workspace = true } e3-zk-prover = { workspace = true } tokio = { workspace = true } diff --git a/crates/entrypoint/src/config/mod.rs b/crates/entrypoint/src/config/mod.rs new file mode 100644 index 0000000000..d54ff967e6 --- /dev/null +++ b/crates/entrypoint/src/config/mod.rs @@ -0,0 +1,2 @@ +pub mod set_address; +pub mod setup; diff --git a/crates/entrypoint/src/config/set_address.rs b/crates/entrypoint/src/config/set_address.rs new file mode 100644 index 0000000000..8559bf675a --- /dev/null +++ b/crates/entrypoint/src/config/set_address.rs @@ -0,0 +1,175 @@ +use alloy::primitives::Address; +use anyhow::{Context, Result}; +use e3_config::AppConfig; +use serde_yaml::Value; +use std::fs; + +type YamlMap = serde_yaml::Mapping; + +/// Write an ethereum address into the appropriate node section of the config YAML +pub fn execute(config: &AppConfig, address: Address) -> Result<()> { + let path = config.config_yaml(); + let updated = apply_address(&config.name(), &fs::read_to_string(&path)?, address)?; + fs::write(&path, updated)?; + Ok(()) +} + +fn apply_address(name: &str, content: &str, address: Address) -> Result { + let addr = format!("{:?}", address); + let mut root: Value = serde_yaml::from_str(content)?; + + // Route to either `node.address` (default) or `nodes..address` (named) + let map = root + .as_mapping_mut() + .context("Expected YAML mapping at root")?; + if name == "_default" { + get_or_insert_map(map, "node") + .insert(Value::String("address".into()), Value::String(addr.clone())); + } else { + let nodes = get_or_insert_map(map, "nodes"); + get_or_insert_map(nodes, name) + .insert(Value::String("address".into()), Value::String(addr.clone())); + } + + // serde_yaml omits quotes; patch output so addresses are quoted strings + let yaml = serde_yaml::to_string(&root)?; + Ok(yaml.replace(&format!("address: {addr}"), &format!("address: \"{addr}\""))) +} + +// Retrieve or create a nested mapping under `key` +fn get_or_insert_map<'a>(map: &'a mut YamlMap, key: &str) -> &'a mut YamlMap { + map.entry(Value::String(key.into())) + .or_insert_with(|| Value::Mapping(YamlMap::new())) + .as_mapping_mut() + .expect("Expected mapping") +} + +#[cfg(test)] +mod tests { + use super::*; + use std::str::FromStr; + + const ADDR1: &str = "0x1234567890123456789012345678901234567890"; + const ADDR2: &str = "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0"; + + #[test] + fn test_apply_address_default() { + let result = apply_address( + "_default", + r#"node: + role: ciphernode +"#, + Address::from_str(ADDR1).unwrap(), + ) + .unwrap(); + assert_eq!( + result, + r#"node: + role: ciphernode + address: "0x1234567890123456789012345678901234567890" +"# + ); + } + + #[test] + fn test_apply_address_named_node() { + let result = apply_address( + "foo", + r#"node: + role: ciphernode +nodes: + foo: + role: aggregator +"#, + Address::from_str(ADDR2).unwrap(), + ) + .unwrap(); + assert_eq!( + result, + r#"node: + role: ciphernode +nodes: + foo: + role: aggregator + address: "0x9fe46736679d2d9a65f0992f2272de9f3c7fa6e0" +"# + ); + } + + #[test] + fn test_apply_address_default_existing_address() { + let result = apply_address( + "_default", + r#"node: + address: "0x0000000000000000000000000000000000000001" + role: ciphernode +"#, + Address::from_str(ADDR1).unwrap(), + ) + .unwrap(); + assert_eq!( + result, + r#"node: + address: "0x1234567890123456789012345678901234567890" + role: ciphernode +"# + ); + } + + #[test] + fn test_apply_address_named_node_existing_address() { + let result = apply_address( + "foo", + r#"nodes: + foo: + address: "0x0000000000000000000000000000000000000001" + role: aggregator +"#, + Address::from_str(ADDR2).unwrap(), + ) + .unwrap(); + assert_eq!( + result, + r#"nodes: + foo: + address: "0x9fe46736679d2d9a65f0992f2272de9f3c7fa6e0" + role: aggregator +"# + ); + } + + #[test] + fn test_apply_address_preserves_other_fields() { + let result = apply_address( + "_default", + r#"data_dir: "/mydata/enclave" +config_dir: "/myconfig/enclave" +chains: + - name: "hardhat" + rpc_url: "ws://localhost:8545" + contracts: + enclave: "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0" +node: + role: ciphernode + db_file: "./foo" +program: + risc0: + risc0_dev_mode: 0 +"#, + Address::from_str(ADDR1).unwrap(), + ) + .unwrap(); + for s in [ + "data_dir:", + "config_dir:", + "chains:", + "hardhat", + "program:", + "risc0_dev_mode: 0", + "db_file:", + "address:", + ] { + assert!(result.contains(s)); + } + } +} diff --git a/crates/entrypoint/src/config_set/mod.rs b/crates/entrypoint/src/config/setup.rs similarity index 97% rename from crates/entrypoint/src/config_set/mod.rs rename to crates/entrypoint/src/config/setup.rs index 4fd48ed288..4f8908fb2a 100644 --- a/crates/entrypoint/src/config_set/mod.rs +++ b/crates/entrypoint/src/config/setup.rs @@ -1,7 +1,7 @@ // SPDX-License-Identifier: LGPL-3.0-only // // This file is provided WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY +// without even even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. use alloy::primitives::Address; diff --git a/crates/entrypoint/src/lib.rs b/crates/entrypoint/src/lib.rs index fa63bc9100..7fbfbb0247 100644 --- a/crates/entrypoint/src/lib.rs +++ b/crates/entrypoint/src/lib.rs @@ -4,7 +4,7 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -pub mod config_set; +pub mod config; pub mod helpers; pub mod net; pub mod nodes; diff --git a/crates/utils/src/alloy.rs b/crates/utils/src/alloy.rs index c8c3085676..776f5d3653 100644 --- a/crates/utils/src/alloy.rs +++ b/crates/utils/src/alloy.rs @@ -4,10 +4,11 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -use alloy::primitives::Address; - use crate::SharedRng; +use alloy::{primitives::Address, signers::local::PrivateKeySigner}; +use anyhow::{Context, Result}; use rand::Rng; +use std::str::FromStr; pub fn rand_eth_addr(rng: &SharedRng) -> String { { @@ -15,3 +16,8 @@ pub fn rand_eth_addr(rng: &SharedRng) -> String { Address::from_slice(rnum).to_string() } } + +pub fn eth_address_from_private_key(private_key: &str) -> Result
{ + let signer = PrivateKeySigner::from_str(private_key).context("invalid private key")?; + Ok(signer.address()) +} From fd24c175ff00c57fdde405184ad99687308f542d Mon Sep 17 00:00:00 2001 From: ryardley Date: Wed, 18 Feb 2026 13:18:35 +0000 Subject: [PATCH 17/45] headers --- crates/entrypoint/src/config/mod.rs | 6 ++++++ crates/entrypoint/src/config/set_address.rs | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/crates/entrypoint/src/config/mod.rs b/crates/entrypoint/src/config/mod.rs index d54ff967e6..9a512d3740 100644 --- a/crates/entrypoint/src/config/mod.rs +++ b/crates/entrypoint/src/config/mod.rs @@ -1,2 +1,8 @@ +// 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. + pub mod set_address; pub mod setup; diff --git a/crates/entrypoint/src/config/set_address.rs b/crates/entrypoint/src/config/set_address.rs index 8559bf675a..cd96881ff7 100644 --- a/crates/entrypoint/src/config/set_address.rs +++ b/crates/entrypoint/src/config/set_address.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 alloy::primitives::Address; use anyhow::{Context, Result}; use e3_config::AppConfig; From b9c34d1edd22d96a21f693932a158996dbabec20 Mon Sep 17 00:00:00 2001 From: ryardley Date: Thu, 19 Feb 2026 02:47:38 +0000 Subject: [PATCH 18/45] add dependency diagram --- .../ciphernode-builder/src/event_system.mmd | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 crates/ciphernode-builder/src/event_system.mmd diff --git a/crates/ciphernode-builder/src/event_system.mmd b/crates/ciphernode-builder/src/event_system.mmd new file mode 100644 index 0000000000..5e4fb964d3 --- /dev/null +++ b/crates/ciphernode-builder/src/event_system.mmd @@ -0,0 +1,19 @@ +graph TD + store --> buffer + store --> handle + + handle --> eventbus + handle --> sequencer + handle --> hlc + handle --> buffer + + sequencer --> eventbus + sequencer --> eventstore_router + + buffer --> aggregate_config + + eventstore_router["eventstore_router | eventstore_getter_seq | eventstore_getter_ts"] --> eventstore_addrs + + eventstore_addrs --> aggregate_config + + hlc --> node_id From 879b687b92caed21f209faa204cac2b174d86b53 Mon Sep 17 00:00:00 2001 From: ryardley Date: Thu, 19 Feb 2026 06:01:40 +0000 Subject: [PATCH 19/45] update yaml --- .../src/ciphernode_builder.rs | 49 ++----- crates/ciphernode-builder/src/event_system.rs | 76 +++-------- .../src/eventbus_factory.rs | 7 +- crates/cli/src/start.rs | 8 +- crates/config/src/app_config.rs | 4 +- crates/data/src/sled_store.rs | 16 ++- crates/entrypoint/src/helpers/datastore.rs | 8 +- .../entrypoint/src/start/aggregator_start.rs | 4 +- crates/entrypoint/src/start/start.rs | 6 +- crates/events/src/bus_handle.rs | 122 +++++++++++++----- crates/events/src/hlc.rs | 13 +- crates/events/src/hlc_factory.rs | 102 +++++++++++++++ crates/events/src/lib.rs | 1 + crates/events/src/sequencer.rs | 4 +- crates/evm/src/evm_chain_gateway.rs | 4 +- crates/evm/tests/integration.rs | 8 +- crates/net/src/document_publisher.rs | 4 +- crates/net/src/net_event_buffer.rs | 4 +- crates/sync/src/sync.rs | 4 +- crates/test-helpers/src/ciphernode_system.rs | 6 +- crates/test-helpers/src/lib.rs | 5 +- crates/tests/tests/integration.rs | 29 ++--- 22 files changed, 303 insertions(+), 181 deletions(-) create mode 100644 crates/events/src/hlc_factory.rs diff --git a/crates/ciphernode-builder/src/ciphernode_builder.rs b/crates/ciphernode-builder/src/ciphernode_builder.rs index 5bc4de5ec6..afc5878c5a 100644 --- a/crates/ciphernode-builder/src/ciphernode_builder.rs +++ b/crates/ciphernode-builder/src/ciphernode_builder.rs @@ -14,6 +14,7 @@ use e3_aggregator::CommitteeFinalizer; use e3_config::chain_config::ChainConfig; use e3_crypto::Cipher; use e3_data::{InMemStore, RepositoriesFactory}; +use e3_events::hlc::Hlc; use e3_events::{ AggregateConfig, AggregateId, BusHandle, EnclaveEvent, EventBus, EventBusConfig, EvmEventConfig, }; @@ -61,7 +62,6 @@ pub struct CiphernodeBuilder { multithread_cache: Option>, multithread_concurrent_jobs: Option, multithread_report: Option>, - name: String, pubkey_agg: bool, rng: SharedRng, sortition_backend: SortitionBackend, @@ -115,7 +115,7 @@ impl CiphernodeBuilder { /// - name - Unique name for the ciphernode /// - rng - Arc Mutex wrapped random number generator /// - cipher - Cipher for encryption and decryption of sensitive data - pub fn new(name: &str, rng: SharedRng, cipher: Arc) -> Self { + pub fn new(rng: SharedRng, cipher: Arc) -> Self { Self { address: None, chains: vec![], @@ -128,7 +128,6 @@ impl CiphernodeBuilder { multithread_cache: None, multithread_concurrent_jobs: None, multithread_report: None, - name: name.to_owned(), pubkey_agg: false, rng, sortition_backend: SortitionBackend::score(), @@ -201,11 +200,11 @@ impl CiphernodeBuilder { self } - /// Use the given Address to represent the node. This should be unique. - pub fn with_address(mut self, addr: &str) -> Self { - self.address = Some(addr.to_owned()); - self - } + // /// Use the given Address to represent the node. This should be unique. + // pub fn with_address(mut self, addr: &str) -> Self { + // self.address = Some(addr.to_owned()); + // self + // } /// Log data actor events pub fn with_logging(mut self) -> Self { @@ -367,15 +366,6 @@ impl CiphernodeBuilder { None }; - let addr = if let Some(addr) = self.address.clone() { - info!("Using eth address = {}", addr); - addr - } else { - info!("Using random eth address"); - // TODO: This is for testing and should not be used for production if we use this to create ciphernodes in production - rand_eth_addr(&self.rng) - }; - // Create provider cache early to use for chain validation let mut provider_cache = if let Some(signer) = self.testmode_signer.take() { ProviderCache::new().with_signer(signer) @@ -387,45 +377,34 @@ impl CiphernodeBuilder { // Get an event system instance. let event_system = if let EventSystemType::Persisted { kv_path, log_path } = self.event_system.clone() { - EventSystem::persisted(&addr, log_path, kv_path) + EventSystem::persisted(log_path, kv_path) .with_event_bus(local_bus) .with_aggregate_config(aggregate_config.clone()) } else { if let Some(ref store) = self.in_mem_store { - EventSystem::in_mem_from_store(&addr, store) + EventSystem::in_mem_from_store(store) .with_event_bus(local_bus) .with_aggregate_config(aggregate_config.clone()) } else { - EventSystem::in_mem(&addr) + EventSystem::in_mem() .with_event_bus(local_bus) .with_aggregate_config(aggregate_config.clone()) } }; - let bus = event_system.handle()?; let store = event_system.store()?; let eventstore_ts = event_system.eventstore_getter_ts()?; let eventstore_seq = event_system.eventstore_getter_seq()?; let cipher = &self.cipher; let repositories = Arc::new(store.repositories()); - - // Now we add write support as store depends on event system let mut provider_cache = provider_cache.with_write_support(Arc::clone(cipher), Arc::clone(&repositories)); - if !self.ignore_address_check { - // Ensure that the private key matches the given address in the config - let signer = provider_cache.ensure_signer().await?; - if signer.address().to_string().to_lowercase() != addr.to_lowercase() { - bail!( - "config address {:?} does not match stored account {:?}!", - addr, - signer.address() - ); - } - } + // We need to supply the Hlc to the bus handle in order to enable it + let addr = provider_cache.ensure_signer().await?.address().to_string(); + let bus = event_system.handle()?.enable(&addr); - // Use the configured backend directly + // Use the configured sortition backend directly let default_backend = self.sortition_backend.clone(); let ciphernode_selector = diff --git a/crates/ciphernode-builder/src/event_system.rs b/crates/ciphernode-builder/src/event_system.rs index 8f58d121ea..055030f417 100644 --- a/crates/ciphernode-builder/src/event_system.rs +++ b/crates/ciphernode-builder/src/event_system.rs @@ -7,21 +7,19 @@ use crate::get_enclave_event_bus; use actix::{Actor, Addr, Handler, Recipient}; use anyhow::{anyhow, Result}; -use e3_aggregator::PublicKeyRepositoryFactory; use e3_data::{ - CommitLogEventLog, DataStore, InMemEventLog, InMemSequenceIndex, InMemStore, - RepositoriesFactory, SledSequenceIndex, SledStore, + CommitLogEventLog, DataStore, InMemEventLog, InMemSequenceIndex, InMemStore, SledSequenceIndex, + SledStore, }; -use e3_events::hlc::Hlc; +use e3_events::hlc_factory::HlcFactory; use e3_events::{ - AggregateConfig, BusHandle, EnclaveEvent, EventBus, EventBusConfig, EventStore, + AggregateConfig, BusHandle, Disabled, EnclaveEvent, EventBus, EventBusConfig, EventStore, EventStoreQueryBy, EventStoreRouter, EventSubscriber, EventType, InsertBatch, SeqAgg, Sequencer, SnapshotBuffer, StoreEventRequested, TsAgg, UpdateDestination, }; use e3_utils::enumerate_path; use once_cell::sync::OnceCell; use std::collections::HashMap; -use std::hash::{DefaultHasher, Hash, Hasher}; use std::path::PathBuf; struct InMemBackend { @@ -46,7 +44,7 @@ struct PersistedBackend { } impl PersistedBackend { - fn get_or_init_store(&self, handle: &BusHandle) -> Result> { + fn get_or_init_store(&self, handle: &BusHandle) -> Result> { self.store .get_or_try_init(|| SledStore::new(handle, &self.sled_path)) .cloned() @@ -75,8 +73,6 @@ pub enum EventStoreAddrs { /// - **WriteBuffer** for batching inserts from actors into a snapshot /// pub struct EventSystem { - /// A nodes id to be used as a tiebreaker in logical clock timestamp differentiation - node_id: u32, /// EventSystem backend either persisted or in memory backend: EventSystemBackend, /// WriteBuffer for batching inserts from actors into a snapshot @@ -86,9 +82,7 @@ pub struct EventSystem { /// EventSystem eventbus eventbus: OnceCell>>, /// EventSystem BusHandle - handle: OnceCell, - /// Hlc override - hlc: OnceCell, + handle: OnceCell>, /// Central configuration for aggregates, including delays and other settings aggregate_config: OnceCell, /// Cached EventStoreAddrs for idempotency @@ -97,14 +91,13 @@ pub struct EventSystem { impl EventSystem { /// Create a new in memory EventSystem with default settings - pub fn new(name: &str) -> Self { - EventSystem::in_mem(name) + pub fn new() -> Self { + EventSystem::in_mem() } /// Create an in memory EventSystem - pub fn in_mem(node_id: &str) -> Self { + pub fn in_mem() -> Self { Self { - node_id: EventSystem::node_id(node_id), backend: EventSystemBackend::InMem(InMemBackend { eventstores: OnceCell::new(), store: OnceCell::new(), @@ -113,16 +106,14 @@ impl EventSystem { sequencer: OnceCell::new(), eventbus: OnceCell::new(), handle: OnceCell::new(), - hlc: OnceCell::new(), aggregate_config: OnceCell::new(), eventstore_addrs: OnceCell::new(), } } /// Create an in memory EventSystem with a given store - pub fn in_mem_from_store(node_id: &str, store: &Addr) -> Self { + pub fn in_mem_from_store(store: &Addr) -> Self { Self { - node_id: EventSystem::node_id(node_id), backend: EventSystemBackend::InMem(InMemBackend { eventstores: OnceCell::new(), store: OnceCell::from(store.to_owned()), @@ -131,16 +122,14 @@ impl EventSystem { sequencer: OnceCell::new(), eventbus: OnceCell::new(), handle: OnceCell::new(), - hlc: OnceCell::new(), aggregate_config: OnceCell::new(), eventstore_addrs: OnceCell::new(), } } /// Create a persisted EventSystem with datafiles at the given paths - pub fn persisted(node_id: &str, log_path: PathBuf, sled_path: PathBuf) -> Self { + pub fn persisted(log_path: PathBuf, sled_path: PathBuf) -> Self { Self { - node_id: EventSystem::node_id(node_id), backend: EventSystemBackend::Persisted(PersistedBackend { log_path, sled_path, @@ -151,7 +140,6 @@ impl EventSystem { sequencer: OnceCell::new(), eventbus: OnceCell::new(), handle: OnceCell::new(), - hlc: OnceCell::new(), aggregate_config: OnceCell::new(), eventstore_addrs: OnceCell::new(), } @@ -171,12 +159,6 @@ impl EventSystem { self } - /// Add an injected hlc - pub fn with_hlc(self, hlc: Hlc) -> Self { - let _ = self.hlc.set(hlc); - self - } - /// Add aggregate configuration including delays and other settings pub fn with_aggregate_config(self, config: AggregateConfig) -> Self { let _ = self.aggregate_config.set(config); @@ -321,18 +303,11 @@ impl EventSystem { } } - /// Get an instance of the Hlc - pub fn hlc(&self) -> Result { - self.hlc - .get_or_try_init(|| Ok(Hlc::new(self.node_id))) - .cloned() - } - /// Get the BusHandle - pub fn handle(&self) -> Result { + pub fn handle(&self) -> Result> { self.handle .get_or_try_init(|| { - let handle = BusHandle::new(self.eventbus(), self.sequencer()?, self.hlc()?); + let handle = BusHandle::new(self.eventbus(), self.sequencer()?, HlcFactory::new()); // Buffer subscribes to all events first // This is important so as to open up a batch for each sequence handle.subscribe(EventType::All, self.buffer()?.recipient()); @@ -364,12 +339,6 @@ impl EventSystem { Ok(store) } - - fn node_id(name: &str) -> u32 { - let mut hasher = DefaultHasher::new(); - name.hash(&mut hasher); - hasher.finish() as u32 - } } struct NoopBatchReceiver; @@ -481,7 +450,7 @@ mod tests { async fn test_persisted() -> Result<()> { let _guard = with_tracing("debug"); let tmp = TempDir::new().unwrap(); - let system = EventSystem::persisted("cn2", tmp.path().join("log"), tmp.path().join("sled")); + let system = EventSystem::persisted(tmp.path().join("log"), tmp.path().join("sled")); let _handle = system.handle().expect("Failed to get handle"); system.store().expect("Failed to get store"); Ok(()) @@ -490,7 +459,7 @@ mod tests { #[actix::test] async fn test_in_mem() { let eventbus = EventBus::::default().start(); - let system = EventSystem::in_mem("cn1").with_event_bus(eventbus); + let system = EventSystem::in_mem().with_event_bus(eventbus); let _handle = system.handle().expect("Failed to get handle"); system.store().expect("Failed to get store"); @@ -506,11 +475,11 @@ mod tests { delays.insert(AggregateId::new(0), Duration::from_secs(1)); // Ag0 is default let config = AggregateConfig::new(delays); - let system = EventSystem::in_mem("cn1") + let system = EventSystem::in_mem() .with_fresh_bus() .with_aggregate_config(config); - let handle = system.handle()?; + let handle = system.handle()?.enable("test"); let datastore = system.store()?; let buffer = system.buffer()?; @@ -662,7 +631,7 @@ mod tests { let aggregate_config = AggregateConfig::new(delays); // Test in-memory eventstores - let system = EventSystem::in_mem("test_multi").with_aggregate_config(aggregate_config); + let system = EventSystem::in_mem().with_aggregate_config(aggregate_config); let Ok(EventStoreAddrs::InMem(addrs)) = system.eventstore_addrs() else { panic!("Expected InMem event store addrs"); }; @@ -676,12 +645,9 @@ mod tests { // Test persistent eventstores let tmp = TempDir::new().unwrap(); - let persisted_system = EventSystem::persisted( - "test_persisted", - tmp.path().join("log"), - tmp.path().join("sled"), - ) - .with_aggregate_config(AggregateConfig::new(HashMap::new())); + let persisted_system = + EventSystem::persisted(tmp.path().join("log"), tmp.path().join("sled")) + .with_aggregate_config(AggregateConfig::new(HashMap::new())); let Ok(EventStoreAddrs::Persisted(addrs)) = persisted_system.eventstore_addrs() else { panic!("Expected Persisted event store addrs"); diff --git a/crates/ciphernode-builder/src/eventbus_factory.rs b/crates/ciphernode-builder/src/eventbus_factory.rs index d2ddc5b6b0..268678ea2a 100644 --- a/crates/ciphernode-builder/src/eventbus_factory.rs +++ b/crates/ciphernode-builder/src/eventbus_factory.rs @@ -7,7 +7,10 @@ use actix::Actor; use actix::Addr; use e3_config::AppConfig; +use e3_data::Repositories; +use e3_events::Disabled; use e3_events::EventType; +use e3_evm::EthPrivateKeyRepositoryFactory; use once_cell::sync::Lazy; use std::any::Any; use std::any::TypeId; @@ -100,9 +103,9 @@ pub fn get_error_collector() -> Addr> { EventBusFactory::instance().get_error_collector() } -pub fn get_enclave_bus_handle(config: &AppConfig) -> anyhow::Result { +pub fn get_enclave_bus_handle() -> anyhow::Result> { let bus = get_enclave_event_bus(); - let system = EventSystem::new(&config.name()).with_event_bus(bus); + let system = EventSystem::new().with_event_bus(bus); system.store()?; // Ensure store is initialized before returning to avoid potentially dropping // events. Ok(system.handle()?) diff --git a/crates/cli/src/start.rs b/crates/cli/src/start.rs index a317588d71..2eada86c1e 100644 --- a/crates/cli/src/start.rs +++ b/crates/cli/src/start.rs @@ -13,9 +13,6 @@ use tracing::{info, instrument}; #[instrument(skip_all)] pub async fn execute(mut config: AppConfig, peers: Vec) -> Result<()> { owo(); - let Some(address) = config.address() else { - return Err(anyhow!("You must provide an address")); - }; // add cli peers to the config config.add_peers(peers); @@ -28,7 +25,6 @@ pub async fn execute(mut config: AppConfig, peers: Vec) -> Result<()> { } => { e3_entrypoint::start::aggregator_start::execute( &config, - address, pubkey_write_path, plaintext_write_path, ) @@ -36,13 +32,13 @@ pub async fn execute(mut config: AppConfig, peers: Vec) -> Result<()> { } // Launch in ciphernode configuration - NodeRole::Ciphernode => e3_entrypoint::start::start::execute(&config, address).await?, + NodeRole::Ciphernode => e3_entrypoint::start::start::execute(&config).await?, }; info!( "LAUNCHING CIPHERNODE: ({}/{}/{})", config.name(), - address, + node.address, node.peer_id ); diff --git a/crates/config/src/app_config.rs b/crates/config/src/app_config.rs index 35cd725f52..89cc8a49a3 100644 --- a/crates/config/src/app_config.rs +++ b/crates/config/src/app_config.rs @@ -252,9 +252,7 @@ impl AppConfig { chains: config.chains, peers: vec![], paths, - config_yaml: config - .found_config_file - .expect("config_yaml should always be set after loading config"), + config_yaml: config.found_config_file.unwrap_or_default(), otel: config.otel, autopassword: node.autopassword, autowallet: node.autowallet, diff --git a/crates/data/src/sled_store.rs b/crates/data/src/sled_store.rs index 12cc3eeb0b..bae983fdb4 100644 --- a/crates/data/src/sled_store.rs +++ b/crates/data/src/sled_store.rs @@ -7,7 +7,10 @@ use crate::SledDb; use actix::{Actor, ActorContext, Addr, Handler}; use anyhow::Result; -use e3_events::{prelude::*, BusHandle, EType, EnclaveEvent, EnclaveEventData, EventType}; +use e3_events::{ + prelude::*, BusHandle, EType, EnclaveEvent, EnclaveEventData, + EnclaveUnsequencedErrorDispatcher, EventType, +}; use e3_events::{Get, Insert, InsertBatch, InsertSync, Remove}; use e3_utils::MAILBOX_LIMIT; use std::path::PathBuf; @@ -15,7 +18,7 @@ use tracing::{error, info}; pub struct SledStore { db: Option, - bus: BusHandle, // Only used for Shutdown + bus: Box, // Only used for Shutdown } impl Actor for SledStore { @@ -26,13 +29,18 @@ impl Actor for SledStore { } impl SledStore { - pub fn new(bus: &BusHandle, path: &PathBuf) -> Result> { + pub fn new(bus: &BusHandle, path: &PathBuf) -> Result> { + // Note we pass in a generic BusHandle which supports the err method for passing on errors. + // This was as stores are required before we can initialize the BusHandle to retrieve the + // address so we have a unique node_id. + // If BusHandle is Disabled that is fine as our subscriptions and error publishing function + // remains intact despite it being enabled elsewhere at a later point info!("Starting SledStore with {:?}", path); let db = SledDb::new(path, "datastore")?; let store = Self { db: Some(db), - bus: bus.clone(), + bus: Box::new(bus.clone()), } .start(); diff --git a/crates/entrypoint/src/helpers/datastore.rs b/crates/entrypoint/src/helpers/datastore.rs index 692ec690df..eea5ee5a09 100644 --- a/crates/entrypoint/src/helpers/datastore.rs +++ b/crates/entrypoint/src/helpers/datastore.rs @@ -12,9 +12,9 @@ use e3_ciphernode_builder::get_enclave_bus_handle; use e3_config::AppConfig; use e3_data::{DataStore, InMemStore, SledDb, SledStore}; use e3_data::{Repositories, RepositoriesFactory}; -use e3_events::BusHandle; +use e3_events::{BusHandle, Disabled}; -pub fn get_sled_store(bus: &BusHandle, db_file: &PathBuf) -> Result { +pub fn get_sled_store(bus: &BusHandle, db_file: &PathBuf) -> Result { Ok((&SledStore::new(bus, db_file)?).into()) } @@ -22,7 +22,7 @@ pub fn get_in_mem_store() -> DataStore { (&InMemStore::new(true).start()).into() } -pub fn setup_datastore(config: &AppConfig, bus: &BusHandle) -> Result { +pub fn setup_datastore(config: &AppConfig, bus: &BusHandle) -> Result { let store: DataStore = if !config.use_in_mem_store() { get_sled_store(&bus, &config.db_file())? } else { @@ -32,7 +32,7 @@ pub fn setup_datastore(config: &AppConfig, bus: &BusHandle) -> Result } pub fn get_repositories(config: &AppConfig) -> Result { - let bus = get_enclave_bus_handle(config)?; + let bus = get_enclave_bus_handle()?; let store = setup_datastore(config, &bus)?; Ok(store.repositories()) } diff --git a/crates/entrypoint/src/start/aggregator_start.rs b/crates/entrypoint/src/start/aggregator_start.rs index 3032e43d0c..1ad2275633 100644 --- a/crates/entrypoint/src/start/aggregator_start.rs +++ b/crates/entrypoint/src/start/aggregator_start.rs @@ -19,14 +19,12 @@ use std::{ pub async fn execute( config: &AppConfig, - address: Address, pubkey_write_path: Option, plaintext_write_path: Option, ) -> Result { let rng = Arc::new(Mutex::new(ChaCha20Rng::from_rng(OsRng)?)); let cipher = Arc::new(Cipher::from_file(config.key_file()).await?); - let node = CiphernodeBuilder::new(&config.name(), rng.clone(), cipher.clone()) - .with_address(&address.to_string()) + let node = CiphernodeBuilder::new(rng.clone(), cipher.clone()) .with_persistence(&config.log_file(), &config.db_file()) .with_chains(&config.chains()) .with_sortition_score() diff --git a/crates/entrypoint/src/start/start.rs b/crates/entrypoint/src/start/start.rs index b541a38d16..6a16d9d5cd 100644 --- a/crates/entrypoint/src/start/start.rs +++ b/crates/entrypoint/src/start/start.rs @@ -4,7 +4,6 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -use alloy::primitives::Address; use anyhow::Result; use e3_ciphernode_builder::{CiphernodeBuilder, CiphernodeHandle}; use e3_config::AppConfig; @@ -16,7 +15,7 @@ use std::sync::{Arc, Mutex}; use tracing::instrument; #[instrument(name = "app", skip_all)] -pub async fn execute(config: &AppConfig, address: Address) -> Result { +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 zk_config = e3_zk_prover::ZkConfig::fetch_or_default().await; @@ -28,8 +27,7 @@ pub async fn execute(config: &AppConfig, address: Address) -> Result { /// EventBus that actors can consume sequenced events from event_bus: Addr>>, /// Sequencer that new events should be produced from sequencer: Addr, /// Hlc clock used to time all events created on this BusHandle - #[derivative(Debug = "ignore")] - hlc: Arc, + #[derivative(Debug = "ignore", PartialEq = "ignore")] + hlc: HlcFactory, /// Temporary context for events the bus publishes ctx: Option>, + #[derivative(Debug = "ignore", PartialEq = "ignore")] + _state: PhantomData, } -impl BusHandle { - /// Create a new BusHandle +impl BusHandle { + /// Create a new disabled BusHandle. Call `enable()` or `enable_with_hlc()` to activate it. pub fn new( event_bus: Addr>>, sequencer: Addr, - hlc: Hlc, + hlc: HlcFactory, ) -> Self { Self { event_bus, sequencer, - hlc: Arc::new(hlc), + hlc, + ctx: None, + _state: PhantomData, + } + } + + /// Enable the BusHandle by providing a node ID string used to create the HLC clock. + pub fn enable(self, node_id: &str) -> BusHandle { + let hlc = Hlc::from_str(node_id); + self.hlc.enable(hlc); + BusHandle { + event_bus: self.event_bus, + sequencer: self.sequencer, + hlc: self.hlc, ctx: None, + _state: PhantomData, } } + /// Enable the BusHandle by providing a pre-configured HLC clock. + pub fn enable_with_hlc(self, hlc: Hlc) -> BusHandle { + self.hlc.enable(hlc); + BusHandle { + event_bus: self.event_bus, + sequencer: self.sequencer, + hlc: self.hlc, + ctx: None, + _state: PhantomData, + } + } +} + +impl BusHandle { /// Return a HistoryCollector for examining events that have passed through on the events bus pub fn history(&self) -> Addr>> { EventBus::>::history(&self.event_bus) @@ -75,7 +122,7 @@ impl BusHandle { } /// Pipe events from this handle to the other handle only when the predicate returns true - pub fn pipe_to(&self, other: &BusHandle, predicate: F) + pub fn pipe_to(&self, other: &BusHandle, predicate: F) where F: Fn(&EnclaveEvent) -> bool + Unpin + 'static, { @@ -90,7 +137,7 @@ impl BusHandle { } } -impl EventPublisher> for BusHandle { +impl EventPublisher> for BusHandle { fn publish( &self, data: impl Into, @@ -129,7 +176,7 @@ impl EventPublisher> for BusHandle { } } -impl BusHandle { +impl BusHandle { fn publish_from_remote_impl( &self, data: impl Into, @@ -153,7 +200,7 @@ impl BusHandle { } } -impl ErrorDispatcher> for BusHandle { +impl ErrorDispatcher> for BusHandle { fn err(&self, err_type: EType, error: impl Into) { match self.event_from_error(err_type, error, self.get_ctx()) { Ok(evt) => self.sequencer.do_send(evt), @@ -162,7 +209,16 @@ impl ErrorDispatcher> for BusHandle { } } -impl EventFactory> for BusHandle { +impl EnclaveUnsequencedErrorDispatcher for BusHandle { + fn err(&self, err_type: EType, error: anyhow::Error) { + match self.event_from_error(err_type, error, self.get_ctx()) { + Ok(evt) => self.sequencer.do_send(evt), + Err(e) => error!("{e}"), + } + } +} + +impl EventFactory> for BusHandle { fn event_from( &self, data: impl Into, @@ -197,7 +253,7 @@ impl EventFactory> for BusHandle { } } -impl ErrorFactory> for BusHandle { +impl ErrorFactory> for BusHandle { fn event_from_error( &self, err_type: EType, @@ -209,7 +265,7 @@ impl ErrorFactory> for BusHandle { } } -impl EventSubscriber> for BusHandle { +impl EventSubscriber> for BusHandle { fn subscribe(&self, event_type: EventType, recipient: Recipient>) { self.event_bus .do_send(Subscribe::new(event_type, recipient)) @@ -232,7 +288,7 @@ impl EventSubscriber> for BusHandle { } } -impl EventContextManager for BusHandle { +impl EventContextManager for BusHandle { fn set_ctx(&mut self, value: C) where C: Into>, @@ -312,18 +368,22 @@ mod tests { // 1. setup up two separate busses with out of sync clocks A and B. B should be 30 seconds // faster than A. - let bus_a = EventSystem::new("a") + // + // NOTE: EventSystem::handle() must return BusHandle for this to compile. + let bus_a = EventSystem::new() .with_fresh_bus() - .with_hlc(Hlc::new(1).with_clock(move || now_micros().saturating_sub(30_000_000))) // Late - .handle()?; - let bus_b = EventSystem::new("b") + .handle()? + .enable_with_hlc( + Hlc::new(1).with_clock(move || now_micros().saturating_sub(30_000_000)), + ); // Late + let bus_b = EventSystem::new() .with_fresh_bus() - .with_hlc(Hlc::new(2)) - .handle()?; - let bus_c = EventSystem::new("c") + .handle()? + .enable_with_hlc(Hlc::new(2)); + let bus_c = EventSystem::new() .with_fresh_bus() - .with_hlc(Hlc::new(3)) - .handle()?; + .handle()? + .enable_with_hlc(Hlc::new(3)); let forwarder = Forwarder { dest: bus_c.clone(), @@ -399,7 +459,7 @@ pub struct BusHandlePipe where F: Fn(&EnclaveEvent) -> bool + Unpin + 'static, { - handle: BusHandle, + handle: BusHandle, predicate: F, } @@ -409,7 +469,7 @@ where { /// Create a new BusHandlePipe only forwarding events to the wrapped handle when the predicate /// function returns true - pub fn new(handle: BusHandle, predicate: F) -> Self { + pub fn new(handle: BusHandle, predicate: F) -> Self { Self { handle, predicate } } } diff --git a/crates/events/src/hlc.rs b/crates/events/src/hlc.rs index c476fdca62..9a96696f64 100644 --- a/crates/events/src/hlc.rs +++ b/crates/events/src/hlc.rs @@ -270,8 +270,11 @@ impl Hlc { .map(|d| d.as_micros() as u64) .map_err(|_| HlcError::ClockError) } +} - pub fn tick(&self) -> Result { +impl HlcMethods for Hlc { + type Error = HlcError; + fn tick(&self) -> Result { let now = self.now_physical()?; let mut inner = self.inner.lock().unwrap(); @@ -298,7 +301,7 @@ impl Hlc { }) } - pub fn receive(&self, remote: &HlcTimestamp) -> Result { + fn receive(&self, remote: &HlcTimestamp) -> Result { let now = self.now_physical()?; if remote.ts > now.saturating_add(self.max_drift) { @@ -352,6 +355,12 @@ impl Hlc { } } +pub trait HlcMethods { + type Error: From; + fn tick(&self) -> Result; + fn receive(&self, remote: &HlcTimestamp) -> Result; +} + #[cfg(test)] mod tests { use super::*; diff --git a/crates/events/src/hlc_factory.rs b/crates/events/src/hlc_factory.rs new file mode 100644 index 0000000000..b71b30ccca --- /dev/null +++ b/crates/events/src/hlc_factory.rs @@ -0,0 +1,102 @@ +use std::sync::{Arc, Mutex, PoisonError}; + +use thiserror::Error; + +use crate::hlc::{Hlc, HlcError, HlcMethods, HlcTimestamp}; + +#[derive(Debug, Error)] +pub enum HlcFactoryError { + #[error(transparent)] + Hlc(#[from] HlcError), + + #[error("HLC not initialized")] + NotReady, + + #[error("lock poisoned: {0}")] + LockPoisoned(String), +} + +impl From> for HlcFactoryError { + fn from(e: PoisonError) -> Self { + HlcFactoryError::LockPoisoned(e.to_string()) + } +} + +enum HlcState { + Init, + Ready(Hlc), +} + +impl HlcState { + pub fn ready(&mut self, hlc: Hlc) { + if let HlcState::Init = self { + *self = HlcState::Ready(hlc); + } + } + + pub fn is_ready(&self) -> bool { + if let HlcState::Init = self { + false + } else { + true + } + } +} + +impl HlcMethods for HlcState { + type Error = HlcFactoryError; + fn receive(&self, remote: &HlcTimestamp) -> Result { + let HlcState::Ready(hlc) = self else { + return Err(HlcFactoryError::NotReady); + }; + + Ok(hlc.receive(remote)?) + } + fn tick(&self) -> Result { + let HlcState::Ready(hlc) = self else { + return Err(HlcFactoryError::NotReady); + }; + + Ok(hlc.tick()?) + } +} + +/// This solves an issue where Hlc needs a node_id which is derived from the address but the +/// address is located in the store but the store needs the handle but the handle needs the hlc +/// which needs the node_id. +#[derive(Clone)] +pub struct HlcFactory { + hlc: Arc>, +} + +impl HlcFactory { + pub fn new() -> Self { + Self { + hlc: Arc::new(Mutex::new(HlcState::Init)), + } + } + + pub fn enable(&self, hlc: Hlc) { + let mut guard = self.hlc.lock().unwrap(); + guard.ready(hlc); + } + + pub fn is_ready(&self) -> bool { + match self.hlc.lock() { + Err(_) => false, + Ok(g) => g.is_ready(), + } + } +} + +impl HlcMethods for HlcFactory { + type Error = HlcFactoryError; + fn tick(&self) -> Result { + let guard = self.hlc.lock()?; + guard.tick() + } + fn receive(&self, remote: &HlcTimestamp) -> Result { + let guard = self.hlc.lock()?; + guard.receive(remote) + } +} diff --git a/crates/events/src/lib.rs b/crates/events/src/lib.rs index d772e652bd..2d0e7b9050 100644 --- a/crates/events/src/lib.rs +++ b/crates/events/src/lib.rs @@ -17,6 +17,7 @@ mod events; mod eventstore; mod eventstore_router; pub mod hlc; +pub mod hlc_factory; mod into_key; mod ordered_set; pub mod prelude; diff --git a/crates/events/src/sequencer.rs b/crates/events/src/sequencer.rs index cb8a929243..976143f742 100644 --- a/crates/events/src/sequencer.rs +++ b/crates/events/src/sequencer.rs @@ -74,8 +74,8 @@ mod tests { #[actix::test] async fn it_adds_seqence_numbers_to_events() -> anyhow::Result<()> { - let system = EventSystem::new("test"); - let bus = system.handle()?; + let system = EventSystem::new(); + let bus = system.handle()?.enable("test"); let history = bus.history(); let event_data = vec![ diff --git a/crates/evm/src/evm_chain_gateway.rs b/crates/evm/src/evm_chain_gateway.rs index ca43f25d7f..65c7677fc4 100644 --- a/crates/evm/src/evm_chain_gateway.rs +++ b/crates/evm/src/evm_chain_gateway.rs @@ -279,8 +279,8 @@ mod tests { .finish(), ); - let system = EventSystem::new("test").with_fresh_bus(); - let bus: BusHandle = system.handle()?; + let system = EventSystem::new().with_fresh_bus(); + let bus: BusHandle = system.handle()?.enable("test"); let history_collector = bus.history(); diff --git a/crates/evm/tests/integration.rs b/crates/evm/tests/integration.rs index a2b0736d6b..6810925ee8 100644 --- a/crates/evm/tests/integration.rs +++ b/crates/evm/tests/integration.rs @@ -109,8 +109,8 @@ async fn evm_reader() -> Result<()> { .await?, ); let contract = EmitLogs::deploy(provider.provider()).await?; - let system = EventSystem::new("test").with_fresh_bus(); - let bus = system.handle()?; + let system = EventSystem::new().with_fresh_bus(); + let bus = system.handle()?.enable("test"); let history_collector = bus.history(); let chain_id = provider.chain_id(); @@ -179,8 +179,8 @@ async fn ensure_historical_events() -> Result<()> { let contract = EmitLogs::deploy(provider.provider()).await?; let contract_address = contract.address().clone(); let chain_id = provider.chain_id(); - let system = EventSystem::new("test").with_fresh_bus(); - let bus = system.handle()?; + let system = EventSystem::new().with_fresh_bus(); + let bus = system.handle()?.enable("test"); let history_collector = bus.history(); let historical_msgs = vec!["these", "are", "historical", "events"]; let live_events = vec!["these", "events", "are", "live"]; diff --git a/crates/net/src/document_publisher.rs b/crates/net/src/document_publisher.rs index 3669b96903..610a556805 100644 --- a/crates/net/src/document_publisher.rs +++ b/crates/net/src/document_publisher.rs @@ -645,8 +645,8 @@ mod tests { let guard = tracing::subscriber::set_default(subscriber); - let system = EventSystem::new("test").with_fresh_bus(); - let bus = system.handle()?; + let system = EventSystem::new().with_fresh_bus(); + let bus = system.handle()?.enable("test"); let (net_cmd_tx, net_cmd_rx) = mpsc::channel(100); let (net_evt_tx, net_evt_rx) = broadcast::channel(100); let net_evt_rx = Arc::new(net_evt_rx); diff --git a/crates/net/src/net_event_buffer.rs b/crates/net/src/net_event_buffer.rs index 77f830eece..fd00109fa3 100644 --- a/crates/net/src/net_event_buffer.rs +++ b/crates/net/src/net_event_buffer.rs @@ -152,8 +152,8 @@ mod tests { #[actix::test] async fn test_buffers_until_sync_ended() -> Result<()> { // Setup - let system = EventSystem::new("test").with_fresh_bus(); - let bus = system.handle()?; + let system = EventSystem::new().with_fresh_bus(); + let bus = system.handle()?.enable("test"); let (input_tx, input_rx) = broadcast::channel(16); let mut output_rx = NetEventBuffer::setup(&bus, &input_rx); diff --git a/crates/sync/src/sync.rs b/crates/sync/src/sync.rs index 4d41bbb6ef..19d97b3dfc 100644 --- a/crates/sync/src/sync.rs +++ b/crates/sync/src/sync.rs @@ -327,8 +327,8 @@ mod tests { /// re-publishes later, causing it to be silently dropped. #[actix::test] async fn infrastructure_events_are_filtered_during_replay() -> anyhow::Result<()> { - let system = EventSystem::new("test-sync-replay").with_fresh_bus(); - let bus = system.handle()?; + let system = EventSystem::new().with_fresh_bus(); + let bus = system.handle()?.enable("test-sync-replay"); let history = bus.history(); let events: Vec = vec![ diff --git a/crates/test-helpers/src/ciphernode_system.rs b/crates/test-helpers/src/ciphernode_system.rs index 8d577670d1..042a34da09 100644 --- a/crates/test-helpers/src/ciphernode_system.rs +++ b/crates/test-helpers/src/ciphernode_system.rs @@ -207,7 +207,11 @@ mod tests { let bus = EventBus::::new(EventBusConfig { deduplicate: true }).start(); let history = EventBus::::history(&bus); let errors = EventBus::::error(&bus); - let bus = EventSystem::new("test").with_event_bus(bus).handle()?; + + let bus = EventSystem::new() + .with_event_bus(bus) + .handle()? + .enable("test"); let handle: JoinHandle> = tokio::spawn(async { Ok(()) }); Ok(CiphernodeHandle { diff --git a/crates/test-helpers/src/lib.rs b/crates/test-helpers/src/lib.rs index 16ef1612f8..f7e958cb04 100644 --- a/crates/test-helpers/src/lib.rs +++ b/crates/test-helpers/src/lib.rs @@ -99,7 +99,10 @@ pub fn get_common_setup( let moduli = param_set.moduli; let (crp_bytes, params) = create_crp_bytes_params(moduli, degree, plaintext_modulus, &seed); let crpoly = CommonRandomPoly::deserialize(&crp_bytes.clone(), ¶ms)?; - let handle = EventSystem::in_mem("cn1").with_event_bus(bus).handle()?; + let handle = EventSystem::in_mem() + .with_event_bus(bus) + .handle()? + .enable("cn1"); Ok((handle, rng, seed, params, crpoly, errors, history)) } diff --git a/crates/tests/tests/integration.rs b/crates/tests/tests/integration.rs index 95d271a878..e4bd5291ac 100644 --- a/crates/tests/tests/integration.rs +++ b/crates/tests/tests/integration.rs @@ -319,8 +319,8 @@ async fn test_trbfv_actor() -> Result<()> { let rng = create_shared_rng_from_u64(42); // Create "trigger" bus - let system = EventSystem::new("test").with_fresh_bus(); - let bus = system.handle()?; + let system = EventSystem::new().with_fresh_bus(); + let bus = system.handle()?.enable("test"); // Parameters (128bits of security) let params_raw = BfvParamSet::from(DEFAULT_BFV_PRESET).build_arc(); @@ -365,8 +365,7 @@ async fn test_trbfv_actor() -> Result<()> { .add_group(1, || async { let addr = rand_eth_addr(&rng); println!("Building collector {}!", addr); - CiphernodeBuilder::new(&addr, rng.clone(), cipher.clone()) - .with_address(&addr) + CiphernodeBuilder::new(rng.clone(), cipher.clone()) .testmode_with_history() .with_shared_taskpool(&task_pool) .with_multithread_concurrent_jobs(concurrent_jobs) @@ -386,8 +385,7 @@ async fn test_trbfv_actor() -> Result<()> { .add_group(19, || async { let addr = rand_eth_addr(&rng); println!("Building normal {}", &addr); - CiphernodeBuilder::new(&addr, rng.clone(), cipher.clone()) - .with_address(&addr) + CiphernodeBuilder::new(rng.clone(), cipher.clone()) .with_shared_taskpool(&task_pool) .with_multithread_concurrent_jobs(concurrent_jobs) .with_shared_multithread_report(&multithread_report) @@ -684,8 +682,8 @@ async fn test_p2p_actor_forwards_events_to_network() -> Result<()> { // Setup elements in test let (cmd_tx, mut cmd_rx) = mpsc::channel(100); // Transmit byte events to the network let (event_tx, _) = broadcast::channel(100); // Receive byte events from the network - let system = EventSystem::new("test"); - let bus = system.handle()?; + let system = EventSystem::new(); + let bus = system.handle()?.enable("test"); let history_collector = bus.history(); let event_rx = Arc::new(event_tx.subscribe()); // Pas cmd and event channels to NetEventTranslator @@ -772,8 +770,8 @@ async fn test_p2p_actor_forwards_events_to_bus() -> Result<()> { // Setup elements in test let (cmd_tx, _) = mpsc::channel(100); // Transmit byte events to the network let (event_tx, event_rx) = broadcast::channel(100); // Receive byte events from the network - let system = EventSystem::new("test").with_fresh_bus(); - let bus = system.handle()?; + let system = EventSystem::new().with_fresh_bus(); + let bus = system.handle()?.enable("test"); let history_collector = bus.history(); NetEventTranslator::setup(&bus, &cmd_tx, &Arc::new(event_rx), "mytopic"); @@ -838,9 +836,8 @@ async fn test_stopped_keyshares_retain_state() -> Result<()> { store: Option>, cipher: &Arc, ) -> Result { - let mut builder = CiphernodeBuilder::new(&addr, rng.clone(), cipher.clone()) + let mut builder = CiphernodeBuilder::new(rng.clone(), cipher.clone()) .with_trbfv() - .with_address(addr) .testmode_with_forked_bus(bus.event_bus()) .testmode_with_history() .testmode_with_errors() @@ -938,11 +935,12 @@ async fn test_stopped_keyshares_retain_state() -> Result<()> { ) }; - let bus = EventSystem::in_mem("cn2") + let bus = EventSystem::in_mem() .with_event_bus( EventBus::::new(EventBusConfig { deduplicate: true }).start(), ) - .handle()?; + .handle()? + .enable("cn2"); let cn1 = setup_local_ciphernode( &bus, &rng, @@ -1045,9 +1043,8 @@ async fn test_duplicate_e3_id_with_different_chain_id() -> Result<()> { store: Option>, cipher: &Arc, ) -> Result { - let mut builder = CiphernodeBuilder::new(&addr, rng.clone(), cipher.clone()) + let mut builder = CiphernodeBuilder::new(rng.clone(), cipher.clone()) .with_trbfv() - .with_address(addr) .testmode_with_forked_bus(bus.event_bus()) .testmode_with_history() .testmode_with_errors() From aa4b55f946bb56b66d80a22193d9aa41ce1fffff Mon Sep 17 00:00:00 2001 From: ryardley Date: Thu, 19 Feb 2026 06:04:06 +0000 Subject: [PATCH 20/45] remove redundant yaml manipulation --- crates/cli/src/wallet_set.rs | 2 - crates/entrypoint/src/config/mod.rs | 1 - crates/entrypoint/src/config/set_address.rs | 181 -------------------- 3 files changed, 184 deletions(-) delete mode 100644 crates/entrypoint/src/config/set_address.rs diff --git a/crates/cli/src/wallet_set.rs b/crates/cli/src/wallet_set.rs index 1c2c5e2579..94cb8fa9cd 100644 --- a/crates/cli/src/wallet_set.rs +++ b/crates/cli/src/wallet_set.rs @@ -31,9 +31,7 @@ pub fn ask_for_private_key(given_key: Option>) -> Result>) -> Result<()> { let input = ask_for_private_key(private_key)?; - let address = eth_address_from_private_key(&input)?; e3_entrypoint::wallet::set::execute(config, input).await?; - e3_entrypoint::config::set_address::execute(config, address)?; println!("Wallet key has been successfully stored and encrypted."); Ok(()) diff --git a/crates/entrypoint/src/config/mod.rs b/crates/entrypoint/src/config/mod.rs index 9a512d3740..598fd7416f 100644 --- a/crates/entrypoint/src/config/mod.rs +++ b/crates/entrypoint/src/config/mod.rs @@ -4,5 +4,4 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -pub mod set_address; pub mod setup; diff --git a/crates/entrypoint/src/config/set_address.rs b/crates/entrypoint/src/config/set_address.rs deleted file mode 100644 index cd96881ff7..0000000000 --- a/crates/entrypoint/src/config/set_address.rs +++ /dev/null @@ -1,181 +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 alloy::primitives::Address; -use anyhow::{Context, Result}; -use e3_config::AppConfig; -use serde_yaml::Value; -use std::fs; - -type YamlMap = serde_yaml::Mapping; - -/// Write an ethereum address into the appropriate node section of the config YAML -pub fn execute(config: &AppConfig, address: Address) -> Result<()> { - let path = config.config_yaml(); - let updated = apply_address(&config.name(), &fs::read_to_string(&path)?, address)?; - fs::write(&path, updated)?; - Ok(()) -} - -fn apply_address(name: &str, content: &str, address: Address) -> Result { - let addr = format!("{:?}", address); - let mut root: Value = serde_yaml::from_str(content)?; - - // Route to either `node.address` (default) or `nodes..address` (named) - let map = root - .as_mapping_mut() - .context("Expected YAML mapping at root")?; - if name == "_default" { - get_or_insert_map(map, "node") - .insert(Value::String("address".into()), Value::String(addr.clone())); - } else { - let nodes = get_or_insert_map(map, "nodes"); - get_or_insert_map(nodes, name) - .insert(Value::String("address".into()), Value::String(addr.clone())); - } - - // serde_yaml omits quotes; patch output so addresses are quoted strings - let yaml = serde_yaml::to_string(&root)?; - Ok(yaml.replace(&format!("address: {addr}"), &format!("address: \"{addr}\""))) -} - -// Retrieve or create a nested mapping under `key` -fn get_or_insert_map<'a>(map: &'a mut YamlMap, key: &str) -> &'a mut YamlMap { - map.entry(Value::String(key.into())) - .or_insert_with(|| Value::Mapping(YamlMap::new())) - .as_mapping_mut() - .expect("Expected mapping") -} - -#[cfg(test)] -mod tests { - use super::*; - use std::str::FromStr; - - const ADDR1: &str = "0x1234567890123456789012345678901234567890"; - const ADDR2: &str = "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0"; - - #[test] - fn test_apply_address_default() { - let result = apply_address( - "_default", - r#"node: - role: ciphernode -"#, - Address::from_str(ADDR1).unwrap(), - ) - .unwrap(); - assert_eq!( - result, - r#"node: - role: ciphernode - address: "0x1234567890123456789012345678901234567890" -"# - ); - } - - #[test] - fn test_apply_address_named_node() { - let result = apply_address( - "foo", - r#"node: - role: ciphernode -nodes: - foo: - role: aggregator -"#, - Address::from_str(ADDR2).unwrap(), - ) - .unwrap(); - assert_eq!( - result, - r#"node: - role: ciphernode -nodes: - foo: - role: aggregator - address: "0x9fe46736679d2d9a65f0992f2272de9f3c7fa6e0" -"# - ); - } - - #[test] - fn test_apply_address_default_existing_address() { - let result = apply_address( - "_default", - r#"node: - address: "0x0000000000000000000000000000000000000001" - role: ciphernode -"#, - Address::from_str(ADDR1).unwrap(), - ) - .unwrap(); - assert_eq!( - result, - r#"node: - address: "0x1234567890123456789012345678901234567890" - role: ciphernode -"# - ); - } - - #[test] - fn test_apply_address_named_node_existing_address() { - let result = apply_address( - "foo", - r#"nodes: - foo: - address: "0x0000000000000000000000000000000000000001" - role: aggregator -"#, - Address::from_str(ADDR2).unwrap(), - ) - .unwrap(); - assert_eq!( - result, - r#"nodes: - foo: - address: "0x9fe46736679d2d9a65f0992f2272de9f3c7fa6e0" - role: aggregator -"# - ); - } - - #[test] - fn test_apply_address_preserves_other_fields() { - let result = apply_address( - "_default", - r#"data_dir: "/mydata/enclave" -config_dir: "/myconfig/enclave" -chains: - - name: "hardhat" - rpc_url: "ws://localhost:8545" - contracts: - enclave: "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0" -node: - role: ciphernode - db_file: "./foo" -program: - risc0: - risc0_dev_mode: 0 -"#, - Address::from_str(ADDR1).unwrap(), - ) - .unwrap(); - for s in [ - "data_dir:", - "config_dir:", - "chains:", - "hardhat", - "program:", - "risc0_dev_mode: 0", - "db_file:", - "address:", - ] { - assert!(result.contains(s)); - } - } -} From 0260dfcd27fc4bb167d615fd98c771e8eff8a018 Mon Sep 17 00:00:00 2001 From: ryardley Date: Thu, 19 Feb 2026 06:05:05 +0000 Subject: [PATCH 21/45] headers --- crates/events/src/hlc_factory.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/crates/events/src/hlc_factory.rs b/crates/events/src/hlc_factory.rs index b71b30ccca..5141b0e218 100644 --- a/crates/events/src/hlc_factory.rs +++ b/crates/events/src/hlc_factory.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::{Arc, Mutex, PoisonError}; use thiserror::Error; From c1011f22fe9046861f8188394aba8706a2e37f24 Mon Sep 17 00:00:00 2001 From: ryardley Date: Thu, 19 Feb 2026 06:26:14 +0000 Subject: [PATCH 22/45] remove net commands --- crates/cli/src/ciphernode/mod.rs | 8 --- crates/cli/src/ciphernode/setup.rs | 31 +--------- crates/cli/src/cli.rs | 20 +----- crates/cli/src/main.rs | 4 -- crates/cli/src/net.rs | 61 ------------------- crates/cli/src/net_generate.rs | 16 ----- crates/cli/src/net_purge.rs | 15 ----- crates/cli/src/net_set.rs | 29 --------- crates/entrypoint/src/lib.rs | 1 - crates/entrypoint/src/net/keypair/generate.rs | 43 ------------- crates/entrypoint/src/net/keypair/mod.rs | 8 --- crates/entrypoint/src/net/keypair/set.rs | 34 ----------- crates/entrypoint/src/net/mod.rs | 8 --- crates/entrypoint/src/net/peer_id/mod.rs | 7 --- crates/entrypoint/src/net/peer_id/purge.rs | 16 ----- crates/entrypoint/src/wallet/set.rs | 22 ++++++- 16 files changed, 23 insertions(+), 300 deletions(-) delete mode 100644 crates/cli/src/net.rs delete mode 100644 crates/cli/src/net_generate.rs delete mode 100644 crates/cli/src/net_purge.rs delete mode 100644 crates/cli/src/net_set.rs delete mode 100644 crates/entrypoint/src/net/keypair/generate.rs delete mode 100644 crates/entrypoint/src/net/keypair/mod.rs delete mode 100644 crates/entrypoint/src/net/keypair/set.rs delete mode 100644 crates/entrypoint/src/net/mod.rs delete mode 100644 crates/entrypoint/src/net/peer_id/mod.rs delete mode 100644 crates/entrypoint/src/net/peer_id/purge.rs diff --git a/crates/cli/src/ciphernode/mod.rs b/crates/cli/src/ciphernode/mod.rs index b978ae2a89..6d1da0b36c 100644 --- a/crates/cli/src/ciphernode/mod.rs +++ b/crates/cli/src/ciphernode/mod.rs @@ -48,14 +48,6 @@ pub enum CiphernodeCommands { /// Wallet Private Key #[arg(short, long, value_parser = ensure_hex_zeroizing)] private_key: Option>, - - /// The network private key (ed25519) - #[arg(long = "net-keypair", short = 'n')] - net_keypair: Option, - - /// Autogenerate a new network keypair - #[arg(long = "generate-net-keypair", short = 'g')] - generate_net_keypair: bool, }, /// Manage ENCL license tokens and bonding state License { diff --git a/crates/cli/src/ciphernode/setup.rs b/crates/cli/src/ciphernode/setup.rs index 63be4ae519..6c2d7d9143 100644 --- a/crates/cli/src/ciphernode/setup.rs +++ b/crates/cli/src/ciphernode/setup.rs @@ -5,25 +5,22 @@ // or FITNESS FOR A PARTICULAR PURPOSE. use anyhow::Result; -use dialoguer::{theme::ColorfulTheme, Confirm, Input}; +use dialoguer::{theme::ColorfulTheme, Input}; use e3_entrypoint::config::setup; use e3_utils::eth_address_from_private_key; use std::path::PathBuf; use tracing::instrument; use zeroize::Zeroizing; -use crate::net::{NetCommands, NetKeypairCommands}; +use crate::password_set; use crate::password_set::ask_for_password; use crate::wallet_set::ask_for_private_key; -use crate::{net, password_set}; #[instrument(name = "app", skip_all)] pub async fn execute( rpc_url: Option, password: Option>, private_key: Option>, - net_keypair: Option, - generate_net_keypair: bool, ) -> Result<()> { let pw = ask_for_password(password)?; let rpc_url = match rpc_url { @@ -60,22 +57,6 @@ pub async fn execute( .interact_text()? .into(); - let net_keypair_command = if generate_net_keypair { - NetKeypairCommands::Generate - } else if net_keypair.is_none() { - let should_generate = Confirm::with_theme(&ColorfulTheme::default()) - .with_prompt("No net keypair specified. Would you like to generate one automatically?") - .default(true) - .interact()?; - if should_generate { - NetKeypairCommands::Generate - } else { - NetKeypairCommands::Set { net_keypair } - } - } else { - NetKeypairCommands::Set { net_keypair } - }; - // Execute let config = setup::execute(rpc_url, &config_dir, &address)?; @@ -83,14 +64,6 @@ pub async fn execute( e3_entrypoint::wallet::set::execute(&config, private_key).await?; - net::execute( - NetCommands::Keypair { - command: net_keypair_command, - }, - &config, - ) - .await?; - println!("Enclave configuration successfully created!"); Ok(()) diff --git a/crates/cli/src/cli.rs b/crates/cli/src/cli.rs index c3d0263456..c7d5f3cb58 100644 --- a/crates/cli/src/cli.rs +++ b/crates/cli/src/cli.rs @@ -8,13 +8,12 @@ use std::path::PathBuf; use crate::ciphernode::{self, CiphernodeCommands}; use crate::helpers::telemetry::{setup_simple_tracing, setup_tracing}; -use crate::net::NetCommands; use crate::nodes::{self, NodeCommands}; use crate::noir::NoirCommands; use crate::password::PasswordCommands; use crate::program::{self, ProgramCommands}; use crate::wallet::WalletCommands; -use crate::{init, net, noir, password, purge_all, rev, wallet}; +use crate::{init, noir, password, purge_all, rev, wallet}; use crate::{print_env, start}; use anyhow::{bail, Result}; use clap::{command, ArgAction, Parser, Subcommand}; @@ -104,16 +103,12 @@ impl Cli { rpc_url, password, private_key, - net_keypair, - generate_net_keypair, } } => { ciphernode::setup::execute( rpc_url, password, private_key, - net_keypair, - generate_net_keypair, ) .await?; println!("You can start your node using `enclave start`"); @@ -124,8 +119,6 @@ impl Cli { None, None, None, - None, - false, ) .await?; }, @@ -150,10 +143,6 @@ impl Cli { e3_entrypoint::password::set::autopassword(&config).await?; } - if config.autonetkey() { - e3_entrypoint::net::keypair::generate::autonetkey(&config).await?; - } - if config.autowallet() { e3_entrypoint::wallet::set::autowallet(&config).await?; } @@ -184,7 +173,6 @@ impl Cli { 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::Net { command } => net::execute(command, &config).await?, Commands::Noir { command } => noir::execute(command, &config).await?, Commands::Rev => rev::execute().await?, } @@ -279,12 +267,6 @@ pub enum Commands { command: WalletCommands, }, - /// Networking related commands - Net { - #[command(subcommand)] - command: NetCommands, - }, - /// Noir prover management and proof generation Noir { #[command(subcommand)] diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index 86b08d4187..e9313afe95 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -12,10 +12,6 @@ mod ciphernode; mod cli; pub mod helpers; mod init; -pub mod net; -mod net_generate; -mod net_purge; -mod net_set; mod nodes; mod nodes_daemon; mod nodes_down; diff --git a/crates/cli/src/net.rs b/crates/cli/src/net.rs deleted file mode 100644 index ee6029e583..0000000000 --- a/crates/cli/src/net.rs +++ /dev/null @@ -1,61 +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 anyhow::*; -use clap::Subcommand; -use e3_config::AppConfig; - -use crate::{net_generate, net_purge, net_set}; - -#[derive(Subcommand, Debug)] -pub enum NetCommands { - /// Generate new net keypair - Keypair { - #[command(subcommand)] - command: NetKeypairCommands, - }, - - /// Purge peer ID - #[command(name = "peer-id")] - PeerId { - #[command(subcommand)] - command: NetPeerIdCommands, - }, -} - -#[derive(Subcommand, Debug)] -pub enum NetKeypairCommands { - /// Generate new net keypair - Generate, - - /// Set net private key - Set { - #[arg(long = "net-keypair")] - net_keypair: Option, - }, -} - -#[derive(Subcommand, Debug)] -pub enum NetPeerIdCommands { - /// Purge peer ID - Purge, -} - -pub async fn execute(command: NetCommands, config: &AppConfig) -> Result<()> { - match command { - NetCommands::Keypair { command } => match command { - NetKeypairCommands::Generate => net_generate::execute(&config).await?, - NetKeypairCommands::Set { net_keypair } => { - net_set::execute(&config, net_keypair).await? - } - }, - NetCommands::PeerId { command } => match command { - NetPeerIdCommands::Purge => net_purge::execute(&config).await?, - }, - }; - - Ok(()) -} diff --git a/crates/cli/src/net_generate.rs b/crates/cli/src/net_generate.rs deleted file mode 100644 index 5cfac6f8ca..0000000000 --- a/crates/cli/src/net_generate.rs +++ /dev/null @@ -1,16 +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 anyhow::Result; -use e3_config::AppConfig; -use e3_entrypoint::net; - -pub async fn execute(config: &AppConfig) -> Result<()> { - let peer_id = net::keypair::generate::execute(config).await?; - println!("Generated new keypair with peer ID: {}", peer_id); - println!("Network keypair has been successfully generated and encrypted."); - Ok(()) -} diff --git a/crates/cli/src/net_purge.rs b/crates/cli/src/net_purge.rs deleted file mode 100644 index 8e6622c0ca..0000000000 --- a/crates/cli/src/net_purge.rs +++ /dev/null @@ -1,15 +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 anyhow::*; -use e3_config::AppConfig; -use e3_entrypoint::net; - -pub async fn execute(config: &AppConfig) -> Result<()> { - net::peer_id::purge::execute(config).await?; - println!("Peer ID has been purged. A new Peer ID will be generated upon restart."); - Ok(()) -} diff --git a/crates/cli/src/net_set.rs b/crates/cli/src/net_set.rs deleted file mode 100644 index c80930d9a9..0000000000 --- a/crates/cli/src/net_set.rs +++ /dev/null @@ -1,29 +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 anyhow::Result; -use dialoguer::{theme::ColorfulTheme, Password}; -use e3_config::AppConfig; -use e3_entrypoint::net::{self, keypair::set::validate_keypair_input}; - -pub async fn execute(config: &AppConfig, net_keypair: Option) -> Result<()> { - let input = if let Some(nkp) = net_keypair { - nkp - } else { - Password::with_theme(&ColorfulTheme::default()) - .with_prompt("Enter your network private key") - .validate_with(validate_keypair_input) - .interact()? - .trim() - .to_string() - }; - - net::keypair::set::execute(config, input).await?; - - println!("Network keypair has been successfully stored and encrypted."); - - Ok(()) -} diff --git a/crates/entrypoint/src/lib.rs b/crates/entrypoint/src/lib.rs index 7fbfbb0247..592464320e 100644 --- a/crates/entrypoint/src/lib.rs +++ b/crates/entrypoint/src/lib.rs @@ -6,7 +6,6 @@ pub mod config; pub mod helpers; -pub mod net; pub mod nodes; pub mod password; pub mod start; diff --git a/crates/entrypoint/src/net/keypair/generate.rs b/crates/entrypoint/src/net/keypair/generate.rs deleted file mode 100644 index 512cfbcb3f..0000000000 --- a/crates/entrypoint/src/net/keypair/generate.rs +++ /dev/null @@ -1,43 +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 anyhow::Result; -use e3_config::AppConfig; -use e3_crypto::Cipher; -use e3_net::NetRepositoryFactory; -use libp2p::{identity::Keypair, PeerId}; -use tracing::warn; -use zeroize::Zeroize; - -use crate::helpers::datastore::get_repositories; - -pub async fn execute(config: &AppConfig) -> Result { - let kp = Keypair::generate_ed25519(); - let peer_id = kp.public().to_peer_id(); - let mut bytes = kp.try_into_ed25519()?.to_bytes().to_vec(); - let cipher = Cipher::from_file(config.key_file()).await?; - let encrypted = cipher.encrypt_data(&mut bytes.clone())?; - let repositories = get_repositories(config)?; - bytes.zeroize(); - repositories.libp2p_keypair().write_sync(&encrypted).await?; - - Ok(peer_id) -} - -pub async fn autonetkey(config: &AppConfig) -> Result<()> { - let repositories = get_repositories(config)?; - if !repositories.libp2p_keypair().has().await { - warn!( - "Auto-generating network keypair because 'autonetkey: true' is set and no keypair exists. \ - This will create a NEW peer identity. If your data directory is not persistent \ - (e.g., running in Docker without volumes), a new identity will be generated on each restart, \ - which will cause network connectivity issues with other peers. \ - For production use, run 'enclave net keypair (generate)/(set --net-keypair )' once and ensure data persistence." - ); - execute(config).await?; - } - Ok(()) -} diff --git a/crates/entrypoint/src/net/keypair/mod.rs b/crates/entrypoint/src/net/keypair/mod.rs deleted file mode 100644 index 82736a3458..0000000000 --- a/crates/entrypoint/src/net/keypair/mod.rs +++ /dev/null @@ -1,8 +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. - -pub mod generate; -pub mod set; diff --git a/crates/entrypoint/src/net/keypair/set.rs b/crates/entrypoint/src/net/keypair/set.rs deleted file mode 100644 index 967a0bdbe7..0000000000 --- a/crates/entrypoint/src/net/keypair/set.rs +++ /dev/null @@ -1,34 +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 alloy::primitives::hex; -use anyhow::Result; -use e3_config::AppConfig; -use e3_crypto::Cipher; -use e3_net::NetRepositoryFactory; -use libp2p::identity::Keypair; - -use crate::helpers::datastore::get_repositories; - -fn create_keypair(input: &String) -> Result { - hex::check(&input)?; - let kp = Keypair::ed25519_from_bytes(hex::decode(&input)?)?; - Ok(kp) -} - -pub fn validate_keypair_input(input: &String) -> Result<()> { - create_keypair(input).map(|_| ()) -} - -pub async fn execute(config: &AppConfig, value: String) -> Result<()> { - let kp = create_keypair(&value)?; - let mut secret = kp.try_into_ed25519()?.to_bytes().to_vec(); - let cipher = Cipher::from_file(config.key_file()).await?; - let encrypted = cipher.encrypt_data(&mut secret)?; - let repositories = get_repositories(config)?; - repositories.libp2p_keypair().write_sync(&encrypted).await?; - Ok(()) -} diff --git a/crates/entrypoint/src/net/mod.rs b/crates/entrypoint/src/net/mod.rs deleted file mode 100644 index cb89278823..0000000000 --- a/crates/entrypoint/src/net/mod.rs +++ /dev/null @@ -1,8 +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. - -pub mod keypair; -pub mod peer_id; diff --git a/crates/entrypoint/src/net/peer_id/mod.rs b/crates/entrypoint/src/net/peer_id/mod.rs deleted file mode 100644 index 66ca85f79f..0000000000 --- a/crates/entrypoint/src/net/peer_id/mod.rs +++ /dev/null @@ -1,7 +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. - -pub mod purge; diff --git a/crates/entrypoint/src/net/peer_id/purge.rs b/crates/entrypoint/src/net/peer_id/purge.rs deleted file mode 100644 index e025591236..0000000000 --- a/crates/entrypoint/src/net/peer_id/purge.rs +++ /dev/null @@ -1,16 +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 crate::helpers::datastore::get_repositories; -use anyhow::*; -use e3_config::AppConfig; -use e3_net::NetRepositoryFactory; - -pub async fn execute(config: &AppConfig) -> Result<()> { - let repositories = get_repositories(config)?; - repositories.libp2p_keypair().clear(); - Ok(()) -} diff --git a/crates/entrypoint/src/wallet/set.rs b/crates/entrypoint/src/wallet/set.rs index e82e311296..c19284937f 100644 --- a/crates/entrypoint/src/wallet/set.rs +++ b/crates/entrypoint/src/wallet/set.rs @@ -9,6 +9,8 @@ use anyhow::{anyhow, Result}; use e3_config::AppConfig; use e3_crypto::Cipher; use e3_evm::EthPrivateKeyRepositoryFactory; +use e3_net::NetRepositoryFactory; +use libp2p::identity::Keypair; use zeroize::{Zeroize, Zeroizing}; use crate::helpers::{datastore::get_repositories, rand::generate_random_bytes}; @@ -23,12 +25,28 @@ pub fn validate_private_key(input: &String) -> Result<()> { pub async fn execute(config: &AppConfig, input: Zeroizing) -> Result<()> { let cipher = Cipher::from_file(config.key_file()).await?; - let encrypted = cipher.encrypt_data(&mut input.as_bytes().to_vec())?; + let mut bytes = input.as_bytes().to_vec(); + + let mut keypair = Keypair::ed25519_from_bytes(&mut bytes.clone())? // this zeroizes bytes so cloning ok + .try_into_ed25519()? + .to_bytes() + .to_vec(); + + let encrypted_private_key = cipher.encrypt_data(&mut bytes)?; // This zeroizes input + let encrypted_keypair = cipher.encrypt_data(&mut keypair)?; // This zeroizes input + + // Save the encrypted keys let repositories = get_repositories(config)?; repositories .eth_private_key() - .write_sync(&encrypted) + .write_sync(&encrypted_private_key) .await?; + + repositories + .libp2p_keypair() + .write_sync(&encrypted_keypair) + .await?; + Ok(()) } From 578968176453b228e88b3e5085a35eb16a417205 Mon Sep 17 00:00:00 2001 From: ryardley Date: Thu, 19 Feb 2026 06:29:25 +0000 Subject: [PATCH 23/45] add config location instructions --- crates/cli/src/ciphernode/setup.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/crates/cli/src/ciphernode/setup.rs b/crates/cli/src/ciphernode/setup.rs index 6c2d7d9143..7db881c562 100644 --- a/crates/cli/src/ciphernode/setup.rs +++ b/crates/cli/src/ciphernode/setup.rs @@ -65,6 +65,11 @@ pub async fn execute( e3_entrypoint::wallet::set::execute(&config, private_key).await?; println!("Enclave configuration successfully created!"); + println!("Configuration has been written to {:?}", config_dir); + println!( + "Run future commands from within this directory tree, or pass --config {:?}", + config_dir + ); Ok(()) } From edb6ad78fdd8c3b37bf0eef11cdd7430b0346003 Mon Sep 17 00:00:00 2001 From: ryardley Date: Thu, 19 Feb 2026 11:58:40 +0000 Subject: [PATCH 24/45] Add wallet get command and improve setup output - Add to retrieve wallet address - Return address and peer_id from wallet set for display - Add Flush message to ensure data is persisted to disk - Show address and peer_id after setup with colorized output --- crates/cli/src/ciphernode/setup.rs | 15 +++++--- crates/cli/src/main.rs | 1 + crates/cli/src/wallet.rs | 5 ++- crates/cli/src/wallet_get.rs | 15 ++++++++ crates/cli/src/wallet_set.rs | 1 - crates/data/src/data_store.rs | 13 ++++++- crates/data/src/in_mem.rs | 10 ++++- crates/data/src/sled_db.rs | 6 ++- crates/data/src/sled_store.rs | 20 ++++++++-- crates/entrypoint/src/wallet/get.rs | 30 +++++++++++++++ crates/entrypoint/src/wallet/mod.rs | 1 + crates/entrypoint/src/wallet/set.rs | 59 +++++++++++++++++++++++------ crates/events/src/bus_handle.rs | 1 + crates/events/src/data_events.rs | 4 ++ 14 files changed, 155 insertions(+), 26 deletions(-) create mode 100644 crates/cli/src/wallet_get.rs create mode 100644 crates/entrypoint/src/wallet/get.rs diff --git a/crates/cli/src/ciphernode/setup.rs b/crates/cli/src/ciphernode/setup.rs index 7db881c562..d5c5ced2e6 100644 --- a/crates/cli/src/ciphernode/setup.rs +++ b/crates/cli/src/ciphernode/setup.rs @@ -7,7 +7,7 @@ use anyhow::Result; use dialoguer::{theme::ColorfulTheme, Input}; use e3_entrypoint::config::setup; -use e3_utils::eth_address_from_private_key; +use e3_utils::{colorize, eth_address_from_private_key, Color}; use std::path::PathBuf; use tracing::instrument; use zeroize::Zeroizing; @@ -28,7 +28,7 @@ pub async fn execute( setup::validate_rpc_url(&url)?; url } - None => Input::::new() + None => Input::::with_theme(&ColorfulTheme::default()) .with_prompt("Enter WebSocket devnet RPC URL") .default("wss://ethereum-sepolia-rpc.publicnode.com".to_string()) .validate_with(setup::validate_rpc_url) @@ -62,13 +62,18 @@ pub async fn execute( password_set::execute(&config, Some(pw)).await?; - e3_entrypoint::wallet::set::execute(&config, private_key).await?; + let (address, peer_id) = e3_entrypoint::wallet::set::execute(&config, private_key).await?; + let abs_dir = config_dir.canonicalize()?; println!("Enclave configuration successfully created!"); - println!("Configuration has been written to {:?}", config_dir); + println!(""); + println!(" Your address:\t{}", colorize(address, Color::Cyan)); + println!(" Your peer id:\t{}", colorize(peer_id, Color::Cyan)); + println!(""); + println!("Configuration has been written to {:?}", abs_dir); println!( "Run future commands from within this directory tree, or pass --config {:?}", - config_dir + abs_dir ); Ok(()) diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index e9313afe95..fcd372cc71 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -32,6 +32,7 @@ mod purge_all; mod rev; mod start; mod wallet; +mod wallet_get; mod wallet_set; const OWO: &str = r#" diff --git a/crates/cli/src/wallet.rs b/crates/cli/src/wallet.rs index a8648d6e43..36225b6ebc 100644 --- a/crates/cli/src/wallet.rs +++ b/crates/cli/src/wallet.rs @@ -9,7 +9,7 @@ use clap::Subcommand; use e3_config::AppConfig; use zeroize::Zeroizing; -use crate::{helpers::ensure_hex_zeroizing, wallet_set}; +use crate::{helpers::ensure_hex_zeroizing, wallet_get, wallet_set}; #[derive(Subcommand, Debug)] pub enum WalletCommands { @@ -20,11 +20,14 @@ pub enum WalletCommands { #[arg(long = "private-key", value_parser = ensure_hex_zeroizing)] private_key: Option>, }, + /// Get your wallet address + Get, } pub async fn execute(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?, }; Ok(()) diff --git a/crates/cli/src/wallet_get.rs b/crates/cli/src/wallet_get.rs new file mode 100644 index 0000000000..383ae256f1 --- /dev/null +++ b/crates/cli/src/wallet_get.rs @@ -0,0 +1,15 @@ +// 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 anyhow::Result; +use e3_config::AppConfig; + +pub async fn execute(config: &AppConfig) -> Result<()> { + let address = e3_entrypoint::wallet::get::execute(config).await?; + println!("{}", address); + + Ok(()) +} diff --git a/crates/cli/src/wallet_set.rs b/crates/cli/src/wallet_set.rs index 94cb8fa9cd..79ac898ad9 100644 --- a/crates/cli/src/wallet_set.rs +++ b/crates/cli/src/wallet_set.rs @@ -8,7 +8,6 @@ use anyhow::Result; use dialoguer::{theme::ColorfulTheme, Password}; use e3_config::AppConfig; use e3_entrypoint::wallet::set::validate_private_key; -use e3_utils::eth_address_from_private_key; use zeroize::Zeroizing; pub fn ask_for_private_key(given_key: Option>) -> Result> { diff --git a/crates/data/src/data_store.rs b/crates/data/src/data_store.rs index 64e8ea4deb..49578a335f 100644 --- a/crates/data/src/data_store.rs +++ b/crates/data/src/data_store.rs @@ -12,7 +12,7 @@ use anyhow::anyhow; use anyhow::Context; use anyhow::Result; use e3_events::IntoKey; -use e3_events::{Get, Insert, InsertSync, Remove}; +use e3_events::{Flush, Get, Insert, InsertSync, Remove}; use serde::{Deserialize, Serialize}; use tracing::error; @@ -41,6 +41,7 @@ pub struct DataStore { insert: Recipient, insert_sync: Recipient, remove: Recipient, + flush: Recipient, } impl DataStore { @@ -57,7 +58,6 @@ impl DataStore { if bytes == [0] { return Ok(None); } - Ok(Some(bincode::deserialize(&bytes)?)) } @@ -82,6 +82,7 @@ impl DataStore { let msg = InsertSync::new(&self.scope, serialized); self.insert_sync.send(msg).await??; + self.flush.send(Flush).await?; // Write sync will flush all pending writes Ok(()) } @@ -158,6 +159,7 @@ impl DataStore { insert_sync: self.insert_sync.clone(), remove: self.remove.clone(), scope, + flush: self.flush.clone(), } } @@ -169,6 +171,7 @@ impl DataStore { insert_sync: self.insert_sync.clone(), remove: self.remove.clone(), scope: key.into_key(), + flush: self.flush.clone(), } } @@ -184,6 +187,7 @@ impl DataStore { insert_sync: addr.clone().recipient(), remove: addr.clone().recipient(), scope: vec![], + flush: addr.clone().recipient(), } } @@ -198,6 +202,7 @@ impl DataStore { insert_sync: addr.clone().recipient(), remove: addr.clone().recipient(), scope: vec![], + flush: addr.clone().recipient(), } } @@ -209,6 +214,7 @@ impl DataStore { insert_sync: addr.clone().recipient(), remove: addr.clone().recipient(), scope: vec![], + flush: addr.clone().recipient(), } } @@ -220,6 +226,7 @@ impl DataStore { insert_sync: addr.clone().recipient(), remove: addr.clone().recipient(), scope: vec![], + flush: addr.clone().recipient(), } } } @@ -233,6 +240,7 @@ impl From<&Addr> for DataStore { insert_sync: addr.clone().recipient(), remove: addr.clone().recipient(), scope: vec![], + flush: addr.clone().recipient(), } } } @@ -246,6 +254,7 @@ impl From<&Addr> for DataStore { insert_sync: addr.clone().recipient(), remove: addr.clone().recipient(), scope: vec![], + flush: addr.clone().recipient(), } } } diff --git a/crates/data/src/in_mem.rs b/crates/data/src/in_mem.rs index c689acd9fb..6f0f2d894c 100644 --- a/crates/data/src/in_mem.rs +++ b/crates/data/src/in_mem.rs @@ -6,10 +6,9 @@ use actix::{Actor, Handler, Message}; use anyhow::{Context, Result}; -use e3_events::{Get, Insert, InsertBatch, InsertSync, Remove}; +use e3_events::{Flush, Get, Insert, InsertBatch, InsertSync, Remove}; use e3_utils::MAILBOX_LIMIT; use std::collections::BTreeMap; -use tracing::info; #[derive(Message, Clone, Debug, PartialEq, Eq, Hash)] #[rtype(result = "Vec")] @@ -121,6 +120,13 @@ impl Handler for InMemStore { } } +impl Handler for InMemStore { + type Result = (); + fn handle(&mut self, _: Flush, _: &mut Self::Context) -> Self::Result { + // noop + } +} + impl Handler for InMemStore { type Result = Vec; fn handle(&mut self, _: GetLog, _: &mut Self::Context) -> Vec { diff --git a/crates/data/src/sled_db.rs b/crates/data/src/sled_db.rs index 72e508d425..e5bb993789 100644 --- a/crates/data/src/sled_db.rs +++ b/crates/data/src/sled_db.rs @@ -58,9 +58,13 @@ impl SledDb { .db .get(key) .context(format!("Failed to fetch {}", str_key))?; - Ok(res.map(|v| v.to_vec())) } + + pub fn flush(&self) -> Result<()> { + self.db.flush()?; + Ok(()) + } } #[cfg(test)] diff --git a/crates/data/src/sled_store.rs b/crates/data/src/sled_store.rs index bae983fdb4..5acb4566fd 100644 --- a/crates/data/src/sled_store.rs +++ b/crates/data/src/sled_store.rs @@ -9,16 +9,16 @@ use actix::{Actor, ActorContext, Addr, Handler}; use anyhow::Result; use e3_events::{ prelude::*, BusHandle, EType, EnclaveEvent, EnclaveEventData, - EnclaveUnsequencedErrorDispatcher, EventType, + EnclaveUnsequencedErrorDispatcher, EventType, Flush, }; use e3_events::{Get, Insert, InsertBatch, InsertSync, Remove}; -use e3_utils::MAILBOX_LIMIT; +use e3_utils::{NotifySync, MAILBOX_LIMIT}; use std::path::PathBuf; use tracing::{error, info}; pub struct SledStore { db: Option, - bus: Box, // Only used for Shutdown + bus: Box, // TODO: fix to work with trap } impl Actor for SledStore { @@ -119,10 +119,24 @@ impl Handler for SledStore { } } } + +impl Handler for SledStore { + type Result = (); + fn handle(&mut self, _: Flush, _: &mut Self::Context) -> Self::Result { + if let Some(ref db) = self.db { + match db.flush() { + Err(err) => self.bus.err(EType::Data, err), + _ => (), + } + } + } +} + impl Handler for SledStore { type Result = (); fn handle(&mut self, msg: EnclaveEvent, ctx: &mut Self::Context) -> Self::Result { if let EnclaveEventData::Shutdown(_) = msg.get_data() { + self.notify_sync(ctx, Flush); // Flush all pending writes let _db = self.db.take(); // db will be dropped ctx.stop() } diff --git a/crates/entrypoint/src/wallet/get.rs b/crates/entrypoint/src/wallet/get.rs new file mode 100644 index 0000000000..debeafc4fd --- /dev/null +++ b/crates/entrypoint/src/wallet/get.rs @@ -0,0 +1,30 @@ +// 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 crate::helpers::datastore::get_repositories; +use alloy::{primitives::FixedBytes, signers::local::PrivateKeySigner}; +use alloy_primitives::Address; +use anyhow::Context; +use anyhow::Result; +use e3_config::AppConfig; +use e3_crypto::Cipher; +use e3_evm::EthPrivateKeyRepositoryFactory; +use zeroize::Zeroizing; + +pub async fn execute(config: &AppConfig) -> Result
{ + let repositories = get_repositories(config)?; + let cipher = Cipher::from_file(config.key_file()).await?; + let encrypted = repositories + .eth_private_key() + .read() + .await? + .context("No wallet has been set.")?; + let private_key = Zeroizing::new(cipher.decrypt_data(&encrypted)?); + let address = + PrivateKeySigner::from_bytes(&FixedBytes::<32>::from_slice(&private_key))?.address(); + + Ok(address) +} diff --git a/crates/entrypoint/src/wallet/mod.rs b/crates/entrypoint/src/wallet/mod.rs index cf1ede937a..a4deda7b2f 100644 --- a/crates/entrypoint/src/wallet/mod.rs +++ b/crates/entrypoint/src/wallet/mod.rs @@ -4,4 +4,5 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. +pub mod get; pub mod set; diff --git a/crates/entrypoint/src/wallet/set.rs b/crates/entrypoint/src/wallet/set.rs index c19284937f..e4c02ff3d6 100644 --- a/crates/entrypoint/src/wallet/set.rs +++ b/crates/entrypoint/src/wallet/set.rs @@ -5,12 +5,13 @@ // or FITNESS FOR A PARTICULAR PURPOSE. use alloy::{hex::FromHex, primitives::FixedBytes, signers::local::PrivateKeySigner}; +use alloy_primitives::Address; use anyhow::{anyhow, Result}; use e3_config::AppConfig; use e3_crypto::Cipher; use e3_evm::EthPrivateKeyRepositoryFactory; use e3_net::NetRepositoryFactory; -use libp2p::identity::Keypair; +use libp2p::{identity::Keypair, PeerId}; use zeroize::{Zeroize, Zeroizing}; use crate::helpers::{datastore::get_repositories, rand::generate_random_bytes}; @@ -23,17 +24,10 @@ pub fn validate_private_key(input: &String) -> Result<()> { Ok(()) } -pub async fn execute(config: &AppConfig, input: Zeroizing) -> Result<()> { +pub async fn execute(config: &AppConfig, input: Zeroizing) -> Result<(Address, PeerId)> { let cipher = Cipher::from_file(config.key_file()).await?; - let mut bytes = input.as_bytes().to_vec(); - let mut keypair = Keypair::ed25519_from_bytes(&mut bytes.clone())? // this zeroizes bytes so cloning ok - .try_into_ed25519()? - .to_bytes() - .to_vec(); - - let encrypted_private_key = cipher.encrypt_data(&mut bytes)?; // This zeroizes input - let encrypted_keypair = cipher.encrypt_data(&mut keypair)?; // This zeroizes input + let (encrypted_private_key, encrypted_keypair, address, peer_id) = process_key(&cipher, input)?; // Save the encrypted keys let repositories = get_repositories(config)?; @@ -46,8 +40,21 @@ pub async fn execute(config: &AppConfig, input: Zeroizing) -> Result<()> .libp2p_keypair() .write_sync(&encrypted_keypair) .await?; + Ok((address, peer_id)) +} - Ok(()) +fn process_key( + cipher: &Cipher, + private_key: Zeroizing, +) -> Result<(Vec, Vec, Address, PeerId)> { + let private_key_bytes = FixedBytes::<32>::from_hex(private_key)?; + let keypair = Keypair::ed25519_from_bytes(&mut private_key_bytes.clone())?; + let peer_id = PeerId::from(&keypair.public()); + let mut keypair = keypair.try_into_ed25519()?.to_bytes().to_vec(); + let address = PrivateKeySigner::from_bytes(&private_key_bytes)?.address(); + let encrypted_private_key = cipher.encrypt_data(&mut private_key_bytes.to_vec())?; + let encrypted_keypair = cipher.encrypt_data(&mut keypair)?; + Ok((encrypted_private_key, encrypted_keypair, address, peer_id)) } pub async fn autowallet(config: &AppConfig) -> Result<()> { @@ -57,3 +64,33 @@ pub async fn autowallet(config: &AppConfig) -> Result<()> { execute(config, input).await?; Ok(()) } + +#[cfg(test)] +mod tests { + use super::*; + + #[tokio::test] + async fn test_process_key() -> Result<()> { + let cipher = Cipher::from_password("test_password").await?; + // Hardhat default private key + let input = Zeroizing::new( + "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80".to_string(), + ); + + let (encrypted_private_key, encrypted_keypair, address, peer_id) = + process_key(&cipher, input)?; + + assert!(!encrypted_private_key.is_empty()); + assert!(!encrypted_keypair.is_empty()); + assert_eq!( + address, + "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266".parse::
()? + ); + assert_eq!( + &peer_id.to_string(), + "12D3KooWEZiPVmEZkwCFEWYxPL6xts6LnPHRFqsSEDGmt1vQ17By" + ); + + Ok(()) + } +} diff --git a/crates/events/src/bus_handle.rs b/crates/events/src/bus_handle.rs index 75d142e2c2..c5970d8f47 100644 --- a/crates/events/src/bus_handle.rs +++ b/crates/events/src/bus_handle.rs @@ -24,6 +24,7 @@ use crate::{ EventType, HistoryCollector, Sequenced, Subscribe, Unsequenced, Unsubscribe, }; +// TODO: this wont work with trap need to fix pub trait EnclaveUnsequencedErrorDispatcher { fn err(&self, err_type: EType, error: anyhow::Error); } diff --git a/crates/events/src/data_events.rs b/crates/events/src/data_events.rs index 5964049eaa..96020db3ed 100644 --- a/crates/events/src/data_events.rs +++ b/crates/events/src/data_events.rs @@ -111,3 +111,7 @@ impl Remove { &self.0 } } + +#[derive(Message)] +#[rtype(result = "()")] +pub struct Flush; From f595da98408b82fa8c181e14c10f9d76bd1e3e86 Mon Sep 17 00:00:00 2001 From: ryardley Date: Thu, 19 Feb 2026 12:11:27 +0000 Subject: [PATCH 25/45] trigger --- crates/entrypoint/src/wallet/set.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/entrypoint/src/wallet/set.rs b/crates/entrypoint/src/wallet/set.rs index e4c02ff3d6..07715d6a9c 100644 --- a/crates/entrypoint/src/wallet/set.rs +++ b/crates/entrypoint/src/wallet/set.rs @@ -54,6 +54,7 @@ fn process_key( let address = PrivateKeySigner::from_bytes(&private_key_bytes)?.address(); let encrypted_private_key = cipher.encrypt_data(&mut private_key_bytes.to_vec())?; let encrypted_keypair = cipher.encrypt_data(&mut keypair)?; + Ok((encrypted_private_key, encrypted_keypair, address, peer_id)) } From 47832fcf50cd5f1ea633e05412b923b72c8eb3ff Mon Sep 17 00:00:00 2001 From: ryardley Date: Thu, 19 Feb 2026 12:39:02 +0000 Subject: [PATCH 26/45] add enclave net get-peer-id --- crates/cli/src/cli.rs | 8 +++++++ crates/cli/src/main.rs | 1 + crates/cli/src/net.rs | 23 ++++++++++++++++++ crates/cli/src/wallet_set.rs | 2 +- crates/entrypoint/src/lib.rs | 1 + crates/entrypoint/src/net/get_peer_id.rs | 30 ++++++++++++++++++++++++ crates/entrypoint/src/net/mod.rs | 1 + 7 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 crates/cli/src/net.rs create mode 100644 crates/entrypoint/src/net/get_peer_id.rs create mode 100644 crates/entrypoint/src/net/mod.rs diff --git a/crates/cli/src/cli.rs b/crates/cli/src/cli.rs index c7d5f3cb58..a54c71e544 100644 --- a/crates/cli/src/cli.rs +++ b/crates/cli/src/cli.rs @@ -8,6 +8,7 @@ use std::path::PathBuf; use crate::ciphernode::{self, CiphernodeCommands}; use crate::helpers::telemetry::{setup_simple_tracing, setup_tracing}; +use crate::net::{self, NetCommands}; use crate::nodes::{self, NodeCommands}; use crate::noir::NoirCommands; use crate::password::PasswordCommands; @@ -174,6 +175,7 @@ impl Cli { 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?, } @@ -284,4 +286,10 @@ pub enum Commands { #[command(subcommand)] command: NodeCommands, }, + + /// Manage net configuration + Net { + #[command(subcommand)] + command: NetCommands, + }, } diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index fcd372cc71..8a761cfa4c 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -12,6 +12,7 @@ mod ciphernode; mod cli; pub mod helpers; mod init; +mod net; mod nodes; mod nodes_daemon; mod nodes_down; diff --git a/crates/cli/src/net.rs b/crates/cli/src/net.rs new file mode 100644 index 0000000000..a76733f2eb --- /dev/null +++ b/crates/cli/src/net.rs @@ -0,0 +1,23 @@ +// 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 anyhow::*; +use clap::Subcommand; +use e3_config::AppConfig; + +#[derive(Subcommand, Debug)] +pub enum NetCommands { + /// Get the ciphernode's libp2p PeerId + GetPeerId, +} + +pub async fn execute(command: NetCommands, config: &AppConfig) -> Result<()> { + match command { + NetCommands::GetPeerId => e3_entrypoint::net::get_peer_id::execute(config).await?, + }; + + Ok(()) +} diff --git a/crates/cli/src/wallet_set.rs b/crates/cli/src/wallet_set.rs index 79ac898ad9..27b38d2964 100644 --- a/crates/cli/src/wallet_set.rs +++ b/crates/cli/src/wallet_set.rs @@ -17,7 +17,7 @@ pub fn ask_for_private_key(given_key: Option>) -> Result Result { + let repositories = get_repositories(config)?; + let cipher = Cipher::from_file(config.key_file()).await?; + let encrypted = repositories + .libp2p_keypair() + .read() + .await? + .context("No wallet has been set.")?; + let mut bytes = Zeroizing::new(cipher.decrypt_data(&encrypted)?); + let keypair: libp2p::identity::Keypair = + ed25519::Keypair::try_from_bytes(&mut bytes)?.try_into()?; + let peer_id = PeerId::from(keypair.public()); + Ok(peer_id) +} diff --git a/crates/entrypoint/src/net/mod.rs b/crates/entrypoint/src/net/mod.rs new file mode 100644 index 0000000000..0591b6daa1 --- /dev/null +++ b/crates/entrypoint/src/net/mod.rs @@ -0,0 +1 @@ +pub mod get_peer_id; From 958f637c2073ac157fba083d63b045d82931e6e3 Mon Sep 17 00:00:00 2001 From: ryardley Date: Thu, 19 Feb 2026 13:27:38 +0000 Subject: [PATCH 27/45] tidy up output --- crates/cli/src/ciphernode/setup.rs | 38 ++++++++++++++++++++------- crates/cli/src/cli.rs | 1 - crates/config/src/app_config.rs | 12 +++++++-- crates/entrypoint/src/config/setup.rs | 2 +- 4 files changed, 40 insertions(+), 13 deletions(-) diff --git a/crates/cli/src/ciphernode/setup.rs b/crates/cli/src/ciphernode/setup.rs index d5c5ced2e6..4624e8ed9b 100644 --- a/crates/cli/src/ciphernode/setup.rs +++ b/crates/cli/src/ciphernode/setup.rs @@ -4,8 +4,10 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. +use alloy::primitives::Address; use anyhow::Result; use dialoguer::{theme::ColorfulTheme, Input}; +use e3_config::AppConfig; use e3_entrypoint::config::setup; use e3_utils::{colorize, eth_address_from_private_key, Color}; use std::path::PathBuf; @@ -58,23 +60,41 @@ pub async fn execute( .into(); // Execute - let config = setup::execute(rpc_url, &config_dir, &address)?; + let config = setup::execute(&rpc_url, &config_dir, &address)?; password_set::execute(&config, Some(pw)).await?; let (address, peer_id) = e3_entrypoint::wallet::set::execute(&config, private_key).await?; - let abs_dir = config_dir.canonicalize()?; + print_info(&config, address, &peer_id.to_string(), &rpc_url)?; + Ok(()) +} + +fn print_info(config: &AppConfig, address: Address, peer_id: &str, rpc_url: &str) -> Result<()> { + let abs_config = config.config_file().canonicalize()?; - println!("Enclave configuration successfully created!"); + println!("\nEnclave configuration successfully created!"); + println!( + "Editable configuration has been written to:\n\n {}", + colorize(abs_config.to_string_lossy(), Color::Yellow) + ); println!(""); - println!(" Your address:\t{}", colorize(address, Color::Cyan)); - println!(" Your peer id:\t{}", colorize(peer_id, Color::Cyan)); + 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!(""); - println!("Configuration has been written to {:?}", abs_dir); + if config.using_custom_config() { + println!( + "Run future commands from within this directory tree, or pass\n {}\n", + colorize( + format!("--config {}", abs_config.to_string_lossy()), + Color::Yellow + ) + ); + } println!( - "Run future commands from within this directory tree, or pass --config {:?}", - abs_dir + "You can start your node using:\n `{}`\n", + colorize("enclave start", Color::Yellow) ); - Ok(()) } diff --git a/crates/cli/src/cli.rs b/crates/cli/src/cli.rs index a54c71e544..1a43d45e79 100644 --- a/crates/cli/src/cli.rs +++ b/crates/cli/src/cli.rs @@ -112,7 +112,6 @@ impl Cli { private_key, ) .await?; - println!("You can start your node using `enclave start`"); } Commands::Start { .. } => { println!("No configuration found. Setting up enclave configuration..."); diff --git a/crates/config/src/app_config.rs b/crates/config/src/app_config.rs index 89cc8a49a3..e620e2675a 100644 --- a/crates/config/src/app_config.rs +++ b/crates/config/src/app_config.rs @@ -180,6 +180,8 @@ pub struct AppConfig { program: ProgramConfig, /// A custom bb implementation has been provided do not download and checksum a binary using_custom_bb: bool, + /// Whether the config has been overridden + using_custom_config: bool, } #[derive(Debug, Clone)] @@ -245,20 +247,21 @@ impl AppConfig { Some(&node.log_file), config.custom_bb.as_ref(), ); - + let found_config_file = config.found_config_file.clone(); Ok(AppConfig { name: name.to_owned(), nodes: config.nodes, chains: config.chains, peers: vec![], paths, - config_yaml: config.found_config_file.unwrap_or_default(), + config_yaml: found_config_file.clone().unwrap_or_default(), otel: config.otel, autopassword: node.autopassword, autowallet: node.autowallet, autonetkey: node.autonetkey, program: config.program.unwrap_or_default(), using_custom_bb: config.custom_bb.is_some(), + using_custom_config: found_config_file.is_some(), }) } @@ -292,6 +295,11 @@ impl AppConfig { } } + /// Whether the config is changed from the default + pub fn using_custom_config(&self) -> bool { + self.using_custom_config + } + /// Get the circuits directory pub fn circuits_dir(&self) -> PathBuf { self.paths.circuits_dir() diff --git a/crates/entrypoint/src/config/setup.rs b/crates/entrypoint/src/config/setup.rs index 4f8908fb2a..215310d6ae 100644 --- a/crates/entrypoint/src/config/setup.rs +++ b/crates/entrypoint/src/config/setup.rs @@ -38,7 +38,7 @@ pub fn validate_eth_address(address: &String) -> Result<()> { } #[instrument(name = "app", skip_all)] -pub fn execute(rpc_url: String, config_dir: &PathBuf, address: &Address) -> Result { +pub fn execute(rpc_url: &str, config_dir: &PathBuf, address: &Address) -> Result { fs::create_dir_all(&config_dir)?; let config_path = config_dir.join("enclave.config.yaml"); From 7514f55dbdcba31d2687027070d0e1b80e65ff6f Mon Sep 17 00:00:00 2001 From: ryardley Date: Thu, 19 Feb 2026 13:29:23 +0000 Subject: [PATCH 28/45] headers --- crates/entrypoint/src/net/mod.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/crates/entrypoint/src/net/mod.rs b/crates/entrypoint/src/net/mod.rs index 0591b6daa1..f07ae66a42 100644 --- a/crates/entrypoint/src/net/mod.rs +++ b/crates/entrypoint/src/net/mod.rs @@ -1 +1,7 @@ +// 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. + pub mod get_peer_id; From 16dace3e01b279ca60ac941306c5ee70e66f385c Mon Sep 17 00:00:00 2001 From: ryardley Date: Thu, 19 Feb 2026 13:32:59 +0000 Subject: [PATCH 29/45] remove diagram --- .../src/ciphernode_builder.rs | 11 ++--------- .../ciphernode-builder/src/event_system.mmd | 19 ------------------- 2 files changed, 2 insertions(+), 28 deletions(-) delete mode 100644 crates/ciphernode-builder/src/event_system.mmd diff --git a/crates/ciphernode-builder/src/ciphernode_builder.rs b/crates/ciphernode-builder/src/ciphernode_builder.rs index afc5878c5a..b44edddbbf 100644 --- a/crates/ciphernode-builder/src/ciphernode_builder.rs +++ b/crates/ciphernode-builder/src/ciphernode_builder.rs @@ -7,14 +7,13 @@ use crate::{CiphernodeHandle, EventSystem, EvmSystemChainBuilder, ProviderCache, WriteEnabled}; use actix::{Actor, Addr}; use alloy::signers::local::PrivateKeySigner; -use anyhow::{bail, Result}; +use anyhow::Result; use derivative::Derivative; use e3_aggregator::ext::{PublicKeyAggregatorExtension, ThresholdPlaintextAggregatorExtension}; use e3_aggregator::CommitteeFinalizer; use e3_config::chain_config::ChainConfig; use e3_crypto::Cipher; use e3_data::{InMemStore, RepositoriesFactory}; -use e3_events::hlc::Hlc; use e3_events::{ AggregateConfig, AggregateId, BusHandle, EnclaveEvent, EventBus, EventBusConfig, EvmEventConfig, }; @@ -31,7 +30,7 @@ use e3_sortition::{ NodeStateRepositoryFactory, Sortition, SortitionBackend, SortitionRepositoryFactory, }; use e3_sync::sync; -use e3_utils::{rand_eth_addr, SharedRng}; +use e3_utils::SharedRng; use e3_zk_prover::{setup_zk_actors, ZkBackend}; use std::time::Duration; use std::{collections::HashMap, path::PathBuf, sync::Arc}; @@ -200,12 +199,6 @@ impl CiphernodeBuilder { self } - // /// Use the given Address to represent the node. This should be unique. - // pub fn with_address(mut self, addr: &str) -> Self { - // self.address = Some(addr.to_owned()); - // self - // } - /// Log data actor events pub fn with_logging(mut self) -> Self { self.logging = true; diff --git a/crates/ciphernode-builder/src/event_system.mmd b/crates/ciphernode-builder/src/event_system.mmd deleted file mode 100644 index 5e4fb964d3..0000000000 --- a/crates/ciphernode-builder/src/event_system.mmd +++ /dev/null @@ -1,19 +0,0 @@ -graph TD - store --> buffer - store --> handle - - handle --> eventbus - handle --> sequencer - handle --> hlc - handle --> buffer - - sequencer --> eventbus - sequencer --> eventstore_router - - buffer --> aggregate_config - - eventstore_router["eventstore_router | eventstore_getter_seq | eventstore_getter_ts"] --> eventstore_addrs - - eventstore_addrs --> aggregate_config - - hlc --> node_id From 25d36928b26f5e8b8dbea1f3a1b08eb50491fa3d Mon Sep 17 00:00:00 2001 From: ryardley Date: Thu, 19 Feb 2026 13:36:07 +0000 Subject: [PATCH 30/45] zeroize validation --- crates/cli/src/helpers/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/cli/src/helpers/mod.rs b/crates/cli/src/helpers/mod.rs index 7d005d35b0..7b4b8bd22e 100644 --- a/crates/cli/src/helpers/mod.rs +++ b/crates/cli/src/helpers/mod.rs @@ -5,7 +5,7 @@ // or FITNESS FOR A PARTICULAR PURPOSE. use anyhow::{bail, Result}; -use zeroize::Zeroizing; +use zeroize::{Zeroize, Zeroizing}; pub mod compile_id; pub mod prompt_password; @@ -29,6 +29,6 @@ fn ensure_hex(s: &str) -> Result<&str> { if !s[2..].chars().all(|c| c.is_ascii_hexdigit()) { bail!("private key must only contain hex characters [0-9a-fA-F]"); } - hex::decode(&s[2..])?; + hex::decode(&s[2..])?.zeroize(); Ok(s) } From 7d18fa7374077a12d6e458b2eb64b311ff1e5964 Mon Sep 17 00:00:00 2001 From: ryardley Date: Thu, 19 Feb 2026 13:40:39 +0000 Subject: [PATCH 31/45] remove address --- crates/entrypoint/src/config/setup.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/crates/entrypoint/src/config/setup.rs b/crates/entrypoint/src/config/setup.rs index 215310d6ae..37ad411c11 100644 --- a/crates/entrypoint/src/config/setup.rs +++ b/crates/entrypoint/src/config/setup.rs @@ -45,9 +45,6 @@ pub fn execute(rpc_url: &str, config_dir: &PathBuf, address: &Address) -> Result let config_content = format!( r#"--- -node: - address: "{}" - # Enclave Configuration File chains: - name: "devnet" @@ -63,7 +60,6 @@ chains: address: "{}" deploy_block: {} "#, - address, rpc_url, get_contract_info("Enclave")?.address, get_contract_info("Enclave")?.deploy_block, From 6a50ae5db811e11f3c1c6e436a0a0639d7f43bcd Mon Sep 17 00:00:00 2001 From: ryardley Date: Thu, 19 Feb 2026 13:44:38 +0000 Subject: [PATCH 32/45] tidy up comments --- crates/events/src/bus_handle.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/crates/events/src/bus_handle.rs b/crates/events/src/bus_handle.rs index c5970d8f47..94ccc6ba25 100644 --- a/crates/events/src/bus_handle.rs +++ b/crates/events/src/bus_handle.rs @@ -369,8 +369,6 @@ mod tests { // 1. setup up two separate busses with out of sync clocks A and B. B should be 30 seconds // faster than A. - // - // NOTE: EventSystem::handle() must return BusHandle for this to compile. let bus_a = EventSystem::new() .with_fresh_bus() .handle()? From 2da767b4c15b7ed5887a9de4251832786e438d78 Mon Sep 17 00:00:00 2001 From: ryardley Date: Thu, 19 Feb 2026 13:59:24 +0000 Subject: [PATCH 33/45] autogenerate net keypair --- crates/cli/src/ciphernode/setup.rs | 3 +-- crates/cli/src/main.rs | 1 + crates/cli/src/net.rs | 4 +++- crates/cli/src/net_get_peer_id.rs | 14 ++++++++++++++ crates/entrypoint/src/config/setup.rs | 2 +- 5 files changed, 20 insertions(+), 4 deletions(-) create mode 100644 crates/cli/src/net_get_peer_id.rs diff --git a/crates/cli/src/ciphernode/setup.rs b/crates/cli/src/ciphernode/setup.rs index 4624e8ed9b..ccfdbf0316 100644 --- a/crates/cli/src/ciphernode/setup.rs +++ b/crates/cli/src/ciphernode/setup.rs @@ -38,7 +38,6 @@ pub async fn execute( }; let private_key = ask_for_private_key(private_key)?; - let address = eth_address_from_private_key(&private_key)?; let default_config_dir = dirs::config_dir() .ok_or_else(|| anyhow::anyhow!("Could not determine home directory"))? .join("enclave"); @@ -60,7 +59,7 @@ pub async fn execute( .into(); // Execute - let config = setup::execute(&rpc_url, &config_dir, &address)?; + let config = setup::execute(&rpc_url, &config_dir)?; password_set::execute(&config, Some(pw)).await?; diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index 8a761cfa4c..4ec39fcea5 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -13,6 +13,7 @@ mod cli; pub mod helpers; mod init; mod net; +mod net_get_peer_id; mod nodes; mod nodes_daemon; mod nodes_down; diff --git a/crates/cli/src/net.rs b/crates/cli/src/net.rs index a76733f2eb..bcb54d0e6c 100644 --- a/crates/cli/src/net.rs +++ b/crates/cli/src/net.rs @@ -8,6 +8,8 @@ use anyhow::*; use clap::Subcommand; use e3_config::AppConfig; +use crate::net_get_peer_id; + #[derive(Subcommand, Debug)] pub enum NetCommands { /// Get the ciphernode's libp2p PeerId @@ -16,7 +18,7 @@ pub enum NetCommands { pub async fn execute(command: NetCommands, config: &AppConfig) -> Result<()> { match command { - NetCommands::GetPeerId => e3_entrypoint::net::get_peer_id::execute(config).await?, + NetCommands::GetPeerId => net_get_peer_id::execute(config).await?, }; Ok(()) diff --git a/crates/cli/src/net_get_peer_id.rs b/crates/cli/src/net_get_peer_id.rs new file mode 100644 index 0000000000..be7a397208 --- /dev/null +++ b/crates/cli/src/net_get_peer_id.rs @@ -0,0 +1,14 @@ +// 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 anyhow::Result; +use e3_config::AppConfig; + +pub async fn execute(config: &AppConfig) -> Result<()> { + let peer_id = e3_entrypoint::net::get_peer_id::execute(config).await?; + println!("{}", peer_id); + Ok(()) +} diff --git a/crates/entrypoint/src/config/setup.rs b/crates/entrypoint/src/config/setup.rs index 37ad411c11..b8764ff05f 100644 --- a/crates/entrypoint/src/config/setup.rs +++ b/crates/entrypoint/src/config/setup.rs @@ -38,7 +38,7 @@ pub fn validate_eth_address(address: &String) -> Result<()> { } #[instrument(name = "app", skip_all)] -pub fn execute(rpc_url: &str, config_dir: &PathBuf, address: &Address) -> Result { +pub fn execute(rpc_url: &str, config_dir: &PathBuf) -> Result { fs::create_dir_all(&config_dir)?; let config_path = config_dir.join("enclave.config.yaml"); From 028ba5acbb60c5ea7a094f21c2b459e26404c6dc Mon Sep 17 00:00:00 2001 From: ryardley Date: Thu, 19 Feb 2026 14:02:16 +0000 Subject: [PATCH 34/45] fix doc tests --- crates/events/src/hlc.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/events/src/hlc.rs b/crates/events/src/hlc.rs index 9a96696f64..e0b201f81c 100644 --- a/crates/events/src/hlc.rs +++ b/crates/events/src/hlc.rs @@ -139,7 +139,7 @@ impl From for HlcTimestamp { /// # Example /// /// ``` -/// # use e3_events::hlc::{Hlc, HlcTimestamp, HlcError}; +/// # use e3_events::hlc::{Hlc, HlcTimestamp, HlcError, HlcMethods}; /// # fn main() -> Result<(), HlcError> { /// let hlc = Hlc::new(1); // Node id /// let ts1 = hlc.tick()?; From 4ec942f6d9cf9497fa4afa5e1c46607c9515ba19 Mon Sep 17 00:00:00 2001 From: ryardley Date: Thu, 19 Feb 2026 14:32:52 +0000 Subject: [PATCH 35/45] add color to errors --- crates/cli/src/main.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index 4ec39fcea5..ca271a26cd 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_utils::{colorize, Color}; use tracing::info; mod ciphernode; @@ -63,7 +64,7 @@ pub async fn main() { // Execute the cli if let Err(err) = Cli::parse().execute().await { - eprintln!("{}", err); + eprintln!("{}", colorize(err, Color::Red)); std::process::exit(1); } } From fdb4ccffae89fcca7f64a7d698531eb5a25c29b3 Mon Sep 17 00:00:00 2001 From: ryardley Date: Thu, 19 Feb 2026 14:42:36 +0000 Subject: [PATCH 36/45] stored data is bytes not string --- Cargo.lock | 1 + crates/evm/Cargo.toml | 9 +++++---- crates/evm/src/helpers.rs | 5 +++-- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6a9cce4ec8..dd7c9a838d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3327,6 +3327,7 @@ dependencies = [ "e3-trbfv", "e3-utils", "futures-util", + "hex", "num-bigint", "serde", "tokio", diff --git a/crates/evm/Cargo.toml b/crates/evm/Cargo.toml index c8446c4095..062431cdfb 100644 --- a/crates/evm/Cargo.toml +++ b/crates/evm/Cargo.toml @@ -15,19 +15,20 @@ anyhow = { workspace = true } async-trait = { workspace = true } base64 = { workspace = true } bloom = { workspace = true } -e3-fhe-params = { workspace = true } -e3-crypto = { workspace = true } e3-config = { workspace = true } +e3-crypto = { workspace = true } e3-data = { workspace = true } e3-events = { workspace = true } +e3-fhe-params = { workspace = true } +e3-sortition = { workspace = true } e3-trbfv = { workspace = true } e3-utils = { workspace = true } futures-util = { workspace = true } -e3-sortition = { workspace = true } +hex = { workspace = true } +num-bigint = { workspace = true } serde = { workspace = true } tokio = { workspace = true } tracing = { workspace = true } -num-bigint = { workspace = true } url = { workspace = true } zeroize = { workspace = true } diff --git a/crates/evm/src/helpers.rs b/crates/evm/src/helpers.rs index 927b92fd66..cdccbcdf35 100644 --- a/crates/evm/src/helpers.rs +++ b/crates/evm/src/helpers.rs @@ -200,8 +200,9 @@ pub async fn load_signer_from_repository( .await? .ok_or_else(|| anyhow::anyhow!("No private key found in repository"))?; - let decrypted = cipher.decrypt_data(&encrypted_key)?; - let private_key = Zeroizing::new(String::from_utf8(decrypted)?); + let mut decrypted = cipher.decrypt_data(&encrypted_key)?; + let private_key = Zeroizing::new(hex::encode(&decrypted)); + decrypted.zeroize(); private_key.parse().map_err(Into::into) } From b5200baaa67d9c3577ab61d22b5a8fc826ed869d Mon Sep 17 00:00:00 2001 From: ryardley Date: Thu, 19 Feb 2026 14:56:34 +0000 Subject: [PATCH 37/45] fix using_custom_config param --- crates/config/src/app_config.rs | 9 +++++++-- crates/evm/src/helpers.rs | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/crates/config/src/app_config.rs b/crates/config/src/app_config.rs index e620e2675a..0de64b7a80 100644 --- a/crates/config/src/app_config.rs +++ b/crates/config/src/app_config.rs @@ -261,7 +261,7 @@ impl AppConfig { autonetkey: node.autonetkey, program: config.program.unwrap_or_default(), using_custom_bb: config.custom_bb.is_some(), - using_custom_config: found_config_file.is_some(), + using_custom_config: config.using_custom_config, }) } @@ -423,7 +423,7 @@ pub struct UnscopedAppConfig { data_dir: Option, /// The config file as found before initialization this is for testing purposes and you should /// not use this in your configurations - found_config_file: Option, + found_config_file: Option, // This is set regardless as the file is resolved /// The default node that runs during commands like `enclave start` without supplying the /// `--name` argument. node: NodeDefinition, @@ -437,6 +437,8 @@ pub struct UnscopedAppConfig { /// is up to the node operator to ensure bb matches the version that exactly matches the /// application. custom_bb: Option, + /// Whether or not the config param was passed in + using_custom_config: bool, } impl Default for UnscopedAppConfig { @@ -451,6 +453,7 @@ impl Default for UnscopedAppConfig { nodes: HashMap::new(), program: None, custom_bb: None, + using_custom_config: false, } } } @@ -490,6 +493,7 @@ impl UnscopedAppConfig { struct CliOverrides { pub otel: Option, pub found_config_file: Option, + pub using_custom_config: bool, } /// Load the config at the config_file or the default location if not provided @@ -518,6 +522,7 @@ pub fn load_config( .merge(Serialized::defaults(&CliOverrides { otel, found_config_file: Some(resolved_config_path), + using_custom_config: found_config_file.is_some(), })) .extract() .context("Could not parse configuration")?; diff --git a/crates/evm/src/helpers.rs b/crates/evm/src/helpers.rs index cdccbcdf35..5233b0d4ad 100644 --- a/crates/evm/src/helpers.rs +++ b/crates/evm/src/helpers.rs @@ -198,7 +198,7 @@ pub async fn load_signer_from_repository( let encrypted_key = repository .read() .await? - .ok_or_else(|| anyhow::anyhow!("No private key found in repository"))?; + .context("No private key found in repository")?; let mut decrypted = cipher.decrypt_data(&encrypted_key)?; let private_key = Zeroizing::new(hex::encode(&decrypted)); From bfebcf8737be382e8ab03b83d01e42c1ca6d09e9 Mon Sep 17 00:00:00 2001 From: ryardley Date: Thu, 19 Feb 2026 15:40:55 +0000 Subject: [PATCH 38/45] fix custom config --- crates/cli/src/ciphernode/mod.rs | 5 +- crates/cli/src/ciphernode/setup.rs | 3 +- crates/config/src/app_config.rs | 9 +-- crates/config/src/paths_engine.rs | 81 +++++++++++++++++++++++++++ crates/entrypoint/src/password/set.rs | 1 + 5 files changed, 89 insertions(+), 10 deletions(-) diff --git a/crates/cli/src/ciphernode/mod.rs b/crates/cli/src/ciphernode/mod.rs index 6d1da0b36c..dadceae7a9 100644 --- a/crates/cli/src/ciphernode/mod.rs +++ b/crates/cli/src/ciphernode/mod.rs @@ -169,7 +169,10 @@ pub async fn execute(command: CiphernodeCommands, config: &AppConfig) -> Result< lifecycle::status(&ctx).await? } CiphernodeCommands::Setup { .. } => { - bail!("Cannot run `enclave ciphernode setup` when a configuration already exists."); + bail!( + "Cannot run `enclave ciphernode setup` when a configuration already exists: {:?}", + config.config_file() + ); } } diff --git a/crates/cli/src/ciphernode/setup.rs b/crates/cli/src/ciphernode/setup.rs index ccfdbf0316..f351ac1cac 100644 --- a/crates/cli/src/ciphernode/setup.rs +++ b/crates/cli/src/ciphernode/setup.rs @@ -61,7 +61,8 @@ pub async fn execute( // Execute let config = setup::execute(&rpc_url, &config_dir)?; - password_set::execute(&config, Some(pw)).await?; + e3_entrypoint::password::set::preflight(&config).await?; + 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)?; diff --git a/crates/config/src/app_config.rs b/crates/config/src/app_config.rs index 0de64b7a80..a5f7f27c8f 100644 --- a/crates/config/src/app_config.rs +++ b/crates/config/src/app_config.rs @@ -180,8 +180,6 @@ pub struct AppConfig { program: ProgramConfig, /// A custom bb implementation has been provided do not download and checksum a binary using_custom_bb: bool, - /// Whether the config has been overridden - using_custom_config: bool, } #[derive(Debug, Clone)] @@ -261,7 +259,6 @@ impl AppConfig { autonetkey: node.autonetkey, program: config.program.unwrap_or_default(), using_custom_bb: config.custom_bb.is_some(), - using_custom_config: config.using_custom_config, }) } @@ -297,7 +294,7 @@ impl AppConfig { /// Whether the config is changed from the default pub fn using_custom_config(&self) -> bool { - self.using_custom_config + !self.paths.is_default_config_file() } /// Get the circuits directory @@ -437,8 +434,6 @@ pub struct UnscopedAppConfig { /// is up to the node operator to ensure bb matches the version that exactly matches the /// application. custom_bb: Option, - /// Whether or not the config param was passed in - using_custom_config: bool, } impl Default for UnscopedAppConfig { @@ -453,7 +448,6 @@ impl Default for UnscopedAppConfig { nodes: HashMap::new(), program: None, custom_bb: None, - using_custom_config: false, } } } @@ -503,7 +497,6 @@ pub fn load_config( otel: Option, ) -> Result { let found_config_file = found_config_file.map(PathBuf::from); - let resolved_config_path = resolve_config_path( find_in_parent, // finding strategy env::current_dir()?, // cwd diff --git a/crates/config/src/paths_engine.rs b/crates/config/src/paths_engine.rs index bed99573e6..93efd8bf02 100644 --- a/crates/config/src/paths_engine.rs +++ b/crates/config/src/paths_engine.rs @@ -86,6 +86,12 @@ impl PathsEngine { clean(self.default_config_dir.join(DEFAULT_CONFIG_NAME)) } + /// Returns true if the config file is the default config file (i.e., not a custom path) + pub fn is_default_config_file(&self) -> bool { + let default_path = clean(self.default_config_dir.join(DEFAULT_CONFIG_NAME)); + self.config_file() == default_path + } + /// Full path to the key file containing secret key pub fn key_file(&self) -> PathBuf { if let Some(key_file) = self.key_file_override.clone() { @@ -600,4 +606,79 @@ mod test { }, ]); } + + #[test] + fn test_is_default_config_file() { + let default_data_dir = PathBuf::from("/home/user/.local/share/enclave"); + let default_config_dir = PathBuf::from("/home/user/.config/enclave"); + let cwd = PathBuf::from("/no/matter"); + + // Test case 1: No found_config_file - should be default + let paths = PathsEngine::new( + "test", + &cwd, + &default_data_dir, + &default_config_dir, + None, + None, + None, + None, + None, + None, + None, + ); + assert!(paths.is_default_config_file()); + + // Test case 2: found_config_file set to different path - not default + let paths = PathsEngine::new( + "test", + &cwd, + &default_data_dir, + &default_config_dir, + Some(&PathBuf::from("/foo/some.config.yaml")), + None, + None, + None, + None, + None, + None, + ); + assert!(!paths.is_default_config_file()); + + // Test case 3: found_config_file set to exact default path - should be default + let paths = PathsEngine::new( + "test", + &cwd, + &default_data_dir, + &default_config_dir, + Some(&PathBuf::from( + "/home/user/.config/enclave/enclave.config.yaml", + )), + None, + None, + None, + None, + None, + None, + ); + assert!(paths.is_default_config_file()); + + // Test case 4: found_config_file set to same path with different representation - should be default + let paths = PathsEngine::new( + "test", + &cwd, + &default_data_dir, + &default_config_dir, + Some(&PathBuf::from( + "/home/user/.config/enclave/./enclave.config.yaml", + )), + None, + None, + None, + None, + None, + None, + ); + assert!(paths.is_default_config_file()); + } } diff --git a/crates/entrypoint/src/password/set.rs b/crates/entrypoint/src/password/set.rs index e40d38548b..a0946e28a3 100644 --- a/crates/entrypoint/src/password/set.rs +++ b/crates/entrypoint/src/password/set.rs @@ -11,6 +11,7 @@ use zeroize::Zeroizing; use crate::helpers::rand::generate_random_bytes; +/// Checks if the Keyfile already exists and fail with a constructive error pub async fn preflight(config: &AppConfig) -> Result<()> { let key_file = config.key_file(); let pm = FilePasswordManager::new(key_file); From 66b9e9be3642a9aa95d9adace043deb3fb6e3884 Mon Sep 17 00:00:00 2001 From: ryardley Date: Fri, 20 Feb 2026 05:16:23 +0000 Subject: [PATCH 39/45] update pnpm-lock --- pnpm-lock.yaml | 191 +++++++++++++++++++++++++++++++------------------ 1 file changed, 121 insertions(+), 70 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 842922e460..0ecb869db9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -712,7 +712,7 @@ importers: version: 5.3.0 '@risc0/ethereum': specifier: file:lib/risc0-ethereum - version: file:templates/default/lib/risc0-ethereum + version: risc0-ethereum@file:templates/default/lib/risc0-ethereum '@types/chai': specifier: ^4.2.0 version: 4.3.20 @@ -3071,9 +3071,6 @@ packages: '@reown/appkit@1.7.8': resolution: {integrity: sha512-51kTleozhA618T1UvMghkhKfaPcc9JlKwLJ5uV+riHyvSoWPKPRIa5A6M1Wano5puNyW0s3fwywhyqTHSilkaA==} - '@risc0/ethereum@file:templates/default/lib/risc0-ethereum': - resolution: {directory: templates/default/lib/risc0-ethereum, type: directory} - '@rolldown/pluginutils@1.0.0-beta.27': resolution: {integrity: sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==} @@ -8727,6 +8724,9 @@ packages: resolution: {integrity: sha512-5Di9UC0+8h1L6ZD2d7awM7E/T4uA1fJRlx6zk/NvdCCVEoAnFqvHmCuNeIKoCeIixBX/q8uM+6ycDvF8woqosA==} engines: {node: '>= 0.8'} + risc0-ethereum@file:templates/default/lib/risc0-ethereum: + resolution: {directory: templates/default/lib/risc0-ethereum, type: directory} + robust-predicates@3.0.2: resolution: {integrity: sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==} @@ -10235,11 +10235,11 @@ snapshots: '@babel/helpers': 7.28.4 '@babel/parser': 7.28.5 '@babel/template': 7.27.2 - '@babel/traverse': 7.28.5(supports-color@5.5.0) + '@babel/traverse': 7.28.5 '@babel/types': 7.28.5 '@jridgewell/remapping': 2.3.5 convert-source-map: 2.0.0 - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -10280,7 +10280,7 @@ snapshots: '@babel/helper-optimise-call-expression': 7.27.1 '@babel/helper-replace-supers': 7.27.1(@babel/core@7.28.5) '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 - '@babel/traverse': 7.28.5(supports-color@5.5.0) + '@babel/traverse': 7.28.5 semver: 6.3.1 transitivePeerDependencies: - supports-color @@ -10297,7 +10297,7 @@ snapshots: '@babel/core': 7.28.5 '@babel/helper-compilation-targets': 7.27.2 '@babel/helper-plugin-utils': 7.27.1 - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) lodash.debounce: 4.0.8 resolve: 1.22.11 transitivePeerDependencies: @@ -10320,7 +10320,14 @@ snapshots: '@babel/helper-member-expression-to-functions@7.28.5': dependencies: - '@babel/traverse': 7.28.5(supports-color@5.5.0) + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-imports@7.27.1': + dependencies: + '@babel/traverse': 7.28.5 '@babel/types': 7.28.5 transitivePeerDependencies: - supports-color @@ -10335,9 +10342,9 @@ snapshots: '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 - '@babel/helper-module-imports': 7.27.1(supports-color@5.5.0) + '@babel/helper-module-imports': 7.27.1 '@babel/helper-validator-identifier': 7.28.5 - '@babel/traverse': 7.28.5(supports-color@5.5.0) + '@babel/traverse': 7.28.5 transitivePeerDependencies: - supports-color @@ -10352,7 +10359,7 @@ snapshots: '@babel/core': 7.28.5 '@babel/helper-annotate-as-pure': 7.27.3 '@babel/helper-wrap-function': 7.28.3 - '@babel/traverse': 7.28.5(supports-color@5.5.0) + '@babel/traverse': 7.28.5 transitivePeerDependencies: - supports-color @@ -10361,13 +10368,13 @@ snapshots: '@babel/core': 7.28.5 '@babel/helper-member-expression-to-functions': 7.28.5 '@babel/helper-optimise-call-expression': 7.27.1 - '@babel/traverse': 7.28.5(supports-color@5.5.0) + '@babel/traverse': 7.28.5 transitivePeerDependencies: - supports-color '@babel/helper-skip-transparent-expression-wrappers@7.27.1': dependencies: - '@babel/traverse': 7.28.5(supports-color@5.5.0) + '@babel/traverse': 7.28.5 '@babel/types': 7.28.5 transitivePeerDependencies: - supports-color @@ -10385,7 +10392,7 @@ snapshots: '@babel/helper-wrap-function@7.28.3': dependencies: '@babel/template': 7.27.2 - '@babel/traverse': 7.28.5(supports-color@5.5.0) + '@babel/traverse': 7.28.5 '@babel/types': 7.28.5 transitivePeerDependencies: - supports-color @@ -10403,7 +10410,7 @@ snapshots: dependencies: '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.27.1 - '@babel/traverse': 7.28.5(supports-color@5.5.0) + '@babel/traverse': 7.28.5 transitivePeerDependencies: - supports-color @@ -10430,7 +10437,7 @@ snapshots: dependencies: '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.27.1 - '@babel/traverse': 7.28.5(supports-color@5.5.0) + '@babel/traverse': 7.28.5 transitivePeerDependencies: - supports-color @@ -10474,14 +10481,14 @@ snapshots: '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.27.1 '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.28.5) - '@babel/traverse': 7.28.5(supports-color@5.5.0) + '@babel/traverse': 7.28.5 transitivePeerDependencies: - supports-color '@babel/plugin-transform-async-to-generator@7.27.1(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 - '@babel/helper-module-imports': 7.27.1(supports-color@5.5.0) + '@babel/helper-module-imports': 7.27.1 '@babel/helper-plugin-utils': 7.27.1 '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.28.5) transitivePeerDependencies: @@ -10521,7 +10528,7 @@ snapshots: '@babel/helper-globals': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 '@babel/helper-replace-supers': 7.27.1(@babel/core@7.28.5) - '@babel/traverse': 7.28.5(supports-color@5.5.0) + '@babel/traverse': 7.28.5 transitivePeerDependencies: - supports-color @@ -10535,7 +10542,7 @@ snapshots: dependencies: '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.27.1 - '@babel/traverse': 7.28.5(supports-color@5.5.0) + '@babel/traverse': 7.28.5 transitivePeerDependencies: - supports-color @@ -10592,7 +10599,7 @@ snapshots: '@babel/core': 7.28.5 '@babel/helper-compilation-targets': 7.27.2 '@babel/helper-plugin-utils': 7.27.1 - '@babel/traverse': 7.28.5(supports-color@5.5.0) + '@babel/traverse': 7.28.5 transitivePeerDependencies: - supports-color @@ -10638,7 +10645,7 @@ snapshots: '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.5) '@babel/helper-plugin-utils': 7.27.1 '@babel/helper-validator-identifier': 7.28.5 - '@babel/traverse': 7.28.5(supports-color@5.5.0) + '@babel/traverse': 7.28.5 transitivePeerDependencies: - supports-color @@ -10678,7 +10685,7 @@ snapshots: '@babel/helper-plugin-utils': 7.27.1 '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.28.5) '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.28.5) - '@babel/traverse': 7.28.5(supports-color@5.5.0) + '@babel/traverse': 7.28.5 transitivePeerDependencies: - supports-color @@ -10761,7 +10768,7 @@ snapshots: dependencies: '@babel/core': 7.28.5 '@babel/helper-annotate-as-pure': 7.27.3 - '@babel/helper-module-imports': 7.27.1(supports-color@5.5.0) + '@babel/helper-module-imports': 7.27.1 '@babel/helper-plugin-utils': 7.27.1 '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.5) '@babel/types': 7.28.5 @@ -10976,11 +10983,23 @@ snapshots: '@babel/helper-split-export-declaration': 7.24.7 '@babel/parser': 7.28.5 '@babel/types': 7.28.5 - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) globals: 11.12.0 transitivePeerDependencies: - supports-color + '@babel/traverse@7.28.5': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.5 + '@babel/helper-globals': 7.28.0 + '@babel/parser': 7.28.5 + '@babel/template': 7.27.2 + '@babel/types': 7.28.5 + debug: 4.4.3(supports-color@8.1.1) + transitivePeerDependencies: + - supports-color + '@babel/traverse@7.28.5(supports-color@5.5.0)': dependencies: '@babel/code-frame': 7.27.1 @@ -11182,7 +11201,7 @@ snapshots: '@emotion/babel-plugin@11.13.5': dependencies: - '@babel/helper-module-imports': 7.27.1(supports-color@5.5.0) + '@babel/helper-module-imports': 7.27.1 '@babel/runtime': 7.28.4 '@emotion/hash': 0.9.2 '@emotion/memoize': 0.9.0 @@ -11502,7 +11521,7 @@ snapshots: '@eslint/config-array@0.21.1': dependencies: '@eslint/object-schema': 2.1.7 - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) minimatch: 3.1.2 transitivePeerDependencies: - supports-color @@ -11518,7 +11537,7 @@ snapshots: '@eslint/eslintrc@3.3.1': dependencies: ajv: 6.12.6 - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) espree: 10.4.0 globals: 14.0.0 ignore: 5.3.2 @@ -12175,7 +12194,7 @@ snapshots: '@scure/base': 1.2.6 '@types/debug': 4.1.12 '@types/lodash': 4.17.20 - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) lodash: 4.17.21 pony-cause: 2.1.11 semver: 7.7.3 @@ -12187,7 +12206,7 @@ snapshots: dependencies: '@ethereumjs/tx': 4.2.0 '@types/debug': 4.1.12 - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) semver: 7.7.3 superstruct: 1.0.4 transitivePeerDependencies: @@ -12200,7 +12219,7 @@ snapshots: '@noble/hashes': 1.8.0 '@scure/base': 1.2.6 '@types/debug': 4.1.12 - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) pony-cause: 2.1.11 semver: 7.7.3 uuid: 9.0.1 @@ -12214,7 +12233,7 @@ snapshots: '@noble/hashes': 1.8.0 '@scure/base': 1.2.6 '@types/debug': 4.1.12 - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) pony-cause: 2.1.11 semver: 7.7.3 uuid: 9.0.1 @@ -12594,7 +12613,7 @@ snapshots: dependencies: '@nomicfoundation/hardhat-errors': 3.0.3 '@nomicfoundation/hardhat-utils': 3.0.5 - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) ethereum-cryptography: 2.2.1 ethers: 6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) hardhat: 3.0.11(bufferutil@4.0.9)(utf-8-validate@5.0.10) @@ -12623,7 +12642,7 @@ snapshots: '@nomicfoundation/ignition-core': 3.0.4(bufferutil@4.0.9)(utf-8-validate@5.0.10) '@nomicfoundation/ignition-ui': 3.0.4 chalk: 5.6.2 - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) hardhat: 3.0.11(bufferutil@4.0.9)(utf-8-validate@5.0.10) json5: 2.2.3 prompts: 2.4.2 @@ -12640,7 +12659,7 @@ snapshots: '@nomicfoundation/hardhat-utils': 3.0.5 '@nomicfoundation/hardhat-zod-utils': 3.0.1(zod@3.25.76) chalk: 5.6.2 - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) hardhat: 3.0.11(bufferutil@4.0.9)(utf-8-validate@5.0.10) zod: 3.25.76 transitivePeerDependencies: @@ -12743,7 +12762,7 @@ snapshots: '@nomicfoundation/hardhat-utils': 3.0.5 '@nomicfoundation/hardhat-zod-utils': 3.0.1(zod@3.25.76) '@typechain/ethers-v6': 0.5.1(ethers@6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(typechain@8.3.2(typescript@5.8.3))(typescript@5.8.3) - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) ethers: 6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) hardhat: 3.0.11(bufferutil@4.0.9)(utf-8-validate@5.0.10) typechain: 8.3.2(typescript@5.8.3) @@ -12755,7 +12774,7 @@ snapshots: '@nomicfoundation/hardhat-utils@3.0.5': dependencies: '@streamparser/json-node': 0.0.22 - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) env-paths: 2.2.1 ethereum-cryptography: 2.2.1 fast-equals: 5.3.2 @@ -12773,7 +12792,7 @@ snapshots: '@nomicfoundation/hardhat-zod-utils': 3.0.1(zod@3.25.76) cbor2: 1.12.0 chalk: 5.6.2 - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) hardhat: 3.0.11(bufferutil@4.0.9)(utf-8-validate@5.0.10) semver: 7.7.3 zod: 3.25.76 @@ -12795,7 +12814,7 @@ snapshots: '@nomicfoundation/hardhat-utils': 3.0.5 '@nomicfoundation/solidity-analyzer': 0.1.2 cbor2: 1.12.0 - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) ethers: 6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) immer: 10.0.2 lodash-es: 4.17.21 @@ -13141,8 +13160,6 @@ snapshots: - utf-8-validate - zod - '@risc0/ethereum@file:templates/default/lib/risc0-ethereum': {} - '@rolldown/pluginutils@1.0.0-beta.27': {} '@rollup/plugin-inject@5.0.5(rollup@4.52.5)': @@ -14017,7 +14034,7 @@ snapshots: '@depay/web3-mock-evm': 14.19.1(bufferutil@4.0.9)(utf-8-validate@5.0.10) '@playwright/test': 1.52.0 '@synthetixio/synpress-core': 0.0.13(@playwright/test@1.52.0) - viem: 2.38.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4) + viem: 2.38.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) transitivePeerDependencies: - '@depay/solana-web3.js' - '@depay/web3-blockchains' @@ -14489,7 +14506,7 @@ snapshots: '@typescript-eslint/types': 7.18.0 '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.8.3) '@typescript-eslint/visitor-keys': 7.18.0 - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) eslint: 9.39.1(jiti@1.21.7) optionalDependencies: typescript: 5.8.3 @@ -14502,7 +14519,7 @@ snapshots: '@typescript-eslint/types': 8.47.0 '@typescript-eslint/typescript-estree': 8.47.0(typescript@5.8.3) '@typescript-eslint/visitor-keys': 8.47.0 - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) eslint: 9.39.1(jiti@1.21.7) typescript: 5.8.3 transitivePeerDependencies: @@ -14512,7 +14529,7 @@ snapshots: dependencies: '@typescript-eslint/tsconfig-utils': 8.47.0(typescript@5.8.3) '@typescript-eslint/types': 8.47.0 - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) typescript: 5.8.3 transitivePeerDependencies: - supports-color @@ -14535,7 +14552,7 @@ snapshots: dependencies: '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.8.3) '@typescript-eslint/utils': 7.18.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.8.3) - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) eslint: 9.39.1(jiti@1.21.7) ts-api-utils: 1.4.3(typescript@5.8.3) optionalDependencies: @@ -14548,7 +14565,7 @@ snapshots: '@typescript-eslint/types': 8.47.0 '@typescript-eslint/typescript-estree': 8.47.0(typescript@5.8.3) '@typescript-eslint/utils': 8.47.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.8.3) - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) eslint: 9.39.1(jiti@1.21.7) ts-api-utils: 2.1.0(typescript@5.8.3) typescript: 5.8.3 @@ -14563,7 +14580,7 @@ snapshots: dependencies: '@typescript-eslint/types': 7.18.0 '@typescript-eslint/visitor-keys': 7.18.0 - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) globby: 11.1.0 is-glob: 4.0.3 minimatch: 9.0.5 @@ -14580,7 +14597,7 @@ snapshots: '@typescript-eslint/tsconfig-utils': 8.47.0(typescript@5.8.3) '@typescript-eslint/types': 8.47.0 '@typescript-eslint/visitor-keys': 8.47.0 - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) fast-glob: 3.3.3 is-glob: 4.0.3 minimatch: 9.0.5 @@ -15774,7 +15791,7 @@ snapshots: dependencies: bytes: 3.1.2 content-type: 1.0.5 - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) http-errors: 2.0.0 iconv-lite: 0.6.3 on-finished: 2.4.1 @@ -17024,7 +17041,7 @@ snapshots: ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.6 - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) escape-string-regexp: 4.0.0 eslint-scope: 8.4.0 eslint-visitor-keys: 4.2.1 @@ -17296,7 +17313,7 @@ snapshots: content-type: 1.0.5 cookie: 0.7.2 cookie-signature: 1.2.2 - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) encodeurl: 2.0.0 escape-html: 1.0.3 etag: 1.8.1 @@ -17409,7 +17426,7 @@ snapshots: finalhandler@2.1.0: dependencies: - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) encodeurl: 2.0.0 escape-html: 1.0.3 on-finished: 2.4.1 @@ -17465,7 +17482,7 @@ snapshots: follow-redirects@1.15.11(debug@4.4.3): optionalDependencies: - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) for-each@0.3.5: dependencies: @@ -17828,7 +17845,7 @@ snapshots: axios: 0.21.4(debug@4.4.3) chalk: 4.1.2 chokidar: 3.6.0 - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) enquirer: 2.4.1 ethers: 5.8.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) form-data: 4.0.4 @@ -17871,7 +17888,7 @@ snapshots: lodash: 4.17.21 markdown-table: 2.0.0 sha1: 1.1.1 - viem: 2.38.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4) + viem: 2.38.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) transitivePeerDependencies: - bufferutil - debug @@ -17890,7 +17907,7 @@ snapshots: adm-zip: 0.4.16 chalk: 5.6.2 chokidar: 4.0.3 - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) enquirer: 2.4.1 ethereum-cryptography: 2.2.1 micro-eth-signer: 0.14.0 @@ -17916,7 +17933,7 @@ snapshots: adm-zip: 0.4.16 chalk: 5.6.2 chokidar: 4.0.3 - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) enquirer: 2.4.1 ethereum-cryptography: 2.2.1 micro-eth-signer: 0.14.0 @@ -18566,7 +18583,7 @@ snapshots: dependencies: chalk: 5.6.2 commander: 13.1.0 - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) execa: 8.0.1 lilconfig: 3.1.3 listr2: 8.3.3 @@ -19397,7 +19414,7 @@ snapshots: micromark@3.2.0: dependencies: '@types/debug': 4.1.12 - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) decode-named-character-reference: 1.2.0 micromark-core-commonmark: 1.1.0 micromark-factory-space: 1.1.0 @@ -19419,7 +19436,7 @@ snapshots: micromark@4.0.2: dependencies: '@types/debug': 4.1.12 - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) decode-named-character-reference: 1.2.0 devlop: 1.1.0 micromark-core-commonmark: 2.0.3 @@ -19928,6 +19945,21 @@ snapshots: transitivePeerDependencies: - zod + ox@0.9.6(typescript@5.8.3): + dependencies: + '@adraffy/ens-normalize': 1.11.1 + '@noble/ciphers': 1.3.0 + '@noble/curves': 1.9.1 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 + abitype: 1.1.1(typescript@5.8.3)(zod@3.22.4) + eventemitter3: 5.0.1 + optionalDependencies: + typescript: 5.8.3 + transitivePeerDependencies: + - zod + ox@0.9.6(typescript@5.8.3)(zod@3.22.4): dependencies: '@adraffy/ens-normalize': 1.11.1 @@ -20793,6 +20825,8 @@ snapshots: hash-base: 3.1.2 inherits: 2.0.4 + risc0-ethereum@file:templates/default/lib/risc0-ethereum: {} + robust-predicates@3.0.2: {} rollup@4.52.5: @@ -20825,7 +20859,7 @@ snapshots: router@2.2.0: dependencies: - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) depd: 2.0.0 is-promise: 4.0.0 parseurl: 1.3.3 @@ -20916,7 +20950,7 @@ snapshots: send@1.2.0: dependencies: - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) encodeurl: 2.0.0 escape-html: 1.0.3 etag: 1.8.1 @@ -21646,7 +21680,7 @@ snapshots: cac: 6.7.14 chokidar: 4.0.3 consola: 3.4.2 - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) esbuild: 0.25.12 fix-dts-default-cjs-exports: 1.0.1 joycon: 3.1.1 @@ -21700,7 +21734,7 @@ snapshots: typechain@8.3.2(typescript@5.8.3): dependencies: '@types/prettier': 2.7.3 - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) fs-extra: 7.0.1 glob: 7.1.7 js-sha3: 0.8.0 @@ -21995,6 +22029,23 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.3 + viem@2.38.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10): + dependencies: + '@noble/curves': 1.9.1 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 + abitype: 1.1.0(typescript@5.8.3)(zod@3.22.4) + isows: 1.0.7(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + ox: 0.9.6(typescript@5.8.3) + ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) + optionalDependencies: + typescript: 5.8.3 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + - zod + viem@2.38.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4): dependencies: '@noble/curves': 1.9.1 @@ -22049,7 +22100,7 @@ snapshots: vite-node@1.6.1(@types/node@22.7.5): dependencies: cac: 6.7.14 - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) pathe: 1.1.2 picocolors: 1.1.1 vite: 5.4.21(@types/node@22.7.5) @@ -22071,7 +22122,7 @@ snapshots: '@volar/typescript': 2.4.23 '@vue/language-core': 2.2.0(typescript@5.8.3) compare-versions: 6.1.1 - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) kolorist: 1.8.0 local-pkg: 1.1.2 magic-string: 0.30.21 @@ -22123,7 +22174,7 @@ snapshots: vite-tsconfig-paths@4.3.2(typescript@5.8.3)(vite@5.4.21(@types/node@22.7.5)): dependencies: - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) globrex: 0.1.2 tsconfck: 3.1.6(typescript@5.8.3) optionalDependencies: @@ -22165,7 +22216,7 @@ snapshots: '@vitest/utils': 1.6.1 acorn-walk: 8.3.4 chai: 4.5.0 - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) execa: 8.0.1 local-pkg: 0.5.1 magic-string: 0.30.21 From e5bb527d40c59cabf94bc1895b2fcab5ae022efd Mon Sep 17 00:00:00 2001 From: ryardley Date: Fri, 20 Feb 2026 05:58:28 +0000 Subject: [PATCH 40/45] add build script to zk-prover --- crates/zk-prover/build.rs | 18 ++++++++++++++++++ crates/zk-prover/scripts/build_fixtures.sh | 7 +++++++ 2 files changed, 25 insertions(+) create mode 100644 crates/zk-prover/build.rs create mode 100644 crates/zk-prover/scripts/build_fixtures.sh diff --git a/crates/zk-prover/build.rs b/crates/zk-prover/build.rs new file mode 100644 index 0000000000..22d5a15c5f --- /dev/null +++ b/crates/zk-prover/build.rs @@ -0,0 +1,18 @@ +// 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::process::Command; + +fn main() { + println!("cargo:rerun-if-env-changed=FORCE_BUILD"); + + assert!(Command::new("bash") + .arg("./scripts/build_fixtures.sh") + .status() + .unwrap() + .success()); + + println!("cargo:rerun-if-changed=./scripts/build_fixtures.sh"); +} diff --git a/crates/zk-prover/scripts/build_fixtures.sh b/crates/zk-prover/scripts/build_fixtures.sh new file mode 100644 index 0000000000..4250249687 --- /dev/null +++ b/crates/zk-prover/scripts/build_fixtures.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +set -e + +echo "Building nargo dependencies..." + +cd ../../circuits/bin/dkg && nargo build From e697c9b1f5832447be5df8bf6f87b4e00ae64942 Mon Sep 17 00:00:00 2001 From: ryardley Date: Fri, 20 Feb 2026 06:31:49 +0000 Subject: [PATCH 41/45] ensure circuits will exist if there are no artifacts --- crates/zk-prover/scripts/build_fixtures.sh | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/crates/zk-prover/scripts/build_fixtures.sh b/crates/zk-prover/scripts/build_fixtures.sh index 4250249687..e1174411e8 100644 --- a/crates/zk-prover/scripts/build_fixtures.sh +++ b/crates/zk-prover/scripts/build_fixtures.sh @@ -2,6 +2,14 @@ set -e -echo "Building nargo dependencies..." -cd ../../circuits/bin/dkg && nargo build +cd "$(git rev-parse --show-toplevel)" + +# if this is a clean checkout we need to have some artifacts to test against +if find ./circuits/bin -name '*.json' -print -quit | grep -q .; then + exit 0 +fi + +echo "Building circuits..." + +pnpm install && pnpm build:circuits From eedb65757ff39d28ad8f4589dd7314bd41232d59 Mon Sep 17 00:00:00 2001 From: ryardley Date: Fri, 20 Feb 2026 06:53:37 +0000 Subject: [PATCH 42/45] if no nargo dont try to build circuits --- crates/zk-prover/scripts/build_fixtures.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/zk-prover/scripts/build_fixtures.sh b/crates/zk-prover/scripts/build_fixtures.sh index e1174411e8..43a00ab88d 100644 --- a/crates/zk-prover/scripts/build_fixtures.sh +++ b/crates/zk-prover/scripts/build_fixtures.sh @@ -10,6 +10,10 @@ if find ./circuits/bin -name '*.json' -print -quit | grep -q .; then exit 0 fi +if ! command -v nargo &> /dev/null; then + exit 0 +fi + echo "Building circuits..." pnpm install && pnpm build:circuits From 8291ace9289d37bbba2254e5c6726da3073a3246 Mon Sep 17 00:00:00 2001 From: ryardley Date: Fri, 20 Feb 2026 06:56:15 +0000 Subject: [PATCH 43/45] add comment --- crates/zk-prover/scripts/build_fixtures.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/zk-prover/scripts/build_fixtures.sh b/crates/zk-prover/scripts/build_fixtures.sh index 43a00ab88d..71a1cbfca4 100644 --- a/crates/zk-prover/scripts/build_fixtures.sh +++ b/crates/zk-prover/scripts/build_fixtures.sh @@ -10,6 +10,7 @@ if find ./circuits/bin -name '*.json' -print -quit | grep -q .; then exit 0 fi +# if we are in CI where circuits have been built ignore if ! command -v nargo &> /dev/null; then exit 0 fi From e7ca739c202a0fb8bb71a7f48d51b0da50367e54 Mon Sep 17 00:00:00 2001 From: ryardley Date: Fri, 20 Feb 2026 07:25:23 +0000 Subject: [PATCH 44/45] update tests to support custom_bb --- crates/zk-prover/src/backend/download.rs | 1 + crates/zk-prover/tests/common/helpers.rs | 7 +- crates/zk-prover/tests/integration_tests.rs | 101 ++++++++++---------- 3 files changed, 56 insertions(+), 53 deletions(-) diff --git a/crates/zk-prover/src/backend/download.rs b/crates/zk-prover/src/backend/download.rs index 3a220839c5..5b27ec1e9f 100644 --- a/crates/zk-prover/src/backend/download.rs +++ b/crates/zk-prover/src/backend/download.rs @@ -21,6 +21,7 @@ use super::ZkBackend; impl ZkBackend { pub async fn download_bb(&self) -> Result<(), ZkError> { if self.using_custom_bb { + println!("IGNORING DOWNLOAD BECAUSE WE ARE USING A CUSTOM BB"); return Ok(()); } diff --git a/crates/zk-prover/tests/common/helpers.rs b/crates/zk-prover/tests/common/helpers.rs index d16606195f..a157f88d08 100644 --- a/crates/zk-prover/tests/common/helpers.rs +++ b/crates/zk-prover/tests/common/helpers.rs @@ -6,7 +6,7 @@ use e3_config::BBPath; use e3_zk_prover::{ZkBackend, ZkConfig}; -use std::path::PathBuf; +use std::{env, path::PathBuf}; use tempfile::TempDir; use tokio::{fs, process::Command}; @@ -120,7 +120,10 @@ pub async fn setup_test_prover(bb: &PathBuf) -> (ZkBackend, TempDir) { /// Lightweight backend for tests that don't need a real bb binary. pub fn test_backend(temp_path: &std::path::Path, config: ZkConfig) -> ZkBackend { let noir_dir = temp_path.join("noir"); - let bb_binary = BBPath::Default(noir_dir.join("bin").join("bb")); + let bb_binary = match env::var("E3_CUSTOM_BB") { + Ok(path) => BBPath::Custom(PathBuf::from(path)), + Err(_) => BBPath::Default(noir_dir.join("bin").join("bb")), + }; let circuits_dir = noir_dir.join("circuits"); let work_dir = noir_dir.join("work").join("test_node"); ZkBackend::new(bb_binary, circuits_dir, work_dir, config) diff --git a/crates/zk-prover/tests/integration_tests.rs b/crates/zk-prover/tests/integration_tests.rs index 95f2f91596..46a4740a17 100644 --- a/crates/zk-prover/tests/integration_tests.rs +++ b/crates/zk-prover/tests/integration_tests.rs @@ -4,13 +4,6 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -// TODO: Remove feature flag & network access requirement for this test. -// a) There are very few situations where we should be reliant on external -// network access for tests to pass - if we can avoid it by using virtualization -// or proxies we should. -// b) This feature flag makes it so that rust_analyzer will not work by default -// without adjusting global editor configuration which makes this code quite hard to work on. - //! Integration tests that require network access to download binaries. //! Run with: cargo test --features integration-tests @@ -22,7 +15,7 @@ use common::test_backend; use e3_fhe_params::BfvPreset; use e3_zk_helpers::circuits::dkg::pk::circuit::{PkCircuit, PkCircuitData}; use e3_zk_prover::{test_utils::get_tempdir, BbTarget, Provable, SetupStatus, ZkConfig, ZkProver}; -use std::path::PathBuf; +use std::{env, path::PathBuf}; fn versions_json_path() -> PathBuf { PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("versions.json") @@ -39,52 +32,54 @@ async fn test_full_flow_download_circuits_prove_and_verify() { let temp = get_tempdir().unwrap(); let backend = test_backend(temp.path(), config); - // --- Step 1: Fresh state should need full setup --- - eprintln!(">>> Step 1: Checking fresh state..."); - assert!(matches!( - backend.check_status().await, - SetupStatus::FullSetupNeeded - )); - eprintln!(">>> Step 1: Done"); - - // --- Step 2: Download bb and verify structure --- - eprintln!(">>> Step 2: Downloading bb..."); - let result = backend.download_bb().await; - eprintln!(">>> Step 2: bb downloaded"); - assert!(result.is_ok(), "download failed: {:?}", result); - assert!(backend.bb_binary.exists(), "bb binary not found"); - - #[cfg(unix)] - { - use std::os::unix::fs::PermissionsExt; - let perms = std::fs::metadata(&backend.bb_binary).unwrap().permissions(); - assert_eq!(perms.mode() & 0o111, 0o111, "bb should be executable"); - } - - let metadata = std::fs::metadata(&backend.bb_binary).unwrap(); - assert!(metadata.len() > 0, "bb binary should not be empty"); - - let version_info = backend.load_version_info().await; - assert_eq!( - version_info.bb_version.as_deref(), - Some(backend.config.required_bb_version.as_str()) - ); - assert!(version_info.last_updated.is_some()); - - if backend - .config - .bb_checksum_for(BbTarget::current().unwrap()) - .is_some() - { - assert!( - version_info.bb_checksum.is_some(), - "checksum should be saved in version.json" + if !backend.using_custom_bb { + // --- Step 1: Fresh state should need full setup --- + eprintln!(">>> Step 1: Checking fresh state..."); + assert!(matches!( + backend.check_status().await, + SetupStatus::FullSetupNeeded + )); + eprintln!(">>> Step 1: Done"); + + // --- Step 2: Download bb and verify structure --- + eprintln!(">>> Step 2: Downloading bb..."); + let result = backend.download_bb().await; + eprintln!(">>> Step 2: bb downloaded"); + assert!(result.is_ok(), "download failed: {:?}", result); + assert!(backend.bb_binary.exists(), "bb binary not found"); + + #[cfg(unix)] + { + use std::os::unix::fs::PermissionsExt; + let perms = std::fs::metadata(&backend.bb_binary).unwrap().permissions(); + assert_eq!(perms.mode() & 0o111, 0o111, "bb should be executable"); + } + + let metadata = std::fs::metadata(&backend.bb_binary).unwrap(); + assert!(metadata.len() > 0, "bb binary should not be empty"); + + let version_info = backend.load_version_info().await; + assert_eq!( + version_info.bb_version.as_deref(), + Some(backend.config.required_bb_version.as_str()) ); + assert!(version_info.last_updated.is_some()); + + if backend + .config + .bb_checksum_for(BbTarget::current().unwrap()) + .is_some() + { + assert!( + version_info.bb_checksum.is_some(), + "checksum should be saved in version.json" + ); + } + + assert!(backend.base_dir.exists()); + assert!(backend.base_dir.join("bin").exists()); } - assert!(backend.base_dir.exists()); - assert!(backend.base_dir.join("bin").exists()); - let version = backend.verify_bb().await; assert!(version.is_ok(), "bb --version failed: {:?}", version); println!("bb version: {}", version.unwrap()); @@ -163,6 +158,10 @@ async fn test_full_flow_download_circuits_prove_and_verify() { #[tokio::test] async fn test_download_bb_rejects_wrong_checksum() { + if env::var("E3_CUSTOM_BB").is_ok() { + return; + } + let mut config = ZkConfig::load(&versions_json_path()) .await .expect("versions.json should exist"); From e9783309bd0b67c6991ad485188174cbe1f7fa7a Mon Sep 17 00:00:00 2001 From: ryardley Date: Fri, 20 Feb 2026 07:30:46 +0000 Subject: [PATCH 45/45] add check for bb --- crates/zk-prover/scripts/build_fixtures.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/zk-prover/scripts/build_fixtures.sh b/crates/zk-prover/scripts/build_fixtures.sh index 71a1cbfca4..c7d17a3d68 100644 --- a/crates/zk-prover/scripts/build_fixtures.sh +++ b/crates/zk-prover/scripts/build_fixtures.sh @@ -15,6 +15,10 @@ if ! command -v nargo &> /dev/null; then exit 0 fi +if ! command -v bb &> /dev/null; then + exit 0 +fi + echo "Building circuits..." pnpm install && pnpm build:circuits