sc_block_builder/
lib.rs

1// This file is part of Substrate.
2
3// Copyright (C) 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
29use codec::Encode;
30
31use sp_api::{
32	ApiExt, ApiRef, CallApiAt, Core, ProvideRuntimeApi, StorageChanges, StorageProof,
33	TransactionOutcome,
34};
35use sp_blockchain::{ApplyExtrinsicFailed, Error, HeaderBackend};
36use sp_core::traits::CallContext;
37use sp_runtime::{
38	legacy,
39	traits::{Block as BlockT, Hash, HashingFor, Header as HeaderT, NumberFor, One},
40	Digest, ExtrinsicInclusionMode,
41};
42use std::marker::PhantomData;
43
44pub use sp_block_builder::BlockBuilder as BlockBuilderApi;
45use sp_trie::proof_size_extension::ProofSizeExt;
46
47/// A builder for creating an instance of [`BlockBuilder`].
48pub struct BlockBuilderBuilder<'a, B, C> {
49	call_api_at: &'a C,
50	_phantom: PhantomData<B>,
51}
52
53impl<'a, B, C> BlockBuilderBuilder<'a, B, C>
54where
55	B: BlockT,
56{
57	/// Create a new instance of the builder.
58	///
59	/// `call_api_at`: Something that implements [`CallApiAt`].
60	pub fn new(call_api_at: &'a C) -> Self {
61		Self { call_api_at, _phantom: PhantomData }
62	}
63
64	/// Specify the parent block to build on top of.
65	pub fn on_parent_block(self, parent_block: B::Hash) -> BlockBuilderBuilderStage1<'a, B, C> {
66		BlockBuilderBuilderStage1 { call_api_at: self.call_api_at, parent_block }
67	}
68}
69
70/// The second stage of the [`BlockBuilderBuilder`].
71///
72/// This type can not be instantiated directly. To get an instance of it
73/// [`BlockBuilderBuilder::new`] needs to be used.
74pub struct BlockBuilderBuilderStage1<'a, B: BlockT, C> {
75	call_api_at: &'a C,
76	parent_block: B::Hash,
77}
78
79impl<'a, B, C> BlockBuilderBuilderStage1<'a, B, C>
80where
81	B: BlockT,
82{
83	/// Fetch the parent block number from the given `header_backend`.
84	///
85	/// The parent block number is used to initialize the block number of the new block.
86	///
87	/// Returns an error if the parent block specified in
88	/// [`on_parent_block`](BlockBuilderBuilder::on_parent_block) does not exist.
89	pub fn fetch_parent_block_number<H: HeaderBackend<B>>(
90		self,
91		header_backend: &H,
92	) -> Result<BlockBuilderBuilderStage2<'a, B, C>, Error> {
93		let parent_number = header_backend.number(self.parent_block)?.ok_or_else(|| {
94			Error::Backend(format!(
95				"Could not fetch block number for block: {:?}",
96				self.parent_block
97			))
98		})?;
99
100		Ok(BlockBuilderBuilderStage2 {
101			call_api_at: self.call_api_at,
102			enable_proof_recording: false,
103			inherent_digests: Default::default(),
104			parent_block: self.parent_block,
105			parent_number,
106		})
107	}
108
109	/// Provide the block number for the parent block directly.
110	///
111	/// The parent block is specified in [`on_parent_block`](BlockBuilderBuilder::on_parent_block).
112	/// The parent block number is used to initialize the block number of the new block.
113	pub fn with_parent_block_number(
114		self,
115		parent_number: NumberFor<B>,
116	) -> BlockBuilderBuilderStage2<'a, B, C> {
117		BlockBuilderBuilderStage2 {
118			call_api_at: self.call_api_at,
119			enable_proof_recording: false,
120			inherent_digests: Default::default(),
121			parent_block: self.parent_block,
122			parent_number,
123		}
124	}
125}
126
127/// The second stage of the [`BlockBuilderBuilder`].
128///
129/// This type can not be instantiated directly. To get an instance of it
130/// [`BlockBuilderBuilder::new`] needs to be used.
131pub struct BlockBuilderBuilderStage2<'a, B: BlockT, C> {
132	call_api_at: &'a C,
133	enable_proof_recording: bool,
134	inherent_digests: Digest,
135	parent_block: B::Hash,
136	parent_number: NumberFor<B>,
137}
138
139impl<'a, B: BlockT, C> BlockBuilderBuilderStage2<'a, B, C> {
140	/// Enable proof recording for the block builder.
141	pub fn enable_proof_recording(mut self) -> Self {
142		self.enable_proof_recording = true;
143		self
144	}
145
146	/// Enable/disable proof recording for the block builder.
147	pub fn with_proof_recording(mut self, enable: bool) -> Self {
148		self.enable_proof_recording = enable;
149		self
150	}
151
152	/// Build the block with the given inherent digests.
153	pub fn with_inherent_digests(mut self, inherent_digests: Digest) -> Self {
154		self.inherent_digests = inherent_digests;
155		self
156	}
157
158	/// Create the instance of the [`BlockBuilder`].
159	pub fn build(self) -> Result<BlockBuilder<'a, B, C>, Error>
160	where
161		C: CallApiAt<B> + ProvideRuntimeApi<B>,
162		C::Api: BlockBuilderApi<B>,
163	{
164		BlockBuilder::new(
165			self.call_api_at,
166			self.parent_block,
167			self.parent_number,
168			self.enable_proof_recording,
169			self.inherent_digests,
170		)
171	}
172}
173
174/// A block that was build by [`BlockBuilder`] plus some additional data.
175///
176/// This additional data includes the `storage_changes`, these changes can be applied to the
177/// backend to get the state of the block. Furthermore an optional `proof` is included which
178/// can be used to proof that the build block contains the expected data. The `proof` will
179/// only be set when proof recording was activated.
180pub struct BuiltBlock<Block: BlockT> {
181	/// The actual block that was build.
182	pub block: Block,
183	/// The changes that need to be applied to the backend to get the state of the build block.
184	pub storage_changes: StorageChanges<Block>,
185	/// An optional proof that was recorded while building the block.
186	pub proof: Option<StorageProof>,
187}
188
189impl<Block: BlockT> BuiltBlock<Block> {
190	/// Convert into the inner values.
191	pub fn into_inner(self) -> (Block, StorageChanges<Block>, Option<StorageProof>) {
192		(self.block, self.storage_changes, self.proof)
193	}
194}
195
196/// Utility for building new (valid) blocks from a stream of extrinsics.
197pub struct BlockBuilder<'a, Block: BlockT, C: ProvideRuntimeApi<Block> + 'a> {
198	extrinsics: Vec<Block::Extrinsic>,
199	api: ApiRef<'a, C::Api>,
200	call_api_at: &'a C,
201	/// Version of the [`BlockBuilderApi`] runtime API.
202	version: u32,
203	parent_hash: Block::Hash,
204	/// The estimated size of the block header.
205	estimated_header_size: usize,
206	extrinsic_inclusion_mode: ExtrinsicInclusionMode,
207}
208
209impl<'a, Block, C> BlockBuilder<'a, Block, C>
210where
211	Block: BlockT,
212	C: CallApiAt<Block> + ProvideRuntimeApi<Block> + 'a,
213	C::Api: BlockBuilderApi<Block>,
214{
215	/// Create a new instance of builder based on the given `parent_hash` and `parent_number`.
216	///
217	/// While proof recording is enabled, all accessed trie nodes are saved.
218	/// These recorded trie nodes can be used by a third party to prove the
219	/// output of this block builder without having access to the full storage.
220	fn new(
221		call_api_at: &'a C,
222		parent_hash: Block::Hash,
223		parent_number: NumberFor<Block>,
224		record_proof: bool,
225		inherent_digests: Digest,
226	) -> Result<Self, Error> {
227		let header = <<Block as BlockT>::Header as HeaderT>::new(
228			parent_number + One::one(),
229			Default::default(),
230			Default::default(),
231			parent_hash,
232			inherent_digests,
233		);
234
235		let estimated_header_size = header.encoded_size();
236
237		let mut api = call_api_at.runtime_api();
238
239		if record_proof {
240			api.record_proof();
241			let recorder = api
242				.proof_recorder()
243				.expect("Proof recording is enabled in the line above; qed.");
244			api.register_extension(ProofSizeExt::new(recorder));
245		}
246
247		api.set_call_context(CallContext::Onchain);
248
249		let core_version = api
250			.api_version::<dyn Core<Block>>(parent_hash)?
251			.ok_or_else(|| Error::VersionInvalid("Core".to_string()))?;
252
253		let extrinsic_inclusion_mode = if core_version >= 5 {
254			api.initialize_block(parent_hash, &header)?
255		} else {
256			#[allow(deprecated)]
257			api.initialize_block_before_version_5(parent_hash, &header)?;
258			ExtrinsicInclusionMode::AllExtrinsics
259		};
260
261		let bb_version = api
262			.api_version::<dyn BlockBuilderApi<Block>>(parent_hash)?
263			.ok_or_else(|| Error::VersionInvalid("BlockBuilderApi".to_string()))?;
264
265		Ok(Self {
266			parent_hash,
267			extrinsics: Vec::new(),
268			api,
269			version: bb_version,
270			estimated_header_size,
271			call_api_at,
272			extrinsic_inclusion_mode,
273		})
274	}
275
276	/// The extrinsic inclusion mode of the runtime for this block.
277	pub fn extrinsic_inclusion_mode(&self) -> ExtrinsicInclusionMode {
278		self.extrinsic_inclusion_mode
279	}
280
281	/// Push onto the block's list of extrinsics.
282	///
283	/// This will ensure the extrinsic can be validly executed (by executing it).
284	pub fn push(&mut self, xt: <Block as BlockT>::Extrinsic) -> Result<(), Error> {
285		let parent_hash = self.parent_hash;
286		let extrinsics = &mut self.extrinsics;
287		let version = self.version;
288
289		self.api.execute_in_transaction(|api| {
290			let res = if version < 6 {
291				#[allow(deprecated)]
292				api.apply_extrinsic_before_version_6(parent_hash, xt.clone())
293					.map(legacy::byte_sized_error::convert_to_latest)
294			} else {
295				api.apply_extrinsic(parent_hash, xt.clone())
296			};
297
298			match res {
299				Ok(Ok(_)) => {
300					extrinsics.push(xt);
301					TransactionOutcome::Commit(Ok(()))
302				},
303				Ok(Err(tx_validity)) => TransactionOutcome::Rollback(Err(
304					ApplyExtrinsicFailed::Validity(tx_validity).into(),
305				)),
306				Err(e) => TransactionOutcome::Rollback(Err(Error::from(e))),
307			}
308		})
309	}
310
311	/// Consume the builder to build a valid `Block` containing all pushed extrinsics.
312	///
313	/// Returns the build `Block`, the changes to the storage and an optional `StorageProof`
314	/// supplied by `self.api`, combined as [`BuiltBlock`].
315	/// The storage proof will be `Some(_)` when proof recording was enabled.
316	pub fn build(mut self) -> Result<BuiltBlock<Block>, Error> {
317		let header = self.api.finalize_block(self.parent_hash)?;
318
319		debug_assert_eq!(
320			header.extrinsics_root().clone(),
321			HashingFor::<Block>::ordered_trie_root(
322				self.extrinsics.iter().map(Encode::encode).collect(),
323				self.api.version(self.parent_hash)?.extrinsics_root_state_version(),
324			),
325		);
326
327		let proof = self.api.extract_proof();
328
329		let state = self.call_api_at.state_at(self.parent_hash)?;
330
331		let storage_changes = self
332			.api
333			.into_storage_changes(&state, self.parent_hash)
334			.map_err(sp_blockchain::Error::StorageChanges)?;
335
336		Ok(BuiltBlock {
337			block: <Block as BlockT>::new(header, self.extrinsics),
338			storage_changes,
339			proof,
340		})
341	}
342
343	/// Create the inherents for the block.
344	///
345	/// Returns the inherents created by the runtime or an error if something failed.
346	pub fn create_inherents(
347		&mut self,
348		inherent_data: sp_inherents::InherentData,
349	) -> Result<Vec<Block::Extrinsic>, Error> {
350		let parent_hash = self.parent_hash;
351		self.api
352			.execute_in_transaction(move |api| {
353				// `create_inherents` should not change any state, to ensure this we always rollback
354				// the transaction.
355				TransactionOutcome::Rollback(api.inherent_extrinsics(parent_hash, inherent_data))
356			})
357			.map_err(|e| Error::Application(Box::new(e)))
358	}
359
360	/// Estimate the size of the block in the current state.
361	///
362	/// If `include_proof` is `true`, the estimated size of the storage proof will be added
363	/// to the estimation.
364	pub fn estimate_block_size(&self, include_proof: bool) -> usize {
365		let size = self.estimated_header_size + self.extrinsics.encoded_size();
366
367		if include_proof {
368			size + self.api.proof_recorder().map(|pr| pr.estimate_encoded_size()).unwrap_or(0)
369		} else {
370			size
371		}
372	}
373}
374
375#[cfg(test)]
376mod tests {
377	use super::*;
378	use sp_blockchain::HeaderBackend;
379	use sp_core::Blake2Hasher;
380	use sp_state_machine::Backend;
381	use substrate_test_runtime_client::{
382		runtime::ExtrinsicBuilder, DefaultTestClientBuilderExt, TestClientBuilderExt,
383	};
384
385	#[test]
386	fn block_building_storage_proof_does_not_include_runtime_by_default() {
387		let builder = substrate_test_runtime_client::TestClientBuilder::new();
388		let client = builder.build();
389
390		let genesis_hash = client.info().best_hash;
391
392		let block = BlockBuilderBuilder::new(&client)
393			.on_parent_block(genesis_hash)
394			.with_parent_block_number(0)
395			.enable_proof_recording()
396			.build()
397			.unwrap()
398			.build()
399			.unwrap();
400
401		let proof = block.proof.expect("Proof is build on request");
402		let genesis_state_root = client.header(genesis_hash).unwrap().unwrap().state_root;
403
404		let backend =
405			sp_state_machine::create_proof_check_backend::<Blake2Hasher>(genesis_state_root, proof)
406				.unwrap();
407
408		assert!(backend
409			.storage(&sp_core::storage::well_known_keys::CODE)
410			.unwrap_err()
411			.contains("Database missing expected key"),);
412	}
413
414	#[test]
415	fn failing_extrinsic_rolls_back_changes_in_storage_proof() {
416		let builder = substrate_test_runtime_client::TestClientBuilder::new();
417		let client = builder.build();
418		let genesis_hash = client.info().best_hash;
419
420		let mut block_builder = BlockBuilderBuilder::new(&client)
421			.on_parent_block(genesis_hash)
422			.with_parent_block_number(0)
423			.enable_proof_recording()
424			.build()
425			.unwrap();
426
427		block_builder.push(ExtrinsicBuilder::new_read_and_panic(8).build()).unwrap_err();
428
429		let block = block_builder.build().unwrap();
430
431		let proof_with_panic = block.proof.expect("Proof is build on request").encoded_size();
432
433		let mut block_builder = BlockBuilderBuilder::new(&client)
434			.on_parent_block(genesis_hash)
435			.with_parent_block_number(0)
436			.enable_proof_recording()
437			.build()
438			.unwrap();
439
440		block_builder.push(ExtrinsicBuilder::new_read(8).build()).unwrap();
441
442		let block = block_builder.build().unwrap();
443
444		let proof_without_panic = block.proof.expect("Proof is build on request").encoded_size();
445
446		let block = BlockBuilderBuilder::new(&client)
447			.on_parent_block(genesis_hash)
448			.with_parent_block_number(0)
449			.enable_proof_recording()
450			.build()
451			.unwrap()
452			.build()
453			.unwrap();
454
455		let proof_empty_block = block.proof.expect("Proof is build on request").encoded_size();
456
457		// Ensure that we rolled back the changes of the panicked transaction.
458		assert!(proof_without_panic > proof_with_panic);
459		assert!(proof_without_panic > proof_empty_block);
460		assert_eq!(proof_empty_block, proof_with_panic);
461	}
462}