Merge branch 'unstable' of https://github.com/sigp/lighthouse into single_attestation

This commit is contained in:
Eitan Seri-Levi
2024-12-13 21:51:49 +07:00
35 changed files with 168 additions and 227 deletions

View File

@@ -66,6 +66,9 @@ impl GraffitiFile {
for line in lines {
let line = line.map_err(|e| Error::InvalidLine(e.to_string()))?;
if line.trim().is_empty() {
continue;
}
let (pk_opt, graffiti) = read_line(&line)?;
match pk_opt {
Some(pk) => {
@@ -133,9 +136,15 @@ mod tests {
const CUSTOM_GRAFFITI1: &str = "custom-graffiti1";
const CUSTOM_GRAFFITI2: &str = "graffitiwall:720:641:#ffff00";
const EMPTY_GRAFFITI: &str = "";
// Newline test cases
const CUSTOM_GRAFFITI4: &str = "newlines-tests";
const PK1: &str = "0x800012708dc03f611751aad7a43a082142832b5c1aceed07ff9b543cf836381861352aa923c70eeb02018b638aa306aa";
const PK2: &str = "0x80001866ce324de7d80ec73be15e2d064dcf121adf1b34a0d679f2b9ecbab40ce021e03bb877e1a2fe72eaaf475e6e21";
const PK3: &str = "0x9035d41a8bc11b08c17d0d93d876087958c9d055afe86fce558e3b988d92434769c8d50b0b463708db80c6aae1160c02";
const PK4: &str = "0x8c0fca2cc70f44188a4b79e5623ac85898f1df479e14a1f4ebb615907810b6fb939c3fb4ba2081b7a5b6e33dc73621d2";
const PK5: &str = "0x87998b0ea4a8826f03d1985e5a5ce7235bd3a56fb7559b02a55b737f4ebc69b0bf35444de5cf2680cb7eb2283eb62050";
const PK6: &str = "0xa2af9b128255568e2ee5c42af118cc4301198123d210dbdbf2ca7ec0222f8d491f308e85076b09a2f44a75875cd6fa0f";
// Create a graffiti file in the required format and return a path to the file.
fn create_graffiti_file() -> PathBuf {
@@ -143,6 +152,9 @@ mod tests {
let pk1 = PublicKeyBytes::deserialize(&hex::decode(&PK1[2..]).unwrap()).unwrap();
let pk2 = PublicKeyBytes::deserialize(&hex::decode(&PK2[2..]).unwrap()).unwrap();
let pk3 = PublicKeyBytes::deserialize(&hex::decode(&PK3[2..]).unwrap()).unwrap();
let pk4 = PublicKeyBytes::deserialize(&hex::decode(&PK4[2..]).unwrap()).unwrap();
let pk5 = PublicKeyBytes::deserialize(&hex::decode(&PK5[2..]).unwrap()).unwrap();
let pk6 = PublicKeyBytes::deserialize(&hex::decode(&PK6[2..]).unwrap()).unwrap();
let file_name = temp.into_path().join("graffiti.txt");
@@ -160,6 +172,29 @@ mod tests {
graffiti_file
.write_all(format!("{}:{}\n", pk3.as_hex_string(), EMPTY_GRAFFITI).as_bytes())
.unwrap();
// Test Lines with leading newlines - these empty lines will be skipped
graffiti_file.write_all(b"\n").unwrap();
graffiti_file.write_all(b" \n").unwrap();
graffiti_file
.write_all(format!("{}: {}\n", pk4.as_hex_string(), CUSTOM_GRAFFITI4).as_bytes())
.unwrap();
// Test Empty lines between entries - these will be skipped
graffiti_file.write_all(b"\n").unwrap();
graffiti_file.write_all(b" \n").unwrap();
graffiti_file.write_all(b"\t\n").unwrap();
graffiti_file
.write_all(format!("{}: {}\n", pk5.as_hex_string(), CUSTOM_GRAFFITI4).as_bytes())
.unwrap();
// Test Trailing empty lines - these will be skipped
graffiti_file
.write_all(format!("{}: {}\n", pk6.as_hex_string(), CUSTOM_GRAFFITI4).as_bytes())
.unwrap();
graffiti_file.write_all(b"\n").unwrap();
graffiti_file.write_all(b" \n").unwrap();
graffiti_file.flush().unwrap();
file_name
}
@@ -172,6 +207,9 @@ mod tests {
let pk1 = PublicKeyBytes::deserialize(&hex::decode(&PK1[2..]).unwrap()).unwrap();
let pk2 = PublicKeyBytes::deserialize(&hex::decode(&PK2[2..]).unwrap()).unwrap();
let pk3 = PublicKeyBytes::deserialize(&hex::decode(&PK3[2..]).unwrap()).unwrap();
let pk4 = PublicKeyBytes::deserialize(&hex::decode(&PK4[2..]).unwrap()).unwrap();
let pk5 = PublicKeyBytes::deserialize(&hex::decode(&PK5[2..]).unwrap()).unwrap();
let pk6 = PublicKeyBytes::deserialize(&hex::decode(&PK6[2..]).unwrap()).unwrap();
// Read once
gf.read_graffiti_file().unwrap();
@@ -190,6 +228,20 @@ mod tests {
GraffitiString::from_str(EMPTY_GRAFFITI).unwrap().into()
);
// Test newline cases - all empty lines should be skipped
assert_eq!(
gf.load_graffiti(&pk4).unwrap().unwrap(),
GraffitiString::from_str(CUSTOM_GRAFFITI4).unwrap().into()
);
assert_eq!(
gf.load_graffiti(&pk5).unwrap().unwrap(),
GraffitiString::from_str(CUSTOM_GRAFFITI4).unwrap().into()
);
assert_eq!(
gf.load_graffiti(&pk6).unwrap().unwrap(),
GraffitiString::from_str(CUSTOM_GRAFFITI4).unwrap().into()
);
// Random pk should return the default graffiti
let random_pk = Keypair::random().pk.compress();
assert_eq!(

View File

@@ -43,6 +43,7 @@ validator_services = { workspace = true }
url = { workspace = true }
warp_utils = { workspace = true }
warp = { workspace = true }
zeroize = { workspace = true }
[dev-dependencies]
itertools = { workspace = true }

View File

@@ -2,7 +2,7 @@ use account_utils::validator_definitions::{PasswordStorage, ValidatorDefinition}
use account_utils::{
eth2_keystore::Keystore,
eth2_wallet::{bip39::Mnemonic, WalletBuilder},
random_mnemonic, random_password, ZeroizeString,
random_mnemonic, random_password,
};
use eth2::lighthouse_vc::types::{self as api_types};
use slot_clock::SlotClock;
@@ -11,6 +11,7 @@ use types::ChainSpec;
use types::EthSpec;
use validator_dir::{keystore_password_path, Builder as ValidatorDirBuilder};
use validator_store::ValidatorStore;
use zeroize::Zeroizing;
/// Create some validator EIP-2335 keystores and store them on disk. Then, enroll the validators in
/// this validator client.
@@ -59,7 +60,7 @@ pub async fn create_validators_mnemonic<P: AsRef<Path>, T: 'static + SlotClock,
for request in validator_requests {
let voting_password = random_password();
let withdrawal_password = random_password();
let voting_password_string = ZeroizeString::from(
let voting_password_string = Zeroizing::from(
String::from_utf8(voting_password.as_bytes().to_vec()).map_err(|e| {
warp_utils::reject::custom_server_error(format!(
"locally generated password is not utf8: {:?}",
@@ -199,7 +200,7 @@ pub async fn create_validators_web3signer<T: 'static + SlotClock, E: EthSpec>(
pub fn get_voting_password_storage(
secrets_dir: &Option<PathBuf>,
voting_keystore: &Keystore,
voting_password_string: &ZeroizeString,
voting_password_string: &Zeroizing<String>,
) -> Result<PasswordStorage, warp::Rejection> {
if let Some(secrets_dir) = &secrets_dir {
let password_path = keystore_password_path(secrets_dir, voting_keystore);

View File

@@ -1,5 +1,5 @@
//! Implementation of the standard keystore management API.
use account_utils::{validator_definitions::PasswordStorage, ZeroizeString};
use account_utils::validator_definitions::PasswordStorage;
use eth2::lighthouse_vc::{
std_types::{
DeleteKeystoreStatus, DeleteKeystoresRequest, DeleteKeystoresResponse,
@@ -22,6 +22,7 @@ use validator_dir::{keystore_password_path, Builder as ValidatorDirBuilder};
use validator_store::ValidatorStore;
use warp::Rejection;
use warp_utils::reject::{custom_bad_request, custom_server_error};
use zeroize::Zeroizing;
pub fn list<T: SlotClock + 'static, E: EthSpec>(
validator_store: Arc<ValidatorStore<T, E>>,
@@ -167,7 +168,7 @@ pub fn import<T: SlotClock + 'static, E: EthSpec>(
fn import_single_keystore<T: SlotClock + 'static, E: EthSpec>(
keystore: Keystore,
password: ZeroizeString,
password: Zeroizing<String>,
validator_dir_path: PathBuf,
secrets_dir: Option<PathBuf>,
validator_store: &ValidatorStore<T, E>,

View File

@@ -2,7 +2,6 @@ use crate::{ApiSecret, Config as HttpConfig, Context};
use account_utils::validator_definitions::ValidatorDefinitions;
use account_utils::{
eth2_wallet::WalletBuilder, mnemonic_from_phrase, random_mnemonic, random_password,
ZeroizeString,
};
use deposit_contract::decode_eth1_tx_data;
use doppelganger_service::DoppelgangerService;
@@ -28,6 +27,7 @@ use task_executor::test_utils::TestRuntime;
use tempfile::{tempdir, TempDir};
use tokio::sync::oneshot;
use validator_store::{Config as ValidatorStoreConfig, ValidatorStore};
use zeroize::Zeroizing;
pub const PASSWORD_BYTES: &[u8] = &[42, 50, 37];
pub const TEST_DEFAULT_FEE_RECIPIENT: Address = Address::repeat_byte(42);
@@ -321,7 +321,7 @@ impl ApiTester {
.collect::<Vec<_>>();
let (response, mnemonic) = if s.specify_mnemonic {
let mnemonic = ZeroizeString::from(random_mnemonic().phrase().to_string());
let mnemonic = Zeroizing::from(random_mnemonic().phrase().to_string());
let request = CreateValidatorsMnemonicRequest {
mnemonic: mnemonic.clone(),
key_derivation_path_offset: s.key_derivation_path_offset,

View File

@@ -9,7 +9,7 @@ use initialized_validators::{Config as InitializedValidatorsConfig, InitializedV
use crate::{ApiSecret, Config as HttpConfig, Context};
use account_utils::{
eth2_wallet::WalletBuilder, mnemonic_from_phrase, random_mnemonic, random_password,
random_password_string, validator_definitions::ValidatorDefinitions, ZeroizeString,
random_password_string, validator_definitions::ValidatorDefinitions,
};
use deposit_contract::decode_eth1_tx_data;
use eth2::{
@@ -33,6 +33,7 @@ use task_executor::test_utils::TestRuntime;
use tempfile::{tempdir, TempDir};
use types::graffiti::GraffitiString;
use validator_store::{Config as ValidatorStoreConfig, ValidatorStore};
use zeroize::Zeroizing;
const PASSWORD_BYTES: &[u8] = &[42, 50, 37];
pub const TEST_DEFAULT_FEE_RECIPIENT: Address = Address::repeat_byte(42);
@@ -282,7 +283,7 @@ impl ApiTester {
.collect::<Vec<_>>();
let (response, mnemonic) = if s.specify_mnemonic {
let mnemonic = ZeroizeString::from(random_mnemonic().phrase().to_string());
let mnemonic = Zeroizing::from(random_mnemonic().phrase().to_string());
let request = CreateValidatorsMnemonicRequest {
mnemonic: mnemonic.clone(),
key_derivation_path_offset: s.key_derivation_path_offset,

View File

@@ -14,8 +14,9 @@ use std::{collections::HashMap, path::Path};
use tokio::runtime::Handle;
use types::{attestation::AttestationBase, Address};
use validator_store::DEFAULT_GAS_LIMIT;
use zeroize::Zeroizing;
fn new_keystore(password: ZeroizeString) -> Keystore {
fn new_keystore(password: Zeroizing<String>) -> Keystore {
let keypair = Keypair::random();
Keystore(
KeystoreBuilder::new(&keypair, password.as_ref(), String::new())

View File

@@ -24,3 +24,4 @@ tokio = { workspace = true }
bincode = { workspace = true }
filesystem = { workspace = true }
validator_metrics = { workspace = true }
zeroize = { workspace = true }

View File

@@ -14,7 +14,6 @@ use account_utils::{
self, SigningDefinition, ValidatorDefinition, ValidatorDefinitions, Web3SignerDefinition,
CONFIG_FILENAME,
},
ZeroizeString,
};
use eth2_keystore::Keystore;
use lockfile::{Lockfile, LockfileError};
@@ -34,6 +33,7 @@ use types::graffiti::GraffitiString;
use types::{Address, Graffiti, Keypair, PublicKey, PublicKeyBytes};
use url::{ParseError, Url};
use validator_dir::Builder as ValidatorDirBuilder;
use zeroize::Zeroizing;
use key_cache::KeyCache;
@@ -74,7 +74,7 @@ pub enum OnDecryptFailure {
pub struct KeystoreAndPassword {
pub keystore: Keystore,
pub password: Option<ZeroizeString>,
pub password: Option<Zeroizing<String>>,
}
#[derive(Debug)]
@@ -262,7 +262,7 @@ impl InitializedValidator {
// If the password is supplied, use it and ignore the path
// (if supplied).
(_, Some(password)) => (
password.as_ref().to_vec().into(),
password.as_bytes().to_vec().into(),
keystore
.decrypt_keypair(password.as_ref())
.map_err(Error::UnableToDecryptKeystore)?,
@@ -282,7 +282,7 @@ impl InitializedValidator {
&keystore,
&keystore_path,
)?;
(password.as_ref().to_vec().into(), keypair)
(password.as_bytes().to_vec().into(), keypair)
}
},
)
@@ -455,7 +455,7 @@ fn build_web3_signer_client(
fn unlock_keystore_via_stdin_password(
keystore: &Keystore,
keystore_path: &Path,
) -> Result<(ZeroizeString, Keypair), Error> {
) -> Result<(Zeroizing<String>, Keypair), Error> {
eprintln!();
eprintln!(
"The {} file does not contain either of the following fields for {:?}:",
@@ -1172,14 +1172,14 @@ impl InitializedValidators {
voting_keystore_path,
} => {
let pw = if let Some(p) = voting_keystore_password {
p.as_ref().to_vec().into()
p.as_bytes().to_vec().into()
} else if let Some(path) = voting_keystore_password_path {
read_password(path).map_err(Error::UnableToReadVotingKeystorePassword)?
} else {
let keystore = open_keystore(voting_keystore_path)?;
unlock_keystore_via_stdin_password(&keystore, voting_keystore_path)?
.0
.as_ref()
.as_bytes()
.to_vec()
.into()
};
@@ -1425,7 +1425,7 @@ impl InitializedValidators {
/// This should only be used for testing, it's rather destructive.
pub fn delete_passwords_from_validator_definitions(
&mut self,
) -> Result<HashMap<PublicKey, ZeroizeString>, Error> {
) -> Result<HashMap<PublicKey, Zeroizing<String>>, Error> {
let mut passwords = HashMap::default();
for def in self.definitions.as_mut_slice() {