1818//! A MMR storage implementations.
1919
2020use codec:: Encode ;
21+ use frame_support:: log;
22+ use mmr_lib:: helper;
23+ use sp_io:: offchain_index;
24+ use sp_std:: iter:: Peekable ;
2125#[ cfg( not( feature = "std" ) ) ]
22- use sp_std:: prelude:: Vec ;
26+ use sp_std:: prelude:: * ;
2327
2428use crate :: {
25- mmr:: { Node , NodeOf } ,
26- primitives, Config , Nodes , NumberOfLeaves , Pallet ,
29+ mmr:: { utils:: NodesUtils , Node , NodeOf } ,
30+ primitives:: { self , NodeIndex } ,
31+ Config , Nodes , NumberOfLeaves , Pallet ,
2732} ;
2833
2934/// A marker type for runtime-specific storage implementation.
3035///
3136/// Allows appending new items to the MMR and proof verification.
3237/// MMR nodes are appended to two different storages:
33- /// 1. We add nodes (leaves) hashes to the on-chain storge (see [crate::Nodes]).
38+ /// 1. We add nodes (leaves) hashes to the on-chain storage (see [crate::Nodes]).
3439/// 2. We add full leaves (and all inner nodes as well) into the `IndexingAPI` during block
3540/// processing, so the values end up in the Offchain DB if indexing is enabled.
3641pub struct RuntimeStorage ;
@@ -60,14 +65,14 @@ where
6065 I : ' static ,
6166 L : primitives:: FullLeaf + codec:: Decode ,
6267{
63- fn get_elem ( & self , pos : u64 ) -> mmr_lib:: Result < Option < NodeOf < T , I , L > > > {
68+ fn get_elem ( & self , pos : NodeIndex ) -> mmr_lib:: Result < Option < NodeOf < T , I , L > > > {
6469 let key = Pallet :: < T , I > :: offchain_key ( pos) ;
6570 // Retrieve the element from Off-chain DB.
6671 Ok ( sp_io:: offchain:: local_storage_get ( sp_core:: offchain:: StorageKind :: PERSISTENT , & key)
6772 . and_then ( |v| codec:: Decode :: decode ( & mut & * v) . ok ( ) ) )
6873 }
6974
70- fn append ( & mut self , _: u64 , _: Vec < NodeOf < T , I , L > > ) -> mmr_lib:: Result < ( ) > {
75+ fn append ( & mut self , _: NodeIndex , _: Vec < NodeOf < T , I , L > > ) -> mmr_lib:: Result < ( ) > {
7176 panic ! ( "MMR must not be altered in the off-chain context." )
7277 }
7378}
@@ -78,32 +83,90 @@ where
7883 I : ' static ,
7984 L : primitives:: FullLeaf ,
8085{
81- fn get_elem ( & self , pos : u64 ) -> mmr_lib:: Result < Option < NodeOf < T , I , L > > > {
86+ fn get_elem ( & self , pos : NodeIndex ) -> mmr_lib:: Result < Option < NodeOf < T , I , L > > > {
8287 Ok ( <Nodes < T , I > >:: get ( pos) . map ( Node :: Hash ) )
8388 }
8489
85- fn append ( & mut self , pos : u64 , elems : Vec < NodeOf < T , I , L > > ) -> mmr_lib:: Result < ( ) > {
86- let mut leaves = crate :: NumberOfLeaves :: < T , I > :: get ( ) ;
87- let mut size = crate :: mmr:: utils:: NodesUtils :: new ( leaves) . size ( ) ;
90+ fn append ( & mut self , pos : NodeIndex , elems : Vec < NodeOf < T , I , L > > ) -> mmr_lib:: Result < ( ) > {
91+ if elems. is_empty ( ) {
92+ return Ok ( ( ) )
93+ }
94+
95+ sp_std:: if_std! {
96+ log:: trace!( "elems: {:?}" , elems. iter( ) . map( |elem| elem. hash( ) ) . collect:: <Vec <_>>( ) ) ;
97+ }
98+
99+ let leaves = NumberOfLeaves :: < T , I > :: get ( ) ;
100+ let size = NodesUtils :: new ( leaves) . size ( ) ;
101+
88102 if pos != size {
89103 return Err ( mmr_lib:: Error :: InconsistentStore )
90104 }
91105
106+ let new_size = size + elems. len ( ) as NodeIndex ;
107+
108+ // A sorted (ascending) iterator over peak indices to prune and persist.
109+ let ( peaks_to_prune, mut peaks_to_store) = peaks_to_prune_and_store ( size, new_size) ;
110+
111+ // Now we are going to iterate over elements to insert
112+ // and keep track of the current `node_index` and `leaf_index`.
113+ let mut leaf_index = leaves;
114+ let mut node_index = size;
115+
92116 for elem in elems {
93- // on-chain we only store the hash (even if it's a leaf)
94- <Nodes < T , I > >:: insert ( size, elem. hash ( ) ) ;
95- // Indexing API is used to store the full leaf content.
96- let key = Pallet :: < T , I > :: offchain_key ( size) ;
97- elem. using_encoded ( |elem| sp_io:: offchain_index:: set ( & key, elem) ) ;
98- size += 1 ;
117+ // Indexing API is used to store the full node content (both leaf and inner).
118+ elem. using_encoded ( |elem| {
119+ offchain_index:: set ( & Pallet :: < T , I > :: offchain_key ( node_index) , elem)
120+ } ) ;
121+
122+ // On-chain we are going to only store new peaks.
123+ if peaks_to_store. next_if_eq ( & node_index) . is_some ( ) {
124+ <Nodes < T , I > >:: insert ( node_index, elem. hash ( ) ) ;
125+ }
99126
127+ // Increase the indices.
100128 if let Node :: Data ( ..) = elem {
101- leaves += 1 ;
129+ leaf_index += 1 ;
102130 }
131+ node_index += 1 ;
103132 }
104133
105- NumberOfLeaves :: < T , I > :: put ( leaves) ;
134+ // Update current number of leaves.
135+ NumberOfLeaves :: < T , I > :: put ( leaf_index) ;
136+
137+ // And remove all remaining items from `peaks_before` collection.
138+ for pos in peaks_to_prune {
139+ <Nodes < T , I > >:: remove ( pos) ;
140+ }
106141
107142 Ok ( ( ) )
108143 }
109144}
145+
146+ fn peaks_to_prune_and_store (
147+ old_size : NodeIndex ,
148+ new_size : NodeIndex ,
149+ ) -> ( impl Iterator < Item = NodeIndex > , Peekable < impl Iterator < Item = NodeIndex > > ) {
150+ // A sorted (ascending) collection of peak indices before and after insertion.
151+ // both collections may share a common prefix.
152+ let peaks_before = if old_size == 0 { vec ! [ ] } else { helper:: get_peaks ( old_size) } ;
153+ let peaks_after = helper:: get_peaks ( new_size) ;
154+ sp_std:: if_std! {
155+ log:: trace!( "peaks_before: {:?}" , peaks_before) ;
156+ log:: trace!( "peaks_after: {:?}" , peaks_after) ;
157+ }
158+ let mut peaks_before = peaks_before. into_iter ( ) . peekable ( ) ;
159+ let mut peaks_after = peaks_after. into_iter ( ) . peekable ( ) ;
160+
161+ // Consume a common prefix between `peaks_before` and `peaks_after`,
162+ // since that's something we will not be touching anyway.
163+ while peaks_before. peek ( ) == peaks_after. peek ( ) {
164+ peaks_before. next ( ) ;
165+ peaks_after. next ( ) ;
166+ }
167+
168+ // what's left in both collections is:
169+ // 1. Old peaks to remove from storage
170+ // 2. New peaks to persist in storage
171+ ( peaks_before, peaks_after)
172+ }
0 commit comments