lighthouse-pulse/common/eth2_interop_keypairs/src/lib.rs
blacktemplar d8cda2d86e Fix new clippy lints (#2036)
## Issue Addressed

NA

## Proposed Changes

Fixes new clippy lints in the whole project (mainly [manual_strip](https://rust-lang.github.io/rust-clippy/master/index.html#manual_strip) and [unnecessary_lazy_evaluations](https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations)). Furthermore, removes `to_string()` calls on literals when used with the `?`-operator.
2020-12-03 01:10:26 +00:00

131 lines
4.4 KiB
Rust

//! Produces the "deterministic" validator private keys used for inter-operability testing for
//! Ethereum 2.0 clients.
//!
//! Each private key is the sha2 hash of the validator index (little-endian, padded to 32 bytes),
//! modulo the BLS-381 curve order.
//!
//! Keys generated here are **not secret** and are **not for production use**. It is trivial to
//! know the secret key for any validator.
//!
//!## Reference
//!
//! Reference implementation:
//!
//! https://github.com/ethereum/eth2.0-pm/blob/6e41fcf383ebeb5125938850d8e9b4e9888389b4/interop/mocked_start/keygen.py
//!
//!
//! This implementation passes the [reference implementation
//! tests](https://github.com/ethereum/eth2.0-pm/blob/6e41fcf383ebeb5125938850d8e9b4e9888389b4/interop/mocked_start/keygen_test_vector.yaml).
#[macro_use]
extern crate lazy_static;
use bls::{Keypair, PublicKey, SecretKey};
use eth2_hashing::hash;
use num_bigint::BigUint;
use serde_derive::{Deserialize, Serialize};
use std::convert::TryInto;
use std::fs::File;
use std::path::PathBuf;
pub const PRIVATE_KEY_BYTES: usize = 32;
pub const PUBLIC_KEY_BYTES: usize = 48;
pub const HASH_BYTES: usize = 32;
lazy_static! {
static ref CURVE_ORDER: BigUint =
"52435875175126190479447740508185965837690552500527637822603658699938581184513"
.parse::<BigUint>()
.expect("Curve order should be valid");
}
/// Return a G1 point for the given `validator_index`, encoded as a compressed point in
/// big-endian byte-ordering.
pub fn be_private_key(validator_index: usize) -> [u8; PRIVATE_KEY_BYTES] {
let preimage = {
let mut bytes = [0; HASH_BYTES];
let index = validator_index.to_le_bytes();
bytes[0..index.len()].copy_from_slice(&index);
bytes
};
let privkey = BigUint::from_bytes_le(&hash(&preimage)) % &*CURVE_ORDER;
let mut bytes = [0; PRIVATE_KEY_BYTES];
let privkey_bytes = privkey.to_bytes_be();
bytes[PRIVATE_KEY_BYTES - privkey_bytes.len()..].copy_from_slice(&privkey_bytes);
bytes
}
/// Return a public and private keypair for a given `validator_index`.
pub fn keypair(validator_index: usize) -> Keypair {
let sk = SecretKey::deserialize(&be_private_key(validator_index)).unwrap_or_else(|_| {
panic!(
"Should build valid private key for validator index {}",
validator_index
)
});
Keypair::from_components(sk.public_key(), sk)
}
#[derive(Serialize, Deserialize)]
struct YamlKeypair {
/// Big-endian.
privkey: String,
/// Big-endian.
pubkey: String,
}
impl TryInto<Keypair> for YamlKeypair {
type Error = String;
fn try_into(self) -> Result<Keypair, Self::Error> {
let privkey = string_to_bytes(&self.privkey)?;
let pubkey = string_to_bytes(&self.pubkey)?;
if (privkey.len() > PRIVATE_KEY_BYTES) || (pubkey.len() > PUBLIC_KEY_BYTES) {
return Err("Public or private key is too long".into());
}
let sk = {
let mut bytes = vec![0; PRIVATE_KEY_BYTES - privkey.len()];
bytes.extend_from_slice(&privkey);
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::deserialize(&bytes)
.map_err(|e| format!("Failed to decode bytes into public key: {:?}", e))?
};
Ok(Keypair::from_components(pk, sk))
}
}
fn string_to_bytes(string: &str) -> Result<Vec<u8>, String> {
let string = if let Some(stripped) = string.strip_prefix("0x") {
stripped
} else {
string
};
hex::decode(string).map_err(|e| format!("Unable to decode public or private key: {}", e))
}
/// Loads keypairs from a YAML encoded file.
///
/// Uses this as reference:
/// https://github.com/ethereum/eth2.0-pm/blob/9a9dbcd95e2b8e10287797bd768014ab3d842e99/interop/mocked_start/keygen_10_validators.yaml
pub fn keypairs_from_yaml_file(path: PathBuf) -> Result<Vec<Keypair>, String> {
let file = File::open(path).map_err(|e| format!("Unable to open YAML key file: {}", e))?;
serde_yaml::from_reader::<_, Vec<YamlKeypair>>(file)
.map_err(|e| format!("Could not parse YAML: {:?}", e))?
.into_iter()
.map(TryInto::try_into)
.collect::<Result<Vec<_>, String>>()
}