// Copyright 2017-2019 Parity Technologies (UK) Ltd. // This file is part of Substrate. // Substrate is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // Substrate is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . //! Test implementation for Externalities. use std::{collections::HashMap, any::{Any, TypeId}}; use hash_db::Hasher; use crate::{ backend::{InMemory, Backend}, OverlayedChanges, changes_trie::{ InMemoryStorage as ChangesTrieInMemoryStorage, BlockNumber as ChangesTrieBlockNumber, }, ext::Ext, }; use primitives::{ storage::{ well_known_keys::{CHANGES_TRIE_CONFIG, CODE, HEAP_PAGES, is_child_storage_key} }, hash::H256, Blake2Hasher, }; use codec::Encode; use externalities::{Extensions, Extension}; type StorageTuple = (HashMap, Vec>, HashMap, HashMap, Vec>>); /// Simple HashMap-based Externalities impl. pub struct TestExternalities=Blake2Hasher, N: ChangesTrieBlockNumber=u64> { overlay: OverlayedChanges, backend: InMemory, changes_trie_storage: ChangesTrieInMemoryStorage, extensions: Extensions, } impl, N: ChangesTrieBlockNumber> TestExternalities { /// Get externalities implementation. pub fn ext(&mut self) -> Ext, ChangesTrieInMemoryStorage> { Ext::new( &mut self.overlay, &self.backend, Some(&self.changes_trie_storage), Some(&mut self.extensions), ) } /// Create a new instance of `TestExternalities` with storage. pub fn new(storage: StorageTuple) -> Self { Self::new_with_code(&[], storage) } /// Create a new instance of `TestExternalities` with code and storage. pub fn new_with_code(code: &[u8], mut storage: StorageTuple) -> Self { let mut overlay = OverlayedChanges::default(); assert!(storage.0.keys().all(|key| !is_child_storage_key(key))); assert!(storage.1.keys().all(|key| is_child_storage_key(key))); super::set_changes_trie_config( &mut overlay, storage.0.get(&CHANGES_TRIE_CONFIG.to_vec()).cloned(), false, ).expect("changes trie configuration is correct in test env; qed"); storage.0.insert(HEAP_PAGES.to_vec(), 8u64.encode()); storage.0.insert(CODE.to_vec(), code.to_vec()); let backend: HashMap<_, _> = storage.1.into_iter() .map(|(keyspace, map)| (Some(keyspace), map)) .chain(Some((None, storage.0)).into_iter()) .collect(); TestExternalities { overlay, changes_trie_storage: ChangesTrieInMemoryStorage::new(), backend: backend.into(), extensions: Default::default(), } } /// Insert key/value into backend pub fn insert(&mut self, k: Vec, v: Vec) { self.backend = self.backend.update(vec![(None, k, Some(v))]); } /// Registers the given extension for this instance. pub fn register_extension(&mut self, ext: E) { self.extensions.register(ext); } /// Get mutable reference to changes trie storage. pub fn changes_trie_storage(&mut self) -> &mut ChangesTrieInMemoryStorage { &mut self.changes_trie_storage } /// Return a new backend with all pending value. pub fn commit_all(&self) -> InMemory { let top = self.overlay.committed.top.clone().into_iter() .chain(self.overlay.prospective.top.clone().into_iter()) .map(|(k, v)| (None, k, v.value)); let children = self.overlay.committed.children.clone().into_iter() .chain(self.overlay.prospective.children.clone().into_iter()) .flat_map(|(keyspace, map)| { map.into_iter() .map(|(k, v)| (Some(keyspace.clone()), k, v.value)) .collect::>() }); self.backend.update(top.chain(children).collect()) } /// Execute the given closure while `self` is set as externalities. /// /// Returns the result of the given closure. pub fn execute_with(&mut self, execute: impl FnOnce() -> R) -> R { let mut ext = self.ext(); externalities::set_and_run_with_externalities(&mut ext, execute) } } impl, N: ChangesTrieBlockNumber> std::fmt::Debug for TestExternalities { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { write!(f, "overlay: {:?}\nbackend: {:?}", self.overlay, self.backend.pairs()) } } impl, N: ChangesTrieBlockNumber> PartialEq for TestExternalities { /// This doesn't test if they are in the same state, only if they contains the /// same data at this state fn eq(&self, other: &TestExternalities) -> bool { self.commit_all().eq(&other.commit_all()) } } impl, N: ChangesTrieBlockNumber> Default for TestExternalities { fn default() -> Self { Self::new(Default::default()) } } impl, N: ChangesTrieBlockNumber> From for TestExternalities { fn from(storage: StorageTuple) -> Self { Self::new(storage) } } impl externalities::ExtensionStore for TestExternalities where H: Hasher, N: ChangesTrieBlockNumber, { fn extension_by_type_id(&mut self, type_id: TypeId) -> Option<&mut dyn Any> { self.extensions.get_mut(type_id) } } #[cfg(test)] mod tests { use super::*; use primitives::traits::Externalities; use hex_literal::hex; #[test] fn commit_should_work() { let mut ext = TestExternalities::::default(); let mut ext = ext.ext(); ext.set_storage(b"doe".to_vec(), b"reindeer".to_vec()); ext.set_storage(b"dog".to_vec(), b"puppy".to_vec()); ext.set_storage(b"dogglesworth".to_vec(), b"cat".to_vec()); const ROOT: [u8; 32] = hex!("2a340d3dfd52f5992c6b117e9e45f479e6da5afffafeb26ab619cf137a95aeb8"); assert_eq!(&ext.storage_root()[..], &ROOT); } #[test] fn set_and_retrieve_code() { let mut ext = TestExternalities::::default(); let mut ext = ext.ext(); let code = vec![1, 2, 3]; ext.set_storage(CODE.to_vec(), code.clone()); assert_eq!(&ext.storage(CODE).unwrap(), &code); } #[test] fn check_send() { fn assert_send() {} assert_send::>(); } }