Skip to content

Commit e4d35d8

Browse files
authored
Merge of #5897
2 parents d9ad1f5 + 99a7dbd commit e4d35d8

File tree

10 files changed

+199
-25
lines changed

10 files changed

+199
-25
lines changed

beacon_node/beacon_chain/src/schema_change.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ mod migration_schema_v17;
33
mod migration_schema_v18;
44
mod migration_schema_v19;
55
mod migration_schema_v20;
6+
mod migration_schema_v21;
67

78
use crate::beacon_chain::BeaconChainTypes;
89
use crate::types::ChainSpec;
@@ -87,6 +88,14 @@ pub fn migrate_schema<T: BeaconChainTypes>(
8788
let ops = migration_schema_v20::downgrade_from_v20::<T>(db.clone(), log)?;
8889
db.store_schema_version_atomically(to, ops)
8990
}
91+
(SchemaVersion(20), SchemaVersion(21)) => {
92+
let ops = migration_schema_v21::upgrade_to_v21::<T>(db.clone(), log)?;
93+
db.store_schema_version_atomically(to, ops)
94+
}
95+
(SchemaVersion(21), SchemaVersion(20)) => {
96+
let ops = migration_schema_v21::downgrade_from_v21::<T>(db.clone(), log)?;
97+
db.store_schema_version_atomically(to, ops)
98+
}
9099
// Anything else is an error.
91100
(_, _) => Err(HotColdDBError::UnsupportedSchemaVersion {
92101
target_version: to,

beacon_node/beacon_chain/src/schema_change/migration_schema_v20.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ pub fn upgrade_to_v20<T: BeaconChainTypes>(
1111
db: Arc<HotColdDB<T::EthSpec, T::HotStore, T::ColdStore>>,
1212
log: Logger,
1313
) -> Result<Vec<KeyValueStoreOp>, Error> {
14+
info!(log, "Upgrading from v19 to v20");
15+
1416
// Load a V15 op pool and transform it to V20.
1517
let Some(PersistedOperationPoolV15::<T::EthSpec> {
1618
attestations_v15,
@@ -52,6 +54,8 @@ pub fn downgrade_from_v20<T: BeaconChainTypes>(
5254
db: Arc<HotColdDB<T::EthSpec, T::HotStore, T::ColdStore>>,
5355
log: Logger,
5456
) -> Result<Vec<KeyValueStoreOp>, Error> {
57+
info!(log, "Downgrading from v20 to v19");
58+
5559
// Load a V20 op pool and transform it to V15.
5660
let Some(PersistedOperationPoolV20::<T::EthSpec> {
5761
attestations,
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
use crate::beacon_chain::BeaconChainTypes;
2+
use crate::validator_pubkey_cache::DatabasePubkey;
3+
use slog::{info, Logger};
4+
use ssz::{Decode, Encode};
5+
use std::sync::Arc;
6+
use store::{
7+
get_key_for_col, DBColumn, Error, HotColdDB, KeyValueStore, KeyValueStoreOp, StoreItem,
8+
};
9+
use types::{Hash256, PublicKey};
10+
11+
const LOG_EVERY: usize = 200_000;
12+
13+
pub fn upgrade_to_v21<T: BeaconChainTypes>(
14+
db: Arc<HotColdDB<T::EthSpec, T::HotStore, T::ColdStore>>,
15+
log: Logger,
16+
) -> Result<Vec<KeyValueStoreOp>, Error> {
17+
info!(log, "Upgrading from v20 to v21");
18+
19+
let mut ops = vec![];
20+
21+
// Iterate through all pubkeys and decompress them.
22+
for (i, res) in db
23+
.hot_db
24+
.iter_column::<Hash256>(DBColumn::PubkeyCache)
25+
.enumerate()
26+
{
27+
let (key, value) = res?;
28+
let pubkey = PublicKey::from_ssz_bytes(&value)?;
29+
let decompressed = DatabasePubkey::from_pubkey(&pubkey);
30+
ops.push(decompressed.as_kv_store_op(key));
31+
32+
if i > 0 && i % LOG_EVERY == 0 {
33+
info!(
34+
log,
35+
"Public key decompression in progress";
36+
"keys_decompressed" => i
37+
);
38+
}
39+
}
40+
info!(log, "Public key decompression complete");
41+
42+
Ok(ops)
43+
}
44+
45+
pub fn downgrade_from_v21<T: BeaconChainTypes>(
46+
db: Arc<HotColdDB<T::EthSpec, T::HotStore, T::ColdStore>>,
47+
log: Logger,
48+
) -> Result<Vec<KeyValueStoreOp>, Error> {
49+
info!(log, "Downgrading from v21 to v20");
50+
51+
let mut ops = vec![];
52+
53+
// Iterate through all pubkeys and recompress them.
54+
for (i, res) in db
55+
.hot_db
56+
.iter_column::<Hash256>(DBColumn::PubkeyCache)
57+
.enumerate()
58+
{
59+
let (key, value) = res?;
60+
let decompressed = DatabasePubkey::from_ssz_bytes(&value)?;
61+
let (_, pubkey_bytes) = decompressed.as_pubkey().map_err(|e| Error::DBError {
62+
message: format!("{e:?}"),
63+
})?;
64+
65+
let db_key = get_key_for_col(DBColumn::PubkeyCache.into(), key.as_bytes());
66+
ops.push(KeyValueStoreOp::PutKeyValue(
67+
db_key,
68+
pubkey_bytes.as_ssz_bytes(),
69+
));
70+
71+
if i > 0 && i % LOG_EVERY == 0 {
72+
info!(
73+
log,
74+
"Public key compression in progress";
75+
"keys_compressed" => i
76+
);
77+
}
78+
}
79+
80+
info!(log, "Public key compression complete");
81+
82+
Ok(ops)
83+
}

beacon_node/beacon_chain/src/validator_pubkey_cache.rs

Lines changed: 38 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
use crate::errors::BeaconChainError;
22
use crate::{BeaconChainTypes, BeaconStore};
3+
use bls::PUBLIC_KEY_UNCOMPRESSED_BYTES_LEN;
4+
use smallvec::SmallVec;
35
use ssz::{Decode, Encode};
6+
use ssz_derive::{Decode, Encode};
47
use std::collections::HashMap;
58
use std::marker::PhantomData;
69
use store::{DBColumn, Error as StoreError, StoreItem, StoreOp};
@@ -49,14 +52,13 @@ impl<T: BeaconChainTypes> ValidatorPubkeyCache<T> {
4952
let mut pubkey_bytes = vec![];
5053

5154
for validator_index in 0.. {
52-
if let Some(DatabasePubkey(pubkey)) =
55+
if let Some(db_pubkey) =
5356
store.get_item(&DatabasePubkey::key_for_index(validator_index))?
5457
{
55-
pubkeys.push((&pubkey).try_into().map_err(|e| {
56-
BeaconChainError::ValidatorPubkeyCacheError(format!("{:?}", e))
57-
})?);
58-
pubkey_bytes.push(pubkey);
59-
indices.insert(pubkey, validator_index);
58+
let (pk, pk_bytes) = DatabasePubkey::as_pubkey(&db_pubkey)?;
59+
pubkeys.push(pk);
60+
indices.insert(pk_bytes, validator_index);
61+
pubkey_bytes.push(pk_bytes);
6062
} else {
6163
break;
6264
}
@@ -104,29 +106,29 @@ impl<T: BeaconChainTypes> ValidatorPubkeyCache<T> {
104106
self.indices.reserve(validator_keys.len());
105107

106108
let mut store_ops = Vec::with_capacity(validator_keys.len());
107-
for pubkey in validator_keys {
109+
for pubkey_bytes in validator_keys {
108110
let i = self.pubkeys.len();
109111

110-
if self.indices.contains_key(&pubkey) {
112+
if self.indices.contains_key(&pubkey_bytes) {
111113
return Err(BeaconChainError::DuplicateValidatorPublicKey);
112114
}
113115

116+
let pubkey = (&pubkey_bytes)
117+
.try_into()
118+
.map_err(BeaconChainError::InvalidValidatorPubkeyBytes)?;
119+
114120
// Stage the new validator key for writing to disk.
115121
// It will be committed atomically when the block that introduced it is written to disk.
116122
// Notably it is NOT written while the write lock on the cache is held.
117123
// See: https://github.com/sigp/lighthouse/issues/2327
118124
store_ops.push(StoreOp::KeyValueOp(
119-
DatabasePubkey(pubkey).as_kv_store_op(DatabasePubkey::key_for_index(i)),
125+
DatabasePubkey::from_pubkey(&pubkey)
126+
.as_kv_store_op(DatabasePubkey::key_for_index(i)),
120127
));
121128

122-
self.pubkeys.push(
123-
(&pubkey)
124-
.try_into()
125-
.map_err(BeaconChainError::InvalidValidatorPubkeyBytes)?,
126-
);
127-
self.pubkey_bytes.push(pubkey);
128-
129-
self.indices.insert(pubkey, i);
129+
self.pubkeys.push(pubkey);
130+
self.pubkey_bytes.push(pubkey_bytes);
131+
self.indices.insert(pubkey_bytes, i);
130132
}
131133

132134
Ok(store_ops)
@@ -166,26 +168,42 @@ impl<T: BeaconChainTypes> ValidatorPubkeyCache<T> {
166168
/// Wrapper for a public key stored in the database.
167169
///
168170
/// Keyed by the validator index as `Hash256::from_low_u64_be(index)`.
169-
struct DatabasePubkey(PublicKeyBytes);
171+
#[derive(Encode, Decode)]
172+
pub struct DatabasePubkey {
173+
pubkey: SmallVec<[u8; PUBLIC_KEY_UNCOMPRESSED_BYTES_LEN]>,
174+
}
170175

171176
impl StoreItem for DatabasePubkey {
172177
fn db_column() -> DBColumn {
173178
DBColumn::PubkeyCache
174179
}
175180

176181
fn as_store_bytes(&self) -> Vec<u8> {
177-
self.0.as_ssz_bytes()
182+
self.as_ssz_bytes()
178183
}
179184

180185
fn from_store_bytes(bytes: &[u8]) -> Result<Self, StoreError> {
181-
Ok(Self(PublicKeyBytes::from_ssz_bytes(bytes)?))
186+
Ok(Self::from_ssz_bytes(bytes)?)
182187
}
183188
}
184189

185190
impl DatabasePubkey {
186191
fn key_for_index(index: usize) -> Hash256 {
187192
Hash256::from_low_u64_be(index as u64)
188193
}
194+
195+
pub fn from_pubkey(pubkey: &PublicKey) -> Self {
196+
Self {
197+
pubkey: pubkey.serialize_uncompressed().into(),
198+
}
199+
}
200+
201+
pub fn as_pubkey(&self) -> Result<(PublicKey, PublicKeyBytes), BeaconChainError> {
202+
let pubkey = PublicKey::deserialize_uncompressed(&self.pubkey)
203+
.map_err(BeaconChainError::InvalidValidatorPubkeyBytes)?;
204+
let pubkey_bytes = pubkey.compress();
205+
Ok((pubkey, pubkey_bytes))
206+
}
189207
}
190208

191209
#[cfg(test)]

beacon_node/store/src/metadata.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use ssz::{Decode, Encode};
44
use ssz_derive::{Decode, Encode};
55
use types::{Checkpoint, Hash256, Slot};
66

7-
pub const CURRENT_SCHEMA_VERSION: SchemaVersion = SchemaVersion(20);
7+
pub const CURRENT_SCHEMA_VERSION: SchemaVersion = SchemaVersion(21);
88

99
// All the keys that get stored under the `BeaconMeta` column.
1010
//

crypto/bls/src/generic_public_key.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ use tree_hash::TreeHash;
1111
/// The byte-length of a BLS public key when serialized in compressed form.
1212
pub const PUBLIC_KEY_BYTES_LEN: usize = 48;
1313

14+
/// The byte-length of a BLS public key when serialized in uncompressed form.
15+
pub const PUBLIC_KEY_UNCOMPRESSED_BYTES_LEN: usize = 96;
16+
1417
/// Represents the public key at infinity.
1518
pub const INFINITY_PUBLIC_KEY: [u8; PUBLIC_KEY_BYTES_LEN] = [
1619
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 {
2326
/// Serialize `self` as compressed bytes.
2427
fn serialize(&self) -> [u8; PUBLIC_KEY_BYTES_LEN];
2528

29+
/// Serialize `self` as uncompressed bytes.
30+
fn serialize_uncompressed(&self) -> [u8; PUBLIC_KEY_UNCOMPRESSED_BYTES_LEN];
31+
2632
/// Deserialize `self` from compressed bytes.
2733
fn deserialize(bytes: &[u8]) -> Result<Self, Error>;
34+
35+
/// Deserialize `self` from uncompressed bytes.
36+
///
37+
/// This function *does not* perform thorough checks of the input bytes and should only be
38+
/// used with bytes output from `Self::serialize_uncompressed`.
39+
fn deserialize_uncompressed(bytes: &[u8]) -> Result<Self, Error>;
2840
}
2941

3042
/// A BLS public key that is generic across some BLS point (`Pub`).
@@ -65,6 +77,11 @@ where
6577
self.point.serialize()
6678
}
6779

80+
/// Serialize `self` as uncompressed bytes.
81+
pub fn serialize_uncompressed(&self) -> [u8; PUBLIC_KEY_UNCOMPRESSED_BYTES_LEN] {
82+
self.point.serialize_uncompressed()
83+
}
84+
6885
/// Deserialize `self` from compressed bytes.
6986
pub fn deserialize(bytes: &[u8]) -> Result<Self, Error> {
7087
if bytes == &INFINITY_PUBLIC_KEY[..] {
@@ -75,6 +92,13 @@ where
7592
})
7693
}
7794
}
95+
96+
/// Deserialize `self` from compressed bytes.
97+
pub fn deserialize_uncompressed(bytes: &[u8]) -> Result<Self, Error> {
98+
Ok(Self {
99+
point: Pub::deserialize_uncompressed(bytes)?,
100+
})
101+
}
78102
}
79103

80104
impl<Pub: TPublicKey> Eq for GenericPublicKey<Pub> {}

crypto/bls/src/impls/blst.rs

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
use crate::{
22
generic_aggregate_public_key::TAggregatePublicKey,
33
generic_aggregate_signature::TAggregateSignature,
4-
generic_public_key::{GenericPublicKey, TPublicKey, PUBLIC_KEY_BYTES_LEN},
4+
generic_public_key::{
5+
GenericPublicKey, TPublicKey, PUBLIC_KEY_BYTES_LEN, PUBLIC_KEY_UNCOMPRESSED_BYTES_LEN,
6+
},
57
generic_secret_key::TSecretKey,
68
generic_signature::{TSignature, SIGNATURE_BYTES_LEN},
7-
Error, Hash256, ZeroizeHash, INFINITY_SIGNATURE,
9+
BlstError, Error, Hash256, ZeroizeHash, INFINITY_SIGNATURE,
810
};
911
pub use blst::min_pk as blst_core;
1012
use blst::{blst_scalar, BLST_ERROR};
@@ -121,6 +123,10 @@ impl TPublicKey for blst_core::PublicKey {
121123
self.compress()
122124
}
123125

126+
fn serialize_uncompressed(&self) -> [u8; PUBLIC_KEY_UNCOMPRESSED_BYTES_LEN] {
127+
blst_core::PublicKey::serialize(self)
128+
}
129+
124130
fn deserialize(bytes: &[u8]) -> Result<Self, Error> {
125131
// key_validate accepts uncompressed bytes too so enforce byte length here.
126132
// 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 {
132138
}
133139
Self::key_validate(bytes).map_err(Into::into)
134140
}
141+
142+
fn deserialize_uncompressed(bytes: &[u8]) -> Result<Self, Error> {
143+
if bytes.len() != PUBLIC_KEY_UNCOMPRESSED_BYTES_LEN {
144+
return Err(Error::InvalidByteLength {
145+
got: bytes.len(),
146+
expected: PUBLIC_KEY_UNCOMPRESSED_BYTES_LEN,
147+
});
148+
}
149+
// Ensure we use the `blst` function rather than the one from this trait.
150+
let result: Result<Self, BlstError> = Self::deserialize(bytes);
151+
let key = result?;
152+
Ok(key)
153+
}
135154
}
136155

137156
/// A wrapper that allows for `PartialEq` and `Clone` impls.

crypto/bls/src/impls/fake_crypto.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
use crate::{
22
generic_aggregate_public_key::TAggregatePublicKey,
33
generic_aggregate_signature::TAggregateSignature,
4-
generic_public_key::{GenericPublicKey, TPublicKey, PUBLIC_KEY_BYTES_LEN},
4+
generic_public_key::{
5+
GenericPublicKey, TPublicKey, PUBLIC_KEY_BYTES_LEN, PUBLIC_KEY_UNCOMPRESSED_BYTES_LEN,
6+
},
57
generic_secret_key::{TSecretKey, SECRET_KEY_BYTES_LEN},
68
generic_signature::{TSignature, SIGNATURE_BYTES_LEN},
79
Error, Hash256, ZeroizeHash, INFINITY_PUBLIC_KEY, INFINITY_SIGNATURE,
@@ -46,11 +48,19 @@ impl TPublicKey for PublicKey {
4648
self.0
4749
}
4850

51+
fn serialize_uncompressed(&self) -> [u8; PUBLIC_KEY_UNCOMPRESSED_BYTES_LEN] {
52+
panic!("fake_crypto does not support uncompressed keys")
53+
}
54+
4955
fn deserialize(bytes: &[u8]) -> Result<Self, Error> {
5056
let mut pubkey = Self::infinity();
5157
pubkey.0[..].copy_from_slice(&bytes[0..PUBLIC_KEY_BYTES_LEN]);
5258
Ok(pubkey)
5359
}
60+
61+
fn deserialize_uncompressed(_: &[u8]) -> Result<Self, Error> {
62+
panic!("fake_crypto does not support uncompressed keys")
63+
}
5464
}
5565

5666
impl Eq for PublicKey {}

crypto/bls/src/lib.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,9 @@ mod zeroize_hash;
3333

3434
pub mod impls;
3535

36-
pub use generic_public_key::{INFINITY_PUBLIC_KEY, PUBLIC_KEY_BYTES_LEN};
36+
pub use generic_public_key::{
37+
INFINITY_PUBLIC_KEY, PUBLIC_KEY_BYTES_LEN, PUBLIC_KEY_UNCOMPRESSED_BYTES_LEN,
38+
};
3739
pub use generic_secret_key::SECRET_KEY_BYTES_LEN;
3840
pub use generic_signature::{INFINITY_SIGNATURE, SIGNATURE_BYTES_LEN};
3941
pub use get_withdrawal_credentials::get_withdrawal_credentials;

0 commit comments

Comments
 (0)