Skip to content

Commit

Permalink
Print loading keys message only when validator client options are pre…
Browse files Browse the repository at this point in the history
…sent #21

Also:

- remove Validator::Keystores enum variant as it is no longer being used
- move keystore keypair loading out of Validators::normalize
  • Loading branch information
Tumas committed Apr 8, 2024
1 parent 3e03716 commit cc27cd2
Show file tree
Hide file tree
Showing 6 changed files with 111 additions and 99 deletions.
25 changes: 14 additions & 11 deletions grandine/src/grandine_args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1106,13 +1106,10 @@ impl GrandineArgs {

let validators = keystore_dir
.zip(keystore_password_file.or(keystore_password_dir))
.map(
|(keystore_dir, keystore_password_file)| Validators::KeystoreDirectory {
keystore_dir,
keystore_password_file,
},
)
.unwrap_or_default();
.map(|(keystore_dir, keystore_password_file)| Validators {
keystore_dir,
keystore_password_file,
});

let minimum = StoreConfig::min_unfinalized_states_in_memory(&chain_config);

Expand Down Expand Up @@ -1622,6 +1619,12 @@ mod tests {
);
}

#[test]
fn validators_from_no_keystore_paths() {
let config = config_from_args([]);
assert_eq!(config.validators, None);
}

