#![warn(missing_docs)]
use codec::Encode;
use sp_runtime::{
generic::BlockId,
traits::{
Header as HeaderT, Hash, Block as BlockT, HashFor, DigestFor, NumberFor, One, HasherFor,
},
};
use sp_blockchain::{ApplyExtrinsicFailed, Error};
use sp_core::ExecutionContext;
use sp_api::{Core, ApiExt, ApiErrorFor, ApiRef, ProvideRuntimeApi, StorageChanges, StorageProof};
use sp_consensus::RecordProof;
pub use sp_block_builder::BlockBuilder as BlockBuilderApi;
use sc_client_api::backend;
pub struct BuiltBlock<Block: BlockT, StateBackend: backend::StateBackend<HasherFor<Block>>> {
pub block: Block,
pub storage_changes: StorageChanges<StateBackend, Block>,
pub proof: Option<StorageProof>,
}
impl<Block: BlockT, StateBackend: backend::StateBackend<HasherFor<Block>>> BuiltBlock<Block, StateBackend> {
pub fn into_inner(self) -> (Block, StorageChanges<StateBackend, Block>, Option<StorageProof>) {
(self.block, self.storage_changes, self.proof)
}
}
pub struct BlockBuilder<'a, Block: BlockT, A: ProvideRuntimeApi<Block>, B> {
extrinsics: Vec<Block::Extrinsic>,
api: ApiRef<'a, A::Api>,
block_id: BlockId<Block>,
parent_hash: Block::Hash,
backend: &'a B,
}
impl<'a, Block, A, B> BlockBuilder<'a, Block, A, B>
where
Block: BlockT,
A: ProvideRuntimeApi<Block> + 'a,
A::Api: BlockBuilderApi<Block, Error = Error> +
ApiExt<Block, StateBackend = backend::StateBackendFor<B, Block>>,
B: backend::Backend<Block>,
{
pub fn new(
api: &'a A,
parent_hash: Block::Hash,
parent_number: NumberFor<Block>,
record_proof: RecordProof,
inherent_digests: DigestFor<Block>,
backend: &'a B,
) -> Result<Self, ApiErrorFor<A, Block>> {
let header = <<Block as BlockT>::Header as HeaderT>::new(
parent_number + One::one(),
Default::default(),
Default::default(),
parent_hash,
inherent_digests,
);
let mut api = api.runtime_api();
if record_proof.yes() {
api.record_proof();
}
let block_id = BlockId::Hash(parent_hash);
api.initialize_block_with_context(
&block_id, ExecutionContext::BlockConstruction, &header,
)?;
Ok(Self {
parent_hash,
extrinsics: Vec::new(),
api,
block_id,
backend,
})
}
pub fn push(&mut self, xt: <Block as BlockT>::Extrinsic) -> Result<(), ApiErrorFor<A, Block>> {
self.push_internal(xt, false)
}
pub fn push_trusted(&mut self, xt: <Block as BlockT>::Extrinsic) -> Result<(), ApiErrorFor<A, Block>> {
self.push_internal(xt, true)
}
fn push_internal(&mut self, xt: <Block as BlockT>::Extrinsic, skip_signature: bool) -> Result<(), ApiErrorFor<A, Block>> {
let block_id = &self.block_id;
let extrinsics = &mut self.extrinsics;
let use_trusted = skip_signature && self
.api
.has_api_with::<dyn BlockBuilderApi<Block, Error = ApiErrorFor<A, Block>>, _>(
block_id,
|version| version >= 5,
)?;
self.api.map_api_result(|api| {
let apply_result = if use_trusted {
api.apply_trusted_extrinsic_with_context(
block_id,
ExecutionContext::BlockConstruction,
xt.clone(),
)?
} else {
api.apply_extrinsic_with_context(
block_id,
ExecutionContext::BlockConstruction,
xt.clone(),
)?
};
match apply_result {
Ok(_) => {
extrinsics.push(xt);
Ok(())
}
Err(tx_validity) => Err(ApplyExtrinsicFailed::Validity(tx_validity).into()),
}
})
}
pub fn build(mut self) -> Result<
BuiltBlock<Block, backend::StateBackendFor<B, Block>>,
ApiErrorFor<A, Block>
> {
let header = self.api.finalize_block_with_context(
&self.block_id, ExecutionContext::BlockConstruction
)?;
debug_assert_eq!(
header.extrinsics_root().clone(),
HashFor::<Block>::ordered_trie_root(
self.extrinsics.iter().map(Encode::encode).collect(),
),
);
let proof = self.api.extract_proof();
let state = self.backend.state_at(self.block_id)?;
let changes_trie_state = backend::changes_tries_state_at_block(
&self.block_id,
self.backend.changes_trie_storage(),
)?;
let parent_hash = self.parent_hash;
let storage_changes = self.api.into_storage_changes(
&state,
changes_trie_state.as_ref(),
parent_hash,
);
{
let _lock = self.backend.get_import_lock().read();
self.backend.destroy_state(state)?;
}
Ok(BuiltBlock {
block: <Block as BlockT>::new(header, self.extrinsics),
storage_changes: storage_changes?,
proof,
})
}
}