@@ -20,18 +20,21 @@ use primitives::H256;
2020use super :: message:: ConsensusMessage ;
2121use super :: types:: { Height , Step , View } ;
2222use crate :: db;
23+ use crate :: db_version;
2324
2425const BACKUP_KEY : & [ u8 ] = b"tendermint-backup" ;
26+ const BACKUP_VERSION : u32 = 1 ;
2527
2628pub 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+
4356pub 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}
0 commit comments