Store pubkey cache decompressed on disk (#5897)

* Support uncompressed keys in crypto/bls

* Use uncompressed keys in cache

* Implement DB upgrade

* Implement downgrade

* More logging on v20 upgrade

* Revert "More logging on v20 upgrade"

This reverts commit cc5789b9d3.

* Merge remote-tracking branch 'origin/unstable' into uncompressed-pubkeys

* Add a little more logging

* Merge remote-tracking branch 'origin/unstable' into uncompressed-pubkeys
This commit is contained in:
Michael Sproul
2024-07-04 14:27:41 +10:00
committed by GitHub
parent d9ad1f5bfa
commit d84e3e391e
10 changed files with 199 additions and 25 deletions

View File

@@ -11,6 +11,9 @@ use tree_hash::TreeHash;
/// The byte-length of a BLS public key when serialized in compressed form.
pub const PUBLIC_KEY_BYTES_LEN: usize = 48;
/// The byte-length of a BLS public key when serialized in uncompressed form.
pub const PUBLIC_KEY_UNCOMPRESSED_BYTES_LEN: usize = 96;
/// Represents the public key at infinity.
pub const INFINITY_PUBLIC_KEY: [u8; PUBLIC_KEY_BYTES_LEN] = [
0xc0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@@ -23,8 +26,17 @@ pub trait TPublicKey: Sized + Clone {
/// Serialize `self` as compressed bytes.
fn serialize(&self) -> [u8; PUBLIC_KEY_BYTES_LEN];
/// Serialize `self` as uncompressed bytes.
fn serialize_uncompressed(&self) -> [u8; PUBLIC_KEY_UNCOMPRESSED_BYTES_LEN];
/// Deserialize `self` from compressed bytes.
fn deserialize(bytes: &[u8]) -> Result<Self, Error>;
/// Deserialize `self` from uncompressed bytes.
///
/// This function *does not* perform thorough checks of the input bytes and should only be
/// used with bytes output from `Self::serialize_uncompressed`.
fn deserialize_uncompressed(bytes: &[u8]) -> Result<Self, Error>;
}
/// A BLS public key that is generic across some BLS point (`Pub`).
@@ -65,6 +77,11 @@ where
self.point.serialize()
}
/// Serialize `self` as uncompressed bytes.
pub fn serialize_uncompressed(&self) -> [u8; PUBLIC_KEY_UNCOMPRESSED_BYTES_LEN] {
self.point.serialize_uncompressed()
}
/// Deserialize `self` from compressed bytes.
pub fn deserialize(bytes: &[u8]) -> Result<Self, Error> {
if bytes == &INFINITY_PUBLIC_KEY[..] {
@@ -75,6 +92,13 @@ where
})
}
}
/// Deserialize `self` from compressed bytes.
pub fn deserialize_uncompressed(bytes: &[u8]) -> Result<Self, Error> {
Ok(Self {
point: Pub::deserialize_uncompressed(bytes)?,
})
}
}
impl<Pub: TPublicKey> Eq for GenericPublicKey<Pub> {}

View File

