mirror of
https://github.com/sigp/lighthouse.git
synced 2026-04-18 21:38:31 +00:00
Update to spec v1.0.0-rc.0 and BLSv4 (#1765)
## Issue Addressed Closes #1504 Closes #1505 Replaces #1703 Closes #1707 ## Proposed Changes * Update BLST and Milagro to versions compatible with BLSv4 spec * Update Lighthouse to spec v1.0.0-rc.0, and update EF test vectors * Use the v1.0.0 constants for `MainnetEthSpec`. * Rename `InteropEthSpec` -> `V012LegacyEthSpec` * Change all constants to suit the mainnet `v0.12.3` specification (i.e., Medalla). * Deprecate the `--spec` flag for the `lighthouse` binary * This value is now obtained from the `config_name` field of the `YamlConfig`. * Built in testnet YAML files have been updated. * Ignore the `--spec` value, if supplied, log a warning that it will be deprecated * `lcli` still has the spec flag, that's fine because it's dev tooling. * Remove the `E: EthSpec` from `YamlConfig` * This means we need to deser the genesis `BeaconState` on-demand, but this is fine. * Swap the old "minimal", "mainnet" strings over to the new `EthSpecId` enum. * Always require a `CONFIG_NAME` field in `YamlConfig` (it used to have a default). ## Additional Info Lots of breaking changes, do not merge! ~~We will likely need a Lighthouse v0.4.0 branch, and possibly a long-term v0.3.0 branch to keep Medalla alive~~. Co-authored-by: Kirk Baird <baird.k@outlook.com> Co-authored-by: Paul Hauner <paul@paulhauner.com>
This commit is contained in:
@@ -1,17 +1,5 @@
|
||||
use crate::{Error, PUBLIC_KEY_BYTES_LEN};
|
||||
|
||||
/// Implemented on some struct from a BLS library so it may be used internally in this crate.
|
||||
pub trait TAggregatePublicKey: Sized + Clone {
|
||||
/// Initialize `Self` to the infinity value which can then have other public keys aggregated
|
||||
/// upon it.
|
||||
fn infinity() -> Self;
|
||||
|
||||
/// Serialize `self` as compressed bytes.
|
||||
fn serialize(&self) -> [u8; PUBLIC_KEY_BYTES_LEN];
|
||||
|
||||
/// Deserialize `self` from compressed bytes.
|
||||
fn deserialize(bytes: &[u8]) -> Result<Self, Error>;
|
||||
}
|
||||
pub trait TAggregatePublicKey: Sized + Clone {}
|
||||
|
||||
/*
|
||||
* Note: there is no immediate need for a `GenericAggregatePublicKey` struct.
|
||||
|
||||
@@ -183,13 +183,6 @@ where
|
||||
return false;
|
||||
}
|
||||
|
||||
if self.is_infinity
|
||||
&& pubkeys.len() == 1
|
||||
&& pubkeys.first().map_or(false, |pk| pk.is_infinity)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
match self.point.as_ref() {
|
||||
Some(point) => point.fast_aggregate_verify(msg, pubkeys),
|
||||
None => false,
|
||||
@@ -207,13 +200,6 @@ where
|
||||
return false;
|
||||
}
|
||||
|
||||
if self.is_infinity
|
||||
&& pubkeys.len() == 1
|
||||
&& pubkeys.first().map_or(false, |pk| pk.is_infinity)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
match self.point.as_ref() {
|
||||
Some(point) => point.aggregate_verify(msgs, pubkeys),
|
||||
None => false,
|
||||
|
||||
@@ -33,8 +33,6 @@ pub trait TPublicKey: Sized + Clone {
|
||||
pub struct GenericPublicKey<Pub> {
|
||||
/// The underlying point which performs *actual* cryptographic operations.
|
||||
point: Pub,
|
||||
/// True if this point is equal to the `INFINITY_PUBLIC_KEY`.
|
||||
pub(crate) is_infinity: bool,
|
||||
}
|
||||
|
||||
impl<Pub> GenericPublicKey<Pub>
|
||||
@@ -42,8 +40,8 @@ where
|
||||
Pub: TPublicKey,
|
||||
{
|
||||
/// Instantiates `Self` from a `point`.
|
||||
pub(crate) fn from_point(point: Pub, is_infinity: bool) -> Self {
|
||||
Self { point, is_infinity }
|
||||
pub(crate) fn from_point(point: Pub) -> Self {
|
||||
Self { point }
|
||||
}
|
||||
|
||||
/// Returns a reference to the underlying BLS point.
|
||||
@@ -63,10 +61,13 @@ where
|
||||
|
||||
/// Deserialize `self` from compressed bytes.
|
||||
pub fn deserialize(bytes: &[u8]) -> Result<Self, Error> {
|
||||
Ok(Self {
|
||||
point: Pub::deserialize(bytes)?,
|
||||
is_infinity: bytes == &INFINITY_PUBLIC_KEY[..],
|
||||
})
|
||||
if bytes == &INFINITY_PUBLIC_KEY[..] {
|
||||
Err(Error::InvalidInfinityPublicKey)
|
||||
} else {
|
||||
Ok(Self {
|
||||
point: Pub::deserialize(bytes)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
generic_public_key::{GenericPublicKey, TPublicKey},
|
||||
Error, INFINITY_PUBLIC_KEY, PUBLIC_KEY_BYTES_LEN,
|
||||
Error, PUBLIC_KEY_BYTES_LEN,
|
||||
};
|
||||
use serde::de::{Deserialize, Deserializer};
|
||||
use serde::ser::{Serialize, Serializer};
|
||||
@@ -32,8 +32,7 @@ where
|
||||
///
|
||||
/// May fail if the bytes are invalid.
|
||||
pub fn decompress(&self) -> Result<GenericPublicKey<Pub>, Error> {
|
||||
let is_infinity = self.bytes[..] == INFINITY_PUBLIC_KEY[..];
|
||||
Pub::deserialize(&self.bytes).map(|point| GenericPublicKey::from_point(point, is_infinity))
|
||||
GenericPublicKey::deserialize(&self.bytes)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -58,8 +58,7 @@ where
|
||||
|
||||
/// Returns the public key that corresponds to self.
|
||||
pub fn public_key(&self) -> GenericPublicKey<Pub> {
|
||||
let is_infinity = false;
|
||||
GenericPublicKey::from_point(self.point.public_key(), is_infinity)
|
||||
GenericPublicKey::from_point(self.point.public_key())
|
||||
}
|
||||
|
||||
/// Serialize `self` as compressed bytes.
|
||||
@@ -79,6 +78,8 @@ where
|
||||
got: bytes.len(),
|
||||
expected: SECRET_KEY_BYTES_LEN,
|
||||
})
|
||||
} else if bytes.iter().all(|b| *b == 0) {
|
||||
Err(Error::InvalidZeroSecretKey)
|
||||
} else {
|
||||
Ok(Self {
|
||||
point: Sec::deserialize(bytes)?,
|
||||
|
||||
@@ -125,10 +125,6 @@ where
|
||||
{
|
||||
/// Returns `true` if `self` is a signature across `msg` by `pubkey`.
|
||||
pub fn verify(&self, pubkey: &GenericPublicKey<Pub>, msg: Hash256) -> bool {
|
||||
if self.is_infinity && pubkey.is_infinity {
|
||||
return true;
|
||||
}
|
||||
|
||||
if let Some(point) = &self.point {
|
||||
point.verify(pubkey.point(), msg)
|
||||
} else {
|
||||
|
||||
@@ -4,7 +4,7 @@ use crate::{
|
||||
generic_public_key::{GenericPublicKey, TPublicKey, PUBLIC_KEY_BYTES_LEN},
|
||||
generic_secret_key::TSecretKey,
|
||||
generic_signature::{TSignature, SIGNATURE_BYTES_LEN},
|
||||
Error, Hash256, ZeroizeHash, INFINITY_PUBLIC_KEY, INFINITY_SIGNATURE,
|
||||
Error, Hash256, ZeroizeHash, INFINITY_SIGNATURE,
|
||||
};
|
||||
pub use blst::min_pk as blst_core;
|
||||
use blst::{blst_scalar, BLST_ERROR};
|
||||
@@ -50,18 +50,12 @@ pub fn verify_signature_sets<'a>(
|
||||
let mut pks = Vec::with_capacity(sets.len());
|
||||
|
||||
for set in &sets {
|
||||
// If this set is simply an infinity signature and infinity pubkey then skip verification.
|
||||
// This has the effect of always declaring that this sig/pubkey combination is valid.
|
||||
if set.signature.is_infinity
|
||||
&& set.signing_keys.len() == 1
|
||||
&& set.signing_keys.first().map_or(false, |pk| pk.is_infinity)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Generate random scalars.
|
||||
let mut vals = [0u64; 4];
|
||||
vals[0] = rng.gen();
|
||||
while vals[0] == 0 {
|
||||
// Do not use zero
|
||||
vals[0] = rng.gen();
|
||||
}
|
||||
let mut rand_i = std::mem::MaybeUninit::<blst_scalar>::uninit();
|
||||
|
||||
// TODO: remove this `unsafe` code-block once we get a safe option from `blst`.
|
||||
@@ -75,8 +69,12 @@ pub fn verify_signature_sets<'a>(
|
||||
// Grab a slice of the message, to satisfy the blst API.
|
||||
msgs_refs.push(set.message.as_bytes());
|
||||
|
||||
// Convert the aggregate signature into a signature.
|
||||
if let Some(point) = set.signature.point() {
|
||||
// Subgroup check the signature
|
||||
if !point.0.subgroup_check() {
|
||||
return false;
|
||||
}
|
||||
// Convert the aggregate signature into a signature.
|
||||
sigs.push(point.0.to_signature())
|
||||
} else {
|
||||
// Any "empty" signature should cause a signature failure.
|
||||
@@ -103,12 +101,6 @@ pub fn verify_signature_sets<'a>(
|
||||
pks.push(blst_core::AggregatePublicKey::aggregate(&signing_keys).to_public_key());
|
||||
}
|
||||
|
||||
// Due to an earlier check, the only case this can be empty is if all the sets consisted of
|
||||
// infinity pubkeys/sigs. In such a case we wish to return `true`.
|
||||
if msgs_refs.is_empty() {
|
||||
return true;
|
||||
}
|
||||
|
||||
let (sig_refs, pks_refs): (Vec<_>, Vec<_>) = sigs.iter().zip(pks.iter()).unzip();
|
||||
|
||||
let err = blst_core::Signature::verify_multiple_aggregate_signatures(
|
||||
@@ -124,7 +116,15 @@ impl TPublicKey for blst_core::PublicKey {
|
||||
}
|
||||
|
||||
fn deserialize(bytes: &[u8]) -> Result<Self, Error> {
|
||||
Self::uncompress(&bytes).map_err(Into::into)
|
||||
// key_validate accepts uncompressed bytes too so enforce byte length here.
|
||||
// It also does subgroup checks, noting infinity check is done in `generic_public_key.rs`.
|
||||
if bytes.len() != PUBLIC_KEY_BYTES_LEN {
|
||||
return Err(Error::InvalidByteLength {
|
||||
got: bytes.len(),
|
||||
expected: PUBLIC_KEY_BYTES_LEN,
|
||||
});
|
||||
}
|
||||
Self::key_validate(&bytes).map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,25 +145,7 @@ impl PartialEq for BlstAggregatePublicKey {
|
||||
}
|
||||
}
|
||||
|
||||
impl TAggregatePublicKey for BlstAggregatePublicKey {
|
||||
fn infinity() -> Self {
|
||||
blst_core::PublicKey::from_bytes(&INFINITY_PUBLIC_KEY)
|
||||
.map(|pk| blst_core::AggregatePublicKey::from_public_key(&pk))
|
||||
.map(Self)
|
||||
.expect("should decode infinity public key")
|
||||
}
|
||||
|
||||
fn serialize(&self) -> [u8; PUBLIC_KEY_BYTES_LEN] {
|
||||
self.0.to_public_key().compress()
|
||||
}
|
||||
|
||||
fn deserialize(bytes: &[u8]) -> Result<Self, Error> {
|
||||
blst_core::PublicKey::from_bytes(&bytes)
|
||||
.map_err(Into::into)
|
||||
.map(|pk| blst_core::AggregatePublicKey::from_public_key(&pk))
|
||||
.map(Self)
|
||||
}
|
||||
}
|
||||
impl TAggregatePublicKey for BlstAggregatePublicKey {}
|
||||
|
||||
impl TSignature<blst_core::PublicKey> for blst_core::Signature {
|
||||
fn serialize(&self) -> [u8; SIGNATURE_BYTES_LEN] {
|
||||
@@ -175,6 +157,9 @@ impl TSignature<blst_core::PublicKey> for blst_core::Signature {
|
||||
}
|
||||
|
||||
fn verify(&self, pubkey: &blst_core::PublicKey, msg: Hash256) -> bool {
|
||||
if !self.subgroup_check() {
|
||||
return false;
|
||||
}
|
||||
self.verify(msg.as_bytes(), DST, &[], pubkey) == BLST_ERROR::BLST_SUCCESS
|
||||
}
|
||||
}
|
||||
@@ -232,6 +217,9 @@ impl TAggregateSignature<blst_core::PublicKey, BlstAggregatePublicKey, blst_core
|
||||
) -> bool {
|
||||
let pubkeys = pubkeys.iter().map(|pk| pk.point()).collect::<Vec<_>>();
|
||||
let signature = self.0.clone().to_signature();
|
||||
if !signature.subgroup_check() {
|
||||
return false;
|
||||
}
|
||||
signature.fast_aggregate_verify(msg.as_bytes(), DST, &pubkeys) == BLST_ERROR::BLST_SUCCESS
|
||||
}
|
||||
|
||||
@@ -243,6 +231,9 @@ impl TAggregateSignature<blst_core::PublicKey, BlstAggregatePublicKey, blst_core
|
||||
let pubkeys = pubkeys.iter().map(|pk| pk.point()).collect::<Vec<_>>();
|
||||
let msgs = msgs.iter().map(|hash| hash.as_bytes()).collect::<Vec<_>>();
|
||||
let signature = self.0.clone().to_signature();
|
||||
if !signature.subgroup_check() {
|
||||
return false;
|
||||
}
|
||||
signature.aggregate_verify(&msgs, DST, &pubkeys) == BLST_ERROR::BLST_SUCCESS
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,25 +63,7 @@ impl PartialEq for PublicKey {
|
||||
#[derive(Clone)]
|
||||
pub struct AggregatePublicKey([u8; PUBLIC_KEY_BYTES_LEN]);
|
||||
|
||||
impl TAggregatePublicKey for AggregatePublicKey {
|
||||
fn infinity() -> Self {
|
||||
Self([0; PUBLIC_KEY_BYTES_LEN])
|
||||
}
|
||||
|
||||
fn serialize(&self) -> [u8; PUBLIC_KEY_BYTES_LEN] {
|
||||
let mut bytes = [0; PUBLIC_KEY_BYTES_LEN];
|
||||
bytes[..].copy_from_slice(&self.0);
|
||||
bytes
|
||||
}
|
||||
|
||||
fn deserialize(bytes: &[u8]) -> Result<Self, Error> {
|
||||
let mut key = [0; PUBLIC_KEY_BYTES_LEN];
|
||||
|
||||
key[..].copy_from_slice(&bytes);
|
||||
|
||||
Ok(Self(key))
|
||||
}
|
||||
}
|
||||
impl TAggregatePublicKey for AggregatePublicKey {}
|
||||
|
||||
impl Eq for AggregatePublicKey {}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ use crate::{
|
||||
generic_public_key::{GenericPublicKey, TPublicKey, PUBLIC_KEY_BYTES_LEN},
|
||||
generic_secret_key::{TSecretKey, SECRET_KEY_BYTES_LEN},
|
||||
generic_signature::{TSignature, SIGNATURE_BYTES_LEN},
|
||||
Error, Hash256, ZeroizeHash, INFINITY_PUBLIC_KEY,
|
||||
Error, Hash256, ZeroizeHash,
|
||||
};
|
||||
pub use milagro_bls as milagro;
|
||||
use rand::thread_rng;
|
||||
@@ -86,21 +86,7 @@ impl TPublicKey for milagro::PublicKey {
|
||||
}
|
||||
}
|
||||
|
||||
impl TAggregatePublicKey for milagro::AggregatePublicKey {
|
||||
fn infinity() -> Self {
|
||||
Self::from_bytes(&INFINITY_PUBLIC_KEY).expect("should decode infinity public key")
|
||||
}
|
||||
|
||||
fn serialize(&self) -> [u8; PUBLIC_KEY_BYTES_LEN] {
|
||||
let mut bytes = [0; PUBLIC_KEY_BYTES_LEN];
|
||||
bytes[..].copy_from_slice(&self.as_bytes());
|
||||
bytes
|
||||
}
|
||||
|
||||
fn deserialize(bytes: &[u8]) -> Result<Self, Error> {
|
||||
Self::from_bytes(&bytes).map_err(Into::into)
|
||||
}
|
||||
}
|
||||
impl TAggregatePublicKey for milagro::AggregatePublicKey {}
|
||||
|
||||
impl TSignature<milagro::PublicKey> for milagro::Signature {
|
||||
fn serialize(&self) -> [u8; SIGNATURE_BYTES_LEN] {
|
||||
|
||||
@@ -56,6 +56,10 @@ pub enum Error {
|
||||
InvalidByteLength { got: usize, expected: usize },
|
||||
/// The provided secret key bytes were an incorrect length.
|
||||
InvalidSecretKeyLength { got: usize, expected: usize },
|
||||
/// The public key represents the point at infinity, which is invalid.
|
||||
InvalidInfinityPublicKey,
|
||||
/// The secret key is all zero bytes, which is invalid.
|
||||
InvalidZeroSecretKey,
|
||||
}
|
||||
|
||||
impl From<AmclError> for Error {
|
||||
|
||||
Reference in New Issue
Block a user