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