From 62d64284a08cf22d039a6fce7e48d00b91c83d5a Mon Sep 17 00:00:00 2001 From: ltitanb <163874448+ltitanb@users.noreply.github.com> Date: Sat, 2 Nov 2024 08:19:20 +0000 Subject: [PATCH] feat(signer): store proxy keys and delegations (#166) * add proxy store * refactor exports * config and cli init * docs * clippy --- config.example.toml | 6 + crates/cli/src/docker_init.rs | 37 ++++- crates/common/src/commit/client.rs | 5 +- crates/common/src/commit/request.rs | 2 +- crates/common/src/config/constants.rs | 5 +- crates/common/src/config/signer.rs | 10 +- crates/common/src/lib.rs | 1 - crates/common/src/signature.rs | 2 +- crates/common/src/{ => signer}/loader.rs | 8 +- crates/common/src/signer/mod.rs | 13 +- crates/common/src/signer/schemes/bls.rs | 6 + crates/common/src/signer/schemes/ecdsa.rs | 6 + crates/common/src/signer/schemes/mod.rs | 7 +- crates/common/src/signer/store.rs | 188 ++++++++++++++++++++++ crates/common/src/signer/types.rs | 35 ++++ crates/signer/src/manager.rs | 94 ++++++----- crates/signer/src/service.rs | 22 ++- docs/docs/get_started/running/binary.md | 2 + tests/tests/pbs_integration.rs | 2 +- 19 files changed, 370 insertions(+), 81 deletions(-) rename crates/common/src/{ => signer}/loader.rs (97%) create mode 100644 crates/common/src/signer/store.rs create mode 100644 crates/common/src/signer/types.rs diff --git a/config.example.toml b/config.example.toml index 012b6971..71c5d964 100644 --- a/config.example.toml +++ b/config.example.toml @@ -102,6 +102,12 @@ 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: +# - File: store keys and delegations from a plain text file (unsafe, use only for testing purposes) +# OPTIONAL, if missing proxies are lost on restart +[signer.store] +# File: path to the keys file +proxy_dir = "./proxies" # Commit-Boost can optionally run "modules" which extend the capabilities of the sidecar. # Currently, two types of modules are supported: diff --git a/crates/cli/src/docker_init.rs b/crates/cli/src/docker_init.rs index 4b6c1e47..e394b5cd 100644 --- a/crates/cli/src/docker_init.rs +++ b/crates/cli/src/docker_init.rs @@ -4,11 +4,12 @@ use cb_common::{ config::{ 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_MODULE_NAME, SIGNER_DEFAULT, - SIGNER_DIR_KEYS_DEFAULT, SIGNER_DIR_KEYS_ENV, SIGNER_DIR_SECRETS, SIGNER_DIR_SECRETS_ENV, - SIGNER_KEYS_ENV, SIGNER_MODULE_NAME, SIGNER_PORT_ENV, SIGNER_URL_ENV, + METRICS_PORT_ENV, MODULE_ID_ENV, MODULE_JWT_ENV, PBS_MODULE_NAME, PROXY_DIR_DEFAULT, + PROXY_DIR_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, }, - loader::SignerLoader, + signer::{ProxyStore, SignerLoader}, types::ModuleId, utils::random_jwt, }; @@ -299,27 +300,47 @@ pub fn handle_docker_init(config_path: String, output_dir: String) -> Result<()> match signer_config.loader { SignerLoader::File { key_path } => { - volumes.push(Volumes::Simple(format!("./{}:{}:ro", key_path, SIGNER_DEFAULT))); + volumes.push(Volumes::Simple(format!( + "{}:{}:ro", + key_path.display(), + SIGNER_DEFAULT + ))); let (k, v) = get_env_val(SIGNER_KEYS_ENV, SIGNER_DEFAULT); signer_envs.insert(k, v); } SignerLoader::ValidatorsDir { keys_path, secrets_path } => { volumes.push(Volumes::Simple(format!( "{}:{}:ro", - keys_path, SIGNER_DIR_KEYS_DEFAULT + keys_path.display(), + SIGNER_DIR_KEYS_DEFAULT ))); let (k, v) = get_env_val(SIGNER_DIR_KEYS_ENV, SIGNER_DIR_KEYS_DEFAULT); signer_envs.insert(k, v); volumes.push(Volumes::Simple(format!( "{}:{}:ro", - secrets_path, SIGNER_DIR_SECRETS + secrets_path.display(), + SIGNER_DIR_SECRETS_DEFAULT ))); - let (k, v) = get_env_val(SIGNER_DIR_SECRETS_ENV, SIGNER_DIR_SECRETS); + let (k, v) = get_env_val(SIGNER_DIR_SECRETS_ENV, SIGNER_DIR_SECRETS_DEFAULT); signer_envs.insert(k, v); } }; + if let Some(store) = signer_config.store { + match store { + ProxyStore::File { proxy_dir } => { + volumes.push(Volumes::Simple(format!( + "{}:{}:rw", + proxy_dir.display(), + PROXY_DIR_DEFAULT + ))); + let (k, v) = get_env_val(PROXY_DIR_ENV, PROXY_DIR_DEFAULT); + signer_envs.insert(k, v); + } + } + } + volumes.extend(get_log_volume(&cb_config.logs, SIGNER_MODULE_NAME)); // networks diff --git a/crates/common/src/commit/client.rs b/crates/common/src/commit/client.rs index 6f8cb753..32e8acea 100644 --- a/crates/common/src/commit/client.rs +++ b/crates/common/src/commit/client.rs @@ -15,10 +15,7 @@ use super::{ }, }; use crate::{ - signer::{ - schemes::{bls::BlsPublicKey, ecdsa::EcdsaSignature}, - EcdsaPublicKey, - }, + signer::{BlsPublicKey, EcdsaPublicKey, EcdsaSignature}, DEFAULT_REQUEST_TIMEOUT, }; diff --git a/crates/common/src/commit/request.rs b/crates/common/src/commit/request.rs index 1b0fd491..f2adeba2 100644 --- a/crates/common/src/commit/request.rs +++ b/crates/common/src/commit/request.rs @@ -10,7 +10,7 @@ use crate::{ constants::COMMIT_BOOST_DOMAIN, error::BlstErrorWrapper, signature::verify_signed_message, - signer::schemes::{bls::BlsPublicKey, ecdsa::EcdsaPublicKey}, + signer::{BlsPublicKey, EcdsaPublicKey}, types::Chain, }; diff --git a/crates/common/src/config/constants.rs b/crates/common/src/config/constants.rs index a51b3bb8..e559b569 100644 --- a/crates/common/src/config/constants.rs +++ b/crates/common/src/config/constants.rs @@ -41,7 +41,10 @@ pub const SIGNER_DIR_KEYS_ENV: &str = "CB_SIGNER_LOADER_KEYS_DIR"; 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: &str = "/secrets"; +pub const SIGNER_DIR_SECRETS_DEFAULT: &str = "/secrets"; +/// Path to store proxies +pub const PROXY_DIR_ENV: &str = "CB_PROXY_STORE_DIR"; +pub const PROXY_DIR_DEFAULT: &str = "/proxies"; ///////////////////////// MODULES ///////////////////////// diff --git a/crates/common/src/config/signer.rs b/crates/common/src/config/signer.rs index 99f31db5..ab630d5d 100644 --- a/crates/common/src/config/signer.rs +++ b/crates/common/src/config/signer.rs @@ -8,7 +8,7 @@ use super::{ CommitBoostConfig, SIGNER_PORT_ENV, }; use crate::{ - loader::SignerLoader, + signer::{ProxyStore, SignerLoader}, types::{Chain, Jwt, ModuleId}, }; @@ -19,6 +19,8 @@ pub struct SignerConfig { pub docker_image: String, /// Which keys to load pub loader: SignerLoader, + /// How to store keys + pub store: Option, } fn default_signer() -> String { @@ -29,6 +31,7 @@ fn default_signer() -> String { pub struct StartSignerConfig { pub chain: Chain, pub loader: SignerLoader, + pub store: Option, pub server_port: u16, pub jwts: BiHashMap, } @@ -40,11 +43,14 @@ impl StartSignerConfig { let jwts = load_jwts()?; let server_port = load_env_var(SIGNER_PORT_ENV)?.parse()?; + let signer_config = config.signer.expect("Signer config is missing"); + Ok(StartSignerConfig { chain: config.chain, - loader: config.signer.expect("Signer config is missing").loader, + loader: signer_config.loader, server_port, jwts, + store: signer_config.store, }) } } diff --git a/crates/common/src/lib.rs b/crates/common/src/lib.rs index 33a44ab4..5042061b 100644 --- a/crates/common/src/lib.rs +++ b/crates/common/src/lib.rs @@ -4,7 +4,6 @@ pub mod commit; pub mod config; pub mod constants; pub mod error; -pub mod loader; pub mod pbs; pub mod signature; pub mod signer; diff --git a/crates/common/src/signature.rs b/crates/common/src/signature.rs index a97b8f12..1c2dd1eb 100644 --- a/crates/common/src/signature.rs +++ b/crates/common/src/signature.rs @@ -5,7 +5,7 @@ use tree_hash_derive::TreeHash; use crate::{ constants::{COMMIT_BOOST_DOMAIN, GENESIS_VALIDATORS_ROOT}, error::BlstErrorWrapper, - signer::{schemes::bls::verify_bls_signature, BlsSecretKey}, + signer::{verify_bls_signature, BlsSecretKey}, types::Chain, }; diff --git a/crates/common/src/loader.rs b/crates/common/src/signer/loader.rs similarity index 97% rename from crates/common/src/loader.rs rename to crates/common/src/signer/loader.rs index 10a362fe..47fac883 100644 --- a/crates/common/src/loader.rs +++ b/crates/common/src/signer/loader.rs @@ -1,4 +1,4 @@ -use std::fs; +use std::{fs, path::PathBuf}; use alloy::{primitives::hex::FromHex, rpc::types::beacon::BlsPublicKey}; use eth2_keystore::Keystore; @@ -15,11 +15,11 @@ use crate::{ pub enum SignerLoader { /// Plain text, do not use in prod File { - key_path: String, + key_path: PathBuf, }, ValidatorsDir { - keys_path: String, - secrets_path: String, + keys_path: PathBuf, + secrets_path: PathBuf, }, } diff --git a/crates/common/src/signer/mod.rs b/crates/common/src/signer/mod.rs index 3622e1c1..e0a164a7 100644 --- a/crates/common/src/signer/mod.rs +++ b/crates/common/src/signer/mod.rs @@ -1,8 +1,11 @@ -pub mod schemes; +mod loader; +mod schemes; +mod store; +mod types; -pub use schemes::{ - bls::{BlsPublicKey, BlsSecretKey, BlsSignature, BlsSigner}, - ecdsa::{EcdsaPublicKey, EcdsaSecretKey, EcdsaSignature, EcdsaSigner}, -}; +pub use loader::*; +pub use schemes::*; +pub use store::*; +pub use types::*; pub type ConsensusSigner = BlsSigner; diff --git a/crates/common/src/signer/schemes/bls.rs b/crates/common/src/signer/schemes/bls.rs index 4f901a1f..82d0c01a 100644 --- a/crates/common/src/signer/schemes/bls.rs +++ b/crates/common/src/signer/schemes/bls.rs @@ -57,6 +57,12 @@ impl BlsSigner { } } + pub fn secret(&self) -> [u8; 32] { + match self { + BlsSigner::Local(secret) => secret.clone().to_bytes(), + } + } + pub async fn sign(&self, chain: Chain, object_root: [u8; 32]) -> BlsSignature { match self { BlsSigner::Local(sk) => sign_commit_boost_root(chain, sk, object_root), diff --git a/crates/common/src/signer/schemes/ecdsa.rs b/crates/common/src/signer/schemes/ecdsa.rs index e813a7a7..3fb72918 100644 --- a/crates/common/src/signer/schemes/ecdsa.rs +++ b/crates/common/src/signer/schemes/ecdsa.rs @@ -162,6 +162,12 @@ impl EcdsaSigner { } } + pub fn secret(&self) -> Vec { + match self { + EcdsaSigner::Local(secret) => secret.to_bytes().to_vec(), + } + } + pub async fn sign(&self, chain: Chain, object_root: [u8; 32]) -> EcdsaSignature { match self { EcdsaSigner::Local(sk) => { diff --git a/crates/common/src/signer/schemes/mod.rs b/crates/common/src/signer/schemes/mod.rs index 91cccfdc..fbdb354d 100644 --- a/crates/common/src/signer/schemes/mod.rs +++ b/crates/common/src/signer/schemes/mod.rs @@ -1,2 +1,5 @@ -pub mod bls; -pub mod ecdsa; +mod bls; +mod ecdsa; + +pub use bls::*; +pub use ecdsa::*; diff --git a/crates/common/src/signer/store.rs b/crates/common/src/signer/store.rs new file mode 100644 index 00000000..5a6e9303 --- /dev/null +++ b/crates/common/src/signer/store.rs @@ -0,0 +1,188 @@ +use std::{ + collections::HashMap, + fs::{create_dir_all, read_to_string}, + io::Write, + path::PathBuf, +}; + +use alloy::primitives::Bytes; +use serde::{Deserialize, Serialize}; + +use crate::{ + commit::request::{PublicKey, SignedProxyDelegation}, + config::{load_env_var, PROXY_DIR_ENV}, + signer::{ + BlsProxySigner, BlsPublicKey, BlsSigner, EcdsaProxySigner, EcdsaPublicKey, EcdsaSigner, + ProxySigners, + }, + types::ModuleId, +}; + +#[derive(Debug, Serialize, Deserialize)] +struct KeyAndDelegation { + secret: Bytes, + delegation: SignedProxyDelegation, +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +#[serde(untagged)] +pub enum ProxyStore { + /// Stores private keys in plaintext to a file, do not use in prod + File { proxy_dir: PathBuf }, +} + +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: PathBuf::from(path) } + } + }) + } + + pub fn store_proxy_bls( + &self, + module_id: &ModuleId, + proxy: &BlsProxySigner, + ) -> eyre::Result<()> { + match self { + ProxyStore::File { proxy_dir } => { + let file_path = proxy_dir + .join(module_id.to_string()) + .join("bls") + .join(proxy.signer.pubkey().to_string()); + let secret = Bytes::from(proxy.signer.secret()); + let to_store = KeyAndDelegation { secret, delegation: proxy.delegation }; + let content = serde_json::to_vec(&to_store)?; + + if let Some(parent) = file_path.parent() { + create_dir_all(parent)?; + } + + let mut file = std::fs::File::create(file_path)?; + file.write_all(content.as_ref())?; + } + } + + Ok(()) + } + + pub fn store_proxy_ecdsa( + &self, + module_id: &ModuleId, + proxy: &EcdsaProxySigner, + ) -> eyre::Result<()> { + match self { + ProxyStore::File { proxy_dir } => { + let file_path = proxy_dir + .join(module_id.to_string()) + .join("ecdsa") + .join(proxy.signer.pubkey().to_string()); + let secret = Bytes::from(proxy.signer.secret()); + let to_store = KeyAndDelegation { secret, delegation: proxy.delegation }; + let content = serde_json::to_vec(&to_store)?; + + if let Some(parent) = file_path.parent() { + create_dir_all(parent)?; + } + + let mut file = std::fs::File::create(file_path)?; + file.write_all(content.as_ref())?; + } + } + + Ok(()) + } + + #[allow(clippy::type_complexity)] + pub fn load_proxies( + &self, + ) -> eyre::Result<( + ProxySigners, + HashMap>, + HashMap>, + )> { + match self { + ProxyStore::File { proxy_dir } => { + // HashMaps to store module_id -> content mappings + let mut proxy_signers = ProxySigners::default(); + let mut bls_map: HashMap> = HashMap::new(); + let mut ecdsa_map: HashMap> = HashMap::new(); + + // Iterate over the entries in the base directory + for entry in std::fs::read_dir(proxy_dir)? { + let entry = entry?; + let module_path = entry.path(); + + // Ensure that the entry is a directory + if module_path.is_dir() { + if let Some(module_id) = + module_path.file_name().and_then(|name| name.to_str()) + { + let module_id = ModuleId(module_id.to_string()); + + // Paths to "bls" and "ecdsa" directories + let bls_path = module_path.join("bls"); + let ecdsa_path = module_path.join("ecdsa"); + + // Read "bls" directory files + if bls_path.is_dir() { + for entry in std::fs::read_dir(bls_path)? { + let entry = entry?; + let path = entry.path(); + + if path.is_file() { + let file_content = read_to_string(&path)?; + let key_and_delegation: KeyAndDelegation = + serde_json::from_str(&file_content)?; + let signer = + BlsSigner::new_from_bytes(&key_and_delegation.secret)?; + let pubkey = signer.pubkey(); + let proxy_signer = BlsProxySigner { + signer, + delegation: key_and_delegation.delegation, + }; + + proxy_signers.bls_signers.insert(pubkey, proxy_signer); + bls_map.entry(module_id.clone()).or_default().push(pubkey); + } + } + } + + // Read "ecdsa" directory files + if ecdsa_path.is_dir() { + for entry in std::fs::read_dir(ecdsa_path)? { + let entry = entry?; + let path = entry.path(); + + if path.is_file() { + let file_content = read_to_string(&path)?; + let key_and_delegation: KeyAndDelegation = + serde_json::from_str(&file_content)?; + let signer = EcdsaSigner::new_from_bytes( + &key_and_delegation.secret, + )?; + let pubkey = signer.pubkey(); + let proxy_signer = EcdsaProxySigner { + signer, + delegation: key_and_delegation.delegation, + }; + + proxy_signers.ecdsa_signers.insert(pubkey, proxy_signer); + ecdsa_map + .entry(module_id.clone()) + .or_default() + .push(pubkey); + } + } + } + } + } + } + + Ok((proxy_signers, bls_map, ecdsa_map)) + } + } + } +} diff --git a/crates/common/src/signer/types.rs b/crates/common/src/signer/types.rs new file mode 100644 index 00000000..bb1bfc9e --- /dev/null +++ b/crates/common/src/signer/types.rs @@ -0,0 +1,35 @@ +use std::collections::HashMap; + +use derive_more::derive::Deref; + +use super::{BlsPublicKey, EcdsaPublicKey, EcdsaSigner}; +use crate::{ + commit::request::{SignedProxyDelegationBls, SignedProxyDelegationEcdsa}, + signer::BlsSigner, +}; + +// For extra safety and to avoid risking signing malicious messages, use a proxy +// setup: proposer creates a new ephemeral keypair which will be used to sign +// commit messages, it also signs a ProxyDelegation associating the new keypair +// with its consensus pubkey When a new commit module starts, pass the +// ProxyDelegation msg and then sign all future commit messages with the proxy +// key for slashing the faulty message + proxy delegation can be used +#[derive(Clone, Deref)] +pub struct BlsProxySigner { + #[deref] + pub signer: BlsSigner, + pub delegation: SignedProxyDelegationBls, +} + +#[derive(Clone, Deref)] +pub struct EcdsaProxySigner { + #[deref] + pub signer: EcdsaSigner, + pub delegation: SignedProxyDelegationEcdsa, +} + +#[derive(Default)] +pub struct ProxySigners { + pub bls_signers: HashMap, + pub ecdsa_signers: HashMap, +} diff --git a/crates/signer/src/manager.rs b/crates/signer/src/manager.rs index 76430cc2..4bf0bfab 100644 --- a/crates/signer/src/manager.rs +++ b/crates/signer/src/manager.rs @@ -7,48 +7,19 @@ use cb_common::{ SignedProxyDelegationEcdsa, }, signer::{ - schemes::{ - bls::BlsPublicKey, - ecdsa::{EcdsaPublicKey, EcdsaSignature}, - }, - BlsSigner, ConsensusSigner, EcdsaSigner, + BlsProxySigner, BlsPublicKey, BlsSigner, ConsensusSigner, EcdsaProxySigner, EcdsaPublicKey, + EcdsaSignature, EcdsaSigner, ProxySigners, ProxyStore, }, types::{Chain, ModuleId}, }; -use derive_more::derive::Deref; use eyre::OptionExt; use tree_hash::TreeHash; use crate::error::SignerModuleError; -// For extra safety and to avoid risking signing malicious messages, use a proxy -// setup: proposer creates a new ephemeral keypair which will be used to sign -// commit messages, it also signs a ProxyDelegation associating the new keypair -// with its consensus pubkey When a new commit module starts, pass the -// ProxyDelegation msg and then sign all future commit messages with the proxy -// key for slashing the faulty message + proxy delegation can be used -#[derive(Clone, Deref)] -pub struct BlsProxySigner { - #[deref] - signer: BlsSigner, - delegation: SignedProxyDelegationBls, -} - -#[derive(Clone, Deref)] -pub struct EcdsaProxySigner { - #[deref] - signer: EcdsaSigner, - delegation: SignedProxyDelegationEcdsa, -} - -#[derive(Default)] -struct ProxySigners { - bls_signers: HashMap, - ecdsa_signers: HashMap, -} - pub struct SigningManager { chain: Chain, + proxy_store: Option, consensus_signers: HashMap, proxy_signers: ProxySigners, /// Map of module ids to their associated proxy pubkeys. @@ -59,30 +30,60 @@ pub struct SigningManager { } impl SigningManager { - pub fn new(chain: Chain) -> Self { - Self { + pub fn new(chain: Chain, proxy_store: Option) -> eyre::Result { + let mut manager = Self { chain, + proxy_store, consensus_signers: Default::default(), proxy_signers: Default::default(), proxy_pubkeys_bls: Default::default(), proxy_pubkeys_ecdsa: Default::default(), + }; + + if let Some(store) = &manager.proxy_store { + let (proxies, bls, ecdsa) = store.load_proxies()?; + manager.proxy_signers = proxies; + manager.proxy_pubkeys_bls = bls; + manager.proxy_pubkeys_ecdsa = ecdsa; } + + Ok(manager) } pub fn add_consensus_signer(&mut self, signer: ConsensusSigner) { self.consensus_signers.insert(signer.pubkey(), signer); } - pub fn add_proxy_signer_bls(&mut self, proxy: BlsProxySigner, module_id: ModuleId) { + pub fn add_proxy_signer_bls( + &mut self, + proxy: BlsProxySigner, + module_id: ModuleId, + ) -> eyre::Result<()> { + if let Some(store) = &self.proxy_store { + store.store_proxy_bls(&module_id, &proxy)?; + } + let proxy_pubkey = proxy.pubkey(); self.proxy_signers.bls_signers.insert(proxy.pubkey(), proxy); - self.proxy_pubkeys_bls.entry(module_id).or_default().push(proxy_pubkey) + self.proxy_pubkeys_bls.entry(module_id).or_default().push(proxy_pubkey); + + Ok(()) } - pub fn add_proxy_signer_ecdsa(&mut self, proxy: EcdsaProxySigner, module_id: ModuleId) { + pub fn add_proxy_signer_ecdsa( + &mut self, + proxy: EcdsaProxySigner, + module_id: ModuleId, + ) -> eyre::Result<()> { + if let Some(store) = &self.proxy_store { + store.store_proxy_ecdsa(&module_id, &proxy)?; + } + let proxy_pubkey = proxy.pubkey(); self.proxy_signers.ecdsa_signers.insert(proxy.pubkey(), proxy); - self.proxy_pubkeys_ecdsa.entry(module_id).or_default().push(proxy_pubkey) + self.proxy_pubkeys_ecdsa.entry(module_id).or_default().push(proxy_pubkey); + + Ok(()) } pub async fn create_proxy_bls( @@ -98,7 +99,8 @@ impl SigningManager { let delegation = SignedProxyDelegationBls { signature, message }; let proxy_signer = BlsProxySigner { signer, delegation }; - self.add_proxy_signer_bls(proxy_signer, module_id); + self.add_proxy_signer_bls(proxy_signer, module_id) + .map_err(|err| SignerModuleError::Internal(err.to_string()))?; Ok(delegation) } @@ -116,7 +118,8 @@ impl SigningManager { let delegation = SignedProxyDelegationEcdsa { signature, message }; let proxy_signer = EcdsaProxySigner { signer, delegation }; - self.add_proxy_signer_ecdsa(proxy_signer, module_id); + self.add_proxy_signer_ecdsa(proxy_signer, module_id) + .map_err(|err| SignerModuleError::Internal(err.to_string()))?; Ok(delegation) } @@ -253,6 +256,10 @@ impl SigningManager { Ok(keys) } + + pub fn proxies(&self) -> &ProxySigners { + &self.proxy_signers + } } #[cfg(test)] @@ -270,7 +277,7 @@ mod tests { } fn init_signing_manager() -> (SigningManager, BlsPublicKey) { - let mut signing_manager = SigningManager::new(CHAIN); + let mut signing_manager = SigningManager::new(CHAIN, None).unwrap(); let consensus_signer = ConsensusSigner::new_random(); let consensus_pk = consensus_signer.pubkey(); @@ -282,8 +289,7 @@ mod tests { mod test_proxy_bls { use cb_common::{ - constants::COMMIT_BOOST_DOMAIN, signature::compute_domain, - signer::schemes::bls::verify_bls_signature, + constants::COMMIT_BOOST_DOMAIN, signature::compute_domain, signer::verify_bls_signature, }; use super::*; @@ -361,7 +367,7 @@ mod tests { mod test_proxy_ecdsa { use cb_common::{ constants::COMMIT_BOOST_DOMAIN, signature::compute_domain, - signer::schemes::ecdsa::verify_ecdsa_signature, + signer::verify_ecdsa_signature, }; use super::*; diff --git a/crates/signer/src/service.rs b/crates/signer/src/service.rs index be5ad6c4..153e75b7 100644 --- a/crates/signer/src/service.rs +++ b/crates/signer/src/service.rs @@ -47,19 +47,27 @@ impl SigningService { if config.jwts.is_empty() { warn!("Signing service was started but no module is registered. Exiting"); return Ok(()); - } else { - let module_ids: Vec = - config.jwts.left_values().cloned().map(Into::into).collect(); - - info!(version = COMMIT_BOOST_VERSION, modules =? module_ids, port =? config.server_port, "Starting signing service"); } - let mut manager = SigningManager::new(config.chain); + let proxy_store = if let Some(store) = config.store { + Some(store.init_from_env()?) + } else { + warn!("Proxy store not configured. Proxies keys and delegations will not be persisted"); + None + }; + + let mut manager = SigningManager::new(config.chain, proxy_store)?; - // TODO: load proxy keys, or pass already loaded? for signer in config.loader.load_keys()? { manager.add_consensus_signer(signer); } + let module_ids: Vec = config.jwts.left_values().cloned().map(Into::into).collect(); + + let loaded_consensus = manager.consensus_pubkeys().len(); + let proxies = manager.proxies(); + let loaded_proxies = proxies.bls_signers.len() + proxies.ecdsa_signers.len(); + + info!(version = COMMIT_BOOST_VERSION, modules =? module_ids, port =? config.server_port, loaded_consensus, loaded_proxies, "Starting signing service"); let state = SigningState { manager: RwLock::new(manager).into(), jwts: config.jwts.into() }; diff --git a/docs/docs/get_started/running/binary.md b/docs/docs/get_started/running/binary.md index b86c8ffa..0f9339fc 100644 --- a/docs/docs/get_started/running/binary.md +++ b/docs/docs/get_started/running/binary.md @@ -29,6 +29,8 @@ Modules need some environment variables to work correctly. For loading keys we currently support: - `CB_SIGNER_LOADER_FILE`: path to a `.json` with plaintext keys (for testing purposes only) - `CB_SIGNER_LOADER_KEYS_DIR` and `CB_SIGNER_LOADER_SECRETS_DIR`: paths to the `keys` and `secrets` directories (ERC-2335 style keystores as used in Lighthouse) +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) ### Modules diff --git a/tests/tests/pbs_integration.rs b/tests/tests/pbs_integration.rs index 005933fa..b92f7c26 100644 --- a/tests/tests/pbs_integration.rs +++ b/tests/tests/pbs_integration.rs @@ -4,7 +4,7 @@ use alloy::primitives::U256; use cb_common::{ config::{PbsConfig, PbsModuleConfig}, pbs::RelayClient, - signer::{schemes::bls::random_secret, BlsPublicKey}, + signer::{random_secret, BlsPublicKey}, types::Chain, utils::blst_pubkey_to_alloy, };