@@ -26,7 +26,7 @@ use sp_std::collections::btree_map::{BTreeMap, Entry};
2626use sp_std:: prelude:: * ;
2727use sp_io:: hashing:: blake2_256;
2828use sp_runtime:: traits:: { Bounded , Zero } ;
29- use frame_support:: traits:: { Currency , Get , Imbalance , SignedImbalance } ;
29+ use frame_support:: traits:: { Currency , Imbalance , SignedImbalance } ;
3030use frame_support:: { storage:: child, StorageMap } ;
3131use frame_system;
3232
@@ -108,7 +108,12 @@ pub trait AccountDb<T: Trait> {
108108 ///
109109 /// Trie id is None iff account doesn't have an associated trie id in <ContractInfoOf<T>>.
110110 /// Because DirectAccountDb bypass the lookup for this association.
111- fn get_storage ( & self , account : & T :: AccountId , trie_id : Option < & TrieId > , location : & StorageKey ) -> Option < Vec < u8 > > ;
111+ fn get_storage (
112+ & self ,
113+ account : & T :: AccountId ,
114+ trie_id : Option < & TrieId > ,
115+ location : & StorageKey ,
116+ ) -> Option < Vec < u8 > > ;
112117 /// If account has an alive contract then return the code hash associated.
113118 fn get_code_hash ( & self , account : & T :: AccountId ) -> Option < CodeHash < T > > ;
114119 /// If account has an alive contract then return the rent allowance associated.
@@ -126,9 +131,10 @@ impl<T: Trait> AccountDb<T> for DirectAccountDb {
126131 & self ,
127132 _account : & T :: AccountId ,
128133 trie_id : Option < & TrieId > ,
129- location : & StorageKey
134+ location : & StorageKey ,
130135 ) -> Option < Vec < u8 > > {
131- trie_id. and_then ( |id| child:: get_raw ( & crate :: child_trie_info ( & id[ ..] ) , & blake2_256 ( location) ) )
136+ trie_id
137+ . and_then ( |id| child:: get_raw ( & crate :: child_trie_info ( & id[ ..] ) , & blake2_256 ( location) ) )
132138 }
133139 fn get_code_hash ( & self , account : & T :: AccountId ) -> Option < CodeHash < T > > {
134140 <ContractInfoOf < T > >:: get ( account) . and_then ( |i| i. as_alive ( ) . map ( |i| i. code_hash ) )
@@ -176,24 +182,26 @@ impl<T: Trait> AccountDb<T> for DirectAccountDb {
176182 child:: kill_storage ( & info. child_trie_info ( ) ) ;
177183 AliveContractInfo :: < T > {
178184 code_hash,
179- storage_size : T :: StorageSizeOffset :: get ( ) ,
185+ storage_size : 0 ,
186+ empty_pair_count : 0 ,
187+ total_pair_count : 0 ,
180188 trie_id : <T as Trait >:: TrieIdGenerator :: trie_id ( & address) ,
181189 deduct_block : <frame_system:: Module < T > >:: block_number ( ) ,
182190 rent_allowance : <BalanceOf < T > >:: max_value ( ) ,
183191 last_write : None ,
184192 }
185193 }
186194 // New contract is being instantiated.
187- ( _, None , Some ( code_hash) ) => {
188- AliveContractInfo :: < T > {
189- code_hash ,
190- storage_size : T :: StorageSizeOffset :: get ( ) ,
191- trie_id : < T as Trait > :: TrieIdGenerator :: trie_id ( & address ) ,
192- deduct_block : <frame_system :: Module < T > > :: block_number ( ) ,
193- rent_allowance : <BalanceOf < T > >:: max_value ( ) ,
194- last_write : None ,
195- }
196- }
195+ ( _, None , Some ( code_hash) ) => AliveContractInfo :: < T > {
196+ code_hash ,
197+ storage_size : 0 ,
198+ empty_pair_count : 0 ,
199+ total_pair_count : 0 ,
200+ trie_id : <T as Trait > :: TrieIdGenerator :: trie_id ( & address ) ,
201+ deduct_block : <frame_system :: Module < T > >:: block_number ( ) ,
202+ rent_allowance : < BalanceOf < T > > :: max_value ( ) ,
203+ last_write : None ,
204+ } ,
197205 // There is no existing at the address nor a new one to be instantiated.
198206 ( _, None , None ) => continue ,
199207 } ;
@@ -210,18 +218,69 @@ impl<T: Trait> AccountDb<T> for DirectAccountDb {
210218 new_info. last_write = Some ( <frame_system:: Module < T > >:: block_number ( ) ) ;
211219 }
212220
213- for ( k, v) in changed. storage . into_iter ( ) {
214- if let Some ( value) = child:: get_raw (
215- & new_info. child_trie_info ( ) ,
216- & blake2_256 ( & k) ,
217- ) {
218- new_info. storage_size -= value. len ( ) as u32 ;
221+ // NB: this call allocates internally. To keep allocations to the minimum we cache
222+ // the child trie info here.
223+ let child_trie_info = new_info. child_trie_info ( ) ;
224+
225+ // Here we iterate over all storage key-value pairs that were changed throughout the
226+ // execution of a contract and apply them to the substrate storage.
227+ for ( key, opt_new_value) in changed. storage . into_iter ( ) {
228+ let hashed_key = blake2_256 ( & key) ;
229+
230+ // In order to correctly update the book keeping we need to fetch the previous
231+ // value of the key-value pair.
232+ //
233+ // It might be a bit more clean if we had an API that supported getting the size
234+ // of the value without going through the loading of it. But at the moment of
235+ // writing, there is no such API.
236+ //
237+ // That's not a show stopper in any case, since the performance cost is
238+ // dominated by the trie traversal anyway.
239+ let opt_prev_value = child:: get_raw ( & child_trie_info, & hashed_key) ;
240+
241+ // Update the total number of KV pairs and the number of empty pairs.
242+ match ( & opt_prev_value, & opt_new_value) {
243+ ( Some ( prev_value) , None ) => {
244+ new_info. total_pair_count -= 1 ;
245+ if prev_value. is_empty ( ) {
246+ new_info. empty_pair_count -= 1 ;
247+ }
248+ } ,
249+ ( None , Some ( new_value) ) => {
250+ new_info. total_pair_count += 1 ;
251+ if new_value. is_empty ( ) {
252+ new_info. empty_pair_count += 1 ;
253+ }
254+ } ,
255+ ( Some ( prev_value) , Some ( new_value) ) => {
256+ if prev_value. is_empty ( ) {
257+ new_info. empty_pair_count -= 1 ;
258+ }
259+ if new_value. is_empty ( ) {
260+ new_info. empty_pair_count += 1 ;
261+ }
262+ }
263+ ( None , None ) => { }
219264 }
220- if let Some ( value) = v {
221- new_info. storage_size += value. len ( ) as u32 ;
222- child:: put_raw ( & new_info. child_trie_info ( ) , & blake2_256 ( & k) , & value[ ..] ) ;
223- } else {
224- child:: kill ( & new_info. child_trie_info ( ) , & blake2_256 ( & k) ) ;
265+
266+ // Update the total storage size.
267+ let prev_value_len = opt_prev_value
268+ . as_ref ( )
269+ . map ( |old_value| old_value. len ( ) as u32 )
270+ . unwrap_or ( 0 ) ;
271+ let new_value_len = opt_new_value
272+ . as_ref ( )
273+ . map ( |new_value| new_value. len ( ) as u32 )
274+ . unwrap_or ( 0 ) ;
275+ new_info. storage_size = new_info
276+ . storage_size
277+ . saturating_add ( new_value_len)
278+ . saturating_sub ( prev_value_len) ;
279+
280+ // Finally, perform the change on the storage.
281+ match opt_new_value {
282+ Some ( new_value) => child:: put_raw ( & child_trie_info, & hashed_key, & new_value[ ..] ) ,
283+ None => child:: kill ( & child_trie_info, & hashed_key) ,
225284 }
226285 }
227286
@@ -239,12 +298,14 @@ impl<T: Trait> AccountDb<T> for DirectAccountDb {
239298 // then it's indicative of a buggy contracts system.
240299 // Panicking is far from ideal as it opens up a DoS attack on block validators, however
241300 // it's a less bad option than allowing arbitrary value to be created.
242- SignedImbalance :: Positive ( ref p) if !p. peek ( ) . is_zero ( ) =>
243- panic ! ( "contract subsystem resulting in positive imbalance!" ) ,
301+ SignedImbalance :: Positive ( ref p) if !p. peek ( ) . is_zero ( ) => {
302+ panic ! ( "contract subsystem resulting in positive imbalance!" )
303+ }
244304 _ => { }
245305 }
246306 }
247307}
308+
248309pub struct OverlayAccountDb < ' a , T : Trait + ' a > {
249310 local : RefCell < ChangeSet < T > > ,
250311 underlying : & ' a dyn AccountDb < T > ,
@@ -267,7 +328,8 @@ impl<'a, T: Trait> OverlayAccountDb<'a, T> {
267328 location : StorageKey ,
268329 value : Option < Vec < u8 > > ,
269330 ) {
270- self . local . borrow_mut ( )
331+ self . local
332+ . borrow_mut ( )
271333 . entry ( account. clone ( ) )
272334 . or_insert ( Default :: default ( ) )
273335 . storage
@@ -285,7 +347,7 @@ impl<'a, T: Trait> OverlayAccountDb<'a, T> {
285347 }
286348
287349 let mut local = self . local . borrow_mut ( ) ;
288- let contract = local. entry ( account. clone ( ) ) . or_insert_with ( || Default :: default ( ) ) ;
350+ let contract = local. entry ( account. clone ( ) ) . or_default ( ) ;
289351
290352 contract. code_hash = Some ( code_hash) ;
291353 contract. rent_allowance = Some ( <BalanceOf < T > >:: max_value ( ) ) ;
@@ -301,7 +363,7 @@ impl<'a, T: Trait> OverlayAccountDb<'a, T> {
301363 ChangeEntry {
302364 reset : true ,
303365 ..Default :: default ( )
304- }
366+ } ,
305367 ) ;
306368 }
307369
@@ -327,7 +389,7 @@ impl<'a, T: Trait> AccountDb<T> for OverlayAccountDb<'a, T> {
327389 & self ,
328390 account : & T :: AccountId ,
329391 trie_id : Option < & TrieId > ,
330- location : & StorageKey
392+ location : & StorageKey ,
331393 ) -> Option < Vec < u8 > > {
332394 self . local
333395 . borrow ( )
0 commit comments