@@ -20,18 +20,21 @@ use primitives::H256;
20
20
use super :: message:: ConsensusMessage ;
21
21
use super :: types:: { Height , Step , View } ;
22
22
use crate :: db;
23
+ use crate :: db_version;
23
24
24
25
const BACKUP_KEY : & [ u8 ] = b"tendermint-backup" ;
26
+ const BACKUP_VERSION : u32 = 1 ;
25
27
26
28
pub struct BackupView < ' a > {
27
29
pub height : & ' a Height ,
28
30
pub view : & ' a View ,
29
31
pub step : & ' a Step ,
30
32
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 > ,
32
35
}
33
36
34
- pub struct BackupData {
37
+ pub struct BackupDataV0 {
35
38
pub height : Height ,
36
39
pub view : View ,
37
40
pub step : Step ,
@@ -40,25 +43,111 @@ pub struct BackupData {
40
43
pub last_confirmed_view : View ,
41
44
}
42
45
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
+
43
56
pub fn backup ( db : & dyn KeyValueDB , backup_data : BackupView ) {
44
57
let BackupView {
45
58
height,
46
59
view,
47
60
step,
48
61
votes,
49
- last_confirmed_view,
62
+ finalized_view_of_previous_block,
63
+ finalized_view_of_current_block,
50
64
} = backup_data;
51
65
let mut s = rlp:: RlpStream :: new ( ) ;
52
- s. begin_list ( 5 ) ;
66
+ s. begin_list ( 6 ) ;
53
67
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) ;
55
70
56
71
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 ) ;
57
77
batch. put ( db:: COL_EXTRA , BACKUP_KEY , & s. drain ( ) . into_vec ( ) ) ;
58
78
db. write ( batch) . expect ( "Low level database error. Some issue with disk?" ) ;
59
79
}
60
80
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 > {
62
151
let value = db. get ( db:: COL_EXTRA , BACKUP_KEY ) . expect ( "Low level database error. Some issue with disk?" ) ;
63
152
let ( height, view, step, votes, last_confirmed_view) = value. map ( |bytes| {
64
153
let bytes = bytes. into_vec ( ) ;
@@ -68,7 +157,7 @@ pub fn restore(db: &dyn KeyValueDB) -> Option<BackupData> {
68
157
69
158
let proposal = find_proposal ( & votes, height, view) ;
70
159
71
- Some ( BackupData {
160
+ Some ( BackupDataV0 {
72
161
height,
73
162
view,
74
163
step,
@@ -78,14 +167,29 @@ pub fn restore(db: &dyn KeyValueDB) -> Option<BackupData> {
78
167
} )
79
168
}
80
169
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
+ } )
91
195
}
0 commit comments