Skip to content

Commit 4b542bd

Browse files
apollo_consensus: Add functionality to consensus state to support reverts (#10248)
1 parent 777185a commit 4b542bd

File tree

4 files changed

+86
-1
lines changed

4 files changed

+86
-1
lines changed

crates/apollo_consensus/src/storage.rs

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ pub trait HeightVotedStorageTrait: Debug + Send + Sync {
3131
/// Sets the last height on which the node voted.
3232
fn set_prev_voted_height(&mut self, height: BlockNumber)
3333
-> Result<(), HeightVotedStorageError>;
34+
/// If the last voted height is higher or equal to the height to revert to, set the last voted
35+
/// height to be just before the height to revert.
36+
fn revert_height(&mut self, height: BlockNumber) -> Result<(), HeightVotedStorageError>;
3437
}
3538

3639
struct HeightVotedStorage {
@@ -40,7 +43,7 @@ struct HeightVotedStorage {
4043
storage_writer: StorageWriter,
4144
}
4245

43-
/// Opens and returns the storage used for storing last voted height state.
46+
/// Returns a new concrete implementation of HeightVotedStorageTrait based on the configuration.
4447
pub fn get_voted_height_storage(config: StorageConfig) -> impl HeightVotedStorageTrait {
4548
let (storage_reader, storage_writer) = open_storage(config).expect("Failed to open storage");
4649
HeightVotedStorage { storage_reader, storage_writer }
@@ -86,6 +89,27 @@ impl HeightVotedStorageTrait for HeightVotedStorage {
8689
}
8790
}
8891
}
92+
93+
fn revert_height(&mut self, height: BlockNumber) -> Result<(), HeightVotedStorageError> {
94+
let mut txn = self.storage_writer.begin_rw_txn()?;
95+
let last_voted = txn.get_last_voted_marker()?;
96+
match last_voted {
97+
None => return Ok(()), // If none, nothing to revert.
98+
Some(last_voted) => {
99+
if last_voted.height >= height {
100+
if let Some(prev_height) = height.prev() {
101+
txn =
102+
txn.set_last_voted_marker(&LastVotedMarker { height: prev_height })?;
103+
} else {
104+
// We're reverting height 0, so clear the last voted height completely.
105+
txn = txn.clear_last_voted_marker()?;
106+
}
107+
}
108+
}
109+
}
110+
txn.commit()?;
111+
Ok(())
112+
}
89113
}
90114

91115
// We must implement Debug for HeightVotedStorageImpl to allow it being used as a member in structs

crates/apollo_consensus/src/storage_test.rs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,54 @@ fn write_last_height_return_error_when_previous_last_height_is_equal() {
6262
Err(HeightVotedStorageError::InconsistentStorageState { error_msg: _ })
6363
);
6464
}
65+
66+
#[test]
67+
fn revert_height_when_no_last_height_in_storage_does_nothing() {
68+
let mut storage = get_voted_height_storage(get_new_storage_config());
69+
assert!(storage.get_prev_voted_height().unwrap().is_none());
70+
storage.revert_height(BlockNumber(1)).unwrap();
71+
assert!(storage.get_prev_voted_height().unwrap().is_none());
72+
}
73+
74+
#[test]
75+
fn revert_height_when_last_height_in_storage_is_lower_than_height_to_revert_to_does_nothing() {
76+
const HEIGHT_TO_REVERT_TO: BlockNumber = BlockNumber(2);
77+
// Storage has a lower height than what we revert (so should be a no-op)
78+
let last_height_in_storage = HEIGHT_TO_REVERT_TO.prev().unwrap();
79+
80+
let mut storage = get_voted_height_storage(get_new_storage_config());
81+
storage.set_prev_voted_height(last_height_in_storage).unwrap();
82+
storage.revert_height(HEIGHT_TO_REVERT_TO).unwrap();
83+
assert_eq!(storage.get_prev_voted_height().unwrap(), Some(last_height_in_storage));
84+
}
85+
86+
#[test]
87+
fn revert_height_when_last_height_in_storage_is_higher_than_revert_height_reverts_the_given_height()
88+
{
89+
const HEIGH_TO_REVERT_TO: BlockNumber = BlockNumber(2);
90+
// Storage has a higher height than what we're reverting.
91+
let last_height_in_storage = HEIGH_TO_REVERT_TO.unchecked_next().unchecked_next();
92+
93+
let mut storage = get_voted_height_storage(get_new_storage_config());
94+
storage.set_prev_voted_height(last_height_in_storage).unwrap();
95+
storage.revert_height(HEIGH_TO_REVERT_TO).unwrap();
96+
assert_eq!(storage.get_prev_voted_height().unwrap(), Some(HEIGH_TO_REVERT_TO.prev().unwrap()));
97+
}
98+
99+
#[test]
100+
fn revert_height_when_last_height_in_storage_is_equal_to_revert_height_reverts_the_given_height() {
101+
const HEIGHT_TO_REVERT_TO: BlockNumber = BlockNumber(2);
102+
103+
let mut storage = get_voted_height_storage(get_new_storage_config());
104+
storage.set_prev_voted_height(HEIGHT_TO_REVERT_TO).unwrap();
105+
storage.revert_height(HEIGHT_TO_REVERT_TO).unwrap();
106+
assert_eq!(storage.get_prev_voted_height().unwrap(), Some(HEIGHT_TO_REVERT_TO.prev().unwrap()));
107+
}
108+
109+
#[test]
110+
fn revert_height_to_0_clears_the_last_height_in_storage() {
111+
let mut storage = get_voted_height_storage(get_new_storage_config());
112+
storage.set_prev_voted_height(BlockNumber(5)).unwrap();
113+
storage.revert_height(BlockNumber(0)).unwrap();
114+
assert!(storage.get_prev_voted_height().unwrap().is_none());
115+
}

crates/apollo_consensus/src/test_utils.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,4 +123,7 @@ impl HeightVotedStorageTrait for NoOpHeightVotedStorage {
123123
) -> Result<(), HeightVotedStorageError> {
124124
Ok(())
125125
}
126+
fn revert_height(&mut self, _height: BlockNumber) -> Result<(), HeightVotedStorageError> {
127+
Ok(())
128+
}
126129
}

crates/apollo_storage/src/consensus.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ where
6161
Self: Sized,
6262
{
6363
fn set_last_voted_marker(self, last_voted_marker: &LastVotedMarker) -> StorageResult<Self>;
64+
fn clear_last_voted_marker(self) -> StorageResult<Self>;
6465
}
6566

6667
impl<Mode: TransactionKind> ConsensusStorageReader for StorageTxn<'_, Mode> {
@@ -76,4 +77,10 @@ impl ConsensusStorageWriter for StorageTxn<'_, RW> {
7677
table.upsert(&self.txn, &(), last_voted_marker)?;
7778
Ok(self)
7879
}
80+
81+
fn clear_last_voted_marker(self) -> StorageResult<Self> {
82+
let table = self.open_table(&self.tables.last_voted_marker)?;
83+
table.delete(&self.txn, &())?;
84+
Ok(self)
85+
}
7986
}

0 commit comments

Comments
 (0)