#[test]
fn validators_from_keystore_password_file() {
let config = config_from_args([
Expand All @@ -1633,10 +1636,10 @@ mod tests {

assert_eq!(
config.validators,
Validators::KeystoreDirectory {
Some(Validators {
keystore_dir: PathBuf::from("dir_value"),
keystore_password_file: PathBuf::from("pass_file"),
},
}),
);
}

Expand All @@ -1651,10 +1654,10 @@ mod tests {

assert_eq!(
config.validators,
Validators::KeystoreDirectory {
Some(Validators {
keystore_dir: PathBuf::from("dir_value"),
keystore_password_file: PathBuf::from("pass_dir"),
},
}),
);
}

Expand Down
2 changes: 1 addition & 1 deletion grandine/src/grandine_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ pub struct GrandineConfig {
pub back_sync: bool,
pub eth1_rpc_urls: Vec<Url>,
pub data_dir: PathBuf,
pub validators: Validators,
pub validators: Option<Validators>,
pub keystore_storage_password_file: Option<PathBuf>,
pub graffiti: Vec<H256>,
pub max_empty_slots: u64,
Expand Down
32 changes: 30 additions & 2 deletions grandine/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use metrics::MetricsServerConfig;
use p2p::{ListenAddr, NetworkConfig};
use reqwest::{Client, ClientBuilder, Url};
use runtime::{MetricsConfig, StorageConfig};
use signer::Signer;
use signer::{KeyOrigin, Signer};
use slasher::SlasherConfig;
use slashing_protection::SlashingProtector;
use ssz::SszRead as _;
Expand Down Expand Up @@ -96,6 +96,7 @@ struct Context {
metrics_config: MetricsConfig,
track_liveness: bool,
slashing_protection_history_limit: u64,
validator_enabled: bool,
}

impl Context {
Expand Down Expand Up @@ -150,6 +151,7 @@ impl Context {
metrics_config,
track_liveness,
slashing_protection_history_limit,
validator_enabled,
} = self;

// Load keys early so we can validate `eth1_rpc_urls`.
Expand Down Expand Up @@ -269,6 +271,7 @@ impl Context {
eth1_api_to_metrics_tx,
eth1_api_to_metrics_rx,
slashing_protection_history_limit,
validator_enabled,
)
.await
}
Expand Down Expand Up @@ -428,8 +431,32 @@ fn try_main() -> Result<()> {
None => ValidatorKeyCache::default(),
};

let validator_enabled = validator_api_config.is_some()
|| cache.is_some()
|| !web3signer_config.is_empty()
|| validators.is_some()
|| validator_config.keystore_storage_password_file.is_some();

if validator_enabled {
info!("started loading validator keys");
}

let mut validator_keys = validators
.map(|validators| {
validators
.normalize(cache.as_mut())
.expect("unable to load local validator keys")
})
.unwrap_or_default();

validator_keys.extend(
keystore_storage
.keypairs()
.map(|(public_key, secret_key)| (public_key, secret_key, KeyOrigin::KeymanagerAPI)),
);

let signer = Arc::new(Signer::new(
validators.normalize(cache.as_mut(), &keystore_storage)?,
validator_keys,
client,
web3signer_config,
metrics.clone(),
Expand Down Expand Up @@ -470,6 +497,7 @@ fn try_main() -> Result<()> {
metrics_config,
track_liveness,
slashing_protection_history_limit,
validator_enabled,
};

match context.chain_config.preset_base {
Expand Down
141 changes: 57 additions & 84 deletions grandine/src/validators.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
use std::{
collections::HashMap,
path::{Path, PathBuf},
sync::Arc,
};
use std::{collections::HashMap, path::PathBuf, sync::Arc};

use anyhow::{Error, Result};
use bls::{PublicKeyBytes, SecretKey};
Expand All @@ -19,15 +15,9 @@ use zeroize::Zeroizing;
#[derive(Educe)]
#[educe(Default)]
#[cfg_attr(test, derive(PartialEq, Eq, Debug))]
pub enum Validators {
#[educe(Default)]
Keystores {
keystore_and_password_paths: HashMap<PathBuf, PathBuf>,
},
KeystoreDirectory {
keystore_dir: PathBuf,
keystore_password_file: PathBuf,
},
pub struct Validators {
pub keystore_dir: PathBuf,
pub keystore_password_file: PathBuf,
}

enum KeystoreExtension {
Expand All @@ -36,12 +26,9 @@ enum KeystoreExtension {
}

impl Validators {
fn keymap_from_paths(
keystore_dir: impl AsRef<Path>,
keystore_password_file: impl AsRef<Path>,
) -> Result<HashMap<PathBuf, PathBuf>> {
let keystore_dir = keystore_dir.as_ref();
let keystore_password_file = keystore_password_file.as_ref();
fn keymap_from_paths(&self) -> Result<HashMap<PathBuf, PathBuf>> {
let keystore_dir = self.keystore_dir.as_path();
let keystore_password_file = self.keystore_password_file.as_path();
let individual_passwords = keystore_password_file.is_dir();
let keystore_glob = "*";

Expand Down Expand Up @@ -92,31 +79,21 @@ impl Validators {
pub fn normalize(
self,
mut validator_key_cache: Option<&mut ValidatorKeyCache>,
keystore_storage: &ValidatorKeyCache,
) -> Result<Vec<(PublicKeyBytes, Arc<SecretKey>, KeyOrigin)>> {
info!("started loading keystore and password files");

// Collect all passwords and keystores first.
// They may be used to load secret keys from the cache.
// Secret keys are decrypted later.
let keystores_with_passwords = match self {
Self::Keystores {
keystore_and_password_paths,
} => keystore_and_password_paths,
Self::KeystoreDirectory {
keystore_dir,
keystore_password_file,
} => Self::keymap_from_paths(keystore_dir, keystore_password_file)?,
}
.into_par_iter()
.map(|(keystore_path, password_path)| {
let password = Zeroizing::new(fs_err::read(password_path)?);
let normalized_password = eip_2335::normalize_password(password.as_slice())?;
let keystore_bytes = Zeroizing::new(fs_err::read(keystore_path)?);
let keystore = serde_json::from_slice::<Keystore>(keystore_bytes.as_slice())?;
Ok((keystore, normalized_password))
})
.collect::<Result<Vec<_>>>()?;
let keystores_with_passwords = self
.keymap_from_paths()?
.into_par_iter()
.map(|(keystore_path, password_path)| {
let password = Zeroizing::new(fs_err::read(password_path)?);
let normalized_password = eip_2335::normalize_password(password.as_slice())?;
let keystore_bytes = Zeroizing::new(fs_err::read(keystore_path)?);
let keystore = serde_json::from_slice::<Keystore>(keystore_bytes.as_slice())?;
Ok((keystore, normalized_password))
})
.collect::<Result<Vec<_>>>()?;

// Collect all passwords for decrypting the cache.
let passwords = keystores_with_passwords
Expand All @@ -133,49 +110,45 @@ impl Validators {
}
}

let keypairs =
keystores_with_passwords
.into_par_iter()
.map(|(keystore, normalized_password)| {
let uuid = keystore.uuid();

let keypair = validator_key_cache
.as_ref()
.and_then(|cache| cache.get(uuid))
.map(Ok::<_, Error>)
.unwrap_or_else(|| {
let secret_key = keystore
.decrypt(normalized_password.as_str())?
.try_conv::<SecretKey>()?
.pipe(Arc::new);

let public_key = secret_key.to_public_key().into();

info!("decrypted validator key {public_key:?}");

Ok((public_key, secret_key))
})?;

Ok((uuid, normalized_password, keypair))
})
.collect::<Result<Vec<_>>>()?
.into_iter()
.map(|(uuid, normalized_password, (public_key, secret_key))| {
if let Some(cache) = validator_key_cache.as_mut() {
cache.add_with_password(
normalized_password,
uuid,
public_key,
secret_key.clone_arc(),
);
}

(public_key, secret_key, KeyOrigin::LocalFileSystem)
})
.chain(keystore_storage.keypairs().map(|(public_key, secret_key)| {
(public_key, secret_key, KeyOrigin::KeymanagerAPI)
}))
.collect();
let keypairs = keystores_with_passwords
.into_par_iter()
.map(|(keystore, normalized_password)| {
let uuid = keystore.uuid();

let keypair = validator_key_cache
.as_ref()
.and_then(|cache| cache.get(uuid))
.map(Ok::<_, Error>)
.unwrap_or_else(|| {
let secret_key = keystore
.decrypt(normalized_password.as_str())?
.try_conv::<SecretKey>()?
.pipe(Arc::new);

let public_key = secret_key.to_public_key().into();

info!("decrypted validator key {public_key:?}");

Ok((public_key, secret_key))
})?;

Ok((uuid, normalized_password, keypair))
})
.collect::<Result<Vec<_>>>()?
.into_iter()
.map(|(uuid, normalized_password, (public_key, secret_key))| {
if let Some(cache) = validator_key_cache.as_mut() {
cache.add_with_password(
normalized_password,
uuid,
public_key,
secret_key.clone_arc(),
);
}

(public_key, secret_key, KeyOrigin::LocalFileSystem)
})
.collect();

Ok(keypairs)
}
Expand Down
3 changes: 2 additions & 1 deletion runtime/src/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ pub async fn run_after_genesis<P: Preset>(
eth1_api_to_metrics_tx: Option<UnboundedSender<Eth1ApiToMetrics>>,
eth1_api_to_metrics_rx: Option<UnboundedReceiver<Eth1ApiToMetrics>>,
slashing_protection_history_limit: u64,
validator_enabled: bool,
) -> Result<()> {
let MetricsConfig {
metrics,
Expand All @@ -89,7 +90,7 @@ pub async fn run_after_genesis<P: Preset>(

if !signer_snapshot.is_empty() {
info!("loaded {} validator key(s)", signer_snapshot.keys().len());
} else if validator_config.keystore_storage_password_file.is_some() {
} else if validator_enabled {
warn!("failed to load validator keys");
}

Expand Down
7 changes: 7 additions & 0 deletions signer/src/web3signer/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@ pub struct Config {
pub urls: Vec<Url>,
}

impl Config {
#[must_use]
pub fn is_empty(&self) -> bool {
self.public_keys.is_empty() || self.urls.is_empty()
}
}

#[derive(Clone)]
pub struct Web3Signer {
client: Client,
Expand Down

0 comments on commit cc27cd2

Please sign in to comment.