Skip to main content

sp_domains/
storage.rs

1#[cfg(not(feature = "std"))]
2extern crate alloc;
3
4use crate::{
5    DomainId, PermissionedActionAllowedBy, evm_chain_id_storage_key,
6    evm_contract_creation_allowed_by_storage_key, self_domain_id_storage_key,
7};
8#[cfg(not(feature = "std"))]
9use alloc::vec::Vec;
10use domain_runtime_primitives::{EVMChainId, EthereumAccountId};
11use hash_db::Hasher;
12use parity_scale_codec::{Codec, Decode, Encode};
13use scale_info::TypeInfo;
14use sp_core::storage::{ChildInfo, well_known_keys};
15#[cfg(feature = "std")]
16use sp_core::storage::{Storage, StorageChild};
17use sp_runtime::StateVersion;
18use sp_state_machine::{Backend, TrieBackend, TrieBackendBuilder};
19use sp_std::collections::btree_map::BTreeMap;
20use sp_trie::{LayoutV0, MemoryDB, RandomState, empty_trie_root};
21
22/// Create a new empty instance of in-memory backend.
23///
24/// NOTE: this function is port from `sp_state_machine::in_memory_backend::new_in_mem` which is
25/// only exported behind `#[cfg(not(substrate_runtime))]` but we need to use it in runtime context.
26/// We must use `sp_trie::RandomState` explicitly so the `TrieBackendStorage` impl matches.
27fn new_in_mem<H>() -> TrieBackend<MemoryDB<H>, H>
28where
29    H: Hasher,
30    H::Out: Codec + Ord,
31{
32    let db = MemoryDB::with_hasher(RandomState::default());
33    // V1 is same as V0 for an empty trie.
34    TrieBackendBuilder::new(db, empty_trie_root::<LayoutV0<H>>()).build()
35}
36
37// NOTE: this is port from `sp_core::storage::StorageKey` with `TypeInfo` supported
38#[derive(Default, Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Encode, Decode, TypeInfo)]
39pub struct StorageKey(pub Vec<u8>);
40
41// NOTE: this is port from `sp_core::storage::StorageData` with `TypeInfo` supported
42#[derive(Default, Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Encode, Decode, TypeInfo)]
43pub struct StorageData(pub Vec<u8>);
44
45type GenesisStorage = BTreeMap<StorageKey, StorageData>;
46
47/// Raw storage content for genesis block
48#[derive(Default, Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Encode, Decode, TypeInfo)]
49pub struct RawGenesis {
50    top: GenesisStorage,
51    children_default: BTreeMap<StorageKey, GenesisStorage>,
52}
53
54impl RawGenesis {
55    pub fn set_domain_id(&mut self, domain_id: DomainId) {
56        let _ = self.top.insert(
57            self_domain_id_storage_key(),
58            StorageData(domain_id.encode()),
59        );
60    }
61
62    pub fn set_evm_chain_id(&mut self, chain_id: EVMChainId) {
63        let _ = self
64            .top
65            .insert(evm_chain_id_storage_key(), StorageData(chain_id.encode()));
66    }
67
68    pub fn set_evm_contract_creation_allowed_by(
69        &mut self,
70        contract_creation_allowed_by: &PermissionedActionAllowedBy<EthereumAccountId>,
71    ) {
72        let _ = self.top.insert(
73            evm_contract_creation_allowed_by_storage_key(),
74            StorageData(contract_creation_allowed_by.encode()),
75        );
76    }
77
78    pub fn set_top_storages(&mut self, storages: Vec<(StorageKey, StorageData)>) {
79        for (k, v) in storages {
80            let _ = self.top.insert(k, v);
81        }
82    }
83
84    fn set_runtime_code(&mut self, code: Vec<u8>) {
85        let _ = self.top.insert(
86            StorageKey(well_known_keys::CODE.to_vec()),
87            StorageData(code),
88        );
89    }
90
91    pub fn get_runtime_code(&self) -> Option<&[u8]> {
92        self.top
93            .get(&StorageKey(well_known_keys::CODE.to_vec()))
94            .map(|sd| sd.0.as_ref())
95    }
96
97    pub fn take_runtime_code(&mut self) -> Option<Vec<u8>> {
98        self.top
99            .remove(&StorageKey(well_known_keys::CODE.to_vec()))
100            .map(|sd| sd.0)
101    }
102
103    pub fn state_root<H>(&self, state_version: StateVersion) -> H::Out
104    where
105        H: Hasher,
106        H::Out: Codec + Ord,
107    {
108        let backend = new_in_mem::<H>();
109
110        // NOTE: the `(k, v)` of `children_default` are iterated separately because the
111        // `full_storage_root` required `&ChildInfo` as input but if we simply map `k`
112        // to `&ChildInfo` it will fail due to temporary value can't live long enough.
113        let child_infos: Vec<_> = self
114            .children_default
115            .keys()
116            .map(|k| ChildInfo::new_default(k.0.as_slice()))
117            .collect();
118        let child_delta = child_infos.iter().zip(
119            self.children_default
120                .values()
121                .map(|v| v.iter().map(|(k, v)| (&k.0[..], Some(&v.0[..])))),
122        );
123
124        let (root, _) = backend.full_storage_root(
125            self.top.iter().map(|(k, v)| (&k.0[..], Some(&v.0[..]))),
126            child_delta,
127            state_version,
128        );
129
130        root
131    }
132
133    pub fn dummy(code: Vec<u8>) -> Self {
134        let mut raw_genesis = Self::default();
135        raw_genesis.set_runtime_code(code);
136        raw_genesis
137    }
138}
139
140#[cfg(feature = "std")]
141impl RawGenesis {
142    /// Construct `RawGenesis` from a given storage
143    //
144    /// NOTE: This function is part from `sc-chain-spec::GenesisSource::resolve`
145    pub fn from_storage(storage: Storage) -> Self {
146        let top = storage
147            .top
148            .into_iter()
149            .map(|(k, v)| (StorageKey(k), StorageData(v)))
150            .collect();
151
152        let children_default = storage
153            .children_default
154            .into_iter()
155            .map(|(k, child)| {
156                (
157                    StorageKey(k),
158                    child
159                        .data
160                        .into_iter()
161                        .map(|(k, v)| (StorageKey(k), StorageData(v)))
162                        .collect(),
163                )
164            })
165            .collect();
166
167        RawGenesis {
168            top,
169            children_default,
170        }
171    }
172
173    /// Convert `RawGenesis` to storage, the opposite of `from_storage`
174    //
175    /// NOTE: This function is part from `<sc-chain-spec::ChainSpec as BuildStorage>::assimilate_storage`
176    pub fn into_storage(self) -> Storage {
177        let RawGenesis {
178            top: map,
179            children_default: children_map,
180        } = self;
181        let mut storage = Storage::default();
182
183        storage.top.extend(map.into_iter().map(|(k, v)| (k.0, v.0)));
184
185        children_map.into_iter().for_each(|(k, v)| {
186            let child_info = ChildInfo::new_default(k.0.as_slice());
187            storage
188                .children_default
189                .entry(k.0)
190                .or_insert_with(|| StorageChild {
191                    data: Default::default(),
192                    child_info,
193                })
194                .data
195                .extend(v.into_iter().map(|(k, v)| (k.0, v.0)));
196        });
197
198        storage
199    }
200}