mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-03 00:31:50 +00:00
Support multiple BLS implementations (#1335)
## Issue Addressed NA ## Proposed Changes - Refactor the `bls` crate to support multiple BLS "backends" (e.g., milagro, blst, etc). - Removes some duplicate, unused code in `common/rest_types/src/validator.rs`. - Removes the old "upgrade legacy keypairs" functionality (these were unencrypted keys that haven't been supported for a few testnets, no one should be using them anymore). ## Additional Info Most of the files changed are just inconsequential changes to function names. ## TODO - [x] Optimization levels - [x] Infinity point: https://github.com/supranational/blst/issues/11 - [x] Ensure milagro *and* blst are tested via CI - [x] What to do with unsafe code? - [x] Test infinity point in signature sets
This commit is contained in:
@@ -98,7 +98,7 @@ mod tests {
|
||||
pubkey: keypair.pk.into(),
|
||||
withdrawal_credentials: Hash256::from_slice(&[42; 32]),
|
||||
amount: u64::max_value(),
|
||||
signature: Signature::empty_signature().into(),
|
||||
signature: Signature::empty().into(),
|
||||
};
|
||||
deposit_data.signature = deposit_data.create_signature(&keypair.sk, spec);
|
||||
deposit_data
|
||||
|
||||
@@ -11,10 +11,10 @@ lazy_static = "1.4.0"
|
||||
num-bigint = "0.3.0"
|
||||
eth2_hashing = "0.1.0"
|
||||
hex = "0.4.2"
|
||||
milagro_bls = { git = "https://github.com/sigp/milagro_bls", tag = "v1.1.0" }
|
||||
serde_yaml = "0.8.11"
|
||||
serde = "1.0.110"
|
||||
serde_derive = "1.0.110"
|
||||
bls = { path = "../../crypto/bls" }
|
||||
|
||||
[dev-dependencies]
|
||||
base64 = "0.12.1"
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
|
||||
use bls::{Keypair, PublicKey, SecretKey};
|
||||
use eth2_hashing::hash;
|
||||
use milagro_bls::{Keypair, PublicKey, SecretKey};
|
||||
use num_bigint::BigUint;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use std::convert::TryInto;
|
||||
@@ -58,17 +58,14 @@ pub fn be_private_key(validator_index: usize) -> [u8; PRIVATE_KEY_BYTES] {
|
||||
|
||||
/// Return a public and private keypair for a given `validator_index`.
|
||||
pub fn keypair(validator_index: usize) -> Keypair {
|
||||
let sk = SecretKey::from_bytes(&be_private_key(validator_index)).unwrap_or_else(|_| {
|
||||
let sk = SecretKey::deserialize(&be_private_key(validator_index)).unwrap_or_else(|_| {
|
||||
panic!(
|
||||
"Should build valid private key for validator index {}",
|
||||
validator_index
|
||||
)
|
||||
});
|
||||
|
||||
Keypair {
|
||||
pk: PublicKey::from_secret_key(&sk),
|
||||
sk,
|
||||
}
|
||||
Keypair::from_components(sk.public_key(), sk)
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
@@ -93,18 +90,18 @@ impl TryInto<Keypair> for YamlKeypair {
|
||||
let sk = {
|
||||
let mut bytes = vec![0; PRIVATE_KEY_BYTES - privkey.len()];
|
||||
bytes.extend_from_slice(&privkey);
|
||||
SecretKey::from_bytes(&bytes)
|
||||
SecretKey::deserialize(&bytes)
|
||||
.map_err(|e| format!("Failed to decode bytes into secret key: {:?}", e))?
|
||||
};
|
||||
|
||||
let pk = {
|
||||
let mut bytes = vec![0; PUBLIC_KEY_BYTES - pubkey.len()];
|
||||
bytes.extend_from_slice(&pubkey);
|
||||
PublicKey::from_bytes(&bytes)
|
||||
PublicKey::deserialize(&bytes)
|
||||
.map_err(|e| format!("Failed to decode bytes into public key: {:?}", e))?
|
||||
};
|
||||
|
||||
Ok(Keypair { pk, sk })
|
||||
Ok(Keypair::from_components(pk, sk))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,8 +14,8 @@ fn load_from_yaml() {
|
||||
|
||||
keypairs.into_iter().enumerate().for_each(|(i, keypair)| {
|
||||
assert_eq!(
|
||||
keypair,
|
||||
reference_keypair(i),
|
||||
keypair.pk,
|
||||
reference_keypair(i).pk,
|
||||
"Decoded key {} does not match generated key",
|
||||
i
|
||||
)
|
||||
|
||||
@@ -53,6 +53,6 @@ fn reference_public_keys() {
|
||||
"Reference should be 48 bytes (public key size)"
|
||||
);
|
||||
|
||||
assert_eq!(pair.pk.as_bytes().to_vec(), reference);
|
||||
assert_eq!(pair.pk.serialize().to_vec(), reference);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
use bls::{PublicKey, PublicKeyBytes, Signature};
|
||||
use eth2_hashing::hash;
|
||||
use bls::{PublicKey, PublicKeyBytes};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use ssz_derive::{Decode, Encode};
|
||||
use std::convert::TryInto;
|
||||
use types::{CommitteeIndex, Epoch, Slot};
|
||||
|
||||
/// A Validator duty with the validator public key represented a `PublicKeyBytes`.
|
||||
@@ -36,23 +34,6 @@ pub struct ValidatorDutyBase<T> {
|
||||
}
|
||||
|
||||
impl<T> ValidatorDutyBase<T> {
|
||||
/// Given a `slot_signature` determines if the validator of this duty is an aggregator.
|
||||
// Note that we assume the signature is for the associated pubkey to avoid the signature
|
||||
// verification
|
||||
pub fn is_aggregator(&self, slot_signature: &Signature) -> bool {
|
||||
if let Some(modulo) = self.aggregator_modulo {
|
||||
let signature_hash = hash(&slot_signature.as_bytes());
|
||||
let signature_hash_int = u64::from_le_bytes(
|
||||
signature_hash[0..8]
|
||||
.try_into()
|
||||
.expect("first 8 bytes of signature should always convert to fixed array"),
|
||||
);
|
||||
signature_hash_int % modulo == 0
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Return `true` if these validator duties are equal, ignoring their `block_proposal_slots`.
|
||||
pub fn eq_ignoring_proposal_slots(&self, other: &Self) -> bool
|
||||
where
|
||||
@@ -95,11 +76,14 @@ pub struct ValidatorSubscription {
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use bls::SecretKey;
|
||||
|
||||
#[test]
|
||||
fn eq_ignoring_proposal_slots() {
|
||||
let validator_pubkey = SecretKey::deserialize(&[1; 32]).unwrap().public_key();
|
||||
|
||||
let duty1 = ValidatorDuty {
|
||||
validator_pubkey: PublicKey::default(),
|
||||
validator_pubkey,
|
||||
validator_index: Some(10),
|
||||
attestation_slot: Some(Slot::new(50)),
|
||||
attestation_committee_index: Some(2),
|
||||
|
||||
@@ -5,7 +5,6 @@ authors = ["Paul Hauner <paul@paulhauner.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[features]
|
||||
unencrypted_keys = []
|
||||
insecure_keys = []
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
@@ -170,7 +170,7 @@ impl<'a> Builder<'a> {
|
||||
pubkey: voting_keypair.pk.clone().into(),
|
||||
withdrawal_credentials,
|
||||
amount,
|
||||
signature: Signature::empty_signature().into(),
|
||||
signature: Signature::empty().into(),
|
||||
};
|
||||
|
||||
deposit_data.signature = deposit_data.create_signature(&voting_keypair.sk, &spec);
|
||||
@@ -220,7 +220,7 @@ impl<'a> Builder<'a> {
|
||||
// Write the withdrawal password to file.
|
||||
write_password_to_file(
|
||||
self.password_dir
|
||||
.join(withdrawal_keypair.pk.as_hex_string()),
|
||||
.join(withdrawal_keypair.pk.to_hex_string()),
|
||||
withdrawal_password.as_bytes(),
|
||||
)?;
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
mod builder;
|
||||
pub mod insecure_keys;
|
||||
mod manager;
|
||||
pub mod unencrypted_keys;
|
||||
mod validator_dir;
|
||||
|
||||
pub use crate::validator_dir::{Error, Eth1DepositData, ValidatorDir, ETH1_DEPOSIT_TX_HASH_FILE};
|
||||
|
||||
@@ -107,14 +107,14 @@ impl Manager {
|
||||
info!(
|
||||
log,
|
||||
"Decrypted validator keystore";
|
||||
"voting_pubkey" => kp.pk.as_hex_string()
|
||||
"voting_pubkey" => kp.pk.to_hex_string()
|
||||
);
|
||||
if lockfile_existed {
|
||||
warn!(
|
||||
log,
|
||||
"Lockfile already existed";
|
||||
"msg" => "ensure no other validator client is running on this host",
|
||||
"voting_pubkey" => kp.pk.as_hex_string()
|
||||
"voting_pubkey" => kp.pk.to_hex_string()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -147,7 +147,7 @@ impl Manager {
|
||||
info!(
|
||||
log,
|
||||
"Decrypted validator keystore";
|
||||
"voting_pubkey" => kp.pk.as_hex_string()
|
||||
"voting_pubkey" => kp.pk.to_hex_string()
|
||||
)
|
||||
}
|
||||
(kp, v)
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
//! The functionality in this module is only required for backward compatibility with the old
|
||||
//! method of key generation (unencrypted, SSZ-encoded keypairs). It should be removed as soon as
|
||||
//! we're confident that no-one is using these keypairs anymore (hopefully mid-June 2020).
|
||||
#![cfg(feature = "unencrypted_keys")]
|
||||
|
||||
use bls::{BLS_PUBLIC_KEY_BYTE_SIZE as PK_LEN, BLS_SECRET_KEY_BYTE_SIZE as SK_LEN};
|
||||
use eth2_keystore::PlainText;
|
||||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
use std::path::Path;
|
||||
use types::{Keypair, PublicKey, SecretKey};
|
||||
|
||||
/// Read a keypair from disk, using the old format where keys were stored as unencrypted
|
||||
/// SSZ-encoded keypairs.
|
||||
///
|
||||
/// This only exists as compatibility with the old scheme and should not be implemented on any new
|
||||
/// features.
|
||||
pub fn load_unencrypted_keypair<P: AsRef<Path>>(path: P) -> Result<Keypair, String> {
|
||||
let path = path.as_ref();
|
||||
|
||||
if !path.exists() {
|
||||
return Err(format!("Keypair file does not exist: {:?}", path));
|
||||
}
|
||||
|
||||
let mut bytes = vec![];
|
||||
|
||||
File::open(&path)
|
||||
.map_err(|e| format!("Unable to open keypair file: {}", e))?
|
||||
.read_to_end(&mut bytes)
|
||||
.map_err(|e| format!("Unable to read keypair file: {}", e))?;
|
||||
|
||||
let bytes: PlainText = bytes.into();
|
||||
|
||||
if bytes.len() != PK_LEN + SK_LEN {
|
||||
return Err(format!("Invalid keypair byte length: {}", bytes.len()));
|
||||
}
|
||||
|
||||
let pk_bytes = &bytes.as_bytes()[..PK_LEN];
|
||||
let sk_bytes = &bytes.as_bytes()[PK_LEN..];
|
||||
|
||||
let pk = PublicKey::from_bytes(pk_bytes)
|
||||
.map_err(|e| format!("Unable to decode public key: {:?}", e))?;
|
||||
|
||||
let sk = SecretKey::from_bytes(sk_bytes)
|
||||
.map_err(|e| format!("Unable to decode secret key: {:?}", e))?;
|
||||
|
||||
Ok(Keypair { pk, sk })
|
||||
}
|
||||
Reference in New Issue
Block a user