domain_block_builder/
custom_api.rs

1//! Custom API that is efficient to collect storage roots.
2
3use hash_db::{HashDB, Hasher, Prefix};
4use parity_scale_codec::{Codec, Decode, Encode};
5use sc_client_api::{backend, ExecutorProvider, StateBackend};
6use sp_core::offchain::OffchainOverlayedChange;
7use sp_core::traits::{CallContext, CodeExecutor, FetchRuntimeCode};
8use sp_inherents::InherentData;
9use sp_runtime::traits::{Block as BlockT, HashingFor, NumberFor};
10use sp_runtime::{ApplyExtrinsicResult, ExtrinsicInclusionMode, TransactionOutcome};
11use sp_state_machine::backend::AsTrieBackend;
12use sp_state_machine::{
13    BackendTransaction, DBValue, IndexOperation, OverlayedChanges, StateMachine, StorageChanges,
14    StorageKey, StorageValue, TrieBackend, TrieBackendBuilder, TrieBackendStorage,
15};
16use std::borrow::Cow;
17use std::collections::HashMap;
18use std::marker::PhantomData;
19use std::sync::Arc;
20use subspace_runtime_primitives::ExtrinsicFor;
21
22type TrieBackendStorageFor<State, Block> =
23    <State as StateBackend<HashingFor<Block>>>::TrieBackendStorage;
24
25pub(crate) type TrieDeltaBackendFor<'a, State, Block> = TrieBackend<
26    DeltaBackend<'a, TrieBackendStorageFor<State, Block>, HashingFor<Block>>,
27    HashingFor<Block>,
28>;
29
30struct MappedStorageChanges<H: Hasher> {
31    /// All changes to the main storage.
32    ///
33    /// A value of `None` means that it was deleted.
34    pub main_storage_changes: HashMap<StorageKey, Option<StorageValue>>,
35    /// All changes to the child storages.
36    pub child_storage_changes: HashMap<StorageKey, HashMap<StorageKey, Option<StorageValue>>>,
37    /// Offchain state changes to write to the offchain database.
38    pub offchain_storage_changes: HashMap<(Vec<u8>, Vec<u8>), OffchainOverlayedChange>,
39    /// A transaction for the backend that contains all changes from
40    /// [`main_storage_changes`](StorageChanges::main_storage_changes) and from
41    /// [`child_storage_changes`](StorageChanges::child_storage_changes) but excluding
42    /// [`offchain_storage_changes`](StorageChanges::offchain_storage_changes).
43    pub transaction: BackendTransaction<H>,
44    /// The storage root after applying the transaction.
45    pub transaction_storage_root: H::Out,
46    /// Changes to the transaction index,
47    pub transaction_index_changes: Vec<IndexOperation>,
48}
49
50#[derive(Clone)]
51struct RuntimeCode {
52    code: Vec<u8>,
53    heap_pages: Option<u64>,
54    hash: Vec<u8>,
55}
56
57impl<H: Hasher> From<MappedStorageChanges<H>> for StorageChanges<H> {
58    fn from(value: MappedStorageChanges<H>) -> Self {
59        let MappedStorageChanges {
60            main_storage_changes,
61            child_storage_changes,
62            offchain_storage_changes,
63            transaction,
64            transaction_storage_root,
65            transaction_index_changes,
66        } = value;
67        StorageChanges {
68            main_storage_changes: main_storage_changes.into_iter().collect(),
69            child_storage_changes: child_storage_changes
70                .into_iter()
71                .map(|(k, v)| (k, v.into_iter().collect()))
72                .collect(),
73            offchain_storage_changes: offchain_storage_changes.into_iter().collect(),
74            transaction,
75            transaction_storage_root,
76            transaction_index_changes,
77        }
78    }
79}
80
81impl<H: Hasher> From<StorageChanges<H>> for MappedStorageChanges<H> {
82    fn from(value: StorageChanges<H>) -> Self {
83        let StorageChanges {
84            main_storage_changes,
85            child_storage_changes,
86            offchain_storage_changes,
87            transaction,
88            transaction_storage_root,
89            transaction_index_changes,
90        } = value;
91        MappedStorageChanges {
92            main_storage_changes: main_storage_changes.into_iter().collect(),
93            child_storage_changes: child_storage_changes
94                .into_iter()
95                .map(|(k, v)| (k, v.into_iter().collect()))
96                .collect(),
97            offchain_storage_changes: offchain_storage_changes.into_iter().collect(),
98            transaction,
99            transaction_storage_root,
100            transaction_index_changes,
101        }
102    }
103}
104
105impl<H: Hasher> MappedStorageChanges<H> {
106    fn consolidate_storage_changes(&mut self, changes: StorageChanges<H>) -> H::Out {
107        let StorageChanges {
108            main_storage_changes,
109            child_storage_changes,
110            offchain_storage_changes,
111            transaction,
112            transaction_storage_root,
113            mut transaction_index_changes,
114        } = changes;
115        self.main_storage_changes.extend(main_storage_changes);
116        child_storage_changes
117            .into_iter()
118            .for_each(|(k, v)| self.child_storage_changes.entry(k).or_default().extend(v));
119        self.offchain_storage_changes
120            .extend(offchain_storage_changes);
121        self.transaction.consolidate(transaction);
122        self.transaction.purge();
123        self.transaction_index_changes
124            .append(&mut transaction_index_changes);
125        let previous_storage_root = self.transaction_storage_root;
126        self.transaction_storage_root = transaction_storage_root;
127        previous_storage_root
128    }
129}
130
131/// Storage changes are the collected throughout the execution.
132pub struct CollectedStorageChanges<H: Hasher> {
133    /// Storage changes that are captured during the execution.
134    /// Can be used for block import.
135    pub storage_changes: StorageChanges<H>,
136    /// Intermediate storage roots in between the execution.
137    pub intermediate_roots: Vec<H::Out>,
138}
139
140/// Create a new trie backend with memory DB delta changes.
141///
142/// This can be used to verify any extrinsic-specific execution on the combined state of `backend`
143/// and `delta`.
144pub fn create_delta_backend<'a, S, H>(
145    backend: &'a TrieBackend<S, H>,
146    delta: &'a BackendTransaction<H>,
147    post_delta_root: H::Out,
148) -> TrieBackend<DeltaBackend<'a, S, H>, H>
149where
150    S: 'a + TrieBackendStorage<H>,
151    H: 'a + Hasher,
152    H::Out: Codec,
153{
154    create_delta_backend_with_maybe_delta(backend, Some(delta), post_delta_root)
155}
156
157fn create_delta_backend_with_maybe_delta<'a, S, H>(
158    backend: &'a TrieBackend<S, H>,
159    maybe_delta: Option<&'a BackendTransaction<H>>,
160    post_delta_root: H::Out,
161) -> TrieBackend<DeltaBackend<'a, S, H>, H>
162where
163    S: 'a + TrieBackendStorage<H>,
164    H: 'a + Hasher,
165    H::Out: Codec,
166{
167    let essence = backend.essence();
168    let delta_backend = DeltaBackend {
169        backend: essence.backend_storage(),
170        delta: maybe_delta,
171        _phantom: PhantomData::<H>,
172    };
173    TrieBackendBuilder::new(delta_backend, post_delta_root).build()
174}
175
176/// DeltaBackend provides the TrieBackend using main backend and some delta changes
177/// that are not part of the main backend.
178pub struct DeltaBackend<'a, S, H>
179where
180    S: 'a + TrieBackendStorage<H>,
181    H: 'a + Hasher,
182{
183    backend: &'a S,
184    delta: Option<&'a BackendTransaction<H>>,
185    _phantom: PhantomData<H>,
186}
187
188impl<'a, S, H> TrieBackendStorage<H> for DeltaBackend<'a, S, H>
189where
190    S: 'a + TrieBackendStorage<H>,
191    H: 'a + Hasher,
192{
193    fn get(&self, key: &H::Out, prefix: Prefix) -> Result<Option<DBValue>, String> {
194        if let Some(db) = &self.delta
195            && let Some(v) = HashDB::get(*db, key, prefix)
196        {
197            Ok(Some(v))
198        } else {
199            Ok(self.backend.get(key, prefix)?)
200        }
201    }
202}
203
204pub(crate) struct TrieBackendApi<Client, Block: BlockT, Backend: backend::Backend<Block>, Exec> {
205    parent_hash: Block::Hash,
206    parent_number: NumberFor<Block>,
207    client: Arc<Client>,
208    state: Backend::State,
209    executor: Arc<Exec>,
210    maybe_storage_changes: Option<MappedStorageChanges<HashingFor<Block>>>,
211    intermediate_roots: Vec<Block::Hash>,
212    runtime_code: RuntimeCode,
213}
214
215impl<Client, Block, Backend, Exec> FetchRuntimeCode for TrieBackendApi<Client, Block, Backend, Exec>
216where
217    Block: BlockT,
218    Backend: backend::Backend<Block>,
219{
220    fn fetch_runtime_code(&self) -> Option<Cow<[u8]>> {
221        Some(Cow::from(&self.runtime_code.code))
222    }
223}
224
225impl<Client, Block, Backend, Exec> TrieBackendApi<Client, Block, Backend, Exec>
226where
227    Block: BlockT,
228    Backend: backend::Backend<Block>,
229    Client: ExecutorProvider<Block>,
230    Exec: CodeExecutor,
231{
232    pub(crate) fn new(
233        parent_hash: Block::Hash,
234        parent_number: NumberFor<Block>,
235        client: Arc<Client>,
236        backend: Arc<Backend>,
237        executor: Arc<Exec>,
238    ) -> Result<Self, sp_blockchain::Error> {
239        let state = backend.state_at(parent_hash)?;
240        let trie_backend = state.as_trie_backend();
241        let state_runtime_code = sp_state_machine::backend::BackendRuntimeCode::new(trie_backend);
242        let sp_core::traits::RuntimeCode {
243            code_fetcher,
244            heap_pages,
245            hash,
246        } = state_runtime_code
247            .runtime_code()
248            .map_err(sp_blockchain::Error::RuntimeCode)?;
249        let runtime_code = RuntimeCode {
250            code: code_fetcher
251                .fetch_runtime_code()
252                .map(|c| c.to_vec())
253                .ok_or(sp_blockchain::Error::RuntimeCode("missing runtime code"))?,
254            heap_pages,
255            hash,
256        };
257        Ok(Self {
258            parent_hash,
259            parent_number,
260            client,
261            state,
262            executor,
263            maybe_storage_changes: None,
264            intermediate_roots: vec![],
265            runtime_code,
266        })
267    }
268
269    fn runtime_code(&self) -> sp_core::traits::RuntimeCode {
270        sp_core::traits::RuntimeCode {
271            code_fetcher: self,
272            heap_pages: self.runtime_code.heap_pages,
273            hash: self.runtime_code.hash.clone(),
274        }
275    }
276
277    fn call_function<R: Decode>(
278        &self,
279        method: &str,
280        call_data: Vec<u8>,
281        trie_backend: &TrieDeltaBackendFor<Backend::State, Block>,
282        overlayed_changes: &mut OverlayedChanges<HashingFor<Block>>,
283    ) -> Result<R, sp_blockchain::Error> {
284        let mut extensions = self
285            .client
286            .execution_extensions()
287            .extensions(self.parent_hash, self.parent_number);
288
289        let runtime_code = self.runtime_code();
290
291        let result = StateMachine::<_, _, _>::new(
292            trie_backend,
293            overlayed_changes,
294            &*self.executor,
295            method,
296            call_data.as_slice(),
297            &mut extensions,
298            &runtime_code,
299            CallContext::Onchain,
300        )
301        .set_parent_hash(self.parent_hash)
302        .execute()?;
303
304        R::decode(&mut result.as_slice())
305            .map_err(|err| sp_blockchain::Error::CallResultDecode("failed to decode Result", err))
306    }
307
308    fn consolidate_storage_changes(&mut self, changes: StorageChanges<HashingFor<Block>>) {
309        let changes = if let Some(mut mapped_changes) = self.maybe_storage_changes.take() {
310            let previous_root = mapped_changes.consolidate_storage_changes(changes);
311            self.intermediate_roots.push(previous_root);
312            mapped_changes
313        } else {
314            changes.into()
315        };
316        self.maybe_storage_changes = Some(changes)
317    }
318
319    pub(crate) fn initialize_block(
320        &self,
321        header: Block::Header,
322        backend: &TrieDeltaBackendFor<Backend::State, Block>,
323        overlayed_changes: &mut OverlayedChanges<HashingFor<Block>>,
324    ) -> Result<ExtrinsicInclusionMode, sp_blockchain::Error> {
325        let call_data = header.encode();
326        self.call_function(
327            "Core_initialize_block",
328            call_data,
329            backend,
330            overlayed_changes,
331        )
332    }
333
334    pub(crate) fn apply_extrinsic(
335        &self,
336        extrinsic: ExtrinsicFor<Block>,
337        backend: &TrieDeltaBackendFor<Backend::State, Block>,
338        overlayed_changes: &mut OverlayedChanges<HashingFor<Block>>,
339    ) -> Result<ApplyExtrinsicResult, sp_blockchain::Error> {
340        let call_data = extrinsic.encode();
341        self.call_function(
342            "BlockBuilder_apply_extrinsic",
343            call_data,
344            backend,
345            overlayed_changes,
346        )
347    }
348
349    pub(crate) fn finalize_block(
350        &self,
351        backend: &TrieDeltaBackendFor<Backend::State, Block>,
352        overlayed_changes: &mut OverlayedChanges<HashingFor<Block>>,
353    ) -> Result<Block::Header, sp_blockchain::Error> {
354        self.call_function(
355            "BlockBuilder_finalize_block",
356            vec![],
357            backend,
358            overlayed_changes,
359        )
360    }
361
362    pub(crate) fn inherent_extrinsics(
363        &self,
364        inherent: InherentData,
365        backend: &TrieDeltaBackendFor<Backend::State, Block>,
366        overlayed_changes: &mut OverlayedChanges<HashingFor<Block>>,
367    ) -> Result<Vec<ExtrinsicFor<Block>>, sp_blockchain::Error> {
368        let call_data = inherent.encode();
369        self.call_function(
370            "BlockBuilder_inherent_extrinsics",
371            call_data,
372            backend,
373            overlayed_changes,
374        )
375    }
376
377    /// Collect storage changes returns the storage changes and intermediate roots collected so far.
378    /// The changes are reset after this call.
379    /// Could return None if there were no execution done.
380    pub(crate) fn collect_storage_changes(
381        &mut self,
382    ) -> Option<CollectedStorageChanges<HashingFor<Block>>> {
383        let mut intermediate_roots = self.intermediate_roots.drain(..).collect::<Vec<_>>();
384        // we did not add the last transaction storage root.
385        // include that and then return
386        let maybe_storage_changes = self.maybe_storage_changes.take();
387        if let Some(storage_changes) = maybe_storage_changes {
388            intermediate_roots.push(storage_changes.transaction_storage_root);
389            Some(CollectedStorageChanges {
390                storage_changes: storage_changes.into(),
391                intermediate_roots,
392            })
393        } else {
394            None
395        }
396    }
397
398    pub(crate) fn execute_in_transaction<F, R>(
399        &mut self,
400        call: F,
401    ) -> Result<R, sp_blockchain::Error>
402    where
403        F: FnOnce(
404            &Self,
405            &TrieDeltaBackendFor<Backend::State, Block>,
406            &mut OverlayedChanges<HashingFor<Block>>,
407        ) -> TransactionOutcome<Result<R, sp_blockchain::Error>>,
408        R: Decode,
409    {
410        let trie_backend = self.state.as_trie_backend();
411        let mut overlayed_changes = OverlayedChanges::default();
412        let (state_root, delta) = if let Some(changes) = &self.maybe_storage_changes {
413            (changes.transaction_storage_root, Some(&changes.transaction))
414        } else {
415            (*trie_backend.root(), None)
416        };
417        let trie_delta_backend =
418            create_delta_backend_with_maybe_delta(trie_backend, delta, state_root);
419
420        let outcome = call(self, &trie_delta_backend, &mut overlayed_changes);
421        match outcome {
422            TransactionOutcome::Commit(result) => {
423                let state_version = sp_core::storage::StateVersion::V1;
424                let storage_changes = overlayed_changes
425                    .drain_storage_changes(&trie_delta_backend, state_version)
426                    .map_err(sp_blockchain::Error::StorageChanges)?;
427                self.consolidate_storage_changes(storage_changes);
428                result
429            }
430            TransactionOutcome::Rollback(result) => result,
431        }
432    }
433}