domain_block_builder/
lib.rs

1// This file is part of Substrate.
2
3// Copyright (C) 2017-2021 Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
5
6// This program is free software: you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation, either version 3 of the License, or
9// (at your option) any later version.
10
11// This program is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14// GNU General Public License for more details.
15
16// You should have received a copy of the GNU General Public License
17// along with this program. If not, see <https://www.gnu.org/licenses/>.
18
19//! Substrate block builder
20//!
21//! This crate provides the [`BlockBuilder`] utility and the corresponding runtime api
22//! [`BlockBuilder`](sp_block_builder::BlockBuilder).
23//!
24//! The block builder utility is used in the node as an abstraction over the runtime api to
25//! initialize a block, to push extrinsics and to finalize a block.
26
27#![warn(missing_docs)]
28
29mod custom_api;
30mod genesis_block_builder;
31
32use crate::custom_api::{TrieBackendApi, TrieDeltaBackendFor};
33pub use custom_api::{CollectedStorageChanges, DeltaBackend, create_delta_backend};
34pub use genesis_block_builder::CustomGenesisBlockBuilder;
35use parity_scale_codec::Encode;
36use sc_client_api::{ExecutorProvider, backend};
37use sp_api::{ProvideRuntimeApi, TransactionOutcome};
38pub use sp_block_builder::BlockBuilder as BlockBuilderApi;
39use sp_blockchain::{ApplyExtrinsicFailed, Error};
40use sp_core::traits::CodeExecutor;
41use sp_runtime::Digest;
42use sp_runtime::traits::{Block as BlockT, Hash, HashingFor, Header as HeaderT, NumberFor, One};
43use sp_state_machine::OverlayedChanges;
44use std::collections::VecDeque;
45use std::sync::Arc;
46
47/// A block that was build by [`BlockBuilder`] plus some additional data.
48///
49/// This additional data includes the `storage_changes`, these changes can be applied to the
50/// backend to get the state of the block.
51pub struct BuiltBlock<Block: BlockT> {
52    /// The actual block that was build.
53    pub block: Block,
54    /// The changes that need to be applied to the backend to get the state of the build block.
55    pub storage_changes: CollectedStorageChanges<HashingFor<Block>>,
56}
57
58impl<Block: BlockT> BuiltBlock<Block> {
59    /// Convert into the inner values.
60    pub fn into_inner(self) -> (Block, CollectedStorageChanges<HashingFor<Block>>) {
61        (self.block, self.storage_changes)
62    }
63}
64
65/// Utility for building new (valid) blocks from a stream of extrinsics.
66pub struct BlockBuilder<Client, Block: BlockT, Backend: backend::Backend<Block>, Exec> {
67    extrinsics: VecDeque<Block::Extrinsic>,
68    api: TrieBackendApi<Client, Block, Backend, Exec>,
69}
70
71impl<Client, Block, Backend, Exec> BlockBuilder<Client, Block, Backend, Exec>
72where
73    Block: BlockT,
74    Client: ProvideRuntimeApi<Block> + ExecutorProvider<Block>,
75    Client::Api: BlockBuilderApi<Block>,
76    Backend: backend::Backend<Block>,
77    Exec: CodeExecutor,
78{
79    /// Create a new instance of builder based on the given `parent_hash` and `parent_number`.
80    ///
81    /// While proof recording is enabled, all accessed trie nodes are saved.
82    /// These recorded trie nodes can be used by a third party to prove the
83    /// output of this block builder without having access to the full storage.
84    #[allow(clippy::too_many_arguments)]
85    pub fn new(
86        client: Arc<Client>,
87        parent_hash: Block::Hash,
88        parent_number: NumberFor<Block>,
89        inherent_digests: Digest,
90        backend: Arc<Backend>,
91        exec: Arc<Exec>,
92        mut extrinsics: VecDeque<Block::Extrinsic>,
93        maybe_inherent_data: Option<sp_inherents::InherentData>,
94    ) -> Result<Self, Error> {
95        let mut api = TrieBackendApi::new(parent_hash, parent_number, client, backend, exec)?;
96        let header = <Block::Header as HeaderT>::new(
97            parent_number + One::one(),
98            Default::default(),
99            Default::default(),
100            parent_hash,
101            inherent_digests,
102        );
103
104        api.execute_in_transaction(
105            |api: &TrieBackendApi<Client, Block, Backend, Exec>,
106             backend: &TrieDeltaBackendFor<Backend::State, Block>,
107             overlayed_changes: &mut OverlayedChanges<HashingFor<Block>>| {
108                match api.initialize_block(header, backend, overlayed_changes) {
109                    Ok(_) => TransactionOutcome::Commit(Ok(())),
110                    Err(e) => TransactionOutcome::Rollback(Err(e)),
111                }
112            },
113        )?;
114
115        if let Some(inherent_data) = maybe_inherent_data {
116            let inherent_extrinsics = Self::create_inherents(&mut api, inherent_data)?;
117
118            // reverse and push the inherents so that order is maintained
119            for inherent_extrinsic in inherent_extrinsics.into_iter().rev() {
120                extrinsics.push_front(inherent_extrinsic)
121            }
122        }
123
124        Ok(Self { extrinsics, api })
125    }
126
127    /// Execute the block's list of extrinsics.
128    fn execute_extrinsics(&mut self) -> Result<(), Error> {
129        for (index, xt) in self.extrinsics.iter().enumerate() {
130            let res = self.api.execute_in_transaction(
131                |api: &TrieBackendApi<Client, Block, Backend, Exec>,
132                 backend: &TrieDeltaBackendFor<Backend::State, Block>,
133                 overlayed_changes: &mut OverlayedChanges<HashingFor<Block>>| {
134                    match api.apply_extrinsic(xt.clone(), backend, overlayed_changes) {
135                        Ok(Ok(_)) => TransactionOutcome::Commit(Ok(())),
136                        Ok(Err(tx_validity)) => TransactionOutcome::Rollback(Err(
137                            ApplyExtrinsicFailed::Validity(tx_validity).into(),
138                        )),
139                        Err(e) => TransactionOutcome::Rollback(Err(e)),
140                    }
141                },
142            );
143
144            if let Err(e) = res {
145                tracing::debug!("Apply extrinsic at index {index} failed: {e}");
146            }
147        }
148
149        Ok(())
150    }
151
152    fn collect_storage_changes(&mut self) -> Option<CollectedStorageChanges<HashingFor<Block>>> {
153        self.api.collect_storage_changes()
154    }
155
156    /// Returns the state before executing the extrinsic at given extrinsic index.
157    pub fn prepare_storage_changes_before(
158        &mut self,
159        extrinsic_index: usize,
160    ) -> Result<CollectedStorageChanges<HashingFor<Block>>, Error> {
161        for (index, xt) in self.extrinsics.iter().enumerate() {
162            if index == extrinsic_index {
163                return self
164                    .collect_storage_changes()
165                    .ok_or(Error::Execution(Box::new("No execution is done")));
166            }
167
168            // TODO: rethink what to do if an error occurs when executing the transaction.
169            self.api.execute_in_transaction(
170                |api: &TrieBackendApi<Client, Block, Backend, Exec>,
171                 backend: &TrieDeltaBackendFor<Backend::State, Block>,
172                 overlayed_changes: &mut OverlayedChanges<HashingFor<Block>>| {
173                    match api.apply_extrinsic(xt.clone(), backend, overlayed_changes) {
174                        Ok(Ok(_)) => TransactionOutcome::Commit(Ok(())),
175                        Ok(Err(tx_validity)) => TransactionOutcome::Rollback(Err(
176                            ApplyExtrinsicFailed::Validity(tx_validity).into(),
177                        )),
178                        Err(e) => TransactionOutcome::Rollback(Err(e)),
179                    }
180                },
181            )?;
182        }
183
184        Err(Error::Execution(Box::new(format!(
185            "Invalid extrinsic index, got: {}, max: {}",
186            extrinsic_index,
187            self.extrinsics.len()
188        ))))
189    }
190
191    /// Returns the state before finalizing the block.
192    pub fn prepare_storage_changes_before_finalize_block(
193        &mut self,
194    ) -> Result<CollectedStorageChanges<HashingFor<Block>>, Error> {
195        self.execute_extrinsics()?;
196        self.collect_storage_changes()
197            .ok_or(Error::Execution(Box::new("No execution is done")))
198    }
199
200    /// Consume the builder to build a valid `Block` containing all pushed extrinsics.
201    ///
202    /// Returns the build `Block`, the changes to the storage and an optional `StorageProof`
203    /// supplied by `self.api`, combined as [`BuiltBlock`].
204    /// The storage proof will be `Some(_)` when proof recording was enabled.
205    pub fn build(mut self) -> Result<BuiltBlock<Block>, Error> {
206        self.execute_extrinsics()?;
207        let header = self.api.execute_in_transaction(
208            |api: &TrieBackendApi<Client, Block, Backend, Exec>,
209             backend: &TrieDeltaBackendFor<Backend::State, Block>,
210             overlayed_changes: &mut OverlayedChanges<HashingFor<Block>>| {
211                match api.finalize_block(backend, overlayed_changes) {
212                    Ok(header) => TransactionOutcome::Commit(Ok(header)),
213                    Err(e) => TransactionOutcome::Rollback(Err(e)),
214                }
215            },
216        )?;
217
218        debug_assert_eq!(
219            header.extrinsics_root().clone(),
220            HashingFor::<Block>::ordered_trie_root(
221                self.extrinsics.iter().map(Encode::encode).collect(),
222                sp_core::storage::StateVersion::V1
223            ),
224        );
225
226        let storage_changes = self
227            .collect_storage_changes()
228            .expect("must always have the storage changes due to execution above");
229
230        Ok(BuiltBlock {
231            block: Block::new(header, self.extrinsics.into()),
232            storage_changes,
233        })
234    }
235
236    /// Create the inherents for the block.
237    ///
238    /// Returns the inherents created by the runtime or an error if something failed.
239    pub(crate) fn create_inherents(
240        api: &mut TrieBackendApi<Client, Block, Backend, Exec>,
241        inherent_data: sp_inherents::InherentData,
242    ) -> Result<VecDeque<Block::Extrinsic>, Error> {
243        let exts = api.execute_in_transaction(
244            |api: &TrieBackendApi<Client, Block, Backend, Exec>,
245             backend: &TrieDeltaBackendFor<Backend::State, Block>,
246             overlayed_changes: &mut OverlayedChanges<HashingFor<Block>>| {
247                // `create_inherents` should not change any state, to ensure this we always rollback
248                // the transaction.
249                TransactionOutcome::Rollback(api.inherent_extrinsics(
250                    inherent_data,
251                    backend,
252                    overlayed_changes,
253                ))
254            },
255        )?;
256        Ok(VecDeque::from(exts))
257    }
258}
259
260// TODO: Unlock this test, it got broken in https://github.com/autonomys/subspace/pull/1548 and
261//  doesn't run on Windows at all, also needs to not use substrate_test_runtime_client
262// #[cfg(test)]
263// mod tests {
264//     use super::*;
265//     use sp_blockchain::HeaderBackend;
266//     use sp_core::Blake2Hasher;
267//     use sp_state_machine::Backend;
268//     // TODO: Remove `substrate_test_runtime_client` dependency for faster build time
269//     use std::collections::VecDeque;
270//     use substrate_test_runtime_client::{DefaultTestClientBuilderExt, TestClientBuilderExt};
271//
272//     #[test]
273//     #[ignore]
274//     fn block_building_storage_proof_does_not_include_runtime_by_default() {
275//         let (client, backend) =
276//             substrate_test_runtime_client::TestClientBuilder::new().build_with_backend();
277//
278//         let block = BlockBuilder::new(
279//             &client,
280//             client.info().best_hash,
281//             client.info().best_number,
282//             RecordProof::Yes,
283//             Default::default(),
284//             &*backend,
285//             VecDeque::new(),
286//             Default::default(),
287//         )
288//         .unwrap()
289//         .build()
290//         .unwrap();
291//
292//         let proof = block.proof.expect("Proof is build on request");
293//
294//         let backend = sp_state_machine::create_proof_check_backend::<Blake2Hasher>(
295//             block.storage_changes.transaction_storage_root,
296//             proof,
297//         )
298//         .unwrap();
299//
300//         assert!(backend
301//             .storage(sp_core::storage::well_known_keys::CODE)
302//             .unwrap_err()
303//             .contains("Database missing expected key"));
304//     }
305// }