1#![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
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 extra_extensions: Extensions::new(),
107 })
108 }
109
110 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
129pub 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 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 pub fn enable_proof_recording(mut self) -> Self {
154 self.proof_recorder = Some(Default::default());
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 with_extra_extensions(mut self, extra_extensions: impl Into<Extensions>) -> Self {
166 self.extra_extensions = extra_extensions.into();
167 self
168 }
169
170 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
187pub struct BuiltBlock<Block: BlockT> {
192 pub block: Block,
194 pub storage_changes: StorageChanges<Block>,
196}
197
198impl<Block: BlockT> BuiltBlock<Block> {
199 pub fn into_inner(self) -> (Block, StorageChanges<Block>) {
201 (self.block, self.storage_changes)
202 }
203}
204
205pub 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: u32,
212 parent_hash: Block::Hash,
213 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 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 pub fn extrinsic_inclusion_mode(&self) -> ExtrinsicInclusionMode {
288 self.extrinsic_inclusion_mode
289 }
290
291 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 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 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 TransactionOutcome::Rollback(api.inherent_extrinsics(parent_hash, inherent_data))
360 })
361 .map_err(|e| Error::Application(Box::new(e)))
362 }
363
364 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 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 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}