@@ -1,10 +1,12 @@
use crate::{
generic_aggregate_public_key::TAggregatePublicKey,
generic_aggregate_signature::TAggregateSignature,
generic_public_key::{GenericPublicKey, TPublicKey, PUBLIC_KEY_BYTES_LEN},
generic_public_key::{
GenericPublicKey, TPublicKey, PUBLIC_KEY_BYTES_LEN, PUBLIC_KEY_UNCOMPRESSED_BYTES_LEN,
},
generic_secret_key::TSecretKey,
generic_signature::{TSignature, SIGNATURE_BYTES_LEN},
Error, Hash256, ZeroizeHash, INFINITY_SIGNATURE,
BlstError, Error, Hash256, ZeroizeHash, INFINITY_SIGNATURE,
};
pub use blst::min_pk as blst_core;
use blst::{blst_scalar, BLST_ERROR};
@@ -121,6 +123,10 @@ impl TPublicKey for blst_core::PublicKey {
self.compress()
}
fn serialize_uncompressed(&self) -> [u8; PUBLIC_KEY_UNCOMPRESSED_BYTES_LEN] {
blst_core::PublicKey::serialize(self)
}
fn deserialize(bytes: &[u8]) -> Result<Self, Error> {
// key_validate accepts uncompressed bytes too so enforce byte length here.
// It also does subgroup checks, noting infinity check is done in `generic_public_key.rs`.
@@ -132,6 +138,19 @@ impl TPublicKey for blst_core::PublicKey {
}
Self::key_validate(bytes).map_err(Into::into)
}
fn deserialize_uncompressed(bytes: &[u8]) -> Result<Self, Error> {
if bytes.len() != PUBLIC_KEY_UNCOMPRESSED_BYTES_LEN {
return Err(Error::InvalidByteLength {
got: bytes.len(),
expected: PUBLIC_KEY_UNCOMPRESSED_BYTES_LEN,
});
}
// Ensure we use the `blst` function rather than the one from this trait.
let result: Result<Self, BlstError> = Self::deserialize(bytes);
let key = result?;
Ok(key)
}
}
/// A wrapper that allows for `PartialEq` and `Clone` impls.

View File

@@ -1,7 +1,9 @@
use crate::{
generic_aggregate_public_key::TAggregatePublicKey,
generic_aggregate_signature::TAggregateSignature,
generic_public_key::{GenericPublicKey, TPublicKey, PUBLIC_KEY_BYTES_LEN},
generic_public_key::{
GenericPublicKey, TPublicKey, PUBLIC_KEY_BYTES_LEN, PUBLIC_KEY_UNCOMPRESSED_BYTES_LEN,
},
generic_secret_key::{TSecretKey, SECRET_KEY_BYTES_LEN},
generic_signature::{TSignature, SIGNATURE_BYTES_LEN},
Error, Hash256, ZeroizeHash, INFINITY_PUBLIC_KEY, INFINITY_SIGNATURE,
@@ -46,11 +48,19 @@ impl TPublicKey for PublicKey {
self.0
}
fn serialize_uncompressed(&self) -> [u8; PUBLIC_KEY_UNCOMPRESSED_BYTES_LEN] {
panic!("fake_crypto does not support uncompressed keys")
}
fn deserialize(bytes: &[u8]) -> Result<Self, Error> {
let mut pubkey = Self::infinity();
pubkey.0[..].copy_from_slice(&bytes[0..PUBLIC_KEY_BYTES_LEN]);
Ok(pubkey)
}
fn deserialize_uncompressed(_: &[u8]) -> Result<Self, Error> {
panic!("fake_crypto does not support uncompressed keys")
}
}
impl Eq for PublicKey {}

View File

@@ -33,7 +33,9 @@ mod zeroize_hash;
pub mod impls;
pub use generic_public_key::{INFINITY_PUBLIC_KEY, PUBLIC_KEY_BYTES_LEN};
pub use generic_public_key::{
INFINITY_PUBLIC_KEY, PUBLIC_KEY_BYTES_LEN, PUBLIC_KEY_UNCOMPRESSED_BYTES_LEN,
};
pub use generic_secret_key::SECRET_KEY_BYTES_LEN;
pub use generic_signature::{INFINITY_SIGNATURE, SIGNATURE_BYTES_LEN};
pub use get_withdrawal_credentials::get_withdrawal_credentials;

View File

@@ -341,6 +341,11 @@ macro_rules! test_suite {
.assert_single_message_verify(true)
}
#[test]
fn deserialize_infinity_public_key() {
PublicKey::deserialize(&bls::INFINITY_PUBLIC_KEY).unwrap_err();
}
/// A helper struct to make it easer to deal with `SignatureSet` lifetimes.
struct OwnedSignatureSet {
signature: AggregateSignature,