@@ -30,6 +30,7 @@ use log::trace;
3030
3131const NON_CANONICAL_JOURNAL : & [ u8 ] = b"noncanonical_journal" ;
3232const LAST_CANONICAL : & [ u8 ] = b"last_canonical" ;
33+ const MAX_BLOCKS_PER_LEVEL : u64 = 32 ;
3334
3435/// See module documentation.
3536#[ derive( parity_util_mem_derive:: MallocSizeOf ) ]
@@ -162,28 +163,30 @@ impl<BlockHash: Hash, Key: Hash> NonCanonicalOverlay<BlockHash, Key> {
162163 let mut total: u64 = 0 ;
163164 block += 1 ;
164165 loop {
165- let mut index: u64 = 0 ;
166166 let mut level = Vec :: new ( ) ;
167- loop {
167+ for index in 0 .. MAX_BLOCKS_PER_LEVEL {
168168 let journal_key = to_journal_key ( block, index) ;
169- match db. get_meta ( & journal_key) . map_err ( |e| Error :: Db ( e) ) ? {
170- Some ( record) => {
171- let record: JournalRecord < BlockHash , Key > = Decode :: decode ( & mut record. as_slice ( ) ) ?;
172- let inserted = record. inserted . iter ( ) . map ( |( k, _) | k. clone ( ) ) . collect ( ) ;
173- let overlay = BlockOverlay {
174- hash : record. hash . clone ( ) ,
175- journal_key,
176- inserted : inserted,
177- deleted : record. deleted ,
178- } ;
179- insert_values ( & mut values, record. inserted ) ;
180- trace ! ( target: "state-db" , "Uncanonicalized journal entry {}.{} ({} inserted, {} deleted)" , block, index, overlay. inserted. len( ) , overlay. deleted. len( ) ) ;
181- level. push ( overlay) ;
182- parents. insert ( record. hash , record. parent_hash ) ;
183- index += 1 ;
184- total += 1 ;
185- } ,
186- None => break ,
169+ if let Some ( record) = db. get_meta ( & journal_key) . map_err ( |e| Error :: Db ( e) ) ? {
170+ let record: JournalRecord < BlockHash , Key > = Decode :: decode ( & mut record. as_slice ( ) ) ?;
171+ let inserted = record. inserted . iter ( ) . map ( |( k, _) | k. clone ( ) ) . collect ( ) ;
172+ let overlay = BlockOverlay {
173+ hash : record. hash . clone ( ) ,
174+ journal_key,
175+ inserted : inserted,
176+ deleted : record. deleted ,
177+ } ;
178+ insert_values ( & mut values, record. inserted ) ;
179+ trace ! (
180+ target: "state-db" ,
181+ "Uncanonicalized journal entry {}.{} ({} inserted, {} deleted)" ,
182+ block,
183+ index,
184+ overlay. inserted. len( ) ,
185+ overlay. deleted. len( )
186+ ) ;
187+ level. push ( overlay) ;
188+ parents. insert ( record. hash , record. parent_hash ) ;
189+ total += 1 ;
187190 }
188191 }
189192 if level. is_empty ( ) {
@@ -241,6 +244,10 @@ impl<BlockHash: Hash, Key: Hash> NonCanonicalOverlay<BlockHash, Key> {
241244 . expect ( "number is [front_block_number .. front_block_number + levels.len()) is asserted in precondition; qed" )
242245 } ;
243246
247+ if level. len ( ) >= MAX_BLOCKS_PER_LEVEL as usize {
248+ return Err ( Error :: TooManySiblingBlocks ) ;
249+ }
250+
244251 let index = level. len ( ) as u64 ;
245252 let journal_key = to_journal_key ( number, index) ;
246253
@@ -513,7 +520,7 @@ mod tests {
513520 use std:: io;
514521 use sp_core:: H256 ;
515522 use super :: { NonCanonicalOverlay , to_journal_key} ;
516- use crate :: { ChangeSet , CommitSet } ;
523+ use crate :: { ChangeSet , CommitSet , MetaDb } ;
517524 use crate :: test:: { make_db, make_changeset} ;
518525
519526 fn contains ( overlay : & NonCanonicalOverlay < H256 , H256 > , key : u64 ) -> bool {
@@ -716,7 +723,6 @@ mod tests {
716723
717724 #[ test]
718725 fn complex_tree ( ) {
719- use crate :: MetaDb ;
720726 let mut db = make_db ( & [ ] ) ;
721727
722728 // - 1 - 1_1 - 1_1_1
@@ -958,4 +964,42 @@ mod tests {
958964 assert ! ( !contains( & overlay, 1 ) ) ;
959965 assert ! ( overlay. pinned. is_empty( ) ) ;
960966 }
967+
968+ #[ test]
969+ fn restore_from_journal_after_canonicalize_no_first ( ) {
970+ // This test discards a branch that is journaled under a non-zero index on level 1,
971+ // making sure all journals are loaded for each level even if some of them are missing.
972+ let root = H256 :: random ( ) ;
973+ let h1 = H256 :: random ( ) ;
974+ let h2 = H256 :: random ( ) ;
975+ let h11 = H256 :: random ( ) ;
976+ let h21 = H256 :: random ( ) ;
977+ let mut db = make_db ( & [ ] ) ;
978+ let mut overlay = NonCanonicalOverlay :: < H256 , H256 > :: new ( & db) . unwrap ( ) ;
979+ db. commit ( & overlay. insert :: < io:: Error > ( & root, 10 , & H256 :: default ( ) , make_changeset ( & [ ] , & [ ] ) ) . unwrap ( ) ) ;
980+ db. commit ( & overlay. insert :: < io:: Error > ( & h1, 11 , & root, make_changeset ( & [ 1 ] , & [ ] ) ) . unwrap ( ) ) ;
981+ db. commit ( & overlay. insert :: < io:: Error > ( & h2, 11 , & root, make_changeset ( & [ 2 ] , & [ ] ) ) . unwrap ( ) ) ;
982+ db. commit ( & overlay. insert :: < io:: Error > ( & h11, 12 , & h1, make_changeset ( & [ 11 ] , & [ ] ) ) . unwrap ( ) ) ;
983+ db. commit ( & overlay. insert :: < io:: Error > ( & h21, 12 , & h2, make_changeset ( & [ 21 ] , & [ ] ) ) . unwrap ( ) ) ;
984+ let mut commit = CommitSet :: default ( ) ;
985+ overlay. canonicalize :: < io:: Error > ( & root, & mut commit) . unwrap ( ) ;
986+ overlay. canonicalize :: < io:: Error > ( & h2, & mut commit) . unwrap ( ) ; // h11 should stay in the DB
987+ db. commit ( & commit) ;
988+ overlay. apply_pending ( ) ;
989+ assert_eq ! ( overlay. levels. len( ) , 1 ) ;
990+ assert ! ( contains( & overlay, 21 ) ) ;
991+ assert ! ( !contains( & overlay, 11 ) ) ;
992+ assert ! ( db. get_meta( & to_journal_key( 12 , 1 ) ) . unwrap( ) . is_some( ) ) ;
993+ assert ! ( db. get_meta( & to_journal_key( 12 , 0 ) ) . unwrap( ) . is_none( ) ) ;
994+
995+ // Restore into a new overlay and check that journaled value exists.
996+ let mut overlay = NonCanonicalOverlay :: < H256 , H256 > :: new ( & db) . unwrap ( ) ;
997+ assert ! ( contains( & overlay, 21 ) ) ;
998+
999+ let mut commit = CommitSet :: default ( ) ;
1000+ overlay. canonicalize :: < io:: Error > ( & h21, & mut commit) . unwrap ( ) ; // h11 should stay in the DB
1001+ db. commit ( & commit) ;
1002+ overlay. apply_pending ( ) ;
1003+ assert ! ( !contains( & overlay, 21 ) ) ;
1004+ }
9611005}
0 commit comments