Skip to content

Commit

Permalink
enclave init
Browse files Browse the repository at this point in the history
  • Loading branch information
ryardley committed Dec 5, 2024
1 parent 196a426 commit a866181
Show file tree
Hide file tree
Showing 8 changed files with 215 additions and 22 deletions.
55 changes: 53 additions & 2 deletions packages/ciphernode/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/ciphernode/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ num = "0.4.3"
rand_chacha = "0.3.1"
rand = "0.8.5"
serde = { version = "1.0.208", features = ["derive"] }
serde_json = { version = "1.0.133" }
sled = "0.34.7"
sha2 = "0.10.8"
tokio = { version = "1.38", features = ["full"] }
Expand Down
14 changes: 14 additions & 0 deletions packages/ciphernode/cipher/src/password_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ pub trait PasswordManager {
async fn get_key(&self) -> Result<Zeroizing<Vec<u8>>>;
async fn delete_key(&mut self) -> Result<()>;
async fn set_key(&mut self, contents: Zeroizing<Vec<u8>>) -> Result<()>;
fn is_set(&self) -> bool;
}

pub struct InMemPasswordManager(pub Option<Zeroizing<Vec<u8>>>);
Expand Down Expand Up @@ -54,6 +55,10 @@ impl PasswordManager for EnvPasswordManager {
self.0 = None;
Ok(())
}

fn is_set(&self) -> bool {
self.0 == None
}
}

#[async_trait]
Expand All @@ -73,6 +78,10 @@ impl PasswordManager for InMemPasswordManager {
self.0 = None;
Ok(())
}

fn is_set(&self) -> bool {
self.0 == None
}
}

pub struct FilePasswordManager {
Expand Down Expand Up @@ -149,6 +158,11 @@ impl PasswordManager for FilePasswordManager {

Ok(())
}

fn is_set(&self) -> bool {
let path = &self.path;
path.exists()
}
}

