referrerpolicy=no-referrer-when-downgrade

sc_consensus_manual_seal/consensus/
babe.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//! BABE consensus data provider, This allows manual seal author blocks that are valid for runtimes
20//! that expect babe-specific digests.
21
22use super::ConsensusDataProvider;
23use crate::{Error, LOG_TARGET};
24use codec::Encode;
25use sc_client_api::{AuxStore, UsageProvider};
26use sc_consensus_babe::{
27	authorship, find_pre_digest, BabeIntermediate, CompatibleDigestItem, Epoch, INTERMEDIATE_KEY,
28};
29use sc_consensus_epochs::{
30	descendent_query, EpochHeader, SharedEpochChanges, ViableEpochDescriptor,
31};
32use sp_keystore::KeystorePtr;
33use std::sync::Arc;
34
35use sc_consensus::{BlockImportParams, ForkChoiceStrategy, Verifier};
36use sp_api::{ProvideRuntimeApi, StorageProof};
37use sp_blockchain::{HeaderBackend, HeaderMetadata};
38use sp_consensus_babe::{
39	digests::{NextEpochDescriptor, PreDigest, SecondaryPlainPreDigest},
40	inherents::BabeInherentData,
41	AuthorityId, BabeApi, BabeAuthorityWeight, BabeConfiguration, ConsensusLog, BABE_ENGINE_ID,
42};
43use sp_consensus_slots::Slot;
44use sp_inherents::InherentData;
45use sp_runtime::{
46	generic::Digest,
47	traits::{Block as BlockT, Header},
48	DigestItem,
49};
50use sp_timestamp::TimestampInherentData;
51
52/// Provides BABE-compatible predigests and BlockImportParams.
53/// Intended for use with BABE runtimes.
54pub struct BabeConsensusDataProvider<B: BlockT, C> {
55	/// shared reference to keystore
56	keystore: KeystorePtr,
57
58	/// Shared reference to the client.
59	client: Arc<C>,
60
61	/// Shared epoch changes
62	epoch_changes: SharedEpochChanges<B, Epoch>,
63
64	/// BABE config, gotten from the runtime.
65	/// NOTE: This is used to fetch `slot_duration` and `epoch_length` in the
66	/// `ConsensusDataProvider` implementation. Correct as far as these values
67	/// are not changed during an epoch change.
68	config: BabeConfiguration,
69
70	/// Authorities to be used for this babe chain.
71	authorities: Vec<(AuthorityId, BabeAuthorityWeight)>,
72}
73
74/// Verifier to be used for babe chains
75pub struct BabeVerifier<B: BlockT, C> {
76	/// Shared epoch changes
77	epoch_changes: SharedEpochChanges<B, Epoch>,
78
79	/// Shared reference to the client.
80	client: Arc<C>,
81}
82
83impl<B: BlockT, C> BabeVerifier<B, C> {
84	/// create a new verifier
85	pub fn new(epoch_changes: SharedEpochChanges<B, Epoch>, client: Arc<C>) -> BabeVerifier<B, C> {
86		BabeVerifier { epoch_changes, client }
87	}
88}
89
90/// The verifier for the manual seal engine; instantly finalizes.
91#[async_trait::async_trait]
92impl<B, C> Verifier<B> for BabeVerifier<B, C>
93where
94	B: BlockT,
95	C: HeaderBackend<B> + HeaderMetadata<B, Error = sp_blockchain::Error>,
96{
97	async fn verify(
98		&self,
99		mut import_params: BlockImportParams<B>,
100	) -> Result<BlockImportParams<B>, String> {
101		import_params.finalized = false;
102		import_params.fork_choice = Some(ForkChoiceStrategy::LongestChain);
103
104		let pre_digest = find_pre_digest::<B>(&import_params.header)?;
105
106		let parent_hash = import_params.header.parent_hash();
107		let parent = self
108			.client
109			.header(*parent_hash)
110			.ok()
111			.flatten()
112			.ok_or_else(|| format!("header for block {} not found", parent_hash))?;
113		let epoch_changes = self.epoch_changes.shared_data();
114		let epoch_descriptor = epoch_changes
115			.epoch_descriptor_for_child_of(
116				descendent_query(&*self.client),
117				&parent.hash(),
118				*parent.number(),
119				pre_digest.slot(),
120			)
121			.map_err(|e| format!("failed to fetch epoch_descriptor: {}", e))?
122			.ok_or_else(|| format!("{}", sp_consensus::Error::InvalidAuthoritiesSet))?;
123		// drop the lock
124		drop(epoch_changes);
125
126		import_params
127			.insert_intermediate(INTERMEDIATE_KEY, BabeIntermediate::<B> { epoch_descriptor });
128
129		Ok(import_params)
130	}
131}
132
133impl<B, C> BabeConsensusDataProvider<B, C>
134where
135	B: BlockT,
136	C: AuxStore
137		+ HeaderBackend<B>
138		+ ProvideRuntimeApi<B>
139		+ HeaderMetadata<B, Error = sp_blockchain::Error>
140		+ UsageProvider<B>,
141	C::Api: BabeApi<B>,
142{
143	pub fn new(
144		client: Arc<C>,
145		keystore: KeystorePtr,
146		epoch_changes: SharedEpochChanges<B, Epoch>,
147		authorities: Vec<(AuthorityId, BabeAuthorityWeight)>,
148	) -> Result<Self, Error> {
149		if authorities.is_empty() {
150			return Err(Error::StringError("Cannot supply empty authority set!".into()))
151		}
152
153		let config = sc_consensus_babe::configuration(&*client)?;
154
155		Ok(Self { config, client, keystore, epoch_changes, authorities })
156	}
157
158	fn epoch(&self, parent: &B::Header, slot: Slot) -> Result<Epoch, Error> {
159		let epoch_changes = self.epoch_changes.shared_data();
160		let epoch_descriptor = epoch_changes
161			.epoch_descriptor_for_child_of(
162				descendent_query(&*self.client),
163				&parent.hash(),
164				*parent.number(),
165				slot,
166			)
167			.map_err(|e| Error::StringError(format!("failed to fetch epoch_descriptor: {}", e)))?
168			.ok_or(sp_consensus::Error::InvalidAuthoritiesSet)?;
169
170		let epoch = epoch_changes
171			.viable_epoch(&epoch_descriptor, |slot| Epoch::genesis(&self.config, slot))
172			.ok_or_else(|| {
173				log::info!(target: LOG_TARGET, "create_digest: no viable_epoch :(");
174				sp_consensus::Error::InvalidAuthoritiesSet
175			})?;
176
177		Ok(epoch.as_ref().clone())
178	}
179}
180
181impl<B, C> ConsensusDataProvider<B> for BabeConsensusDataProvider<B, C>
182where
183	B: BlockT,
184	C: AuxStore
185		+ HeaderBackend<B>
186		+ HeaderMetadata<B, Error = sp_blockchain::Error>
187		+ UsageProvider<B>
188		+ ProvideRuntimeApi<B>,
189	C::Api: BabeApi<B>,
190{
191	fn create_digest(&self, parent: &B::Header, inherents: &InherentData) -> Result<Digest, Error> {
192		let slot = inherents
193			.babe_inherent_data()?
194			.ok_or_else(|| Error::StringError("No babe inherent data".into()))?;
195		let epoch = self.epoch(parent, slot)?;
196
197		// this is a dev node environment, we should always be able to claim a slot.
198		let logs = if let Some((predigest, _)) =
199			authorship::claim_slot(slot, &epoch, &self.keystore)
200		{
201			vec![<DigestItem as CompatibleDigestItem>::babe_pre_digest(predigest)]
202		} else {
203			// well we couldn't claim a slot because this is an existing chain and we're not in the
204			// authorities. we need to tell BabeBlockImport that the epoch has changed, and we put
205			// ourselves in the authorities.
206			let predigest =
207				PreDigest::SecondaryPlain(SecondaryPlainPreDigest { slot, authority_index: 0_u32 });
208
209			let mut epoch_changes = self.epoch_changes.shared_data();
210			let epoch_descriptor = epoch_changes
211				.epoch_descriptor_for_child_of(
212					descendent_query(&*self.client),
213					&parent.hash(),
214					*parent.number(),
215					slot,
216				)
217				.map_err(|e| {
218					Error::StringError(format!("failed to fetch epoch_descriptor: {}", e))
219				})?
220				.ok_or(sp_consensus::Error::InvalidAuthoritiesSet)?;
221
222			match epoch_descriptor {
223				ViableEpochDescriptor::Signaled(identifier, _epoch_header) => {
224					let epoch_mut = epoch_changes
225						.epoch_mut(&identifier)
226						.ok_or(sp_consensus::Error::InvalidAuthoritiesSet)?;
227
228					// mutate the current epoch
229					epoch_mut.authorities = self.authorities.clone();
230
231					let next_epoch = ConsensusLog::NextEpochData(NextEpochDescriptor {
232						authorities: self.authorities.clone(),
233						// copy the old randomness
234						randomness: epoch_mut.randomness,
235					});
236
237					vec![
238						DigestItem::PreRuntime(BABE_ENGINE_ID, predigest.encode()),
239						DigestItem::Consensus(BABE_ENGINE_ID, next_epoch.encode()),
240					]
241				},
242				ViableEpochDescriptor::UnimportedGenesis(_) => {
243					// since this is the genesis, secondary predigest works for now.
244					vec![DigestItem::PreRuntime(BABE_ENGINE_ID, predigest.encode())]
245				},
246			}
247		};
248
249		Ok(Digest { logs })
250	}
251
252	fn append_block_import(
253		&self,
254		parent: &B::Header,
255		params: &mut BlockImportParams<B>,
256		inherents: &InherentData,
257		_proof: StorageProof,
258	) -> Result<(), Error> {
259		let slot = inherents
260			.babe_inherent_data()?
261			.ok_or_else(|| Error::StringError("No babe inherent data".into()))?;
262		let epoch_changes = self.epoch_changes.shared_data();
263		let mut epoch_descriptor = epoch_changes
264			.epoch_descriptor_for_child_of(
265				descendent_query(&*self.client),
266				&parent.hash(),
267				*parent.number(),
268				slot,
269			)
270			.map_err(|e| Error::StringError(format!("failed to fetch epoch_descriptor: {}", e)))?
271			.ok_or(sp_consensus::Error::InvalidAuthoritiesSet)?;
272		// drop the lock
273		drop(epoch_changes);
274		// a quick check to see if we're in the authorities
275		let epoch = self.epoch(parent, slot)?;
276		let (authority, _) = self.authorities.first().expect("authorities is non-emptyp; qed");
277		let has_authority = epoch.authorities.iter().any(|(id, _)| *id == *authority);
278
279		if !has_authority {
280			log::info!(target: LOG_TARGET, "authority not found");
281			let timestamp = inherents
282				.timestamp_inherent_data()?
283				.ok_or_else(|| Error::StringError("No timestamp inherent data".into()))?;
284
285			let slot = Slot::from_timestamp(timestamp, self.config.slot_duration());
286
287			// manually hard code epoch descriptor
288			epoch_descriptor = match epoch_descriptor {
289				ViableEpochDescriptor::Signaled(identifier, _header) =>
290					ViableEpochDescriptor::Signaled(
291						identifier,
292						EpochHeader {
293							start_slot: slot,
294							end_slot: (*slot * self.config.epoch_length).into(),
295						},
296					),
297				_ => unreachable!(
298					"we're not in the authorities, so this isn't the genesis epoch; qed"
299				),
300			};
301		}
302
303		params.insert_intermediate(INTERMEDIATE_KEY, BabeIntermediate::<B> { epoch_descriptor });
304
305		Ok(())
306	}
307}