Skip to content

Commit e305cb1

Browse files
authored
Custody persist fix (#7661)
N/A Persist the epoch -> cgc values. This is to ensure that `ValidatorRegistrations::latest_validator_custody_requirement` always returns a `Some` value post restart assuming the `epoch_validator_custody_requirements` map has been updated in the previous runs.
1 parent 257d270 commit e305cb1

File tree

7 files changed

+131
-12
lines changed

7 files changed

+131
-12
lines changed

beacon_node/beacon_chain/src/beacon_chain.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -654,6 +654,10 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
654654

655655
/// Persists the custody information to disk.
656656
pub fn persist_custody_context(&self) -> Result<(), Error> {
657+
if !self.spec.is_peer_das_scheduled() {
658+
return Ok(());
659+
}
660+
657661
let custody_context: CustodyContextSsz = self
658662
.data_availability_checker
659663
.custody_context()

beacon_node/beacon_chain/src/persisted_custody.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use types::{EthSpec, Hash256};
77
/// 32-byte key for accessing the `CustodyContext`. All zero because `CustodyContext` has its own column.
88
pub const CUSTODY_DB_KEY: Hash256 = Hash256::ZERO;
99

10-
pub struct PersistedCustody(CustodyContextSsz);
10+
pub struct PersistedCustody(pub CustodyContextSsz);
1111

1212
pub fn load_custody_context<E: EthSpec, Hot: ItemStore<E>, Cold: ItemStore<E>>(
1313
store: Arc<HotColdDB<E, Hot, Cold>>,

beacon_node/beacon_chain/src/schema_change.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
mod migration_schema_v23;
33
mod migration_schema_v24;
44
mod migration_schema_v25;
5+
mod migration_schema_v26;
56

67
use crate::beacon_chain::BeaconChainTypes;
78
use std::sync::Arc;
@@ -58,6 +59,14 @@ pub fn migrate_schema<T: BeaconChainTypes>(
5859
let ops = migration_schema_v25::downgrade_from_v25()?;
5960
db.store_schema_version_atomically(to, ops)
6061
}
62+
(SchemaVersion(25), SchemaVersion(26)) => {
63+
let ops = migration_schema_v26::upgrade_to_v26::<T>(db.clone())?;
64+
db.store_schema_version_atomically(to, ops)
65+
}
66+
(SchemaVersion(26), SchemaVersion(25)) => {
67+
let ops = migration_schema_v26::downgrade_from_v26::<T>(db.clone())?;
68+
db.store_schema_version_atomically(to, ops)
69+
}
6170
// Anything else is an error.
6271
(_, _) => Err(HotColdDBError::UnsupportedSchemaVersion {
6372
target_version: to,
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
use crate::persisted_custody::{PersistedCustody, CUSTODY_DB_KEY};
2+
use crate::validator_custody::CustodyContextSsz;
3+
use crate::BeaconChainTypes;
4+
use ssz::{Decode, Encode};
5+
use ssz_derive::{Decode, Encode};
6+
use std::sync::Arc;
7+
use store::{DBColumn, Error, HotColdDB, KeyValueStoreOp, StoreItem};
8+
use tracing::info;
9+
10+
#[derive(Debug, Encode, Decode, Clone)]
11+
pub(crate) struct CustodyContextSszV24 {
12+
pub(crate) validator_custody_at_head: u64,
13+
pub(crate) persisted_is_supernode: bool,
14+
}
15+
16+
pub(crate) struct PersistedCustodyV24(CustodyContextSszV24);
17+
18+
impl StoreItem for PersistedCustodyV24 {
19+
fn db_column() -> DBColumn {
20+
DBColumn::CustodyContext
21+
}
22+
23+
fn as_store_bytes(&self) -> Vec<u8> {
24+
self.0.as_ssz_bytes()
25+
}
26+
27+
fn from_store_bytes(bytes: &[u8]) -> Result<Self, Error> {
28+
let custody_context = CustodyContextSszV24::from_ssz_bytes(bytes)?;
29+
Ok(PersistedCustodyV24(custody_context))
30+
}
31+
}
32+
33+
/// Upgrade the `CustodyContext` entry to v26.
34+
pub fn upgrade_to_v26<T: BeaconChainTypes>(
35+
db: Arc<HotColdDB<T::EthSpec, T::HotStore, T::ColdStore>>,
36+
) -> Result<Vec<KeyValueStoreOp>, Error> {
37+
let ops = if db.spec.is_peer_das_scheduled() {
38+
match db.get_item::<PersistedCustodyV24>(&CUSTODY_DB_KEY) {
39+
Ok(Some(PersistedCustodyV24(ssz_v24))) => {
40+
info!("Migrating `CustodyContext` to v26 schema");
41+
let custody_context_v2 = CustodyContextSsz {
42+
validator_custody_at_head: ssz_v24.validator_custody_at_head,
43+
persisted_is_supernode: ssz_v24.persisted_is_supernode,
44+
epoch_validator_custody_requirements: vec![],
45+
};
46+
vec![KeyValueStoreOp::PutKeyValue(
47+
DBColumn::CustodyContext,
48+
CUSTODY_DB_KEY.as_slice().to_vec(),
49+
PersistedCustody(custody_context_v2).as_store_bytes(),
50+
)]
51+
}
52+
_ => {
53+
vec![]
54+
}
55+
}
56+
} else {
57+
// Delete it from db if PeerDAS hasn't been scheduled
58+
vec![KeyValueStoreOp::DeleteKey(
59+
DBColumn::CustodyContext,
60+
CUSTODY_DB_KEY.as_slice().to_vec(),
61+
)]
62+
};
63+
64+
Ok(ops)
65+
}
66+
67+
pub fn downgrade_from_v26<T: BeaconChainTypes>(
68+
db: Arc<HotColdDB<T::EthSpec, T::HotStore, T::ColdStore>>,
69+
) -> Result<Vec<KeyValueStoreOp>, Error> {
70+
let res = db.get_item::<PersistedCustody>(&CUSTODY_DB_KEY);
71+
let ops = match res {
72+
Ok(Some(PersistedCustody(ssz_v26))) => {
73+
info!("Migrating `CustodyContext` back from v26 schema");
74+
let custody_context_v24 = CustodyContextSszV24 {
75+
validator_custody_at_head: ssz_v26.validator_custody_at_head,
76+
persisted_is_supernode: ssz_v26.persisted_is_supernode,
77+
};
78+
vec![KeyValueStoreOp::PutKeyValue(
79+
DBColumn::CustodyContext,
80+
CUSTODY_DB_KEY.as_slice().to_vec(),
81+
PersistedCustodyV24(custody_context_v24).as_store_bytes(),
82+
)]
83+
}
84+
_ => {
85+
// no op if it's not on the db, as previous versions gracefully handle data missing from disk.
86+
vec![]
87+
}
88+
};
89+
90+
Ok(ops)
91+
}

beacon_node/beacon_chain/src/validator_custody.rs

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,13 @@ impl CustodyContext {
163163
validator_custody_count: AtomicU64::new(ssz_context.validator_custody_at_head),
164164
current_is_supernode: is_supernode,
165165
persisted_is_supernode: ssz_context.persisted_is_supernode,
166-
validator_registrations: Default::default(),
166+
validator_registrations: RwLock::new(ValidatorRegistrations {
167+
validators: Default::default(),
168+
epoch_validator_custody_requirements: ssz_context
169+
.epoch_validator_custody_requirements
170+
.into_iter()
171+
.collect(),
172+
}),
167173
}
168174
}
169175

@@ -263,15 +269,23 @@ pub struct CustodyCountChanged {
263269
/// The custody information that gets persisted across runs.
264270
#[derive(Debug, Encode, Decode, Clone)]
265271
pub struct CustodyContextSsz {
266-
validator_custody_at_head: u64,
267-
persisted_is_supernode: bool,
272+
pub validator_custody_at_head: u64,
273+
pub persisted_is_supernode: bool,
274+
pub epoch_validator_custody_requirements: Vec<(Epoch, u64)>,
268275
}
269276

270277
impl From<&CustodyContext> for CustodyContextSsz {
271278
fn from(context: &CustodyContext) -> Self {
272279
CustodyContextSsz {
273280
validator_custody_at_head: context.validator_custody_count.load(Ordering::Relaxed),
274281
persisted_is_supernode: context.persisted_is_supernode,
282+
epoch_validator_custody_requirements: context
283+
.validator_registrations
284+
.read()
285+
.epoch_validator_custody_requirements
286+
.iter()
287+
.map(|(epoch, count)| (*epoch, *count))
288+
.collect(),
275289
}
276290
}
277291
}

beacon_node/beacon_chain/tests/schema_stability.rs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ async fn schema_stability() {
8888
check_db_columns();
8989
check_metadata_sizes(&store);
9090
check_op_pool(&store);
91-
check_custody_context(&store);
91+
check_custody_context(&store, &harness.spec);
9292
check_persisted_chain(&store);
9393

9494
// Not covered here:
@@ -134,12 +134,13 @@ fn check_op_pool(store: &Store<E>) {
134134
assert_eq!(op_pool.as_store_bytes().len(), 28);
135135
}
136136

137-
fn check_custody_context(store: &Store<E>) {
138-
let custody_context = store
139-
.get_item::<PersistedCustody>(&Hash256::ZERO)
140-
.unwrap()
141-
.unwrap();
142-
assert_eq!(custody_context.as_store_bytes().len(), 9);
137+
fn check_custody_context(store: &Store<E>, spec: &ChainSpec) {
138+
let custody_context_opt = store.get_item::<PersistedCustody>(&Hash256::ZERO).unwrap();
139+
if spec.is_peer_das_scheduled() {
140+
assert_eq!(custody_context_opt.unwrap().as_store_bytes().len(), 13);
141+
} else {
142+
assert!(custody_context_opt.is_none());
143+
}
143144
}
144145

145146
fn check_persisted_chain(store: &Store<E>) {

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::{Hash256, Slot};
66

7-
pub const CURRENT_SCHEMA_VERSION: SchemaVersion = SchemaVersion(25);
7+
pub const CURRENT_SCHEMA_VERSION: SchemaVersion = SchemaVersion(26);
88

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

0 commit comments

Comments
 (0)