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 codec::Encode;
23use futures::prelude::*;
24use sc_consensus::{BlockImport, BlockImportParams, ForkChoiceStrategy, ImportResult, StateAction};
25use sc_transaction_pool_api::TransactionPool;
26use sp_api::{ProofRecorder, ProvideRuntimeApi};
27use sp_blockchain::HeaderBackend;
28use sp_consensus::{self, BlockOrigin, Environment, ProposeArgs, Proposer, SelectChain};
29use sp_externalities::Extensions;
30use sp_inherents::{CreateInherentDataProviders, InherentDataProvider};
31use sp_runtime::traits::{Block as BlockT, Header as HeaderT};
32use sp_trie::proof_size_extension::ProofSizeExt;
33use std::{sync::Arc, time::Duration};
34
35/// max duration for creating a proposal in secs
36pub const MAX_PROPOSAL_DURATION: u64 = 10;
37
38/// params for sealing a new block
39pub struct SealBlockParams<'a, B: BlockT, BI, SC, C: ProvideRuntimeApi<B>, E, TP, CIDP> {
40	/// if true, empty blocks(without extrinsics) will be created.
41	/// otherwise, will return Error::EmptyTransactionPool.
42	pub create_empty: bool,
43	/// instantly finalize this block?
44	pub finalize: bool,
45	/// specify the parent hash of the about-to-created block
46	pub parent_hash: Option<<B as BlockT>::Hash>,
47	/// sender to report errors/success to the rpc.
48	pub sender: rpc::Sender<CreatedBlock<<B as BlockT>::Hash>>,
49	/// transaction pool
50	pub pool: Arc<TP>,
51	/// header backend
52	pub client: Arc<C>,
53	/// Environment trait object for creating a proposer
54	pub env: &'a mut E,
55	/// SelectChain object
56	pub select_chain: &'a SC,
57	/// Digest provider for inclusion in blocks.
58	pub consensus_data_provider: Option<&'a dyn ConsensusDataProvider<B>>,
59	/// block import object
60	pub block_import: &'a mut BI,
61	/// Something that can create the inherent data providers.
62	pub create_inherent_data_providers: &'a CIDP,
63}
64
65/// seals a new block with the given params
66pub async fn seal_block<B, BI, SC, C, E, TP, CIDP>(
67	SealBlockParams {
68		create_empty,
69		finalize,
70		pool,
71		parent_hash,
72		client,
73		select_chain,
74		block_import,
75		env,
76		create_inherent_data_providers,
77		consensus_data_provider: digest_provider,
78		mut sender,
79	}: SealBlockParams<'_, B, BI, SC, C, E, TP, CIDP>,
80) where
81	B: BlockT,
82	BI: BlockImport<B, Error = sp_consensus::Error> + Send + Sync + 'static,
83	C: HeaderBackend<B> + ProvideRuntimeApi<B>,
84	E: Environment<B>,
85	E::Proposer: Proposer<B>,
86	TP: TransactionPool<Block = B>,
87	SC: SelectChain<B>,
88	CIDP: CreateInherentDataProviders<B, ()>,
89{
90	let future = async {
91		if pool.status().ready == 0 && !create_empty {
92			return Err(Error::EmptyTransactionPool)
93		}
94
95		// get the header to build this new block on.
96		// use the parent_hash supplied via `EngineCommand`
97		// or fetch the best_block.
98		let parent = match parent_hash {
99			Some(hash) =>
100				client.header(hash)?.ok_or_else(|| Error::BlockNotFound(format!("{}", hash)))?,
101			None => select_chain.best_chain().await?,
102		};
103
104		let inherent_data_providers = create_inherent_data_providers
105			.create_inherent_data_providers(parent.hash(), ())
106			.await
107			.map_err(|e| Error::Other(e))?;
108
109		let inherent_data = inherent_data_providers.create_inherent_data().await?;
110
111		let proposer = env.init(&parent).map_err(|err| Error::StringError(err.to_string())).await?;
112		let inherents_len = inherent_data.len();
113
114		let inherent_digests = if let Some(digest_provider) = digest_provider {
115			digest_provider.create_digest(&parent, &inherent_data)?
116		} else {
117			Default::default()
118		};
119
120		let storage_proof_recorder = ProofRecorder::<B>::default();
121
122		let mut extra_extensions = Extensions::default();
123		// Required by parachains
124		extra_extensions.register(ProofSizeExt::new(storage_proof_recorder.clone()));
125
126		let propose_args = ProposeArgs {
127			inherent_data: inherent_data.clone(),
128			inherent_digests,
129			max_duration: Duration::from_secs(MAX_PROPOSAL_DURATION),
130			storage_proof_recorder: Some(storage_proof_recorder.clone()),
131			extra_extensions,
132			..Default::default()
133		};
134
135		let proposal = proposer
136			.propose(propose_args)
137			.map_err(|err| Error::StringError(err.to_string()))
138			.await?;
139
140		if proposal.block.extrinsics().len() == inherents_len && !create_empty {
141			return Err(Error::EmptyTransactionPool)
142		}
143
144		let proof = storage_proof_recorder.drain_storage_proof();
145
146		let (header, body) = proposal.block.deconstruct();
147		let proof_size = proof.encoded_size();
148		let mut params = BlockImportParams::new(BlockOrigin::Own, header.clone());
149		params.body = Some(body);
150		params.finalized = finalize;
151		params.fork_choice = Some(ForkChoiceStrategy::LongestChain);
152		params.state_action = StateAction::ApplyChanges(sc_consensus::StorageChanges::Changes(
153			proposal.storage_changes,
154		));
155
156		if let Some(digest_provider) = digest_provider {
157			digest_provider.append_block_import(&parent, &mut params, &inherent_data, proof)?;
158		}
159
160		// Make sure we return the same post-hash that will be calculated when importing the block
161		// This is important in case the digest_provider added any signature, seal, etc.
162		let mut post_header = header.clone();
163		post_header.digest_mut().logs.extend(params.post_digests.iter().cloned());
164
165		match block_import.import_block(params).await? {
166			ImportResult::Imported(aux) => Ok(CreatedBlock {
167				hash: <B as BlockT>::Header::hash(&post_header),
168				aux,
169				proof_size,
170			}),
171			other => Err(other.into()),
172		}
173	};
174
175	rpc::send_result(&mut sender, future.await)
176}