1#![warn(missing_docs)]
28
29use codec::Encode;
30
31use sp_api::{
32	ApiExt, ApiRef, CallApiAt, Core, ProofRecorder, ProvideRuntimeApi, StorageChanges,
33	StorageProof, 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
47pub 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	pub fn new(call_api_at: &'a C) -> Self {
61		Self { call_api_at, _phantom: PhantomData }
62	}
63
64	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
70pub 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	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		})
107	}
108
109	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			proof_recorder: None,
120			inherent_digests: Default::default(),
121			parent_block: self.parent_block,
122			parent_number,
123		}
124	}
125}
126
127pub struct BlockBuilderBuilderStage2<'a, B: BlockT, C> {
132	call_api_at: &'a C,
133	proof_recorder: Option<ProofRecorder<B>>,
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	pub fn enable_proof_recording(mut self) -> Self {
142		self.proof_recorder = Some(Default::default());
143		self
144	}
145
146	pub fn with_proof_recording(mut self, enable: bool) -> Self {
148		self.proof_recorder = enable.then(|| Default::default());
149		self
150	}
151
152	pub fn with_proof_recorder(mut self, proof_recorder: Option<ProofRecorder<B>>) -> Self {
154		self.proof_recorder = proof_recorder;
155		self
156	}
157
158	pub fn with_inherent_digests(mut self, inherent_digests: Digest) -> Self {
160		self.inherent_digests = inherent_digests;
161		self
162	}
163
164	pub fn build(self) -> Result<BlockBuilder<'a, B, C>, Error>
166	where
167		C: CallApiAt<B> + ProvideRuntimeApi<B>,
168		C::Api: BlockBuilderApi<B>,
169	{
170		BlockBuilder::new(
171			self.call_api_at,
172			self.parent_block,
173			self.parent_number,
174			self.proof_recorder,
175			self.inherent_digests,
176		)
177	}
178}
179
180pub struct BuiltBlock<Block: BlockT> {
187	pub block: Block,
189	pub storage_changes: StorageChanges<Block>,
191	pub proof: Option<StorageProof>,
193}
194
195impl<Block: BlockT> BuiltBlock<Block> {
196	pub fn into_inner(self) -> (Block, StorageChanges<Block>, Option<StorageProof>) {
198		(self.block, self.storage_changes, self.proof)
199	}
200}
201
202pub struct BlockBuilder<'a, Block: BlockT, C: ProvideRuntimeApi<Block> + 'a> {
204	extrinsics: Vec<Block::Extrinsic>,
205	api: ApiRef<'a, C::Api>,
206	call_api_at: &'a C,
207	version: u32,
209	parent_hash: Block::Hash,
210	estimated_header_size: usize,
212	extrinsic_inclusion_mode: ExtrinsicInclusionMode,
213}
214
215impl<'a, Block, C> BlockBuilder<'a, Block, C>
216where
217	Block: BlockT,
218	C: CallApiAt<Block> + ProvideRuntimeApi<Block> + 'a,
219	C::Api: BlockBuilderApi<Block>,
220{
221	fn new(
227		call_api_at: &'a C,
228		parent_hash: Block::Hash,
229		parent_number: NumberFor<Block>,
230		proof_recorder: Option<ProofRecorder<Block>>,
231		inherent_digests: Digest,
232	) -> Result<Self, Error> {
233		let header = <<Block as BlockT>::Header as HeaderT>::new(
234			parent_number + One::one(),
235			Default::default(),
236			Default::default(),
237			parent_hash,
238			inherent_digests,
239		);
240
241		let estimated_header_size = header.encoded_size();
242
243		let mut api = call_api_at.runtime_api();
244
245		if let Some(recorder) = proof_recorder {
246			api.record_proof_with_recorder(recorder.clone());
247			api.register_extension(ProofSizeExt::new(recorder));
248		}
249
250		api.set_call_context(CallContext::Onchain);
251
252		let core_version = api
253			.api_version::<dyn Core<Block>>(parent_hash)?
254			.ok_or_else(|| Error::VersionInvalid("Core".to_string()))?;
255
256		let extrinsic_inclusion_mode = if core_version >= 5 {
257			api.initialize_block(parent_hash, &header)?
258		} else {
259			#[allow(deprecated)]
260			api.initialize_block_before_version_5(parent_hash, &header)?;
261			ExtrinsicInclusionMode::AllExtrinsics
262		};
263
264		let bb_version = api
265			.api_version::<dyn BlockBuilderApi<Block>>(parent_hash)?
266			.ok_or_else(|| Error::VersionInvalid("BlockBuilderApi".to_string()))?;
267
268		Ok(Self {
269			parent_hash,
270			extrinsics: Vec::new(),
271			api,
272			version: bb_version,
273			estimated_header_size,
274			call_api_at,
275			extrinsic_inclusion_mode,
276		})
277	}
278
279	pub fn extrinsic_inclusion_mode(&self) -> ExtrinsicInclusionMode {
281		self.extrinsic_inclusion_mode
282	}
283
284	pub fn push(&mut self, xt: <Block as BlockT>::Extrinsic) -> Result<(), Error> {
288		let parent_hash = self.parent_hash;
289		let extrinsics = &mut self.extrinsics;
290		let version = self.version;
291
292		self.api.execute_in_transaction(|api| {
293			let res = if version < 6 {
294				#[allow(deprecated)]
295				api.apply_extrinsic_before_version_6(parent_hash, xt.clone())
296					.map(legacy::byte_sized_error::convert_to_latest)
297			} else {
298				api.apply_extrinsic(parent_hash, xt.clone())
299			};
300
301			match res {
302				Ok(Ok(_)) => {
303					extrinsics.push(xt);
304					TransactionOutcome::Commit(Ok(()))
305				},
306				Ok(Err(tx_validity)) => TransactionOutcome::Rollback(Err(
307					ApplyExtrinsicFailed::Validity(tx_validity).into(),
308				)),
309				Err(e) => TransactionOutcome::Rollback(Err(Error::from(e))),
310			}
311		})
312	}
313
314	pub fn build(mut self) -> Result<BuiltBlock<Block>, Error> {
320		let header = self.api.finalize_block(self.parent_hash)?;
321
322		debug_assert_eq!(
323			header.extrinsics_root().clone(),
324			HashingFor::<Block>::ordered_trie_root(
325				self.extrinsics.iter().map(Encode::encode).collect(),
326				self.api.version(self.parent_hash)?.extrinsics_root_state_version(),
327			),
328		);
329
330		let proof = self.api.extract_proof();
331
332		let state = self.call_api_at.state_at(self.parent_hash)?;
333
334		let storage_changes = self
335			.api
336			.into_storage_changes(&state, self.parent_hash)
337			.map_err(sp_blockchain::Error::StorageChanges)?;
338
339		Ok(BuiltBlock {
340			block: <Block as BlockT>::new(header, self.extrinsics),
341			storage_changes,
342			proof,
343		})
344	}
345
346	pub fn create_inherents(
350		&mut self,
351		inherent_data: sp_inherents::InherentData,
352	) -> Result<Vec<Block::Extrinsic>, Error> {
353		let parent_hash = self.parent_hash;
354		self.api
355			.execute_in_transaction(move |api| {
356				TransactionOutcome::Rollback(api.inherent_extrinsics(parent_hash, inherent_data))
359			})
360			.map_err(|e| Error::Application(Box::new(e)))
361	}
362
363	pub fn estimate_block_size(&self, include_proof: bool) -> usize {
368		let size = self.estimated_header_size + self.extrinsics.encoded_size();
369
370		if include_proof {
371			size + self.api.proof_recorder().map(|pr| pr.estimate_encoded_size()).unwrap_or(0)
372		} else {
373			size
374		}
375	}
376}
377
378#[cfg(test)]
379mod tests {
380	use super::*;
381	use sp_blockchain::HeaderBackend;
382	use sp_core::Blake2Hasher;
383	use sp_state_machine::Backend;
384	use substrate_test_runtime_client::{
385		runtime::ExtrinsicBuilder, DefaultTestClientBuilderExt, TestClientBuilderExt,
386	};
387
388	#[test]
389	fn block_building_storage_proof_does_not_include_runtime_by_default() {
390		let builder = substrate_test_runtime_client::TestClientBuilder::new();
391		let client = builder.build();
392
393		let genesis_hash = client.info().best_hash;
394
395		let block = BlockBuilderBuilder::new(&client)
396			.on_parent_block(genesis_hash)
397			.with_parent_block_number(0)
398			.enable_proof_recording()
399			.build()
400			.unwrap()
401			.build()
402			.unwrap();
403
404		let proof = block.proof.expect("Proof is build on request");
405		let genesis_state_root = client.header(genesis_hash).unwrap().unwrap().state_root;
406
407		let backend =
408			sp_state_machine::create_proof_check_backend::<Blake2Hasher>(genesis_state_root, proof)
409				.unwrap();
410
411		assert!(backend
412			.storage(&sp_core::storage::well_known_keys::CODE)
413			.unwrap_err()
414			.contains("Database missing expected key"),);
415	}
416
417	#[test]
418	fn failing_extrinsic_rolls_back_changes_in_storage_proof() {
419		let builder = substrate_test_runtime_client::TestClientBuilder::new();
420		let client = builder.build();
421		let genesis_hash = client.info().best_hash;
422
423		let mut block_builder = BlockBuilderBuilder::new(&client)
424			.on_parent_block(genesis_hash)
425			.with_parent_block_number(0)
426			.enable_proof_recording()
427			.build()
428			.unwrap();
429
430		block_builder.push(ExtrinsicBuilder::new_read_and_panic(8).build()).unwrap_err();
431
432		let block = block_builder.build().unwrap();
433
434		let proof_with_panic = block.proof.expect("Proof is build on request").encoded_size();
435
436		let mut block_builder = BlockBuilderBuilder::new(&client)
437			.on_parent_block(genesis_hash)
438			.with_parent_block_number(0)
439			.enable_proof_recording()
440			.build()
441			.unwrap();
442
443		block_builder.push(ExtrinsicBuilder::new_read(8).build()).unwrap();
444
445		let block = block_builder.build().unwrap();
446
447		let proof_without_panic = block.proof.expect("Proof is build on request").encoded_size();
448
449		let block = BlockBuilderBuilder::new(&client)
450			.on_parent_block(genesis_hash)
451			.with_parent_block_number(0)
452			.enable_proof_recording()
453			.build()
454			.unwrap()
455			.build()
456			.unwrap();
457
458		let proof_empty_block = block.proof.expect("Proof is build on request").encoded_size();
459
460		assert!(proof_without_panic > proof_with_panic);
462		assert!(proof_without_panic > proof_empty_block);
463		assert_eq!(proof_empty_block, proof_with_panic);
464	}
465}