1#![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
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 enable_proof_recording: false,
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 enable_proof_recording: false,
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 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 pub fn enable_proof_recording(mut self) -> Self {
142 self.enable_proof_recording = true;
143 self
144 }
145
146 pub fn with_proof_recording(mut self, enable: bool) -> Self {
148 self.enable_proof_recording = enable;
149 self
150 }
151
152 pub fn with_inherent_digests(mut self, inherent_digests: Digest) -> Self {
154 self.inherent_digests = inherent_digests;
155 self
156 }
157
158 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
174pub struct BuiltBlock<Block: BlockT> {
181 pub block: Block,
183 pub storage_changes: StorageChanges<Block>,
185 pub proof: Option<StorageProof>,
187}
188
189impl<Block: BlockT> BuiltBlock<Block> {
190 pub fn into_inner(self) -> (Block, StorageChanges<Block>, Option<StorageProof>) {
192 (self.block, self.storage_changes, self.proof)
193 }
194}
195
196pub 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: u32,
203 parent_hash: Block::Hash,
204 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 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 pub fn extrinsic_inclusion_mode(&self) -> ExtrinsicInclusionMode {
278 self.extrinsic_inclusion_mode
279 }
280
281 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 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 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 TransactionOutcome::Rollback(api.inherent_extrinsics(parent_hash, inherent_data))
356 })
357 .map_err(|e| Error::Application(Box::new(e)))
358 }
359
360 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 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}