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