diff --git a/substrate/core/client/db/src/lib.rs b/substrate/core/client/db/src/lib.rs index 82d570567b..dd38f10bcd 100644 --- a/substrate/core/client/db/src/lib.rs +++ b/substrate/core/client/db/src/lib.rs @@ -72,6 +72,97 @@ const MIN_BLOCKS_TO_KEEP_CHANGES_TRIES_FOR: u32 = 32768; /// DB-backed patricia trie state, transaction type is an overlay of changes to commit. pub type DbState = state_machine::TrieBackend>, Blake2Hasher>; +pub struct RefTrackingState { + state: DbState, + storage: Arc>, + parent_hash: Option, +} + +impl RefTrackingState { + fn new(state: DbState, storage: Arc>, parent_hash: Option) -> RefTrackingState { + if let Some(hash) = &parent_hash { + storage.state_db.pin(hash); + } + RefTrackingState { + state, + parent_hash, + storage, + } + } +} + +impl Drop for RefTrackingState { + fn drop(&mut self) { + if let Some(hash) = &self.parent_hash { + self.storage.state_db.unpin(hash); + } + } +} + +impl StateBackend for RefTrackingState { + type Error = >::Error; + type Transaction = >::Transaction; + type TrieBackendStorage = >::TrieBackendStorage; + + fn storage(&self, key: &[u8]) -> Result>, Self::Error> { + self.state.storage(key) + } + + fn storage_hash(&self, key: &[u8]) -> Result, Self::Error> { + self.state.storage_hash(key) + } + + fn child_storage(&self, storage_key: &[u8], key: &[u8]) -> Result>, Self::Error> { + self.state.child_storage(storage_key, key) + } + + fn exists_storage(&self, key: &[u8]) -> Result { + self.state.exists_storage(key) + } + + fn exists_child_storage(&self, storage_key: &[u8], key: &[u8]) -> Result { + self.state.exists_child_storage(storage_key, key) + } + + fn for_keys_with_prefix(&self, prefix: &[u8], f: F) { + self.state.for_keys_with_prefix(prefix, f) + } + + fn for_keys_in_child_storage(&self, storage_key: &[u8], f: F) { + self.state.for_keys_in_child_storage(storage_key, f) + } + + fn storage_root(&self, delta: I) -> (H256, Self::Transaction) + where + I: IntoIterator, Option>)> + { + self.state.storage_root(delta) + } + + fn child_storage_root(&self, storage_key: &[u8], delta: I) -> (Vec, bool, Self::Transaction) + where + I: IntoIterator, Option>)>, + { + self.state.child_storage_root(storage_key, delta) + } + + fn pairs(&self) -> Vec<(Vec, Vec)> { + self.state.pairs() + } + + fn keys(&self, prefix: &[u8]) -> Vec> { + self.state.keys(prefix) + } + + fn child_keys(&self, child_key: &[u8], prefix: &[u8]) -> Vec> { + self.state.child_keys(child_key, prefix) + } + + fn as_trie_backend(&mut self) -> Option<&state_machine::TrieBackend> { + self.state.as_trie_backend() + } +} + /// Database settings. pub struct DatabaseSettings { /// Cache size in bytes. If `None` default is used. @@ -270,7 +361,7 @@ impl client::blockchain::ProvideCache for BlockchainDb { - old_state: CachingState, + old_state: CachingState, Block>, db_updates: PrefixedMemoryDB, storage_updates: Vec<(Vec, Option>)>, changes_trie_updates: MemoryDB, @@ -295,7 +386,7 @@ impl client::backend::BlockImportOperation for BlockImportOperation where Block: BlockT, { - type State = CachingState; + type State = CachingState, Block>; fn state(&self) -> Result, client::error::Error> { Ok(Some(&self.old_state)) @@ -922,6 +1013,8 @@ impl> Backend { let changes_trie_updates = operation.changes_trie_updates; self.changes_tries_storage.commit(&mut transaction, changes_trie_updates); + let cache = operation.old_state.release(); // release state reference so that it can be finalized + if finalized { // TODO: ensure best chain contains this block. @@ -953,7 +1046,7 @@ impl> Backend { meta_updates.push((hash, number, pending_block.leaf_state.is_best(), finalized)); - Some((number, hash, enacted, retracted, displaced_leaf, is_best)) + Some((number, hash, enacted, retracted, displaced_leaf, is_best, cache)) } else { None }; @@ -975,7 +1068,7 @@ impl> Backend { let write_result = self.storage.db.write(transaction).map_err(db_err); - if let Some((number, hash, enacted, retracted, displaced_leaf, is_best)) = imported { + if let Some((number, hash, enacted, retracted, displaced_leaf, is_best, mut cache)) = imported { if let Err(e) = write_result { let mut leaves = self.blockchain.leaves.write(); let mut undo = leaves.undo(); @@ -990,7 +1083,7 @@ impl> Backend { return Err(e) } - operation.old_state.sync_cache( + cache.sync_cache( &enacted, &retracted, operation.storage_updates, @@ -1090,7 +1183,7 @@ impl client::backend::AuxStore for Backend where Block: BlockT client::backend::Backend for Backend where Block: BlockT { type BlockImportOperation = BlockImportOperation; type Blockchain = BlockchainDb; - type State = CachingState; + type State = CachingState, Block>; type ChangesTrieStorage = DbChangesTrieStorage; fn begin_operation(&self) -> Result { @@ -1217,7 +1310,8 @@ impl client::backend::Backend for Backend whe BlockId::Hash(h) if h == Default::default() => { let genesis_storage = DbGenesisStorage::new(); let root = genesis_storage.0.clone(); - let state = DbState::new(Arc::new(genesis_storage), root); + let db_state = DbState::new(Arc::new(genesis_storage), root); + let state = RefTrackingState::new(db_state, self.storage.clone(), None); return Ok(CachingState::new(state, self.shared_cache.clone(), None)); }, _ => {} @@ -1228,7 +1322,8 @@ impl client::backend::Backend for Backend whe let hash = hdr.hash(); if !self.storage.state_db.is_pruned(&hash, (*hdr.number()).saturated_into::()) { let root = H256::from_slice(hdr.state_root().as_ref()); - let state = DbState::new(self.storage.clone(), root); + let db_state = DbState::new(self.storage.clone(), root); + let state = RefTrackingState::new(db_state, self.storage.clone(), Some(hash.clone())); Ok(CachingState::new(state, self.shared_cache.clone(), Some(hash))) } else { Err(client::error::Error::UnknownBlock(format!("State already discarded for {:?}", block))) @@ -1243,10 +1338,10 @@ impl client::backend::Backend for Backend whe !self.storage.state_db.is_pruned(hash, number.saturated_into::()) } - fn destroy_state(&self, mut state: Self::State) -> Result<(), client::error::Error> { - if let Some(hash) = state.parent_hash.clone() { + fn destroy_state(&self, state: Self::State) -> Result<(), client::error::Error> { + if let Some(hash) = state.cache.parent_hash.clone() { let is_best = || self.blockchain.meta.read().best_hash == hash; - state.sync_cache(&[], &[], vec![], None, None, is_best); + state.release().sync_cache(&[], &[], vec![], None, None, is_best); } Ok(()) } diff --git a/substrate/core/client/db/src/storage_cache.rs b/substrate/core/client/db/src/storage_cache.rs index bc0a179cf8..cc4670866c 100644 --- a/substrate/core/client/db/src/storage_cache.rs +++ b/substrate/core/client/db/src/storage_cache.rs @@ -99,6 +99,17 @@ struct LocalCache { hashes: HashMap>, } +/// Cache changes. +pub struct CacheChanges { + /// Shared canonical state cache. + shared_cache: SharedCache, + /// Local cache of values for this state. + local_cache: RwLock>, + /// Hash of the block on top of which this instance was created or + /// `None` if cache is disabled + pub parent_hash: Option, +} + /// State abstraction. /// Manages shared global state cache which reflects the canonical /// state as it is on the disk. @@ -109,56 +120,11 @@ struct LocalCache { pub struct CachingState, B: Block> { /// Backing state. state: S, - /// Shared canonical state cache. - shared_cache: SharedCache, - /// Local cache of values for this state. - local_cache: RwLock>, - /// Hash of the block on top of which this instance was created or - /// `None` if cache is disabled - pub parent_hash: Option, + /// Cache data. + pub cache: CacheChanges } -impl, B: Block> CachingState { - /// Create a new instance wrapping generic State and shared cache. - pub fn new(state: S, shared_cache: SharedCache, parent_hash: Option) -> CachingState { - CachingState { - state, - shared_cache, - local_cache: RwLock::new(LocalCache { - storage: Default::default(), - hashes: Default::default(), - }), - parent_hash: parent_hash, - } - } - - fn storage_insert(cache: &mut Cache, k: StorageValue, v: Option) { - if let Some(v_) = &v { - while cache.storage_used_size + v_.len() > cache.shared_cache_size { - // pop until space constraint satisfied - match cache.storage.remove_lru() { - Some((_, Some(popped_v))) => - cache.storage_used_size = cache.storage_used_size - popped_v.len(), - Some((_, None)) => continue, - None => break, - }; - } - cache.storage_used_size = cache.storage_used_size + v_.len(); - } - cache.storage.insert(k, v); - } - - fn storage_remove( - storage: &mut LruCache>, - k: &StorageKey, - storage_used_size: &mut usize, - ) { - let v = storage.remove(k); - if let Some(Some(v_)) = v { - *storage_used_size = *storage_used_size - v_.len(); - } - } - +impl CacheChanges { /// Propagate local cache into the shared cache and synchronize /// the shared cache with the best block state. /// This function updates the shared cache by removing entries @@ -189,7 +155,7 @@ impl, B: Block> CachingState { m.is_canon = true; for a in &m.storage { trace!("Reverting enacted key {:?}", a); - CachingState::::storage_remove(&mut cache.storage, a, &mut cache.storage_used_size); + CacheChanges::::storage_remove(&mut cache.storage, a, &mut cache.storage_used_size); } false } else { @@ -205,7 +171,7 @@ impl, B: Block> CachingState { m.is_canon = false; for a in &m.storage { trace!("Retracted key {:?}", a); - CachingState::::storage_remove(&mut cache.storage, a, &mut cache.storage_used_size); + CacheChanges::::storage_remove(&mut cache.storage, a, &mut cache.storage_used_size); } false } else { @@ -228,7 +194,7 @@ impl, B: Block> CachingState { if is_best { trace!("Committing {} local, {} hashes, {} modified entries", local_cache.storage.len(), local_cache.hashes.len(), changes.len()); for (k, v) in local_cache.storage.drain() { - CachingState::::storage_insert(cache, k, v); + CacheChanges::::storage_insert(cache, k, v); } for (k, v) in local_cache.hashes.drain() { cache.hashes.insert(k, v); @@ -248,7 +214,7 @@ impl, B: Block> CachingState { modifications.insert(k.clone()); if is_best { cache.hashes.remove(&k); - CachingState::::storage_insert(cache, k, v); + CacheChanges::::storage_insert(cache, k, v); } } // Save modified storage. These are ordered by the block number. @@ -272,6 +238,50 @@ impl, B: Block> CachingState { } } + fn storage_insert(cache: &mut Cache, k: StorageValue, v: Option) { + if let Some(v_) = &v { + while cache.storage_used_size + v_.len() > cache.shared_cache_size { + // pop until space constraint satisfied + match cache.storage.remove_lru() { + Some((_, Some(popped_v))) => + cache.storage_used_size = cache.storage_used_size - popped_v.len(), + Some((_, None)) => continue, + None => break, + }; + } + cache.storage_used_size = cache.storage_used_size + v_.len(); + } + cache.storage.insert(k, v); + } + + fn storage_remove( + storage: &mut LruCache>, + k: &StorageKey, + storage_used_size: &mut usize, + ) { + let v = storage.remove(k); + if let Some(Some(v_)) = v { + *storage_used_size = *storage_used_size - v_.len(); + } + } +} + +impl, B: Block> CachingState { + /// Create a new instance wrapping generic State and shared cache. + pub fn new(state: S, shared_cache: SharedCache, parent_hash: Option) -> CachingState { + CachingState { + state, + cache: CacheChanges { + shared_cache, + local_cache: RwLock::new(LocalCache { + storage: Default::default(), + hashes: Default::default(), + }), + parent_hash: parent_hash, + }, + } + } + /// Check if the key can be returned from cache by matching current block parent hash against canonical /// state and filtering out entries modified in later blocks. fn is_allowed( @@ -312,6 +322,11 @@ impl, B: Block> CachingState { trace!("Cache lookup skipped for {:?}: parent hash is unknown", key); false } + + /// Dispose state and return cache data. + pub fn release(self) -> CacheChanges { + self.cache + } } impl, B:Block> StateBackend for CachingState { @@ -320,13 +335,13 @@ impl, B:Block> StateBackend for CachingState Result>, Self::Error> { - let local_cache = self.local_cache.upgradable_read(); + let local_cache = self.cache.local_cache.upgradable_read(); if let Some(entry) = local_cache.storage.get(key).cloned() { trace!("Found in local cache: {:?}", key); return Ok(entry) } - let mut cache = self.shared_cache.lock(); - if Self::is_allowed(key, &self.parent_hash, &cache.modifications) { + let mut cache = self.cache.shared_cache.lock(); + if Self::is_allowed(key, &self.cache.parent_hash, &cache.modifications) { if let Some(entry) = cache.storage.get_mut(key).map(|a| a.clone()) { trace!("Found in shared cache: {:?}", key); return Ok(entry) @@ -339,13 +354,13 @@ impl, B:Block> StateBackend for CachingState Result, Self::Error> { - let local_cache = self.local_cache.upgradable_read(); + let local_cache = self.cache.local_cache.upgradable_read(); if let Some(entry) = local_cache.hashes.get(key).cloned() { trace!("Found hash in local cache: {:?}", key); return Ok(entry) } - let mut cache = self.shared_cache.lock(); - if Self::is_allowed(key, &self.parent_hash, &cache.modifications) { + let mut cache = self.cache.shared_cache.lock(); + if Self::is_allowed(key, &self.cache.parent_hash, &cache.modifications) { if let Some(entry) = cache.hashes.get_mut(key).map(|a| a.clone()) { trace!("Found hash in shared cache: {:?}", key); return Ok(entry) @@ -405,8 +420,8 @@ impl, B:Block> StateBackend for CachingState Option> { - self.state.try_into_trie_backend() + fn as_trie_backend(&mut self) -> Option<&TrieBackend> { + self.state.as_trie_backend() } } @@ -436,22 +451,22 @@ mod tests { // blocks [ 3a(c) 2a(c) 2b 1b 1a(c) 0 ] // state [ 5 5 4 3 2 2 ] let mut s = CachingState::new(InMemory::::default(), shared.clone(), Some(root_parent.clone())); - s.sync_cache(&[], &[], vec![(key.clone(), Some(vec![2]))], Some(h0.clone()), Some(0), || true); + s.cache.sync_cache(&[], &[], vec![(key.clone(), Some(vec![2]))], Some(h0.clone()), Some(0), || true); let mut s = CachingState::new(InMemory::::default(), shared.clone(), Some(h0.clone())); - s.sync_cache(&[], &[], vec![], Some(h1a.clone()), Some(1), || true); + s.cache.sync_cache(&[], &[], vec![], Some(h1a.clone()), Some(1), || true); let mut s = CachingState::new(InMemory::::default(), shared.clone(), Some(h0.clone())); - s.sync_cache(&[], &[], vec![(key.clone(), Some(vec![3]))], Some(h1b.clone()), Some(1), || false); + s.cache.sync_cache(&[], &[], vec![(key.clone(), Some(vec![3]))], Some(h1b.clone()), Some(1), || false); let mut s = CachingState::new(InMemory::::default(), shared.clone(), Some(h1b.clone())); - s.sync_cache(&[], &[], vec![(key.clone(), Some(vec![4]))], Some(h2b.clone()), Some(2), || false); + s.cache.sync_cache(&[], &[], vec![(key.clone(), Some(vec![4]))], Some(h2b.clone()), Some(2), || false); let mut s = CachingState::new(InMemory::::default(), shared.clone(), Some(h1a.clone())); - s.sync_cache(&[], &[], vec![(key.clone(), Some(vec![5]))], Some(h2a.clone()), Some(2), || true); + s.cache.sync_cache(&[], &[], vec![(key.clone(), Some(vec![5]))], Some(h2a.clone()), Some(2), || true); let mut s = CachingState::new(InMemory::::default(), shared.clone(), Some(h2a.clone())); - s.sync_cache(&[], &[], vec![], Some(h3a.clone()), Some(3), || true); + s.cache.sync_cache(&[], &[], vec![], Some(h3a.clone()), Some(3), || true); let s = CachingState::new(InMemory::::default(), shared.clone(), Some(h3a.clone())); assert_eq!(s.storage(&key).unwrap().unwrap(), vec![5]); @@ -468,7 +483,14 @@ mod tests { // reorg to 3b // blocks [ 3b(c) 3a 2a 2b(c) 1b 1a 0 ] let mut s = CachingState::new(InMemory::::default(), shared.clone(), Some(h2b.clone())); - s.sync_cache(&[h1b.clone(), h2b.clone(), h3b.clone()], &[h1a.clone(), h2a.clone(), h3a.clone()], vec![], Some(h3b.clone()), Some(3), || true); + s.cache.sync_cache( + &[h1b.clone(), h2b.clone(), h3b.clone()], + &[h1a.clone(), h2a.clone(), h3a.clone()], + vec![], + Some(h3b.clone()), + Some(3), + || true + ); let s = CachingState::new(InMemory::::default(), shared.clone(), Some(h3a.clone())); assert!(s.storage(&key).unwrap().is_none()); } @@ -482,11 +504,11 @@ mod tests { let mut s = CachingState::new(InMemory::::default(), shared.clone(), Some(root_parent.clone())); let key = H256::random()[..].to_vec(); - s.sync_cache(&[], &[], vec![(key.clone(), Some(vec![1, 2, 3]))], Some(h0.clone()), Some(0), || true); + s.cache.sync_cache(&[], &[], vec![(key.clone(), Some(vec![1, 2, 3]))], Some(h0.clone()), Some(0), || true); assert_eq!(shared.lock().used_storage_cache_size(), 3 /* bytes */); let key = H256::random()[..].to_vec(); - s.sync_cache(&[], &[], vec![(key.clone(), Some(vec![1, 2]))], Some(h0.clone()), Some(0), || true); + s.cache.sync_cache(&[], &[], vec![(key.clone(), Some(vec![1, 2]))], Some(h0.clone()), Some(0), || true); assert_eq!(shared.lock().used_storage_cache_size(), 5 /* bytes */); } @@ -499,11 +521,11 @@ mod tests { let mut s = CachingState::new(InMemory::::default(), shared.clone(), Some(root_parent.clone())); let key = H256::random()[..].to_vec(); - s.sync_cache(&[], &[], vec![(key.clone(), Some(vec![1, 2, 3, 4]))], Some(h0.clone()), Some(0), || true); + s.cache.sync_cache(&[], &[], vec![(key.clone(), Some(vec![1, 2, 3, 4]))], Some(h0.clone()), Some(0), || true); assert_eq!(shared.lock().used_storage_cache_size(), 4 /* bytes */); let key = H256::random()[..].to_vec(); - s.sync_cache(&[], &[], vec![(key.clone(), Some(vec![1, 2]))], Some(h0.clone()), Some(0), || true); + s.cache.sync_cache(&[], &[], vec![(key.clone(), Some(vec![1, 2]))], Some(h0.clone()), Some(0), || true); assert_eq!(shared.lock().used_storage_cache_size(), 2 /* bytes */); } } diff --git a/substrate/core/client/src/call_executor.rs b/substrate/core/client/src/call_executor.rs index 13ee96400f..bb59be44c3 100644 --- a/substrate/core/client/src/call_executor.rs +++ b/substrate/core/client/src/call_executor.rs @@ -117,17 +117,17 @@ where /// No changes are made. fn prove_at_state>( &self, - state: S, + mut state: S, overlay: &mut OverlayedChanges, method: &str, call_data: &[u8] ) -> Result<(Vec, Vec>), error::Error> { - let trie_state = state.try_into_trie_backend() + let trie_state = state.as_trie_backend() .ok_or_else(|| Box::new(state_machine::ExecutionError::UnableToGenerateProof) as Box )?; - self.prove_at_trie_state(&trie_state, overlay, method, call_data) + self.prove_at_trie_state(trie_state, overlay, method, call_data) } /// Execute a call to a contract on top of given trie state, gathering execution proof. @@ -239,18 +239,18 @@ where _ => {}, } - let state = self.backend.state_at(*at)?; + let mut state = self.backend.state_at(*at)?; match recorder { Some(recorder) => { - let trie_state = state.try_into_trie_backend() + let trie_state = state.as_trie_backend() .ok_or_else(|| Box::new(state_machine::ExecutionError::UnableToGenerateProof) as Box )?; let backend = state_machine::ProvingBackend::new_with_recorder( - &trie_state, + trie_state, recorder.clone() ); diff --git a/substrate/core/client/src/cht.rs b/substrate/core/client/src/cht.rs index 996a6b37f1..fc8920327e 100644 --- a/substrate/core/client/src/cht.rs +++ b/substrate/core/client/src/cht.rs @@ -101,14 +101,14 @@ pub fn build_proof( .into_iter() .map(|(k, v)| (None, k, Some(v))) .collect::>(); - let storage = InMemoryState::::default().update(transaction); - let trie_storage = storage.try_into_trie_backend() - .expect("InMemoryState::try_into_trie_backend always returns Some; qed"); + let mut storage = InMemoryState::::default().update(transaction); + let trie_storage = storage.as_trie_backend() + .expect("InMemoryState::as_trie_backend always returns Some; qed"); let mut total_proof = HashSet::new(); for block in blocks.into_iter() { debug_assert_eq!(block_to_cht_number(cht_size, block), Some(cht_num)); - let (value, proof) = prove_read_on_trie_backend(&trie_storage, &encode_cht_key(block))?; + let (value, proof) = prove_read_on_trie_backend(trie_storage, &encode_cht_key(block))?; assert!(value.is_some(), "we have just built trie that includes the value for block"); total_proof.extend(proof); } diff --git a/substrate/core/client/src/client.rs b/substrate/core/client/src/client.rs index 874cf7636b..1f19b96d3c 100644 --- a/substrate/core/client/src/client.rs +++ b/substrate/core/client/src/client.rs @@ -1277,7 +1277,7 @@ impl Client where /// Prepare in-memory header that is used in execution environment. fn prepare_environment_block(&self, parent: &BlockId) -> error::Result { - let parent_header = self.backend().blockchain().expect_header(*parent)?; + let parent_header = self.backend.blockchain().expect_header(*parent)?; Ok(<::Header as HeaderT>::new( self.backend.blockchain().expect_block_number_from_id(parent)? + One::one(), Default::default(), diff --git a/substrate/core/client/src/light/backend.rs b/substrate/core/client/src/light/backend.rs index bf5eb11f26..0c2279bf3d 100644 --- a/substrate/core/client/src/light/backend.rs +++ b/substrate/core/client/src/light/backend.rs @@ -38,13 +38,13 @@ use consensus::well_known_cache_keys; const IN_MEMORY_EXPECT_PROOF: &str = "InMemory state backend has Void error type and always suceeds; qed"; /// Light client backend. -pub struct Backend { +pub struct Backend { blockchain: Arc>, genesis_state: RwLock>>, } /// Light block (header and justification) import operation. -pub struct ImportOperation { +pub struct ImportOperation { header: Option, cache: HashMap>, leaf_state: NewBlockState, @@ -64,14 +64,14 @@ pub struct OnDemandState { } /// On-demand or in-memory genesis state. -pub enum OnDemandOrGenesisState { +pub enum OnDemandOrGenesisState { /// On-demand state - storage values are fetched from remote nodes. OnDemand(OnDemandState), /// Genesis state - storage values are stored in-memory. Genesis(InMemoryState), } -impl Backend { +impl Backend { /// Create new light backend. pub fn new(blockchain: Arc>) -> Self { Self { @@ -86,7 +86,7 @@ impl Backend { } } -impl AuxStore for Backend { +impl AuxStore for Backend { fn insert_aux< 'a, 'b: 'a, @@ -387,7 +387,7 @@ where Vec::new() } - fn try_into_trie_backend(self) -> Option> { + fn as_trie_backend(&mut self) -> Option<&TrieBackend> { None } } @@ -482,10 +482,10 @@ where } } - fn try_into_trie_backend(self) -> Option> { + fn as_trie_backend(&mut self) -> Option<&TrieBackend> { match self { - OnDemandOrGenesisState::OnDemand(state) => state.try_into_trie_backend(), - OnDemandOrGenesisState::Genesis(state) => state.try_into_trie_backend(), + OnDemandOrGenesisState::OnDemand(ref mut state) => state.as_trie_backend(), + OnDemandOrGenesisState::Genesis(ref mut state) => state.as_trie_backend(), } } } diff --git a/substrate/core/client/src/light/call_executor.rs b/substrate/core/client/src/light/call_executor.rs index 5fb7e7308a..5b6d1b3a29 100644 --- a/substrate/core/client/src/light/call_executor.rs +++ b/substrate/core/client/src/light/call_executor.rs @@ -388,7 +388,7 @@ impl CallExecutor for /// Method is executed using passed header as environment' current block. /// Proof includes both environment preparation proof and method execution proof. pub fn prove_execution( - state: S, + mut state: S, header: Block::Header, executor: &E, method: &str, @@ -399,13 +399,13 @@ pub fn prove_execution( S: StateBackend, E: CallExecutor, { - let trie_state = state.try_into_trie_backend() + let trie_state = state.as_trie_backend() .ok_or_else(|| Box::new(state_machine::ExecutionError::UnableToGenerateProof) as Box)?; // prepare execution environment + record preparation proof let mut changes = Default::default(); let (_, init_proof) = executor.prove_at_trie_state( - &trie_state, + trie_state, &mut changes, "Core_initialize_block", &header.encode(), diff --git a/substrate/core/state-db/src/lib.rs b/substrate/core/state-db/src/lib.rs index 8d9cf9c965..8986dda32d 100644 --- a/substrate/core/state-db/src/lib.rs +++ b/substrate/core/state-db/src/lib.rs @@ -37,7 +37,7 @@ use std::fmt; use parking_lot::RwLock; use parity_codec as codec; use codec::Codec; -use std::collections::HashSet; +use std::collections::{VecDeque, HashMap, hash_map::Entry}; use noncanonical::NonCanonicalOverlay; use pruning::RefWindow; use log::trace; @@ -78,6 +78,8 @@ pub enum Error { InvalidBlockNumber, /// Trying to insert block with unknown parent. InvalidParent, + /// Canonicalization would discard pinned state. + DiscardingPinned, } impl fmt::Debug for Error { @@ -88,6 +90,7 @@ impl fmt::Debug for Error { Error::InvalidBlock => write!(f, "Trying to canonicalize invalid block"), Error::InvalidBlockNumber => write!(f, "Trying to insert block with invalid number"), Error::InvalidParent => write!(f, "Trying to insert block with unknown parent"), + Error::DiscardingPinned => write!(f, "Trying to discard pinned state"), } } } @@ -112,7 +115,7 @@ pub struct CommitSet { } /// Pruning constraints. If none are specified pruning is -#[derive(Default, Debug, Clone)] +#[derive(Default, Debug, Clone, Eq, PartialEq)] pub struct Constraints { /// Maximum blocks. Defaults to 0 when unspecified, effectively keeping only non-canonical states. pub max_blocks: Option, @@ -121,7 +124,7 @@ pub struct Constraints { } /// Pruning mode. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Eq, PartialEq)] pub enum PruningMode { /// Maintain a pruning window. Constrained(Constraints), @@ -165,13 +168,14 @@ fn to_meta_key(suffix: &[u8], data: &S) -> Vec { struct StateDbSync { mode: PruningMode, non_canonical: NonCanonicalOverlay, + canonicalization_queue: VecDeque, pruning: Option>, - pinned: HashSet, + pinned: HashMap, } impl StateDbSync { pub fn new(mode: PruningMode, db: &D) -> Result, Error> { - trace!("StateDb settings: {:?}", mode); + trace!(target: "state-db", "StateDb settings: {:?}", mode); let non_canonical: NonCanonicalOverlay = NonCanonicalOverlay::new(db)?; let pruning: Option> = match mode { PruningMode::Constrained(Constraints { @@ -186,6 +190,7 @@ impl StateDbSync { non_canonical, pruning, pinned: Default::default(), + canonicalization_queue: Default::default(), }) } @@ -206,21 +211,30 @@ impl StateDbSync { } pub fn canonicalize_block(&mut self, hash: &BlockHash) -> Result, Error> { - let mut commit = match self.mode { - PruningMode::ArchiveAll => { - CommitSet::default() - }, - PruningMode::ArchiveCanonical => { - let mut commit = self.non_canonical.canonicalize(hash)?; - commit.data.deleted.clear(); - commit - }, - PruningMode::Constrained(_) => { - self.non_canonical.canonicalize(hash)? - }, - }; - if let Some(ref mut pruning) = self.pruning { - pruning.note_canonical(hash, &mut commit); + let mut commit = CommitSet::default(); + if self.mode == PruningMode::ArchiveAll { + return Ok(commit) + } + self.canonicalization_queue.push_back(hash.clone()); + while let Some(hash) = self.canonicalization_queue.front().cloned() { + if self.pinned.contains_key(&hash) { + break; + } + match self.non_canonical.canonicalize(&hash, &self.pinned, &mut commit) { + Ok(()) => { + self.canonicalization_queue.pop_front(); + if self.mode == PruningMode::ArchiveCanonical { + commit.data.deleted.clear(); + } + } + Err(Error::DiscardingPinned) => { + break; + } + Err(e) => return Err(e), + }; + if let Some(ref mut pruning) = self.pruning { + pruning.note_canonical(&hash, &mut commit); + } } self.prune(&mut commit); Ok(commit) @@ -255,7 +269,7 @@ impl StateDbSync { } let pinned = &self.pinned; - if pruning.next_hash().map_or(false, |h| pinned.contains(&h)) { + if pruning.next_hash().map_or(false, |h| pinned.contains_key(&h)) { break; } pruning.prune_one(commit); @@ -278,11 +292,23 @@ impl StateDbSync { } pub fn pin(&mut self, hash: &BlockHash) { - self.pinned.insert(hash.clone()); + trace!(target: "state-db", "Pinned block: {:?}", hash); + *self.pinned.entry(hash.clone()).or_default() += 1; } pub fn unpin(&mut self, hash: &BlockHash) { - self.pinned.remove(hash); + match self.pinned.entry(hash.clone()) { + Entry::Occupied(mut entry) => { + *entry.get_mut() -= 1; + if *entry.get() == 0 { + trace!(target: "state-db", "Unpinned block: {:?}", hash); + entry.remove(); + } else { + trace!(target: "state-db", "Releasing reference for {:?}", hash); + } + }, + Entry::Vacant(_) => {}, + } } pub fn get(&self, key: &Key, db: &D) -> Result, Error> diff --git a/substrate/core/state-db/src/noncanonical.rs b/substrate/core/state-db/src/noncanonical.rs index da957335ba..0d43389a0b 100644 --- a/substrate/core/state-db/src/noncanonical.rs +++ b/substrate/core/state-db/src/noncanonical.rs @@ -230,13 +230,20 @@ impl NonCanonicalOverlay { Ok(commit) } - fn discard_journals(&self, level_index: usize, discarded_journals: &mut Vec>, hash: &BlockHash) { + fn discard_journals( + &self, + level_index: usize, + discarded_journals: &mut Vec>, + discarded_blocks: &mut Vec, + hash: &BlockHash + ) { if let Some(level) = self.levels.get(level_index) { level.iter().for_each(|overlay| { let parent = self.parents.get(&overlay.hash).expect("there is a parent entry for each entry in levels; qed").clone(); if parent == *hash { discarded_journals.push(overlay.journal_key.clone()); - self.discard_journals(level_index + 1, discarded_journals, &overlay.hash); + discarded_blocks.push(overlay.hash.clone()); + self.discard_journals(level_index + 1, discarded_journals, discarded_blocks, &overlay.hash); } }); } @@ -268,7 +275,12 @@ impl NonCanonicalOverlay { /// Select a top-level root and canonicalized it. Discards all sibling subtrees and the root. /// Returns a set of changes that need to be added to the DB. - pub fn canonicalize(&mut self, hash: &BlockHash) -> Result, Error> { + pub fn canonicalize( + &mut self, + hash: &BlockHash, + pinned: &HashMap, + commit: &mut CommitSet, + ) -> Result<(), Error> { trace!(target: "state-db", "Canonicalizing {:?}", hash); let level = self.levels.get(self.pending_canonicalizations.len()).ok_or_else(|| Error::InvalidBlock)?; let index = level @@ -276,26 +288,40 @@ impl NonCanonicalOverlay { .position(|overlay| overlay.hash == *hash) .ok_or_else(|| Error::InvalidBlock)?; - let mut commit = CommitSet::default(); let mut discarded_journals = Vec::new(); - for (i, overlay) in level.into_iter().enumerate() { - if i == index { - // that's the one we need to canonicalize - commit.data.inserted = overlay.inserted.iter() - .map(|k| (k.clone(), self.values.get(k).expect("For each key in verlays there's a value in values").1.clone())) - .collect(); - commit.data.deleted = overlay.deleted.clone(); - } else { - self.discard_journals(self.pending_canonicalizations.len() + 1, &mut discarded_journals, &overlay.hash); + let mut discarded_blocks = Vec::new(); + for (i, overlay) in level.iter().enumerate() { + if i != index { + self.discard_journals( + self.pending_canonicalizations.len() + 1, + &mut discarded_journals, + &mut discarded_blocks, + &overlay.hash + ); } discarded_journals.push(overlay.journal_key.clone()); + discarded_blocks.push(overlay.hash.clone()); } + + for hash in discarded_blocks.into_iter() { + if pinned.contains_key(&hash) { + trace!(target: "state-db", "Refusing to discard pinned state {:?}", hash); + return Err(Error::DiscardingPinned) + } + } + + // get the one we need to canonicalize + let overlay = &level[index]; + commit.data.inserted.extend(overlay.inserted.iter() + .map(|k| (k.clone(), self.values.get(k).expect("For each key in overlays there's a value in values").1.clone()))); + commit.data.deleted.extend(overlay.deleted.clone()); + commit.meta.deleted.append(&mut discarded_journals); let canonicalized = (hash.clone(), self.front_block_number() + self.pending_canonicalizations.len() as u64); commit.meta.inserted.push((to_meta_key(LAST_CANONICAL, &()), canonicalized.encode())); trace!(target: "state-db", "Discarding {} records", commit.meta.deleted.len()); self.pending_canonicalizations.push(hash.clone()); - Ok(commit) + Ok(()) } fn apply_canonicalizations(&mut self) { @@ -385,10 +411,10 @@ impl NonCanonicalOverlay { #[cfg(test)] mod tests { - use std::io; + use std::{collections::HashMap, io}; use primitives::H256; use super::{NonCanonicalOverlay, to_journal_key}; - use crate::ChangeSet; + use crate::{ChangeSet, CommitSet}; use crate::test::{make_db, make_changeset}; fn contains(overlay: &NonCanonicalOverlay, key: u64) -> bool { @@ -409,7 +435,8 @@ mod tests { fn canonicalize_empty_panics() { let db = make_db(&[]); let mut overlay = NonCanonicalOverlay::::new(&db).unwrap(); - overlay.canonicalize::(&H256::default()).unwrap(); + let mut commit = CommitSet::default(); + overlay.canonicalize::(&H256::default(), &HashMap::default(), &mut commit).unwrap(); } #[test] @@ -453,7 +480,8 @@ mod tests { let db = make_db(&[]); let mut overlay = NonCanonicalOverlay::::new(&db).unwrap(); overlay.insert::(&h1, 1, &H256::default(), ChangeSet::default()).unwrap(); - overlay.canonicalize::(&h2).unwrap(); + let mut commit = CommitSet::default(); + overlay.canonicalize::(&h2, &HashMap::default(), &mut commit).unwrap(); } #[test] @@ -468,7 +496,8 @@ mod tests { assert_eq!(insertion.meta.inserted.len(), 2); assert_eq!(insertion.meta.deleted.len(), 0); db.commit(&insertion); - let finalization = overlay.canonicalize::(&h1).unwrap(); + let mut finalization = CommitSet::default(); + overlay.canonicalize::(&h1, &HashMap::default(), &mut finalization).unwrap(); assert_eq!(finalization.data.inserted.len(), changeset.inserted.len()); assert_eq!(finalization.data.deleted.len(), changeset.deleted.len()); assert_eq!(finalization.meta.inserted.len(), 1); @@ -501,7 +530,9 @@ mod tests { let mut overlay = NonCanonicalOverlay::::new(&db).unwrap(); db.commit(&overlay.insert::(&h1, 10, &H256::default(), make_changeset(&[3, 4], &[2])).unwrap()); db.commit(&overlay.insert::(&h2, 11, &h1, make_changeset(&[5], &[3])).unwrap()); - db.commit(&overlay.canonicalize::(&h1).unwrap()); + let mut commit = CommitSet::default(); + overlay.canonicalize::(&h1, &HashMap::default(), &mut commit).unwrap(); + db.commit(&commit); overlay.apply_pending(); assert_eq!(overlay.levels.len(), 1); @@ -526,7 +557,9 @@ mod tests { assert!(contains(&overlay, 5)); assert_eq!(overlay.levels.len(), 2); assert_eq!(overlay.parents.len(), 2); - db.commit(&overlay.canonicalize::(&h1).unwrap()); + let mut commit = CommitSet::default(); + overlay.canonicalize::(&h1, &HashMap::default(), &mut commit).unwrap(); + db.commit(&commit); assert!(contains(&overlay, 5)); assert_eq!(overlay.levels.len(), 2); assert_eq!(overlay.parents.len(), 2); @@ -535,7 +568,9 @@ mod tests { assert_eq!(overlay.parents.len(), 1); assert!(!contains(&overlay, 5)); assert!(contains(&overlay, 7)); - db.commit(&overlay.canonicalize::(&h2).unwrap()); + let mut commit = CommitSet::default(); + overlay.canonicalize::(&h2, &HashMap::default(), &mut commit).unwrap(); + db.commit(&commit); overlay.apply_pending(); assert_eq!(overlay.levels.len(), 0); assert_eq!(overlay.parents.len(), 0); @@ -552,7 +587,9 @@ mod tests { db.commit(&overlay.insert::(&h_1, 1, &H256::default(), c_1).unwrap()); db.commit(&overlay.insert::(&h_2, 1, &H256::default(), c_2).unwrap()); assert!(contains(&overlay, 1)); - db.commit(&overlay.canonicalize::(&h_1).unwrap()); + let mut commit = CommitSet::default(); + overlay.canonicalize::(&h_1, &HashMap::default(), &mut commit).unwrap(); + db.commit(&commit); assert!(contains(&overlay, 1)); overlay.apply_pending(); assert!(!contains(&overlay, 1)); @@ -569,8 +606,10 @@ mod tests { db.commit(&overlay.insert::(&h1, 1, &H256::default(), changeset.clone()).unwrap()); db.commit(&overlay.insert::(&h2, 2, &h1, changeset.clone()).unwrap()); overlay.apply_pending(); - db.commit(&overlay.canonicalize::(&h1).unwrap()); - db.commit(&overlay.canonicalize::(&h2).unwrap()); + let mut commit = CommitSet::default(); + overlay.canonicalize::(&h1, &HashMap::default(), &mut commit).unwrap(); + overlay.canonicalize::(&h2, &HashMap::default(), &mut commit).unwrap(); + db.commit(&commit); db.commit(&overlay.insert::(&h3, 3, &h2, changeset.clone()).unwrap()); overlay.apply_pending(); assert_eq!(overlay.levels.len(), 1); @@ -639,7 +678,9 @@ mod tests { assert_eq!(overlay.last_canonicalized, overlay2.last_canonicalized); // canonicalize 1. 2 and all its children should be discarded - db.commit(&overlay.canonicalize::(&h_1).unwrap()); + let mut commit = CommitSet::default(); + overlay.canonicalize::(&h_1, &HashMap::default(), &mut commit).unwrap(); + db.commit(&commit); overlay.apply_pending(); assert_eq!(overlay.levels.len(), 2); assert_eq!(overlay.parents.len(), 6); @@ -657,8 +698,15 @@ mod tests { assert!(db.get_meta(&to_journal_key(2, 2)).unwrap().is_none()); assert!(db.get_meta(&to_journal_key(2, 3)).unwrap().is_none()); + // check that discarding pinned state produces an error. + let mut commit = CommitSet::default(); + let pinned = vec![(h_1_1_1, 1)].into_iter().collect(); + assert!(overlay.canonicalize::(&h_1_2, &pinned, &mut commit).is_err()); + // canonicalize 1_2. 1_1 and all its children should be discarded - db.commit(&overlay.canonicalize::(&h_1_2).unwrap()); + let mut commit = CommitSet::default(); + overlay.canonicalize::(&h_1_2, &HashMap::default(), &mut commit).unwrap(); + db.commit(&commit); overlay.apply_pending(); assert_eq!(overlay.levels.len(), 1); assert_eq!(overlay.parents.len(), 3); @@ -673,7 +721,9 @@ mod tests { assert!(!overlay.have_block(&h_1_1_1)); // canonicalize 1_2_2 - db.commit(&overlay.canonicalize::(&h_1_2_2).unwrap()); + let mut commit = CommitSet::default(); + overlay.canonicalize::(&h_1_2_2, &HashMap::default(), &mut commit).unwrap(); + db.commit(&commit); overlay.apply_pending(); assert_eq!(overlay.levels.len(), 0); assert_eq!(overlay.parents.len(), 0); diff --git a/substrate/core/state-machine/src/backend.rs b/substrate/core/state-machine/src/backend.rs index fd143a553e..81529c6da3 100644 --- a/substrate/core/state-machine/src/backend.rs +++ b/substrate/core/state-machine/src/backend.rs @@ -106,7 +106,7 @@ pub trait Backend { } /// Try convert into trie backend. - fn try_into_trie_backend(self) -> Option>; + fn as_trie_backend(&mut self) -> Option<&TrieBackend>; /// Calculate the storage root, with given delta over what is already stored /// in the backend, and produce a "transaction" that can be used to commit. @@ -185,31 +185,33 @@ impl error::Error for Void { /// In-memory backend. Fully recomputes tries on each commit but useful for /// tests. -#[derive(Eq)] -pub struct InMemory { +pub struct InMemory { inner: HashMap>, HashMap, Vec>>, + trie: Option, H>>, _hasher: PhantomData, } -impl Default for InMemory { +impl Default for InMemory { fn default() -> Self { InMemory { inner: Default::default(), + trie: None, _hasher: PhantomData, } } } -impl Clone for InMemory { +impl Clone for InMemory { fn clone(&self) -> Self { InMemory { inner: self.inner.clone(), + trie: None, _hasher: PhantomData, } } } -impl PartialEq for InMemory { +impl PartialEq for InMemory { fn eq(&self, other: &Self) -> bool { self.inner.eq(&other.inner) } @@ -230,27 +232,29 @@ impl InMemory { } } -impl From>, HashMap, Vec>>> for InMemory { +impl From>, HashMap, Vec>>> for InMemory { fn from(inner: HashMap>, HashMap, Vec>>) -> Self { InMemory { inner: inner, + trie: None, _hasher: PhantomData, } } } -impl From, Vec>> for InMemory { +impl From, Vec>> for InMemory { fn from(inner: HashMap, Vec>) -> Self { let mut expanded = HashMap::new(); expanded.insert(None, inner); InMemory { inner: expanded, + trie: None, _hasher: PhantomData, } } } -impl From>, Vec, Option>)>> for InMemory { +impl From>, Vec, Option>)>> for InMemory { fn from(inner: Vec<(Option>, Vec, Option>)>) -> Self { let mut expanded: HashMap>, HashMap, Vec>> = HashMap::new(); for (child_key, key, value) in inner { @@ -365,16 +369,14 @@ impl Backend for InMemory { .collect() } - fn try_into_trie_backend( - self - )-> Option> { + fn as_trie_backend(&mut self)-> Option<&TrieBackend> { let mut mdb = MemoryDB::default(); let mut root = None; let mut new_child_roots = Vec::new(); let mut root_map = None; - for (storage_key, map) in self.inner { + for (storage_key, map) in &self.inner { if let Some(storage_key) = storage_key.as_ref() { - let ch = insert_into_memory_db::(&mut mdb, map.into_iter())?; + let ch = insert_into_memory_db::(&mut mdb, map.clone().into_iter())?; new_child_roots.push((storage_key.clone(), ch.as_ref().into())); } else { root_map = Some(map); @@ -384,14 +386,15 @@ impl Backend for InMemory { if let Some(map) = root_map.take() { root = Some(insert_into_memory_db::( &mut mdb, - map.into_iter().chain(new_child_roots.into_iter()) + map.clone().into_iter().chain(new_child_roots.into_iter()) )?); } let root = match root { Some(root) => root, None => insert_into_memory_db::(&mut mdb, ::std::iter::empty())?, }; - Some(TrieBackend::new(mdb, root)) + self.trie = Some(TrieBackend::new(mdb, root)); + self.trie.as_ref() } } diff --git a/substrate/core/state-machine/src/lib.rs b/substrate/core/state-machine/src/lib.rs index f16c3db14e..9f0e20ce01 100644 --- a/substrate/core/state-machine/src/lib.rs +++ b/substrate/core/state-machine/src/lib.rs @@ -669,7 +669,7 @@ impl<'a, H, N, B, T, O, Exec> StateMachine<'a, H, N, B, T, O, Exec> where /// Prove execution using the given state backend, overlayed changes, and call executor. pub fn prove_execution( - backend: B, + mut backend: B, overlay: &mut OverlayedChanges, exec: &Exec, method: &str, @@ -681,9 +681,9 @@ where Exec: CodeExecutor, H::Out: Ord + 'static, { - let trie_backend = backend.try_into_trie_backend() + let trie_backend = backend.as_trie_backend() .ok_or_else(|| Box::new(ExecutionError::UnableToGenerateProof) as Box)?; - prove_execution_on_trie_backend(&trie_backend, overlay, exec, method, call_data) + prove_execution_on_trie_backend(trie_backend, overlay, exec, method, call_data) } /// Prove execution using the given trie backend, overlayed changes, and call executor. @@ -778,7 +778,7 @@ where /// Generate storage read proof. pub fn prove_read( - backend: B, + mut backend: B, key: &[u8] ) -> Result<(Option>, Vec>), Box> where @@ -786,16 +786,16 @@ where H: Hasher, H::Out: Ord { - let trie_backend = backend.try_into_trie_backend() + let trie_backend = backend.as_trie_backend() .ok_or_else( ||Box::new(ExecutionError::UnableToGenerateProof) as Box )?; - prove_read_on_trie_backend(&trie_backend, key) + prove_read_on_trie_backend(trie_backend, key) } /// Generate child storage read proof. pub fn prove_child_read( - backend: B, + mut backend: B, storage_key: &[u8], key: &[u8], ) -> Result<(Option>, Vec>), Box> @@ -804,9 +804,9 @@ where H: Hasher, H::Out: Ord { - let trie_backend = backend.try_into_trie_backend() + let trie_backend = backend.as_trie_backend() .ok_or_else(|| Box::new(ExecutionError::UnableToGenerateProof) as Box)?; - prove_child_read_on_trie_backend(&trie_backend, storage_key, key) + prove_child_read_on_trie_backend(trie_backend, storage_key, key) } @@ -1100,7 +1100,8 @@ mod tests { b"abc".to_vec() => b"2".to_vec(), b"bbb".to_vec() => b"3".to_vec() ]; - let backend = InMemory::::from(initial).try_into_trie_backend().unwrap(); + let mut state = InMemory::::from(initial); + let backend = state.as_trie_backend().unwrap(); let mut overlay = OverlayedChanges { committed: map![ b"aba".to_vec() => OverlayedValue::from(Some(b"1312".to_vec())), @@ -1115,7 +1116,7 @@ mod tests { { let changes_trie_storage = InMemoryChangesTrieStorage::::new(); - let mut ext = Ext::new(&mut overlay, &backend, Some(&changes_trie_storage), NeverOffchainExt::new()); + let mut ext = Ext::new(&mut overlay, backend, Some(&changes_trie_storage), NeverOffchainExt::new()); ext.clear_prefix(b"ab"); } overlay.commit_prospective(); @@ -1136,12 +1137,13 @@ mod tests { #[test] fn set_child_storage_works() { - let backend = InMemory::::default().try_into_trie_backend().unwrap(); + let mut state = InMemory::::default(); + let backend = state.as_trie_backend().unwrap(); let changes_trie_storage = InMemoryChangesTrieStorage::::new(); let mut overlay = OverlayedChanges::default(); let mut ext = Ext::new( &mut overlay, - &backend, + backend, Some(&changes_trie_storage), NeverOffchainExt::new() ); diff --git a/substrate/core/state-machine/src/proving_backend.rs b/substrate/core/state-machine/src/proving_backend.rs index c23838bc21..f9472ed747 100644 --- a/substrate/core/state-machine/src/proving_backend.rs +++ b/substrate/core/state-machine/src/proving_backend.rs @@ -184,7 +184,7 @@ impl<'a, S, H> Backend for ProvingBackend<'a, S, H> self.backend.child_storage_root(storage_key, delta) } - fn try_into_trie_backend(self) -> Option> { + fn as_trie_backend(&mut self) -> Option<&TrieBackend> { None } } @@ -269,16 +269,16 @@ mod tests { fn proof_recorded_and_checked() { let contents = (0..64).map(|i| (None, vec![i], Some(vec![i]))).collect::>(); let in_memory = InMemory::::default(); - let in_memory = in_memory.update(contents); + let mut in_memory = in_memory.update(contents); let in_memory_root = in_memory.storage_root(::std::iter::empty()).0; (0..64).for_each(|i| assert_eq!(in_memory.storage(&[i]).unwrap().unwrap(), vec![i])); - let trie = in_memory.try_into_trie_backend().unwrap(); + let trie = in_memory.as_trie_backend().unwrap(); let trie_root = trie.storage_root(::std::iter::empty()).0; assert_eq!(in_memory_root, trie_root); (0..64).for_each(|i| assert_eq!(trie.storage(&[i]).unwrap().unwrap(), vec![i])); - let proving = ProvingBackend::new(&trie); + let proving = ProvingBackend::new(trie); assert_eq!(proving.storage(&[42]).unwrap().unwrap(), vec![42]); let proof = proving.extract_proof(); @@ -302,7 +302,7 @@ mod tests { .chain((10..15).map(|i| (Some(own2.clone()), vec![i], Some(vec![i])))) .collect::>(); let in_memory = InMemory::::default(); - let in_memory = in_memory.update(contents); + let mut in_memory = in_memory.update(contents); let in_memory_root = in_memory.full_storage_root::<_, Vec<_>, _>( ::std::iter::empty(), in_memory.child_storage_keys().map(|k|(k.to_vec(), Vec::new())) @@ -320,7 +320,7 @@ mod tests { vec![i] )); - let trie = in_memory.try_into_trie_backend().unwrap(); + let trie = in_memory.as_trie_backend().unwrap(); let trie_root = trie.storage_root(::std::iter::empty()).0; assert_eq!(in_memory_root, trie_root); (0..64).for_each(|i| assert_eq!( @@ -328,7 +328,7 @@ mod tests { vec![i] )); - let proving = ProvingBackend::new(&trie); + let proving = ProvingBackend::new(trie); assert_eq!(proving.storage(&[42]).unwrap().unwrap(), vec![42]); let proof = proving.extract_proof(); @@ -343,7 +343,7 @@ mod tests { assert_eq!(proof_check.storage(&[41]).unwrap().unwrap(), vec![41]); assert_eq!(proof_check.storage(&[64]).unwrap(), None); - let proving = ProvingBackend::new(&trie); + let proving = ProvingBackend::new(trie); assert_eq!(proving.child_storage(&own1[..], &[64]), Ok(Some(vec![64]))); let proof = proving.extract_proof(); diff --git a/substrate/core/state-machine/src/trie_backend.rs b/substrate/core/state-machine/src/trie_backend.rs index 00c0aca006..0c57cf3682 100644 --- a/substrate/core/state-machine/src/trie_backend.rs +++ b/substrate/core/state-machine/src/trie_backend.rs @@ -179,7 +179,7 @@ impl, H: Hasher> Backend for TrieBackend where (root, is_default, write_overlay) } - fn try_into_trie_backend(self) -> Option> { + fn as_trie_backend(&mut self) -> Option<&TrieBackend> { Some(self) } }