Skip to content

Commit e96dc62

Browse files
committed
Save finalized_view_of_previous_block and finalized_view_of_current_block
Tendermint worker's last_confirmed_view is used for two purposes. One is the previous height's finalized view, and the other one is the current height's finalized view. This commit is part of splitting the last_confirmed_view variable.
1 parent e146ed6 commit e96dc62

File tree

2 files changed

+129
-19
lines changed

2 files changed

+129
-19
lines changed

core/src/consensus/tendermint/backup.rs

Lines changed: 121 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -20,18 +20,21 @@ use primitives::H256;
2020
use super::message::ConsensusMessage;
2121
use super::types::{Height, Step, View};
2222
use crate::db;
23+
use crate::db_version;
2324

2425
const BACKUP_KEY: &[u8] = b"tendermint-backup";
26+
const BACKUP_VERSION: u32 = 1;
2527

2628
pub struct BackupView<'a> {
2729
pub height: &'a Height,
2830
pub view: &'a View,
2931
pub step: &'a Step,
3032
pub votes: &'a [ConsensusMessage],
31-
pub last_confirmed_view: &'a View,
33+
pub finalized_view_of_previous_block: &'a View,
34+
pub finalized_view_of_current_block: &'a Option<View>,
3235
}
3336

34-
pub struct BackupData {
37+
pub struct BackupDataV0 {
3538
pub height: Height,
3639
pub view: View,
3740
pub step: Step,
@@ -40,25 +43,111 @@ pub struct BackupData {
4043
pub last_confirmed_view: View,
4144
}
4245

46+
pub struct BackupDataV1 {
47+
pub height: Height,
48+
pub view: View,
49+
pub step: Step,
50+
pub votes: Vec<ConsensusMessage>,
51+
pub proposal: Option<H256>,
52+
pub finalized_view_of_previous_block: View,
53+
pub finalized_view_of_current_block: Option<View>,
54+
}
55+
4356
pub fn backup(db: &dyn KeyValueDB, backup_data: BackupView) {
4457
let BackupView {
4558
height,
4659
view,
4760
step,
4861
votes,
49-
last_confirmed_view,
62+
finalized_view_of_previous_block,
63+
finalized_view_of_current_block,
5064
} = backup_data;
5165
let mut s = rlp::RlpStream::new();
52-
s.begin_list(5);
66+
s.begin_list(6);
5367
s.append(height).append(view).append(step).append_list(votes);
54-
s.append(last_confirmed_view);
68+
s.append(finalized_view_of_previous_block);
69+
s.append(finalized_view_of_current_block);
5570

5671
let mut batch = DBTransaction::new();
72+
debug_assert!(
73+
db_version::VERSION_KEY_TENDERMINT_BACKUP.ends_with(BACKUP_KEY),
74+
"version key should end with the backup key"
75+
);
76+
db_version::set_version(&mut batch, db_version::VERSION_KEY_TENDERMINT_BACKUP, BACKUP_VERSION);
5777
batch.put(db::COL_EXTRA, BACKUP_KEY, &s.drain().into_vec());
5878
db.write(batch).expect("Low level database error. Some issue with disk?");
5979
}
6080

61-
pub fn restore(db: &dyn KeyValueDB) -> Option<BackupData> {
81+
pub fn restore(db: &dyn KeyValueDB) -> Option<BackupDataV1> {
82+
let version = db_version::get_version(db, db_version::VERSION_KEY_TENDERMINT_BACKUP);
83+
if version < BACKUP_VERSION {
84+
migrate(db);
85+
}
86+
load_v1(db)
87+
}
88+
89+
fn find_proposal(votes: &[ConsensusMessage], height: Height, view: View) -> Option<H256> {
90+
votes
91+
.iter()
92+
.rev()
93+
.map(|vote| &vote.on)
94+
.find(|vote_on| {
95+
vote_on.step.step == Step::Propose && vote_on.step.view == view && vote_on.step.height == height
96+
})
97+
.map(|vote_on| vote_on.block_hash)
98+
.unwrap_or(None)
99+
}
100+
101+
fn migrate(db: &dyn KeyValueDB) {
102+
let version = db_version::get_version(db, db_version::VERSION_KEY_TENDERMINT_BACKUP);
103+
assert!(
104+
version < BACKUP_VERSION,
105+
"migrate function should be called when the saved version is less than BACKUP_VERSION"
106+
);
107+
108+
match version {
109+
0 => {
110+
migrate_from_0_to_1(db);
111+
}
112+
_ => panic!("Invalid migration version {}", version),
113+
}
114+
}
115+
116+
fn migrate_from_0_to_1(db: &dyn KeyValueDB) {
117+
let v0 = if let Some(v0) = load_v0(db) {
118+
v0
119+
} else {
120+
return
121+
};
122+
let step = v0.step;
123+
let v1 = BackupDataV1 {
124+
height: v0.height,
125+
view: v0.view,
126+
step: v0.step,
127+
votes: v0.votes,
128+
proposal: v0.proposal,
129+
// This is not a correct behavior if step == Step::Commit.
130+
// In Commit state, the Tendermint module overwrote the last_confirmed_view to finalized_view_of_current_block.
131+
// So we can't restore finalized_view_of_previous block.
132+
// The code below maintain older code's behavior:
133+
finalized_view_of_previous_block: v0.last_confirmed_view,
134+
finalized_view_of_current_block: if step == Step::Commit {
135+
Some(v0.last_confirmed_view)
136+
} else {
137+
None
138+
},
139+
};
140+
backup(db, BackupView {
141+
height: &v1.height,
142+
view: &v1.view,
143+
step: &v1.step,
144+
votes: &v1.votes,
145+
finalized_view_of_previous_block: &v1.finalized_view_of_previous_block,
146+
finalized_view_of_current_block: &v1.finalized_view_of_current_block,
147+
})
148+
}
149+
150+
fn load_v0(db: &dyn KeyValueDB) -> Option<BackupDataV0> {
62151
let value = db.get(db::COL_EXTRA, BACKUP_KEY).expect("Low level database error. Some issue with disk?");
63152
let (height, view, step, votes, last_confirmed_view) = value.map(|bytes| {
64153
let bytes = bytes.into_vec();
@@ -68,7 +157,7 @@ pub fn restore(db: &dyn KeyValueDB) -> Option<BackupData> {
68157

69158
let proposal = find_proposal(&votes, height, view);
70159

71-
Some(BackupData {
160+
Some(BackupDataV0 {
72161
height,
73162
view,
74163
step,
@@ -78,14 +167,29 @@ pub fn restore(db: &dyn KeyValueDB) -> Option<BackupData> {
78167
})
79168
}
80169

81-
fn find_proposal(votes: &[ConsensusMessage], height: Height, view: View) -> Option<H256> {
82-
votes
83-
.iter()
84-
.rev()
85-
.map(|vote| &vote.on)
86-
.find(|vote_on| {
87-
vote_on.step.step == Step::Propose && vote_on.step.view == view && vote_on.step.height == height
88-
})
89-
.map(|vote_on| vote_on.block_hash)
90-
.unwrap_or(None)
170+
fn load_v1(db: &dyn KeyValueDB) -> Option<BackupDataV1> {
171+
#[derive(RlpDecodable)]
172+
struct Backup {
173+
height: Height,
174+
view: View,
175+
step: Step,
176+
votes: Vec<ConsensusMessage>,
177+
finalized_view_of_previous_block: View,
178+
finalized_view_of_current_block: Option<View>,
179+
}
180+
181+
let value = db.get(db::COL_EXTRA, BACKUP_KEY).expect("Low level database error. Some issue with disk?")?;
182+
let backup: Backup = rlp::decode(&value);
183+
184+
let proposal = find_proposal(&backup.votes, backup.height, backup.view);
185+
186+
Some(BackupDataV1 {
187+
height: backup.height,
188+
view: backup.view,
189+
step: backup.step,
190+
votes: backup.votes,
191+
proposal,
192+
finalized_view_of_previous_block: backup.finalized_view_of_previous_block,
193+
finalized_view_of_current_block: backup.finalized_view_of_current_block,
194+
})
91195
}

core/src/consensus/tendermint/worker.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -981,7 +981,8 @@ impl Worker {
981981
view: &self.view,
982982
step: &self.step.to_step(),
983983
votes: &self.votes.get_all(),
984-
last_confirmed_view: &self.last_confirmed_view,
984+
finalized_view_of_previous_block: &self.last_confirmed_view,
985+
finalized_view_of_current_block: &Some(self.last_confirmed_view),
985986
});
986987
}
987988

@@ -1001,7 +1002,12 @@ impl Worker {
10011002
self.step = backup_step;
10021003
self.height = backup.height;
10031004
self.view = backup.view;
1004-
self.last_confirmed_view = backup.last_confirmed_view;
1005+
if backup.step == Step::Commit {
1006+
self.last_confirmed_view =
1007+
backup.finalized_view_of_current_block.expect("In commit step the finalized view exist")
1008+
} else {
1009+
self.last_confirmed_view = backup.finalized_view_of_previous_block;
1010+
}
10051011
if let Some(proposal) = backup.proposal {
10061012
if client.block(&BlockId::Hash(proposal)).is_some() {
10071013
self.proposal = Proposal::ProposalImported(proposal);

0 commit comments

Comments
 (0)