From fcbb8ecc96ecdf2818342a0cf29da0132a643c1e Mon Sep 17 00:00:00 2001 From: shreyas-londhe Date: Wed, 18 Dec 2024 10:43:39 +0530 Subject: [PATCH] feat: modularised bls crate to have mulitple backend impl --- .github/workflows/ci.yml | 6 +- Cargo.lock | 71 +++++++ Cargo.toml | 10 +- benches/benches/helper_functions.rs | 1 + block_producer/src/block_producer.rs | 5 +- bls/Cargo.toml | 27 +-- bls/bls-blst/Cargo.toml | 25 +++ bls/bls-blst/src/cached_public_key.rs | 10 + bls/bls-blst/src/lib.rs | 7 + bls/{ => bls-blst}/src/public_key.rs | 37 ++-- bls/bls-blst/src/public_key_bytes.rs | 23 +++ bls/bls-blst/src/secret_key.rs | 57 ++++++ bls/bls-blst/src/secret_key_bytes.rs | 7 + bls/{ => bls-blst}/src/signature.rs | 61 +++--- bls/bls-blst/src/signature_bytes.rs | 20 ++ bls/bls-core/Cargo.toml | 14 ++ bls/{ => bls-core}/src/consts.rs | 0 bls/bls-core/src/error.rs | 13 ++ bls/bls-core/src/lib.rs | 7 + bls/bls-core/src/traits/cached_public_key.rs | 141 ++++++++++++++ bls/bls-core/src/traits/mod.rs | 15 ++ bls/bls-core/src/traits/public_key.rs | 25 +++ bls/bls-core/src/traits/public_key_bytes.rs | 76 ++++++++ bls/bls-core/src/traits/secret_key.rs | 54 ++++++ bls/bls-core/src/traits/secret_key_bytes.rs | 82 ++++++++ bls/bls-core/src/traits/signature.rs | 33 ++++ bls/bls-core/src/traits/signature_bytes.rs | 80 ++++++++ bls/bls-zkcrypto/Cargo.toml | 23 +++ bls/bls-zkcrypto/src/cached_public_key.rs | 10 + bls/bls-zkcrypto/src/lib.rs | 7 + bls/bls-zkcrypto/src/public_key.rs | 51 +++++ bls/bls-zkcrypto/src/public_key_bytes.rs | 23 +++ bls/bls-zkcrypto/src/secret_key.rs | 80 ++++++++ bls/bls-zkcrypto/src/secret_key_bytes.rs | 7 + bls/bls-zkcrypto/src/signature.rs | 176 ++++++++++++++++++ bls/bls-zkcrypto/src/signature_bytes.rs | 21 +++ bls/src/cached_public_key.rs | 109 ----------- bls/src/error.rs | 14 -- bls/src/lib.rs | 39 ++-- bls/src/public_key_bytes.rs | 52 ------ bls/src/secret_key.rs | 98 ---------- bls/src/secret_key_bytes.rs | 78 -------- bls/src/signature_bytes.rs | 62 ------ .../src/doppelganger_protection.rs | 1 + factory/src/lib.rs | 5 +- genesis/src/lib.rs | 2 +- grandine/src/validators.rs | 2 +- helper_functions/src/accessors.rs | 5 +- helper_functions/src/fork.rs | 5 +- helper_functions/src/mutators.rs | 5 +- helper_functions/src/predicates.rs | 7 +- helper_functions/src/signing.rs | 2 +- helper_functions/src/spec_tests.rs | 4 +- helper_functions/src/verifier.rs | 3 +- http_api/src/context.rs | 2 +- http_api/src/error.rs | 2 +- http_api/src/gui.rs | 2 +- http_api/src/standard.rs | 5 +- interop/src/lib.rs | 2 +- keymanager/src/keystores.rs | 2 +- .../src/attestation_agg_pool/tasks.rs | 2 +- .../src/sync_committee_agg_pool/pool.rs | 1 + .../src/sync_committee_agg_pool/tasks.rs | 1 + scripts/ci/clippy.bash | 1 + signer/src/signer.rs | 2 +- .../src/altair/block_processing.rs | 1 + .../src/altair/state_transition.rs | 1 + .../src/bellatrix/state_transition.rs | 1 + .../src/capella/state_transition.rs | 1 + .../src/deneb/state_transition.rs | 1 + .../src/electra/block_processing.rs | 2 +- .../src/electra/state_transition.rs | 1 + .../src/phase0/block_processing.rs | 1 + .../src/phase0/state_transition.rs | 1 + .../src/unphased/block_processing.rs | 2 +- types/src/altair/container_impls.rs | 2 +- validator/src/own_beacon_committee_members.rs | 1 + .../src/own_sync_committee_subscriptions.rs | 2 +- validator/src/slot_head.rs | 2 +- validator/src/validator.rs | 2 +- validator_key_cache/src/lib.rs | 2 +- 81 files changed, 1304 insertions(+), 539 deletions(-) create mode 100644 bls/bls-blst/Cargo.toml create mode 100644 bls/bls-blst/src/cached_public_key.rs create mode 100644 bls/bls-blst/src/lib.rs rename bls/{ => bls-blst}/src/public_key.rs (54%) create mode 100644 bls/bls-blst/src/public_key_bytes.rs create mode 100644 bls/bls-blst/src/secret_key.rs create mode 100644 bls/bls-blst/src/secret_key_bytes.rs rename bls/{ => bls-blst}/src/signature.rs (76%) create mode 100644 bls/bls-blst/src/signature_bytes.rs create mode 100644 bls/bls-core/Cargo.toml rename bls/{ => bls-core}/src/consts.rs (100%) create mode 100644 bls/bls-core/src/error.rs create mode 100644 bls/bls-core/src/lib.rs create mode 100644 bls/bls-core/src/traits/cached_public_key.rs create mode 100644 bls/bls-core/src/traits/mod.rs create mode 100644 bls/bls-core/src/traits/public_key.rs create mode 100644 bls/bls-core/src/traits/public_key_bytes.rs create mode 100644 bls/bls-core/src/traits/secret_key.rs create mode 100644 bls/bls-core/src/traits/secret_key_bytes.rs create mode 100644 bls/bls-core/src/traits/signature.rs create mode 100644 bls/bls-core/src/traits/signature_bytes.rs create mode 100644 bls/bls-zkcrypto/Cargo.toml create mode 100644 bls/bls-zkcrypto/src/cached_public_key.rs create mode 100644 bls/bls-zkcrypto/src/lib.rs create mode 100644 bls/bls-zkcrypto/src/public_key.rs create mode 100644 bls/bls-zkcrypto/src/public_key_bytes.rs create mode 100644 bls/bls-zkcrypto/src/secret_key.rs create mode 100644 bls/bls-zkcrypto/src/secret_key_bytes.rs create mode 100644 bls/bls-zkcrypto/src/signature.rs create mode 100644 bls/bls-zkcrypto/src/signature_bytes.rs delete mode 100644 bls/src/cached_public_key.rs delete mode 100644 bls/src/error.rs delete mode 100644 bls/src/public_key_bytes.rs delete mode 100644 bls/src/secret_key.rs delete mode 100644 bls/src/secret_key_bytes.rs delete mode 100644 bls/src/signature_bytes.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c2435faa..899cd8f8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,7 +24,7 @@ jobs: # Running `rustc` or Cargo should automatically install the toolchain specified in `rust-toolchain.toml`. - uses: Swatinem/rust-cache@v2 - name: Run cargo build - run: cargo build --release --features default-networks + run: cargo build --release --features default-networks --features blst - name: Check if code is formatted (Linux) if: runner.os == 'Linux' run: cargo fmt --check @@ -34,10 +34,10 @@ jobs: # It's a known issue that networking tests randomly fails on Macos - name: Run tests if: runner.os == 'Macos' - run: cargo test --release --no-fail-fast -- --skip behaviour --skip common + run: cargo test --release --no-fail-fast --features blst -- --skip behaviour --skip common - name: Run tests if: runner.os != 'Macos' - run: cargo test --release --no-fail-fast + run: cargo test --release --no-fail-fast --features zkcrypto - name: Check consensus-spec-tests coverage (Linux) if: runner.os == 'Linux' run: scripts/ci/consensus-spec-tests-coverage.rb diff --git a/Cargo.lock b/Cargo.lock index 026c828f..79147097 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -921,6 +921,16 @@ 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", @@ -936,11 +946,62 @@ dependencies = [ "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", + "derivative", + "derive_more 1.0.0", + "ff", + "fixed-hash", + "hex", + "impl-serde", + "itertools 0.13.0", + "once_cell", + "rand", + "serde", + "serde_utils", + "sha2 0.10.8", + "ssz", + "static_assertions", + "typenum", + "zeroize", +] + +[[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", + "subtle", +] + [[package]] name = "blst" version = "0.3.13" @@ -2529,6 +2590,7 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" dependencies = [ + "bitvec", "rand_core", "subtle", ] @@ -5602,6 +5664,15 @@ dependencies = [ "sha2 0.10.8", ] +[[package]] +name = "pairing" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fec4625e73cf41ef4bb6846cafa6d44736525f442ba45e407c4a000a13996f" +dependencies = [ + "group", +] + [[package]] name = "panics" version = "0.0.0" diff --git a/Cargo.toml b/Cargo.toml index 92fd5f56..f3bbecda 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,9 @@ members = [ 'binary_utils', 'block_producer', 'bls', + 'bls/bls-blst', + 'bls/bls-core', + 'bls/bls-zkcrypto', 'builder_api', 'clock', 'database', @@ -291,6 +294,7 @@ base64 = '0.22' bincode = '1' bit_field = '0.10' bitvec = '1' +bls12_381 = { git = "https://github.com/zkcrypto/bls12_381.git" } blst = { version = '0.3', features = ['portable'] } bstr = '1' build-time = '0.1' @@ -323,6 +327,7 @@ enum-map = '2' enumset = '1' env_logger = '0.11' ethereum-types = '0.14' +ff = "0.13.0" fixed-hash = '0.8.0' fnv = '1' fs-err = { version = '2', features = ['tokio'] } @@ -457,6 +462,9 @@ attestation_verifier = { path = 'attestation_verifier' } binary_utils = { path = 'binary_utils' } block_producer = { path = 'block_producer' } bls = { path = 'bls' } +bls-blst = { path = 'bls/bls-blst' } +bls-core = { path = 'bls/bls-core' } +bls-zkcrypto = { path = 'bls/bls-zkcrypto' } builder_api = { path = 'builder_api' } clock = { path = 'clock' } database = { path = 'database' } @@ -566,4 +574,4 @@ codegen-units = 1 [patch.crates-io] # `geth` responds to invalid payloads with objects containing `method` and `params`. # We had to fork `jsonrpc` because it does not allow nonstandard members. -jsonrpc-core = { git = 'https://github.com/grandinetech/jsonrpc.git' } +jsonrpc-core = { git = 'https://github.com/grandinetech/jsonrpc.git' } \ No newline at end of file diff --git a/benches/benches/helper_functions.rs b/benches/benches/helper_functions.rs index 220cc15f..ace96dbf 100644 --- a/benches/benches/helper_functions.rs +++ b/benches/benches/helper_functions.rs @@ -7,6 +7,7 @@ use std::sync::Arc; use allocator as _; +use bls::traits::CachedPublicKey as _; use criterion::{BatchSize, Criterion, Throughput}; use easy_ext::ext; use eth2_cache_utils::{goerli, mainnet, medalla, LazyBeaconState}; diff --git a/block_producer/src/block_producer.rs b/block_producer/src/block_producer.rs index 5040fa3f..6bcd8de7 100644 --- a/block_producer/src/block_producer.rs +++ b/block_producer/src/block_producer.rs @@ -5,7 +5,10 @@ use std::{ }; use anyhow::{Context as _, Error as AnyhowError, Result}; -use bls::{AggregateSignature, PublicKeyBytes, SignatureBytes}; +use bls::{ + traits::{CachedPublicKey as _, Signature as _}, + AggregateSignature, PublicKeyBytes, SignatureBytes, +}; use builder_api::{combined::SignedBuilderBid, BuilderApi}; use cached::{Cached as _, SizedCache}; use dedicated_executor::{DedicatedExecutor, Job}; diff --git a/bls/Cargo.toml b/bls/Cargo.toml index 4a1c0d0d..e290d364 100644 --- a/bls/Cargo.toml +++ b/bls/Cargo.toml @@ -6,24 +6,11 @@ authors = ["Grandine "] [lints] workspace = true -[dependencies] -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 } -thiserror = { workspace = true } -typenum = { workspace = true } -zeroize = { workspace = true } +[features] +blst = ["dep:bls-blst"] +zkcrypto = ["dep:bls-zkcrypto"] -[dev-dependencies] -std_ext = { workspace = true } -tap = { workspace = true } +[dependencies] +bls-core = { workspace = true } +bls-blst = { workspace = true, optional = true } +bls-zkcrypto = { workspace = true, optional = true } \ No newline at end of file diff --git a/bls/bls-blst/Cargo.toml b/bls/bls-blst/Cargo.toml new file mode 100644 index 00000000..d5f43106 --- /dev/null +++ b/bls/bls-blst/Cargo.toml @@ -0,0 +1,25 @@ +[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 new file mode 100644 index 00000000..d48a97f6 --- /dev/null +++ b/bls/bls-blst/src/cached_public_key.rs @@ -0,0 +1,10 @@ +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 new file mode 100644 index 00000000..e72dc5dc --- /dev/null +++ b/bls/bls-blst/src/lib.rs @@ -0,0 +1,7 @@ +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/src/public_key.rs b/bls/bls-blst/src/public_key.rs similarity index 54% rename from bls/src/public_key.rs rename to bls/bls-blst/src/public_key.rs index 566000ca..be7289a2 100644 --- a/bls/src/public_key.rs +++ b/bls/bls-blst/src/public_key.rs @@ -1,59 +1,44 @@ use blst::min_pk::{AggregatePublicKey as RawAggregatePublicKey, PublicKey as RawPublicKey}; use derive_more::From; -use crate::{Error, PublicKeyBytes}; +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 From for PublicKeyBytes { - #[inline] - fn from(public_key: PublicKey) -> Self { - Self(public_key.as_raw().compress()) - } -} - impl TryFrom for PublicKey { type Error = Error; #[inline] fn try_from(bytes: PublicKeyBytes) -> Result { - let raw = RawPublicKey::uncompress(bytes.as_bytes())?; + 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()?; + raw.validate().map_err(|_| Error::InvalidPublicKey)?; Ok(Self(raw)) } } -impl PublicKey { - /// [`eth_aggregate_pubkeys`](https://github.com/ethereum/consensus-specs/blob/86fb82b221474cc89387fa6436806507b3849d88/specs/altair/bls.md#eth_aggregate_pubkeys) - pub fn aggregate_nonempty(public_keys: impl IntoIterator) -> Result { - public_keys - .into_iter() - .reduce(Self::aggregate) - .ok_or(Error::NoPublicKeysToAggregate) - } - - #[inline] - #[must_use] - pub fn aggregate(mut self, other: Self) -> Self { - self.aggregate_in_place(other); - self - } +impl PublicKeyTrait for PublicKey { + type PublicKeyBytes = PublicKeyBytes; #[inline] - pub fn aggregate_in_place(&mut self, other: Self) { + 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 new file mode 100644 index 00000000..19fd60a1 --- /dev/null +++ b/bls/bls-blst/src/public_key_bytes.rs @@ -0,0 +1,23 @@ +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 new file mode 100644 index 00000000..36ef58d8 --- /dev/null +++ b/bls/bls-blst/src/secret_key.rs @@ -0,0 +1,57 @@ +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, +}; + +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 { + Ok(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 new file mode 100644 index 00000000..bb96b017 --- /dev/null +++ b/bls/bls-blst/src/secret_key_bytes.rs @@ -0,0 +1,7 @@ +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/src/signature.rs b/bls/bls-blst/src/signature.rs similarity index 76% rename from bls/src/signature.rs rename to bls/bls-blst/src/signature.rs index 3d6cbcfb..5a48c71d 100644 --- a/bls/src/signature.rs +++ b/bls/bls-blst/src/signature.rs @@ -9,7 +9,13 @@ use derive_more::From; use itertools::Itertools as _; use rand::Rng as _; -use crate::{consts::DOMAIN_SEPARATION_TAG, Error, PublicKey, SignatureBytes}; +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; @@ -26,27 +32,23 @@ impl Default for Signature { } } -impl From for SignatureBytes { - #[inline] - fn from(signature: Signature) -> Self { - Self(signature.as_raw().compress()) - } -} - impl TryFrom for Signature { type Error = Error; #[inline] fn try_from(bytes: SignatureBytes) -> Result { - RawSignature::uncompress(bytes.as_bytes()) + Ok(RawSignature::uncompress(bytes.as_bytes()) .map(Self) - .map_err(Into::into) + .map_err(|_| Error::InvalidSignature)?) } } -impl Signature { +impl SignatureTrait for Signature { + type SignatureBytes = SignatureBytes; + type PublicKey = PublicKey; + #[must_use] - pub fn verify(self, message: impl AsRef<[u8]>, public_key: PublicKey) -> bool { + fn verify(&self, message: impl AsRef<[u8]>, public_key: Self::PublicKey) -> bool { let result = self.as_raw().verify( true, message.as_ref(), @@ -60,14 +62,7 @@ impl Signature { } #[inline] - #[must_use] - pub fn aggregate(mut self, other: Self) -> Self { - self.aggregate_in_place(other); - self - } - - #[inline] - pub fn aggregate_in_place(&mut self, other: Self) { + 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); @@ -75,7 +70,7 @@ impl Signature { } #[must_use] - pub fn fast_aggregate_verify<'keys>( + fn fast_aggregate_verify<'keys>( &self, message: impl AsRef<[u8]>, public_keys: impl IntoIterator, @@ -93,7 +88,7 @@ impl Signature { } #[must_use] - pub fn multi_verify<'all>( + fn multi_verify<'all>( messages: impl IntoIterator, signatures: impl IntoIterator, public_keys: impl IntoIterator, @@ -127,18 +122,22 @@ impl Signature { result == BLST_ERROR::BLST_SUCCESS } +} - const fn as_raw(&self) -> &RawSignature { +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::{SecretKey, SecretKeyBytes}; + use crate::{secret_key::SecretKey, secret_key_bytes::SecretKeyBytes}; use super::*; @@ -147,28 +146,28 @@ mod tests { #[test] fn signature_verify_succeeds_on_correct_triple() { let secret_key = secret_key(); - let public_key = secret_key.to_public_key(); - let signature = secret_key.sign(MESSAGE); + let public_key = SecretKey::to_public_key(&secret_key); + let signature = SecretKey::sign(&secret_key, MESSAGE); - assert!(signature.verify(MESSAGE, public_key)); + 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 = secret_key.sign(MESSAGE); + let signature = SecretKey::sign(&secret_key, MESSAGE); - assert!(!signature.verify(MESSAGE, public_key)); + assert!(!Signature::verify(&signature, MESSAGE, public_key)); } #[test] fn signature_verify_fails_on_incorrect_signature() { let secret_key = secret_key(); - let public_key = secret_key.to_public_key(); + let public_key = SecretKey::to_public_key(&secret_key); let signature = Signature::default(); - assert!(!signature.verify(MESSAGE, public_key)); + assert!(!Signature::verify(&signature, MESSAGE, public_key)); } fn secret_key() -> SecretKey { diff --git a/bls/bls-blst/src/signature_bytes.rs b/bls/bls-blst/src/signature_bytes.rs new file mode 100644 index 00000000..eb93121e --- /dev/null +++ b/bls/bls-blst/src/signature_bytes.rs @@ -0,0 +1,20 @@ +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 new file mode 100644 index 00000000..7a45e27f --- /dev/null +++ b/bls/bls-core/Cargo.toml @@ -0,0 +1,14 @@ +[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/src/consts.rs b/bls/bls-core/src/consts.rs similarity index 100% rename from bls/src/consts.rs rename to bls/bls-core/src/consts.rs diff --git a/bls/bls-core/src/error.rs b/bls/bls-core/src/error.rs new file mode 100644 index 00000000..f789f52a --- /dev/null +++ b/bls/bls-core/src/error.rs @@ -0,0 +1,13 @@ +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("invalid public key")] + InvalidPublicKey, + #[error("invalid secret key")] + InvalidSecretKey, + #[error("invalid signature")] + InvalidSignature, + #[error("no public keys to aggregate")] + NoPublicKeysToAggregate, + #[error("failed to decompress point")] + DecompressionFailed, +} diff --git a/bls/bls-core/src/lib.rs b/bls/bls-core/src/lib.rs new file mode 100644 index 00000000..cf5e799d --- /dev/null +++ b/bls/bls-core/src/lib.rs @@ -0,0 +1,7 @@ +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 new file mode 100644 index 00000000..a641d889 --- /dev/null +++ b/bls/bls-core/src/traits/cached_public_key.rs @@ -0,0 +1,141 @@ +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, Debug, 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>, + } + + 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 $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 new file mode 100644 index 00000000..47f79b6c --- /dev/null +++ b/bls/bls-core/src/traits/mod.rs @@ -0,0 +1,15 @@ +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 new file mode 100644 index 00000000..d6176160 --- /dev/null +++ b/bls/bls-core/src/traits/public_key.rs @@ -0,0 +1,25 @@ +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; + + 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 new file mode 100644 index 00000000..c9215873 --- /dev/null +++ b/bls/bls-core/src/traits/public_key_bytes.rs @@ -0,0 +1,76 @@ +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 new file mode 100644 index 00000000..6accd4c6 --- /dev/null +++ b/bls/bls-core/src/traits/secret_key.rs @@ -0,0 +1,54 @@ +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)] + #[debug("[REDACTED]")] + pub struct $name($raw); + + 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 new file mode 100644 index 00000000..ba0693df --- /dev/null +++ b/bls/bls-core/src/traits/secret_key_bytes.rs @@ -0,0 +1,82 @@ +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) => { + #[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], + } + + 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 new file mode 100644 index 00000000..8a701c3a --- /dev/null +++ b/bls/bls-core/src/traits/signature.rs @@ -0,0 +1,33 @@ +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 new file mode 100644 index 00000000..061ce431 --- /dev/null +++ b/bls/bls-core/src/traits/signature_bytes.rs @@ -0,0 +1,80 @@ +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 new file mode 100644 index 00000000..8fe01b73 --- /dev/null +++ b/bls/bls-zkcrypto/Cargo.toml @@ -0,0 +1,23 @@ +[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 new file mode 100644 index 00000000..a3a279dc --- /dev/null +++ b/bls/bls-zkcrypto/src/cached_public_key.rs @@ -0,0 +1,10 @@ +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 new file mode 100644 index 00000000..e72dc5dc --- /dev/null +++ b/bls/bls-zkcrypto/src/lib.rs @@ -0,0 +1,7 @@ +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 new file mode 100644 index 00000000..80b3aeb0 --- /dev/null +++ b/bls/bls-zkcrypto/src/public_key.rs @@ -0,0 +1,51 @@ +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 new file mode 100644 index 00000000..56115287 --- /dev/null +++ b/bls/bls-zkcrypto/src/public_key_bytes.rs @@ -0,0 +1,23 @@ +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 new file mode 100644 index 00000000..b67f6bf4 --- /dev/null +++ b/bls/bls-zkcrypto/src/secret_key.rs @@ -0,0 +1,80 @@ +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 new file mode 100644 index 00000000..37a0cfdd --- /dev/null +++ b/bls/bls-zkcrypto/src/secret_key_bytes.rs @@ -0,0 +1,7 @@ +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 new file mode 100644 index 00000000..3943b780 --- /dev/null +++ b/bls/bls-zkcrypto/src/signature.rs @@ -0,0 +1,176 @@ +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 new file mode 100644 index 00000000..da052b16 --- /dev/null +++ b/bls/bls-zkcrypto/src/signature_bytes.rs @@ -0,0 +1,21 @@ +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/cached_public_key.rs b/bls/src/cached_public_key.rs deleted file mode 100644 index e428edc7..00000000 --- a/bls/src/cached_public_key.rs +++ /dev/null @@ -1,109 +0,0 @@ -use derivative::Derivative; -use once_cell::race::OnceBox; -use serde::{Deserialize, Serialize}; -use ssz::{ReadError, Size, SszHash, SszRead, SszReadDefault as _, SszSize, SszWrite, H256}; - -use crate::{Error, PublicKey, PublicKeyBytes}; - -#[derive(Default, Debug, Derivative, Deserialize, Serialize)] -#[derivative(PartialEq, Eq)] -#[serde(transparent)] -pub struct CachedPublicKey { - bytes: PublicKeyBytes, - #[derivative(PartialEq = "ignore")] - #[serde(skip)] - decompressed: OnceBox, -} - -// `OnceBox` does not implement `Clone`. -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 From for CachedPublicKey { - #[inline] - fn from(bytes: PublicKeyBytes) -> Self { - Self { - bytes, - decompressed: OnceBox::new(), - } - } -} - -impl From for CachedPublicKey { - #[inline] - fn from(public_key: PublicKey) -> Self { - Self::new(public_key.into(), public_key) - } -} - -impl SszSize for CachedPublicKey { - const SIZE: Size = PublicKeyBytes::SIZE; -} - -impl SszRead for CachedPublicKey { - #[inline] - fn from_ssz_unchecked(_context: &C, bytes: &[u8]) -> Result { - Ok(Self { - bytes: PublicKeyBytes::from_ssz_default(bytes)?, - decompressed: 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) -> H256 { - self.bytes.hash_tree_root() - } -} - -impl CachedPublicKey { - fn new(bytes: PublicKeyBytes, public_key: PublicKey) -> Self { - let decompressed = OnceBox::new(); - - decompressed - .set(Box::new(public_key)) - .expect("decompressed is empty because OnceBox::new returns an empty cell"); - - Self { - bytes, - decompressed, - } - } - - #[inline] - pub const fn as_bytes(&self) -> &PublicKeyBytes { - &self.bytes - } - - #[inline] - pub const fn to_bytes(&self) -> PublicKeyBytes { - self.bytes - } - - #[inline] - pub fn decompress(&self) -> Result<&PublicKey, Error> { - self.decompressed - .get_or_try_init(|| self.bytes.try_into().map(Box::new)) - } -} diff --git a/bls/src/error.rs b/bls/src/error.rs deleted file mode 100644 index 3f6fa064..00000000 --- a/bls/src/error.rs +++ /dev/null @@ -1,14 +0,0 @@ -use blst::BLST_ERROR; -use derive_more::From; -use static_assertions::assert_eq_size; -use thiserror::Error; - -#[derive(Debug, From, Error)] -pub enum Error { - #[error("decompression failed: {0:?}")] - DecompressionFailed(BLST_ERROR), - #[error("no public keys to aggregate")] - NoPublicKeysToAggregate, -} - -assert_eq_size!(Error, u32); diff --git a/bls/src/lib.rs b/bls/src/lib.rs index a61f4994..1f67bbbc 100644 --- a/bls/src/lib.rs +++ b/bls/src/lib.rs @@ -1,20 +1,23 @@ -pub use crate::{ - cached_public_key::CachedPublicKey, error::Error, public_key::PublicKey, - public_key_bytes::PublicKeyBytes, secret_key::SecretKey, secret_key_bytes::SecretKeyBytes, - signature::Signature, signature_bytes::SignatureBytes, -}; +pub use bls_core::*; -mod cached_public_key; -mod consts; -mod error; -mod public_key; -mod public_key_bytes; -mod secret_key; -mod secret_key_bytes; -mod signature; -mod signature_bytes; +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 type AggregatePublicKey = PublicKey; -pub type AggregatePublicKeyBytes = PublicKeyBytes; -pub type AggregateSignature = Signature; -pub type AggregateSignatureBytes = SignatureBytes; + pub type AggregatePublicKey = PublicKey; + pub type AggregatePublicKeyBytes = PublicKeyBytes; + pub type AggregateSignature = Signature; + pub type AggregateSignatureBytes = SignatureBytes; + }; +} + +#[cfg(feature = "blst")] +implement_backend!(bls_blst); + +#[cfg(feature = "zkcrypto")] +implement_backend!(bls_zkcrypto); diff --git a/bls/src/public_key_bytes.rs b/bls/src/public_key_bytes.rs deleted file mode 100644 index 4850a6c2..00000000 --- a/bls/src/public_key_bytes.rs +++ /dev/null @@ -1,52 +0,0 @@ -use derive_more::AsRef; -use fixed_hash::construct_fixed_hash; -use hex::FromHex; -use impl_serde::impl_fixed_hash_serde; -use ssz::{BytesToDepth, MerkleTree, ReadError, Size, SszHash, SszRead, SszSize, SszWrite, H256}; -use typenum::{Unsigned as _, U1, U48}; - -type CompressedSize = U48; - -construct_fixed_hash! { - #[derive(AsRef)] - pub struct PublicKeyBytes(CompressedSize::USIZE); -} - -impl_fixed_hash_serde!(PublicKeyBytes, CompressedSize::USIZE); - -impl FromHex for PublicKeyBytes { - type Error = <[u8; CompressedSize::USIZE] as FromHex>::Error; - - fn from_hex>(digits: T) -> Result { - FromHex::from_hex(digits).map(Self) - } -} - -impl SszSize for PublicKeyBytes { - const SIZE: Size = Size::Fixed { - size: CompressedSize::USIZE, - }; -} - -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 SszHash for PublicKeyBytes { - type PackingFactor = U1; - - #[inline] - fn hash_tree_root(&self) -> H256 { - MerkleTree::>::merkleize_bytes(self) - } -} diff --git a/bls/src/secret_key.rs b/bls/src/secret_key.rs deleted file mode 100644 index d50dd58a..00000000 --- a/bls/src/secret_key.rs +++ /dev/null @@ -1,98 +0,0 @@ -use core::{ - fmt::{Binary, Display, LowerExp, LowerHex, Octal, Pointer, UpperExp, UpperHex}, - hash::{Hash, Hasher}, - ops::Deref, -}; -use std::borrow::ToOwned; - -use blst::min_pk::SecretKey as RawSecretKey; -use derive_more::Debug; -use serde::Serialize; -use ssz::{SszHash, SszWrite}; -use static_assertions::assert_not_impl_any; - -use crate::{consts::DOMAIN_SEPARATION_TAG, Error, PublicKey, SecretKeyBytes, Signature}; - -// `RawSecretKey` already implements `Zeroize` (with `zeroize(drop)`): -// -#[derive(Debug)] -// Inspired by `DebugSecret` from the `secrecy` crate. -#[debug("[REDACTED]")] -pub struct SecretKey(RawSecretKey); - -// Prevent `SecretKey` from implementing some traits to avoid leaking secret keys. -// This could also be done by wrapping it in `secrecy::Secret`. -assert_not_impl_any! { - SecretKey: - - Clone, - Copy, - Deref, - ToOwned, - - Binary, - Display, - LowerExp, - LowerHex, - Octal, - Pointer, - UpperExp, - UpperHex, - - Serialize, - SszHash, - SszWrite, -} - -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 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(Into::into) - } -} - -impl Hash for SecretKey { - fn hash(&self, hasher: &mut H) { - self.as_raw().to_bytes().hash(hasher) - } -} - -impl SecretKey { - #[inline] - #[must_use] - pub fn to_public_key(&self) -> PublicKey { - self.as_raw().sk_to_pk().into() - } - - #[inline] - #[must_use] - pub fn sign(&self, message: impl AsRef<[u8]>) -> Signature { - self.as_raw() - .sign(message.as_ref(), DOMAIN_SEPARATION_TAG, &[]) - .into() - } - - #[inline] - #[must_use] - pub fn to_bytes(&self) -> SecretKeyBytes { - let bytes = self.as_raw().to_bytes(); - SecretKeyBytes { bytes } - } - - const fn as_raw(&self) -> &RawSecretKey { - &self.0 - } -} diff --git a/bls/src/secret_key_bytes.rs b/bls/src/secret_key_bytes.rs deleted file mode 100644 index 63879ce3..00000000 --- a/bls/src/secret_key_bytes.rs +++ /dev/null @@ -1,78 +0,0 @@ -use core::{ - fmt::{Binary, Debug, Display, LowerExp, LowerHex, Octal, Pointer, UpperExp, UpperHex}, - ops::Deref, -}; - -use derive_more::{AsMut, AsRef, From}; -use hex::FromHex; -use serde::{Deserialize, Serialize}; -use ssz::{ReadError, Size, SszHash, SszRead, SszSize, SszWrite}; -use static_assertions::assert_not_impl_any; -use zeroize::{Zeroize, ZeroizeOnDrop}; - -use crate::SecretKey; - -// Unlike public keys and signatures, secret keys are not compressed. -const SIZE: usize = size_of::(); - -#[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], -} - -// Prevent `SecretKeyBytes` from implementing some traits to avoid leaking secret keys. -// This could also be done by wrapping it in `secrecy::Secret`. -assert_not_impl_any! { - SecretKeyBytes: - - Clone, - Copy, - Deref, - ToOwned, - - Debug, - Binary, - Display, - LowerExp, - LowerHex, - Octal, - Pointer, - UpperExp, - UpperHex, - - Serialize, - SszHash, -} - -impl FromHex for SecretKeyBytes { - type Error = <[u8; SIZE] as FromHex>::Error; - - fn from_hex>(digits: T) -> Result { - let bytes = FromHex::from_hex(digits)?; - Ok(Self { bytes }) - } -} - -impl SszSize for SecretKeyBytes { - const SIZE: Size = 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_bytes.rs b/bls/src/signature_bytes.rs deleted file mode 100644 index cba3a162..00000000 --- a/bls/src/signature_bytes.rs +++ /dev/null @@ -1,62 +0,0 @@ -use derive_more::AsRef; -use fixed_hash::construct_fixed_hash; -use impl_serde::impl_fixed_hash_serde; -use ssz::{BytesToDepth, MerkleTree, ReadError, Size, SszHash, SszRead, SszSize, SszWrite, H256}; -use typenum::{Unsigned as _, U1, U96}; - -type CompressedSize = U96; - -construct_fixed_hash! { - #[derive(AsRef)] - pub struct SignatureBytes(CompressedSize::USIZE); -} - -impl_fixed_hash_serde!(SignatureBytes, CompressedSize::USIZE); - -impl SszSize for SignatureBytes { - const SIZE: Size = Size::Fixed { - size: CompressedSize::USIZE, - }; -} - -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 = U1; - - #[inline] - fn hash_tree_root(&self) -> H256 { - MerkleTree::>::merkleize_bytes(self) - } -} - -impl SignatureBytes { - #[inline] - #[must_use] - pub fn empty() -> Self { - let mut bytes = Self::zero(); - - // The first byte of an empty signature must be 0xc0. - bytes.as_mut()[0] = 0xc0; - - bytes - } - - #[inline] - #[must_use] - pub fn is_empty(self) -> bool { - self == Self::empty() - } -} diff --git a/doppelganger_protection/src/doppelganger_protection.rs b/doppelganger_protection/src/doppelganger_protection.rs index 76a418e9..80db5c73 100644 --- a/doppelganger_protection/src/doppelganger_protection.rs +++ b/doppelganger_protection/src/doppelganger_protection.rs @@ -261,6 +261,7 @@ impl LivenessChecker { #[cfg(test)] mod tests { + use bls::traits::CachedPublicKey as _; use types::{config::Config, preset::Minimal}; use super::*; diff --git a/factory/src/lib.rs b/factory/src/lib.rs index 1ed691e9..6d5a2319 100644 --- a/factory/src/lib.rs +++ b/factory/src/lib.rs @@ -7,7 +7,10 @@ use core::ops::Range; use std::sync::Arc; use anyhow::{bail, ensure, Result}; -use bls::AggregateSignature; +use bls::{ + traits::{CachedPublicKey as _, SecretKey as _, Signature as _}, + AggregateSignature, +}; use deposit_tree::DepositTree; use helper_functions::{ accessors, misc, diff --git a/genesis/src/lib.rs b/genesis/src/lib.rs index 6ebb84fc..372a02bf 100644 --- a/genesis/src/lib.rs +++ b/genesis/src/lib.rs @@ -474,7 +474,7 @@ mod spec_tests { #[cfg(test)] mod extra_tests { - use bls::{SecretKey, SecretKeyBytes}; + use bls::{traits::SecretKey as _, SecretKey, SecretKeyBytes}; use helper_functions::signing::SignForAllForks; use std_ext::CopyExt as _; use tap::{Conv as _, TryConv as _}; diff --git a/grandine/src/validators.rs b/grandine/src/validators.rs index 54fcffe1..cac5a184 100644 --- a/grandine/src/validators.rs +++ b/grandine/src/validators.rs @@ -1,7 +1,7 @@ use std::{collections::HashMap, path::PathBuf, sync::Arc}; use anyhow::{Error, Result}; -use bls::{PublicKeyBytes, SecretKey}; +use bls::{traits::SecretKey as _, PublicKeyBytes, SecretKey}; use eip_2335::Keystore; use log::{info, warn}; use rayon::iter::{IntoParallelIterator as _, ParallelIterator as _}; diff --git a/helper_functions/src/accessors.rs b/helper_functions/src/accessors.rs index 1037818c..58ebe6fc 100644 --- a/helper_functions/src/accessors.rs +++ b/helper_functions/src/accessors.rs @@ -8,7 +8,10 @@ use std::sync::Arc; use anyhow::{bail, ensure, Result}; use arithmetic::U64Ext as _; use bit_field::BitField as _; -use bls::{AggregatePublicKey, CachedPublicKey, PublicKeyBytes}; +use bls::{ + traits::{CachedPublicKey as _, PublicKey as _}, + AggregatePublicKey, CachedPublicKey, PublicKeyBytes, +}; use im::HashMap; use itertools::{EitherOrBoth, Itertools as _}; use num_integer::Roots as _; diff --git a/helper_functions/src/fork.rs b/helper_functions/src/fork.rs index 59e85e26..365b48ce 100644 --- a/helper_functions/src/fork.rs +++ b/helper_functions/src/fork.rs @@ -2,7 +2,10 @@ use core::ops::BitOrAssign as _; use std::sync::Arc; use anyhow::Result; -use bls::SignatureBytes; +use bls::{ + traits::{CachedPublicKey as _, SignatureBytes as _}, + SignatureBytes, +}; use itertools::Itertools as _; use ssz::PersistentList; use std_ext::ArcExt as _; diff --git a/helper_functions/src/mutators.rs b/helper_functions/src/mutators.rs index 37eaab7b..fed16b6d 100644 --- a/helper_functions/src/mutators.rs +++ b/helper_functions/src/mutators.rs @@ -1,7 +1,10 @@ use core::cmp::Ordering; use anyhow::Result; -use bls::SignatureBytes; +use bls::{ + traits::{CachedPublicKey as _, SignatureBytes as _}, + 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 a29d8c93..e0742bce 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::SignatureBytes; +use bls::{traits::CachedPublicKey as _, SignatureBytes}; use itertools::Itertools as _; use ssz::SszHash as _; use tap::TryConv as _; @@ -586,7 +586,10 @@ mod spec_tests { #[cfg(test)] mod extra_tests { - use bls::{SecretKey, SecretKeyBytes}; + use bls::{ + traits::{SecretKey as _, Signature as _}, + SecretKey, SecretKeyBytes, + }; use std_ext::CopyExt as _; use tap::Conv as _; use types::{ diff --git a/helper_functions/src/signing.rs b/helper_functions/src/signing.rs index 72c8a87e..8c59005b 100644 --- a/helper_functions/src/signing.rs +++ b/helper_functions/src/signing.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use bls::{CachedPublicKey, SecretKey, Signature, SignatureBytes}; +use bls::{traits::SecretKey as _, CachedPublicKey, SecretKey, Signature, SignatureBytes}; use derive_more::From; use ssz::{Ssz, SszHash}; use types::{ diff --git a/helper_functions/src/spec_tests.rs b/helper_functions/src/spec_tests.rs index 68ae0b36..0cb279d0 100644 --- a/helper_functions/src/spec_tests.rs +++ b/helper_functions/src/spec_tests.rs @@ -1,6 +1,8 @@ use bls::{ + error::Error, + traits::{PublicKey as _, SecretKey as _, Signature as _}, AggregatePublicKey, AggregatePublicKeyBytes, AggregateSignature, AggregateSignatureBytes, - Error, PublicKey, PublicKeyBytes, SecretKey, SecretKeyBytes, Signature, SignatureBytes, + PublicKey, PublicKeyBytes, SecretKey, SecretKeyBytes, Signature, SignatureBytes, }; use serde::Deserialize; use spec_test_utils::Case; diff --git a/helper_functions/src/verifier.rs b/helper_functions/src/verifier.rs index 79ce63a2..56d8f2a7 100644 --- a/helper_functions/src/verifier.rs +++ b/helper_functions/src/verifier.rs @@ -2,6 +2,7 @@ use anyhow::{ensure, Result}; use bls::{ + traits::{CachedPublicKey as _, PublicKey as _, Signature as _, SignatureBytes as _}, AggregatePublicKey, AggregateSignature, CachedPublicKey, PublicKey, Signature, SignatureBytes, }; use derive_more::Constructor; @@ -437,7 +438,7 @@ pub enum VerifierOption { #[cfg(test)] mod tests { - use bls::{SecretKey, SecretKeyBytes}; + use bls::{traits::SecretKey as _, SecretKey, SecretKeyBytes}; use std_ext::CopyExt as _; use tap::{Conv as _, TryConv as _}; diff --git a/http_api/src/context.rs b/http_api/src/context.rs index a5089420..bde3f7de 100644 --- a/http_api/src/context.rs +++ b/http_api/src/context.rs @@ -4,7 +4,7 @@ use std::sync::Arc; use anyhow::Result; use attestation_verifier::AttestationVerifier; use block_producer::{BlockProducer, Options as BlockProducerOptions}; -use bls::{PublicKeyBytes, SecretKey}; +use bls::{traits::SecretKey as _, PublicKeyBytes, SecretKey}; use clock::Tick; use database::Database; use dedicated_executor::DedicatedExecutor; diff --git a/http_api/src/error.rs b/http_api/src/error.rs index 21fe7809..2d6e7fe3 100644 --- a/http_api/src/error.rs +++ b/http_api/src/error.rs @@ -9,7 +9,7 @@ use axum::{ Extension, Json, }; use axum_extra::extract::QueryRejection; -use bls::SignatureBytes; +use bls::{traits::SignatureBytes as _, SignatureBytes}; use futures::channel::oneshot::Canceled; use http_api_utils::ApiError; use serde::{Serialize, Serializer}; diff --git a/http_api/src/gui.rs b/http_api/src/gui.rs index 8ca65779..70047cd5 100644 --- a/http_api/src/gui.rs +++ b/http_api/src/gui.rs @@ -2,7 +2,7 @@ use core::num::NonZeroU64; use std::collections::{BTreeMap, HashMap, HashSet}; use anyhow::Result; -use bls::PublicKeyBytes; +use bls::{traits::CachedPublicKey as _, PublicKeyBytes}; use eth1_api::ApiController; use fork_choice_control::Wait; use futures::channel::mpsc::UnboundedSender; diff --git a/http_api/src/standard.rs b/http_api/src/standard.rs index cb5286ed..a9ba371f 100644 --- a/http_api/src/standard.rs +++ b/http_api/src/standard.rs @@ -15,7 +15,10 @@ use axum::{ Json, }; use block_producer::{BlockBuildOptions, BlockProducer, ProposerData, ValidatorBlindedBlock}; -use bls::{PublicKeyBytes, SignatureBytes}; +use bls::{ + traits::{CachedPublicKey as _, SignatureBytes as _}, + PublicKeyBytes, SignatureBytes, +}; use builder_api::unphased::containers::SignedValidatorRegistrationV1; use enum_iterator::Sequence as _; use eth1_api::{ApiController, Eth1Api}; diff --git a/interop/src/lib.rs b/interop/src/lib.rs index fd49bc65..36218d2f 100644 --- a/interop/src/lib.rs +++ b/interop/src/lib.rs @@ -1,7 +1,7 @@ use core::num::NonZeroU64; use anyhow::Result; -use bls::{SecretKey, SecretKeyBytes}; +use bls::{traits::SecretKey as _, SecretKey, SecretKeyBytes}; use deposit_tree::DepositTree; use genesis::Incremental; use helper_functions::{misc, signing::SignForAllForks}; diff --git a/keymanager/src/keystores.rs b/keymanager/src/keystores.rs index a3509594..04ffa19a 100644 --- a/keymanager/src/keystores.rs +++ b/keymanager/src/keystores.rs @@ -5,7 +5,7 @@ use std::{ }; use anyhow::{bail, ensure, Result}; -use bls::{PublicKeyBytes, SecretKey}; +use bls::{traits::SecretKey as _, PublicKeyBytes, SecretKey}; use eip_2335::Keystore; use futures::lock::{MappedMutexGuard, Mutex, MutexGuard}; use itertools::Itertools as _; diff --git a/operation_pools/src/attestation_agg_pool/tasks.rs b/operation_pools/src/attestation_agg_pool/tasks.rs index 365bf025..a688233c 100644 --- a/operation_pools/src/attestation_agg_pool/tasks.rs +++ b/operation_pools/src/attestation_agg_pool/tasks.rs @@ -6,7 +6,7 @@ use std::{ }; use anyhow::Result; -use bls::PublicKeyBytes; +use bls::{traits::Signature as _, PublicKeyBytes}; use eth1_api::ApiController; use features::Feature::DebugAttestationPacker; use fork_choice_control::Wait; diff --git a/operation_pools/src/sync_committee_agg_pool/pool.rs b/operation_pools/src/sync_committee_agg_pool/pool.rs index 68e51f59..4fb308d6 100644 --- a/operation_pools/src/sync_committee_agg_pool/pool.rs +++ b/operation_pools/src/sync_committee_agg_pool/pool.rs @@ -1,6 +1,7 @@ use std::{collections::HashSet, sync::Arc}; use anyhow::{anyhow, Result}; +use bls::traits::Signature as _; use helper_functions::accessors; use itertools::Itertools as _; use log::debug; diff --git a/operation_pools/src/sync_committee_agg_pool/tasks.rs b/operation_pools/src/sync_committee_agg_pool/tasks.rs index c69517eb..96cade77 100644 --- a/operation_pools/src/sync_committee_agg_pool/tasks.rs +++ b/operation_pools/src/sync_committee_agg_pool/tasks.rs @@ -1,6 +1,7 @@ use std::sync::Arc; use anyhow::{ensure, Result}; +use bls::traits::CachedPublicKey as _; use eth1_api::ApiController; use fork_choice_control::Wait; use futures::channel::mpsc::UnboundedSender; diff --git a/scripts/ci/clippy.bash b/scripts/ci/clippy.bash index c9496be2..4a8f2962 100755 --- a/scripts/ci/clippy.bash +++ b/scripts/ci/clippy.bash @@ -8,6 +8,7 @@ options=( --profile test --features eth2-cache + --features blst # TODO(Grandine Team): Clean up `dedicated_executor` and `eth2_libp2p`. # Go back to linting all packages implicitly. diff --git a/signer/src/signer.rs b/signer/src/signer.rs index c12ccea3..269f2f26 100644 --- a/signer/src/signer.rs +++ b/signer/src/signer.rs @@ -5,7 +5,7 @@ use std::{ use anyhow::Result; use arc_swap::{ArcSwap, Guard}; -use bls::{PublicKeyBytes, SecretKey, Signature}; +use bls::{traits::SecretKey as _, PublicKeyBytes, SecretKey, Signature}; use doppelganger_protection::DoppelgangerProtection; use futures::{ lock::Mutex, diff --git a/transition_functions/src/altair/block_processing.rs b/transition_functions/src/altair/block_processing.rs index e230e84d..ca156c0b 100644 --- a/transition_functions/src/altair/block_processing.rs +++ b/transition_functions/src/altair/block_processing.rs @@ -1,6 +1,7 @@ use anyhow::{ensure, Result}; use arithmetic::U64Ext as _; use bit_field::BitField as _; +use bls::traits::CachedPublicKey as _; use helper_functions::{ accessors::{ self, attestation_epoch, get_attestation_participation_flags, get_base_reward, diff --git a/transition_functions/src/altair/state_transition.rs b/transition_functions/src/altair/state_transition.rs index 9c9c08fb..b33a45b7 100644 --- a/transition_functions/src/altair/state_transition.rs +++ b/transition_functions/src/altair/state_transition.rs @@ -1,6 +1,7 @@ use core::ops::Not as _; use anyhow::{anyhow, Error as AnyhowError, Result}; +use bls::traits::CachedPublicKey as _; use helper_functions::{ accessors, error::SignatureKind, diff --git a/transition_functions/src/bellatrix/state_transition.rs b/transition_functions/src/bellatrix/state_transition.rs index 203c5929..af351f91 100644 --- a/transition_functions/src/bellatrix/state_transition.rs +++ b/transition_functions/src/bellatrix/state_transition.rs @@ -1,6 +1,7 @@ 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, diff --git a/transition_functions/src/capella/state_transition.rs b/transition_functions/src/capella/state_transition.rs index 88d7a07a..8789105c 100644 --- a/transition_functions/src/capella/state_transition.rs +++ b/transition_functions/src/capella/state_transition.rs @@ -1,6 +1,7 @@ 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, diff --git a/transition_functions/src/deneb/state_transition.rs b/transition_functions/src/deneb/state_transition.rs index 56664f68..6d41890f 100644 --- a/transition_functions/src/deneb/state_transition.rs +++ b/transition_functions/src/deneb/state_transition.rs @@ -1,6 +1,7 @@ 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, diff --git a/transition_functions/src/electra/block_processing.rs b/transition_functions/src/electra/block_processing.rs index c808bd4c..28ba5146 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::CachedPublicKey; +use bls::{traits::CachedPublicKey as _, CachedPublicKey}; use execution_engine::{ExecutionEngine, NullExecutionEngine}; use helper_functions::{ accessors::{ diff --git a/transition_functions/src/electra/state_transition.rs b/transition_functions/src/electra/state_transition.rs index 6d3addd6..b464f918 100644 --- a/transition_functions/src/electra/state_transition.rs +++ b/transition_functions/src/electra/state_transition.rs @@ -1,6 +1,7 @@ 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, diff --git a/transition_functions/src/phase0/block_processing.rs b/transition_functions/src/phase0/block_processing.rs index f81b87fd..178ab206 100644 --- a/transition_functions/src/phase0/block_processing.rs +++ b/transition_functions/src/phase0/block_processing.rs @@ -1,5 +1,6 @@ use anyhow::{ensure, Result}; use arithmetic::U64Ext as _; +use bls::traits::CachedPublicKey as _; use helper_functions::{ accessors::{ self, attestation_epoch, get_beacon_proposer_index, index_of_public_key, diff --git a/transition_functions/src/phase0/state_transition.rs b/transition_functions/src/phase0/state_transition.rs index 15c838cf..95be92d2 100644 --- a/transition_functions/src/phase0/state_transition.rs +++ b/transition_functions/src/phase0/state_transition.rs @@ -1,6 +1,7 @@ use core::ops::Not as _; use anyhow::{anyhow, Error as AnyhowError, Result}; +use bls::traits::CachedPublicKey as _; use helper_functions::{ accessors, error::SignatureKind, diff --git a/transition_functions/src/unphased/block_processing.rs b/transition_functions/src/unphased/block_processing.rs index 153b5189..deeedbf2 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::{CachedPublicKey, SignatureBytes}; +use bls::{traits::CachedPublicKey as _, CachedPublicKey, SignatureBytes}; use helper_functions::{ accessors::{ attestation_epoch, get_beacon_proposer_index, get_current_epoch, get_randao_mix, diff --git a/types/src/altair/container_impls.rs b/types/src/altair/container_impls.rs index 56a025e9..91fdbba1 100644 --- a/types/src/altair/container_impls.rs +++ b/types/src/altair/container_impls.rs @@ -1,4 +1,4 @@ -use bls::AggregateSignatureBytes; +use bls::{traits::SignatureBytes as _, AggregateSignatureBytes}; use crate::{altair::containers::SyncAggregate, preset::Preset}; diff --git a/validator/src/own_beacon_committee_members.rs b/validator/src/own_beacon_committee_members.rs index c99e6927..e6cb72b7 100644 --- a/validator/src/own_beacon_committee_members.rs +++ b/validator/src/own_beacon_committee_members.rs @@ -241,6 +241,7 @@ fn slot_index_from_slot(slot: Slot) -> usize { #[cfg(test)] mod tests { + use bls::traits::SecretKey as _; use reqwest::Client; use signer::{KeyOrigin, Web3SignerConfig}; use types::preset::Minimal; diff --git a/validator/src/own_sync_committee_subscriptions.rs b/validator/src/own_sync_committee_subscriptions.rs index 1d3f1c50..af9dc789 100644 --- a/validator/src/own_sync_committee_subscriptions.rs +++ b/validator/src/own_sync_committee_subscriptions.rs @@ -1,7 +1,7 @@ use core::marker::PhantomData; use std::collections::{BTreeMap, HashMap, HashSet}; -use bls::PublicKeyBytes; +use bls::{traits::CachedPublicKey as _, PublicKeyBytes}; use helper_functions::{accessors, misc}; use itertools::Itertools as _; use p2p::SyncCommitteeSubscription; diff --git a/validator/src/slot_head.rs b/validator/src/slot_head.rs index cc113b15..9bdfd805 100644 --- a/validator/src/slot_head.rs +++ b/validator/src/slot_head.rs @@ -2,7 +2,7 @@ use core::fmt::Debug; use std::sync::Arc; use anyhow::Result; -use bls::{CachedPublicKey, PublicKeyBytes, SignatureBytes}; +use bls::{traits::CachedPublicKey as _, CachedPublicKey, PublicKeyBytes, SignatureBytes}; use futures::lock::Mutex; use helper_functions::{ accessors, misc, predicates, diff --git a/validator/src/validator.rs b/validator/src/validator.rs index b93cc11e..da36af77 100644 --- a/validator/src/validator.rs +++ b/validator/src/validator.rs @@ -9,7 +9,7 @@ use std::{ use anyhow::{Error as AnyhowError, Result}; use block_producer::{BlockBuildOptions, BlockProducer, ValidatorBlindedBlock}; -use bls::{PublicKeyBytes, Signature, SignatureBytes}; +use bls::{traits::CachedPublicKey as _, PublicKeyBytes, Signature, SignatureBytes}; use builder_api::{ consts::EPOCHS_PER_VALIDATOR_REGISTRATION_SUBMISSION, unphased::containers::{SignedValidatorRegistrationV1, ValidatorRegistrationV1}, diff --git a/validator_key_cache/src/lib.rs b/validator_key_cache/src/lib.rs index fa0953e3..bdbbf1f5 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::{PublicKeyBytes, SecretKey, SecretKeyBytes}; +use bls::{traits::SecretKey as _, PublicKeyBytes, SecretKey, SecretKeyBytes}; use eip_2335::Crypto; use log::info; use sha2::{Digest as _, Sha256};