diff --git a/Cargo.lock b/Cargo.lock index cba667dc5..ae7edff47 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1068,16 +1068,6 @@ dependencies = [ name = "bls" version = "0.0.0" dependencies = [ - "bls-blst", - "bls-core", - "bls-zkcrypto", -] - -[[package]] -name = "bls-blst" -version = "0.0.0" -dependencies = [ - "bls-core", "blst", "derivative", "derive_more 1.0.0", @@ -1091,51 +1081,11 @@ dependencies = [ "serde_utils", "ssz", "static_assertions", - "std_ext", - "tap", - "typenum", - "zeroize", -] - -[[package]] -name = "bls-core" -version = "0.0.0" -dependencies = [ - "derivative", - "hex", - "once_cell", - "serde", - "ssz", - "static_assertions", "thiserror 1.0.69", "typenum", "zeroize", ] -[[package]] -name = "bls-zkcrypto" -version = "0.0.0" -dependencies = [ - "bls-core", - "bls12_381 0.8.0 (git+https://github.com/zkcrypto/bls12_381.git)", - "derivative", - "derive_more 1.0.0", - "ff", - "fixed-hash", - "hex", - "impl-serde", - "itertools 0.13.0", - "once_cell", - "rand 0.8.5", - "serde", - "serde_utils", - "sha2 0.10.8", - "ssz", - "static_assertions", - "typenum", - "zeroize", -] - [[package]] name = "bls12_381" version = "0.8.0" @@ -1148,19 +1098,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "bls12_381" -version = "0.8.0" -source = "git+https://github.com/zkcrypto/bls12_381.git#9ea427c0eb1a7e2ac16902a322aea156c496ddb0" -dependencies = [ - "digest 0.10.7", - "ff", - "group", - "pairing", - "rand_core 0.6.4", - "subtle", -] - [[package]] name = "blst" version = "0.3.13" @@ -2921,6 +2858,7 @@ dependencies = [ "anyhow", "arc-swap", "arithmetic", + "bls", "bytesize", "clock", "crossbeam-utils", @@ -2974,6 +2912,7 @@ version = "0.0.0" dependencies = [ "anyhow", "arithmetic", + "bls", "clock", "crossbeam-skiplist", "derivative", @@ -7141,7 +7080,7 @@ name = "rust-kzg-zkcrypto" version = "0.1.0" source = "git+https://github.com/grandinetech/rust-kzg.git#6ea2a2ad93ecac70d627edc91334ccb5f4ecd4fb" dependencies = [ - "bls12_381 0.8.0 (git+https://github.com/grandinetech/rust-kzg.git)", + "bls12_381", "byteorder", "ff", "hex", diff --git a/Cargo.toml b/Cargo.toml index cd26374c1..01664cf3e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,9 +8,6 @@ members = [ 'binary_utils', 'block_producer', 'bls', - 'bls/bls-blst', - 'bls/bls-core', - 'bls/bls-zkcrypto', 'builder_api', 'clock', 'database', diff --git a/ad_hoc_bench/src/main.rs b/ad_hoc_bench/src/main.rs index ffd4594b5..2fc25089c 100644 --- a/ad_hoc_bench/src/main.rs +++ b/ad_hoc_bench/src/main.rs @@ -357,6 +357,7 @@ fn run( anchor_block, anchor_state, futures::sink::drain(), + Backend::default(), ); controller.on_slot(last_slot); diff --git a/bls/Cargo.toml b/bls/Cargo.toml index e290d364f..b803fff30 100644 --- a/bls/Cargo.toml +++ b/bls/Cargo.toml @@ -1,16 +1,24 @@ [package] -name = 'bls' +name = "bls" edition = { workspace = true } -authors = ["Grandine "] -[lints] -workspace = true +[dependencies] +blst = { workspace = true, optional = true } +derivative = { workspace = true } +derive_more = { workspace = true } +fixed-hash = { workspace = true } +hex = { workspace = true } +impl-serde = { workspace = true } +once_cell = { workspace = true } +serde = { workspace = true } +serde_utils = { workspace = true } +ssz = { workspace = true } +static_assertions = { workspace = true } +thiserror = { workspace = true } +typenum = { workspace = true } +zeroize = { workspace = true } +itertools = { workspace = true } +rand = { workspace = true } [features] -blst = ["dep:bls-blst"] -zkcrypto = ["dep:bls-zkcrypto"] - -[dependencies] -bls-core = { workspace = true } -bls-blst = { workspace = true, optional = true } -bls-zkcrypto = { workspace = true, optional = true } \ No newline at end of file +blst = ["dep:blst"] diff --git a/bls/bls-blst/Cargo.toml b/bls/bls-blst/Cargo.toml deleted file mode 100644 index d5f431062..000000000 --- a/bls/bls-blst/Cargo.toml +++ /dev/null @@ -1,25 +0,0 @@ -[package] -name = "bls-blst" -edition = { workspace = true } - -[dependencies] -bls-core = { workspace = true } -blst = { workspace = true } -derivative = { workspace = true } -derive_more = { workspace = true } -fixed-hash = { workspace = true } -hex = { workspace = true } -impl-serde = { workspace = true } -itertools = { workspace = true } -once_cell = { workspace = true } -rand = { workspace = true } -serde = { workspace = true } -serde_utils = { workspace = true } -ssz = { workspace = true } -static_assertions = { workspace = true } -typenum = { workspace = true } -zeroize = { workspace = true } - -[dev-dependencies] -std_ext = { workspace = true } -tap = { workspace = true } \ No newline at end of file diff --git a/bls/bls-blst/src/cached_public_key.rs b/bls/bls-blst/src/cached_public_key.rs deleted file mode 100644 index d48a97f6b..000000000 --- a/bls/bls-blst/src/cached_public_key.rs +++ /dev/null @@ -1,10 +0,0 @@ -use super::{public_key::PublicKey, public_key_bytes::PublicKeyBytes}; - -use bls_core::{impl_cached_public_key, traits::CachedPublicKey as CachedPublicKeyTrait}; - -impl_cached_public_key!( - CachedPublicKeyTrait, - CachedPublicKey, - PublicKeyBytes, - PublicKey -); diff --git a/bls/bls-blst/src/lib.rs b/bls/bls-blst/src/lib.rs deleted file mode 100644 index e72dc5dcc..000000000 --- a/bls/bls-blst/src/lib.rs +++ /dev/null @@ -1,7 +0,0 @@ -pub mod cached_public_key; -pub mod public_key; -pub mod public_key_bytes; -pub mod secret_key; -pub mod secret_key_bytes; -pub mod signature; -pub mod signature_bytes; diff --git a/bls/bls-blst/src/public_key.rs b/bls/bls-blst/src/public_key.rs deleted file mode 100644 index be7289a2f..000000000 --- a/bls/bls-blst/src/public_key.rs +++ /dev/null @@ -1,45 +0,0 @@ -use blst::min_pk::{AggregatePublicKey as RawAggregatePublicKey, PublicKey as RawPublicKey}; -use derive_more::From; - -use bls_core::{error::Error, traits::PublicKey as PublicKeyTrait}; - -use super::public_key_bytes::PublicKeyBytes; - -#[derive(Clone, Copy, PartialEq, Eq, Default, Debug, From)] -pub struct PublicKey(RawPublicKey); - -impl TryFrom for PublicKey { - type Error = Error; - - #[inline] - fn try_from(bytes: PublicKeyBytes) -> Result { - let raw = - RawPublicKey::uncompress(bytes.as_bytes()).map_err(|_| Error::InvalidPublicKey)?; - - // This is needed to pass `fast_aggregate_verify` tests. - // See the following for more information: - // - - // - - raw.validate().map_err(|_| Error::InvalidPublicKey)?; - - Ok(Self(raw)) - } -} - -impl PublicKeyTrait for PublicKey { - type PublicKeyBytes = PublicKeyBytes; - - #[inline] - fn aggregate_in_place(&mut self, other: Self) { - let mut self_aggregate = RawAggregatePublicKey::from_public_key(self.as_raw()); - let other_aggregate = RawAggregatePublicKey::from_public_key(other.as_raw()); - self_aggregate.add_aggregate(&other_aggregate); - self.0 = self_aggregate.to_public_key(); - } -} - -impl PublicKey { - pub(crate) const fn as_raw(&self) -> &RawPublicKey { - &self.0 - } -} diff --git a/bls/bls-blst/src/public_key_bytes.rs b/bls/bls-blst/src/public_key_bytes.rs deleted file mode 100644 index 19fd60a12..000000000 --- a/bls/bls-blst/src/public_key_bytes.rs +++ /dev/null @@ -1,23 +0,0 @@ -use derive_more::derive::AsRef; -use fixed_hash::construct_fixed_hash; -use impl_serde::impl_fixed_hash_serde; - -use bls_core::{impl_public_key_bytes, traits::COMPRESSED_SIZE}; - -use super::public_key::PublicKey; - -construct_fixed_hash! { - #[derive(AsRef)] - pub struct PublicKeyBytes(COMPRESSED_SIZE); -} - -impl_fixed_hash_serde!(PublicKeyBytes, COMPRESSED_SIZE); - -impl_public_key_bytes!(PublicKeyBytes); - -impl From for PublicKeyBytes { - #[inline] - fn from(public_key: PublicKey) -> Self { - Self(public_key.as_raw().compress()) - } -} diff --git a/bls/bls-blst/src/secret_key.rs b/bls/bls-blst/src/secret_key.rs deleted file mode 100644 index d6e08608b..000000000 --- a/bls/bls-blst/src/secret_key.rs +++ /dev/null @@ -1,59 +0,0 @@ -use bls_core::{ - consts::DOMAIN_SEPARATION_TAG, error::Error, impl_secret_key, - traits::SecretKey as SecretKeyTrait, -}; -use blst::min_pk::SecretKey as RawSecretKey; - -use super::{ - public_key::PublicKey, - secret_key_bytes::{SecretKeyBytes, SIZE}, - signature::Signature, -}; - -// `RawSecretKey` already implements `Zeroize` (with `zeroize(drop)`): -// -impl_secret_key!( - SecretKeyTrait, - SecretKey, - RawSecretKey, - SecretKeyBytes, - PublicKey, - Signature -); - -impl TryFrom for SecretKey { - type Error = Error; - - #[inline] - fn try_from(secret_key_bytes: SecretKeyBytes) -> Result { - RawSecretKey::from_bytes(secret_key_bytes.as_ref()) - .map(Self) - .map_err(|_| Error::InvalidSecretKey) - } -} - -impl SecretKeyTrait for SecretKey { - type SecretKeyBytes = SecretKeyBytes; - type PublicKey = PublicKey; - type Signature = Signature; - - #[inline] - #[must_use] - fn to_bytes(&self) -> SecretKeyBytes { - SecretKeyBytes { - bytes: self.as_raw().to_bytes(), - } - } - - #[inline] - fn to_public_key(&self) -> PublicKey { - self.as_raw().sk_to_pk().into() - } - - #[inline] - fn sign(&self, message: impl AsRef<[u8]>) -> Signature { - self.as_raw() - .sign(message.as_ref(), DOMAIN_SEPARATION_TAG, &[]) - .into() - } -} diff --git a/bls/bls-blst/src/secret_key_bytes.rs b/bls/bls-blst/src/secret_key_bytes.rs deleted file mode 100644 index bb96b017e..000000000 --- a/bls/bls-blst/src/secret_key_bytes.rs +++ /dev/null @@ -1,7 +0,0 @@ -use bls_core::impl_secret_key_bytes; - -use super::secret_key::SecretKey; - -pub const SIZE: usize = size_of::(); - -impl_secret_key_bytes!(SecretKeyBytes, SIZE); diff --git a/bls/bls-blst/src/signature.rs b/bls/bls-blst/src/signature.rs deleted file mode 100644 index 9134f9cf4..000000000 --- a/bls/bls-blst/src/signature.rs +++ /dev/null @@ -1,180 +0,0 @@ -use core::num::NonZeroU64; - -use blst::{ - blst_scalar, - min_pk::{AggregateSignature as RawAggregateSignature, Signature as RawSignature}, - BLST_ERROR, -}; -use derive_more::From; -use itertools::Itertools as _; -use rand::Rng as _; - -use bls_core::{ - consts::DOMAIN_SEPARATION_TAG, - error::Error, - traits::{Signature as SignatureTrait, SignatureBytes as SignatureBytesTrait}, -}; - -use super::{public_key::PublicKey, signature_bytes::SignatureBytes}; - -const MULTI_VERIFY_RANDOM_BYTES: usize = size_of::(); -const MULTI_VERIFY_RANDOM_BITS: usize = MULTI_VERIFY_RANDOM_BYTES * 8; - -#[derive(Clone, Copy, PartialEq, Eq, Debug, From)] -pub struct Signature(RawSignature); - -impl Default for Signature { - #[inline] - fn default() -> Self { - SignatureBytes::empty() - .try_into() - .expect("compressed signature constructed in SignatureBytes::empty is valid") - } -} - -impl TryFrom for Signature { - type Error = Error; - - #[inline] - fn try_from(bytes: SignatureBytes) -> Result { - RawSignature::uncompress(bytes.as_bytes()) - .map(Self) - .map_err(|_| Error::InvalidSignature) - } -} - -impl SignatureTrait for Signature { - type SignatureBytes = SignatureBytes; - type PublicKey = PublicKey; - - #[must_use] - fn verify(&self, message: impl AsRef<[u8]>, public_key: Self::PublicKey) -> bool { - let result = self.as_raw().verify( - true, - message.as_ref(), - DOMAIN_SEPARATION_TAG, - &[], - public_key.as_raw(), - false, - ); - - result == BLST_ERROR::BLST_SUCCESS - } - - #[inline] - fn aggregate_in_place(&mut self, other: Self) { - let mut self_aggregate = RawAggregateSignature::from_signature(self.as_raw()); - let other_aggregate = RawAggregateSignature::from_signature(other.as_raw()); - self_aggregate.add_aggregate(&other_aggregate); - self.0 = self_aggregate.to_signature(); - } - - #[must_use] - fn fast_aggregate_verify<'keys>( - &self, - message: impl AsRef<[u8]>, - public_keys: impl IntoIterator, - ) -> bool { - let public_keys = public_keys.into_iter().map(PublicKey::as_raw).collect_vec(); - - let result = self.as_raw().fast_aggregate_verify( - true, - message.as_ref(), - DOMAIN_SEPARATION_TAG, - public_keys.as_slice(), - ); - - result == BLST_ERROR::BLST_SUCCESS - } - - #[must_use] - fn multi_verify<'all>( - messages: impl IntoIterator, - signatures: impl IntoIterator, - public_keys: impl IntoIterator, - ) -> bool { - let messages = messages.into_iter().collect_vec(); - let signatures = signatures.into_iter().map(Self::as_raw).collect_vec(); - let public_keys = public_keys.into_iter().map(PublicKey::as_raw).collect_vec(); - - // `ThreadRng` is cryptographically secure. - let mut rng = rand::thread_rng(); - - let randoms = core::iter::repeat_with(|| { - let mut scalar = blst_scalar::default(); - let nonzero_bytes = rng.gen::().get().to_le_bytes(); - scalar.b[..MULTI_VERIFY_RANDOM_BYTES].copy_from_slice(&nonzero_bytes); - scalar - }) - .take(signatures.len()) - .collect_vec(); - - let result = RawSignature::verify_multiple_aggregate_signatures( - messages.as_slice(), - DOMAIN_SEPARATION_TAG, - public_keys.as_slice(), - false, - signatures.as_slice(), - false, - randoms.as_slice(), - MULTI_VERIFY_RANDOM_BITS, - ); - - result == BLST_ERROR::BLST_SUCCESS - } -} - -impl Signature { - #[must_use] - pub const fn as_raw(&self) -> &RawSignature { - &self.0 - } -} - -#[cfg(test)] -mod tests { - use bls_core::SecretKey as _; - use std_ext::CopyExt as _; - use tap::{Conv as _, TryConv as _}; - - use crate::{secret_key::SecretKey, secret_key_bytes::SecretKeyBytes}; - - use super::*; - - const MESSAGE: &str = "foo"; - - #[test] - fn signature_verify_succeeds_on_correct_triple() { - let secret_key = secret_key(); - let public_key = SecretKey::to_public_key(&secret_key); - let signature = SecretKey::sign(&secret_key, MESSAGE); - - assert!(Signature::verify(&signature, MESSAGE, public_key)); - } - - #[test] - fn signature_verify_fails_on_incorrect_public_key() { - let secret_key = secret_key(); - let public_key = PublicKey::default(); - let signature = SecretKey::sign(&secret_key, MESSAGE); - - assert!(!Signature::verify(&signature, MESSAGE, public_key)); - } - - #[test] - fn signature_verify_fails_on_incorrect_signature() { - let secret_key = secret_key(); - let public_key = SecretKey::to_public_key(&secret_key); - let signature = Signature::default(); - - assert!(!Signature::verify(&signature, MESSAGE, public_key)); - } - - fn secret_key() -> SecretKey { - b"????????????????????????????????" - .copy() - .conv::() - .try_conv::() - .expect("bytes encode a valid secret key") - } -} diff --git a/bls/bls-blst/src/signature_bytes.rs b/bls/bls-blst/src/signature_bytes.rs deleted file mode 100644 index eb93121e0..000000000 --- a/bls/bls-blst/src/signature_bytes.rs +++ /dev/null @@ -1,20 +0,0 @@ -use derive_more::AsRef; -use fixed_hash::construct_fixed_hash; -use impl_serde::impl_fixed_hash_serde; -use typenum::Unsigned as _; - -use bls_core::{ - impl_signature_bytes, - traits::{CompressedSize, SignatureBytes as SignatureBytesTrait}, -}; - -use super::signature::Signature; - -impl_signature_bytes!(SignatureBytesTrait, SignatureBytes, CompressedSize::USIZE); - -impl From for SignatureBytes { - #[inline] - fn from(signature: Signature) -> Self { - Self(signature.as_raw().compress()) - } -} diff --git a/bls/bls-core/Cargo.toml b/bls/bls-core/Cargo.toml deleted file mode 100644 index 7a45e27f0..000000000 --- a/bls/bls-core/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "bls-core" -edition = { workspace = true } - -[dependencies] -derivative = { workspace = true } -hex = { workspace = true } -once_cell = { workspace = true } -serde = { workspace = true } -ssz = { workspace = true } -static_assertions = { workspace = true } -thiserror = { workspace = true } -typenum = { workspace = true } -zeroize = { workspace = true } \ No newline at end of file diff --git a/bls/bls-core/src/lib.rs b/bls/bls-core/src/lib.rs deleted file mode 100644 index cf5e799da..000000000 --- a/bls/bls-core/src/lib.rs +++ /dev/null @@ -1,7 +0,0 @@ -pub mod consts; -pub mod error; -pub mod traits; - -pub use consts::*; -pub use error::*; -pub use traits::*; diff --git a/bls/bls-core/src/traits/cached_public_key.rs b/bls/bls-core/src/traits/cached_public_key.rs deleted file mode 100644 index e7fc6f2e2..000000000 --- a/bls/bls-core/src/traits/cached_public_key.rs +++ /dev/null @@ -1,150 +0,0 @@ -use core::fmt::Debug; -use serde::{Deserialize, Serialize}; -use ssz::{SszHash, SszRead, SszSize, SszWrite}; - -use crate::error::Error; - -use super::{PublicKey as PublicKeyTrait, PublicKeyBytes as PublicKeyBytesTrait}; - -#[expect(clippy::trait_duplication_in_bounds)] -pub trait CachedPublicKey: - Default - + Debug - + Deserialize<'static> - + Serialize - + Clone - + From - + From - + PartialEq - + Eq - + SszSize - + SszRead - + SszWrite - + SszHash -{ - type PublicKeyBytes: PublicKeyBytesTrait; - type PublicKey: PublicKeyTrait; - - fn new(bytes: Self::PublicKeyBytes, public_key: Self::PublicKey) -> Self; - fn as_bytes(&self) -> &Self::PublicKeyBytes; - fn to_bytes(&self) -> Self::PublicKeyBytes; - fn decompress(&self) -> Result<&Self::PublicKey, Error>; -} - -#[expect(clippy::module_name_repetitions)] -#[macro_export] -macro_rules! impl_cached_public_key { - ($trait:ty, $name:ident, $pkb:ty, $pk:ty) => { - #[derive(Default, derivative::Derivative, serde::Deserialize, serde::Serialize)] - #[derivative(PartialEq, Eq)] - #[serde(transparent)] - pub struct $name { - bytes: $pkb, - #[derivative(PartialEq = "ignore")] - #[serde(skip)] - decompressed: once_cell::race::OnceBox<$pk>, - } - - // `OnceBox` does not implement `Clone`. - impl Clone for $name { - fn clone(&self) -> Self { - let Self { - bytes, - ref decompressed, - } = *self; - match decompressed.get().copied() { - Some(public_key) => Self::new(bytes, public_key), - None => bytes.into(), - } - } - } - - impl From<$pkb> for $name { - #[inline] - fn from(bytes: $pkb) -> Self { - Self { - bytes, - decompressed: once_cell::race::OnceBox::new(), - } - } - } - - impl From<$pk> for $name { - #[inline] - fn from(public_key: $pk) -> Self { - Self::new(public_key.into(), public_key) - } - } - - impl ssz::SszSize for $name { - const SIZE: ssz::Size = <$pkb>::SIZE; - } - - impl ssz::SszRead for $name { - #[inline] - fn from_ssz_unchecked(_context: &C, bytes: &[u8]) -> Result { - Ok(Self { - bytes: <$pkb as ssz::SszReadDefault>::from_ssz_default(bytes)?, - decompressed: once_cell::race::OnceBox::new(), - }) - } - } - - impl ssz::SszWrite for $name { - #[inline] - fn write_fixed(&self, bytes: &mut [u8]) { - self.bytes.write_fixed(bytes); - } - } - - impl ssz::SszHash for $name { - type PackingFactor = <$pkb as ssz::SszHash>::PackingFactor; - - #[inline] - fn hash_tree_root(&self) -> ssz::H256 { - self.bytes.hash_tree_root() - } - } - - impl std::fmt::Debug for $name { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("CachedPublicKey") - .field("bytes", &self.bytes) - .finish() - } - } - - impl $trait for $name { - type PublicKeyBytes = $pkb; - type PublicKey = $pk; - - fn new(bytes: Self::PublicKeyBytes, public_key: Self::PublicKey) -> Self { - let decompressed = once_cell::race::OnceBox::new(); - decompressed - .set(Box::new(public_key)) - .expect("decompressed is empty because OnceBox::new returns an empty cell"); - - Self { - bytes, - decompressed, - } - } - - #[inline] - fn as_bytes(&self) -> &Self::PublicKeyBytes { - &self.bytes - } - - #[inline] - fn to_bytes(&self) -> Self::PublicKeyBytes { - self.bytes - } - - #[inline] - fn decompress(&self) -> Result<&Self::PublicKey, bls_core::error::Error> { - self.decompressed - .get_or_try_init(|| self.bytes.try_into().map(Box::new)) - } - } - }; -} diff --git a/bls/bls-core/src/traits/mod.rs b/bls/bls-core/src/traits/mod.rs deleted file mode 100644 index 47f79b6c7..000000000 --- a/bls/bls-core/src/traits/mod.rs +++ /dev/null @@ -1,15 +0,0 @@ -mod cached_public_key; -mod public_key; -mod public_key_bytes; -mod secret_key; -mod secret_key_bytes; -mod signature; -mod signature_bytes; - -pub use cached_public_key::*; -pub use public_key::*; -pub use public_key_bytes::*; -pub use secret_key::*; -pub use secret_key_bytes::*; -pub use signature::*; -pub use signature_bytes::*; diff --git a/bls/bls-core/src/traits/public_key.rs b/bls/bls-core/src/traits/public_key.rs deleted file mode 100644 index d39375315..000000000 --- a/bls/bls-core/src/traits/public_key.rs +++ /dev/null @@ -1,26 +0,0 @@ -use core::fmt::Debug; - -use crate::error::Error; - -use super::PublicKeyBytes as PublicKeyBytesTrait; - -pub trait PublicKey: - Clone + Copy + PartialEq + Eq + Default + Debug + TryFrom -{ - type PublicKeyBytes: PublicKeyBytesTrait; - - /// [`eth_aggregate_pubkeys`](https://github.com/ethereum/consensus-specs/blob/86fb82b221474cc89387fa6436806507b3849d88/specs/altair/bls.md#eth_aggregate_pubkeys) - fn aggregate_nonempty(keys: impl IntoIterator) -> Result { - keys.into_iter() - .reduce(Self::aggregate) - .ok_or(Error::NoPublicKeysToAggregate) - } - - #[must_use] - fn aggregate(mut self, other: Self) -> Self { - self.aggregate_in_place(other); - self - } - - fn aggregate_in_place(&mut self, other: Self); -} diff --git a/bls/bls-core/src/traits/public_key_bytes.rs b/bls/bls-core/src/traits/public_key_bytes.rs deleted file mode 100644 index c9215873c..000000000 --- a/bls/bls-core/src/traits/public_key_bytes.rs +++ /dev/null @@ -1,76 +0,0 @@ -use core::{fmt::Debug, str::FromStr}; -use hex::FromHex; -use ssz::{SszHash, SszRead, SszSize, SszWrite}; - -use super::PublicKey as PublicKeyTrait; - -pub const COMPRESSED_SIZE: usize = 48; - -pub trait PublicKeyBytes: - AsRef<[u8]> - + AsMut<[u8]> - + Copy - + Clone - + Send - + Sync - + Default - + PartialEq - + Eq - + Debug - + FromStr - + FromHex - + From - + SszSize - + SszRead - + SszWrite - + SszHash -{ - type PublicKey: PublicKeyTrait; -} - -#[expect(clippy::module_name_repetitions)] -#[macro_export] -macro_rules! impl_public_key_bytes { - ($name:ident) => { - impl hex::FromHex for $name { - type Error = <[u8; $crate::traits::COMPRESSED_SIZE] as hex::FromHex>::Error; - - fn from_hex>(digits: T) -> Result { - hex::FromHex::from_hex(digits).map(Self) - } - } - - impl ssz::SszSize for $name { - const SIZE: ssz::Size = ssz::Size::Fixed { - size: $crate::traits::COMPRESSED_SIZE, - }; - } - - impl ssz::SszRead for $name { - #[inline] - fn from_ssz_unchecked(_context: &C, bytes: &[u8]) -> Result { - Ok(Self::from_slice(bytes)) - } - } - - impl ssz::SszWrite for $name { - #[inline] - fn write_fixed(&self, bytes: &mut [u8]) { - bytes.copy_from_slice(self.as_bytes()); - } - } - - impl ssz::SszHash for $name { - type PackingFactor = typenum::U1; - - #[inline] - fn hash_tree_root(&self) -> ssz::H256 { - ssz::MerkleTree::>::merkleize_bytes(self) - } - } - - impl $crate::traits::PublicKeyBytes for $name { - type PublicKey = PublicKey; - } - }; -} diff --git a/bls/bls-core/src/traits/secret_key.rs b/bls/bls-core/src/traits/secret_key.rs deleted file mode 100644 index 4235fb7fc..000000000 --- a/bls/bls-core/src/traits/secret_key.rs +++ /dev/null @@ -1,58 +0,0 @@ -use core::{fmt::Debug, hash::Hash}; - -use super::{ - PublicKey as PublicKeyTrait, SecretKeyBytes as SecretKeyBytesTrait, Signature as SignatureTrait, -}; - -pub trait SecretKey: Debug + PartialEq + Eq + Hash { - type SecretKeyBytes: SecretKeyBytesTrait; - type PublicKey: PublicKeyTrait; - type Signature: SignatureTrait; - - fn to_public_key(&self) -> Self::PublicKey; - fn sign(&self, message: impl AsRef<[u8]>) -> Self::Signature; - fn to_bytes(&self) -> Self::SecretKeyBytes; -} - -#[expect(clippy::module_name_repetitions)] -#[macro_export] -macro_rules! impl_secret_key { - ($trait:ty, $name:ident, $raw:ty, $skb:ty, $pk:ty, $sig:ty) => { - #[derive(derive_more::Debug)] - // Inspired by `DebugSecret` from the `secrecy` crate. - #[debug("[REDACTED]")] - pub struct $name($raw); - - - // Prevent `SecretKey` from implementing some traits to avoid leaking secret keys. - // This could also be done by wrapping it in `secrecy::Secret`. - static_assertions::assert_not_impl_any! { - $name: - Clone, Copy, core::ops::Deref, ToOwned, - core::fmt::Binary, core::fmt::Display, core::fmt::LowerExp, core::fmt::LowerHex, core::fmt::Octal, - core::fmt::Pointer, core::fmt::UpperExp, core::fmt::UpperHex, - serde::Serialize, ssz::SszHash, ssz::SszWrite, - } - - impl PartialEq for $name { - #[inline] - fn eq(&self, other: &Self) -> bool { - self.as_raw().to_bytes() == other.as_raw().to_bytes() - } - } - - impl Eq for $name {} - - impl core::hash::Hash for $name { - fn hash(&self, hasher: &mut H) { - self.as_raw().to_bytes().hash(hasher) - } - } - - impl $name { - const fn as_raw(&self) -> &$raw { - &self.0 - } - } - }; -} diff --git a/bls/bls-core/src/traits/secret_key_bytes.rs b/bls/bls-core/src/traits/secret_key_bytes.rs deleted file mode 100644 index 03978509d..000000000 --- a/bls/bls-core/src/traits/secret_key_bytes.rs +++ /dev/null @@ -1,85 +0,0 @@ -use hex::FromHex; -use serde::Deserialize; -use ssz::{SszRead, SszSize, SszWrite}; -use zeroize::{Zeroize, ZeroizeOnDrop}; - -pub trait SecretKeyBytes: - Default - + AsRef<[u8]> - + AsMut<[u8]> - + From<[u8; N]> - + Zeroize - + ZeroizeOnDrop - + Deserialize<'static> - + FromHex - + SszSize - + SszRead - + SszWrite -{ -} - -#[expect(clippy::module_name_repetitions)] -#[macro_export] -macro_rules! impl_secret_key_bytes { - ($name:ident, $size:expr) => { - // Unlike public keys and signatures, secret keys are not compressed. - #[derive( - Default, - derive_more::AsRef, - derive_more::AsMut, - derive_more::From, - zeroize::Zeroize, - zeroize::ZeroizeOnDrop, - serde::Deserialize, - )] - #[as_ref(forward)] - #[as_mut(forward)] - #[serde(transparent)] - pub struct $name { - #[serde(with = "serde_utils::prefixed_hex_or_bytes_array")] - pub(crate) bytes: [u8; $size], - } - - // Prevent `SecretKeyBytes` from implementing some traits to avoid leaking secret keys. - // This could also be done by wrapping it in `secrecy::Secret`. - static_assertions::assert_not_impl_any! { - $name: - Clone, Copy, core::ops::Deref, std::borrow::ToOwned, - core::fmt::Debug, core::fmt::Binary, core::fmt::Display, - core::fmt::LowerExp, core::fmt::LowerHex, core::fmt::Octal, - core::fmt::Pointer, core::fmt::UpperExp, core::fmt::UpperHex, - serde::Serialize, ssz::SszHash, - } - - impl hex::FromHex for $name { - type Error = <[u8; $size] as hex::FromHex>::Error; - - fn from_hex>(digits: T) -> Result { - let bytes = hex::FromHex::from_hex(digits)?; - Ok(Self { bytes }) - } - } - - impl ssz::SszSize for $name { - const SIZE: ssz::Size = ssz::Size::Fixed { size: $size }; - } - - impl ssz::SszRead for $name { - #[inline] - fn from_ssz_unchecked(_context: &C, bytes: &[u8]) -> Result { - let mut secret_key = Self::default(); - secret_key.bytes.copy_from_slice(bytes); - Ok(secret_key) - } - } - - impl ssz::SszWrite for $name { - #[inline] - fn write_fixed(&self, bytes: &mut [u8]) { - bytes.copy_from_slice(&self.bytes); - } - } - - impl $crate::traits::SecretKeyBytes<$size> for $name {} - }; -} diff --git a/bls/bls-core/src/traits/signature.rs b/bls/bls-core/src/traits/signature.rs deleted file mode 100644 index 8a701c3a1..000000000 --- a/bls/bls-core/src/traits/signature.rs +++ /dev/null @@ -1,33 +0,0 @@ -use core::fmt::Debug; - -use super::{PublicKey as PublicKeyTrait, SignatureBytes as SignatureBytesTrait}; - -pub trait Signature: Clone + Copy + PartialEq + Eq + Debug + Default + 'static -where - Self::PublicKey: 'static, -{ - type SignatureBytes: SignatureBytesTrait; - type PublicKey: PublicKeyTrait; - - fn verify(&self, message: impl AsRef<[u8]>, public_key: Self::PublicKey) -> bool; - - #[must_use] - fn aggregate(mut self, other: Self) -> Self { - self.aggregate_in_place(other); - self - } - - fn aggregate_in_place(&mut self, other: Self); - - fn fast_aggregate_verify<'keys>( - &self, - message: impl AsRef<[u8]>, - public_keys: impl IntoIterator, - ) -> bool; - - fn multi_verify<'all>( - messages: impl IntoIterator, - signatures: impl IntoIterator, - public_keys: impl IntoIterator, - ) -> bool; -} diff --git a/bls/bls-core/src/traits/signature_bytes.rs b/bls/bls-core/src/traits/signature_bytes.rs deleted file mode 100644 index 061ce4312..000000000 --- a/bls/bls-core/src/traits/signature_bytes.rs +++ /dev/null @@ -1,80 +0,0 @@ -use core::{fmt::Debug, str::FromStr}; -use ssz::{SszHash, SszRead, SszSize, SszWrite}; -use typenum::U96; - -pub type CompressedSize = U96; - -pub trait SignatureBytes: - AsRef<[u8]> - + AsMut<[u8]> - + Copy - + Clone - + Send - + Sync - + Default - + PartialEq - + Eq - + Debug - + FromStr - + SszSize - + SszRead - + SszWrite - + SszHash -{ - fn empty() -> Self; - fn is_empty(self) -> bool; -} - -#[expect(clippy::module_name_repetitions)] -#[macro_export] -macro_rules! impl_signature_bytes { - ($trait:ident, $name:ident, $size:expr) => { - construct_fixed_hash! { - #[derive(derive_more::AsRef)] - pub struct $name($size); - } - - impl_fixed_hash_serde!($name, $size); - - impl ssz::SszSize for $name { - const SIZE: ssz::Size = ssz::Size::Fixed { size: $size }; - } - - impl ssz::SszRead for $name { - #[inline] - fn from_ssz_unchecked(_context: &C, bytes: &[u8]) -> Result { - Ok(Self::from_slice(bytes)) - } - } - - impl ssz::SszWrite for $name { - #[inline] - fn write_fixed(&self, bytes: &mut [u8]) { - bytes.copy_from_slice(self.as_bytes()); - } - } - - impl ssz::SszHash for $name { - type PackingFactor = typenum::U1; - - #[inline] - fn hash_tree_root(&self) -> ssz::H256 { - ssz::MerkleTree::>::merkleize_bytes(self) - } - } - - impl $trait for $name { - #[inline] - fn empty() -> Self { - let mut bytes = Self::zero(); - bytes.as_mut()[0] = 0xc0; - bytes - } - - #[inline] - fn is_empty(self) -> bool { - self == Self::empty() - } - } - }; -} diff --git a/bls/bls-zkcrypto/Cargo.toml b/bls/bls-zkcrypto/Cargo.toml deleted file mode 100644 index 8fe01b737..000000000 --- a/bls/bls-zkcrypto/Cargo.toml +++ /dev/null @@ -1,23 +0,0 @@ -[package] -name = "bls-zkcrypto" -edition = { workspace = true } - -[dependencies] -bls-core = { workspace = true } -bls12_381 = { workspace = true, features = ["default", "experimental"] } -derivative = { workspace = true } -derive_more = { workspace = true } -hex = { workspace = true } -once_cell = { workspace = true } -ff = { workspace = true } -fixed-hash = { workspace = true } -itertools = { workspace = true } -impl-serde = { workspace = true } -rand = { workspace = true } -serde = { workspace = true } -serde_utils = { workspace = true } -sha2 = { workspace = true } -ssz = { workspace = true } -static_assertions = { workspace = true } -typenum = { workspace = true } -zeroize = { workspace = true } \ No newline at end of file diff --git a/bls/bls-zkcrypto/src/cached_public_key.rs b/bls/bls-zkcrypto/src/cached_public_key.rs deleted file mode 100644 index a3a279dca..000000000 --- a/bls/bls-zkcrypto/src/cached_public_key.rs +++ /dev/null @@ -1,10 +0,0 @@ -use bls_core::{impl_cached_public_key, CachedPublicKey as CachedPublicKeyTrait}; - -use super::{public_key::PublicKey, public_key_bytes::PublicKeyBytes}; - -impl_cached_public_key!( - CachedPublicKeyTrait, - CachedPublicKey, - PublicKeyBytes, - PublicKey -); diff --git a/bls/bls-zkcrypto/src/lib.rs b/bls/bls-zkcrypto/src/lib.rs deleted file mode 100644 index e72dc5dcc..000000000 --- a/bls/bls-zkcrypto/src/lib.rs +++ /dev/null @@ -1,7 +0,0 @@ -pub mod cached_public_key; -pub mod public_key; -pub mod public_key_bytes; -pub mod secret_key; -pub mod secret_key_bytes; -pub mod signature; -pub mod signature_bytes; diff --git a/bls/bls-zkcrypto/src/public_key.rs b/bls/bls-zkcrypto/src/public_key.rs deleted file mode 100644 index 80b3aeb0f..000000000 --- a/bls/bls-zkcrypto/src/public_key.rs +++ /dev/null @@ -1,51 +0,0 @@ -use bls12_381::{G1Affine, G1Projective}; -use derive_more::From; - -use bls_core::{error::Error, traits::PublicKey as PublicKeyTrait}; - -use super::public_key_bytes::PublicKeyBytes; - -#[derive(Clone, Copy, PartialEq, Eq, Debug, From)] -pub struct PublicKey(G1Projective); - -impl Default for PublicKey { - #[inline] - fn default() -> Self { - Self(G1Projective::identity()) - } -} - -impl TryFrom for PublicKey { - type Error = Error; - - #[inline] - fn try_from(bytes: PublicKeyBytes) -> Result { - let point: G1Affine = Option::from(G1Affine::from_compressed(bytes.as_ref())) - .ok_or(Error::DecompressionFailed)?; - - if bool::from(point.is_identity()) { - return Err(Error::InvalidPublicKey); - } - - if !bool::from(point.is_torsion_free()) { - return Err(Error::DecompressionFailed); - } - - Ok(Self(point.into())) - } -} - -impl PublicKeyTrait for PublicKey { - type PublicKeyBytes = PublicKeyBytes; - - #[inline] - fn aggregate_in_place(&mut self, other: Self) { - self.0 = self.as_raw().add(other.as_raw()); - } -} - -impl PublicKey { - pub(crate) const fn as_raw(&self) -> &G1Projective { - &self.0 - } -} diff --git a/bls/bls-zkcrypto/src/public_key_bytes.rs b/bls/bls-zkcrypto/src/public_key_bytes.rs deleted file mode 100644 index 561152878..000000000 --- a/bls/bls-zkcrypto/src/public_key_bytes.rs +++ /dev/null @@ -1,23 +0,0 @@ -use bls12_381::G1Affine; -use bls_core::{impl_public_key_bytes, COMPRESSED_SIZE}; -use derive_more::derive::AsRef; -use fixed_hash::construct_fixed_hash; -use impl_serde::impl_fixed_hash_serde; - -use super::public_key::PublicKey; - -construct_fixed_hash! { - #[derive(AsRef)] - pub struct PublicKeyBytes(COMPRESSED_SIZE); -} - -impl_fixed_hash_serde!(PublicKeyBytes, COMPRESSED_SIZE); - -impl_public_key_bytes!(PublicKeyBytes); - -impl From for PublicKeyBytes { - #[inline] - fn from(public_key: PublicKey) -> Self { - Self(G1Affine::from(public_key.as_raw()).to_compressed()) - } -} diff --git a/bls/bls-zkcrypto/src/secret_key.rs b/bls/bls-zkcrypto/src/secret_key.rs deleted file mode 100644 index 348b3709b..000000000 --- a/bls/bls-zkcrypto/src/secret_key.rs +++ /dev/null @@ -1,80 +0,0 @@ -use bls12_381::{ - hash_to_curve::{ExpandMsgXmd, HashToCurve}, - G1Projective, G2Projective, Scalar, -}; -use bls_core::{ - consts::DOMAIN_SEPARATION_TAG, error::Error, impl_secret_key, - traits::SecretKey as SecretKeyTrait, -}; -use sha2::Sha256; - -use super::{ - public_key::PublicKey, - secret_key_bytes::{SecretKeyBytes, SIZE}, - signature::Signature, -}; - -impl_secret_key!( - SecretKeyTrait, - SecretKey, - Scalar, - SecretKeyBytes, - PublicKey, - Signature -); - -impl TryFrom for SecretKey { - type Error = Error; - - #[inline] - fn try_from(secret_key_bytes: SecretKeyBytes) -> Result { - if secret_key_bytes.bytes.iter().all(|&b| b == 0) { - return Err(Error::InvalidSecretKey); - } - - let mut le_bytes = secret_key_bytes.bytes; - le_bytes.reverse(); - - let scalar = match Option::from(Scalar::from_bytes(&le_bytes)) { - Some(scalar) => scalar, - None => { - return Err(Error::InvalidSecretKey); - } - }; - Ok(Self(scalar)) - } -} - -impl SecretKeyTrait for SecretKey { - type SecretKeyBytes = SecretKeyBytes; - type PublicKey = PublicKey; - type Signature = Signature; - - #[inline] - #[must_use] - fn to_bytes(&self) -> SecretKeyBytes { - let mut bytes = self.as_raw().to_bytes(); - bytes.reverse(); - - SecretKeyBytes { bytes } - } - - #[inline] - #[must_use] - fn to_public_key(&self) -> PublicKey { - let point = G1Projective::generator() * self.as_raw(); - PublicKey::from(point) - } - - #[inline] - #[must_use] - fn sign(&self, message: impl AsRef<[u8]>) -> Signature { - let h = >>::hash_to_curve( - [message.as_ref()], - DOMAIN_SEPARATION_TAG, - ); - let signature = h * self.as_raw(); - - Signature::from(signature) - } -} diff --git a/bls/bls-zkcrypto/src/secret_key_bytes.rs b/bls/bls-zkcrypto/src/secret_key_bytes.rs deleted file mode 100644 index 37a0cfddb..000000000 --- a/bls/bls-zkcrypto/src/secret_key_bytes.rs +++ /dev/null @@ -1,7 +0,0 @@ -use bls_core::impl_secret_key_bytes; - -use crate::secret_key::SecretKey; - -pub const SIZE: usize = size_of::(); - -impl_secret_key_bytes!(SecretKeyBytes, SIZE); diff --git a/bls/bls-zkcrypto/src/signature.rs b/bls/bls-zkcrypto/src/signature.rs deleted file mode 100644 index 3e0026c08..000000000 --- a/bls/bls-zkcrypto/src/signature.rs +++ /dev/null @@ -1,176 +0,0 @@ -use bls12_381::{ - hash_to_curve::{ExpandMsgXmd, HashToCurve}, - pairing, G1Affine, G1Projective, G2Affine, G2Projective, Scalar, -}; -use bls_core::{consts::DOMAIN_SEPARATION_TAG, error::Error, traits::Signature as SignatureTrait}; -use derive_more::From; -use ff::Field; -use itertools::Itertools as _; -use rand::thread_rng; -use sha2::Sha256; - -use super::{public_key::PublicKey, signature_bytes::SignatureBytes}; - -#[derive(Clone, Copy, PartialEq, Eq, Debug, From)] -pub struct Signature(G2Projective); - -impl Default for Signature { - #[inline] - fn default() -> Self { - Self(G2Projective::identity()) - } -} - -impl TryFrom for Signature { - type Error = Error; - - #[inline] - fn try_from(bytes: SignatureBytes) -> Result { - let point: G2Affine = Option::from(G2Affine::from_compressed(bytes.as_ref())) - .ok_or(Error::DecompressionFailed)?; - - if !bool::from(point.is_torsion_free()) { - return Err(Error::DecompressionFailed); - } - - Ok(Self(point.into())) - } -} - -impl SignatureTrait for Signature { - type SignatureBytes = SignatureBytes; - type PublicKey = PublicKey; - - #[must_use] - fn verify(&self, message: impl AsRef<[u8]>, public_key: Self::PublicKey) -> bool { - let h = >>::hash_to_curve( - [message.as_ref()], - DOMAIN_SEPARATION_TAG, - ); - - let gt1 = pairing(&G1Affine::from(public_key.as_raw()), &G2Affine::from(h)); - let gt2 = pairing(&G1Affine::generator(), &G2Affine::from(self.as_raw())); - - gt1 == gt2 - } - - #[inline] - fn aggregate_in_place(&mut self, other: Self) { - self.0 = self.as_raw().add(other.as_raw()); - } - - #[must_use] - fn fast_aggregate_verify<'keys>( - &self, - message: impl AsRef<[u8]>, - public_keys: impl IntoIterator, - ) -> bool { - if bool::from(self.as_raw().is_identity()) { - return false; - } - - let agg_pk = public_keys - .into_iter() - .fold(G1Projective::identity(), |acc, pk| acc + pk.as_raw()); - - let h = >>::hash_to_curve( - [message.as_ref()], - DOMAIN_SEPARATION_TAG, - ); - - pairing(&agg_pk.into(), &h.into()) == pairing(&G1Affine::generator(), &self.as_raw().into()) - } - - #[must_use] - fn multi_verify<'all>( - messages: impl IntoIterator, - signatures: impl IntoIterator, - public_keys: impl IntoIterator, - ) -> bool { - let mut rng = thread_rng(); - - let msgs: Vec<&[u8]> = messages.into_iter().collect_vec(); - let sigs: Vec<&G2Projective> = signatures.into_iter().map(Self::as_raw).collect_vec(); - let pks: Vec<&G1Projective> = public_keys.into_iter().map(PublicKey::as_raw).collect_vec(); - - if msgs.len() != sigs.len() || sigs.len() != pks.len() { - return false; - } - - if sigs.iter().any(|sig| bool::from(sig.is_identity())) { - return false; - } - - let rand_scalars: Vec = (0..sigs.len()) - .map(|_| Scalar::random(&mut rng)) - .collect_vec(); - - let mut agg_sig = G2Projective::identity(); - let mut lhs = pairing(&G1Affine::generator(), &G2Affine::identity()); - - for i in 0..sigs.len() { - let h = >>::hash_to_curve( - [msgs[i]], - DOMAIN_SEPARATION_TAG, - ); - - agg_sig += sigs[i] * rand_scalars[i]; - - lhs += pairing(&(pks[i] * rand_scalars[i]).into(), &h.into()); - } - - lhs == pairing(&G1Affine::generator(), &agg_sig.into()) - } -} - -impl Signature { - pub(crate) const fn as_raw(&self) -> &G2Projective { - &self.0 - } -} - -#[cfg(test)] -mod tests { - use bls_core::SecretKey as _; - - use crate::{secret_key::SecretKey, secret_key_bytes::SecretKeyBytes}; - - use super::*; - - const MESSAGE: &str = "foo"; - - #[test] - fn signature_verify_succeeds_on_correct_triple() { - let secret_key = secret_key(); - let public_key = SecretKey::to_public_key(&secret_key); - let signature = SecretKey::sign(&secret_key, MESSAGE); - - assert!(Signature::verify(&signature, MESSAGE, public_key)); - } - - #[test] - fn signature_verify_fails_on_incorrect_public_key() { - let secret_key = secret_key(); - let public_key = PublicKey::default(); - let signature = SecretKey::sign(&secret_key, MESSAGE); - - assert!(!Signature::verify(&signature, MESSAGE, public_key)); - } - - #[test] - fn signature_verify_fails_on_incorrect_signature() { - let secret_key = secret_key(); - let public_key = SecretKey::to_public_key(&secret_key); - let signature = Signature::default(); - - assert!(!Signature::verify(&signature, MESSAGE, public_key)); - } - - fn secret_key() -> SecretKey { - let bytes = b"????????????????????????????????"; - SecretKey::try_from(SecretKeyBytes { - bytes: bytes.to_owned(), - }) - .expect("bytes encode a valid secret key") - } -} diff --git a/bls/bls-zkcrypto/src/signature_bytes.rs b/bls/bls-zkcrypto/src/signature_bytes.rs deleted file mode 100644 index da052b165..000000000 --- a/bls/bls-zkcrypto/src/signature_bytes.rs +++ /dev/null @@ -1,21 +0,0 @@ -use bls12_381::G2Affine; -use derive_more::AsRef; -use fixed_hash::construct_fixed_hash; -use impl_serde::impl_fixed_hash_serde; -use typenum::Unsigned as _; - -use bls_core::{ - impl_signature_bytes, - traits::{CompressedSize, SignatureBytes as SignatureBytesTrait}, -}; - -use super::signature::Signature; - -impl_signature_bytes!(SignatureBytesTrait, SignatureBytes, CompressedSize::USIZE); - -impl From for SignatureBytes { - #[inline] - fn from(signature: Signature) -> Self { - Self(G2Affine::from(signature.as_raw()).to_compressed()) - } -} diff --git a/bls/src/backend.rs b/bls/src/backend.rs new file mode 100644 index 000000000..a3e417dbf --- /dev/null +++ b/bls/src/backend.rs @@ -0,0 +1,6 @@ +#[derive(Clone, Copy, Debug, Default)] +pub enum Backend { + #[cfg(feature = "blst")] + #[default] + Blst, +} diff --git a/bls/src/cached_public_key.rs b/bls/src/cached_public_key.rs new file mode 100644 index 000000000..091e99d76 --- /dev/null +++ b/bls/src/cached_public_key.rs @@ -0,0 +1,115 @@ +use core::fmt::Debug; +use once_cell::race::OnceBox; +use serde::{Deserialize, Serialize}; +use ssz::{SszHash, SszRead, SszSize, SszWrite}; + +use crate::{backend::Backend, error::Error, PublicKey}; + +use super::public_key_bytes::PublicKeyBytes; + +#[derive(Default, derivative::Derivative, Deserialize, Serialize)] +#[derivative(PartialEq, Eq)] +#[serde(transparent)] +pub struct CachedPublicKey { + bytes: PublicKeyBytes, + + #[derivative(PartialEq = "ignore")] + #[serde(skip)] + decompressed: OnceBox, +} + +impl From for CachedPublicKey { + #[inline] + fn from(bytes: PublicKeyBytes) -> Self { + Self { + bytes, + decompressed: once_cell::race::OnceBox::new(), + } + } +} + +impl From for CachedPublicKey { + #[inline] + fn from(public_key: PublicKey) -> Self { + Self::new(public_key.into(), public_key) + } +} + +impl Clone for CachedPublicKey { + fn clone(&self) -> Self { + let Self { + bytes, + ref decompressed, + } = *self; + match decompressed.get().copied() { + Some(public_key) => Self::new(bytes, public_key), + None => bytes.into(), + } + } +} + +impl CachedPublicKey { + pub fn new(bytes: PublicKeyBytes, public_key: PublicKey) -> Self { + let decompressed = once_cell::race::OnceBox::new(); + decompressed + .set(Box::new(public_key)) + .expect("decompressed is empty because OnceBox::new returns an empty cell"); + + Self { + bytes, + decompressed, + } + } + + pub fn as_bytes(&self) -> &PublicKeyBytes { + &self.bytes + } + + pub fn to_bytes(&self) -> PublicKeyBytes { + self.bytes + } + + pub fn decompress(&self, backend: Backend) -> Result<&PublicKey, Error> { + self.decompressed.get_or_try_init(|| match backend { + Backend::Blst => PublicKey::try_from_with_backend(self.bytes, backend).map(Box::new), + }) + } +} + +impl Debug for CachedPublicKey { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("CachedPublicKey") + .field("bytes", &self.bytes) + .finish() + } +} + +impl SszSize for CachedPublicKey { + const SIZE: ssz::Size = PublicKeyBytes::SIZE; +} + +impl SszRead for CachedPublicKey { + #[inline] + fn from_ssz_unchecked(_context: &C, bytes: &[u8]) -> Result { + Ok(Self { + bytes: ::from_ssz_default(bytes)?, + decompressed: once_cell::race::OnceBox::new(), + }) + } +} + +impl SszWrite for CachedPublicKey { + #[inline] + fn write_fixed(&self, bytes: &mut [u8]) { + self.bytes.write_fixed(bytes); + } +} + +impl SszHash for CachedPublicKey { + type PackingFactor = ::PackingFactor; + + #[inline] + fn hash_tree_root(&self) -> ssz::H256 { + self.bytes.hash_tree_root() + } +} diff --git a/bls/bls-core/src/consts.rs b/bls/src/consts.rs similarity index 100% rename from bls/bls-core/src/consts.rs rename to bls/src/consts.rs diff --git a/bls/bls-core/src/error.rs b/bls/src/error.rs similarity index 100% rename from bls/bls-core/src/error.rs rename to bls/src/error.rs diff --git a/bls/src/lib.rs b/bls/src/lib.rs index 1f67bbbc1..322235b53 100644 --- a/bls/src/lib.rs +++ b/bls/src/lib.rs @@ -1,23 +1,29 @@ -pub use bls_core::*; +pub mod consts; +pub mod error; -macro_rules! implement_backend { - ($backend:path) => { - pub use $backend::{ - cached_public_key::CachedPublicKey, public_key::PublicKey, - public_key_bytes::PublicKeyBytes, secret_key::SecretKey, - secret_key_bytes::SecretKeyBytes, signature::Signature, - signature_bytes::SignatureBytes, - }; +pub use consts::*; +pub use error::*; - pub type AggregatePublicKey = PublicKey; - pub type AggregatePublicKeyBytes = PublicKeyBytes; - pub type AggregateSignature = Signature; - pub type AggregateSignatureBytes = SignatureBytes; - }; -} +mod backend; +mod cached_public_key; +mod public_key; +mod public_key_bytes; +mod secret_key; +mod secret_key_bytes; +mod signature; +mod signature_bytes; -#[cfg(feature = "blst")] -implement_backend!(bls_blst); +pub use backend::Backend; -#[cfg(feature = "zkcrypto")] -implement_backend!(bls_zkcrypto); +pub use cached_public_key::CachedPublicKey; +pub use public_key::PublicKey; +pub use public_key_bytes::PublicKeyBytes; +pub use secret_key::SecretKey; +pub use secret_key_bytes::SecretKeyBytes; +pub use signature::Signature; +pub use signature_bytes::SignatureBytes; + +pub type AggregatePublicKey = PublicKey; +pub type AggregatePublicKeyBytes = PublicKeyBytes; +pub type AggregateSignature = Signature; +pub type AggregateSignatureBytes = SignatureBytes; diff --git a/bls/src/public_key.rs b/bls/src/public_key.rs new file mode 100644 index 000000000..e118b80c6 --- /dev/null +++ b/bls/src/public_key.rs @@ -0,0 +1,124 @@ +use core::fmt::Debug; + +use derive_more::derive::From; + +use crate::{backend::Backend, error::Error, public_key_bytes::PublicKeyBytes}; + +pub trait PublicKeyTrait: + Clone + Copy + PartialEq + Eq + Default + Debug + TryFrom +{ + fn aggregate_in_place(&mut self, other: Self); +} + +#[derive(Clone, Copy, PartialEq, Eq, Debug, From)] +pub enum PublicKey { + #[cfg(feature = "blst")] + Blst(blst::PublicKey), +} + +impl Into for PublicKey { + #[inline] + fn into(self) -> PublicKeyBytes { + match self { + #[cfg(feature = "blst")] + Self::Blst(v) => v.into(), + } + } +} + +#[allow(irrefutable_let_patterns)] +impl PublicKey { + pub fn default(backend: Backend) -> Self { + match backend { + #[cfg(feature = "blst")] + Backend::Blst => PublicKey::Blst(blst::PublicKey::default()), + } + } + + #[inline] + pub fn try_from_with_backend(bytes: PublicKeyBytes, backend: Backend) -> Result { + match backend { + #[cfg(feature = "blst")] + Backend::Blst => Ok(PublicKey::Blst(blst::PublicKey::try_from(bytes)?)), + } + } + + #[inline] + pub fn aggregate_in_place(&mut self, other: Self) { + match self { + #[cfg(feature = "blst")] + PublicKey::Blst(v) => { + let PublicKey::Blst(other) = other else { + panic!("trying to mix backends - this should never happen"); + }; + v.aggregate_in_place(other); + } + } + } + + /// [`eth_aggregate_pubkeys`](https://github.com/ethereum/consensus-specs/blob/86fb82b221474cc89387fa6436806507b3849d88/specs/altair/bls.md#eth_aggregate_pubkeys) + pub fn aggregate_nonempty(keys: impl IntoIterator) -> Result { + keys.into_iter() + .reduce(Self::aggregate) + .ok_or(Error::NoPublicKeysToAggregate) + } + + #[must_use] + pub fn aggregate(mut self, other: Self) -> Self { + self.aggregate_in_place(other); + self + } +} + +#[cfg(feature = "blst")] +pub(crate) mod blst { + use blst::min_pk::{AggregatePublicKey as RawAggregatePublicKey, PublicKey as RawPublicKey}; + use derive_more::From; + + use super::PublicKeyTrait; + use crate::{error::Error, PublicKeyBytes}; + + #[derive(Clone, Copy, PartialEq, Eq, Default, Debug, From)] + pub struct PublicKey(RawPublicKey); + + impl Into for PublicKey { + #[inline] + fn into(self) -> PublicKeyBytes { + PublicKeyBytes(self.as_raw().compress()) + } + } + + impl TryFrom for PublicKey { + type Error = Error; + + #[inline] + fn try_from(bytes: PublicKeyBytes) -> Result { + let raw = + RawPublicKey::uncompress(bytes.as_bytes()).map_err(|_| Error::InvalidPublicKey)?; + + // This is needed to pass `fast_aggregate_verify` tests. + // See the following for more information: + // - + // - + raw.validate().map_err(|_| Error::InvalidPublicKey)?; + + Ok(Self(raw)) + } + } + + impl PublicKeyTrait for PublicKey { + #[inline] + fn aggregate_in_place(&mut self, other: Self) { + let mut self_aggregate = RawAggregatePublicKey::from_public_key(self.as_raw()); + let other_aggregate = RawAggregatePublicKey::from_public_key(other.as_raw()); + self_aggregate.add_aggregate(&other_aggregate); + self.0 = self_aggregate.to_public_key(); + } + } + + impl PublicKey { + pub(crate) const fn as_raw(&self) -> &RawPublicKey { + &self.0 + } + } +} diff --git a/bls/src/public_key_bytes.rs b/bls/src/public_key_bytes.rs new file mode 100644 index 000000000..b3278837e --- /dev/null +++ b/bls/src/public_key_bytes.rs @@ -0,0 +1,50 @@ +use derive_more::derive::AsRef; +use fixed_hash::construct_fixed_hash; +use impl_serde::impl_fixed_hash_serde; +use ssz::{SszRead, SszSize, SszWrite}; + +pub const COMPRESSED_SIZE: usize = 48; + +construct_fixed_hash! { + #[derive(AsRef)] + pub struct PublicKeyBytes(COMPRESSED_SIZE); +} + +impl_fixed_hash_serde!(PublicKeyBytes, COMPRESSED_SIZE); + +impl hex::FromHex for PublicKeyBytes { + type Error = <[u8; COMPRESSED_SIZE] as hex::FromHex>::Error; + + fn from_hex>(digits: T) -> Result { + hex::FromHex::from_hex(digits).map(Self) + } +} + +impl SszSize for PublicKeyBytes { + const SIZE: ssz::Size = ssz::Size::Fixed { + size: COMPRESSED_SIZE, + }; +} + +impl SszRead for PublicKeyBytes { + #[inline] + fn from_ssz_unchecked(_context: &C, bytes: &[u8]) -> Result { + Ok(Self::from_slice(bytes)) + } +} + +impl SszWrite for PublicKeyBytes { + #[inline] + fn write_fixed(&self, bytes: &mut [u8]) { + bytes.copy_from_slice(self.as_bytes()); + } +} + +impl ssz::SszHash for PublicKeyBytes { + type PackingFactor = typenum::U1; + + #[inline] + fn hash_tree_root(&self) -> ssz::H256 { + ssz::MerkleTree::>::merkleize_bytes(self) + } +} diff --git a/bls/src/secret_key.rs b/bls/src/secret_key.rs new file mode 100644 index 000000000..d79737dcf --- /dev/null +++ b/bls/src/secret_key.rs @@ -0,0 +1,131 @@ +use core::{fmt::Debug, hash::Hash}; + +use crate::{ + public_key::{PublicKey, PublicKeyTrait}, + secret_key_bytes::SecretKeyBytes, + signature::{Signature, SignatureTrait}, + Backend, Error, +}; + +pub trait SecretKeyTrait: Debug + PartialEq + Eq + Hash { + type PublicKey: PublicKeyTrait; + type Signature: SignatureTrait; + + fn to_public_key(&self) -> Self::PublicKey; + fn sign(&self, message: impl AsRef<[u8]>) -> Self::Signature; + fn to_bytes(&self) -> SecretKeyBytes; +} + +#[derive(PartialEq, Eq, Hash, derive_more::Debug)] +pub enum SecretKey { + #[cfg(feature = "blst")] + #[debug("[REDACTED]")] + Blst(blst::SecretKey), +} + +impl SecretKey { + pub fn to_public_key(&self) -> PublicKey { + match self { + #[cfg(feature = "blst")] + SecretKey::Blst(secret_key) => PublicKey::Blst(secret_key.to_public_key()), + } + } + + pub fn sign(&self, message: impl AsRef<[u8]>) -> Signature { + match self { + #[cfg(feature = "blst")] + Self::Blst(secret_key) => Signature::Blst(secret_key.sign(message)), + } + } + + pub fn to_bytes(&self) -> SecretKeyBytes { + match self { + #[cfg(feature = "blst")] + Self::Blst(secret_key) => secret_key.to_bytes(), + } + } + + pub fn try_from_with_backend( + bytes: SecretKeyBytes, + backend: Backend, + ) -> Result { + match backend { + #[cfg(feature = "blst")] + Backend::Blst => Ok(SecretKey::Blst(blst::SecretKey::try_from(bytes)?)), + } + } +} + +#[cfg(feature = "blst")] +pub(crate) mod blst { + use blst::min_pk::SecretKey as RawSecretKey; + + use crate::{ + error::Error, public_key::blst::PublicKey, secret_key_bytes::SecretKeyBytes, + signature::blst::Signature, DOMAIN_SEPARATION_TAG, + }; + + use super::SecretKeyTrait; + + #[derive(derive_more::Debug)] + // Inspired by `DebugSecret` from the `secrecy` crate. + #[debug("[REDACTED]")] + pub struct SecretKey(RawSecretKey); + + impl PartialEq for SecretKey { + #[inline] + fn eq(&self, other: &Self) -> bool { + self.as_raw().to_bytes() == other.as_raw().to_bytes() + } + } + + impl Eq for SecretKey {} + + impl core::hash::Hash for SecretKey { + fn hash(&self, hasher: &mut H) { + self.as_raw().to_bytes().hash(hasher) + } + } + + impl SecretKey { + const fn as_raw(&self) -> &RawSecretKey { + &self.0 + } + } + + impl TryFrom for SecretKey { + type Error = Error; + + #[inline] + fn try_from(secret_key_bytes: SecretKeyBytes) -> Result { + RawSecretKey::from_bytes(secret_key_bytes.as_ref()) + .map(Self) + .map_err(|_| Error::InvalidSecretKey) + } + } + + impl SecretKeyTrait<32> for SecretKey { + type PublicKey = PublicKey; + type Signature = Signature; + + #[inline] + #[must_use] + fn to_bytes(&self) -> SecretKeyBytes { + SecretKeyBytes { + bytes: self.as_raw().to_bytes(), + } + } + + #[inline] + fn to_public_key(&self) -> PublicKey { + self.as_raw().sk_to_pk().into() + } + + #[inline] + fn sign(&self, message: impl AsRef<[u8]>) -> Signature { + self.as_raw() + .sign(message.as_ref(), DOMAIN_SEPARATION_TAG, &[]) + .into() + } + } +} diff --git a/bls/src/secret_key_bytes.rs b/bls/src/secret_key_bytes.rs new file mode 100644 index 000000000..6ee906eb0 --- /dev/null +++ b/bls/src/secret_key_bytes.rs @@ -0,0 +1,47 @@ +// use super::secret_key::SecretKey; +// pub const SIZE: usize = size_of::(); +pub const SIZE: usize = 32; + +use derive_more::derive::{AsMut, AsRef, From}; +use hex::FromHex; +use serde::Deserialize; +use ssz::{SszRead, SszSize, SszWrite}; +use zeroize::{Zeroize, ZeroizeOnDrop}; + +#[derive(Default, AsRef, AsMut, From, Zeroize, ZeroizeOnDrop, Deserialize)] +#[as_ref(forward)] +#[as_mut(forward)] +#[serde(transparent)] +pub struct SecretKeyBytes { + #[serde(with = "serde_utils::prefixed_hex_or_bytes_array")] + pub(crate) bytes: [u8; SIZE], +} + +impl FromHex for SecretKeyBytes { + type Error = <[u8; 32] as FromHex>::Error; + + fn from_hex>(digits: T) -> Result { + let bytes = FromHex::from_hex(digits)?; + Ok(Self { bytes }) + } +} + +impl SszSize for SecretKeyBytes { + const SIZE: ssz::Size = ssz::Size::Fixed { size: SIZE }; +} + +impl SszRead for SecretKeyBytes { + #[inline] + fn from_ssz_unchecked(_context: &C, bytes: &[u8]) -> Result { + let mut secret_key = Self::default(); + secret_key.bytes.copy_from_slice(bytes); + Ok(secret_key) + } +} + +impl SszWrite for SecretKeyBytes { + #[inline] + fn write_fixed(&self, bytes: &mut [u8]) { + bytes.copy_from_slice(&self.bytes); + } +} diff --git a/bls/src/signature.rs b/bls/src/signature.rs new file mode 100644 index 000000000..de83edd0c --- /dev/null +++ b/bls/src/signature.rs @@ -0,0 +1,273 @@ +use core::fmt::Debug; + +use derive_more::derive::From; + +use crate::{public_key::PublicKeyTrait, Backend, Error, PublicKey, SignatureBytes}; + +pub trait SignatureTrait: Clone + Copy + PartialEq + Eq + Debug + Default + 'static +where + Self::PublicKey: 'static, +{ + type PublicKey: PublicKeyTrait; + + fn verify(&self, message: impl AsRef<[u8]>, public_key: Self::PublicKey) -> bool; + + fn aggregate_in_place(&mut self, other: Self); + + fn fast_aggregate_verify<'keys>( + &self, + message: impl AsRef<[u8]>, + public_keys: impl IntoIterator, + ) -> bool; + + fn multi_verify<'all>( + messages: impl IntoIterator, + signatures: impl IntoIterator, + public_keys: impl IntoIterator, + ) -> bool; +} + +#[derive(Clone, Copy, PartialEq, Eq, Debug, From)] +pub enum Signature { + #[cfg(feature = "blst")] + Blst(blst::Signature), +} + +#[allow(irrefutable_let_patterns)] +#[allow(unreachable_patterns)] +impl Signature { + pub fn verify(&self, message: impl AsRef<[u8]>, public_key: PublicKey) -> bool { + match self { + #[cfg(feature = "blst")] + Self::Blst(v) => { + let PublicKey::Blst(public_key) = public_key else { + panic!("mixed backends"); + }; + + v.verify(message, public_key) + } + } + } + + #[must_use] + pub fn aggregate(mut self, other: Self) -> Self { + self.aggregate_in_place(other); + self + } + + pub fn aggregate_in_place(&mut self, other: Self) { + match self { + #[cfg(feature = "blst")] + Self::Blst(v) => { + let Self::Blst(other) = other else { + panic!("mixed backends"); + }; + v.aggregate_in_place(other); + } + } + } + + pub fn fast_aggregate_verify<'keys>( + &self, + message: impl AsRef<[u8]>, + public_keys: impl IntoIterator, + ) -> bool { + match self { + #[cfg(feature = "blst")] + Self::Blst(v) => v.fast_aggregate_verify( + message, + public_keys.into_iter().map(|key| match key { + PublicKey::Blst(k) => k, + _ => panic!("mixed backends"), + }), + ), + } + } + + pub fn multi_verify<'all>( + messages: impl IntoIterator, + signatures: impl IntoIterator, + public_keys: impl IntoIterator, + ) -> bool { + let mut signatures = signatures.into_iter(); + let Some(first_signature) = signatures.next() else { + todo!(); + }; + + match first_signature { + #[cfg(feature = "blst")] + Signature::Blst(blst) => blst::Signature::multi_verify( + messages, + std::iter::once(blst).chain(signatures.map(|sig| match sig { + Signature::Blst(sig) => sig, + _ => panic!("mixed backends"), + })), + public_keys.into_iter().map(|pubkey| match pubkey { + PublicKey::Blst(key) => key, + _ => panic!("mixed backends"), + }), + ), + } + } + + pub fn try_from_with_backend(bytes: SignatureBytes, backend: Backend) -> Result { + match backend { + #[cfg(feature = "blst")] + Backend::Blst => Ok(Signature::Blst(blst::Signature::try_from(bytes)?)), + } + } + + pub fn default_with_backend(backend: Backend) -> Self { + match backend { + #[cfg(feature = "blst")] + Backend::Blst => Signature::Blst(blst::Signature::default()), + } + } +} + +impl Into for Signature { + fn into(self) -> SignatureBytes { + match self { + #[cfg(feature = "blst")] + Self::Blst(sig) => sig.into(), + } + } +} + +#[cfg(feature = "blst")] +pub(crate) mod blst { + use core::num::NonZeroU64; + + use blst::{ + blst_scalar, + min_pk::{AggregateSignature as RawAggregateSignature, Signature as RawSignature}, + BLST_ERROR, + }; + use derive_more::From; + use itertools::Itertools as _; + use rand::Rng as _; + + use crate::{ + consts::DOMAIN_SEPARATION_TAG, error::Error, public_key::blst::PublicKey, + signature::SignatureTrait, signature_bytes::SignatureBytes, + }; + + const MULTI_VERIFY_RANDOM_BYTES: usize = size_of::(); + const MULTI_VERIFY_RANDOM_BITS: usize = MULTI_VERIFY_RANDOM_BYTES * 8; + + #[derive(Clone, Copy, PartialEq, Eq, Debug, From)] + pub struct Signature(RawSignature); + + impl Default for Signature { + #[inline] + fn default() -> Self { + SignatureBytes::empty() + .try_into() + .expect("compressed signature constructed in SignatureBytes::empty is valid") + } + } + + impl TryFrom for Signature { + type Error = Error; + + #[inline] + fn try_from(bytes: SignatureBytes) -> Result { + RawSignature::uncompress(bytes.as_bytes()) + .map(Self) + .map_err(|_| Error::InvalidSignature) + } + } + + impl Into for Signature { + fn into(self) -> SignatureBytes { + SignatureBytes(self.as_raw().compress()) + } + } + + impl SignatureTrait for Signature { + type PublicKey = PublicKey; + + #[must_use] + fn verify(&self, message: impl AsRef<[u8]>, public_key: Self::PublicKey) -> bool { + let result = self.as_raw().verify( + true, + message.as_ref(), + DOMAIN_SEPARATION_TAG, + &[], + public_key.as_raw(), + false, + ); + + result == BLST_ERROR::BLST_SUCCESS + } + + #[inline] + fn aggregate_in_place(&mut self, other: Self) { + let mut self_aggregate = RawAggregateSignature::from_signature(self.as_raw()); + let other_aggregate = RawAggregateSignature::from_signature(other.as_raw()); + self_aggregate.add_aggregate(&other_aggregate); + self.0 = self_aggregate.to_signature(); + } + + #[must_use] + fn fast_aggregate_verify<'keys>( + &self, + message: impl AsRef<[u8]>, + public_keys: impl IntoIterator, + ) -> bool { + let public_keys = public_keys.into_iter().map(PublicKey::as_raw).collect_vec(); + + let result = self.as_raw().fast_aggregate_verify( + true, + message.as_ref(), + DOMAIN_SEPARATION_TAG, + public_keys.as_slice(), + ); + + result == BLST_ERROR::BLST_SUCCESS + } + + #[must_use] + fn multi_verify<'all>( + messages: impl IntoIterator, + signatures: impl IntoIterator, + public_keys: impl IntoIterator, + ) -> bool { + let messages = messages.into_iter().collect_vec(); + let signatures = signatures.into_iter().map(Self::as_raw).collect_vec(); + let public_keys = public_keys.into_iter().map(PublicKey::as_raw).collect_vec(); + + // `ThreadRng` is cryptographically secure. + let mut rng = rand::thread_rng(); + + let randoms = core::iter::repeat_with(|| { + let mut scalar = blst_scalar::default(); + let nonzero_bytes = rng.gen::().get().to_le_bytes(); + scalar.b[..MULTI_VERIFY_RANDOM_BYTES].copy_from_slice(&nonzero_bytes); + scalar + }) + .take(signatures.len()) + .collect_vec(); + + let result = RawSignature::verify_multiple_aggregate_signatures( + messages.as_slice(), + DOMAIN_SEPARATION_TAG, + public_keys.as_slice(), + false, + signatures.as_slice(), + false, + randoms.as_slice(), + MULTI_VERIFY_RANDOM_BITS, + ); + + result == BLST_ERROR::BLST_SUCCESS + } + } + + impl Signature { + #[must_use] + pub const fn as_raw(&self) -> &RawSignature { + &self.0 + } + } +} diff --git a/bls/src/signature_bytes.rs b/bls/src/signature_bytes.rs new file mode 100644 index 000000000..37e51a649 --- /dev/null +++ b/bls/src/signature_bytes.rs @@ -0,0 +1,51 @@ +use fixed_hash::construct_fixed_hash; +use impl_serde::impl_fixed_hash_serde; +use ssz::{SszHash, SszRead, SszSize, SszWrite}; + +construct_fixed_hash! { + #[derive(derive_more::AsRef)] + pub struct SignatureBytes(96); +} + +impl_fixed_hash_serde!(SignatureBytes, 96); + +impl SszSize for SignatureBytes { + const SIZE: ssz::Size = ssz::Size::Fixed { size: 96 }; +} + +impl SszRead for SignatureBytes { + #[inline] + fn from_ssz_unchecked(_context: &C, bytes: &[u8]) -> Result { + Ok(Self::from_slice(bytes)) + } +} + +impl SszWrite for SignatureBytes { + #[inline] + fn write_fixed(&self, bytes: &mut [u8]) { + bytes.copy_from_slice(self.as_bytes()); + } +} + +impl SszHash for SignatureBytes { + type PackingFactor = typenum::U1; + + #[inline] + fn hash_tree_root(&self) -> ssz::H256 { + ssz::MerkleTree::>::merkleize_bytes(self) + } +} + +impl SignatureBytes { + #[inline] + pub fn empty() -> Self { + let mut bytes = Self::zero(); + bytes.as_mut()[0] = 0xc0; + bytes + } + + #[inline] + pub fn is_empty(self) -> bool { + self == Self::empty() + } +} diff --git a/builder_api/src/api.rs b/builder_api/src/api.rs index e66bda28e..554782278 100644 --- a/builder_api/src/api.rs +++ b/builder_api/src/api.rs @@ -3,7 +3,7 @@ use std::{sync::Arc, time::SystemTime}; use anyhow::{bail, ensure, Result}; use arc_swap::ArcSwap; -use bls::PublicKeyBytes; +use bls::{PublicKeyBytes, Backend}; use helper_functions::{misc, signing::SignForAllForks}; use http_api_utils::ETH_CONSENSUS_VERSION; use itertools::Itertools as _; @@ -74,17 +74,19 @@ pub struct Api { metrics: Option>, supports_block_ssz: ArcSwap>, supports_validators_ssz: ArcSwap>, + backend: Backend, } impl Api { #[must_use] - pub fn new(config: BuilderConfig, client: Client, metrics: Option>) -> Self { + pub fn new(config: BuilderConfig, client: Client, metrics: Option>, backend: Backend) -> Self { Self { config, client, metrics, supports_block_ssz: ArcSwap::from_pointee(None), supports_validators_ssz: ArcSwap::from_pointee(None), + backend, } } @@ -269,22 +271,22 @@ impl Api { SignedBuilderBid::Bellatrix(builder_bid) => { builder_bid .message - .verify(chain_config, signature, &public_key)? + .verify(chain_config, signature, &public_key, self.backend)? } SignedBuilderBid::Capella(builder_bid) => { builder_bid .message - .verify(chain_config, signature, &public_key)? + .verify(chain_config, signature, &public_key, self.backend)? } SignedBuilderBid::Deneb(builder_bid) => { builder_bid .message - .verify(chain_config, signature, &public_key)? + .verify(chain_config, signature, &public_key, self.backend)? } SignedBuilderBid::Electra(builder_bid) => { builder_bid .message - .verify(chain_config, signature, &public_key)? + .verify(chain_config, signature, &public_key, self.backend)? } } diff --git a/factory/src/lib.rs b/factory/src/lib.rs index ace485c48..b57b372a4 100644 --- a/factory/src/lib.rs +++ b/factory/src/lib.rs @@ -7,10 +7,7 @@ use core::ops::Range; use std::sync::Arc; use anyhow::{bail, ensure, Result}; -use bls::{ - traits::{CachedPublicKey as _, SecretKey as _, Signature as _}, - AggregateSignature, -}; +use bls::{AggregateSignature, Backend}; use deposit_tree::DepositTree; use helper_functions::{ accessors, misc, @@ -59,11 +56,15 @@ use types::{ type BlockWithState

= (Arc>, Arc>); -pub fn min_genesis_state(config: &Config) -> Result<(Arc>, DepositTree)> { +pub fn min_genesis_state( + config: &Config, + backend: Backend, +) -> Result<(Arc>, DepositTree)> { let (genesis_state, deposit_tree) = interop::quick_start_beacon_state( config, config.min_genesis_time, config.min_genesis_active_validator_count, + backend, )?; Ok((Arc::new(genesis_state), deposit_tree)) @@ -74,8 +75,9 @@ pub fn empty_block( pre_state: Arc>, slot: Slot, graffiti: H256, + backend: Backend, ) -> Result> { - let advanced_state = advance_state(config, pre_state, slot)?; + let advanced_state = advance_state(config, pre_state, slot, backend)?; let eth1_data = advanced_state.eth1_data(); let attestations = core::iter::empty(); let deposits = ContiguousList::default(); @@ -91,6 +93,7 @@ pub fn empty_block( deposits, sync_aggregate, execution_payload, + backend, ) } @@ -99,12 +102,14 @@ pub fn block_justifying_previous_epoch( pre_state: Arc>, epoch: Epoch, graffiti: H256, + backend: Backend, ) -> Result> { let block_slot = misc::compute_start_slot_at_epoch::

(epoch); - let advanced_state = advance_state(config, pre_state, block_slot)?; + let advanced_state = advance_state(config, pre_state, block_slot, backend)?; let eth1_data = advanced_state.eth1_data(); let attestation_slots = misc::slots_in_epoch::

(epoch - 1); - let attestations = full_block_attestations(config, &advanced_state, attestation_slots)?; + let attestations = + full_block_attestations(config, &advanced_state, attestation_slots, backend)?; let deposits = ContiguousList::default(); let sync_aggregate = SyncAggregate::empty(); let execution_payload = None; @@ -118,6 +123,7 @@ pub fn block_justifying_previous_epoch( deposits, sync_aggregate, execution_payload, + backend, ) } @@ -127,12 +133,14 @@ pub fn block_justifying_current_epoch( epoch: Epoch, graffiti: H256, execution_payload: Option>, + backend: Backend, ) -> Result> { let block_slot = misc::compute_start_slot_at_epoch::

(epoch + 1) - 1; - let advanced_state = advance_state(config, pre_state, block_slot)?; + let advanced_state = advance_state(config, pre_state, block_slot, backend)?; let eth1_data = advanced_state.eth1_data(); let attestation_slots = misc::compute_start_slot_at_epoch::

(epoch)..block_slot; - let attestations = full_block_attestations(config, &advanced_state, attestation_slots)?; + let attestations = + full_block_attestations(config, &advanced_state, attestation_slots, backend)?; let deposits = ContiguousList::default(); let sync_aggregate = SyncAggregate::empty(); @@ -145,6 +153,7 @@ pub fn block_justifying_current_epoch( deposits, sync_aggregate, execution_payload, + backend, ) } @@ -153,8 +162,9 @@ pub fn block_with_deposits( pre_state: Arc>, slot: Slot, deposits: ContiguousList, + backend: Backend, ) -> Result> { - let advanced_state = advance_state(config, pre_state, slot)?; + let advanced_state = advance_state(config, pre_state, slot, backend)?; let eth1_data = advanced_state.eth1_data(); let graffiti = H256::zero(); let attestations = core::iter::empty(); @@ -170,6 +180,7 @@ pub fn block_with_deposits( deposits, sync_aggregate, execution_payload, + backend, ) } @@ -179,8 +190,9 @@ pub fn block_with_eth1_vote_and_deposits( slot: Slot, eth1_vote: Eth1Data, deposits: ContiguousList, + backend: Backend, ) -> Result> { - let advanced_state = advance_state(config, pre_state, slot)?; + let advanced_state = advance_state(config, pre_state, slot, backend)?; let graffiti = H256::zero(); let attestations = core::iter::empty(); let sync_aggregate = SyncAggregate::empty(); @@ -195,6 +207,7 @@ pub fn block_with_eth1_vote_and_deposits( deposits, sync_aggregate, execution_payload, + backend, ) } @@ -204,8 +217,9 @@ pub fn block_with_payload( slot: Slot, graffiti: H256, execution_payload: ExecutionPayload

, + backend: Backend, ) -> Result> { - let advanced_state = advance_state(config, pre_state, slot)?; + let advanced_state = advance_state(config, pre_state, slot, backend)?; let eth1_data = advanced_state.eth1_data(); let attestations = core::iter::empty(); let deposits = ContiguousList::default(); @@ -221,6 +235,7 @@ pub fn block_with_payload( deposits, sync_aggregate, execution_payload, + backend, ) } @@ -228,6 +243,7 @@ pub fn full_blocks_up_to_epoch( config: &Config, genesis_state: Arc>, end_epoch: Epoch, + backend: Backend, ) -> Result>>> { let start_slot = GENESIS_SLOT + 1; let end_slot = misc::compute_start_slot_at_epoch::

(end_epoch); @@ -237,12 +253,13 @@ pub fn full_blocks_up_to_epoch( // This has to be a loop because it moves out of `pre_state`. for slot in start_slot..=end_slot { - let advanced_state = advance_state(config, pre_state, slot)?; + let advanced_state = advance_state(config, pre_state, slot, backend)?; let eth1_data = advanced_state.eth1_data(); let graffiti = H256::zero(); - let attestations = full_block_attestations(config, &advanced_state, (slot - 1)..slot)?; + let attestations = + full_block_attestations(config, &advanced_state, (slot - 1)..slot, backend)?; let deposits = ContiguousList::default(); - let sync_aggregate = full_sync_aggregate(config, &advanced_state); + let sync_aggregate = full_sync_aggregate(config, &advanced_state, backend); let execution_payload = None; let (block, post_state) = block( @@ -254,6 +271,7 @@ pub fn full_blocks_up_to_epoch( deposits, sync_aggregate, execution_payload, + backend, )?; pre_state = post_state; @@ -268,9 +286,10 @@ pub fn singular_attestation( state: Arc>, epoch: Epoch, validator_index: ValidatorIndex, + backend: Backend, ) -> Result<(Attestation

, SubnetId)> { let start_slot = misc::compute_start_slot_at_epoch::

(epoch); - let state_in_epoch = advance_state(config, state, start_slot)?; + let state_in_epoch = advance_state(config, state, start_slot, backend)?; for slot in misc::slots_in_epoch::

(epoch) { let committees = accessors::beacon_committees(&state_in_epoch, slot)?; @@ -302,7 +321,7 @@ pub fn singular_attestation( target: Checkpoint { epoch, root }, }; - let secret_key = interop::secret_key(validator_index); + let secret_key = interop::secret_key(validator_index, backend); let signature = data.sign(config, &state_in_epoch, &secret_key).into(); let attestation = if pre_electra { @@ -405,10 +424,11 @@ fn block( deposits: ContiguousList, sync_aggregate: SyncAggregate

, mut execution_payload: Option>, + backend: Backend, ) -> Result> { let slot = advanced_state.slot(); let proposer_index = accessors::get_beacon_proposer_index(&advanced_state)?; - let secret_key = interop::secret_key(proposer_index); + let secret_key = interop::secret_key(proposer_index, backend); let parent_root = accessors::latest_block_root(&advanced_state); let randao_reveal = RandaoEpoch::from(accessors::get_current_epoch(&advanced_state)) @@ -567,6 +587,7 @@ fn full_block_attestations( config: &Config, advanced_state: &BeaconState

, slots: Range, + backend: Backend, ) -> Result>> { let mut attestations = vec![]; @@ -607,9 +628,11 @@ fn full_block_attestations( let signature = committee .into_iter() - .map(|validator_index| interop::secret_key(validator_index).sign(signing_root)) + .map(|validator_index| { + interop::secret_key(validator_index, backend).sign(signing_root) + }) .reduce(AggregateSignature::aggregate) - .unwrap_or_default() + .unwrap_or(AggregateSignature::default_with_backend(backend)) .into(); attestations.push(Attestation::from(Phase0Attestation { @@ -656,9 +679,11 @@ fn full_block_attestations( let signature = committees .flatten() - .map(|validator_index| interop::secret_key(validator_index).sign(signing_root)) + .map(|validator_index| { + interop::secret_key(validator_index, backend).sign(signing_root) + }) .reduce(AggregateSignature::aggregate) - .unwrap_or_default() + .unwrap_or(AggregateSignature::default_with_backend(backend)) .into(); attestations.push(Attestation::from(ElectraAttestation { @@ -676,6 +701,7 @@ fn full_block_attestations( fn full_sync_aggregate( config: &Config, advanced_state: &BeaconState

, + backend: Backend, ) -> SyncAggregate

{ let Some(advanced_state) = advanced_state.post_altair() else { return SyncAggregate::empty(); @@ -694,9 +720,9 @@ fn full_sync_aggregate( are taken from advanced_state.validators", ) }) - .map(|validator_index| interop::secret_key(validator_index).sign(signing_root)) + .map(|validator_index| interop::secret_key(validator_index, backend).sign(signing_root)) .reduce(AggregateSignature::aggregate) - .unwrap_or_default() + .unwrap_or(AggregateSignature::default_with_backend(backend)) .into(); SyncAggregate { @@ -709,9 +735,10 @@ fn advance_state( config: &Config, mut state: Arc>, slot: Slot, + backend: Backend, ) -> Result>> { if state.slot() < slot { - combined::process_slots(config, state.make_mut(), slot)?; + combined::process_slots(config, state.make_mut(), slot, backend)?; } Ok(state) diff --git a/fork_choice_control/Cargo.toml b/fork_choice_control/Cargo.toml index 0d30c5bb8..3c2b86fd2 100644 --- a/fork_choice_control/Cargo.toml +++ b/fork_choice_control/Cargo.toml @@ -10,6 +10,7 @@ workspace = true anyhow = { workspace = true } arc-swap = { workspace = true } arithmetic = { workspace = true } +bls = { workspace = true } clock = { workspace = true } crossbeam-utils = { workspace = true } database = { workspace = true } diff --git a/fork_choice_control/src/block_processor.rs b/fork_choice_control/src/block_processor.rs index 87f3c225d..dbaaa5f32 100644 --- a/fork_choice_control/src/block_processor.rs +++ b/fork_choice_control/src/block_processor.rs @@ -1,6 +1,7 @@ use std::sync::Arc; use anyhow::Result; +use bls::Backend; use derive_more::Constructor; use execution_engine::ExecutionEngine; use fork_choice_store::{ @@ -31,6 +32,7 @@ use types::{ pub struct BlockProcessor { chain_config: Arc, state_cache: Arc>, + backend: Backend, } impl BlockProcessor

{ @@ -167,10 +169,10 @@ impl BlockProcessor

{ // > Make a copy of the state to avoid mutability issues let state = self .state_cache - .try_state_at_slot(store, parent.block_root, block_slot)? + .try_state_at_slot(store, parent.block_root, block_slot, self.backend)? .unwrap_or_else(|| parent.state(store)); - combined::process_block_for_gossip(&self.chain_config, &state, block)?; + combined::process_block_for_gossip(&self.chain_config, &state, block, self.backend)?; Ok(None) }) diff --git a/fork_choice_control/src/controller.rs b/fork_choice_control/src/controller.rs index 7825d6bdb..3eef6cecd 100644 --- a/fork_choice_control/src/controller.rs +++ b/fork_choice_control/src/controller.rs @@ -17,6 +17,7 @@ use std::{ use anyhow::{Context as _, Result}; use arc_swap::{ArcSwap, Guard}; +use bls::Backend; use clock::Tick; use eth2_libp2p::{GossipId, PeerId}; use execution_engine::{ExecutionEngine, PayloadStatusV1}; @@ -107,6 +108,7 @@ where storage: Arc>, unfinalized_blocks: impl DoubleEndedIterator>>>, finished_back_sync: bool, + backend: Backend, ) -> Result<(Arc, MutatorHandle)> { let finished_initial_forward_sync = anchor_block.message().slot() >= tick.slot; @@ -117,6 +119,7 @@ where anchor_state, finished_initial_forward_sync, finished_back_sync, + backend, ); store.apply_tick(tick)?; @@ -126,7 +129,7 @@ where let thread_pool = ThreadPool::new()?; let (mutator_tx, mutator_rx) = std::sync::mpsc::channel(); - let block_processor = Arc::new(BlockProcessor::new(chain_config, state_cache.clone_arc())); + let block_processor = Arc::new(BlockProcessor::new(chain_config, state_cache.clone_arc(), backend)); let mut mutator = Mutator::new( store_snapshot.clone_arc(), @@ -145,6 +148,7 @@ where subnet_tx, sync_tx, validator_tx, + backend, ); mutator.process_unfinalized_blocks(unfinalized_blocks)?; diff --git a/fork_choice_control/src/mutator.rs b/fork_choice_control/src/mutator.rs index 33b00fff5..36c87450c 100644 --- a/fork_choice_control/src/mutator.rs +++ b/fork_choice_control/src/mutator.rs @@ -26,6 +26,7 @@ use std::{ use anyhow::{anyhow, Result}; use arc_swap::ArcSwap; +use bls::Backend; use clock::{Tick, TickKind}; use drain_filter_polyfill::VecExt as _; use eth2_libp2p::{GossipId, PeerId}; @@ -120,6 +121,7 @@ pub struct Mutator { subnet_tx: NS, sync_tx: SS, validator_tx: VS, + backend: Backend, } impl Mutator @@ -152,6 +154,7 @@ where subnet_tx: NS, sync_tx: SS, validator_tx: VS, + backend: Backend, ) -> Self { Self { store: store_snapshot.load_full(), @@ -176,6 +179,7 @@ where subnet_tx, sync_tx, validator_tx, + backend, } } @@ -2295,6 +2299,7 @@ where wait_group, checkpoint, metrics: self.metrics.clone(), + backend: self.backend, }); } @@ -2310,6 +2315,7 @@ where head_block_root: self.store.head().block_root, next_slot: self.store.slot() + 1, metrics: self.metrics.clone(), + backend: self.backend, }) } diff --git a/fork_choice_control/src/queries.rs b/fork_choice_control/src/queries.rs index c050de338..693a320db 100644 --- a/fork_choice_control/src/queries.rs +++ b/fork_choice_control/src/queries.rs @@ -480,7 +480,7 @@ where let head = store.head(); self.state_cache() - .state_at_slot(&store, head.block_root, store.slot()) + .state_at_slot(&store, head.block_root, store.slot(), self.storage().backend) } pub fn preprocessed_state_at_next_slot(&self) -> Result>> { @@ -488,7 +488,7 @@ where let head = store.head(); self.state_cache() - .state_at_slot(&store, head.block_root, store.slot() + 1) + .state_at_slot(&store, head.block_root, store.slot() + 1, self.storage().backend) } // The `block_root` and `state` parameters are needed @@ -502,7 +502,7 @@ where if let Some(state) = self .state_cache() - .try_state_at_slot(&store, block_root, slot)? + .try_state_at_slot(&store, block_root, slot, self.storage().backend)? { return Ok(state); } @@ -510,7 +510,7 @@ where if let Some(state) = self.storage().state_post_block(block_root)? { return self .state_cache() - .process_slots(&store, state, block_root, slot); + .process_slots(&store, state, block_root, slot, self.storage().backend); } bail!(Error::StateNotFound { block_root }) @@ -536,7 +536,7 @@ where let state = self .state_cache() - .state_at_slot(&store, head.block_root, requested_slot) + .state_at_slot(&store, head.block_root, requested_slot, self.storage().backend) .unwrap_or_else(|_| head.state(&store)); Ok(WithStatus { @@ -780,7 +780,7 @@ impl Snapshot<'_, P> { let slot = misc::compute_start_slot_at_epoch::

(epoch); self.state_cache - .try_state_at_slot(&self.store_snapshot, root, slot) + .try_state_at_slot(&self.store_snapshot, root, slot, self.storage.backend) } #[must_use] @@ -868,9 +868,12 @@ impl Snapshot<'_, P> { let store = &self.store_snapshot; if let Some(chain_link) = store.chain_link_before_or_at(slot) { - let state = self - .state_cache - .state_at_slot(store, chain_link.block_root, slot)?; + let state = self.state_cache.state_at_slot( + store, + chain_link.block_root, + slot, + self.storage.backend, + )?; return Ok(Some(WithStatus { value: state, diff --git a/fork_choice_control/src/specialized.rs b/fork_choice_control/src/specialized.rs index 0a5198460..ac713116d 100644 --- a/fork_choice_control/src/specialized.rs +++ b/fork_choice_control/src/specialized.rs @@ -1,6 +1,7 @@ use core::ops::DerefMut as _; use std::sync::Arc; +use bls::Backend; use clock::Tick; use crossbeam_utils::sync::WaitGroup; use database::Database; @@ -96,6 +97,7 @@ where execution_engine: E, metrics: Option>, p2p_tx: impl UnboundedSink>, + backend: Backend, ) -> (Arc, MutatorHandle) { let tick = Tick::block_proposal(&anchor_block); @@ -104,6 +106,7 @@ where Database::in_memory(), DEFAULT_ARCHIVAL_EPOCH_INTERVAL, StorageMode::Standard, + backend, )); let event_channels = Arc::new(EventChannels::default()); @@ -126,6 +129,7 @@ where storage, core::iter::empty(), true, + backend, ) .expect("Controller::new should not fail in tests and benchmarks") } @@ -138,6 +142,7 @@ impl AdHocBenchController

{ anchor_block: Arc>, anchor_state: Arc>, p2p_tx: impl UnboundedSink>, + backend: Backend, ) -> (Arc, MutatorHandle) { Self::new_internal( chain_config, @@ -147,6 +152,7 @@ impl AdHocBenchController

{ NullExecutionEngine, None, p2p_tx, + backend, ) } } @@ -157,6 +163,7 @@ impl BenchController

{ chain_config: Arc, anchor_block: Arc>, anchor_state: Arc>, + backend: Backend, ) -> (Arc, MutatorHandle) { Self::new_internal( chain_config, @@ -166,6 +173,7 @@ impl BenchController

{ NullExecutionEngine, None, futures::sink::drain(), + backend, ) } } @@ -193,6 +201,7 @@ impl TestController

{ anchor_state: Arc>, execution_engine: TestExecutionEngine

, p2p_tx: impl UnboundedSink>, + backend: Backend, ) -> (Arc, MutatorHandle) { let store_config = StoreConfig::aggressive(&chain_config); @@ -204,6 +213,7 @@ impl TestController

{ execution_engine, None, p2p_tx, + backend, ) } diff --git a/fork_choice_control/src/storage.rs b/fork_choice_control/src/storage.rs index d5e14eba5..ef1977756 100644 --- a/fork_choice_control/src/storage.rs +++ b/fork_choice_control/src/storage.rs @@ -3,6 +3,7 @@ use std::{borrow::Cow, sync::Arc}; use anyhow::{bail, ensure, Context as _, Error as AnyhowError, Result}; use arithmetic::U64Ext as _; +use bls::Backend; use database::Database; use derive_more::Display; use fork_choice_store::{ChainLink, Store}; @@ -59,6 +60,7 @@ pub struct Storage

{ pub(crate) archival_epoch_interval: NonZeroU64, storage_mode: StorageMode, phantom: PhantomData

, + pub(crate) backend: Backend, } impl Storage

{ @@ -68,6 +70,7 @@ impl Storage

{ database: Database, archival_epoch_interval: NonZeroU64, storage_mode: StorageMode, + backend: Backend, ) -> Self { Self { config, @@ -75,6 +78,7 @@ impl Storage

{ archival_epoch_interval, storage_mode, phantom: PhantomData, + backend, } } @@ -586,7 +590,7 @@ impl Storage

{ } if state.slot() < slot { - combined::process_slots(&self.config, state.make_mut(), slot)?; + combined::process_slots(&self.config, state.make_mut(), slot, self.backend)?; } Ok(Some(state)) diff --git a/fork_choice_control/src/storage_back_sync.rs b/fork_choice_control/src/storage_back_sync.rs index 5fea5ac9d..e146c9f74 100644 --- a/fork_choice_control/src/storage_back_sync.rs +++ b/fork_choice_control/src/storage_back_sync.rs @@ -90,7 +90,7 @@ impl Storage

{ combined::untrusted_state_transition(self.config(), state.make_mut(), &block)?; previous_block = Some(block); } else { - combined::process_slots(self.config(), state.make_mut(), slot)?; + combined::process_slots(self.config(), state.make_mut(), slot, self.backend)?; } batch.push(serialize(SlotByStateRoot(state.hash_tree_root()), slot)?); diff --git a/fork_choice_control/src/storage_tool.rs b/fork_choice_control/src/storage_tool.rs index 549bed87b..44f936f9f 100644 --- a/fork_choice_control/src/storage_tool.rs +++ b/fork_choice_control/src/storage_tool.rs @@ -1,6 +1,7 @@ use std::path::{Path, PathBuf}; use anyhow::Result; +use bls::Backend; use genesis::AnchorCheckpointProvider; use log::info; use ssz::{SszHash as _, SszRead, SszWrite as _}; @@ -49,6 +50,7 @@ pub fn export_state_and_blocks( storage.config(), temporary_state.make_mut(), state_slot, + storage.backend, )?; } @@ -88,6 +90,7 @@ pub fn replay_blocks( input_dir: &Path, from_slot: Slot, to_slot: Slot, + backend: Backend, ) -> Result<()> { let first_state_file_prefix = format!("beacon_state_slot_{from_slot:06}_root_"); let mut state = @@ -104,7 +107,7 @@ pub fn replay_blocks( } if state.slot() < to_slot { - combined::process_slots(config, &mut state, to_slot)?; + combined::process_slots(config, &mut state, to_slot, backend)?; } let last_state_file_prefix = format!("beacon_state_slot_{to_slot:06}_root_"); diff --git a/fork_choice_control/src/tasks.rs b/fork_choice_control/src/tasks.rs index 7b3cee309..8148410d4 100644 --- a/fork_choice_control/src/tasks.rs +++ b/fork_choice_control/src/tasks.rs @@ -5,6 +5,7 @@ use std::{ }; use anyhow::Result; +use bls::Backend; use eth2_libp2p::GossipId; use execution_engine::{ExecutionEngine, NullExecutionEngine}; use features::Feature; @@ -407,6 +408,7 @@ pub struct CheckpointStateTask { pub wait_group: W, pub checkpoint: Checkpoint, pub metrics: Option>, + pub backend: Backend, } impl Run for CheckpointStateTask { @@ -418,6 +420,7 @@ impl Run for CheckpointStateTask { wait_group, checkpoint, metrics, + backend, } = self; let _timer = metrics @@ -427,13 +430,14 @@ impl Run for CheckpointStateTask { let Checkpoint { epoch, root } = checkpoint; let slot = misc::compute_start_slot_at_epoch::

(epoch); - let checkpoint_state = match state_cache.try_state_at_slot(&store_snapshot, root, slot) { - Ok(state) => state, - Err(error) => { - warn!("failed to compute checkpoint state: {error:?}"); - return; - } - }; + let checkpoint_state = + match state_cache.try_state_at_slot(&store_snapshot, root, slot, backend) { + Ok(state) => state, + Err(error) => { + warn!("failed to compute checkpoint state: {error:?}"); + return; + } + }; MutatorMessage::CheckpointState { wait_group, @@ -451,6 +455,7 @@ pub struct PreprocessStateTask { pub head_block_root: H256, pub next_slot: Slot, pub metrics: Option>, + pub backend: Backend, } impl Run for PreprocessStateTask { @@ -462,13 +467,15 @@ impl Run for PreprocessStateTask { head_block_root, next_slot, metrics, + backend, } = self; let _timer = metrics .as_ref() .map(|metrics| metrics.fc_preprocess_state_task_times.start_timer()); - match state_cache.state_at_slot_quiet(&store_snapshot, head_block_root, next_slot) { + match state_cache.state_at_slot_quiet(&store_snapshot, head_block_root, next_slot, backend) + { Ok(state) => { if let Err(error) = initialize_preprocessed_state_cache(&state) { warn!("failed to initialize preprocessed state's cache values: {error:?}"); diff --git a/fork_choice_store/Cargo.toml b/fork_choice_store/Cargo.toml index 2205b3cdc..2064c393a 100644 --- a/fork_choice_store/Cargo.toml +++ b/fork_choice_store/Cargo.toml @@ -9,6 +9,7 @@ workspace = true [dependencies] anyhow = { workspace = true } arithmetic = { workspace = true } +bls = { workspace = true } clock = { workspace = true } crossbeam-skiplist = { workspace = true } derivative = { workspace = true } diff --git a/fork_choice_store/src/state_cache_processor.rs b/fork_choice_store/src/state_cache_processor.rs index b3551e1b6..f44bbb11c 100644 --- a/fork_choice_store/src/state_cache_processor.rs +++ b/fork_choice_store/src/state_cache_processor.rs @@ -2,6 +2,7 @@ use core::time::Duration; use std::{backtrace::Backtrace, collections::HashSet, sync::Arc}; use anyhow::{bail, Result}; +use bls::Backend; use features::Feature; use log::warn; use state_cache::{StateCache, StateWithRewards}; @@ -76,12 +77,14 @@ impl StateCacheProcessor

{ store: &Store

, block_root: H256, slot: Slot, + backend: Backend, ) -> Result>>> { self.try_get_state_at_slot( store, block_root, slot, should_print_slot_processing_warning(store), + backend, ) } @@ -90,8 +93,9 @@ impl StateCacheProcessor

{ store: &Store

, block_root: H256, slot: Slot, + backend: Backend, ) -> Result>> { - self.try_state_at_slot(store, block_root, slot)? + self.try_state_at_slot(store, block_root, slot, backend)? .ok_or(Error::StateNotFound { block_root }) .map_err(Into::into) } @@ -101,8 +105,9 @@ impl StateCacheProcessor

{ store: &Store

, block_root: H256, slot: Slot, + backend: Backend, ) -> Result>> { - self.try_get_state_at_slot(store, block_root, slot, false)? + self.try_get_state_at_slot(store, block_root, slot, false, backend)? .ok_or(Error::StateNotFound { block_root }) .map_err(Into::into) } @@ -113,6 +118,7 @@ impl StateCacheProcessor

{ state: Arc>, block_root: H256, slot: Slot, + backend: Backend, ) -> Result>> { let post_state = process_slots( store, @@ -120,6 +126,7 @@ impl StateCacheProcessor

{ block_root, slot, should_print_slot_processing_warning(store), + backend, )?; if store.is_forward_synced() { @@ -136,6 +143,7 @@ impl StateCacheProcessor

{ block_root: H256, slot: Slot, warn_on_slot_processing: bool, + backend: Backend, ) -> Result>>> { if !store.is_forward_synced() { return match self.before_or_at_slot(store, block_root, slot) { @@ -145,6 +153,7 @@ impl StateCacheProcessor

{ block_root, slot, warn_on_slot_processing, + backend, )?)), None => Ok(None), }; @@ -159,7 +168,14 @@ impl StateCacheProcessor

{ return Ok(None); }; - let state = process_slots(store, state, block_root, slot, warn_on_slot_processing)?; + let state = process_slots( + store, + state, + block_root, + slot, + warn_on_slot_processing, + backend, + )?; Ok(Some((state, None))) })? @@ -174,6 +190,7 @@ fn process_slots( block_root: H256, slot: Slot, warn_on_slot_processing: bool, + backend: Backend, ) -> Result>> { if state.slot() < slot { if warn_on_slot_processing && store.is_forward_synced() { @@ -199,7 +216,7 @@ fn process_slots( }); } - combined::process_slots(store.chain_config(), state.make_mut(), slot)?; + combined::process_slots(store.chain_config(), state.make_mut(), slot, backend)?; } Ok(state) diff --git a/fork_choice_store/src/store.rs b/fork_choice_store/src/store.rs index d3eb1c293..c5317dcee 100644 --- a/fork_choice_store/src/store.rs +++ b/fork_choice_store/src/store.rs @@ -7,6 +7,7 @@ use std::{ use anyhow::{anyhow, bail, ensure, Result}; use arithmetic::NonZeroExt as _; +use bls::Backend; use clock::Tick; use execution_engine::ExecutionEngine; use features::Feature; @@ -210,6 +211,7 @@ pub struct Store { rejected_block_roots: HashSet, finished_initial_forward_sync: bool, finished_back_sync: bool, + backend: Backend, } impl Store

{ @@ -222,6 +224,7 @@ impl Store

{ anchor_state: Arc>, finished_initial_forward_sync: bool, finished_back_sync: bool, + backend: Backend, ) -> Self { let block_root = anchor_block.message().hash_tree_root(); let state_root = anchor_state.hash_tree_root(); @@ -284,6 +287,7 @@ impl Store

{ rejected_block_roots: HashSet::default(), finished_initial_forward_sync, finished_back_sync, + backend, } } @@ -1302,16 +1306,26 @@ impl Store

{ // > The `aggregate_and_proof.selection_proof` is a valid signature of the // > `aggregate.data.slot` by the validator with index // > `aggregate_and_proof.aggregator_index`. - if let Err(error) = - slot.verify(chain_config, &target_state, selection_proof, public_key) - { + if let Err(error) = slot.verify( + chain_config, + &target_state, + selection_proof, + public_key, + self.backend, + ) { bail!(error.context(Error::InvalidSelectionProof { aggregate_and_proof, })); } // > The aggregator signature, `signed_aggregate_and_proof.signature`, is valid. - if let Err(error) = message.verify(chain_config, &target_state, signature, public_key) { + if let Err(error) = message.verify( + chain_config, + &target_state, + signature, + public_key, + self.backend, + ) { bail!(error.context(Error::InvalidAggregateAndProofSignature { aggregate_and_proof, })); @@ -1624,7 +1638,7 @@ impl Store

{ &self.chain_config, target_state, &indexed_attestation, - SingleVerifier, + SingleVerifier::new(self.backend), )?; } @@ -1641,7 +1655,7 @@ impl Store

{ &self.chain_config, target_state, &indexed_attestation, - SingleVerifier, + SingleVerifier::new(self.backend), )?; } @@ -1658,7 +1672,7 @@ impl Store

{ &self.chain_config, target_state, &indexed_attestation, - SingleVerifier, + SingleVerifier::new(self.backend), )?; } @@ -1681,6 +1695,7 @@ impl Store

{ &self.chain_config, self.justified_state(), attester_slashing, + self.backend, ) } else { unphased::validate_attester_slashing_with_verifier( @@ -1697,6 +1712,7 @@ impl Store

{ &self.chain_config, self.justified_state(), attester_slashing, + self.backend, ) } else { unphased::validate_attester_slashing_with_verifier( @@ -1781,7 +1797,7 @@ impl Store

{ }; // [REJECT] The proposer signature of blob_sidecar.signed_block_header, is valid with respect to the block_header.proposer_index pubkey. - SingleVerifier.verify_singular( + SingleVerifier::new(self.backend).verify_singular( blob_sidecar .signed_block_header .message @@ -1898,13 +1914,19 @@ impl Store

{ }, || { self.state_cache - .try_state_at_slot(self, block_header.parent_root, block_header.slot) + .try_state_at_slot( + self, + block_header.parent_root, + block_header.slot, + self.backend, + ) .transpose() .unwrap_or_else(|| { self.state_cache.state_at_slot( self, self.head().block_root, block_header.slot, + self.backend, ) }) }, diff --git a/genesis/Cargo.toml b/genesis/Cargo.toml index 05da7d188..e825179a8 100644 --- a/genesis/Cargo.toml +++ b/genesis/Cargo.toml @@ -9,6 +9,7 @@ workspace = true [dependencies] anyhow = { workspace = true } arithmetic = { workspace = true } +bls = { workspace = true } deposit_tree = { workspace = true } helper_functions = { workspace = true } ssz = { workspace = true } diff --git a/genesis/src/lib.rs b/genesis/src/lib.rs index 12254aea0..1f7070b16 100644 --- a/genesis/src/lib.rs +++ b/genesis/src/lib.rs @@ -3,6 +3,7 @@ use std::sync::Arc; use anyhow::{ensure, Result}; use arithmetic::U64Ext as _; +use bls::Backend; use deposit_tree::DepositTree; use helper_functions::{accessors, misc, mutators::increase_balance}; use ssz::{PersistentList, PersistentVector, SszHash as _}; @@ -57,12 +58,13 @@ pub struct Incremental<'config, P: Preset> { config: &'config Config, beacon_state: BeaconState

, deposit_tree: DepositTree, + backend: Backend, } impl<'config, P: Preset> Incremental<'config, P> { /// #[must_use] - pub fn new(config: &'config Config) -> Self { + pub fn new(config: &'config Config, backend: Backend) -> Self { let slot = GENESIS_SLOT; let phase = config.phase_at_slot::

(slot); let version = config.version(phase); @@ -138,6 +140,7 @@ impl<'config, P: Preset> Incremental<'config, P> { config, beacon_state, deposit_tree: DepositTree::default(), + backend, } } @@ -164,7 +167,7 @@ impl<'config, P: Preset> Incremental<'config, P> { eth1_data.deposit_count = self.deposit_tree.deposit_count; if let Some(validator_index) = - combined::process_deposit_data(self.config, &mut self.beacon_state, data)? + combined::process_deposit_data(self.config, &mut self.beacon_state, data, self.backend)? { if let Some(state) = self.beacon_state.post_electra_mut() { let pending_deposits = state.pending_deposits().clone(); @@ -235,7 +238,7 @@ impl<'config, P: Preset> Incremental<'config, P> { // > [New in Altair] Fill in sync committees // > Note: A duplicate committee is assigned for the current and next committee at genesis if let Some(state) = beacon_state.post_altair_mut() { - let sync_committee = accessors::get_next_sync_committee(state)?; + let sync_committee = accessors::get_next_sync_committee(state, self.backend)?; *state.current_sync_committee_mut() = sync_committee.clone_arc(); *state.next_sync_committee_mut() = sync_committee; } @@ -352,6 +355,7 @@ fn validate_genesis_state(config: &Config, state: &BeaconState

) -> #[cfg(test)] mod spec_tests { + use bls::Backend; use duplicate::duplicate_item; use serde::Deserialize; use spec_test_utils::Case; @@ -430,7 +434,7 @@ mod spec_tests { meta.execution_payload_header, ); - let mut incremental = Incremental::new(&config); + let mut incremental = Incremental::new(&config, Backend::default()); incremental.set_eth1_timestamp(eth1_timestamp); @@ -464,10 +468,10 @@ mod spec_tests { #[cfg(test)] mod extra_tests { - use bls::{traits::SecretKey as _, SecretKey, SecretKeyBytes}; + use bls::{Backend, SecretKey, SecretKeyBytes}; use helper_functions::signing::SignForAllForks; use std_ext::CopyExt as _; - use tap::{Conv as _, TryConv as _}; + use tap::Conv as _; use types::{ phase0::{containers::DepositMessage, primitives::H256}, preset::Mainnet, @@ -481,7 +485,7 @@ mod extra_tests { let half_deposit_data = half_deposit_data::()?; let eth1_block_hash = ExecutionBlockHash::default(); - let mut incremental = Incremental::::new(&config); + let mut incremental = Incremental::::new(&config, Backend::default()); incremental.add_deposit_data(half_deposit_data, 0)?; incremental.add_deposit_data(half_deposit_data, 1)?; @@ -498,10 +502,12 @@ mod extra_tests { } fn half_deposit_data() -> Result { - let secret_key = b"????????????????????????????????" - .copy() - .conv::() - .try_conv::()?; + let secret_key = SecretKey::try_from_with_backend( + b"????????????????????????????????" + .copy() + .conv::(), + Backend::default(), + )?; let pubkey = secret_key.to_public_key().into(); let withdrawal_credentials = H256::default(); diff --git a/helper_functions/src/accessors.rs b/helper_functions/src/accessors.rs index cffce8ae3..10b908d71 100644 --- a/helper_functions/src/accessors.rs +++ b/helper_functions/src/accessors.rs @@ -8,10 +8,7 @@ use std::sync::Arc; use anyhow::{bail, ensure, Result}; use arithmetic::U64Ext as _; use bit_field::BitField as _; -use bls::{ - traits::{CachedPublicKey as _, PublicKey as _}, - AggregatePublicKey, CachedPublicKey, PublicKeyBytes, -}; +use bls::{AggregatePublicKey, Backend, CachedPublicKey, PublicKeyBytes}; use im::HashMap; use itertools::{EitherOrBoth, Itertools as _}; use num_integer::Roots as _; @@ -664,6 +661,7 @@ fn get_next_sync_committee_indices_post_electra( pub fn get_next_sync_committee( state: &(impl BeaconState

+ ?Sized), + backend: Backend, ) -> Result>>> { let indices = get_next_sync_committee_indices(state)?; @@ -675,7 +673,9 @@ pub fn get_next_sync_committee( } let aggregate_pubkey = itertools::process_results( - pubkeys.iter().map(CachedPublicKey::decompress), + pubkeys + .iter() + .map(|k| CachedPublicKey::decompress(k, backend)), |public_keys| AggregatePublicKey::aggregate_nonempty(public_keys.copied()), )?? .into(); diff --git a/helper_functions/src/fork.rs b/helper_functions/src/fork.rs index 48850d933..63506d75e 100644 --- a/helper_functions/src/fork.rs +++ b/helper_functions/src/fork.rs @@ -2,10 +2,7 @@ use core::ops::BitOrAssign as _; use std::sync::Arc; use anyhow::Result; -use bls::{ - traits::{CachedPublicKey as _, SignatureBytes as _}, - SignatureBytes, -}; +use bls::{Backend, SignatureBytes}; use itertools::Itertools as _; use ssz::PersistentList; use std_ext::ArcExt as _; @@ -43,6 +40,7 @@ use crate::{accessors, misc, mutators, phase0, predicates}; pub fn upgrade_to_altair( config: &Config, pre: Phase0BeaconState

, + backend: Backend, ) -> Result> { let epoch = accessors::get_current_epoch(&pre); @@ -125,7 +123,7 @@ pub fn upgrade_to_altair( // > Fill in sync committees // > Note: A duplicate committee is assigned for the current and next committee at the fork // > boundary - let sync_committee = accessors::get_next_sync_committee(&post)?; + let sync_committee = accessors::get_next_sync_committee(&post, backend)?; post.current_sync_committee = sync_committee.clone_arc(); post.next_sync_committee = sync_committee; @@ -729,7 +727,7 @@ mod spec_tests { let pre = case.ssz_default("pre"); let expected_post = case.ssz_default("post"); - let actual_post = upgrade_to_altair::

(&P::default_config(), pre) + let actual_post = upgrade_to_altair::

(&P::default_config(), pre, Backend::default()) .expect("upgrade from Phase 0 to Altair to should succeed"); assert_eq!(actual_post, expected_post); diff --git a/helper_functions/src/mutators.rs b/helper_functions/src/mutators.rs index fed16b6d6..37eaab7ba 100644 --- a/helper_functions/src/mutators.rs +++ b/helper_functions/src/mutators.rs @@ -1,10 +1,7 @@ use core::cmp::Ordering; use anyhow::Result; -use bls::{ - traits::{CachedPublicKey as _, SignatureBytes as _}, - SignatureBytes, -}; +use bls::SignatureBytes; use types::{ config::Config, electra::{consts::COMPOUNDING_WITHDRAWAL_PREFIX, containers::PendingDeposit}, diff --git a/helper_functions/src/predicates.rs b/helper_functions/src/predicates.rs index e0742bce8..e108a396b 100644 --- a/helper_functions/src/predicates.rs +++ b/helper_functions/src/predicates.rs @@ -6,7 +6,7 @@ use core::{ use anyhow::{ensure, Error as AnyhowError, Result}; use arithmetic::U64Ext as _; use bit_field::BitField as _; -use bls::{traits::CachedPublicKey as _, SignatureBytes}; +use bls::SignatureBytes; use itertools::Itertools as _; use ssz::SszHash as _; use tap::TryConv as _; @@ -123,13 +123,15 @@ fn validate_indexed_attestation( ); } + let backend = verifier.backend(); + // > Verify aggregate signature itertools::process_results( indexed_attestation .attesting_indices() .map(|validator_index| { accessors::public_key(state, validator_index)? - .decompress() + .decompress(backend) .map_err(AnyhowError::new) }), |public_keys| { @@ -586,10 +588,7 @@ mod spec_tests { #[cfg(test)] mod extra_tests { - use bls::{ - traits::{SecretKey as _, Signature as _}, - SecretKey, SecretKeyBytes, - }; + use bls::{Backend, SecretKey, SecretKeyBytes}; use std_ext::CopyExt as _; use tap::Conv as _; use types::{ @@ -839,7 +838,7 @@ mod extra_tests { &Config::mainnet(), &state, &attestation, - SingleVerifier, + SingleVerifier::new(Backend::default()), ) .expect_err("validation should fail"); } @@ -857,7 +856,7 @@ mod extra_tests { &Config::mainnet(), &state, &attestation, - SingleVerifier, + SingleVerifier::new(Backend::default()), ) .expect_err("validation should fail"); } @@ -884,7 +883,7 @@ mod extra_tests { &Config::mainnet(), &state, &attestation, - SingleVerifier, + SingleVerifier::new(Backend::default()), ) .expect_err("validation should fail"); } @@ -893,15 +892,19 @@ mod extra_tests { fn validate_received_indexed_attestation_valid_signature() -> Result<()> { let config = Config::mainnet(); - let secret_key_1 = b"????????????????????????????????" - .copy() - .conv::() - .try_conv::()?; + let secret_key_1 = SecretKey::try_from_with_backend( + b"????????????????????????????????" + .copy() + .conv::(), + Backend::default(), + )?; - let secret_key_2 = b"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" - .copy() - .conv::() - .try_conv::()?; + let secret_key_2 = SecretKey::try_from_with_backend( + b"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + .copy() + .conv::(), + Backend::default(), + )?; let state = Phase0BeaconState:: { validators: [ @@ -934,7 +937,12 @@ mod extra_tests { signature: aggregate_signature.into(), }; - validate_received_indexed_attestation(&config, &state, &attestation, SingleVerifier) + validate_received_indexed_attestation( + &config, + &state, + &attestation, + SingleVerifier::new(Backend::default()), + ) } fn inactive_validator() -> Validator { diff --git a/helper_functions/src/signing.rs b/helper_functions/src/signing.rs index 8c59005b9..600f91f0c 100644 --- a/helper_functions/src/signing.rs +++ b/helper_functions/src/signing.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use bls::{traits::SecretKey as _, CachedPublicKey, SecretKey, Signature, SignatureBytes}; +use bls::{Backend, CachedPublicKey, SecretKey, Signature, SignatureBytes}; use derive_more::From; use ssz::{Ssz, SszHash}; use types::{ @@ -77,8 +77,9 @@ pub trait SignForAllForks: SszHash { config: &Config, signature_bytes: SignatureBytes, cached_public_key: &CachedPublicKey, + backend: Backend, ) -> Result<()> { - SingleVerifier.verify_singular( + SingleVerifier::new(backend).verify_singular( self.signing_root(config), signature_bytes, cached_public_key, @@ -112,8 +113,9 @@ pub trait SignForAllForksWithGenesis: SszHash { beacon_state: &(impl BeaconState

+ ?Sized), signature_bytes: SignatureBytes, cached_public_key: &CachedPublicKey, + backend: Backend, ) -> Result<()> { - SingleVerifier.verify_singular( + SingleVerifier::new(backend).verify_singular( self.signing_root(config, beacon_state), signature_bytes, cached_public_key, @@ -149,8 +151,9 @@ pub trait SignForSingleFork: SszHash { beacon_state: &(impl BeaconState

+ ?Sized), signature_bytes: SignatureBytes, cached_public_key: &CachedPublicKey, + backend: Backend, ) -> Result<()> { - SingleVerifier.verify_singular( + SingleVerifier::new(backend).verify_singular( self.signing_root(config, beacon_state), signature_bytes, cached_public_key, @@ -191,8 +194,9 @@ pub trait SignForSingleForkAtSlot: SszHash { slot: Slot, signature_bytes: SignatureBytes, cached_public_key: &CachedPublicKey, + backend: Backend, ) -> Result<()> { - SingleVerifier.verify_singular( + SingleVerifier::new(backend).verify_singular( self.signing_root(config, beacon_state, slot), signature_bytes, cached_public_key, diff --git a/helper_functions/src/spec_tests.rs b/helper_functions/src/spec_tests.rs index 0cb279d0c..c49835a72 100644 --- a/helper_functions/src/spec_tests.rs +++ b/helper_functions/src/spec_tests.rs @@ -83,8 +83,7 @@ fn aggregate(case: Case) { let actual_output = input .into_iter() .map(|bytes| { - bytes - .try_into() + Signature::try_from_with_backend(bytes, Backend::default()) .expect("every aggregate test case contains a valid signature") }) .reduce(AggregateSignature::aggregate) @@ -99,7 +98,7 @@ fn eth_aggregate_pubkeys(case: Case) { #[rustfmt::skip] let result = itertools::process_results( - input.into_iter().map(PublicKey::try_from), + input.into_iter().map(|i| PublicKey::try_from_with_backend(bytes, Backend::default())), |public_keys| AggregatePublicKey::aggregate_nonempty(public_keys), ); @@ -129,10 +128,10 @@ fn eth_fast_aggregate_verify(case: Case) { let result = pubkeys .into_iter() - .map(PublicKey::try_from) + .map(|i| PublicKey::try_from_with_backend(i, Backend::default())) .collect::, _>>() .map(|public_keys| { - SingleVerifier.verify_aggregate_allowing_empty( + SingleVerifier::new(Backend::default()).verify_aggregate_allowing_empty( message, signature, public_keys.iter(), @@ -165,10 +164,10 @@ fn fast_aggregate_verify(case: Case) { let run = || -> Result<_, Error> { let public_keys = pubkeys .into_iter() - .map(PublicKey::try_from) + .map(|i| PublicKey::try_from_with_backend(i, Backend::default())) .collect::, _>>()?; - let signature = Signature::try_from(signature)?; + let signature = Signature::try_from_with_backend(signature, Backend::default())?; Ok(signature.fast_aggregate_verify(message, public_keys.iter())) }; @@ -193,7 +192,7 @@ fn sign(case: Case) { output, } = case.yaml("data"); - let secret_key = SecretKey::try_from(privkey); + let secret_key = SecretKey::try_from_with_backend(privkey, Backend::default()); if let Some(expected_output) = output { let actual_output = secret_key @@ -218,8 +217,8 @@ fn verify(case: Case) { } = input; let run = || -> Result<_, Error> { - let public_key = PublicKey::try_from(pubkey)?; - let signature = Signature::try_from(signature)?; + let public_key = PublicKey::try_from_with_backend(pubkey, Backend::default())?; + let signature = Signature::try_from_with_backend(signature, Backend::default())?; Ok(signature.verify(message, public_key)) }; diff --git a/helper_functions/src/verifier.rs b/helper_functions/src/verifier.rs index 56d8f2a7f..a09c6ade5 100644 --- a/helper_functions/src/verifier.rs +++ b/helper_functions/src/verifier.rs @@ -2,14 +2,13 @@ use anyhow::{ensure, Result}; use bls::{ - traits::{CachedPublicKey as _, PublicKey as _, Signature as _, SignatureBytes as _}, - AggregatePublicKey, AggregateSignature, CachedPublicKey, PublicKey, Signature, SignatureBytes, + AggregatePublicKey, AggregateSignature, Backend, CachedPublicKey, PublicKey, Signature, + SignatureBytes, }; use derive_more::Constructor; use enumset::{EnumSet, EnumSetType}; use rayon::iter::{IntoParallelRefIterator as _, ParallelBridge as _, ParallelIterator as _}; use static_assertions::assert_not_impl_any; -use tap::TryConv as _; use types::phase0::primitives::H256; use crate::error::{Error, SignatureKind}; @@ -67,6 +66,8 @@ pub trait Verifier { fn finish(&self) -> Result<()>; fn has_option(&self, option: VerifierOption) -> bool; + + fn backend(&self) -> Backend; } impl Verifier for &mut V { @@ -117,6 +118,11 @@ impl Verifier for &mut V { fn has_option(&self, option: VerifierOption) -> bool { (**self).has_option(option) } + + #[inline] + fn backend(&self) -> Backend { + (**self).backend() + } } pub struct NullVerifier; @@ -167,9 +173,16 @@ impl Verifier for NullVerifier { fn has_option(&self, _option: VerifierOption) -> bool { false } + + #[inline] + fn backend(&self) -> Backend { + Default::default() + } } -pub struct SingleVerifier; +pub struct SingleVerifier { + backend: Backend, +} impl Verifier for SingleVerifier { const IS_NULL: bool = false; @@ -185,8 +198,8 @@ impl Verifier for SingleVerifier { cached_public_key: &CachedPublicKey, signature_kind: SignatureKind, ) -> Result<()> { - let public_key = *cached_public_key.decompress()?; - let triple = Triple::new(message, signature_bytes, public_key); + let public_key = *cached_public_key.decompress(self.backend)?; + let triple = Triple::new(message, signature_bytes, public_key, self.backend); self.extend(core::iter::once(triple), signature_kind) } @@ -203,8 +216,7 @@ impl Verifier for SingleVerifier { // verifying signatures individually. Block processing now uses `Signature::multi_verify`, // which is even faster. ensure!( - signature_bytes - .try_conv::()? + AggregateSignature::try_from_with_backend(signature_bytes, self.backend)? .fast_aggregate_verify(message, public_keys.into_iter()), Error::SignatureInvalid(signature_kind), ); @@ -223,9 +235,10 @@ impl Verifier for SingleVerifier { message, signature_bytes, public_key, + backend, } = triple; - let signature = Signature::try_from(signature_bytes)?; + let signature = Signature::try_from_with_backend(signature_bytes, backend)?; ensure!( signature.verify(message, public_key), @@ -245,12 +258,24 @@ impl Verifier for SingleVerifier { fn has_option(&self, _option: VerifierOption) -> bool { false } + + #[inline] + fn backend(&self) -> Backend { + self.backend + } +} + +impl SingleVerifier { + pub fn new(backend: Backend) -> Self { + Self { backend } + } } #[derive(Default)] pub struct MultiVerifier { triples: Vec, options: EnumSet, + backend: Backend, } impl Verifier for MultiVerifier { @@ -269,8 +294,8 @@ impl Verifier for MultiVerifier { cached_public_key: &CachedPublicKey, _signature_kind: SignatureKind, ) -> Result<()> { - let public_key = *cached_public_key.decompress()?; - let triple = Triple::new(message, signature_bytes, public_key); + let public_key = *cached_public_key.decompress(self.backend)?; + let triple = Triple::new(message, signature_bytes, public_key, self.backend); self.triples.push(triple); Ok(()) } @@ -310,7 +335,9 @@ impl Verifier for MultiVerifier { let signatures = self .triples .par_iter() - .map(|triple| triple.signature_bytes.try_into()) + .map(|triple| { + Signature::try_from_with_backend(triple.signature_bytes, triple.backend.clone()) + }) .collect::, _>>()?; let public_keys = self.triples.iter().map(|triple| &triple.public_key); @@ -327,6 +354,11 @@ impl Verifier for MultiVerifier { fn has_option(&self, option: VerifierOption) -> bool { self.options.contains(option) } + + #[inline] + fn backend(&self) -> Backend { + self.backend + } } impl From> for MultiVerifier { @@ -347,11 +379,23 @@ impl MultiVerifier { } } -#[derive(Default, Constructor)] +#[derive(Constructor)] pub struct Triple { message: H256, signature_bytes: SignatureBytes, public_key: PublicKey, + backend: Backend, +} + +impl Default for Triple { + fn default() -> Self { + Self { + message: Default::default(), + signature_bytes: Default::default(), + public_key: PublicKey::default(Default::default()), + backend: Default::default(), + } + } } // `Triple` was originally an alias for a tuple and thus implemented `Copy`. @@ -394,13 +438,12 @@ impl Verifier for Triple { ) -> Result<()> { // TODO(Grandine Team): This may no longer be true as of Rayon 1.6.1. Benchmark again. // The `ParallelBridge::par_bridge` here outperforms "native" parallel iterators. - let public_key = public_keys - .into_iter() - .par_bridge() - .copied() - .reduce(AggregatePublicKey::default, AggregatePublicKey::aggregate); + let public_key = public_keys.into_iter().par_bridge().copied().reduce( + || AggregatePublicKey::default(self.backend.clone()), + AggregatePublicKey::aggregate, + ); - *self = Self::new(message, signature_bytes, public_key); + *self = Self::new(message, signature_bytes, public_key, self.backend.clone()); Ok(()) } @@ -423,6 +466,11 @@ impl Verifier for Triple { fn has_option(&self, _option: VerifierOption) -> bool { false } + + #[inline] + fn backend(&self) -> Backend { + self.backend + } } // TODO(Grandine Team): The first 2 variants are no longer used at runtime because @@ -438,9 +486,9 @@ pub enum VerifierOption { #[cfg(test)] mod tests { - use bls::{traits::SecretKey as _, SecretKey, SecretKeyBytes}; + use bls::{SecretKey, SecretKeyBytes}; use std_ext::CopyExt as _; - use tap::{Conv as _, TryConv as _}; + use tap::Conv as _; use super::*; @@ -451,7 +499,7 @@ mod tests { #[test] fn multi_verifier_finalize_succeeds_with_1_signature() -> Result<()> { - let secret_key = secret_key(); + let secret_key = secret_key(Default::default()); let public_key = secret_key.to_public_key().into(); let message = H256::default(); let signature = secret_key.sign(message).into(); @@ -461,11 +509,13 @@ mod tests { verifier.finish() } - fn secret_key() -> SecretKey { - b"????????????????????????????????" - .copy() - .conv::() - .try_conv::() - .expect("bytes encode a valid secret key") + fn secret_key(backend: Backend) -> SecretKey { + SecretKey::try_from_with_backend( + b"????????????????????????????????" + .copy() + .conv::(), + backend, + ) + .expect("bytes encode a valid secret key") } } diff --git a/interop/src/lib.rs b/interop/src/lib.rs index 36218d2f0..a2eb416e2 100644 --- a/interop/src/lib.rs +++ b/interop/src/lib.rs @@ -1,7 +1,7 @@ use core::num::NonZeroU64; use anyhow::Result; -use bls::{traits::SecretKey as _, SecretKey, SecretKeyBytes}; +use bls::{Backend, SecretKey, SecretKeyBytes}; use deposit_tree::DepositTree; use genesis::Incremental; use helper_functions::{misc, signing::SignForAllForks}; @@ -39,13 +39,14 @@ pub fn quick_start_beacon_state( config: &Config, genesis_time: UnixSeconds, validator_count: NonZeroU64, + backend: Backend, ) -> Result<(CombinedBeaconState

, DepositTree)> { - let mut incremental = Incremental::new(config); + let mut incremental = Incremental::new(config, backend); incremental.set_eth1_timestamp(QUICK_START_ETH1_BLOCK_TIMESTAMP); for index in 0..validator_count.get() { - let deposit_data = quick_start_deposit_data::

(config, &secret_key(index)); + let deposit_data = quick_start_deposit_data::

(config, &secret_key(index, backend)); incremental.add_deposit_data(deposit_data, index)?; } @@ -62,7 +63,7 @@ pub fn quick_start_beacon_state( /// #[must_use] -pub fn secret_key(validator_index: ValidatorIndex) -> SecretKey { +pub fn secret_key(validator_index: ValidatorIndex, backend: Backend) -> SecretKey { let index_hash = hashing::hash_256(validator_index.hash_tree_root()); let curve_order = BigUint::from_bytes_be(CURVE_ORDER); let secret_key_uint = BigUint::from_bytes_le(index_hash.as_bytes()) % &curve_order; @@ -70,8 +71,7 @@ pub fn secret_key(validator_index: ValidatorIndex) -> SecretKey { let mut padded = SecretKeyBytes::default(); padded.as_mut()[size_of::() - unpadded.len()..] .copy_from_slice(unpadded.as_slice()); - padded - .try_into() + SecretKey::try_from_with_backend(padded, backend) .expect("the algorithm given in the standard should produce valid secret keys") } @@ -101,7 +101,7 @@ pub fn quick_start_deposit_data(config: &Config, secret_key: &SecretK #[cfg(test)] mod tests { - use bls::PublicKeyBytes; + use bls::{PublicKey, PublicKeyBytes}; use super::*; @@ -162,14 +162,12 @@ mod tests { ]; for ((sk_bytes, pk_bytes), validator_index) in expected_keypairs.iter().copied().zip(0..) { - let expected_secret_key = SecretKeyBytes::from(sk_bytes) - .try_into() + let expected_secret_key = SecretKey::try_from_with_backend(SecretKeyBytes::from(sk_bytes), Backend::default()) .expect("every secret key given in the standard should be valid"); - let expected_public_key = PublicKeyBytes::from(pk_bytes) - .try_into() + let expected_public_key = PublicKey::try_from_with_backend(PublicKeyBytes::from(pk_bytes), Backend::default()) .expect("every public key given in the standard should be valid"); - let actual_secret_key = secret_key(validator_index); + let actual_secret_key = secret_key(validator_index, Backend::default()); let actual_public_key = actual_secret_key.to_public_key(); assert_eq!(actual_secret_key, expected_secret_key); diff --git a/operation_pools/src/sync_committee_agg_pool/tasks.rs b/operation_pools/src/sync_committee_agg_pool/tasks.rs index 96ead1fb2..85dcbe554 100644 --- a/operation_pools/src/sync_committee_agg_pool/tasks.rs +++ b/operation_pools/src/sync_committee_agg_pool/tasks.rs @@ -432,11 +432,12 @@ fn validate_external_contribution_and_proof( SignatureKind::ContributionAndProof, )?; + let backend = verifier.backend(); let participant_pubkeys = subcommittee_pubkeys .iter() .zip(contribution.aggregation_bits) .filter(|(_, bit)| *bit) - .map(|(pubkey, _)| pubkey.decompress()); + .map(|(pubkey, _)| pubkey.decompress(backend)); let signing_root = contribution diff --git a/transition_functions/src/altair/block_processing.rs b/transition_functions/src/altair/block_processing.rs index ca156c0b8..0fee57018 100644 --- a/transition_functions/src/altair/block_processing.rs +++ b/transition_functions/src/altair/block_processing.rs @@ -1,7 +1,7 @@ use anyhow::{ensure, Result}; use arithmetic::U64Ext as _; use bit_field::BitField as _; -use bls::traits::CachedPublicKey as _; +use bls::Backend; use helper_functions::{ accessors::{ self, attestation_epoch, get_attestation_participation_flags, get_base_reward, @@ -83,12 +83,13 @@ pub fn process_block_for_gossip( config: &Config, state: &BeaconState

, block: &SignedBeaconBlock

, + backend: Backend, ) -> Result<()> { debug_assert_eq!(state.slot, block.message.slot); unphased::process_block_header_for_gossip(state, &block.message)?; - SingleVerifier.verify_singular( + SingleVerifier::new(backend).verify_singular( block.message.signing_root(config, state), block.signature, accessors::public_key(state, block.message.proposer_index)?, @@ -208,8 +209,12 @@ fn process_operations( // The conditional is not needed for correctness. // It only serves to avoid overhead when processing blocks with no deposits. if !body.deposits.is_empty() { - let combined_deposits = - unphased::validate_deposits(config, state, body.deposits.iter().copied())?; + let combined_deposits = unphased::validate_deposits( + config, + state, + body.deposits.iter().copied(), + verifier.backend(), + )?; apply_deposits(state, body.deposits.len(), combined_deposits, slot_report)?; } @@ -343,6 +348,7 @@ pub fn process_deposit_data( config: &Config, state: &mut impl PostAltairBeaconState

, deposit_data: DepositData, + backend: Backend, ) -> Result> { let DepositData { pubkey, @@ -372,7 +378,10 @@ pub fn process_deposit_data( let pubkey = pubkey.into(); // > Fork-agnostic domain since deposits are valid across forks - if deposit_message.verify(config, signature, &pubkey).is_ok() { + if deposit_message + .verify(config, signature, &pubkey, backend) + .is_ok() + { let validator_index = state.validators().len_u64(); let combined_deposit = CombinedDeposit::NewValidator { @@ -557,13 +566,14 @@ pub fn verify_sync_aggregate_signature( // (see the doc comment for `Verifier::verify_aggregate_allowing_empty`). // This should not matter much in practice because empty sync aggregates are rare. + let backend = verifier.backend(); let participant_pubkeys = state .current_sync_committee() .pubkeys .iter() .zip(sync_committee_bits) .filter(|(_, bit)| *bit) - .map(|(pubkey, _)| pubkey.decompress()); + .map(|(pubkey, _)| pubkey.decompress(backend)); let previous_slot = misc::previous_slot(state.slot()); @@ -673,7 +683,7 @@ mod spec_tests { config, state, proposer_slashing, - SingleVerifier, + SingleVerifier::new(Backend::default()), NullSlotReport, ) }, @@ -689,7 +699,7 @@ mod spec_tests { config, state, &attester_slashing, - SingleVerifier, + SingleVerifier::new(Backend::default()), NullSlotReport, ) }, @@ -727,7 +737,8 @@ mod spec_tests { process_deposit_data, |config, state, deposit, _| { unphased::verify_deposit_merkle_branch(state, state.eth1_deposit_index, deposit)?; - process_deposit_data(config, state, deposit.data)?; + process_deposit_data(config, state, deposit.data, + SingleVerifier::new(Backend::default()))?; Ok(()) }, "deposit", @@ -742,7 +753,7 @@ mod spec_tests { config, state, voluntary_exit, - SingleVerifier, + SingleVerifier::new(Backend::default()), ) }, "voluntary_exit", @@ -757,7 +768,7 @@ mod spec_tests { config, state, sync_aggregate, - SingleVerifier, + SingleVerifier::new(Backend::default()), NullSlotReport, ) }, @@ -847,7 +858,7 @@ mod spec_tests { config, state, attestation, - SingleVerifier, + SingleVerifier::new(Backend::default()), )? } BlsSetting::Ignored => unphased::validate_attestation_with_verifier( diff --git a/transition_functions/src/altair/epoch_processing.rs b/transition_functions/src/altair/epoch_processing.rs index 286bafb95..970eca95f 100644 --- a/transition_functions/src/altair/epoch_processing.rs +++ b/transition_functions/src/altair/epoch_processing.rs @@ -3,6 +3,7 @@ use std::collections::HashMap; use anyhow::Result; use arithmetic::U64Ext as _; +use bls::Backend; use helper_functions::{ accessors::{get_current_epoch, get_next_sync_committee, total_active_balance}, misc::vec_of_default, @@ -39,7 +40,11 @@ pub struct EpochReport { pub post_balances: Vec, } -pub fn process_epoch(config: &Config, state: &mut AltairBeaconState) -> Result<()> { +pub fn process_epoch( + config: &Config, + state: &mut AltairBeaconState, + backend: Backend, +) -> Result<()> { #[cfg(feature = "metrics")] let _timer = METRICS .get() @@ -79,7 +84,7 @@ pub fn process_epoch(config: &Config, state: &mut AltairBeaconState unphased::process_randao_mixes_reset(state); unphased::process_historical_roots_update(state)?; process_participation_flag_updates(state); - process_sync_committee_updates(state)?; + process_sync_committee_updates(state, backend)?; state.cache.advance_epoch(); @@ -89,6 +94,7 @@ pub fn process_epoch(config: &Config, state: &mut AltairBeaconState pub fn epoch_report( config: &Config, state: &mut AltairBeaconState

, + backend: Backend, ) -> Result { let (statistics, mut summaries, participation) = epoch_intermediates::statistics(state); @@ -130,7 +136,7 @@ pub fn epoch_report( unphased::process_randao_mixes_reset(state); unphased::process_historical_roots_update(state)?; process_participation_flag_updates(state); - process_sync_committee_updates(state)?; + process_sync_committee_updates(state, backend)?; state.cache.advance_epoch(); @@ -269,11 +275,12 @@ pub fn process_participation_flag_updates(state: &mut impl PostAltair pub fn process_sync_committee_updates( state: &mut impl PostAltairBeaconState

, + backend: Backend, ) -> Result<()> { let next_epoch = get_current_epoch(state) + 1; if next_epoch.is_multiple_of(P::EPOCHS_PER_SYNC_COMMITTEE_PERIOD) { - let committee = get_next_sync_committee(state)?; + let committee = get_next_sync_committee(state, backend)?; *state.current_sync_committee_mut() = core::mem::replace(state.next_sync_committee_mut(), committee); } @@ -555,7 +562,9 @@ mod spec_tests { } fn run_sync_committee_updates_case(case: Case) { - run_case::

(case, process_sync_committee_updates); + run_case::

(case, |state| { + process_sync_committee_updates(state, Backend::default()) + }); } fn run_case( diff --git a/transition_functions/src/altair/slot_processing.rs b/transition_functions/src/altair/slot_processing.rs index 33ed33674..0bac5298b 100644 --- a/transition_functions/src/altair/slot_processing.rs +++ b/transition_functions/src/altair/slot_processing.rs @@ -1,4 +1,5 @@ use anyhow::{ensure, Result}; +use bls::Backend; use helper_functions::misc; use ssz::Hc; use types::{ @@ -12,6 +13,7 @@ pub fn process_slots( config: &Config, state: &mut Hc>, slot: Slot, + backend: Backend, ) -> Result<()> { ensure!( state.slot < slot, @@ -26,7 +28,7 @@ pub fn process_slots( // > Process epoch on the start slot of the next epoch if misc::is_epoch_start::

(state.slot + 1) { - epoch_processing::process_epoch(config, state)?; + epoch_processing::process_epoch(config, state, backend)?; } state.slot += 1; diff --git a/transition_functions/src/altair/state_transition.rs b/transition_functions/src/altair/state_transition.rs index b33a45b7e..4ec7db6cf 100644 --- a/transition_functions/src/altair/state_transition.rs +++ b/transition_functions/src/altair/state_transition.rs @@ -1,7 +1,6 @@ use core::ops::Not as _; use anyhow::{anyhow, Error as AnyhowError, Result}; -use bls::traits::CachedPublicKey as _; use helper_functions::{ accessors, error::SignatureKind, @@ -34,7 +33,7 @@ pub fn state_transition( // > Process slots (including those with no blocks) since block if process_slots.should_process(state, block) { - slot_processing::process_slots(config, state, block.slot)?; + slot_processing::process_slots(config, state, block.slot, verifier.backend())?; } // Verifying signatures and processing the block in parallel yields a significant speedup with @@ -130,6 +129,7 @@ pub fn verify_signatures( // Attester slashings + let backend = verifier.backend(); for attester_slashing in &block.message.body.attester_slashings { for attestation in [ &attester_slashing.attestation_1, @@ -142,7 +142,7 @@ pub fn verify_signatures( .copied() .map(|validator_index| { accessors::public_key(state, validator_index)? - .decompress() + .decompress(backend) .map_err(AnyhowError::new) }), |public_keys| { diff --git a/transition_functions/src/bellatrix/block_processing.rs b/transition_functions/src/bellatrix/block_processing.rs index 36480e7b0..4af96b4fe 100644 --- a/transition_functions/src/bellatrix/block_processing.rs +++ b/transition_functions/src/bellatrix/block_processing.rs @@ -1,4 +1,5 @@ use anyhow::{ensure, Result}; +use bls::Backend; use execution_engine::{ExecutionEngine, NullExecutionEngine}; use helper_functions::{ accessors::{self, get_current_epoch, get_randao_mix, initialize_shuffled_indices}, @@ -77,6 +78,7 @@ pub fn process_block_for_gossip( config: &Config, state: &BeaconState

, block: &SignedBeaconBlock

, + backend: Backend, ) -> Result<()> { debug_assert_eq!(state.slot, block.message.slot); @@ -86,7 +88,7 @@ pub fn process_block_for_gossip( process_execution_payload_for_gossip(config, state, &block.message.body)?; } - SingleVerifier.verify_singular( + SingleVerifier::new(backend).verify_singular( block.message.signing_root(config, state), block.signature, accessors::public_key(state, block.message.proposer_index)?, @@ -278,8 +280,12 @@ pub fn process_operations( // The conditional is not needed for correctness. // It only serves to avoid overhead when processing blocks with no deposits. if !body.deposits().is_empty() { - let combined_deposits = - unphased::validate_deposits(config, state, body.deposits().iter().copied())?; + let combined_deposits = unphased::validate_deposits( + config, + state, + body.deposits().iter().copied(), + verifier.backend(), + )?; altair::apply_deposits(state, body.deposits().len(), combined_deposits, slot_report)?; } @@ -439,7 +445,7 @@ mod spec_tests { config, state, proposer_slashing, - SingleVerifier, + SingleVerifier::new(Backend::default()), NullSlotReport, ) }, @@ -455,7 +461,7 @@ mod spec_tests { config, state, &attester_slashing, - SingleVerifier, + SingleVerifier::new(Backend::default()), NullSlotReport, ) }, @@ -508,7 +514,7 @@ mod spec_tests { config, state, voluntary_exit, - SingleVerifier, + SingleVerifier::new(Backend::default()), ) }, "voluntary_exit", @@ -523,7 +529,7 @@ mod spec_tests { config, state, sync_aggregate, - SingleVerifier, + SingleVerifier::new(Backend::default()), NullSlotReport, ) }, @@ -535,7 +541,7 @@ mod spec_tests { validation_tests! { validate_proposer_slashing, |config, state, proposer_slashing| { - unphased::validate_proposer_slashing(config, state, proposer_slashing) + unphased::validate_proposer_slashing(config, state, proposer_slashing, Backend::default()) }, "proposer_slashing", "consensus-spec-tests/tests/mainnet/bellatrix/operations/proposer_slashing/*/*", @@ -545,7 +551,7 @@ mod spec_tests { validation_tests! { validate_attester_slashing, |config, state, attester_slashing: AttesterSlashing

| { - unphased::validate_attester_slashing(config, state, &attester_slashing) + unphased::validate_attester_slashing(config, state, &attester_slashing, Backend::default()) }, "attester_slashing", "consensus-spec-tests/tests/mainnet/bellatrix/operations/attester_slashing/*/*", @@ -555,7 +561,7 @@ mod spec_tests { validation_tests! { validate_voluntary_exit, |config, state, voluntary_exit| { - unphased::validate_voluntary_exit(config, state, voluntary_exit) + unphased::validate_voluntary_exit(config, state, voluntary_exit, Backend::default()) }, "voluntary_exit", "consensus-spec-tests/tests/mainnet/bellatrix/operations/voluntary_exit/*/*", @@ -651,7 +657,7 @@ mod spec_tests { config, state, attestation, - SingleVerifier, + SingleVerifier::new(Backend::default()), )? } BlsSetting::Ignored => unphased::validate_attestation_with_verifier( @@ -670,8 +676,12 @@ mod spec_tests { state: &mut BeaconState

, deposit: Deposit, ) -> Result<()> { - let combined_deposits = - unphased::validate_deposits(config, state, core::iter::once(deposit))?; + let combined_deposits = unphased::validate_deposits( + config, + state, + core::iter::once(deposit), + Backend::default(), + )?; altair::apply_deposits(state, 1, combined_deposits, NullSlotReport) } diff --git a/transition_functions/src/bellatrix/epoch_processing.rs b/transition_functions/src/bellatrix/epoch_processing.rs index 2b93e56a9..f4619d2c8 100644 --- a/transition_functions/src/bellatrix/epoch_processing.rs +++ b/transition_functions/src/bellatrix/epoch_processing.rs @@ -1,6 +1,7 @@ use core::{cell::LazyCell, ops::Mul as _}; use anyhow::Result; +use bls::Backend; use helper_functions::{ accessors::{get_current_epoch, total_active_balance}, misc::vec_of_default, @@ -21,7 +22,11 @@ use crate::{ #[cfg(feature = "metrics")] use prometheus_metrics::METRICS; -pub fn process_epoch(config: &Config, state: &mut CapellaBeaconState) -> Result<()> { +pub fn process_epoch( + config: &Config, + state: &mut CapellaBeaconState, + backend: Backend, +) -> Result<()> { #[cfg(feature = "metrics")] let _timer = METRICS .get() @@ -61,7 +66,7 @@ pub fn process_epoch(config: &Config, state: &mut CapellaBeaconState( config: &Config, state: &mut CapellaBeaconState

, + backend: Backend, ) -> Result { let (statistics, mut summaries, participation) = altair::statistics(state); @@ -112,7 +118,7 @@ pub fn epoch_report( unphased::process_randao_mixes_reset(state); unphased::process_historical_roots_update(state)?; altair::process_participation_flag_updates(state); - altair::process_sync_committee_updates(state)?; + altair::process_sync_committee_updates(state, backend)?; state.cache.advance_epoch(); @@ -449,7 +455,9 @@ mod spec_tests { } fn run_sync_committee_updates_case(case: Case) { - run_case::

(case, altair::process_sync_committee_updates); + run_case::

(case, |state| { + altair::process_sync_committee_updates(state, Backend::default()) + }); } fn run_case( diff --git a/transition_functions/src/bellatrix/slot_processing.rs b/transition_functions/src/bellatrix/slot_processing.rs index 5c12694e1..982e97659 100644 --- a/transition_functions/src/bellatrix/slot_processing.rs +++ b/transition_functions/src/bellatrix/slot_processing.rs @@ -1,4 +1,5 @@ use anyhow::{ensure, Result}; +use bls::Backend; use helper_functions::misc; use ssz::Hc; use types::{ @@ -12,6 +13,7 @@ pub fn process_slots( config: &Config, state: &mut Hc>, slot: Slot, + backend: Backend, ) -> Result<()> { ensure!( state.slot < slot, @@ -26,7 +28,7 @@ pub fn process_slots( // > Process epoch on the start slot of the next epoch if misc::is_epoch_start::

(state.slot + 1) { - epoch_processing::process_epoch(config, state)?; + epoch_processing::process_epoch(config, state, backend)?; } state.slot += 1; diff --git a/transition_functions/src/bellatrix/state_transition.rs b/transition_functions/src/bellatrix/state_transition.rs index af351f91b..4ade2249f 100644 --- a/transition_functions/src/bellatrix/state_transition.rs +++ b/transition_functions/src/bellatrix/state_transition.rs @@ -1,7 +1,6 @@ use core::ops::Not as _; use anyhow::{anyhow, Error as AnyhowError, Result}; -use bls::traits::CachedPublicKey as _; use execution_engine::ExecutionEngine; use helper_functions::{ accessors, @@ -39,8 +38,9 @@ pub fn state_transition( let block = &signed_block.message; // > Process slots (including those with no blocks) since block + let backend = verifier.backend(); if process_slots.should_process(state, block) { - slot_processing::process_slots(config, state, block.slot)?; + slot_processing::process_slots(config, state, block.slot, backend)?; } let verify_signatures = V::IS_NULL.not().then(|| { @@ -135,6 +135,7 @@ pub fn verify_signatures( // Attester slashings + let backend = verifier.backend(); for attester_slashing in &block.message.body.attester_slashings { for attestation in [ &attester_slashing.attestation_1, @@ -147,7 +148,7 @@ pub fn verify_signatures( .copied() .map(|validator_index| { accessors::public_key(state, validator_index)? - .decompress() + .decompress(backend) .map_err(AnyhowError::new) }), |public_keys| { diff --git a/transition_functions/src/capella/block_processing.rs b/transition_functions/src/capella/block_processing.rs index 3ad62727b..3bfb4e394 100644 --- a/transition_functions/src/capella/block_processing.rs +++ b/transition_functions/src/capella/block_processing.rs @@ -1,6 +1,7 @@ use core::ops::{Add as _, Index as _, Rem as _}; use anyhow::{ensure, Result}; +use bls::Backend; use execution_engine::{ExecutionEngine, NullExecutionEngine}; use helper_functions::{ accessors::{self, get_current_epoch, get_randao_mix, initialize_shuffled_indices}, @@ -83,6 +84,7 @@ pub fn process_block_for_gossip( config: &Config, state: &BeaconState

, block: &SignedBeaconBlock

, + backend: Backend, ) -> Result<()> { debug_assert_eq!(state.slot, block.message.slot); @@ -90,7 +92,7 @@ pub fn process_block_for_gossip( process_execution_payload_for_gossip(config, state, &block.message.body)?; - SingleVerifier.verify_singular( + SingleVerifier::new(backend).verify_singular( block.message.signing_root(config, state), block.signature, accessors::public_key(state, block.message.proposer_index)?, @@ -289,8 +291,12 @@ pub fn process_operations( // The conditional is not needed for correctness. // It only serves to avoid overhead when processing blocks with no deposits. if !body.deposits().is_empty() { - let combined_deposits = - unphased::validate_deposits(config, state, body.deposits().iter().copied())?; + let combined_deposits = unphased::validate_deposits( + config, + state, + body.deposits().iter().copied(), + verifier.backend(), + )?; altair::apply_deposits(state, body.deposits().len(), combined_deposits, slot_report)?; } @@ -336,12 +342,13 @@ pub fn validate_bls_to_execution_change( config: &Config, state: &(impl PostCapellaBeaconState

+ ?Sized), bls_to_execution_change: SignedBlsToExecutionChange, + backend: Backend, ) -> Result<()> { validate_bls_to_execution_change_with_verifier( config, state, bls_to_execution_change, - SingleVerifier, + SingleVerifier::new(backend), ) } @@ -602,7 +609,7 @@ mod spec_tests { config, state, proposer_slashing, - SingleVerifier, + SingleVerifier::new(Backend::default()), NullSlotReport, ) }, @@ -618,7 +625,7 @@ mod spec_tests { config, state, &attester_slashing, - SingleVerifier, + SingleVerifier::new(Backend::default()), NullSlotReport, ) }, @@ -649,7 +656,7 @@ mod spec_tests { config, state, bls_to_execution_change, - SingleVerifier, + SingleVerifier::new(Backend::default()), ) }, "address_change", @@ -671,7 +678,7 @@ mod spec_tests { process_deposit_data, |config, state, deposit, _| { unphased::verify_deposit_merkle_branch(state, state.eth1_deposit_index, deposit)?; - altair::process_deposit_data(config, state, deposit.data)?; + altair::process_deposit_data(config, state, deposit.data, Backend::default())?; Ok(()) }, "deposit", @@ -686,7 +693,7 @@ mod spec_tests { config, state, voluntary_exit, - SingleVerifier, + SingleVerifier::new(Backend::default()), ) }, "voluntary_exit", @@ -701,7 +708,7 @@ mod spec_tests { config, state, sync_aggregate, - SingleVerifier, + SingleVerifier::new(Backend::default()), NullSlotReport, ) }, @@ -713,7 +720,7 @@ mod spec_tests { validation_tests! { validate_proposer_slashing, |config, state, proposer_slashing| { - unphased::validate_proposer_slashing(config, state, proposer_slashing) + unphased::validate_proposer_slashing(config, state, proposer_slashing, Backend::default()) }, "proposer_slashing", "consensus-spec-tests/tests/mainnet/capella/operations/proposer_slashing/*/*", @@ -723,7 +730,7 @@ mod spec_tests { validation_tests! { validate_attester_slashing, |config, state, attester_slashing: AttesterSlashing

| { - unphased::validate_attester_slashing(config, state, &attester_slashing) + unphased::validate_attester_slashing(config, state, &attester_slashing, Backend::default()) }, "attester_slashing", "consensus-spec-tests/tests/mainnet/capella/operations/attester_slashing/*/*", @@ -733,7 +740,7 @@ mod spec_tests { validation_tests! { validate_voluntary_exit, |config, state, voluntary_exit| { - unphased::validate_voluntary_exit(config, state, voluntary_exit) + unphased::validate_voluntary_exit(config, state, voluntary_exit, Backend::default()) }, "voluntary_exit", "consensus-spec-tests/tests/mainnet/capella/operations/voluntary_exit/*/*", @@ -743,7 +750,7 @@ mod spec_tests { validation_tests! { validate_bls_to_execution_change, |config, state, bls_to_execution_change| { - validate_bls_to_execution_change(config, state, bls_to_execution_change) + validate_bls_to_execution_change(config, state, bls_to_execution_change, Backend::default()) }, "address_change", "consensus-spec-tests/tests/mainnet/capella/operations/bls_to_execution_change/*/*", @@ -860,7 +867,7 @@ mod spec_tests { config, state, attestation, - SingleVerifier, + SingleVerifier::new(Backend::default()), )? } BlsSetting::Ignored => unphased::validate_attestation_with_verifier( @@ -879,8 +886,12 @@ mod spec_tests { state: &mut BeaconState

, deposit: Deposit, ) -> Result<()> { - let combined_deposits = - unphased::validate_deposits(config, state, core::iter::once(deposit))?; + let combined_deposits = unphased::validate_deposits( + config, + state, + core::iter::once(deposit), + Backend::default(), + )?; altair::apply_deposits(state, 1, combined_deposits, NullSlotReport) } diff --git a/transition_functions/src/capella/epoch_processing.rs b/transition_functions/src/capella/epoch_processing.rs index 2732d0a0f..728a388fa 100644 --- a/transition_functions/src/capella/epoch_processing.rs +++ b/transition_functions/src/capella/epoch_processing.rs @@ -1,5 +1,6 @@ use anyhow::Result; use arithmetic::{NonZeroExt as _, U64Ext as _}; +use bls::Backend; use helper_functions::{accessors::get_next_epoch, misc::vec_of_default}; use ssz::SszHash as _; use types::{ @@ -18,7 +19,11 @@ use crate::{ #[cfg(feature = "metrics")] use prometheus_metrics::METRICS; -pub fn process_epoch(config: &Config, state: &mut BeaconState) -> Result<()> { +pub fn process_epoch( + config: &Config, + state: &mut BeaconState, + backend: Backend, +) -> Result<()> { #[cfg(feature = "metrics")] let _timer = METRICS .get() @@ -61,14 +66,18 @@ pub fn process_epoch(config: &Config, state: &mut BeaconState) -> R process_historical_summaries_update(state)?; altair::process_participation_flag_updates(state); - altair::process_sync_committee_updates(state)?; + altair::process_sync_committee_updates(state, backend)?; state.cache.advance_epoch(); Ok(()) } -pub fn epoch_report(config: &Config, state: &mut BeaconState

) -> Result { +pub fn epoch_report( + config: &Config, + state: &mut BeaconState

, + backend: Backend, +) -> Result { let (statistics, mut summaries, participation) = altair::statistics(state); altair::process_justification_and_finalization(state, statistics); @@ -109,7 +118,7 @@ pub fn epoch_report(config: &Config, state: &mut BeaconState

) -> R unphased::process_randao_mixes_reset(state); unphased::process_historical_roots_update(state)?; altair::process_participation_flag_updates(state); - altair::process_sync_committee_updates(state)?; + altair::process_sync_committee_updates(state, backend)?; state.cache.advance_epoch(); @@ -410,7 +419,9 @@ mod spec_tests { } fn run_sync_committee_updates_case(case: Case) { - run_case::

(case, altair::process_sync_committee_updates); + run_case::

(case, |state| { + altair::process_sync_committee_updates(state, Backend::default()) + }); } fn run_case( diff --git a/transition_functions/src/capella/slot_processing.rs b/transition_functions/src/capella/slot_processing.rs index 45ff060cc..4f239d1ff 100644 --- a/transition_functions/src/capella/slot_processing.rs +++ b/transition_functions/src/capella/slot_processing.rs @@ -1,4 +1,5 @@ use anyhow::{ensure, Result}; +use bls::Backend; use helper_functions::misc; use ssz::Hc; use types::{ @@ -12,6 +13,7 @@ pub fn process_slots( config: &Config, state: &mut Hc>, slot: Slot, + backend: Backend, ) -> Result<()> { ensure!( state.slot < slot, @@ -26,7 +28,7 @@ pub fn process_slots( // > Process epoch on the start slot of the next epoch if misc::is_epoch_start::

(state.slot + 1) { - epoch_processing::process_epoch(config, state)?; + epoch_processing::process_epoch(config, state, backend)?; } state.slot += 1; diff --git a/transition_functions/src/capella/state_transition.rs b/transition_functions/src/capella/state_transition.rs index 8789105c7..c0ac1209b 100644 --- a/transition_functions/src/capella/state_transition.rs +++ b/transition_functions/src/capella/state_transition.rs @@ -1,7 +1,6 @@ use core::ops::Not as _; use anyhow::{anyhow, Error as AnyhowError, Result}; -use bls::traits::CachedPublicKey as _; use execution_engine::ExecutionEngine; use helper_functions::{ accessors, @@ -37,10 +36,11 @@ pub fn state_transition( slot_report: impl SlotReport + Send, ) -> Result<()> { let block = &signed_block.message; + let backend = verifier.backend(); // > Process slots (including those with no blocks) since block if process_slots.should_process(state, block) { - slot_processing::process_slots(config, state, block.slot)?; + slot_processing::process_slots(config, state, block.slot, backend)?; } let verify_signatures = V::IS_NULL.not().then(|| { @@ -135,6 +135,7 @@ pub fn verify_signatures( // Attester slashings + let backend = verifier.backend(); for attester_slashing in &block.message.body.attester_slashings { for attestation in [ &attester_slashing.attestation_1, @@ -147,7 +148,7 @@ pub fn verify_signatures( .copied() .map(|validator_index| { accessors::public_key(state, validator_index)? - .decompress() + .decompress(backend) .map_err(AnyhowError::new) }), |public_keys| { diff --git a/transition_functions/src/combined.rs b/transition_functions/src/combined.rs index dfbc5dc80..8a7da0ae0 100644 --- a/transition_functions/src/combined.rs +++ b/transition_functions/src/combined.rs @@ -1,4 +1,5 @@ use anyhow::{bail, ensure, Result}; +use bls::Backend; use derive_more::From; use enum_iterator::Sequence as _; use execution_engine::{ExecutionEngine, NullExecutionEngine}; @@ -116,7 +117,7 @@ pub fn custom_state_transition( ) -> Result<()> { // > Process slots (including those with no blocks) since block if process_slots.should_process(state, block.message()) { - self::process_slots(config, state, block.message().slot())?; + self::process_slots(config, state, block.message().slot(), verifier.backend())?; } let process_slots = ProcessSlots::Never; @@ -200,6 +201,7 @@ pub fn verify_base_signature_with_head_state( config: &Config, head_state: &BeaconState

, block: &SignedBeaconBlock

, + backend: Backend, ) -> Result<()> { let phase = config.phase_at_slot::

(block.message().slot()); let fork_version = config.version(phase); @@ -214,7 +216,7 @@ pub fn verify_base_signature_with_head_state( let signing_root = misc::compute_signing_root(block.message(), domain); - SingleVerifier.verify_singular( + SingleVerifier::new(backend).verify_singular( signing_root, block.signature(), accessors::public_key(head_state, block.message().proposer_index())?, @@ -264,6 +266,7 @@ pub fn process_slots( config: &Config, state: &mut BeaconState

, slot: Slot, + backend: Backend, ) -> Result<()> { // This validation is not required to pass `consensus-spec-tests`. // `process_block_header` already prevents multiple blocks from being applied in the same slot @@ -307,7 +310,9 @@ pub fn process_slots( } if Toption::Some(last_slot_in_phase) == altair_fork_slot { - *state = fork::upgrade_to_altair(config, phase0_state.as_ref().clone())?.into(); + *state = + fork::upgrade_to_altair(config, phase0_state.as_ref().clone(), backend)? + .into(); made_progress = true; } @@ -320,7 +325,7 @@ pub fn process_slots( .expect("result of min should always be Some because slot is always Some"); if altair_state.slot < last_slot_in_phase { - altair::process_slots(config, altair_state, last_slot_in_phase)?; + altair::process_slots(config, altair_state, last_slot_in_phase, backend)?; made_progress = true; } @@ -340,7 +345,7 @@ pub fn process_slots( .expect("result of min should always be Some because slot is always Some"); if bellatrix_state.slot < last_slot_in_phase { - bellatrix::process_slots(config, bellatrix_state, last_slot_in_phase)?; + bellatrix::process_slots(config, bellatrix_state, last_slot_in_phase, backend)?; made_progress = true; } @@ -360,7 +365,7 @@ pub fn process_slots( .expect("result of min should always be Some because slot is always Some"); if capella_state.slot < last_slot_in_phase { - capella::process_slots(config, capella_state, last_slot_in_phase)?; + capella::process_slots(config, capella_state, last_slot_in_phase, backend)?; made_progress = true; } @@ -379,7 +384,7 @@ pub fn process_slots( .expect("result of min should always be Some because slot is always Some"); if deneb_state.slot < last_slot_in_phase { - deneb::process_slots(config, deneb_state, last_slot_in_phase)?; + deneb::process_slots(config, deneb_state, last_slot_in_phase, backend)?; made_progress = true; } @@ -391,7 +396,7 @@ pub fn process_slots( } } BeaconState::Electra(electra_state) => { - electra::process_slots(config, electra_state, slot)?; + electra::process_slots(config, electra_state, slot, backend)?; made_progress = true; } @@ -436,30 +441,38 @@ pub fn process_justification_and_finalization(state: &mut BeaconState) -> Result<()> { +pub fn process_epoch( + config: &Config, + state: &mut BeaconState, + backend: Backend, +) -> Result<()> { match state { BeaconState::Phase0(state) => phase0::process_epoch(config, state), - BeaconState::Altair(state) => altair::process_epoch(config, state), - BeaconState::Bellatrix(state) => bellatrix::process_epoch(config, state), - BeaconState::Capella(state) => capella::process_epoch(config, state), - BeaconState::Deneb(state) => deneb::process_epoch(config, state), - BeaconState::Electra(state) => electra::process_epoch(config, state), + BeaconState::Altair(state) => altair::process_epoch(config, state, backend), + BeaconState::Bellatrix(state) => bellatrix::process_epoch(config, state, backend), + BeaconState::Capella(state) => capella::process_epoch(config, state, backend), + BeaconState::Deneb(state) => deneb::process_epoch(config, state, backend), + BeaconState::Electra(state) => electra::process_epoch(config, state, backend), } } -pub fn epoch_report(config: &Config, state: &mut BeaconState) -> Result { - process_slots_for_epoch_report(config, state)?; +pub fn epoch_report( + config: &Config, + state: &mut BeaconState, + backend: Backend, +) -> Result { + process_slots_for_epoch_report(config, state, backend)?; let report = match state { BeaconState::Phase0(state) => phase0::epoch_report(config, state)?.into(), - BeaconState::Altair(state) => altair::epoch_report(config, state)?.into(), - BeaconState::Bellatrix(state) => bellatrix::epoch_report(config, state)?.into(), - BeaconState::Capella(state) => capella::epoch_report(config, state)?.into(), - BeaconState::Deneb(state) => deneb::epoch_report(config, state)?.into(), - BeaconState::Electra(state) => electra::epoch_report(config, state)?.into(), + BeaconState::Altair(state) => altair::epoch_report(config, state, backend)?.into(), + BeaconState::Bellatrix(state) => bellatrix::epoch_report(config, state, backend)?.into(), + BeaconState::Capella(state) => capella::epoch_report(config, state, backend)?.into(), + BeaconState::Deneb(state) => deneb::epoch_report(config, state, backend)?.into(), + BeaconState::Electra(state) => electra::epoch_report(config, state, backend)?.into(), }; - post_process_slots_for_epoch_report(config, state)?; + post_process_slots_for_epoch_report(config, state, backend)?; Ok(report) } @@ -467,12 +480,13 @@ pub fn epoch_report(config: &Config, state: &mut BeaconState) -> Re fn process_slots_for_epoch_report( config: &Config, state: &mut BeaconState

, + backend: Backend, ) -> Result<()> { let next_epoch = accessors::get_next_epoch(state); let last_slot = misc::compute_start_slot_at_epoch::

(next_epoch) - 1; if state.slot() < last_slot { - process_slots(config, state, last_slot)?; + process_slots(config, state, last_slot, backend)?; } unphased::process_slot(state); @@ -485,6 +499,7 @@ fn process_slots_for_epoch_report( fn post_process_slots_for_epoch_report( config: &Config, state: &mut BeaconState

, + backend: Backend, ) -> Result<()> { let post_slot = state.slot() + 1; @@ -502,7 +517,9 @@ fn post_process_slots_for_epoch_report( let altair_fork_slot = config.fork_slot::

(Phase::Altair); if Toption::Some(post_slot) == altair_fork_slot { - *state = fork::upgrade_to_altair(config, phase0_state.as_ref().clone())?.into(); + *state = + fork::upgrade_to_altair(config, phase0_state.as_ref().clone(), backend)? + .into(); } } BeaconState::Altair(altair_state) => { @@ -610,25 +627,26 @@ pub fn process_block_for_gossip( config: &Config, state: &BeaconState

, block: &SignedBeaconBlock

, + backend: Backend, ) -> Result<()> { match (state, block) { (BeaconState::Phase0(state), SignedBeaconBlock::Phase0(block)) => { - phase0::process_block_for_gossip(config, state, block) + phase0::process_block_for_gossip(config, state, block, backend) } (BeaconState::Altair(state), SignedBeaconBlock::Altair(block)) => { - altair::process_block_for_gossip(config, state, block) + altair::process_block_for_gossip(config, state, block, backend) } (BeaconState::Bellatrix(state), SignedBeaconBlock::Bellatrix(block)) => { - bellatrix::process_block_for_gossip(config, state, block) + bellatrix::process_block_for_gossip(config, state, block, backend) } (BeaconState::Capella(state), SignedBeaconBlock::Capella(block)) => { - capella::process_block_for_gossip(config, state, block) + capella::process_block_for_gossip(config, state, block, backend) } (BeaconState::Deneb(state), SignedBeaconBlock::Deneb(block)) => { - deneb::process_block_for_gossip(config, state, block) + deneb::process_block_for_gossip(config, state, block, backend) } (BeaconState::Electra(state), SignedBeaconBlock::Electra(block)) => { - electra::process_block_for_gossip(config, state, block) + electra::process_block_for_gossip(config, state, block, backend) } (state, _) => { // This match arm will silently match any new phases. @@ -705,26 +723,33 @@ pub fn process_deposit_data( config: &Config, state: &mut BeaconState, deposit_data: DepositData, + backend: Backend, ) -> Result> { match state { - BeaconState::Phase0(state) => phase0::process_deposit_data(config, state, deposit_data), - BeaconState::Altair(state) => altair::process_deposit_data(config, state, deposit_data), + BeaconState::Phase0(state) => { + phase0::process_deposit_data(config, state, deposit_data, backend) + } + BeaconState::Altair(state) => { + altair::process_deposit_data(config, state, deposit_data, backend) + } BeaconState::Bellatrix(state) => { // The use of `altair::process_deposit_data` is intentional. // Bellatrix does not modify `process_deposit_data`. - altair::process_deposit_data(config, state, deposit_data) + altair::process_deposit_data(config, state, deposit_data, backend) } BeaconState::Capella(state) => { // The use of `altair::process_deposit_data` is intentional. // Capella does not modify `process_deposit_data`. - altair::process_deposit_data(config, state, deposit_data) + altair::process_deposit_data(config, state, deposit_data, backend) } BeaconState::Deneb(state) => { // The use of `altair::process_deposit_data` is intentional. // Deneb does not modify `process_deposit_data`. - altair::process_deposit_data(config, state, deposit_data) + altair::process_deposit_data(config, state, deposit_data, backend) + } + BeaconState::Electra(state) => { + electra::process_deposit_data(config, state, deposit_data, backend) } - BeaconState::Electra(state) => electra::process_deposit_data(config, state, deposit_data), } } @@ -873,7 +898,7 @@ mod spec_tests { let slots = case.yaml::("slots"); let last_slot = state.slot() + slots; - process_slots(config, &mut state, last_slot) + process_slots(config, &mut state, last_slot, Backend::default()) .expect("every slot processing test should perform processing successfully"); assert_eq!(state, expected_post); @@ -989,7 +1014,7 @@ mod spec_tests { blocks .into_iter() .try_for_each(|block| { - process_slots(config, state, block.message().slot())?; + process_slots(config, state, block.message().slot(), Backend::default())?; let (message, _) = block.split(); diff --git a/transition_functions/src/deneb/block_processing.rs b/transition_functions/src/deneb/block_processing.rs index 2c8c9d918..f40ced292 100644 --- a/transition_functions/src/deneb/block_processing.rs +++ b/transition_functions/src/deneb/block_processing.rs @@ -1,4 +1,5 @@ use anyhow::{ensure, Result}; +use bls::Backend; use execution_engine::{ExecutionEngine, NullExecutionEngine}; use helper_functions::{ accessors::{ @@ -82,6 +83,7 @@ pub fn process_block_for_gossip( config: &Config, state: &DenebBeaconState

, block: &SignedBeaconBlock

, + backend: Backend, ) -> Result<()> { debug_assert_eq!(state.slot, block.message.slot); @@ -89,7 +91,7 @@ pub fn process_block_for_gossip( process_execution_payload_for_gossip(config, state, &block.message.body)?; - SingleVerifier.verify_singular( + SingleVerifier::new(backend).verify_singular( block.message.signing_root(config, state), block.signature, accessors::public_key(state, block.message.proposer_index)?, @@ -298,8 +300,12 @@ pub fn process_operations( // The conditional is not needed for correctness. // It only serves to avoid overhead when processing blocks with no deposits. if !body.deposits().is_empty() { - let combined_deposits = - unphased::validate_deposits(config, state, body.deposits().iter().copied())?; + let combined_deposits = unphased::validate_deposits( + config, + state, + body.deposits().iter().copied(), + verifier.backend(), + )?; altair::apply_deposits(state, body.deposits().len(), combined_deposits, slot_report)?; } @@ -478,7 +484,7 @@ mod spec_tests { config, state, proposer_slashing, - SingleVerifier, + SingleVerifier::new(Backend::default()), NullSlotReport, ) }, @@ -494,7 +500,7 @@ mod spec_tests { config, state, &attester_slashing, - SingleVerifier, + SingleVerifier::new(Backend::default()), NullSlotReport, ) }, @@ -525,7 +531,7 @@ mod spec_tests { config, state, bls_to_execution_change, - SingleVerifier, + SingleVerifier::new(Backend::default()), ) }, "address_change", @@ -547,7 +553,7 @@ mod spec_tests { process_deposit_data, |config, state, deposit, _| { unphased::verify_deposit_merkle_branch(state, state.eth1_deposit_index, deposit)?; - altair::process_deposit_data(config, state, deposit.data)?; + altair::process_deposit_data(config, state, deposit.data, Backend::default())?; Ok(()) }, "deposit", @@ -562,7 +568,7 @@ mod spec_tests { config, state, voluntary_exit, - SingleVerifier, + SingleVerifier::new(Backend::default()), ) }, "voluntary_exit", @@ -577,7 +583,7 @@ mod spec_tests { config, state, sync_aggregate, - SingleVerifier, + SingleVerifier::new(Backend::default()), NullSlotReport, ) }, @@ -589,7 +595,7 @@ mod spec_tests { validation_tests! { validate_proposer_slashing, |config, state, proposer_slashing| { - unphased::validate_proposer_slashing(config, state, proposer_slashing) + unphased::validate_proposer_slashing(config, state, proposer_slashing, Backend::default()) }, "proposer_slashing", "consensus-spec-tests/tests/mainnet/deneb/operations/proposer_slashing/*/*", @@ -599,7 +605,7 @@ mod spec_tests { validation_tests! { validate_attester_slashing, |config, state, attester_slashing: AttesterSlashing

| { - unphased::validate_attester_slashing(config, state, &attester_slashing) + unphased::validate_attester_slashing(config, state, &attester_slashing, Backend::default()) }, "attester_slashing", "consensus-spec-tests/tests/mainnet/deneb/operations/attester_slashing/*/*", @@ -609,7 +615,7 @@ mod spec_tests { validation_tests! { validate_voluntary_exit, |config, state, voluntary_exit| { - unphased::validate_voluntary_exit(config, state, voluntary_exit) + unphased::validate_voluntary_exit(config, state, voluntary_exit, Backend::default()) }, "voluntary_exit", "consensus-spec-tests/tests/mainnet/deneb/operations/voluntary_exit/*/*", @@ -620,7 +626,7 @@ mod spec_tests { validation_tests! { validate_bls_to_execution_change, |config, state, bls_to_execution_change| { - capella::validate_bls_to_execution_change(config, state, bls_to_execution_change) + capella::validate_bls_to_execution_change(config, state, bls_to_execution_change, Backend::default()) }, "address_change", "consensus-spec-tests/tests/mainnet/deneb/operations/bls_to_execution_change/*/*", @@ -732,9 +738,12 @@ mod spec_tests { bls_setting: BlsSetting, ) -> Result<()> { match bls_setting { - BlsSetting::Optional | BlsSetting::Required => { - validate_attestation_with_verifier(config, state, attestation, SingleVerifier)? - } + BlsSetting::Optional | BlsSetting::Required => validate_attestation_with_verifier( + config, + state, + attestation, + SingleVerifier::new(Backend::default()), + )?, BlsSetting::Ignored => { validate_attestation_with_verifier(config, state, attestation, NullVerifier)? } @@ -748,8 +757,12 @@ mod spec_tests { state: &mut DenebBeaconState

, deposit: Deposit, ) -> Result<()> { - let combined_deposits = - unphased::validate_deposits(config, state, core::iter::once(deposit))?; + let combined_deposits = unphased::validate_deposits( + config, + state, + core::iter::once(deposit), + Backend::default(), + )?; altair::apply_deposits(state, 1, combined_deposits, NullSlotReport) } diff --git a/transition_functions/src/deneb/epoch_processing.rs b/transition_functions/src/deneb/epoch_processing.rs index a7320a528..58b8933b9 100644 --- a/transition_functions/src/deneb/epoch_processing.rs +++ b/transition_functions/src/deneb/epoch_processing.rs @@ -1,5 +1,6 @@ use anyhow::Result; use arithmetic::{NonZeroExt as _, U64Ext as _}; +use bls::Backend; use helper_functions::{ accessors::{get_current_epoch, get_next_epoch, get_validator_activation_churn_limit}, misc::{compute_activation_exit_epoch, vec_of_default}, @@ -24,7 +25,11 @@ use crate::{ #[cfg(feature = "metrics")] use prometheus_metrics::METRICS; -pub fn process_epoch(config: &Config, state: &mut BeaconState) -> Result<()> { +pub fn process_epoch( + config: &Config, + state: &mut BeaconState, + backend: Backend, +) -> Result<()> { #[cfg(feature = "metrics")] let _timer = METRICS .get() @@ -68,14 +73,18 @@ pub fn process_epoch(config: &Config, state: &mut BeaconState) -> R process_historical_summaries_update(state)?; altair::process_participation_flag_updates(state); - altair::process_sync_committee_updates(state)?; + altair::process_sync_committee_updates(state, backend)?; state.cache.advance_epoch(); Ok(()) } -pub fn epoch_report(config: &Config, state: &mut BeaconState

) -> Result { +pub fn epoch_report( + config: &Config, + state: &mut BeaconState

, + backend: Backend, +) -> Result { let (statistics, mut summaries, participation) = altair::statistics(state); altair::process_justification_and_finalization(state, statistics); @@ -116,7 +125,7 @@ pub fn epoch_report(config: &Config, state: &mut BeaconState

) -> R unphased::process_randao_mixes_reset(state); unphased::process_historical_roots_update(state)?; altair::process_participation_flag_updates(state); - altair::process_sync_committee_updates(state)?; + altair::process_sync_committee_updates(state, backend)?; state.cache.advance_epoch(); @@ -492,7 +501,9 @@ mod spec_tests { } fn run_sync_committee_updates_case(case: Case) { - run_case::

(case, altair::process_sync_committee_updates); + run_case::

(case, |state| { + altair::process_sync_committee_updates(state, Backend::default()) + }); } fn run_case( diff --git a/transition_functions/src/deneb/slot_processing.rs b/transition_functions/src/deneb/slot_processing.rs index 983e918ba..413e87851 100644 --- a/transition_functions/src/deneb/slot_processing.rs +++ b/transition_functions/src/deneb/slot_processing.rs @@ -1,4 +1,5 @@ use anyhow::{ensure, Result}; +use bls::Backend; use helper_functions::misc; use ssz::Hc; use types::{ @@ -12,6 +13,7 @@ pub fn process_slots( config: &Config, state: &mut Hc>, slot: Slot, + backend: Backend, ) -> Result<()> { ensure!( state.slot < slot, @@ -26,7 +28,7 @@ pub fn process_slots( // > Process epoch on the start slot of the next epoch if misc::is_epoch_start::

(state.slot + 1) { - epoch_processing::process_epoch(config, state)?; + epoch_processing::process_epoch(config, state, backend)?; } state.slot += 1; diff --git a/transition_functions/src/deneb/state_transition.rs b/transition_functions/src/deneb/state_transition.rs index 6d41890f9..8405c2192 100644 --- a/transition_functions/src/deneb/state_transition.rs +++ b/transition_functions/src/deneb/state_transition.rs @@ -1,7 +1,6 @@ use core::ops::Not as _; use anyhow::{anyhow, Error as AnyhowError, Result}; -use bls::traits::CachedPublicKey as _; use execution_engine::ExecutionEngine; use helper_functions::{ accessors, @@ -37,10 +36,11 @@ pub fn state_transition( slot_report: impl SlotReport + Send, ) -> Result<()> { let block = &signed_block.message; + let backend = verifier.backend(); // > Process slots (including those with no blocks) since block if process_slots.should_process(state, block) { - slot_processing::process_slots(config, state, block.slot)?; + slot_processing::process_slots(config, state, block.slot, backend)?; } let verify_signatures = V::IS_NULL.not().then(|| { @@ -134,6 +134,7 @@ pub fn verify_signatures( } // Attester slashings + let backend = verifier.backend(); for attester_slashing in &block.message.body.attester_slashings { for attestation in [ @@ -147,7 +148,7 @@ pub fn verify_signatures( .copied() .map(|validator_index| { accessors::public_key(state, validator_index)? - .decompress() + .decompress(backend) .map_err(AnyhowError::new) }), |public_keys| { diff --git a/transition_functions/src/electra/block_processing.rs b/transition_functions/src/electra/block_processing.rs index 0aa5c081c..95dc29e04 100644 --- a/transition_functions/src/electra/block_processing.rs +++ b/transition_functions/src/electra/block_processing.rs @@ -3,7 +3,7 @@ use core::ops::{Add as _, Index as _, Rem as _}; use anyhow::{ensure, Result}; use arithmetic::U64Ext as _; use bit_field::BitField as _; -use bls::{traits::CachedPublicKey as _, CachedPublicKey}; +use bls::{Backend, CachedPublicKey}; use execution_engine::{ExecutionEngine, NullExecutionEngine}; use helper_functions::{ accessors::{ @@ -120,6 +120,7 @@ pub fn process_block_for_gossip( config: &Config, state: &ElectraBeaconState

, block: &SignedBeaconBlock

, + backend: Backend, ) -> Result<()> { debug_assert_eq!(state.slot, block.message.slot); @@ -127,7 +128,7 @@ pub fn process_block_for_gossip( process_execution_payload_for_gossip(config, state, &block.message.body)?; - SingleVerifier.verify_singular( + SingleVerifier::new(backend).verify_singular( block.message.signing_root(config, state), block.signature, accessors::public_key(state, block.message.proposer_index)?, @@ -569,8 +570,12 @@ pub fn process_operations( // The conditional is not needed for correctness. // It only serves to avoid overhead when processing blocks with no deposits. if !body.deposits().is_empty() { - let combined_deposits = - unphased::validate_deposits(config, state, body.deposits().iter().copied())?; + let combined_deposits = unphased::validate_deposits( + config, + state, + body.deposits().iter().copied(), + verifier.backend(), + )?; let deposit_count = body.deposits().len(); @@ -778,6 +783,7 @@ pub fn process_deposit_data( config: &Config, state: &mut impl PostElectraBeaconState

, deposit_data: DepositData, + backend: Backend, ) -> Result> { let DepositData { pubkey, @@ -809,7 +815,10 @@ pub fn process_deposit_data( let pubkey = pubkey.into(); // > Fork-agnostic domain since deposits are valid across forks - if deposit_message.verify(config, signature, &pubkey).is_ok() { + if deposit_message + .verify(config, signature, &pubkey, backend) + .is_ok() + { let validator_index = state.validators().len_u64(); let combined_deposit = CombinedDeposit::NewValidator { @@ -969,8 +978,14 @@ pub fn validate_voluntary_exit( config: &Config, state: &impl PostElectraBeaconState

, signed_voluntary_exit: SignedVoluntaryExit, + backend: Backend, ) -> Result<()> { - validate_voluntary_exit_with_verifier(config, state, signed_voluntary_exit, SingleVerifier) + validate_voluntary_exit_with_verifier( + config, + state, + signed_voluntary_exit, + SingleVerifier::new(backend), + ) } fn validate_voluntary_exit_with_verifier( @@ -1382,7 +1397,7 @@ mod spec_tests { config, state, proposer_slashing, - SingleVerifier, + SingleVerifier::new(Backend::default()), NullSlotReport, ) }, @@ -1398,7 +1413,7 @@ mod spec_tests { config, state, &attester_slashing, - SingleVerifier, + SingleVerifier::new(Backend::default()), NullSlotReport, ) }, @@ -1429,7 +1444,7 @@ mod spec_tests { config, state, bls_to_execution_change, - SingleVerifier, + SingleVerifier::new(Backend::default()), ) }, "address_change", @@ -1451,7 +1466,7 @@ mod spec_tests { process_deposit_data, |config, state, deposit, _| { unphased::verify_deposit_merkle_branch(state, state.eth1_deposit_index, deposit)?; - process_deposit_data(config, state, deposit.data)?; + process_deposit_data(config, state, deposit.data, Backend::default())?; Ok(()) }, "deposit", @@ -1466,7 +1481,7 @@ mod spec_tests { config, state, voluntary_exit, - SingleVerifier, + SingleVerifier::new(Backend::default()), ) }, "voluntary_exit", @@ -1481,7 +1496,7 @@ mod spec_tests { config, state, sync_aggregate, - SingleVerifier, + SingleVerifier::new(Backend::default()), NullSlotReport, ) }, @@ -1529,7 +1544,8 @@ mod spec_tests { validation_tests! { validate_voluntary_exit, |config, state, voluntary_exit| { - validate_voluntary_exit_with_verifier(config, state, voluntary_exit, SingleVerifier) + validate_voluntary_exit_with_verifier(config, state, voluntary_exit, + SingleVerifier::new(Backend::default())) }, "voluntary_exit", "consensus-spec-tests/tests/mainnet/electra/operations/voluntary_exit/*/*", @@ -1657,9 +1673,12 @@ mod spec_tests { bls_setting: BlsSetting, ) -> Result<()> { match bls_setting { - BlsSetting::Optional | BlsSetting::Required => { - validate_attestation_with_verifier(config, state, attestation, SingleVerifier)? - } + BlsSetting::Optional | BlsSetting::Required => validate_attestation_with_verifier( + config, + state, + attestation, + SingleVerifier::new(Backend::default()), + )?, BlsSetting::Ignored => { validate_attestation_with_verifier(config, state, attestation, NullVerifier)? } diff --git a/transition_functions/src/electra/epoch_processing.rs b/transition_functions/src/electra/epoch_processing.rs index 3ffc98cbd..58c8a0d6f 100644 --- a/transition_functions/src/electra/epoch_processing.rs +++ b/transition_functions/src/electra/epoch_processing.rs @@ -2,6 +2,7 @@ use core::{cell::LazyCell, ops::Mul as _}; use anyhow::Result; use arithmetic::{NonZeroExt as _, U64Ext as _}; +use bls::Backend; use helper_functions::{ accessors::{ self, get_activation_exit_churn_limit, get_current_epoch, get_next_epoch, @@ -44,7 +45,11 @@ use crate::{ #[cfg(feature = "metrics")] use prometheus_metrics::METRICS; -pub fn process_epoch(config: &Config, state: &mut ElectraBeaconState) -> Result<()> { +pub fn process_epoch( + config: &Config, + state: &mut ElectraBeaconState, + backend: Backend, +) -> Result<()> { #[cfg(feature = "metrics")] let _timer = METRICS .get() @@ -80,7 +85,7 @@ pub fn process_epoch(config: &Config, state: &mut ElectraBeaconState(state, summaries); unphased::process_eth1_data_reset(state); - process_pending_deposits(config, state)?; + process_pending_deposits(config, state, backend)?; process_pending_consolidations(state)?; process_effective_balance_updates(state); unphased::process_slashings_reset(state); @@ -90,7 +95,7 @@ pub fn process_epoch(config: &Config, state: &mut ElectraBeaconState( config: &Config, state: &mut ElectraBeaconState

, + backend: Backend, ) -> Result { let (statistics, mut summaries, participation) = altair::statistics(state); @@ -141,7 +147,7 @@ pub fn epoch_report( unphased::process_randao_mixes_reset(state); unphased::process_historical_roots_update(state)?; altair::process_participation_flag_updates(state); - altair::process_sync_committee_updates(state)?; + altair::process_sync_committee_updates(state, backend)?; state.cache.advance_epoch(); @@ -224,6 +230,7 @@ fn process_registry_updates( fn process_pending_deposits( config: &Config, state: &mut impl PostElectraBeaconState

, + backend: Backend, ) -> Result<()> { let next_epoch = get_current_epoch(state) + 1; let available_for_processing = @@ -265,7 +272,7 @@ fn process_pending_deposits( if is_validator_withdrawn { // > Deposited balance will never become active. Increase balance but do not consume churn - apply_pending_deposit(config, state, deposit)?; + apply_pending_deposit(config, state, deposit, backend)?; } else if is_validator_exited { // > Validator is exiting, postpone the deposit until after withdrawable epoch deposits_to_postpone.push(*deposit); @@ -279,7 +286,7 @@ fn process_pending_deposits( // > Consume churn and apply deposit. processed_amount += deposit.amount; - apply_pending_deposit(config, state, deposit)?; + apply_pending_deposit(config, state, deposit, backend)?; } // > Regardless of how the deposit was handled, we move on in the queue. @@ -308,6 +315,7 @@ fn apply_pending_deposit( config: &Config, state: &mut impl PostElectraBeaconState

, deposit: &PendingDeposit, + backend: Backend, ) -> Result<()> { let PendingDeposit { pubkey, @@ -318,7 +326,7 @@ fn apply_pending_deposit( if let Some(validator_index) = accessors::index_of_public_key(state, deposit.pubkey) { increase_balance(balance(state, validator_index)?, *amount); - } else if is_valid_deposit_signature(config, deposit) { + } else if is_valid_deposit_signature(config, deposit, backend) { block_processing::add_validator_to_registry::

( state, (*pubkey).into(), @@ -330,7 +338,7 @@ fn apply_pending_deposit( Ok(()) } -fn is_valid_deposit_signature(config: &Config, deposit: &PendingDeposit) -> bool { +fn is_valid_deposit_signature(config: &Config, deposit: &PendingDeposit, backend: Backend) -> bool { let PendingDeposit { pubkey, withdrawal_credentials, @@ -346,7 +354,7 @@ fn is_valid_deposit_signature(config: &Config, deposit: &PendingDeposit) -> bool }; deposit_message - .verify(config, signature, &pubkey.into()) + .verify(config, signature, &pubkey.into(), backend) .is_ok() } @@ -808,12 +816,14 @@ mod spec_tests { } fn run_sync_committee_updates_case(case: Case) { - run_case::

(case, altair::process_sync_committee_updates); + run_case::

(case, |state| { + altair::process_sync_committee_updates(state, Backend::default()) + }); } fn run_pending_deposits_case(case: Case) { run_case::

(case, |state| { - process_pending_deposits(&P::default_config(), state) + process_pending_deposits(&P::default_config(), state, Backend::default()) }); } diff --git a/transition_functions/src/electra/slot_processing.rs b/transition_functions/src/electra/slot_processing.rs index 4d00c44ec..ea2bbe9bd 100644 --- a/transition_functions/src/electra/slot_processing.rs +++ b/transition_functions/src/electra/slot_processing.rs @@ -1,4 +1,5 @@ use anyhow::{ensure, Result}; +use bls::Backend; use helper_functions::misc; use ssz::Hc; use types::{ @@ -12,6 +13,7 @@ pub fn process_slots( config: &Config, state: &mut Hc>, slot: Slot, + backend: Backend, ) -> Result<()> { ensure!( state.slot < slot, @@ -26,7 +28,7 @@ pub fn process_slots( // > Process epoch on the start slot of the next epoch if misc::is_epoch_start::

(state.slot + 1) { - epoch_processing::process_epoch(config, state)?; + epoch_processing::process_epoch(config, state, backend)?; } state.slot += 1; diff --git a/transition_functions/src/electra/state_transition.rs b/transition_functions/src/electra/state_transition.rs index b464f9183..d333dd0e3 100644 --- a/transition_functions/src/electra/state_transition.rs +++ b/transition_functions/src/electra/state_transition.rs @@ -1,7 +1,6 @@ use core::ops::Not as _; use anyhow::{Error as AnyhowError, Result}; -use bls::traits::CachedPublicKey as _; use execution_engine::ExecutionEngine; use helper_functions::{ accessors, electra, @@ -38,9 +37,11 @@ pub fn state_transition( ) -> Result<()> { let block = &signed_block.message; + let backend = verifier.backend(); + // > Process slots (including those with no blocks) since block if process_slots.should_process(state, block) { - slot_processing::process_slots(config, state, block.slot)?; + slot_processing::process_slots(config, state, block.slot, backend)?; } let verify_signatures = V::IS_NULL.not().then(|| { @@ -121,6 +122,7 @@ pub fn verify_signatures( // Attester slashings + let backend = verifier.backend(); for attester_slashing in &block.message.body.attester_slashings { for attestation in [ &attester_slashing.attestation_1, @@ -133,7 +135,7 @@ pub fn verify_signatures( .copied() .map(|validator_index| { accessors::public_key(state, validator_index)? - .decompress() + .decompress(backend) .map_err(AnyhowError::new) }), |public_keys| { diff --git a/transition_functions/src/phase0/block_processing.rs b/transition_functions/src/phase0/block_processing.rs index 178ab2060..2fc2810fa 100644 --- a/transition_functions/src/phase0/block_processing.rs +++ b/transition_functions/src/phase0/block_processing.rs @@ -1,6 +1,6 @@ use anyhow::{ensure, Result}; use arithmetic::U64Ext as _; -use bls::traits::CachedPublicKey as _; +use bls::Backend; use helper_functions::{ accessors::{ self, attestation_epoch, get_beacon_proposer_index, index_of_public_key, @@ -69,12 +69,13 @@ pub fn process_block_for_gossip( config: &Config, state: &BeaconState

, block: &SignedBeaconBlock

, + backend: Backend, ) -> Result<()> { debug_assert_eq!(state.slot, block.message.slot); unphased::process_block_header_for_gossip(state, &block.message)?; - SingleVerifier.verify_singular( + SingleVerifier::new(backend).verify_singular( block.message.signing_root(config, state), block.signature, accessors::public_key(state, block.message.proposer_index)?, @@ -191,8 +192,12 @@ fn process_operations( // The conditional is not needed for correctness. // It only serves to avoid overhead when processing blocks with no deposits. if !body.deposits.is_empty() { - let combined_deposits = - unphased::validate_deposits(config, state, body.deposits.iter().copied())?; + let combined_deposits = unphased::validate_deposits( + config, + state, + body.deposits.iter().copied(), + verifier.backend(), + )?; apply_deposits(state, body.deposits.len(), combined_deposits, slot_report)?; } @@ -285,6 +290,7 @@ pub fn process_deposit_data( config: &Config, state: &mut BeaconState

, deposit_data: DepositData, + backend: Backend, ) -> Result> { let DepositData { pubkey, @@ -314,7 +320,10 @@ pub fn process_deposit_data( let pubkey = pubkey.into(); // > Fork-agnostic domain since deposits are valid across forks - if deposit_message.verify(config, signature, &pubkey).is_ok() { + if deposit_message + .verify(config, signature, &pubkey, backend) + .is_ok() + { let validator_index = state.validators.len_u64(); let combined_deposit = CombinedDeposit::NewValidator { @@ -501,7 +510,7 @@ mod spec_tests { config, state, proposer_slashing, - SingleVerifier, + SingleVerifier::new(Backend::default()), NullSlotReport, ) }, @@ -517,7 +526,7 @@ mod spec_tests { config, state, &attester_slashing, - SingleVerifier, + SingleVerifier::new(Backend::default()), NullSlotReport, ) }, @@ -550,7 +559,7 @@ mod spec_tests { process_deposit_data, |config, state, deposit, _| { unphased::verify_deposit_merkle_branch(state, state.eth1_deposit_index, deposit)?; - process_deposit_data(config, state, deposit.data)?; + process_deposit_data(config, state, deposit.data, Backend::default())?; Ok(()) }, "deposit", @@ -561,7 +570,8 @@ mod spec_tests { processing_tests! { process_voluntary_exit, |config, state, voluntary_exit, _| { - unphased::process_voluntary_exit(config, state, voluntary_exit, SingleVerifier) + unphased::process_voluntary_exit(config, state, voluntary_exit, + SingleVerifier::new(Backend::default())) }, "voluntary_exit", "consensus-spec-tests/tests/mainnet/phase0/operations/voluntary_exit/*/*", @@ -649,7 +659,7 @@ mod spec_tests { config, state, attestation, - SingleVerifier, + SingleVerifier::new(Backend::default()), )? } BlsSetting::Ignored => unphased::validate_attestation_with_verifier( diff --git a/transition_functions/src/phase0/state_transition.rs b/transition_functions/src/phase0/state_transition.rs index 95be92d2e..c60aa8fd1 100644 --- a/transition_functions/src/phase0/state_transition.rs +++ b/transition_functions/src/phase0/state_transition.rs @@ -1,7 +1,6 @@ use core::ops::Not as _; use anyhow::{anyhow, Error as AnyhowError, Result}; -use bls::traits::CachedPublicKey as _; use helper_functions::{ accessors, error::SignatureKind, @@ -135,6 +134,7 @@ pub fn verify_signatures( // Attester slashings + let backend = verifier.backend(); for attester_slashing in &block.message.body.attester_slashings { for attestation in [ &attester_slashing.attestation_1, @@ -147,7 +147,7 @@ pub fn verify_signatures( .copied() .map(|validator_index| { accessors::public_key(state, validator_index)? - .decompress() + .decompress(backend) .map_err(AnyhowError::new) }), |public_keys| { diff --git a/transition_functions/src/unphased/block_processing.rs b/transition_functions/src/unphased/block_processing.rs index deeedbf2f..9382c2f7e 100644 --- a/transition_functions/src/unphased/block_processing.rs +++ b/transition_functions/src/unphased/block_processing.rs @@ -1,5 +1,5 @@ use anyhow::{ensure, Result}; -use bls::{traits::CachedPublicKey as _, CachedPublicKey, SignatureBytes}; +use bls::{Backend, CachedPublicKey, SignatureBytes}; use helper_functions::{ accessors::{ attestation_epoch, get_beacon_proposer_index, get_current_epoch, get_randao_mix, @@ -204,8 +204,14 @@ pub fn validate_proposer_slashing( config: &Config, state: &impl BeaconState

, proposer_slashing: ProposerSlashing, + backend: Backend, ) -> Result<()> { - validate_proposer_slashing_with_verifier(config, state, proposer_slashing, SingleVerifier) + validate_proposer_slashing_with_verifier( + config, + state, + proposer_slashing, + SingleVerifier::new(backend), + ) } pub fn validate_proposer_slashing_with_verifier( @@ -273,8 +279,14 @@ pub fn validate_attester_slashing( config: &Config, state: &impl BeaconState

, attester_slashing: &impl AttesterSlashing

, + backend: Backend, ) -> Result> { - validate_attester_slashing_with_verifier(config, state, attester_slashing, SingleVerifier) + validate_attester_slashing_with_verifier( + config, + state, + attester_slashing, + SingleVerifier::new(backend), + ) } pub fn validate_attester_slashing_with_verifier( @@ -322,8 +334,9 @@ pub fn validate_attestation( config: &Config, state: &impl BeaconState

, attestation: &Attestation

, + backend: Backend, ) -> Result<()> { - validate_attestation_with_verifier(config, state, attestation, SingleVerifier) + validate_attestation_with_verifier(config, state, attestation, SingleVerifier::new(backend)) } pub fn validate_attestation_with_verifier( @@ -390,6 +403,7 @@ pub fn validate_deposits( config: &Config, state: &impl BeaconState

, deposits: impl IntoIterator, + backend: Backend, ) -> Result> { let deposits_by_pubkey = (0..) .zip(deposits) @@ -414,7 +428,7 @@ pub fn validate_deposits( .map(|(_, cached_public_key, deposits)| { let (_, first_deposit) = deposits[0]; - let public_key = *cached_public_key.decompress()?; + let public_key = *cached_public_key.decompress(backend)?; // > Verify the deposit signature (proof of possession) // > which is not checked by the deposit contract @@ -427,6 +441,7 @@ pub fn validate_deposits( signing_root, first_deposit.data.signature, public_key, + backend, )) }) .collect::>>() @@ -479,7 +494,7 @@ pub fn validate_deposits( // > Fork-agnostic domain since deposits are valid across forks deposit_message - .verify(config, deposit.data.signature, &cached_public_key) + .verify(config, deposit.data.signature, &cached_public_key, backend) .is_ok() }) }; @@ -563,8 +578,14 @@ pub fn validate_voluntary_exit( config: &Config, state: &impl BeaconState

, signed_voluntary_exit: SignedVoluntaryExit, + backend: Backend, ) -> Result<()> { - validate_voluntary_exit_with_verifier(config, state, signed_voluntary_exit, SingleVerifier) + validate_voluntary_exit_with_verifier( + config, + state, + signed_voluntary_exit, + SingleVerifier::new(backend), + ) } pub fn validate_voluntary_exit_with_verifier( diff --git a/types/src/altair/container_impls.rs b/types/src/altair/container_impls.rs index 91fdbba1b..56a025e9c 100644 --- a/types/src/altair/container_impls.rs +++ b/types/src/altair/container_impls.rs @@ -1,4 +1,4 @@ -use bls::{traits::SignatureBytes as _, AggregateSignatureBytes}; +use bls::AggregateSignatureBytes; use crate::{altair::containers::SyncAggregate, preset::Preset}; diff --git a/validator_key_cache/src/lib.rs b/validator_key_cache/src/lib.rs index bdbbf1f5c..23f81af09 100644 --- a/validator_key_cache/src/lib.rs +++ b/validator_key_cache/src/lib.rs @@ -7,7 +7,7 @@ use std::{ }; use anyhow::{bail, Result}; -use bls::{traits::SecretKey as _, PublicKeyBytes, SecretKey, SecretKeyBytes}; +use bls::{Backend, PublicKeyBytes, SecretKey, SecretKeyBytes}; use eip_2335::Crypto; use log::info; use sha2::{Digest as _, Sha256}; @@ -33,15 +33,17 @@ pub struct ValidatorKeyCache { keys: BTreeMap)>, passwords: BTreeMap>, validator_directory: PathBuf, + backend: Backend, } impl ValidatorKeyCache { #[must_use] - pub const fn new(validator_directory: PathBuf) -> Self { + pub const fn new(validator_directory: PathBuf, backend: Backend) -> Self { Self { keys: BTreeMap::new(), passwords: BTreeMap::new(), validator_directory, + backend, } } @@ -90,7 +92,10 @@ impl ValidatorKeyCache { } = record; let uuid = Uuid::from_u128(uuid); - let secret_key = Arc::new(secret_key.try_into()?); + let secret_key = Arc::new(SecretKey::try_from_with_backend( + secret_key, + self.backend.clone(), + )?); Ok((uuid, (public_key, secret_key))) })