From b422096479c4e83d60f844254a2080a9f580f7f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20I=C3=B1aki=20Bilbao?= Date: Thu, 28 Nov 2024 18:43:25 -0300 Subject: [PATCH 01/18] Add ERC2335 ProxyStore type with loader --- crates/cli/src/docker_init.rs | 20 ++++- crates/common/src/config/constants.rs | 8 +- crates/common/src/signer/loader.rs | 2 +- crates/common/src/signer/store.rs | 107 +++++++++++++++++++++++++- 4 files changed, 130 insertions(+), 7 deletions(-) diff --git a/crates/cli/src/docker_init.rs b/crates/cli/src/docker_init.rs index 8da6e7ea..7d4533a7 100644 --- a/crates/cli/src/docker_init.rs +++ b/crates/cli/src/docker_init.rs @@ -9,7 +9,8 @@ use cb_common::{ CommitBoostConfig, LogsSettings, ModuleKind, BUILDER_PORT_ENV, BUILDER_URLS_ENV, CHAIN_SPEC_ENV, CONFIG_DEFAULT, CONFIG_ENV, JWTS_ENV, LOGS_DIR_DEFAULT, LOGS_DIR_ENV, METRICS_PORT_ENV, MODULE_ID_ENV, MODULE_JWT_ENV, PBS_ENDPOINT_ENV, PBS_MODULE_NAME, - PROXY_DIR_DEFAULT, PROXY_DIR_ENV, SIGNER_DEFAULT, SIGNER_DIR_KEYS_DEFAULT, + PROXY_DIR_DEFAULT, PROXY_DIR_ENV, PROXY_DIR_KEYS_DEFAULT, PROXY_DIR_KEYS_ENV, + PROXY_DIR_SECRETS_DEFAULT, PROXY_DIR_SECRETS_ENV, SIGNER_DEFAULT, SIGNER_DIR_KEYS_DEFAULT, SIGNER_DIR_KEYS_ENV, SIGNER_DIR_SECRETS_DEFAULT, SIGNER_DIR_SECRETS_ENV, SIGNER_KEYS_ENV, SIGNER_MODULE_NAME, SIGNER_PORT_ENV, SIGNER_URL_ENV, }, @@ -349,6 +350,23 @@ pub fn handle_docker_init(config_path: String, output_dir: String) -> Result<()> let (k, v) = get_env_val(PROXY_DIR_ENV, PROXY_DIR_DEFAULT); signer_envs.insert(k, v); } + ProxyStore::ERC2335 { keys_path, secrets_path } => { + volumes.push(Volumes::Simple(format!( + "{}:{}:rw", + keys_path.display(), + PROXY_DIR_KEYS_DEFAULT + ))); + let (k, v) = get_env_val(PROXY_DIR_KEYS_ENV, PROXY_DIR_KEYS_DEFAULT); + signer_envs.insert(k, v); + + volumes.push(Volumes::Simple(format!( + "{}:{}:rw", + secrets_path.display(), + PROXY_DIR_SECRETS_DEFAULT + ))); + let (k, v) = get_env_val(PROXY_DIR_SECRETS_ENV, PROXY_DIR_SECRETS_DEFAULT); + signer_envs.insert(k, v); + } } } diff --git a/crates/common/src/config/constants.rs b/crates/common/src/config/constants.rs index 123df0ad..19965215 100644 --- a/crates/common/src/config/constants.rs +++ b/crates/common/src/config/constants.rs @@ -45,9 +45,15 @@ pub const SIGNER_DIR_KEYS_DEFAULT: &str = "/keys"; /// Path to `secrets` folder pub const SIGNER_DIR_SECRETS_ENV: &str = "CB_SIGNER_LOADER_SECRETS_DIR"; pub const SIGNER_DIR_SECRETS_DEFAULT: &str = "/secrets"; -/// Path to store proxies +/// Path to store proxies with plaintext keys (testing only) pub const PROXY_DIR_ENV: &str = "CB_PROXY_STORE_DIR"; pub const PROXY_DIR_DEFAULT: &str = "/proxies"; +/// Path to store proxy keys +pub const PROXY_DIR_KEYS_ENV: &str = "CB_PROXY_KEYS_DIR"; +pub const PROXY_DIR_KEYS_DEFAULT: &str = "/proxy_keys"; +/// Path to store proxy secrets +pub const PROXY_DIR_SECRETS_ENV: &str = "CB_PROXY_SECRETS_DIR"; +pub const PROXY_DIR_SECRETS_DEFAULT: &str = "/proxy_secrets"; ///////////////////////// MODULES ///////////////////////// diff --git a/crates/common/src/signer/loader.rs b/crates/common/src/signer/loader.rs index c06f6716..7c17ccc3 100644 --- a/crates/common/src/signer/loader.rs +++ b/crates/common/src/signer/loader.rs @@ -105,7 +105,7 @@ fn load_secrets_and_keys( Ok(signers) } -fn load_one(ks_path: String, pw_path: String) -> eyre::Result { +pub fn load_one(ks_path: String, pw_path: String) -> eyre::Result { let keystore = Keystore::from_json_file(ks_path).map_err(|_| eyre!("failed reading json"))?; let password = fs::read(pw_path)?; let key = diff --git a/crates/common/src/signer/store.rs b/crates/common/src/signer/store.rs index 5a6e9303..be2b0ecd 100644 --- a/crates/common/src/signer/store.rs +++ b/crates/common/src/signer/store.rs @@ -3,14 +3,20 @@ use std::{ fs::{create_dir_all, read_to_string}, io::Write, path::PathBuf, + str::FromStr, }; -use alloy::primitives::Bytes; +use alloy::{ + primitives::{Bytes, FixedBytes}, + rpc::types::beacon::constants::BLS_SIGNATURE_BYTES_LEN, +}; use serde::{Deserialize, Serialize}; +use serde_utils::hex; +use tracing::warn; use crate::{ - commit::request::{PublicKey, SignedProxyDelegation}, - config::{load_env_var, PROXY_DIR_ENV}, + commit::request::{ProxyDelegation, PublicKey, SignedProxyDelegation}, + config::{load_env_var, PROXY_DIR_ENV, PROXY_DIR_KEYS_ENV, PROXY_DIR_SECRETS_ENV}, signer::{ BlsProxySigner, BlsPublicKey, BlsSigner, EcdsaProxySigner, EcdsaPublicKey, EcdsaSigner, ProxySigners, @@ -18,6 +24,8 @@ use crate::{ types::ModuleId, }; +use super::load_one; + #[derive(Debug, Serialize, Deserialize)] struct KeyAndDelegation { secret: Bytes, @@ -28,7 +36,13 @@ struct KeyAndDelegation { #[serde(untagged)] pub enum ProxyStore { /// Stores private keys in plaintext to a file, do not use in prod - File { proxy_dir: PathBuf }, + File { + proxy_dir: PathBuf, + }, + ERC2335 { + keys_path: PathBuf, + secrets_path: PathBuf, + }, } impl ProxyStore { @@ -38,6 +52,12 @@ impl ProxyStore { let path = load_env_var(PROXY_DIR_ENV)?; ProxyStore::File { proxy_dir: PathBuf::from(path) } } + ProxyStore::ERC2335 { .. } => { + let keys_path = PathBuf::from_str(&load_env_var(PROXY_DIR_KEYS_ENV)?)?; + let secrets_path = PathBuf::from_str(&load_env_var(PROXY_DIR_SECRETS_ENV)?)?; + + ProxyStore::ERC2335 { keys_path, secrets_path } + } }) } @@ -63,6 +83,7 @@ impl ProxyStore { let mut file = std::fs::File::create(file_path)?; file.write_all(content.as_ref())?; } + ProxyStore::ERC2335 { keys_path, secrets_path } => {} } Ok(()) @@ -90,6 +111,7 @@ impl ProxyStore { let mut file = std::fs::File::create(file_path)?; file.write_all(content.as_ref())?; } + ProxyStore::ERC2335 { keys_path, secrets_path } => {} } Ok(()) @@ -183,6 +205,83 @@ impl ProxyStore { Ok((proxy_signers, bls_map, ecdsa_map)) } + ProxyStore::ERC2335 { keys_path, secrets_path } => { + let mut proxy_signers = ProxySigners::default(); + let mut bls_map: HashMap> = HashMap::new(); + let mut ecdsa_map: HashMap> = HashMap::new(); + + for entry in std::fs::read_dir(keys_path)? { + let entry = entry?; + let consensus_key_path = entry.path(); + let consensus_pubkey = + match hex::decode(&entry.file_name().to_string_lossy().to_string()) { + Ok(pubkey) => BlsPublicKey::from(FixedBytes::from_slice(&pubkey)), + Err(e) => { + warn!("Failed to parse consensus pubkey: {e}"); + continue; + } + }; + + if consensus_key_path.is_file() { + warn!("{consensus_key_path:?} is a file"); + continue; + } + + for entry in std::fs::read_dir(&consensus_key_path)? { + let entry = entry?; + let path = entry.path(); + let file_name = entry.file_name().to_string_lossy().to_string(); + let module_id = match file_name.rsplit_once(".") { + Some((module_id, ext)) if ext == "json" => module_id, + _ => continue, + }; + + if path.is_dir() { + warn!("{path:?} is a directory"); + continue; + } + + let signer = load_one( + path.to_string_lossy().to_string(), + secrets_path + .join(format!("{consensus_pubkey:#x}")) + .join(&module_id) + .to_string_lossy() + .to_string(), + )?; + + let delegation_signature = match std::fs::read_to_string( + consensus_key_path.join(format!("{module_id}.sig")), + ) { + Ok(sig) => sig, + Err(e) => { + warn!("Failed to read delegation signature: {e}"); + continue; + } + }; + let delegation_signature = + FixedBytes::::from_str(&delegation_signature)?; + + let proxy_signer = BlsProxySigner { + signer: signer.clone(), + delegation: SignedProxyDelegation:: { + message: ProxyDelegation { + delegator: consensus_pubkey, + proxy: signer.pubkey(), + }, + signature: delegation_signature, + }, + }; + + proxy_signers.bls_signers.insert(signer.pubkey(), proxy_signer); + bls_map + .entry(ModuleId(module_id.into())) + .or_default() + .push(signer.pubkey()); + } + } + Ok((proxy_signers, bls_map, ecdsa_map)) + } } } } From 6e108945980ec60469e7bf0871dbe27bcd44fd8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20I=C3=B1aki=20Bilbao?= Date: Fri, 29 Nov 2024 11:03:42 -0300 Subject: [PATCH 02/18] Small refactor --- crates/common/src/signer/store.rs | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/crates/common/src/signer/store.rs b/crates/common/src/signer/store.rs index be2b0ecd..ca183393 100644 --- a/crates/common/src/signer/store.rs +++ b/crates/common/src/signer/store.rs @@ -11,7 +11,6 @@ use alloy::{ rpc::types::beacon::constants::BLS_SIGNATURE_BYTES_LEN, }; use serde::{Deserialize, Serialize}; -use serde_utils::hex; use tracing::warn; use crate::{ @@ -213,14 +212,15 @@ impl ProxyStore { for entry in std::fs::read_dir(keys_path)? { let entry = entry?; let consensus_key_path = entry.path(); - let consensus_pubkey = - match hex::decode(&entry.file_name().to_string_lossy().to_string()) { - Ok(pubkey) => BlsPublicKey::from(FixedBytes::from_slice(&pubkey)), - Err(e) => { - warn!("Failed to parse consensus pubkey: {e}"); - continue; - } - }; + let consensus_pubkey = match FixedBytes::from_str( + &entry.file_name().to_string_lossy().to_string(), + ) { + Ok(bytes) => BlsPublicKey::from(bytes), + Err(e) => { + warn!("Failed to parse consensus pubkey: {e}"); + continue; + } + }; if consensus_key_path.is_file() { warn!("{consensus_key_path:?} is a file"); @@ -253,14 +253,12 @@ impl ProxyStore { let delegation_signature = match std::fs::read_to_string( consensus_key_path.join(format!("{module_id}.sig")), ) { - Ok(sig) => sig, + Ok(sig) => FixedBytes::::from_str(&sig)?, Err(e) => { warn!("Failed to read delegation signature: {e}"); continue; } }; - let delegation_signature = - FixedBytes::::from_str(&delegation_signature)?; let proxy_signer = BlsProxySigner { signer: signer.clone(), From 4bbad9ff707dd3df1151792556145af3ff58f7e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20I=C3=B1aki=20Bilbao?= Date: Fri, 29 Nov 2024 15:50:59 -0300 Subject: [PATCH 03/18] Store proxy keys and secrets when generated --- crates/common/src/signer/store.rs | 70 ++++++++++++++++++++++++++++++- 1 file changed, 69 insertions(+), 1 deletion(-) diff --git a/crates/common/src/signer/store.rs b/crates/common/src/signer/store.rs index ca183393..e7cde012 100644 --- a/crates/common/src/signer/store.rs +++ b/crates/common/src/signer/store.rs @@ -7,9 +7,19 @@ use std::{ }; use alloy::{ + hex, primitives::{Bytes, FixedBytes}, rpc::types::beacon::constants::BLS_SIGNATURE_BYTES_LEN, }; +use eth2_keystore::{ + default_kdf, + json_keystore::{ + Aes128Ctr, ChecksumModule, Cipher, CipherModule, Crypto, JsonKeystore, KdfModule, + Sha256Checksum, + }, + Uuid, IV_SIZE, SALT_SIZE, +}; +use rand::Rng; use serde::{Deserialize, Serialize}; use tracing::warn; @@ -82,7 +92,65 @@ impl ProxyStore { let mut file = std::fs::File::create(file_path)?; file.write_all(content.as_ref())?; } - ProxyStore::ERC2335 { keys_path, secrets_path } => {} + ProxyStore::ERC2335 { keys_path, secrets_path } => { + let password_bytes: [u8; 32] = rand::thread_rng().gen(); + let password = hex::encode(password_bytes); + + let pass_path = + secrets_path.join(format!("{:#x}", proxy.delegation.message.delegator)); + std::fs::create_dir_all(&pass_path)?; + let pass_path = pass_path.join(format!("{}", &module_id.0)); + let mut pass_file = std::fs::File::create(&pass_path)?; + pass_file.write_all(password.as_bytes())?; + + let sig_path = keys_path.join(format!("{:#x}", proxy.delegation.message.delegator)); + std::fs::create_dir_all(&sig_path)?; + let sig_path = sig_path.join(format!("{}.sig", &module_id.0)); + + let mut sig_file = std::fs::File::create(sig_path)?; + sig_file.write_all(format!("{:#x}", proxy.delegation.signature).as_bytes())?; + + let salt: [u8; SALT_SIZE] = rand::thread_rng().gen(); + let iv: [u8; IV_SIZE] = rand::thread_rng().gen(); + let kdf = default_kdf(salt.to_vec()); + let cipher = Cipher::Aes128Ctr(Aes128Ctr { iv: iv.to_vec().into() }); + let (cipher_text, checksum) = + eth2_keystore::encrypt(&proxy.secret(), password.as_bytes(), &kdf, &cipher) + .map_err(|_| eyre::eyre!("Error encrypting key"))?; + + let keystore = JsonKeystore { + crypto: Crypto { + kdf: KdfModule { + function: kdf.function(), + params: kdf, + message: eth2_keystore::json_keystore::EmptyString, + }, + checksum: ChecksumModule { + function: Sha256Checksum::function(), + params: eth2_keystore::json_keystore::EmptyMap, + message: checksum.to_vec().into(), + }, + cipher: CipherModule { + function: cipher.function(), + params: cipher, + message: cipher_text.into(), + }, + }, + uuid: Uuid::new_v4(), + path: None, + pubkey: format!("{:x}", proxy.pubkey()), + version: eth2_keystore::json_keystore::Version::V4, + description: Some(format!("{:#x}", proxy.pubkey())), + name: None, + }; + + let json_path = + keys_path.join(format!("{:#x}", proxy.delegation.message.delegator)); + std::fs::create_dir_all(&json_path)?; + let json_path = json_path.join(format!("{}.json", &module_id.0)); + let mut json_file = std::fs::File::create(&json_path)?; + json_file.write_all(serde_json::to_string(&keystore)?.as_bytes())?; + } } Ok(()) From d8daf0731556bf9b51d720df9f88d390bc0d2c1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20I=C3=B1aki=20Bilbao?= Date: Fri, 29 Nov 2024 17:08:55 -0300 Subject: [PATCH 04/18] Add support for ECDSA keys --- crates/common/src/signer/store.rs | 233 +++++++++++++++++++++++------- 1 file changed, 181 insertions(+), 52 deletions(-) diff --git a/crates/common/src/signer/store.rs b/crates/common/src/signer/store.rs index e7cde012..93c79fc4 100644 --- a/crates/common/src/signer/store.rs +++ b/crates/common/src/signer/store.rs @@ -96,16 +96,19 @@ impl ProxyStore { let password_bytes: [u8; 32] = rand::thread_rng().gen(); let password = hex::encode(password_bytes); - let pass_path = - secrets_path.join(format!("{:#x}", proxy.delegation.message.delegator)); + let pass_path = secrets_path + .join(format!("{:#x}", proxy.delegation.message.delegator)) + .join(&module_id.0); std::fs::create_dir_all(&pass_path)?; - let pass_path = pass_path.join(format!("{}", &module_id.0)); + let pass_path = pass_path.join("bls"); let mut pass_file = std::fs::File::create(&pass_path)?; pass_file.write_all(password.as_bytes())?; - let sig_path = keys_path.join(format!("{:#x}", proxy.delegation.message.delegator)); + let sig_path = keys_path + .join(format!("{:#x}", proxy.delegation.message.delegator)) + .join(&module_id.0); std::fs::create_dir_all(&sig_path)?; - let sig_path = sig_path.join(format!("{}.sig", &module_id.0)); + let sig_path = sig_path.join("bls.sig"); let mut sig_file = std::fs::File::create(sig_path)?; sig_file.write_all(format!("{:#x}", proxy.delegation.signature).as_bytes())?; @@ -144,10 +147,11 @@ impl ProxyStore { name: None, }; - let json_path = - keys_path.join(format!("{:#x}", proxy.delegation.message.delegator)); + let json_path = keys_path + .join(format!("{:#x}", proxy.delegation.message.delegator)) + .join(&module_id.0); std::fs::create_dir_all(&json_path)?; - let json_path = json_path.join(format!("{}.json", &module_id.0)); + let json_path = json_path.join("bls.json"); let mut json_file = std::fs::File::create(&json_path)?; json_file.write_all(serde_json::to_string(&keystore)?.as_bytes())?; } @@ -178,7 +182,69 @@ impl ProxyStore { let mut file = std::fs::File::create(file_path)?; file.write_all(content.as_ref())?; } - ProxyStore::ERC2335 { keys_path, secrets_path } => {} + ProxyStore::ERC2335 { keys_path, secrets_path } => { + let password_bytes: [u8; 32] = rand::thread_rng().gen(); + let password = hex::encode(password_bytes); + + let pass_path = secrets_path + .join(format!("{:#x}", proxy.delegation.message.delegator)) + .join(&module_id.0); + std::fs::create_dir_all(&pass_path)?; + let pass_path = pass_path.join("ecdsa"); + let mut pass_file = std::fs::File::create(&pass_path)?; + pass_file.write_all(password.as_bytes())?; + + let sig_path = keys_path + .join(format!("{:#x}", proxy.delegation.message.delegator)) + .join(&module_id.0); + std::fs::create_dir_all(&sig_path)?; + let sig_path = sig_path.join("ecdsa.sig"); + + let mut sig_file = std::fs::File::create(sig_path)?; + sig_file.write_all(format!("{:#x}", proxy.delegation.signature).as_bytes())?; + + let salt: [u8; SALT_SIZE] = rand::thread_rng().gen(); + let iv: [u8; IV_SIZE] = rand::thread_rng().gen(); + let kdf = default_kdf(salt.to_vec()); + let cipher = Cipher::Aes128Ctr(Aes128Ctr { iv: iv.to_vec().into() }); + let (cipher_text, checksum) = + eth2_keystore::encrypt(&proxy.secret(), password.as_bytes(), &kdf, &cipher) + .map_err(|_| eyre::eyre!("Error encrypting key"))?; + + let keystore = JsonKeystore { + crypto: Crypto { + kdf: KdfModule { + function: kdf.function(), + params: kdf, + message: eth2_keystore::json_keystore::EmptyString, + }, + checksum: ChecksumModule { + function: Sha256Checksum::function(), + params: eth2_keystore::json_keystore::EmptyMap, + message: checksum.to_vec().into(), + }, + cipher: CipherModule { + function: cipher.function(), + params: cipher, + message: cipher_text.into(), + }, + }, + uuid: Uuid::new_v4(), + path: None, + pubkey: format!("{:x}", proxy.pubkey()), + version: eth2_keystore::json_keystore::Version::V4, + description: Some(format!("{:#x}", proxy.pubkey())), + name: None, + }; + + let json_path = keys_path + .join(format!("{:#x}", proxy.delegation.message.delegator)) + .join(&module_id.0); + std::fs::create_dir_all(&json_path)?; + let json_path = json_path.join("ecdsa.json"); + let mut json_file = std::fs::File::create(&json_path)?; + json_file.write_all(serde_json::to_string(&keystore)?.as_bytes())?; + } } Ok(()) @@ -290,60 +356,123 @@ impl ProxyStore { } }; - if consensus_key_path.is_file() { - warn!("{consensus_key_path:?} is a file"); + if !consensus_key_path.is_dir() { + warn!("{consensus_key_path:?} is not a directory"); continue; } for entry in std::fs::read_dir(&consensus_key_path)? { let entry = entry?; - let path = entry.path(); - let file_name = entry.file_name().to_string_lossy().to_string(); - let module_id = match file_name.rsplit_once(".") { - Some((module_id, ext)) if ext == "json" => module_id, - _ => continue, - }; - - if path.is_dir() { - warn!("{path:?} is a directory"); + let module_path = entry.path(); + let module_id = entry.file_name().to_string_lossy().to_string(); + + if !module_path.is_dir() { + warn!("{module_path:?} is not a directory"); continue; } - let signer = load_one( - path.to_string_lossy().to_string(), - secrets_path - .join(format!("{consensus_pubkey:#x}")) - .join(&module_id) + for entry in std::fs::read_dir(&module_path)? { + let entry = entry?; + let path = entry.path(); + let kind = match entry + .file_name() .to_string_lossy() - .to_string(), - )?; - - let delegation_signature = match std::fs::read_to_string( - consensus_key_path.join(format!("{module_id}.sig")), - ) { - Ok(sig) => FixedBytes::::from_str(&sig)?, - Err(e) => { - warn!("Failed to read delegation signature: {e}"); + .to_string() + .rsplit_once(".") + { + Some((kind, ext)) if ext == "json" => kind.to_string(), + _ => continue, + }; + + if kind == "bls" { + let signer = load_one( + path.to_string_lossy().to_string(), + secrets_path + .join(format!("{consensus_pubkey:#x}")) + .join(&module_id.clone()) + .join("bls") + .to_string_lossy() + .to_string(), + )?; + + let delegation_signature = + match std::fs::read_to_string(module_path.join("bls.sig")) { + Ok(sig) => { + FixedBytes::::from_str(&sig)? + } + Err(e) => { + warn!("Failed to read delegation signature: {e}"); + continue; + } + }; + + let proxy_signer = BlsProxySigner { + signer: signer.clone(), + delegation: SignedProxyDelegation:: { + message: ProxyDelegation { + delegator: consensus_pubkey, + proxy: signer.pubkey(), + }, + signature: delegation_signature, + }, + }; + + proxy_signers.bls_signers.insert(signer.pubkey(), proxy_signer); + bls_map + .entry(ModuleId(module_id.clone().into())) + .or_default() + .push(signer.pubkey()); + } else if kind == "ecdsa" { + let password_file = + std::fs::File::open(path.to_string_lossy().to_string())?; + let password_reader = std::io::BufReader::new(password_file); + let keystore: JsonKeystore = + serde_json::from_reader(password_reader)?; + let password = std::fs::read( + secrets_path + .join(format!("{consensus_pubkey:#x}")) + .join(&module_id) + .join("ecdsa") + .to_string_lossy() + .to_string(), + )?; + let decrypted_password = + eth2_keystore::decrypt(&password, &keystore.crypto).unwrap(); + + let signer = + EcdsaSigner::new_from_bytes(decrypted_password.as_bytes())?; + let delegation_signature = + match std::fs::read_to_string(module_path.join("ecdsa.sig")) { + Ok(sig) => { + FixedBytes::::from_str(&sig)? + } + Err(e) => { + warn!("Failed to read delegation signature: {e}"); + continue; + } + }; + + let proxy_signer = EcdsaProxySigner { + signer: signer.clone(), + delegation: SignedProxyDelegation:: { + message: ProxyDelegation { + delegator: consensus_pubkey, + proxy: signer.pubkey(), + }, + signature: delegation_signature, + }, + }; + + proxy_signers.ecdsa_signers.insert(signer.pubkey(), proxy_signer); + ecdsa_map + .entry(ModuleId(module_id.clone().into())) + .or_default() + .push(signer.pubkey()); + } else { + warn!("Unsupported key type: {kind}"); continue; } - }; - - let proxy_signer = BlsProxySigner { - signer: signer.clone(), - delegation: SignedProxyDelegation:: { - message: ProxyDelegation { - delegator: consensus_pubkey, - proxy: signer.pubkey(), - }, - signature: delegation_signature, - }, - }; - - proxy_signers.bls_signers.insert(signer.pubkey(), proxy_signer); - bls_map - .entry(ModuleId(module_id.into())) - .or_default() - .push(signer.pubkey()); + } } } Ok((proxy_signers, bls_map, ecdsa_map)) From 565cf49b3c68962376dbd7889dc2f40eeecdaaa3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20I=C3=B1aki=20Bilbao?= Date: Fri, 29 Nov 2024 17:59:09 -0300 Subject: [PATCH 05/18] Refactor --- crates/common/src/commit/request.rs | 26 ++- crates/common/src/signer/loader.rs | 20 +- crates/common/src/signer/store.rs | 344 ++++++++++++---------------- 3 files changed, 194 insertions(+), 196 deletions(-) diff --git a/crates/common/src/commit/request.rs b/crates/common/src/commit/request.rs index f2adeba2..6ab1d11d 100644 --- a/crates/common/src/commit/request.rs +++ b/crates/common/src/commit/request.rs @@ -1,4 +1,7 @@ -use std::fmt::{self, Debug, Display, LowerHex}; +use std::{ + fmt::{self, Debug, Display, LowerHex}, + str::FromStr, +}; use alloy::rpc::types::beacon::BlsSignature; use derive_more::derive::From; @@ -133,6 +136,27 @@ pub enum EncryptionScheme { Ecdsa, } +impl ToString for EncryptionScheme { + fn to_string(&self) -> String { + match self { + EncryptionScheme::Bls => "bls".to_string(), + EncryptionScheme::Ecdsa => "ecdsa".to_string(), + } + } +} + +impl FromStr for EncryptionScheme { + type Err = String; + + fn from_str(s: &str) -> Result { + match s { + "bls" => Ok(EncryptionScheme::Bls), + "ecdsa" => Ok(EncryptionScheme::Ecdsa), + _ => Err(format!("Unknown scheme: {s}")), + } + } +} + // TODO(David): This struct shouldn't be visible to module authors #[derive(Debug, Clone, Serialize, Deserialize)] pub struct GenerateProxyRequest { diff --git a/crates/common/src/signer/loader.rs b/crates/common/src/signer/loader.rs index 7c17ccc3..87ebba92 100644 --- a/crates/common/src/signer/loader.rs +++ b/crates/common/src/signer/loader.rs @@ -1,7 +1,7 @@ use std::{fs, path::PathBuf}; use alloy::{primitives::hex::FromHex, rpc::types::beacon::BlsPublicKey}; -use eth2_keystore::Keystore; +use eth2_keystore::{json_keystore::JsonKeystore, Keystore}; use eyre::{eyre, Context}; use serde::{de, Deserialize, Deserializer, Serialize}; use tracing::warn; @@ -11,6 +11,8 @@ use crate::{ signer::ConsensusSigner, }; +use super::{BlsSigner, EcdsaSigner}; + #[derive(Debug, Serialize, Deserialize, Clone)] #[serde(untagged)] pub enum SignerLoader { @@ -105,7 +107,7 @@ fn load_secrets_and_keys( Ok(signers) } -pub fn load_one(ks_path: String, pw_path: String) -> eyre::Result { +fn load_one(ks_path: String, pw_path: String) -> eyre::Result { let keystore = Keystore::from_json_file(ks_path).map_err(|_| eyre!("failed reading json"))?; let password = fs::read(pw_path)?; let key = @@ -113,6 +115,20 @@ pub fn load_one(ks_path: String, pw_path: String) -> eyre::Result eyre::Result { + load_one(keys_path.to_string_lossy().to_string(), secrets_path.to_string_lossy().to_string()) +} + +pub fn load_ecdsa_signer(keys_path: PathBuf, secrets_path: PathBuf) -> eyre::Result { + let key_file = std::fs::File::open(keys_path.to_string_lossy().to_string())?; + let key_reader = std::io::BufReader::new(key_file); + let keystore: JsonKeystore = serde_json::from_reader(key_reader)?; + let password = std::fs::read(secrets_path)?; + let decrypted_password = eth2_keystore::decrypt(&password, &keystore.crypto).unwrap(); + + EcdsaSigner::new_from_bytes(decrypted_password.as_bytes()) +} + #[cfg(test)] mod tests { diff --git a/crates/common/src/signer/store.rs b/crates/common/src/signer/store.rs index 93c79fc4..cd7c0aad 100644 --- a/crates/common/src/signer/store.rs +++ b/crates/common/src/signer/store.rs @@ -24,7 +24,7 @@ use serde::{Deserialize, Serialize}; use tracing::warn; use crate::{ - commit::request::{ProxyDelegation, PublicKey, SignedProxyDelegation}, + commit::request::{EncryptionScheme, ProxyDelegation, PublicKey, SignedProxyDelegation}, config::{load_env_var, PROXY_DIR_ENV, PROXY_DIR_KEYS_ENV, PROXY_DIR_SECRETS_ENV}, signer::{ BlsProxySigner, BlsPublicKey, BlsSigner, EcdsaProxySigner, EcdsaPublicKey, EcdsaSigner, @@ -33,7 +33,7 @@ use crate::{ types::ModuleId, }; -use super::load_one; +use super::{load_bls_signer, load_ecdsa_signer}; #[derive(Debug, Serialize, Deserialize)] struct KeyAndDelegation { @@ -93,67 +93,14 @@ impl ProxyStore { file.write_all(content.as_ref())?; } ProxyStore::ERC2335 { keys_path, secrets_path } => { - let password_bytes: [u8; 32] = rand::thread_rng().gen(); - let password = hex::encode(password_bytes); - - let pass_path = secrets_path - .join(format!("{:#x}", proxy.delegation.message.delegator)) - .join(&module_id.0); - std::fs::create_dir_all(&pass_path)?; - let pass_path = pass_path.join("bls"); - let mut pass_file = std::fs::File::create(&pass_path)?; - pass_file.write_all(password.as_bytes())?; - - let sig_path = keys_path - .join(format!("{:#x}", proxy.delegation.message.delegator)) - .join(&module_id.0); - std::fs::create_dir_all(&sig_path)?; - let sig_path = sig_path.join("bls.sig"); - - let mut sig_file = std::fs::File::create(sig_path)?; - sig_file.write_all(format!("{:#x}", proxy.delegation.signature).as_bytes())?; - - let salt: [u8; SALT_SIZE] = rand::thread_rng().gen(); - let iv: [u8; IV_SIZE] = rand::thread_rng().gen(); - let kdf = default_kdf(salt.to_vec()); - let cipher = Cipher::Aes128Ctr(Aes128Ctr { iv: iv.to_vec().into() }); - let (cipher_text, checksum) = - eth2_keystore::encrypt(&proxy.secret(), password.as_bytes(), &kdf, &cipher) - .map_err(|_| eyre::eyre!("Error encrypting key"))?; - - let keystore = JsonKeystore { - crypto: Crypto { - kdf: KdfModule { - function: kdf.function(), - params: kdf, - message: eth2_keystore::json_keystore::EmptyString, - }, - checksum: ChecksumModule { - function: Sha256Checksum::function(), - params: eth2_keystore::json_keystore::EmptyMap, - message: checksum.to_vec().into(), - }, - cipher: CipherModule { - function: cipher.function(), - params: cipher, - message: cipher_text.into(), - }, - }, - uuid: Uuid::new_v4(), - path: None, - pubkey: format!("{:x}", proxy.pubkey()), - version: eth2_keystore::json_keystore::Version::V4, - description: Some(format!("{:#x}", proxy.pubkey())), - name: None, - }; - - let json_path = keys_path - .join(format!("{:#x}", proxy.delegation.message.delegator)) - .join(&module_id.0); - std::fs::create_dir_all(&json_path)?; - let json_path = json_path.join("bls.json"); - let mut json_file = std::fs::File::create(&json_path)?; - json_file.write_all(serde_json::to_string(&keystore)?.as_bytes())?; + store_erc2335_key( + module_id, + proxy.delegation, + proxy.secret().to_vec(), + keys_path, + secrets_path, + EncryptionScheme::Bls, + )?; } } @@ -183,67 +130,14 @@ impl ProxyStore { file.write_all(content.as_ref())?; } ProxyStore::ERC2335 { keys_path, secrets_path } => { - let password_bytes: [u8; 32] = rand::thread_rng().gen(); - let password = hex::encode(password_bytes); - - let pass_path = secrets_path - .join(format!("{:#x}", proxy.delegation.message.delegator)) - .join(&module_id.0); - std::fs::create_dir_all(&pass_path)?; - let pass_path = pass_path.join("ecdsa"); - let mut pass_file = std::fs::File::create(&pass_path)?; - pass_file.write_all(password.as_bytes())?; - - let sig_path = keys_path - .join(format!("{:#x}", proxy.delegation.message.delegator)) - .join(&module_id.0); - std::fs::create_dir_all(&sig_path)?; - let sig_path = sig_path.join("ecdsa.sig"); - - let mut sig_file = std::fs::File::create(sig_path)?; - sig_file.write_all(format!("{:#x}", proxy.delegation.signature).as_bytes())?; - - let salt: [u8; SALT_SIZE] = rand::thread_rng().gen(); - let iv: [u8; IV_SIZE] = rand::thread_rng().gen(); - let kdf = default_kdf(salt.to_vec()); - let cipher = Cipher::Aes128Ctr(Aes128Ctr { iv: iv.to_vec().into() }); - let (cipher_text, checksum) = - eth2_keystore::encrypt(&proxy.secret(), password.as_bytes(), &kdf, &cipher) - .map_err(|_| eyre::eyre!("Error encrypting key"))?; - - let keystore = JsonKeystore { - crypto: Crypto { - kdf: KdfModule { - function: kdf.function(), - params: kdf, - message: eth2_keystore::json_keystore::EmptyString, - }, - checksum: ChecksumModule { - function: Sha256Checksum::function(), - params: eth2_keystore::json_keystore::EmptyMap, - message: checksum.to_vec().into(), - }, - cipher: CipherModule { - function: cipher.function(), - params: cipher, - message: cipher_text.into(), - }, - }, - uuid: Uuid::new_v4(), - path: None, - pubkey: format!("{:x}", proxy.pubkey()), - version: eth2_keystore::json_keystore::Version::V4, - description: Some(format!("{:#x}", proxy.pubkey())), - name: None, - }; - - let json_path = keys_path - .join(format!("{:#x}", proxy.delegation.message.delegator)) - .join(&module_id.0); - std::fs::create_dir_all(&json_path)?; - let json_path = json_path.join("ecdsa.json"); - let mut json_file = std::fs::File::create(&json_path)?; - json_file.write_all(serde_json::to_string(&keystore)?.as_bytes())?; + store_erc2335_key( + module_id, + proxy.delegation, + proxy.secret(), + keys_path, + secrets_path, + EncryptionScheme::Ecdsa, + )?; } } @@ -374,29 +268,32 @@ impl ProxyStore { for entry in std::fs::read_dir(&module_path)? { let entry = entry?; let path = entry.path(); - let kind = match entry + let scheme: EncryptionScheme = match entry .file_name() .to_string_lossy() .to_string() .rsplit_once(".") { - Some((kind, ext)) if ext == "json" => kind.to_string(), + Some((scheme, ext)) if ext == "json" => { + EncryptionScheme::from_str(scheme) + .map_err(|e| eyre::eyre!(e))? + } _ => continue, }; - if kind == "bls" { - let signer = load_one( - path.to_string_lossy().to_string(), - secrets_path - .join(format!("{consensus_pubkey:#x}")) - .join(&module_id.clone()) - .join("bls") - .to_string_lossy() - .to_string(), - )?; - - let delegation_signature = - match std::fs::read_to_string(module_path.join("bls.sig")) { + match scheme { + EncryptionScheme::Bls => { + let signer = load_bls_signer( + path, + secrets_path + .join(format!("{consensus_pubkey:#x}")) + .join(&module_id) + .join("bls"), + )?; + + let delegation_signature = match std::fs::read_to_string( + module_path.join("bls.sig"), + ) { Ok(sig) => { FixedBytes::::from_str(&sig)? } @@ -406,43 +303,34 @@ impl ProxyStore { } }; - let proxy_signer = BlsProxySigner { - signer: signer.clone(), - delegation: SignedProxyDelegation:: { - message: ProxyDelegation { - delegator: consensus_pubkey, - proxy: signer.pubkey(), + let proxy_signer = BlsProxySigner { + signer: signer.clone(), + delegation: SignedProxyDelegation { + message: ProxyDelegation { + delegator: consensus_pubkey, + proxy: signer.pubkey(), + }, + signature: delegation_signature, }, - signature: delegation_signature, - }, - }; - - proxy_signers.bls_signers.insert(signer.pubkey(), proxy_signer); - bls_map - .entry(ModuleId(module_id.clone().into())) - .or_default() - .push(signer.pubkey()); - } else if kind == "ecdsa" { - let password_file = - std::fs::File::open(path.to_string_lossy().to_string())?; - let password_reader = std::io::BufReader::new(password_file); - let keystore: JsonKeystore = - serde_json::from_reader(password_reader)?; - let password = std::fs::read( - secrets_path - .join(format!("{consensus_pubkey:#x}")) - .join(&module_id) - .join("ecdsa") - .to_string_lossy() - .to_string(), - )?; - let decrypted_password = - eth2_keystore::decrypt(&password, &keystore.crypto).unwrap(); - - let signer = - EcdsaSigner::new_from_bytes(decrypted_password.as_bytes())?; - let delegation_signature = - match std::fs::read_to_string(module_path.join("ecdsa.sig")) { + }; + + proxy_signers.bls_signers.insert(signer.pubkey(), proxy_signer); + bls_map + .entry(ModuleId(module_id.clone().into())) + .or_default() + .push(signer.pubkey()); + } + EncryptionScheme::Ecdsa => { + let signer = load_ecdsa_signer( + path, + secrets_path + .join(format!("{consensus_pubkey:#x}")) + .join(&module_id) + .join("ecdsa"), + )?; + let delegation_signature = match std::fs::read_to_string( + module_path.join("ecdsa.sig"), + ) { Ok(sig) => { FixedBytes::::from_str(&sig)? } @@ -452,25 +340,25 @@ impl ProxyStore { } }; - let proxy_signer = EcdsaProxySigner { - signer: signer.clone(), - delegation: SignedProxyDelegation:: { - message: ProxyDelegation { - delegator: consensus_pubkey, - proxy: signer.pubkey(), + let proxy_signer = EcdsaProxySigner { + signer: signer.clone(), + delegation: SignedProxyDelegation { + message: ProxyDelegation { + delegator: consensus_pubkey, + proxy: signer.pubkey(), + }, + signature: delegation_signature, }, - signature: delegation_signature, - }, - }; - - proxy_signers.ecdsa_signers.insert(signer.pubkey(), proxy_signer); - ecdsa_map - .entry(ModuleId(module_id.clone().into())) - .or_default() - .push(signer.pubkey()); - } else { - warn!("Unsupported key type: {kind}"); - continue; + }; + + proxy_signers + .ecdsa_signers + .insert(signer.pubkey(), proxy_signer); + ecdsa_map + .entry(ModuleId(module_id.clone().into())) + .or_default() + .push(signer.pubkey()); + } } } } @@ -480,3 +368,73 @@ impl ProxyStore { } } } + +fn store_erc2335_key( + module_id: &ModuleId, + delegation: SignedProxyDelegation, + secret: Vec, + keys_path: &PathBuf, + secrets_path: &PathBuf, + scheme: EncryptionScheme, +) -> eyre::Result<()> { + let password_bytes: [u8; 32] = rand::thread_rng().gen(); + let password = hex::encode(password_bytes); + + let pass_path = + secrets_path.join(format!("{:#x}", delegation.message.delegator)).join(&module_id.0); + std::fs::create_dir_all(&pass_path)?; + let pass_path = pass_path.join(scheme.to_string()); + let mut pass_file = std::fs::File::create(&pass_path)?; + pass_file.write_all(password.as_bytes())?; + + let sig_path = + keys_path.join(format!("{:#x}", delegation.message.delegator)).join(&module_id.0); + std::fs::create_dir_all(&sig_path)?; + let sig_path = sig_path.join(format!("{}.sig", scheme.to_string())); + + let mut sig_file = std::fs::File::create(sig_path)?; + sig_file.write_all(format!("{:#x}", delegation.signature).as_bytes())?; + + let salt: [u8; SALT_SIZE] = rand::thread_rng().gen(); + let iv: [u8; IV_SIZE] = rand::thread_rng().gen(); + let kdf = default_kdf(salt.to_vec()); + let cipher = Cipher::Aes128Ctr(Aes128Ctr { iv: iv.to_vec().into() }); + let (cipher_text, checksum) = + eth2_keystore::encrypt(&secret, password.as_bytes(), &kdf, &cipher) + .map_err(|_| eyre::eyre!("Error encrypting key"))?; + + let keystore = JsonKeystore { + crypto: Crypto { + kdf: KdfModule { + function: kdf.function(), + params: kdf, + message: eth2_keystore::json_keystore::EmptyString, + }, + checksum: ChecksumModule { + function: Sha256Checksum::function(), + params: eth2_keystore::json_keystore::EmptyMap, + message: checksum.to_vec().into(), + }, + cipher: CipherModule { + function: cipher.function(), + params: cipher, + message: cipher_text.into(), + }, + }, + uuid: Uuid::new_v4(), + path: None, + pubkey: format!("{:x}", delegation.message.proxy), + version: eth2_keystore::json_keystore::Version::V4, + description: Some(format!("{:#x}", delegation.message.proxy)), + name: None, + }; + + let json_path = + keys_path.join(format!("{:#x}", delegation.message.delegator)).join(&module_id.0); + std::fs::create_dir_all(&json_path)?; + let json_path = json_path.join(format!("{}.json", scheme.to_string())); + let mut json_file = std::fs::File::create(&json_path)?; + json_file.write_all(serde_json::to_string(&keystore)?.as_bytes())?; + + Ok(()) +} From 9c9eec553b908aa769d7e3cb3cfbfa7969fe1382 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20I=C3=B1aki=20Bilbao?= Date: Fri, 29 Nov 2024 18:52:29 -0300 Subject: [PATCH 06/18] Update docs --- config.example.toml | 7 ++- docs/docs/get_started/configuration.md | 86 ++++++++++++++++++++++++-- 2 files changed, 87 insertions(+), 6 deletions(-) diff --git a/config.example.toml b/config.example.toml index 86110b5b..91faed60 100644 --- a/config.example.toml +++ b/config.example.toml @@ -133,12 +133,17 @@ key_path = "./keys.example.json" # keys_path = "" # ValidatorsDir: path to the secrets directory # secrets_path = "" -# Configuration for how the Signer module should store proxy delegations. Currently one type of store is supported: +# Configuration for how the Signer module should store proxy delegations. Supported types of store are: # - File: store keys and delegations from a plain text file (unsafe, use only for testing purposes) +# - ERC2335: store keys and delegations safely using ERC-2335 style keystores. More details can be found in the docs () # OPTIONAL, if missing proxies are lost on restart [signer.store] # File: path to the keys file proxy_dir = "./proxies" +# ERC2335: path to the keys directory +# keys_path = "" +# ERC2335: path to the secrets directory +# secrets_path = "" # Commit-Boost can optionally run "modules" which extend the capabilities of the sidecar. # Currently, two types of modules are supported: diff --git a/docs/docs/get_started/configuration.md b/docs/docs/get_started/configuration.md index 0fd55179..0c9dfbff 100644 --- a/docs/docs/get_started/configuration.md +++ b/docs/docs/get_started/configuration.md @@ -29,12 +29,90 @@ After the sidecar is started, it will expose a port (`18550` in this example), t Note that in this setup, the signer module will not be started. +## Signer Module + +### Proxy keys store + +Proxy keys can be used to sign transactions with a different key than the one used to sign the block. Proxy keys are generated by the Signer module and authorized by the validator key. Each module have their own proxy keys, that can be BLS or ECDSA. + +To persist proxy keys across restarts, you must enable the proxy store in the config file. There are 2 options for this: + +
+ File + + The keys are stored in plain text in a file. This method is unsafe and should only be used for testing. + + #### File structure + + ``` + + └── + └── bls + ├── + └── + ``` + + #### Configuration + + ```toml + [signer.store] + proxy_dir = "path/to/proxy_dir" + ``` + + Where each `` file contains the following: + ```json + { + "secret": "0x...", + "delegation": { + "message": { + "delegator": "0x...", + "proxy": "0x..." + }, + "signature": "0x..." + } + } + ``` +
+ +
+ ERC2335 + + The keys are stored in a ERC-2335 style keystore, among with a password. This way, you can safely share the keys directory so without the password they are useless. + + #### File structure + + ``` + ├── + │ └── + │ └── + │ ├── bls.json + │ ├── bls.sig + │ ├── ecdsa.json + │ └── ecdsa.sig + └── + └── + └── + ├── bls + └── ecdsa + ``` + + #### Configuration + + ```toml + [signer.store] + keys_path = "path/to/keys" + secrets_path = "path/to/secrets" + ``` + + Where the `bls.json` and `ecdsa.json` files contain ERC-2335 keystore, the `bls.sig` and `ecdsa.sig` files contain the signature of the delegation, and `bls` and `ecdsa` files contain the password to decrypt the keystores. +
+ ## Custom module We currently provide a test module that needs to be built locally. To build the module run: ```bash bash scripts/build_local_modules.sh ``` -This will create a Docker image called `test_da_commit` that periodically requests signatures from the validator, and a `test_builder_log` module that logs BuilderAPI events. +This will create a Docker image called `test_da_commit` that periodically requests signatures from the validator, and a `test_builder_log` module that logs BuilderAPI events. The `cb-config.toml` file needs to be updated as follows: ```toml @@ -80,7 +158,7 @@ You can setup Commit-Boost with Vouch in two ways. For simplicity, assume that in Vouch `blockrelay.listen-address: 127.0.0.0:19550` and in Commit-Boost `pbs.port = 18550`. #### Beacon Node to Vouch -In this setup, the BN Builder-API endpoint will be pointing to the Vouch `blockrelay` (e.g. for Lighthouse you will need the flag `--builder=http://127.0.0.0:19550`). +In this setup, the BN Builder-API endpoint will be pointing to the Vouch `blockrelay` (e.g. for Lighthouse you will need the flag `--builder=http://127.0.0.0:19550`). Modify the `blockrelay.config` file to add Commit-Boost: ```json @@ -90,7 +168,7 @@ Modify the `blockrelay.config` file to add Commit-Boost: ``` #### Beacon Node to Commit Boost -In this setup, the BN Builder-API endpoint will be pointing to the PBS module (e.g. for Lighthouse you will need the flag `--builder=http://127.0.0.0:18550`). +In this setup, the BN Builder-API endpoint will be pointing to the PBS module (e.g. for Lighthouse you will need the flag `--builder=http://127.0.0.0:18550`). This will bypass the `blockrelay` entirely so make sure all relays are properly configured in the `[[relays]]` section. @@ -99,5 +177,3 @@ This will bypass the `blockrelay` entirely so make sure all relays are properly ### Notes - It's up to you to decide which relays will be connected via Commit-Boost (`[[relays]]` section in the `toml` config) and which via Vouch (additional entries in the `relays` field). Remember that any rate-limit will be shared across the two sidecars, if running on the same machine. - You may occasionally see a `timeout` error during registrations, especially if you're running a large number of validators in the same instance. This can resolve itself as registrations will be cleared later in the epoch when relays are less busy processing other registrations. Alternatively you can also adjust the `builderclient.timeout` option in `.vouch.yml`. - - From c7bb3c3e9948662226606d0d7bcb60f2410aa7aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20I=C3=B1aki=20Bilbao?= Date: Mon, 2 Dec 2024 10:44:57 -0300 Subject: [PATCH 07/18] Add multiple proxies support for same module --- crates/common/src/signer/store.rs | 221 ++++++++++++++++-------------- 1 file changed, 121 insertions(+), 100 deletions(-) diff --git a/crates/common/src/signer/store.rs b/crates/common/src/signer/store.rs index cd7c0aad..5c77dcba 100644 --- a/crates/common/src/signer/store.rs +++ b/crates/common/src/signer/store.rs @@ -265,100 +265,113 @@ impl ProxyStore { continue; } - for entry in std::fs::read_dir(&module_path)? { - let entry = entry?; - let path = entry.path(); - let scheme: EncryptionScheme = match entry - .file_name() - .to_string_lossy() - .to_string() - .rsplit_once(".") - { - Some((scheme, ext)) if ext == "json" => { - EncryptionScheme::from_str(scheme) - .map_err(|e| eyre::eyre!(e))? + let bls_path = module_path.join("bls"); + if let Ok(bls_keys) = std::fs::read_dir(&bls_path) { + for entry in bls_keys { + let entry = entry?; + let path = entry.path(); + + if !path.is_file() + || !path.extension().is_some_and(|ext| ext == "json") + { + continue; } - _ => continue, - }; - - match scheme { - EncryptionScheme::Bls => { - let signer = load_bls_signer( - path, - secrets_path - .join(format!("{consensus_pubkey:#x}")) - .join(&module_id) - .join("bls"), - )?; - - let delegation_signature = match std::fs::read_to_string( - module_path.join("bls.sig"), - ) { - Ok(sig) => { - FixedBytes::::from_str(&sig)? - } - Err(e) => { - warn!("Failed to read delegation signature: {e}"); - continue; - } - }; - - let proxy_signer = BlsProxySigner { - signer: signer.clone(), - delegation: SignedProxyDelegation { - message: ProxyDelegation { - delegator: consensus_pubkey, - proxy: signer.pubkey(), - }, - signature: delegation_signature, + + let name = entry.file_name().to_string_lossy().to_string(); + let name = name.trim_end_matches(".json"); + + let signer = load_bls_signer( + path, + secrets_path + .join(consensus_pubkey.to_string()) + .join(&module_id) + .join("bls") + .join(name), + ) + .map_err(|e| eyre::eyre!("Error loading BLS signer: {e}"))?; + + let delegation_signature = match std::fs::read_to_string( + bls_path.join(format!("{name}.sig")), + ) { + Ok(sig) => { + FixedBytes::::from_str(&sig)? + } + Err(e) => { + warn!("Failed to read delegation signature: {e}"); + continue; + } + }; + + let proxy_signer = BlsProxySigner { + signer: signer.clone(), + delegation: SignedProxyDelegation { + message: ProxyDelegation { + delegator: consensus_pubkey, + proxy: signer.pubkey(), }, - }; + signature: delegation_signature, + }, + }; + + proxy_signers.bls_signers.insert(signer.pubkey(), proxy_signer); + bls_map + .entry(ModuleId(module_id.clone().into())) + .or_default() + .push(signer.pubkey()); + } + } + + let ecdsa_path = module_path.join("ecdsa"); + if let Ok(ecdsa_keys) = std::fs::read_dir(&ecdsa_path) { + for entry in ecdsa_keys { + let entry = entry?; + let path = entry.path(); - proxy_signers.bls_signers.insert(signer.pubkey(), proxy_signer); - bls_map - .entry(ModuleId(module_id.clone().into())) - .or_default() - .push(signer.pubkey()); + if !path.is_file() + || !path.extension().is_some_and(|ext| ext == "json") + { + continue; } - EncryptionScheme::Ecdsa => { - let signer = load_ecdsa_signer( - path, - secrets_path - .join(format!("{consensus_pubkey:#x}")) - .join(&module_id) - .join("ecdsa"), - )?; - let delegation_signature = match std::fs::read_to_string( - module_path.join("ecdsa.sig"), - ) { - Ok(sig) => { - FixedBytes::::from_str(&sig)? - } - Err(e) => { - warn!("Failed to read delegation signature: {e}"); - continue; - } - }; - - let proxy_signer = EcdsaProxySigner { - signer: signer.clone(), - delegation: SignedProxyDelegation { - message: ProxyDelegation { - delegator: consensus_pubkey, - proxy: signer.pubkey(), - }, - signature: delegation_signature, + + let name = entry.file_name().to_string_lossy().to_string(); + let name = name.trim_end_matches(".json"); + + let signer = load_ecdsa_signer( + path, + secrets_path + .join(format!("{consensus_pubkey:#x}")) + .join(&module_id) + .join("ecdsa") + .join(name), + )?; + let delegation_signature = match std::fs::read_to_string( + ecdsa_path.join(format!("{name}.sig")), + ) { + Ok(sig) => { + FixedBytes::::from_str(&sig)? + } + Err(e) => { + warn!("Failed to read delegation signature: {e}",); + continue; + } + }; + + let proxy_signer = EcdsaProxySigner { + signer: signer.clone(), + delegation: SignedProxyDelegation { + message: ProxyDelegation { + delegator: consensus_pubkey, + proxy: signer.pubkey(), }, - }; - - proxy_signers - .ecdsa_signers - .insert(signer.pubkey(), proxy_signer); - ecdsa_map - .entry(ModuleId(module_id.clone().into())) - .or_default() - .push(signer.pubkey()); - } + signature: delegation_signature, + }, + }; + + proxy_signers.ecdsa_signers.insert(signer.pubkey(), proxy_signer); + ecdsa_map + .entry(ModuleId(module_id.clone().into())) + .or_default() + .push(signer.pubkey()); } } } @@ -377,23 +390,29 @@ fn store_erc2335_key( secrets_path: &PathBuf, scheme: EncryptionScheme, ) -> eyre::Result<()> { + let proxy_pubkey = delegation.message.proxy; + let password_bytes: [u8; 32] = rand::thread_rng().gen(); let password = hex::encode(password_bytes); - let pass_path = - secrets_path.join(format!("{:#x}", delegation.message.delegator)).join(&module_id.0); + let pass_path = secrets_path + .join(delegation.message.delegator.to_string()) + .join(&module_id.0) + .join(scheme.to_string()); std::fs::create_dir_all(&pass_path)?; - let pass_path = pass_path.join(scheme.to_string()); + let pass_path = pass_path.join(proxy_pubkey.to_string()); let mut pass_file = std::fs::File::create(&pass_path)?; pass_file.write_all(password.as_bytes())?; - let sig_path = - keys_path.join(format!("{:#x}", delegation.message.delegator)).join(&module_id.0); + let sig_path = keys_path + .join(delegation.message.delegator.to_string()) + .join(&module_id.0) + .join(scheme.to_string()); std::fs::create_dir_all(&sig_path)?; - let sig_path = sig_path.join(format!("{}.sig", scheme.to_string())); + let sig_path = sig_path.join(format!("{}.sig", proxy_pubkey.to_string())); let mut sig_file = std::fs::File::create(sig_path)?; - sig_file.write_all(format!("{:#x}", delegation.signature).as_bytes())?; + sig_file.write_all(delegation.signature.to_string().as_bytes())?; let salt: [u8; SALT_SIZE] = rand::thread_rng().gen(); let iv: [u8; IV_SIZE] = rand::thread_rng().gen(); @@ -425,14 +444,16 @@ fn store_erc2335_key( path: None, pubkey: format!("{:x}", delegation.message.proxy), version: eth2_keystore::json_keystore::Version::V4, - description: Some(format!("{:#x}", delegation.message.proxy)), + description: Some(delegation.message.proxy.to_string()), name: None, }; - let json_path = - keys_path.join(format!("{:#x}", delegation.message.delegator)).join(&module_id.0); + let json_path = keys_path + .join(delegation.message.delegator.to_string()) + .join(&module_id.0) + .join(scheme.to_string()); std::fs::create_dir_all(&json_path)?; - let json_path = json_path.join(format!("{}.json", scheme.to_string())); + let json_path = json_path.join(format!("{}.json", proxy_pubkey)); let mut json_file = std::fs::File::create(&json_path)?; json_file.write_all(serde_json::to_string(&keystore)?.as_bytes())?; From b8add6ce17b060e602708a219765486bfb6ce6af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20I=C3=B1aki=20Bilbao?= Date: Mon, 2 Dec 2024 10:52:20 -0300 Subject: [PATCH 08/18] Fix clippy --- crates/common/src/commit/request.rs | 8 +++--- crates/common/src/signer/loader.rs | 3 +-- crates/common/src/signer/store.rs | 40 ++++++++++++++--------------- 3 files changed, 24 insertions(+), 27 deletions(-) diff --git a/crates/common/src/commit/request.rs b/crates/common/src/commit/request.rs index 6ab1d11d..7f6ce49e 100644 --- a/crates/common/src/commit/request.rs +++ b/crates/common/src/commit/request.rs @@ -136,11 +136,11 @@ pub enum EncryptionScheme { Ecdsa, } -impl ToString for EncryptionScheme { - fn to_string(&self) -> String { +impl Display for EncryptionScheme { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - EncryptionScheme::Bls => "bls".to_string(), - EncryptionScheme::Ecdsa => "ecdsa".to_string(), + EncryptionScheme::Bls => write!(f, "bls"), + EncryptionScheme::Ecdsa => write!(f, "ecdsa"), } } } diff --git a/crates/common/src/signer/loader.rs b/crates/common/src/signer/loader.rs index 87ebba92..a37672ad 100644 --- a/crates/common/src/signer/loader.rs +++ b/crates/common/src/signer/loader.rs @@ -6,13 +6,12 @@ use eyre::{eyre, Context}; use serde::{de, Deserialize, Deserializer, Serialize}; use tracing::warn; +use super::{BlsSigner, EcdsaSigner}; use crate::{ config::{load_env_var, SIGNER_DIR_KEYS_ENV, SIGNER_DIR_SECRETS_ENV, SIGNER_KEYS_ENV}, signer::ConsensusSigner, }; -use super::{BlsSigner, EcdsaSigner}; - #[derive(Debug, Serialize, Deserialize, Clone)] #[serde(untagged)] pub enum SignerLoader { diff --git a/crates/common/src/signer/store.rs b/crates/common/src/signer/store.rs index 5c77dcba..431eb709 100644 --- a/crates/common/src/signer/store.rs +++ b/crates/common/src/signer/store.rs @@ -2,7 +2,7 @@ use std::{ collections::HashMap, fs::{create_dir_all, read_to_string}, io::Write, - path::PathBuf, + path::{Path, PathBuf}, str::FromStr, }; @@ -23,6 +23,7 @@ use rand::Rng; use serde::{Deserialize, Serialize}; use tracing::warn; +use super::{load_bls_signer, load_ecdsa_signer}; use crate::{ commit::request::{EncryptionScheme, ProxyDelegation, PublicKey, SignedProxyDelegation}, config::{load_env_var, PROXY_DIR_ENV, PROXY_DIR_KEYS_ENV, PROXY_DIR_SECRETS_ENV}, @@ -33,8 +34,6 @@ use crate::{ types::ModuleId, }; -use super::{load_bls_signer, load_ecdsa_signer}; - #[derive(Debug, Serialize, Deserialize)] struct KeyAndDelegation { secret: Bytes, @@ -240,15 +239,14 @@ impl ProxyStore { for entry in std::fs::read_dir(keys_path)? { let entry = entry?; let consensus_key_path = entry.path(); - let consensus_pubkey = match FixedBytes::from_str( - &entry.file_name().to_string_lossy().to_string(), - ) { - Ok(bytes) => BlsPublicKey::from(bytes), - Err(e) => { - warn!("Failed to parse consensus pubkey: {e}"); - continue; - } - }; + let consensus_pubkey = + match FixedBytes::from_str(&entry.file_name().to_string_lossy()) { + Ok(bytes) => BlsPublicKey::from(bytes), + Err(e) => { + warn!("Failed to parse consensus pubkey: {e}"); + continue; + } + }; if !consensus_key_path.is_dir() { warn!("{consensus_key_path:?} is not a directory"); @@ -271,8 +269,8 @@ impl ProxyStore { let entry = entry?; let path = entry.path(); - if !path.is_file() - || !path.extension().is_some_and(|ext| ext == "json") + if !path.is_file() || + !path.extension().is_some_and(|ext| ext == "json") { continue; } @@ -315,7 +313,7 @@ impl ProxyStore { proxy_signers.bls_signers.insert(signer.pubkey(), proxy_signer); bls_map - .entry(ModuleId(module_id.clone().into())) + .entry(ModuleId(module_id.clone())) .or_default() .push(signer.pubkey()); } @@ -327,8 +325,8 @@ impl ProxyStore { let entry = entry?; let path = entry.path(); - if !path.is_file() - || !path.extension().is_some_and(|ext| ext == "json") + if !path.is_file() || + !path.extension().is_some_and(|ext| ext == "json") { continue; } @@ -369,7 +367,7 @@ impl ProxyStore { proxy_signers.ecdsa_signers.insert(signer.pubkey(), proxy_signer); ecdsa_map - .entry(ModuleId(module_id.clone().into())) + .entry(ModuleId(module_id.clone())) .or_default() .push(signer.pubkey()); } @@ -386,8 +384,8 @@ fn store_erc2335_key( module_id: &ModuleId, delegation: SignedProxyDelegation, secret: Vec, - keys_path: &PathBuf, - secrets_path: &PathBuf, + keys_path: &Path, + secrets_path: &Path, scheme: EncryptionScheme, ) -> eyre::Result<()> { let proxy_pubkey = delegation.message.proxy; @@ -409,7 +407,7 @@ fn store_erc2335_key( .join(&module_id.0) .join(scheme.to_string()); std::fs::create_dir_all(&sig_path)?; - let sig_path = sig_path.join(format!("{}.sig", proxy_pubkey.to_string())); + let sig_path = sig_path.join(format!("{}.sig", proxy_pubkey)); let mut sig_file = std::fs::File::create(sig_path)?; sig_file.write_all(delegation.signature.to_string().as_bytes())?; From 8b9f3df469579b1773fb94569c266caa4f87e74c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20I=C3=B1aki=20Bilbao?= Date: Mon, 2 Dec 2024 14:50:21 -0300 Subject: [PATCH 09/18] Add tests --- crates/common/src/signer/store.rs | 202 ++++++++++++++++++ ...8e6b1123b8869032ba18b2383e8873294f0ba.json | 1 + ...78e6b1123b8869032ba18b2383e8873294f0ba.sig | 1 + ...fd5478e6b1123b8869032ba18b2383e8873294f0ba | 1 + 4 files changed, 205 insertions(+) create mode 100644 tests/data/proxy/keys/0xac5e059177afc33263e95d0be0690138b9a1d79a6e19018086a0362e0c30a50bf9e05a08cb44785724d0b2718c5c7118/TEST_MODULE/bls/0xa77084280678d9f1efe4ef47a3d62af27872ce82db19a35ee012c4fd5478e6b1123b8869032ba18b2383e8873294f0ba.json create mode 100644 tests/data/proxy/keys/0xac5e059177afc33263e95d0be0690138b9a1d79a6e19018086a0362e0c30a50bf9e05a08cb44785724d0b2718c5c7118/TEST_MODULE/bls/0xa77084280678d9f1efe4ef47a3d62af27872ce82db19a35ee012c4fd5478e6b1123b8869032ba18b2383e8873294f0ba.sig create mode 100644 tests/data/proxy/secrets/0xac5e059177afc33263e95d0be0690138b9a1d79a6e19018086a0362e0c30a50bf9e05a08cb44785724d0b2718c5c7118/TEST_MODULE/bls/0xa77084280678d9f1efe4ef47a3d62af27872ce82db19a35ee012c4fd5478e6b1123b8869032ba18b2383e8873294f0ba diff --git a/crates/common/src/signer/store.rs b/crates/common/src/signer/store.rs index 431eb709..0d35fc2b 100644 --- a/crates/common/src/signer/store.rs +++ b/crates/common/src/signer/store.rs @@ -457,3 +457,205 @@ fn store_erc2335_key( Ok(()) } + +#[cfg(test)] +mod test { + use hex::FromHex; + use tree_hash::TreeHash; + + use super::*; + use crate::{ + commit::request::{ProxyDelegationBls, SignedProxyDelegationBls}, + signer::ConsensusSigner, + types::Chain, + }; + + #[tokio::test] + async fn test_erc2335_storage_format() { + let tmp_path = std::env::temp_dir(); + let keys_path = tmp_path.join("keys"); + let secrets_path = tmp_path.join("secrets"); + let store = ProxyStore::ERC2335 { + keys_path: keys_path.clone(), + secrets_path: secrets_path.clone(), + }; + + let module_id = ModuleId("TEST_MODULE".to_string()); + let consensus_signer = ConsensusSigner::new_from_bytes(&hex!( + "0088e364a5396a81b50febbdc8784663fb9089b5e67cbdc173991a00c587673f" + )) + .unwrap(); + let proxy_signer = BlsSigner::new_from_bytes(&hex!( + "13000f8b3d7747e7754022720d33d5b506490429f3d593162f00e254f97d2940" + )) + .unwrap(); + + let message = ProxyDelegationBls { + delegator: consensus_signer.pubkey(), + proxy: proxy_signer.pubkey(), + }; + let signature = consensus_signer.sign(Chain::Mainnet, message.tree_hash_root().0).await; + let delegation = SignedProxyDelegationBls { signature, message }; + let proxy_signer = BlsProxySigner { signer: proxy_signer, delegation }; + + store.store_proxy_bls(&module_id, &proxy_signer).unwrap(); + + let json_path = keys_path + .join(consensus_signer.pubkey().to_string()) + .join("TEST_MODULE") + .join("BLS") + .join(format!("{}.json", proxy_signer.pubkey().to_string())); + let sig_path = keys_path + .join(consensus_signer.pubkey().to_string()) + .join("TEST_MODULE") + .join("BLS") + .join(format!("{}.sig", proxy_signer.pubkey().to_string())); + let pass_path = secrets_path + .join(consensus_signer.pubkey().to_string()) + .join("TEST_MODULE") + .join("BLS") + .join(proxy_signer.pubkey().to_string()); + + assert!(json_path.exists()); + assert!(sig_path.exists()); + assert!(pass_path.exists()); + + let keystore: JsonKeystore = serde_json::de::from_str( + &std::fs::read_to_string( + keys_path + .join(consensus_signer.pubkey().to_string()) + .join("TEST_MODULE") + .join("bls") + .join(format!("{}.json", proxy_signer.pubkey().to_string())), + ) + .unwrap(), + ) + .unwrap(); + + assert_eq!(keystore.pubkey, proxy_signer.pubkey().to_string().trim_start_matches("0x")); + + let sig = FixedBytes::from_hex(std::fs::read_to_string(sig_path).unwrap()); + assert!(sig.is_ok()); + assert_eq!(sig.unwrap(), signature); + } + + #[test] + fn test_erc2335_load() { + let keys_path = Path::new("../../tests/data/proxy/keys").to_path_buf(); + let secrets_path = Path::new("../../tests/data/proxy/secrets").to_path_buf(); + let store = ProxyStore::ERC2335 { + keys_path: keys_path.clone(), + secrets_path: secrets_path.clone(), + }; + + let (proxy_signers, bls_keys, ecdsa_keys) = store.load_proxies().unwrap(); + assert_eq!(bls_keys.len(), 1); + assert_eq!(ecdsa_keys.len(), 0); + assert_eq!(proxy_signers.bls_signers.len(), 1); + assert_eq!(proxy_signers.ecdsa_signers.len(), 0); + + let proxy_key = BlsPublicKey::from( + FixedBytes::from_hex( + "a77084280678d9f1efe4ef47a3d62af27872ce82db19a35ee012c4fd5478e6b1123b8869032ba18b2383e8873294f0ba" + ).unwrap() + ); + let consensus_key = BlsPublicKey::from( + FixedBytes::from_hex( + "ac5e059177afc33263e95d0be0690138b9a1d79a6e19018086a0362e0c30a50bf9e05a08cb44785724d0b2718c5c7118" + ).unwrap() + ); + + let proxy_signer = proxy_signers.bls_signers.get(&proxy_key); + + assert!(proxy_signer.is_some()); + let proxy_signer = proxy_signer.unwrap(); + + assert_eq!( + proxy_signer.delegation.signature, + FixedBytes::from_hex( + std::fs::read_to_string( + keys_path + .join(consensus_key.to_string()) + .join("TEST_MODULE") + .join("bls") + .join(format!("{proxy_key}.sig")) + ) + .unwrap() + ) + .unwrap() + ); + assert_eq!(proxy_signer.delegation.message.delegator, consensus_key); + assert_eq!(proxy_signer.delegation.message.proxy, proxy_key); + + assert!(bls_keys + .get(&ModuleId("TEST_MODULE".into())) + .is_some_and(|keys| keys.contains(&proxy_key))); + } + + #[tokio::test] + async fn test_erc2335_store_and_load() { + let tmp_path = std::env::temp_dir(); + let keys_path = tmp_path.join("keys"); + let secrets_path = tmp_path.join("secrets"); + let store = ProxyStore::ERC2335 { + keys_path: keys_path.clone(), + secrets_path: secrets_path.clone(), + }; + + let module_id = ModuleId("TEST_MODULE".to_string()); + let consensus_signer = ConsensusSigner::new_from_bytes(&hex!( + "0088e364a5396a81b50febbdc8784663fb9089b5e67cbdc173991a00c587673f" + )) + .unwrap(); + let proxy_signer = BlsSigner::new_from_bytes(&hex!( + "13000f8b3d7747e7754022720d33d5b506490429f3d593162f00e254f97d2940" + )) + .unwrap(); + + let message = ProxyDelegationBls { + delegator: consensus_signer.pubkey(), + proxy: proxy_signer.pubkey(), + }; + let signature = consensus_signer.sign(Chain::Mainnet, message.tree_hash_root().0).await; + let delegation = SignedProxyDelegationBls { signature, message }; + let proxy_signer = BlsProxySigner { signer: proxy_signer, delegation }; + + store.store_proxy_bls(&module_id, &proxy_signer).unwrap(); + + let load_result = store.load_proxies(); + assert!(load_result.is_ok()); + + let (proxy_signers, bls_keys, ecdsa_keys) = load_result.unwrap(); + + assert_eq!(bls_keys.len(), 1); + assert_eq!(ecdsa_keys.len(), 0); + assert_eq!(proxy_signers.bls_signers.len(), 1); + assert_eq!(proxy_signers.ecdsa_signers.len(), 0); + + let loaded_proxy_signer = proxy_signers.bls_signers.get(&proxy_signer.pubkey()); + + assert!(loaded_proxy_signer.is_some()); + let loaded_proxy_signer = loaded_proxy_signer.unwrap(); + + assert_eq!( + loaded_proxy_signer.delegation.signature, + FixedBytes::from_hex( + std::fs::read_to_string( + keys_path + .join(consensus_signer.pubkey().to_string()) + .join("TEST_MODULE") + .join("bls") + .join(format!("{}.sig", proxy_signer.pubkey().to_string())) + ) + .unwrap() + ) + .unwrap() + ); + assert_eq!(loaded_proxy_signer.delegation.message.delegator, consensus_signer.pubkey()); + assert_eq!(loaded_proxy_signer.delegation.message.proxy, proxy_signer.pubkey()); + + assert!(bls_keys + .get(&ModuleId("TEST_MODULE".into())) + .is_some_and(|keys| keys.contains(&proxy_signer.pubkey()))); + } +} diff --git a/tests/data/proxy/keys/0xac5e059177afc33263e95d0be0690138b9a1d79a6e19018086a0362e0c30a50bf9e05a08cb44785724d0b2718c5c7118/TEST_MODULE/bls/0xa77084280678d9f1efe4ef47a3d62af27872ce82db19a35ee012c4fd5478e6b1123b8869032ba18b2383e8873294f0ba.json b/tests/data/proxy/keys/0xac5e059177afc33263e95d0be0690138b9a1d79a6e19018086a0362e0c30a50bf9e05a08cb44785724d0b2718c5c7118/TEST_MODULE/bls/0xa77084280678d9f1efe4ef47a3d62af27872ce82db19a35ee012c4fd5478e6b1123b8869032ba18b2383e8873294f0ba.json new file mode 100644 index 00000000..e55d22ab --- /dev/null +++ b/tests/data/proxy/keys/0xac5e059177afc33263e95d0be0690138b9a1d79a6e19018086a0362e0c30a50bf9e05a08cb44785724d0b2718c5c7118/TEST_MODULE/bls/0xa77084280678d9f1efe4ef47a3d62af27872ce82db19a35ee012c4fd5478e6b1123b8869032ba18b2383e8873294f0ba.json @@ -0,0 +1 @@ +{"crypto":{"kdf":{"function":"scrypt","params":{"dklen":32,"n":262144,"r":8,"p":1,"salt":"c84961e82805391c0f761cf342c1e6293dab474d388179f4fdea8386310d3920"},"message":""},"checksum":{"function":"sha256","params":{},"message":"4a6ed334d558abeb81ea04893eeed79214eaec476d6225bacccbc7ffbde95843"},"cipher":{"function":"aes-128-ctr","params":{"iv":"bff99639dd8ad6e3339177bad87dcac4"},"message":"e9bca9829d688baa09e65ddecadedd1cb6b49c024a9fff98630817cf835aa9bb"}},"uuid":"38fcc27a-da59-4604-8858-cf3d58d06acc","path":null,"pubkey":"a77084280678d9f1efe4ef47a3d62af27872ce82db19a35ee012c4fd5478e6b1123b8869032ba18b2383e8873294f0ba","version":4,"description":"0xa77084280678d9f1efe4ef47a3d62af27872ce82db19a35ee012c4fd5478e6b1123b8869032ba18b2383e8873294f0ba","name":null} \ No newline at end of file diff --git a/tests/data/proxy/keys/0xac5e059177afc33263e95d0be0690138b9a1d79a6e19018086a0362e0c30a50bf9e05a08cb44785724d0b2718c5c7118/TEST_MODULE/bls/0xa77084280678d9f1efe4ef47a3d62af27872ce82db19a35ee012c4fd5478e6b1123b8869032ba18b2383e8873294f0ba.sig b/tests/data/proxy/keys/0xac5e059177afc33263e95d0be0690138b9a1d79a6e19018086a0362e0c30a50bf9e05a08cb44785724d0b2718c5c7118/TEST_MODULE/bls/0xa77084280678d9f1efe4ef47a3d62af27872ce82db19a35ee012c4fd5478e6b1123b8869032ba18b2383e8873294f0ba.sig new file mode 100644 index 00000000..2ac675c2 --- /dev/null +++ b/tests/data/proxy/keys/0xac5e059177afc33263e95d0be0690138b9a1d79a6e19018086a0362e0c30a50bf9e05a08cb44785724d0b2718c5c7118/TEST_MODULE/bls/0xa77084280678d9f1efe4ef47a3d62af27872ce82db19a35ee012c4fd5478e6b1123b8869032ba18b2383e8873294f0ba.sig @@ -0,0 +1 @@ +0xb2e44e777cc68b50b9d19cbded2b2b6a0a5c428e3c341b5ade22f90e67679116511855b94e26ae930d1350628933994713f4fd48d1d70715a99d875a564c88e229aa9bb2d89e9f60b725c97300659bd0fc7bc1e2e599f12625b81ef63890f857 \ No newline at end of file diff --git a/tests/data/proxy/secrets/0xac5e059177afc33263e95d0be0690138b9a1d79a6e19018086a0362e0c30a50bf9e05a08cb44785724d0b2718c5c7118/TEST_MODULE/bls/0xa77084280678d9f1efe4ef47a3d62af27872ce82db19a35ee012c4fd5478e6b1123b8869032ba18b2383e8873294f0ba b/tests/data/proxy/secrets/0xac5e059177afc33263e95d0be0690138b9a1d79a6e19018086a0362e0c30a50bf9e05a08cb44785724d0b2718c5c7118/TEST_MODULE/bls/0xa77084280678d9f1efe4ef47a3d62af27872ce82db19a35ee012c4fd5478e6b1123b8869032ba18b2383e8873294f0ba new file mode 100644 index 00000000..6d8f0bc3 --- /dev/null +++ b/tests/data/proxy/secrets/0xac5e059177afc33263e95d0be0690138b9a1d79a6e19018086a0362e0c30a50bf9e05a08cb44785724d0b2718c5c7118/TEST_MODULE/bls/0xa77084280678d9f1efe4ef47a3d62af27872ce82db19a35ee012c4fd5478e6b1123b8869032ba18b2383e8873294f0ba @@ -0,0 +1 @@ +4ecdc703bdc0b4957876643fbba74f20f5cf7e4435b852fcd9b2d0c2b977a854 \ No newline at end of file From 84a0fc7a4a2f1299ddfee515e4d67bbfbe2a1c4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20I=C3=B1aki=20Bilbao?= Date: Mon, 2 Dec 2024 15:04:41 -0300 Subject: [PATCH 10/18] Update docs --- config.example.toml | 2 +- docs/docs/get_started/configuration.md | 19 +++++++++++++------ 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/config.example.toml b/config.example.toml index 91faed60..5e8a0475 100644 --- a/config.example.toml +++ b/config.example.toml @@ -135,7 +135,7 @@ key_path = "./keys.example.json" # secrets_path = "" # Configuration for how the Signer module should store proxy delegations. Supported types of store are: # - File: store keys and delegations from a plain text file (unsafe, use only for testing purposes) -# - ERC2335: store keys and delegations safely using ERC-2335 style keystores. More details can be found in the docs () +# - ERC2335: store keys and delegations safely using ERC-2335 style keystores. More details can be found in the docs (https://commit-boost.github.io/commit-boost-client/get_started/configuration#proxy-keys-store) # OPTIONAL, if missing proxies are lost on restart [signer.store] # File: path to the keys file diff --git a/docs/docs/get_started/configuration.md b/docs/docs/get_started/configuration.md index 0c9dfbff..558a4c94 100644 --- a/docs/docs/get_started/configuration.md +++ b/docs/docs/get_started/configuration.md @@ -85,15 +85,22 @@ To persist proxy keys across restarts, you must enable the proxy store in the co ├── │ └── │ └── - │ ├── bls.json - │ ├── bls.sig - │ ├── ecdsa.json - │ └── ecdsa.sig + │ ├── bls/ + │ │ ├── .json + │ │ ├── .sig + │ │ ├── .json + │ │ └── .sig + │ └── ecdsa/ + │ ├── .json + │ └── .sig └── └── └── - ├── bls + ├── bls/ + │ ├── + │ └── └── ecdsa + └── ``` #### Configuration @@ -104,7 +111,7 @@ To persist proxy keys across restarts, you must enable the proxy store in the co secrets_path = "path/to/secrets" ``` - Where the `bls.json` and `ecdsa.json` files contain ERC-2335 keystore, the `bls.sig` and `ecdsa.sig` files contain the signature of the delegation, and `bls` and `ecdsa` files contain the password to decrypt the keystores. + Where the `.json` files contain ERC-2335 keystore, the `.sig` files contain the signature of the delegation, and `` files contain the password to decrypt the keystores. ## Custom module From 77fe6e3535b6eddd5b9b819f62e060222580e539 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20I=C3=B1aki=20Bilbao?= Date: Mon, 2 Dec 2024 16:08:01 -0300 Subject: [PATCH 11/18] Fix parallel tests failures --- crates/common/src/signer/store.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/common/src/signer/store.rs b/crates/common/src/signer/store.rs index 0d35fc2b..b0d13822 100644 --- a/crates/common/src/signer/store.rs +++ b/crates/common/src/signer/store.rs @@ -472,7 +472,7 @@ mod test { #[tokio::test] async fn test_erc2335_storage_format() { - let tmp_path = std::env::temp_dir(); + let tmp_path = std::env::temp_dir().join("test_erc2335_storage_format"); let keys_path = tmp_path.join("keys"); let secrets_path = tmp_path.join("secrets"); let store = ProxyStore::ERC2335 { @@ -594,7 +594,7 @@ mod test { #[tokio::test] async fn test_erc2335_store_and_load() { - let tmp_path = std::env::temp_dir(); + let tmp_path = std::env::temp_dir().join("test_erc2335_store_and_load"); let keys_path = tmp_path.join("keys"); let secrets_path = tmp_path.join("secrets"); let store = ProxyStore::ERC2335 { From 25d6509e27dfd75cf1abc1518e5ffc5d20996b28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20I=C3=B1aki=20Bilbao?= Date: Mon, 2 Dec 2024 16:52:29 -0300 Subject: [PATCH 12/18] Run tests sequentially --- crates/common/src/signer/store.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/crates/common/src/signer/store.rs b/crates/common/src/signer/store.rs index b0d13822..27c2eee6 100644 --- a/crates/common/src/signer/store.rs +++ b/crates/common/src/signer/store.rs @@ -470,7 +470,6 @@ mod test { types::Chain, }; - #[tokio::test] async fn test_erc2335_storage_format() { let tmp_path = std::env::temp_dir().join("test_erc2335_storage_format"); let keys_path = tmp_path.join("keys"); @@ -539,7 +538,6 @@ mod test { assert_eq!(sig.unwrap(), signature); } - #[test] fn test_erc2335_load() { let keys_path = Path::new("../../tests/data/proxy/keys").to_path_buf(); let secrets_path = Path::new("../../tests/data/proxy/secrets").to_path_buf(); @@ -592,7 +590,6 @@ mod test { .is_some_and(|keys| keys.contains(&proxy_key))); } - #[tokio::test] async fn test_erc2335_store_and_load() { let tmp_path = std::env::temp_dir().join("test_erc2335_store_and_load"); let keys_path = tmp_path.join("keys"); @@ -658,4 +655,11 @@ mod test { .get(&ModuleId("TEST_MODULE".into())) .is_some_and(|keys| keys.contains(&proxy_signer.pubkey()))); } + + #[tokio::test] + async fn test_erc2335_store() { + test_erc2335_storage_format().await; + test_erc2335_load(); + test_erc2335_store_and_load().await; + } } From a758efbda12ae79177511bbc7e0796c144188c2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20I=C3=B1aki=20Bilbao?= Date: Mon, 2 Dec 2024 17:57:42 -0300 Subject: [PATCH 13/18] Fix paths --- crates/common/src/signer/store.rs | 29 ++++++++--------------------- 1 file changed, 8 insertions(+), 21 deletions(-) diff --git a/crates/common/src/signer/store.rs b/crates/common/src/signer/store.rs index 27c2eee6..e2c9c44b 100644 --- a/crates/common/src/signer/store.rs +++ b/crates/common/src/signer/store.rs @@ -470,6 +470,7 @@ mod test { types::Chain, }; + #[tokio::test] async fn test_erc2335_storage_format() { let tmp_path = std::env::temp_dir().join("test_erc2335_storage_format"); let keys_path = tmp_path.join("keys"); @@ -502,34 +503,25 @@ mod test { let json_path = keys_path .join(consensus_signer.pubkey().to_string()) .join("TEST_MODULE") - .join("BLS") + .join("bls") .join(format!("{}.json", proxy_signer.pubkey().to_string())); let sig_path = keys_path .join(consensus_signer.pubkey().to_string()) .join("TEST_MODULE") - .join("BLS") + .join("bls") .join(format!("{}.sig", proxy_signer.pubkey().to_string())); let pass_path = secrets_path .join(consensus_signer.pubkey().to_string()) .join("TEST_MODULE") - .join("BLS") + .join("bls") .join(proxy_signer.pubkey().to_string()); assert!(json_path.exists()); assert!(sig_path.exists()); assert!(pass_path.exists()); - let keystore: JsonKeystore = serde_json::de::from_str( - &std::fs::read_to_string( - keys_path - .join(consensus_signer.pubkey().to_string()) - .join("TEST_MODULE") - .join("bls") - .join(format!("{}.json", proxy_signer.pubkey().to_string())), - ) - .unwrap(), - ) - .unwrap(); + let keystore: JsonKeystore = + serde_json::de::from_str(&std::fs::read_to_string(json_path).unwrap()).unwrap(); assert_eq!(keystore.pubkey, proxy_signer.pubkey().to_string().trim_start_matches("0x")); @@ -538,6 +530,7 @@ mod test { assert_eq!(sig.unwrap(), signature); } + #[test] fn test_erc2335_load() { let keys_path = Path::new("../../tests/data/proxy/keys").to_path_buf(); let secrets_path = Path::new("../../tests/data/proxy/secrets").to_path_buf(); @@ -590,6 +583,7 @@ mod test { .is_some_and(|keys| keys.contains(&proxy_key))); } + #[tokio::test] async fn test_erc2335_store_and_load() { let tmp_path = std::env::temp_dir().join("test_erc2335_store_and_load"); let keys_path = tmp_path.join("keys"); @@ -655,11 +649,4 @@ mod test { .get(&ModuleId("TEST_MODULE".into())) .is_some_and(|keys| keys.contains(&proxy_signer.pubkey()))); } - - #[tokio::test] - async fn test_erc2335_store() { - test_erc2335_storage_format().await; - test_erc2335_load(); - test_erc2335_store_and_load().await; - } } From f3ce1ac2854691398987df3b50ebfc2482d1b80e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20I=C3=B1aki=20Bilbao?= Date: Thu, 5 Dec 2024 14:17:32 -0300 Subject: [PATCH 14/18] Read paths from config if env var is not present --- crates/common/src/signer/loader.rs | 6 ++++-- crates/common/src/signer/store.rs | 20 +++++++++++++++----- docs/docs/get_started/running/binary.md | 1 + 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/crates/common/src/signer/loader.rs b/crates/common/src/signer/loader.rs index 06fd1c3d..1ed0b068 100644 --- a/crates/common/src/signer/loader.rs +++ b/crates/common/src/signer/loader.rs @@ -56,8 +56,10 @@ impl SignerLoader { pub fn load_from_env(self) -> eyre::Result> { Ok(match self { - SignerLoader::File { .. } => { - let path = load_env_var(SIGNER_KEYS_ENV)?; + SignerLoader::File { key_path } => { + let path = load_env_var(SIGNER_KEYS_ENV).unwrap_or( + key_path.to_str().ok_or_eyre("Missing signer key path")?.to_string(), + ); let file = std::fs::read_to_string(path) .unwrap_or_else(|_| panic!("Unable to find keys file")); diff --git a/crates/common/src/signer/store.rs b/crates/common/src/signer/store.rs index e2c9c44b..878fdb67 100644 --- a/crates/common/src/signer/store.rs +++ b/crates/common/src/signer/store.rs @@ -19,6 +19,7 @@ use eth2_keystore::{ }, Uuid, IV_SIZE, SALT_SIZE, }; +use eyre::OptionExt; use rand::Rng; use serde::{Deserialize, Serialize}; use tracing::warn; @@ -56,13 +57,22 @@ pub enum ProxyStore { impl ProxyStore { pub fn init_from_env(self) -> eyre::Result { Ok(match self { - ProxyStore::File { .. } => { - let path = load_env_var(PROXY_DIR_ENV)?; + ProxyStore::File { proxy_dir } => { + let path = load_env_var(PROXY_DIR_ENV) + .unwrap_or(proxy_dir.to_str().ok_or_eyre("Missing proxy dir")?.to_string()); ProxyStore::File { proxy_dir: PathBuf::from(path) } } - ProxyStore::ERC2335 { .. } => { - let keys_path = PathBuf::from_str(&load_env_var(PROXY_DIR_KEYS_ENV)?)?; - let secrets_path = PathBuf::from_str(&load_env_var(PROXY_DIR_SECRETS_ENV)?)?; + ProxyStore::ERC2335 { keys_path, secrets_path } => { + let keys_path = if let Ok(path) = load_env_var(PROXY_DIR_KEYS_ENV) { + PathBuf::from_str(&path)? + } else { + keys_path + }; + let secrets_path = if let Ok(path) = load_env_var(PROXY_DIR_SECRETS_ENV) { + PathBuf::from_str(&path)? + } else { + secrets_path + }; ProxyStore::ERC2335 { keys_path, secrets_path } } diff --git a/docs/docs/get_started/running/binary.md b/docs/docs/get_started/running/binary.md index 20f9b8ad..2c93d3f2 100644 --- a/docs/docs/get_started/running/binary.md +++ b/docs/docs/get_started/running/binary.md @@ -32,6 +32,7 @@ For loading keys we currently support: - `CB_SIGNER_LOADER_FORMAT`, `CB_SIGNER_LOADER_KEYS_DIR` and `CB_SIGNER_LOADER_SECRETS_DIR`: paths to the `keys` and `secrets` directories or files (ERC-2335 style keystores, see [Signer config](../configuration/#signer-module) for more info) For storing proxy keys we currently support: - `CB_PROXY_STORE_DIR`: directory where proxy keys and delegations will be saved in plaintext (for testing purposes only) + - `CB_PROXY_KEYS_DIR` and `CB_PROXY_SECRETS_DIR`: paths to the `keys` and `secrets` directories (ERC-2335 style keystores, see [Signer config](../configuration#proxy-keys-store) for more info) ### Modules From 5ba591471430ac3bf9847e848a1bd7b4c9d8573a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20I=C3=B1aki=20Bilbao?= Date: Thu, 5 Dec 2024 15:25:34 -0300 Subject: [PATCH 15/18] Add healthcheck to signer container --- crates/cli/src/docker_init.rs | 24 ++++++++++++++++++++---- crates/common/src/commit/constants.rs | 1 + crates/signer/src/service.rs | 14 ++++++++++++-- docker/signer.Dockerfile | 1 + 4 files changed, 34 insertions(+), 6 deletions(-) diff --git a/crates/cli/src/docker_init.rs b/crates/cli/src/docker_init.rs index 6813d595..87c849b6 100644 --- a/crates/cli/src/docker_init.rs +++ b/crates/cli/src/docker_init.rs @@ -19,9 +19,9 @@ use cb_common::{ utils::random_jwt, }; use docker_compose_types::{ - Compose, ComposeVolume, DependsOnOptions, EnvFile, Environment, Labels, LoggingParameters, - MapOrEmpty, NetworkSettings, Networks, Ports, Service, Services, SingleValue, TopLevelVolumes, - Volumes, + Compose, ComposeVolume, DependsCondition, DependsOnOptions, EnvFile, Environment, Healthcheck, + HealthcheckTest, Labels, LoggingParameters, MapOrEmpty, NetworkSettings, Networks, Ports, + Service, Services, SingleValue, TopLevelVolumes, Volumes, }; use eyre::Result; use indexmap::IndexMap; @@ -148,13 +148,19 @@ pub fn handle_docker_init(config_path: String, output_dir: String) -> Result<()> module_volumes.extend(chain_spec_volume.clone()); module_volumes.extend(get_log_volume(&cb_config.logs, &module.id)); + // depends_on + let mut module_dependencies = IndexMap::new(); + module_dependencies.insert("cb_signer".into(), DependsCondition { + condition: "service_healthy".into(), + }); + Service { container_name: Some(module_cid.clone()), image: Some(module.docker_image), networks: Networks::Simple(module_networks), volumes: module_volumes, environment: Environment::KvPair(module_envs), - depends_on: DependsOnOptions::Simple(vec!["cb_signer".to_owned()]), + depends_on: DependsOnOptions::Conditional(module_dependencies), env_file, ..Service::default() } @@ -394,6 +400,16 @@ pub fn handle_docker_init(config_path: String, output_dir: String) -> Result<()> networks: Networks::Simple(signer_networks), volumes, environment: Environment::KvPair(signer_envs), + healthcheck: Some(Healthcheck { + test: Some(HealthcheckTest::Single( + "curl -f http://localhost:20000/status".into(), + )), + interval: Some("5s".into()), + timeout: Some("5s".into()), + retries: 5, + start_period: Some("0s".into()), + disable: false, + }), ..Service::default() }; diff --git a/crates/common/src/commit/constants.rs b/crates/common/src/commit/constants.rs index 0bcfc174..3335833a 100644 --- a/crates/common/src/commit/constants.rs +++ b/crates/common/src/commit/constants.rs @@ -1,3 +1,4 @@ pub const GET_PUBKEYS_PATH: &str = "/signer/v1/get_pubkeys"; pub const REQUEST_SIGNATURE_PATH: &str = "/signer/v1/request_signature"; pub const GENERATE_PROXY_KEY_PATH: &str = "/signer/v1/generate_proxy_key"; +pub const STATUS_PATH: &str = "/status"; diff --git a/crates/signer/src/service.rs b/crates/signer/src/service.rs index 6e27e590..14703a09 100644 --- a/crates/signer/src/service.rs +++ b/crates/signer/src/service.rs @@ -12,7 +12,9 @@ use axum_extra::TypedHeader; use bimap::BiHashMap; use cb_common::{ commit::{ - constants::{GENERATE_PROXY_KEY_PATH, GET_PUBKEYS_PATH, REQUEST_SIGNATURE_PATH}, + constants::{ + GENERATE_PROXY_KEY_PATH, GET_PUBKEYS_PATH, REQUEST_SIGNATURE_PATH, STATUS_PATH, + }, request::{ EncryptionScheme, GenerateProxyRequest, GetPubkeysResponse, SignConsensusRequest, SignProxyRequest, SignRequest, @@ -77,11 +79,14 @@ impl SigningService { .route(GENERATE_PROXY_KEY_PATH, post(handle_generate_proxy)) .with_state(state.clone()) .route_layer(middleware::from_fn_with_state(state.clone(), jwt_auth)); + let status_router = axum::Router::new().route(STATUS_PATH, get(handle_status)); let address = SocketAddr::from(([0, 0, 0, 0], config.server_port)); let listener = TcpListener::bind(address).await?; - axum::serve(listener, app).await.wrap_err("signer server exited") + axum::serve(listener, axum::Router::new().merge(app).merge(status_router)) + .await + .wrap_err("signer server exited") } } @@ -104,6 +109,11 @@ async fn jwt_auth( Ok(next.run(req).await) } +/// Status endpoint for the Signer API +async fn handle_status() -> Result { + Ok((StatusCode::OK, "OK")) +} + /// Implements get_pubkeys from the Signer API async fn handle_get_pubkeys( Extension(module_id): Extension, diff --git a/docker/signer.Dockerfile b/docker/signer.Dockerfile index d26d679b..3f267806 100644 --- a/docker/signer.Dockerfile +++ b/docker/signer.Dockerfile @@ -22,6 +22,7 @@ RUN apt-get update && apt-get install -y \ ca-certificates \ libssl3 \ libssl-dev \ + curl \ && apt-get clean autoclean \ && rm -rf /var/lib/apt/lists/* From a669ec8915d81970ffdfa63c1edc1375f0c825da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20I=C3=B1aki=20Bilbao?= Date: Thu, 5 Dec 2024 20:50:50 -0300 Subject: [PATCH 16/18] Fix format --- crates/cli/src/docker_init.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/crates/cli/src/docker_init.rs b/crates/cli/src/docker_init.rs index 4a089d01..a186231a 100644 --- a/crates/cli/src/docker_init.rs +++ b/crates/cli/src/docker_init.rs @@ -155,10 +155,9 @@ pub fn handle_docker_init(config_path: String, output_dir: String) -> Result<()> // depends_on let mut module_dependencies = IndexMap::new(); - module_dependencies.insert( - "cb_signer".into(), - DependsCondition { condition: "service_healthy".into() }, - ); + module_dependencies.insert("cb_signer".into(), DependsCondition { + condition: "service_healthy".into(), + }); Service { container_name: Some(module_cid.clone()), From e900b66b19996713465ddf7d50e2d75a5a7165e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20I=C3=B1aki=20Bilbao?= Date: Fri, 6 Dec 2024 10:39:22 -0300 Subject: [PATCH 17/18] Update example config --- config.example.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config.example.toml b/config.example.toml index fa19390d..7b6a4a11 100644 --- a/config.example.toml +++ b/config.example.toml @@ -166,9 +166,9 @@ key_path = "./keys.example.json" # File: path to the keys file proxy_dir = "./proxies" # ERC2335: path to the keys directory -# keys_path = "" +# keys_path = "./tests/data/proxy/keys" # ERC2335: path to the secrets directory -# secrets_path = "" +# secrets_path = "./tests/data/proxy/secrets" # Commit-Boost can optionally run "modules" which extend the capabilities of the sidecar. # Currently, two types of modules are supported: From bda52559371bd4c4bb6b22061cdbe80db81fa448 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20I=C3=B1aki=20Bilbao?= Date: Fri, 6 Dec 2024 10:39:41 -0300 Subject: [PATCH 18/18] Use signer_port var for healthcheck --- crates/cli/src/docker_init.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/cli/src/docker_init.rs b/crates/cli/src/docker_init.rs index a186231a..1f135f66 100644 --- a/crates/cli/src/docker_init.rs +++ b/crates/cli/src/docker_init.rs @@ -410,9 +410,9 @@ pub fn handle_docker_init(config_path: String, output_dir: String) -> Result<()> volumes, environment: Environment::KvPair(signer_envs), healthcheck: Some(Healthcheck { - test: Some(HealthcheckTest::Single( - "curl -f http://localhost:20000/status".into(), - )), + test: Some(HealthcheckTest::Single(format!( + "curl -f http://localhost:{signer_port}/status" + ))), interval: Some("5s".into()), timeout: Some("5s".into()), retries: 5,