referrerpolicy=no-referrer-when-downgrade

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