referrerpolicy=no-referrer-when-downgrade

sc_consensus_manual_seal/
seal_block.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
5
6// This program is free software: you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation, either version 3 of the License, or
9// (at your option) any later version.
10
11// This program is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14// GNU General Public License for more details.
15
16// You should have received a copy of the GNU General Public License
17// along with this program. If not, see <https://www.gnu.org/licenses/>.
18
19//! Block sealing utilities
20
21use crate::{rpc, ConsensusDataProvider, CreatedBlock, Error};
22use futures::prelude::*;
23use sc_consensus::{BlockImport, BlockImportParams, ForkChoiceStrategy, ImportResult, StateAction};
24use sc_transaction_pool_api::TransactionPool;
25use sp_api::ProvideRuntimeApi;
26use sp_blockchain::HeaderBackend;
27use sp_consensus::{self, BlockOrigin, Environment, Proposer, SelectChain};
28use sp_inherents::{CreateInherentDataProviders, InherentDataProvider};
29use sp_runtime::traits::{Block as BlockT, Header as HeaderT};
30use std::{sync::Arc, time::Duration};
31
32/// max duration for creating a proposal in secs
33pub const MAX_PROPOSAL_DURATION: u64 = 10;
34
35/// params for sealing a new block
36pub struct SealBlockParams<'a, B: BlockT, BI, SC, C: ProvideRuntimeApi<B>, E, TP, CIDP, P> {
37	/// if true, empty blocks(without extrinsics) will be created.
38	/// otherwise, will return Error::EmptyTransactionPool.
39	pub create_empty: bool,
40	/// instantly finalize this block?
41	pub finalize: bool,
42	/// specify the parent hash of the about-to-created block
43	pub parent_hash: Option<<B as BlockT>::Hash>,
44	/// sender to report errors/success to the rpc.
45	pub sender: rpc::Sender<CreatedBlock<<B as BlockT>::Hash>>,
46	/// transaction pool
47	pub pool: Arc<TP>,
48	/// header backend
49	pub client: Arc<C>,
50	/// Environment trait object for creating a proposer
51	pub env: &'a mut E,
52	/// SelectChain object
53	pub select_chain: &'a SC,
54	/// Digest provider for inclusion in blocks.
55	pub consensus_data_provider: Option<&'a dyn ConsensusDataProvider<B, Proof = P>>,
56	/// block import object
57	pub block_import: &'a mut BI,
58	/// Something that can create the inherent data providers.
59	pub create_inherent_data_providers: &'a CIDP,
60}
61
62/// seals a new block with the given params
63pub async fn seal_block<B, BI, SC, C, E, TP, CIDP, P>(
64	SealBlockParams {
65		create_empty,
66		finalize,
67		pool,
68		parent_hash,
69		client,
70		select_chain,
71		block_import,
72		env,
73		create_inherent_data_providers,
74		consensus_data_provider: digest_provider,
75		mut sender,
76	}: SealBlockParams<'_, B, BI, SC, C, E, TP, CIDP, P>,
77) where
78	B: BlockT,
79	BI: BlockImport<B, Error = sp_consensus::Error> + Send + Sync + 'static,
80	C: HeaderBackend<B> + ProvideRuntimeApi<B>,
81	E: Environment<B>,
82	E::Proposer: Proposer<B, Proof = P>,
83	TP: TransactionPool<Block = B>,
84	SC: SelectChain<B>,
85	CIDP: CreateInherentDataProviders<B, ()>,
86	P: codec::Encode + Send + Sync + 'static,
87{
88	let future = async {
89		if pool.status().ready == 0 && !create_empty {
90			return Err(Error::EmptyTransactionPool)
91		}
92
93		// get the header to build this new block on.
94		// use the parent_hash supplied via `EngineCommand`
95		// or fetch the best_block.
96		let parent = match parent_hash {
97			Some(hash) =>
98				client.header(hash)?.ok_or_else(|| Error::BlockNotFound(format!("{}", hash)))?,
99			None => select_chain.best_chain().await?,
100		};
101
102		let inherent_data_providers = create_inherent_data_providers
103			.create_inherent_data_providers(parent.hash(), ())
104			.await
105			.map_err(|e| Error::Other(e))?;
106
107		let inherent_data = inherent_data_providers.create_inherent_data().await?;
108
109		let proposer = env.init(&parent).map_err(|err| Error::StringError(err.to_string())).await?;
110		let inherents_len = inherent_data.len();
111
112		let digest = if let Some(digest_provider) = digest_provider {
113			digest_provider.create_digest(&parent, &inherent_data)?
114		} else {
115			Default::default()
116		};
117
118		let proposal = proposer
119			.propose(
120				inherent_data.clone(),
121				digest,
122				Duration::from_secs(MAX_PROPOSAL_DURATION),
123				None,
124			)
125			.map_err(|err| Error::StringError(err.to_string()))
126			.await?;
127
128		if proposal.block.extrinsics().len() == inherents_len && !create_empty {
129			return Err(Error::EmptyTransactionPool)
130		}
131
132		let (header, body) = proposal.block.deconstruct();
133		let proof = proposal.proof;
134		let proof_size = proof.encoded_size();
135		let mut params = BlockImportParams::new(BlockOrigin::Own, header.clone());
136		params.body = Some(body);
137		params.finalized = finalize;
138		params.fork_choice = Some(ForkChoiceStrategy::LongestChain);
139		params.state_action = StateAction::ApplyChanges(sc_consensus::StorageChanges::Changes(
140			proposal.storage_changes,
141		));
142
143		if let Some(digest_provider) = digest_provider {
144			digest_provider.append_block_import(&parent, &mut params, &inherent_data, proof)?;
145		}
146
147		// Make sure we return the same post-hash that will be calculated when importing the block
148		// This is important in case the digest_provider added any signature, seal, ect.
149		let mut post_header = header.clone();
150		post_header.digest_mut().logs.extend(params.post_digests.iter().cloned());
151
152		match block_import.import_block(params).await? {
153			ImportResult::Imported(aux) => Ok(CreatedBlock {
154				hash: <B as BlockT>::Header::hash(&post_header),
155				aux,
156				proof_size,
157			}),
158			other => Err(other.into()),
159		}
160	};
161
162	rpc::send_result(&mut sender, future.await)
163}