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}