diff --git a/beacon_node/store/src/config.rs b/beacon_node/store/src/config.rs index 5d257ead9d..bc5adc066b 100644 --- a/beacon_node/store/src/config.rs +++ b/beacon_node/store/src/config.rs @@ -30,8 +30,10 @@ pub struct StoreConfig { pub compact_on_prune: bool, /// Whether to prune payloads on initialization and finalization. pub prune_payloads: bool, - /// Whether to store finalized blocks in the freezer database. - pub separate_blocks: bool, + /// Whether to store finalized blocks compressed and linearised in the freezer database. + pub linear_blocks: bool, + /// Whether to store finalized states compressed and linearised in the freezer database. + pub linear_restore_points: bool, } /// Variant of `StoreConfig` that gets written to disk. Contains immutable configuration params. @@ -39,7 +41,8 @@ pub struct StoreConfig { pub struct OnDiskStoreConfig { pub slots_per_restore_point: u64, // FIXME(sproul): schema migration - pub separate_blocks: bool, + pub linear_blocks: bool, + pub linear_restore_points: bool, } #[derive(Debug, Clone)] @@ -60,7 +63,8 @@ impl Default for StoreConfig { compact_on_init: false, compact_on_prune: true, prune_payloads: true, - separate_blocks: true, + linear_blocks: true, + linear_restore_points: true, } } } @@ -69,7 +73,8 @@ impl StoreConfig { pub fn as_disk_config(&self) -> OnDiskStoreConfig { OnDiskStoreConfig { slots_per_restore_point: self.slots_per_restore_point, - separate_blocks: self.separate_blocks, + linear_blocks: self.linear_blocks, + linear_restore_points: self.linear_restore_points, } } diff --git a/beacon_node/store/src/hot_cold_store.rs b/beacon_node/store/src/hot_cold_store.rs index 9545eec373..b2d0d0cca6 100644 --- a/beacon_node/store/src/hot_cold_store.rs +++ b/beacon_node/store/src/hot_cold_store.rs @@ -99,6 +99,7 @@ pub enum HotColdDBError { }, MissingStateToFreeze(Hash256), MissingRestorePointHash(u64), + MissingRestorePointState(Slot), MissingRestorePoint(Hash256), MissingColdStateSummary(Hash256), MissingHotStateSummary(Hash256), @@ -1221,7 +1222,7 @@ impl, Cold: ItemStore> HotColdDB // 1. Convert to PartialBeaconState and store that in the DB. let partial_state = PartialBeaconState::from_state_forgetful(state); - let op = partial_state.as_kv_store_op(*state_root, &self.config)?; + let op = partial_state.as_kv_store_op(&self.config)?; ops.push(op); // 2. Store updated vector entries. @@ -1232,8 +1233,11 @@ impl, Cold: ItemStore> HotColdDB store_updated_vector(RandaoMixes, db, state, &self.spec, ops)?; // 3. Store restore point. + // FIXME(sproul): backwards compat + /* let restore_point_index = state.slot().as_u64() / self.config.slots_per_restore_point; self.store_restore_point_hash(restore_point_index, *state_root, ops)?; + */ Ok(()) } @@ -1259,8 +1263,7 @@ impl, Cold: ItemStore> HotColdDB if slot <= lower_limit || slot >= upper_limit { if slot % self.config.slots_per_restore_point == 0 { - let restore_point_idx = slot.as_u64() / self.config.slots_per_restore_point; - self.load_restore_point_by_index(restore_point_idx) + self.load_restore_point(slot) } else { self.load_cold_intermediate_state(slot) } @@ -1270,12 +1273,15 @@ impl, Cold: ItemStore> HotColdDB } } - /// Load a restore point state by its `state_root`. - fn load_restore_point(&self, state_root: &Hash256) -> Result, Error> { + /// Load a restore point state by its `slot`. + fn load_restore_point(&self, slot: Slot) -> Result, Error> { let bytes = self .cold_db - .get_bytes(DBColumn::BeaconState.into(), state_root.as_bytes())? - .ok_or(HotColdDBError::MissingRestorePoint(*state_root))?; + .get_bytes( + DBColumn::BeaconRestorePointState.into(), + &slot.as_u64().to_be_bytes(), + )? + .ok_or(HotColdDBError::MissingRestorePointState(slot))?; let mut ssz_bytes = Vec::with_capacity(self.config.estimate_decompressed_size(bytes.len())); let mut decoder = Decoder::new(&*bytes).map_err(Error::Compression)?; @@ -1298,26 +1304,29 @@ impl, Cold: ItemStore> HotColdDB partial_state.try_into_full_state(immutable_validators) } + /* FIXME(sproul): backwards compat /// Load a restore point state by its `restore_point_index`. - fn load_restore_point_by_index( + fn load_legacy_restore_point_by_index( &self, restore_point_index: u64, ) -> Result, Error> { let state_root = self.load_restore_point_hash(restore_point_index)?; self.load_restore_point(&state_root) } + */ /// Load a frozen state that lies between restore points. fn load_cold_intermediate_state(&self, slot: Slot) -> Result, Error> { // 1. Load the restore points either side of the intermediate state. - let low_restore_point_idx = slot.as_u64() / self.config.slots_per_restore_point; - let high_restore_point_idx = low_restore_point_idx + 1; + let sprp = self.config.slots_per_restore_point; + let low_restore_point_slot = slot / sprp * sprp; + let high_restore_point_slot = low_restore_point_slot + sprp; // Acquire the read lock, so that the split can't change while this is happening. let split = self.split.read_recursive(); - let low_restore_point = self.load_restore_point_by_index(low_restore_point_idx)?; - let high_restore_point = self.get_restore_point(high_restore_point_idx, &split)?; + let low_restore_point = self.load_restore_point(low_restore_point_slot)?; + let high_restore_point = self.get_restore_point(high_restore_point_slot, &split)?; // 2. Load the blocks from the high restore point back to the low restore point. let blocks = self.load_blocks_to_replay( @@ -1342,10 +1351,10 @@ impl, Cold: ItemStore> HotColdDB /// Get the restore point with the given index, or if it is out of bounds, the split state. pub(crate) fn get_restore_point( &self, - restore_point_idx: u64, + slot: Slot, split: &Split, ) -> Result, Error> { - if restore_point_idx * self.config.slots_per_restore_point >= split.slot.as_u64() { + if slot >= split.slot.as_u64() { self.get_state(&split.state_root, Some(split.slot))? .ok_or(HotColdDBError::MissingSplitState( split.state_root, @@ -1353,7 +1362,7 @@ impl, Cold: ItemStore> HotColdDB )) .map_err(Into::into) } else { - self.load_restore_point_by_index(restore_point_idx) + self.load_restore_point(slot) } } diff --git a/beacon_node/store/src/lib.rs b/beacon_node/store/src/lib.rs index 7003d7c449..40f2c9311a 100644 --- a/beacon_node/store/src/lib.rs +++ b/beacon_node/store/src/lib.rs @@ -234,9 +234,12 @@ pub enum DBColumn { ForkChoice, #[strum(serialize = "pkc")] PubkeyCache, - /// For the table mapping restore point numbers to state roots. + /// For the legacy table mapping restore point numbers to state roots. #[strum(serialize = "brp")] BeaconRestorePoint, + /// For the new table mapping restore point slots to compressed beacon states. + #[strum(serialize = "rps")] + BeaconRestorePointState, #[strum(serialize = "bbr")] BeaconBlockRoots, #[strum(serialize = "bsr")] @@ -291,7 +294,8 @@ impl DBColumn { | Self::BeaconStateRoots | Self::BeaconHistoricalRoots | Self::BeaconRandaoMixes - | Self::BeaconBlockFrozen => 8, + | Self::BeaconBlockFrozen + | Self::BeaconRestorePointState => 8, } } } diff --git a/beacon_node/store/src/partial_beacon_state.rs b/beacon_node/store/src/partial_beacon_state.rs index 6f423b2905..9b26a385d6 100644 --- a/beacon_node/store/src/partial_beacon_state.rs +++ b/beacon_node/store/src/partial_beacon_state.rs @@ -21,6 +21,7 @@ use zstd::Encoder; )] #[derive(Debug, PartialEq, Clone, Encode)] #[ssz(enum_behaviour = "transparent")] +// FIXME(sproul): schema migration pub struct PartialBeaconState where T: EthSpec, @@ -209,12 +210,11 @@ impl PartialBeaconState { } /// Prepare the partial state for storage in the KV database. - pub fn as_kv_store_op( - &self, - state_root: Hash256, - config: &StoreConfig, - ) -> Result { - let db_key = get_key_for_col(DBColumn::BeaconState.into(), state_root.as_bytes()); + pub fn as_kv_store_op(&self, config: &StoreConfig) -> Result { + let db_key = get_key_for_col( + DBColumn::BeaconRestorePointState.into(), + &self.slot().as_u64().to_be_bytes(), + ); let ssz_bytes = self.as_ssz_bytes(); diff --git a/beacon_node/store/src/reconstruct.rs b/beacon_node/store/src/reconstruct.rs index d2cac0e4d3..f9ae8eb929 100644 --- a/beacon_node/store/src/reconstruct.rs +++ b/beacon_node/store/src/reconstruct.rs @@ -42,10 +42,7 @@ where // Iterate blocks from the state lower limit to the upper limit. let lower_limit_slot = anchor.state_lower_limit; let split = self.get_split_info(); - let upper_limit_state = self.get_restore_point( - anchor.state_upper_limit.as_u64() / slots_per_restore_point, - &split, - )?; + let upper_limit_state = self.get_restore_point(anchor.state_upper_limit, &split)?; let upper_limit_slot = upper_limit_state.slot(); // Use a dummy root, as we never read the block for the upper limit state.