fn ensure_file_permissions(path: &PathBuf, perms: u32) -> Result<()> {
Expand Down
9 changes: 7 additions & 2 deletions packages/ciphernode/enclave/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ version = "0.1.0"
edition = "2021"
description = ": coordinates the encryption and decryption of enclave computations"
repository = "https://github.com/gnosisguild/enclave/packages/ciphernode"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
build = "build.rs"

[dependencies]
actix = { workspace = true }
Expand All @@ -24,6 +23,12 @@ once_cell = "1.20.2"
router = { path = "../router" }
rpassword = "7.3.1"
tokio = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
tracing = { workspace = true }
tracing-subscriber = { workspace = true }
zeroize = { workspace = true }
phf = { version = "0.11", features = ["macros"] }

[build-dependencies]
serde_json = { workspace = true }
63 changes: 63 additions & 0 deletions packages/ciphernode/enclave/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Here we build some contract information from the EVM deployment artifacts that we can use within
// our binaries. Specifically we wbuild out a rust file that has a structure we can import and use
// within our configuration builder
use serde_json::{from_reader, Value};
use std::env;
use std::fs::{self, File};
use std::path::Path;

fn main() -> std::io::Result<()> {
// Get the manifest directory (where Cargo.toml is located)
let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap();

// Path to deployment artifacts
let deployments_path = Path::new(&manifest_dir)
.join("..") // Adjust based on your actual path structure
.join("..")
.join("evm")
.join("deployments")
.join("sepolia");

// Create output string for contract info
let mut contract_info = String::from(
"pub struct ContractInfo {\n pub address: &'static str,\n pub deploy_block: u64,\n}\n\n"
);
contract_info.push_str(
"pub static CONTRACT_DEPLOYMENTS: phf::Map<&'static str, ContractInfo> = phf::phf_map! {\n",
);

// Process each JSON file in the deployments directory
for entry in fs::read_dir(deployments_path)? {
let entry = entry?;
let path = entry.path();

if path.extension().and_then(|s| s.to_str()) == Some("json") {
let contract_name = path.file_stem().and_then(|s| s.to_str()).unwrap();

let file = File::open(&path)?;
let json: Value = from_reader(file)?;

// Extract address and block number
if let (Some(address), Some(deploy_block)) = (
json["address"].as_str(),
json["receipt"]["blockNumber"].as_u64(),
) {
contract_info.push_str(&format!(
" \"{}\" => ContractInfo {{\n address: \"{}\",\n deploy_block: {},\n }},\n",
contract_name, address, deploy_block
));
}
}
}

contract_info.push_str("};\n");

// Write the generated code to a file
let out_dir = env::var("OUT_DIR").unwrap();
let dest_path = Path::new(&out_dir).join("contract_deployments.rs");
fs::write(dest_path, contract_info)?;

println!("cargo:rerun-if-changed=../packages/evm/deployments/sepolia");

Ok(())
}
84 changes: 67 additions & 17 deletions packages/ciphernode/enclave/src/commands/init.rs
Original file line number Diff line number Diff line change
@@ -1,61 +1,111 @@
use crate::commands::password::{self, PasswordCommands};
use alloy::transports::http::reqwest::Url;
use anyhow::anyhow;
use anyhow::Result;
use config::load_config;
use dialoguer::{theme::ColorfulTheme, Input};
use enclave_core::get_tag;
use std::fs;
use tracing::instrument;

use crate::commands::password::{self, PasswordCommands};
// Import a built file:
// see /target/debug/enclave-xxxxxx/out/contract_deployments.rs
// also see build.rs
include!(concat!(env!("OUT_DIR"), "/contract_deployments.rs"));

// Get the ContractInfo object
fn get_contract_info(name: &str) -> Result<&ContractInfo> {
Ok(CONTRACT_DEPLOYMENTS
.get(name)
.ok_or(anyhow!("Could not get contract info"))?)
}

#[instrument(name = "app", skip_all, fields(id = get_tag()))]
pub async fn execute() -> Result<()> {
// Prompt for Ethereum address
let eth_address: String = Input::with_theme(&ColorfulTheme::default())
.with_prompt("Enter your Ethereum address")
let rpc_url = Input::<String>::new()
.with_prompt("Enter WebSocket devnet RPC URL")
.default("wss://ethereum-sepolia-rpc.publicnode.com".to_string())
.validate_with(|input: &String| {
if let Ok(url) = Url::parse(input) {
if url.scheme() == "wss" {
return Ok(());
}
}
Err("Please enter a valid WSS URL")
})
.interact_text()?;

let eth_address: Option<String> = Input::with_theme(&ColorfulTheme::default())
.with_prompt("Enter your Ethereum address (press Enter to skip)")
.allow_empty(true)
.validate_with(|input: &String| -> Result<(), &str> {
// Basic Ethereum address validation
if input.is_empty() {
return Ok(());
}
if !input.starts_with("0x") {
return Err("Address must start with '0x'");
}
if input.len() != 42 {
return Err("Address must be 42 characters long (including '0x')");
}
if !input[2..].chars().all(|c| c.is_ascii_hexdigit()) {
return Err("Address must contain only hexadecimal characters");
for c in input[2..].chars() {
if !c.is_ascii_hexdigit() {
return Err("Address must contain only hexadecimal characters");
}
}
Ok(())
})
.interact()?;
.interact()
.ok()
.map(|s| if s.is_empty() { None } else { Some(s) })
.flatten();

// Create config directory if it doesn't exist
let config_dir = dirs::home_dir()
.ok_or_else(|| anyhow::anyhow!("Could not determine home directory"))?
.join(".config")
.join("enclave");
fs::create_dir_all(&config_dir)?;

// Create config file path
let config_path = config_dir.join("config.yaml");

// Create YAML content using indented heredoc style
let config_content = format!(
r#"---
# Enclave Configuration File
# Ethereum Account Configuration
address: "{}"
{}
chains:
- name: "devnet"
rpc_url: "{}"
contracts:
enclave:
address: "{}"
deploy_block: {}
ciphernode_registry:
address: "{}"
deploy_block: {}
filter_registry:
address: "{}"
deploy_block: {}
"#,
eth_address
eth_address.map_or(String::new(), |addr| format!(
"# Ethereum Account Configuration\naddress: \"{}\"",
addr
)),
rpc_url,
get_contract_info("Enclave")?.address,
get_contract_info("Enclave")?.deploy_block,
get_contract_info("CiphernodeRegistryOwnable")?.address,
get_contract_info("CiphernodeRegistryOwnable")?.deploy_block,
get_contract_info("NaiveRegistryFilter")?.address,
get_contract_info("NaiveRegistryFilter")?.deploy_block,
);

// Write to file
fs::write(config_path.clone(), config_content)?;

// Load with default location
let config = load_config(Some(&config_path.display().to_string()))?;

password::execute(PasswordCommands::Create { password: None }, config).await?;

// password::execute(/* command */, config)

println!("Enclave configuration successfully created!");
println!("You can start your node using `enclave start`");

Expand Down
5 changes: 5 additions & 0 deletions packages/ciphernode/enclave/src/commands/password/create.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ fn get_zeroizing_pw_vec(input: Option<String>) -> Result<Zeroizing<Vec<u8>>> {
pub async fn execute(config: &AppConfig, input: Option<String>) -> Result<()> {
let key_file = config.key_file();
let mut pm = FilePasswordManager::new(key_file);

if pm.is_set() {
bail!("Keyfile already exists. Refusing to overwrite. Try using `enclave password overwrite` or `enclave password delete` in order to change or delete your password.")
}

let pw = get_zeroizing_pw_vec(input)?;

match pm.set_key(pw).await {
Expand Down
6 changes: 5 additions & 1 deletion packages/ciphernode/enclave/src/commands/password/delete.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,13 @@ pub async fn prompt_delete(config: &AppConfig, delete_mode: DeleteMode) -> Resul
.interact()?;

if proceed {
let mut pw_str = prompt_password("\n\nPlease enter the current password: ")?;
let key_file = config.key_file();
let mut pm = FilePasswordManager::new(key_file);
if !pm.is_set() {
println!("Password is not set. Nothing to do.");
return Ok(false);
}
let mut pw_str = prompt_password("\n\nPlease enter the current password: ")?;
let mut cur_pw = pm.get_key().await?;

if pw_str != String::from_utf8_lossy(&cur_pw) {
Expand Down

0 comments on commit a866181

Please sign in to comment.