Skip to main content

sc_consensus_beefy/
lib.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
19use crate::{
20	communication::{
21		notification::{
22			BeefyBestBlockSender, BeefyBestBlockStream, BeefyVersionedFinalityProofSender,
23			BeefyVersionedFinalityProofStream,
24		},
25		peers::KnownPeers,
26		request_response::{
27			outgoing_requests_engine::OnDemandJustificationsEngine, BeefyJustifsRequestHandler,
28		},
29	},
30	error::Error,
31	import::BeefyBlockImport,
32	metrics::register_metrics,
33};
34use futures::{stream::Fuse, FutureExt, StreamExt};
35use log::{debug, error, info, trace, warn};
36use parking_lot::Mutex;
37use prometheus_endpoint::Registry;
38use sc_client_api::{Backend, BlockBackend, BlockchainEvents, FinalityNotification, Finalizer};
39use sc_consensus::BlockImport;
40use sc_network::{NetworkRequest, NotificationService, ProtocolName};
41use sc_network_gossip::{GossipEngine, Network as GossipNetwork, Syncing as GossipSyncing};
42use sc_utils::mpsc::{tracing_unbounded, TracingUnboundedReceiver};
43use sp_api::ProvideRuntimeApi;
44use sp_blockchain::{Backend as BlockchainBackend, HeaderBackend};
45use sp_consensus::{Error as ConsensusError, SyncOracle};
46use sp_consensus_beefy::{
47	AuthorityIdBound, BeefyApi, ConsensusLog, PayloadProvider, ValidatorSet, BEEFY_ENGINE_ID,
48};
49use sp_keystore::KeystorePtr;
50use sp_runtime::traits::{Block, Header as HeaderT, NumberFor, Zero};
51use std::{
52	collections::{BTreeMap, VecDeque},
53	future::Future,
54	marker::PhantomData,
55	pin::Pin,
56	sync::Arc,
57	time::Duration,
58};
59
60mod aux_schema;
61mod error;
62mod keystore;
63mod metrics;
64mod round;
65mod worker;
66
67pub mod communication;
68pub mod import;
69pub mod justification;
70
71use crate::{
72	communication::gossip::GossipValidator,
73	fisherman::Fisherman,
74	justification::BeefyVersionedFinalityProof,
75	keystore::BeefyKeystore,
76	metrics::VoterMetrics,
77	round::Rounds,
78	worker::{BeefyWorker, PersistedState},
79};
80pub use communication::beefy_protocol_name::{
81	gossip_protocol_name, justifications_protocol_name as justifs_protocol_name,
82};
83use sp_runtime::generic::OpaqueDigestItemId;
84
85mod fisherman;
86#[cfg(test)]
87mod tests;
88
89const LOG_TARGET: &str = "beefy";
90
91const HEADER_SYNC_DELAY: Duration = Duration::from_secs(60);
92
93type FinalityNotifications<Block> =
94	sc_utils::mpsc::TracingUnboundedReceiver<UnpinnedFinalityNotification<Block>>;
95/// A convenience BEEFY client trait that defines all the type bounds a BEEFY client
96/// has to satisfy. Ideally that should actually be a trait alias. Unfortunately as
97/// of today, Rust does not allow a type alias to be used as a trait bound. Tracking
98/// issue is <https://github.com/rust-lang/rust/issues/41517>.
99pub trait Client<B, BE>:
100	BlockchainEvents<B> + HeaderBackend<B> + Finalizer<B, BE> + Send + Sync
101where
102	B: Block,
103	BE: Backend<B>,
104{
105	// empty
106}
107
108impl<B, BE, T> Client<B, BE> for T
109where
110	B: Block,
111	BE: Backend<B>,
112	T: BlockchainEvents<B>
113		+ HeaderBackend<B>
114		+ Finalizer<B, BE>
115		+ ProvideRuntimeApi<B>
116		+ Send
117		+ Sync,
118{
119	// empty
120}
121
122/// Links between the block importer, the background voter and the RPC layer,
123/// to be used by the voter.
124#[derive(Clone)]
125pub struct BeefyVoterLinks<B: Block, AuthorityId: AuthorityIdBound> {
126	// BlockImport -> Voter links
127	/// Stream of BEEFY signed commitments from block import to voter.
128	pub from_block_import_justif_stream: BeefyVersionedFinalityProofStream<B, AuthorityId>,
129
130	// Voter -> RPC links
131	/// Sends BEEFY signed commitments from voter to RPC.
132	pub to_rpc_justif_sender: BeefyVersionedFinalityProofSender<B, AuthorityId>,
133	/// Sends BEEFY best block hashes from voter to RPC.
134	pub to_rpc_best_block_sender: BeefyBestBlockSender<B>,
135}
136
137/// Links used by the BEEFY RPC layer, from the BEEFY background voter.
138#[derive(Clone)]
139pub struct BeefyRPCLinks<B: Block, AuthorityId: AuthorityIdBound> {
140	/// Stream of signed commitments coming from the voter.
141	pub from_voter_justif_stream: BeefyVersionedFinalityProofStream<B, AuthorityId>,
142	/// Stream of BEEFY best block hashes coming from the voter.
143	pub from_voter_best_beefy_stream: BeefyBestBlockStream<B>,
144}
145
146/// Make block importer and link half necessary to tie the background voter to it.
147pub fn beefy_block_import_and_links<B, BE, RuntimeApi, I, AuthorityId: AuthorityIdBound>(
148	wrapped_block_import: I,
149	backend: Arc<BE>,
150	runtime: Arc<RuntimeApi>,
151	prometheus_registry: Option<Registry>,
152) -> (
153	BeefyBlockImport<B, BE, RuntimeApi, I, AuthorityId>,
154	BeefyVoterLinks<B, AuthorityId>,
155	BeefyRPCLinks<B, AuthorityId>,
156)
157where
158	B: Block,
159	BE: Backend<B>,
160	I: BlockImport<B, Error = ConsensusError> + Send + Sync,
161	RuntimeApi: ProvideRuntimeApi<B> + Send + Sync,
162	RuntimeApi::Api: BeefyApi<B, AuthorityId>,
163	AuthorityId: AuthorityIdBound,
164{
165	// Voter -> RPC links
166	let (to_rpc_justif_sender, from_voter_justif_stream) =
167		BeefyVersionedFinalityProofStream::<B, AuthorityId>::channel();
168	let (to_rpc_best_block_sender, from_voter_best_beefy_stream) =
169		BeefyBestBlockStream::<B>::channel();
170
171	// BlockImport -> Voter links
172	let (to_voter_justif_sender, from_block_import_justif_stream) =
173		BeefyVersionedFinalityProofStream::<B, AuthorityId>::channel();
174	let metrics = register_metrics(prometheus_registry);
175
176	// BlockImport
177	let import = BeefyBlockImport::new(
178		backend,
179		runtime,
180		wrapped_block_import,
181		to_voter_justif_sender,
182		metrics,
183	);
184	let voter_links = BeefyVoterLinks {
185		from_block_import_justif_stream,
186		to_rpc_justif_sender,
187		to_rpc_best_block_sender,
188	};
189	let rpc_links = BeefyRPCLinks { from_voter_best_beefy_stream, from_voter_justif_stream };
190
191	(import, voter_links, rpc_links)
192}
193
194/// BEEFY gadget network parameters.
195pub struct BeefyNetworkParams<B: Block, N, S> {
196	/// Network implementing gossip, requests and sync-oracle.
197	pub network: Arc<N>,
198	/// Syncing service implementing a sync oracle and an event stream for peers.
199	pub sync: Arc<S>,
200	/// Handle for receiving notification events.
201	pub notification_service: Box<dyn NotificationService>,
202	/// Chain specific BEEFY gossip protocol name. See
203	/// [`communication::beefy_protocol_name::gossip_protocol_name`].
204	pub gossip_protocol_name: ProtocolName,
205	/// Chain specific BEEFY on-demand justifications protocol name. See
206	/// [`communication::beefy_protocol_name::justifications_protocol_name`].
207	pub justifications_protocol_name: ProtocolName,
208
209	pub _phantom: PhantomData<B>,
210}
211
212/// BEEFY gadget initialization parameters.
213pub struct BeefyParams<B: Block, BE, C, N, P, R, S, AuthorityId: AuthorityIdBound> {
214	/// BEEFY client
215	pub client: Arc<C>,
216	/// Client Backend
217	pub backend: Arc<BE>,
218	/// BEEFY Payload provider
219	pub payload_provider: P,
220	/// Runtime Api Provider
221	pub runtime: Arc<R>,
222	/// Local key store
223	pub key_store: Option<KeystorePtr>,
224	/// BEEFY voter network params
225	pub network_params: BeefyNetworkParams<B, N, S>,
226	/// Minimal delta between blocks, BEEFY should vote for
227	pub min_block_delta: u32,
228	/// Prometheus metric registry
229	pub prometheus_registry: Option<Registry>,
230	/// Links between the block importer, the background voter and the RPC layer.
231	pub links: BeefyVoterLinks<B, AuthorityId>,
232	/// Handler for incoming BEEFY justifications requests from a remote peer.
233	pub on_demand_justifications_handler: BeefyJustifsRequestHandler<B, C>,
234	/// Whether running under "Authority" role.
235	pub is_authority: bool,
236}
237/// Helper object holding BEEFY worker communication/gossip components.
238///
239/// These are created once, but will be reused if worker is restarted/reinitialized.
240pub(crate) struct BeefyComms<B: Block, N, AuthorityId: AuthorityIdBound> {
241	pub gossip_engine: GossipEngine<B>,
242	pub gossip_validator: Arc<GossipValidator<B, N, AuthorityId>>,
243	pub on_demand_justifications: OnDemandJustificationsEngine<B, AuthorityId>,
244}
245
246/// Helper builder object for building [worker::BeefyWorker].
247///
248/// It has to do it in two steps: initialization and build, because the first step can sleep waiting
249/// for certain chain and backend conditions, and while sleeping we still need to pump the
250/// GossipEngine. Once initialization is done, the GossipEngine (and other pieces) are added to get
251/// the complete [worker::BeefyWorker] object.
252pub(crate) struct BeefyWorkerBuilder<B: Block, BE, RuntimeApi, AuthorityId: AuthorityIdBound> {
253	// utilities
254	backend: Arc<BE>,
255	runtime: Arc<RuntimeApi>,
256	key_store: BeefyKeystore<AuthorityId>,
257	// voter metrics
258	metrics: Option<VoterMetrics>,
259	persisted_state: PersistedState<B, AuthorityId>,
260}
261
262impl<B, BE, R, AuthorityId> BeefyWorkerBuilder<B, BE, R, AuthorityId>
263where
264	B: Block + codec::Codec,
265	BE: Backend<B>,
266	R: ProvideRuntimeApi<B>,
267	R::Api: BeefyApi<B, AuthorityId>,
268	AuthorityId: AuthorityIdBound,
269{
270	/// This will wait for the chain to enable BEEFY (if not yet enabled) and also wait for the
271	/// backend to sync all headers required by the voter to build a contiguous chain of mandatory
272	/// justifications. Then it builds the initial voter state using a combination of previously
273	/// persisted state in AUX DB and latest chain information/progress.
274	///
275	/// Returns a sane `BeefyWorkerBuilder` that can build the `BeefyWorker`.
276	pub async fn async_initialize<N>(
277		backend: Arc<BE>,
278		runtime: Arc<R>,
279		key_store: BeefyKeystore<AuthorityId>,
280		metrics: Option<VoterMetrics>,
281		min_block_delta: u32,
282		gossip_validator: Arc<GossipValidator<B, N, AuthorityId>>,
283		finality_notifications: &mut Fuse<FinalityNotifications<B>>,
284		is_authority: bool,
285	) -> Result<Self, Error> {
286		// Wait for BEEFY pallet to be active before starting voter.
287		let (beefy_genesis, best_grandpa) =
288			wait_for_runtime_pallet(&*runtime, finality_notifications).await?;
289
290		let persisted_state = Self::load_or_init_state(
291			beefy_genesis,
292			best_grandpa,
293			min_block_delta,
294			backend.clone(),
295			runtime.clone(),
296			&key_store,
297			&metrics,
298			is_authority,
299		)
300		.await?;
301		// Update the gossip validator with the right starting round and set id.
302		persisted_state
303			.gossip_filter_config()
304			.map(|f| gossip_validator.update_filter(f))?;
305
306		Ok(BeefyWorkerBuilder { backend, runtime, key_store, metrics, persisted_state })
307	}
308
309	/// Takes rest of missing pieces as params and builds the `BeefyWorker`.
310	pub fn build<P, S, N>(
311		self,
312		payload_provider: P,
313		sync: Arc<S>,
314		comms: BeefyComms<B, N, AuthorityId>,
315		links: BeefyVoterLinks<B, AuthorityId>,
316		pending_justifications: BTreeMap<NumberFor<B>, BeefyVersionedFinalityProof<B, AuthorityId>>,
317		is_authority: bool,
318	) -> BeefyWorker<B, BE, P, R, S, N, AuthorityId> {
319		let key_store = Arc::new(self.key_store);
320		BeefyWorker {
321			backend: self.backend.clone(),
322			runtime: self.runtime.clone(),
323			key_store: key_store.clone(),
324			payload_provider,
325			sync,
326			fisherman: Arc::new(Fisherman::new(self.backend, self.runtime, key_store)),
327			metrics: self.metrics,
328			persisted_state: self.persisted_state,
329			comms,
330			links,
331			pending_justifications,
332			is_authority,
333		}
334	}
335
336	// If no persisted state present, walk back the chain from first GRANDPA notification to either:
337	//  - latest BEEFY finalized block, or if none found on the way,
338	//  - BEEFY pallet genesis;
339	// Enqueue any BEEFY mandatory blocks (session boundaries) found on the way, for voter to
340	// finalize.
341	async fn init_state(
342		beefy_genesis: NumberFor<B>,
343		best_grandpa: <B as Block>::Header,
344		min_block_delta: u32,
345		backend: Arc<BE>,
346		runtime: Arc<R>,
347	) -> Result<PersistedState<B, AuthorityId>, Error> {
348		let blockchain = backend.blockchain();
349
350		let beefy_genesis = runtime
351			.runtime_api()
352			.beefy_genesis(best_grandpa.hash())
353			.ok()
354			.flatten()
355			.filter(|genesis| *genesis == beefy_genesis)
356			.ok_or_else(|| Error::Backend("BEEFY pallet expected to be active.".into()))?;
357		// Walk back the imported blocks and initialize voter either, at the last block with
358		// a BEEFY justification, or at pallet genesis block; voter will resume from there.
359		let mut sessions = VecDeque::new();
360		let mut header = best_grandpa.clone();
361		let state = loop {
362			if let Some(true) = blockchain
363				.justifications(header.hash())
364				.ok()
365				.flatten()
366				.map(|justifs| justifs.get(BEEFY_ENGINE_ID).is_some())
367			{
368				debug!(
369					target: LOG_TARGET,
370					"🥩 Initialize BEEFY voter at last BEEFY finalized block: {:?}.",
371					*header.number()
372				);
373				let best_beefy = *header.number();
374				// If no session boundaries detected so far, just initialize new rounds here.
375				if sessions.is_empty() {
376					let active_set =
377						expect_validator_set(runtime.as_ref(), backend.as_ref(), &header).await?;
378					let mut rounds = Rounds::new(best_beefy, active_set);
379					// Mark the round as already finalized.
380					rounds.conclude(best_beefy);
381					sessions.push_front(rounds);
382				}
383				let state = PersistedState::checked_new(
384					best_grandpa,
385					best_beefy,
386					sessions,
387					min_block_delta,
388					beefy_genesis,
389				)
390				.ok_or_else(|| Error::Backend("Invalid BEEFY chain".into()))?;
391				break state;
392			}
393
394			if *header.number() == beefy_genesis {
395				// We've reached BEEFY genesis, initialize voter here.
396				let genesis_set =
397					expect_validator_set(runtime.as_ref(), backend.as_ref(), &header).await?;
398				info!(
399					target: LOG_TARGET,
400					"🥩 Loading BEEFY voter state from genesis on what appears to be first startup. \
401					Starting voting rounds at block {:?}, genesis validator set {:?}.",
402					beefy_genesis,
403					genesis_set,
404				);
405
406				sessions.push_front(Rounds::new(beefy_genesis, genesis_set));
407				break PersistedState::checked_new(
408					best_grandpa,
409					Zero::zero(),
410					sessions,
411					min_block_delta,
412					beefy_genesis,
413				)
414				.ok_or_else(|| Error::Backend("Invalid BEEFY chain".into()))?;
415			}
416
417			if let Some(active) = find_authorities_change::<B, AuthorityId>(&header) {
418				debug!(
419					target: LOG_TARGET,
420					"🥩 Marking block {:?} as BEEFY Mandatory.",
421					*header.number()
422				);
423				sessions.push_front(Rounds::new(*header.number(), active));
424			}
425
426			// Move up the chain.
427			header = wait_for_parent_header(blockchain, header, HEADER_SYNC_DELAY).await?;
428		};
429
430		aux_schema::write_current_version_and_voter_state(backend.as_ref(), &state)?;
431		Ok(state)
432	}
433
434	async fn load_or_init_state(
435		beefy_genesis: NumberFor<B>,
436		best_grandpa: <B as Block>::Header,
437		min_block_delta: u32,
438		backend: Arc<BE>,
439		runtime: Arc<R>,
440		key_store: &BeefyKeystore<AuthorityId>,
441		metrics: &Option<VoterMetrics>,
442		is_authority: bool,
443	) -> Result<PersistedState<B, AuthorityId>, Error> {
444		// Initialize voter state from AUX DB if compatible.
445		if let Some(mut state) = crate::aux_schema::load_and_migrate_persistent(backend.as_ref())?
446			// Verify state pallet genesis matches runtime.
447			.filter(|state| state.pallet_genesis() == beefy_genesis)
448		{
449			// Overwrite persisted state with current best GRANDPA block.
450			state.set_best_grandpa(best_grandpa.clone());
451			// Overwrite persisted data with newly provided `min_block_delta`.
452			state.set_min_block_delta(min_block_delta);
453			debug!(target: LOG_TARGET, "🥩 Loading BEEFY voter state from db.");
454			trace!(target: LOG_TARGET, "🥩 Loaded state: {:?}.", state);
455
456			// Make sure that all the headers that we need have been synced.
457			let mut new_sessions = vec![];
458			let mut header = best_grandpa.clone();
459			while *header.number() > state.best_beefy() {
460				if state.voting_oracle().can_add_session(*header.number()) {
461					if let Some(active) = find_authorities_change::<B, AuthorityId>(&header) {
462						new_sessions.push((active, *header.number()));
463					}
464				}
465				header =
466					wait_for_parent_header(backend.blockchain(), header, HEADER_SYNC_DELAY).await?;
467			}
468
469			// Make sure we didn't miss any sessions during node restart.
470			for (validator_set, new_session_start) in new_sessions.drain(..).rev() {
471				debug!(
472					target: LOG_TARGET,
473					"🥩 Handling missed BEEFY session after node restart: {:?}.",
474					new_session_start
475				);
476				state.init_session_at(
477					new_session_start,
478					validator_set,
479					key_store,
480					metrics,
481					is_authority,
482				);
483			}
484			return Ok(state);
485		}
486
487		// No valid voter-state persisted, re-initialize from pallet genesis.
488		Self::init_state(beefy_genesis, best_grandpa, min_block_delta, backend, runtime).await
489	}
490}
491
492/// Finality notification for consumption by BEEFY worker.
493/// This is a stripped down version of `sc_client_api::FinalityNotification` which does not keep
494/// blocks pinned.
495struct UnpinnedFinalityNotification<B: Block> {
496	/// Finalized block header hash.
497	pub hash: B::Hash,
498	/// Finalized block header.
499	pub header: B::Header,
500	/// Path from the old finalized to new finalized parent (implicitly finalized blocks).
501	///
502	/// This maps to the range `(old_finalized, new_finalized)`.
503	pub tree_route: Arc<[B::Hash]>,
504}
505
506impl<B: Block> From<FinalityNotification<B>> for UnpinnedFinalityNotification<B> {
507	fn from(value: FinalityNotification<B>) -> Self {
508		UnpinnedFinalityNotification {
509			hash: value.hash,
510			header: value.header,
511			tree_route: value.tree_route,
512		}
513	}
514}
515
516/// Start the BEEFY gadget.
517///
518/// This is a thin shim around running and awaiting a BEEFY worker.
519pub async fn start_beefy_gadget<B, BE, C, N, P, R, S, AuthorityId>(
520	beefy_params: BeefyParams<B, BE, C, N, P, R, S, AuthorityId>,
521) where
522	B: Block,
523	BE: Backend<B>,
524	C: Client<B, BE> + BlockBackend<B>,
525	P: PayloadProvider<B> + Clone,
526	R: ProvideRuntimeApi<B>,
527	R::Api: BeefyApi<B, AuthorityId>,
528	N: GossipNetwork<B> + NetworkRequest + Send + Sync + 'static,
529	S: GossipSyncing<B> + SyncOracle + 'static,
530	AuthorityId: AuthorityIdBound,
531{
532	let BeefyParams {
533		client,
534		backend,
535		payload_provider,
536		runtime,
537		key_store,
538		network_params,
539		min_block_delta,
540		prometheus_registry,
541		links,
542		mut on_demand_justifications_handler,
543		is_authority,
544	} = beefy_params;
545
546	let BeefyNetworkParams {
547		network,
548		sync,
549		notification_service,
550		gossip_protocol_name,
551		justifications_protocol_name,
552		..
553	} = network_params;
554
555	let metrics = register_metrics(prometheus_registry.clone());
556
557	let mut block_import_justif = links.from_block_import_justif_stream.subscribe(100_000).fuse();
558
559	// Subscribe to finality notifications and justifications before waiting for runtime pallet and
560	// reuse the streams, so we don't miss notifications while waiting for pallet to be available.
561	let finality_notifications = client.finality_notification_stream();
562	let (mut transformer, mut finality_notifications) =
563		finality_notification_transformer_future(finality_notifications);
564
565	let known_peers = Arc::new(Mutex::new(KnownPeers::new()));
566	// Default votes filter is to discard everything.
567	// Validator is updated later with correct starting round and set id.
568	let gossip_validator =
569		communication::gossip::GossipValidator::new(known_peers.clone(), network.clone());
570	let gossip_validator = Arc::new(gossip_validator);
571	let gossip_engine = GossipEngine::new(
572		network.clone(),
573		sync.clone(),
574		notification_service,
575		gossip_protocol_name.clone(),
576		gossip_validator.clone(),
577		None,
578	);
579
580	// The `GossipValidator` adds and removes known peers based on valid votes and network
581	// events.
582	let on_demand_justifications = OnDemandJustificationsEngine::new(
583		network.clone(),
584		justifications_protocol_name.clone(),
585		known_peers,
586		prometheus_registry.clone(),
587	);
588	let mut beefy_comms = BeefyComms { gossip_engine, gossip_validator, on_demand_justifications };
589
590	// We re-create and re-run the worker in this loop in order to quickly reinit and resume after
591	// select recoverable errors.
592	loop {
593		// Make sure to pump gossip engine while waiting for initialization conditions.
594		let worker_builder = futures::select! {
595			builder_init_result = BeefyWorkerBuilder::async_initialize(
596				backend.clone(),
597				runtime.clone(),
598				key_store.clone().into(),
599				metrics.clone(),
600				min_block_delta,
601				beefy_comms.gossip_validator.clone(),
602				&mut finality_notifications,
603				is_authority,
604			).fuse() => {
605				match builder_init_result {
606					Ok(builder) => builder,
607					Err(e) => {
608						error!(target: LOG_TARGET, "🥩 Error: {:?}. Terminating.", e);
609						return
610					},
611				}
612			},
613			// Pump gossip engine.
614			_ = &mut beefy_comms.gossip_engine => {
615				error!(target: LOG_TARGET, "🥩 Gossip engine has unexpectedly terminated.");
616				return
617			},
618			_ = &mut transformer => {
619				error!(target: LOG_TARGET, "🥩 Finality notification transformer task has unexpectedly terminated.");
620				return
621			},
622		};
623
624		let worker = worker_builder.build(
625			payload_provider.clone(),
626			sync.clone(),
627			beefy_comms,
628			links.clone(),
629			BTreeMap::new(),
630			is_authority,
631		);
632
633		futures::select! {
634			result = worker.run(&mut block_import_justif, &mut finality_notifications).fuse() => {
635				match result {
636					(error::Error::ConsensusReset, reuse_comms) => {
637						error!(target: LOG_TARGET, "🥩 Error: {:?}. Restarting voter.", error::Error::ConsensusReset);
638						beefy_comms = reuse_comms;
639						continue;
640					},
641					(err, _) => {
642						error!(target: LOG_TARGET, "🥩 Error: {:?}. Terminating.", err)
643					}
644				}
645			},
646			odj_handler_error = on_demand_justifications_handler.run().fuse() => {
647				error!(target: LOG_TARGET, "🥩 Error: {:?}. Terminating.", odj_handler_error)
648			},
649			_ = &mut transformer => {
650				error!(target: LOG_TARGET, "🥩 Finality notification transformer task has unexpectedly terminated.");
651			}
652		}
653		return;
654	}
655}
656
657/// Produce a future that transformes finality notifications into a struct that does not keep blocks
658/// pinned.
659fn finality_notification_transformer_future<B>(
660	mut finality_notifications: sc_client_api::FinalityNotifications<B>,
661) -> (
662	Pin<Box<futures::future::Fuse<impl Future<Output = ()> + Sized>>>,
663	Fuse<TracingUnboundedReceiver<UnpinnedFinalityNotification<B>>>,
664)
665where
666	B: Block,
667{
668	let (tx, rx) = tracing_unbounded("beefy-notification-transformer-channel", 10000);
669	let transformer_fut = async move {
670		while let Some(notification) = finality_notifications.next().await {
671			debug!(target: LOG_TARGET, "🥩 Transforming grandpa notification. #{}({:?})", notification.header.number(), notification.hash);
672			if let Err(err) = tx.unbounded_send(UnpinnedFinalityNotification::from(notification)) {
673				error!(target: LOG_TARGET, "🥩 Unable to send transformed notification. Shutting down. err = {}", err);
674				return;
675			};
676		}
677	};
678	(Box::pin(transformer_fut.fuse()), rx.fuse())
679}
680
681/// Waits until the parent header of `current` is available and returns it.
682///
683/// When the node uses GRANDPA warp sync it initially downloads only the mandatory GRANDPA headers.
684/// The rest of the headers (gap sync) are lazily downloaded later. But the BEEFY voter also needs
685/// the headers in range `[beefy_genesis..=best_grandpa]` to be available. This helper method
686/// enables us to wait until these headers have been synced.
687async fn wait_for_parent_header<B, BC>(
688	blockchain: &BC,
689	current: <B as Block>::Header,
690	delay: Duration,
691) -> Result<<B as Block>::Header, Error>
692where
693	B: Block,
694	BC: BlockchainBackend<B>,
695{
696	if *current.number() == Zero::zero() {
697		let msg = format!("header {} is Genesis, there is no parent for it", current.hash());
698		warn!(target: LOG_TARGET, "{}", msg);
699		return Err(Error::Backend(msg));
700	}
701	loop {
702		match blockchain
703			.header(*current.parent_hash())
704			.map_err(|e| Error::Backend(e.to_string()))?
705		{
706			Some(parent) => return Ok(parent),
707			None => {
708				info!(
709					target: LOG_TARGET,
710					"🥩 Parent of header number {} not found. \
711					BEEFY gadget waiting for header sync to finish ...",
712					current.number()
713				);
714				tokio::time::sleep(delay).await;
715			},
716		}
717	}
718}
719
720/// Wait for BEEFY runtime pallet to be available, return active validator set.
721/// Should be called only once during worker initialization.
722async fn wait_for_runtime_pallet<B, R, AuthorityId: AuthorityIdBound>(
723	runtime: &R,
724	finality: &mut Fuse<FinalityNotifications<B>>,
725) -> Result<(NumberFor<B>, <B as Block>::Header), Error>
726where
727	B: Block,
728	R: ProvideRuntimeApi<B>,
729	R::Api: BeefyApi<B, AuthorityId>,
730{
731	info!(target: LOG_TARGET, "🥩 BEEFY gadget waiting for BEEFY pallet to become available...");
732	loop {
733		let notif = finality.next().await.ok_or_else(|| {
734			let err_msg = "🥩 Finality stream has unexpectedly terminated.".into();
735			error!(target: LOG_TARGET, "{}", err_msg);
736			Error::Backend(err_msg)
737		})?;
738		let at = notif.header.hash();
739		if let Some(start) = runtime.runtime_api().beefy_genesis(at).ok().flatten() {
740			if *notif.header.number() >= start {
741				// Beefy pallet available, return header for best grandpa at the time.
742				info!(
743					target: LOG_TARGET,
744					"🥩 BEEFY pallet available: block {:?} beefy genesis {:?}",
745					notif.header.number(), start
746				);
747				return Ok((start, notif.header));
748			}
749		}
750	}
751}
752
753/// Provides validator set active `at_header`. It tries to get it from state, otherwise falls
754/// back to walk up the chain looking the validator set enactment in header digests.
755///
756/// Note: function will `async::sleep()` when walking back the chain if some needed header hasn't
757/// been synced yet (as it happens when warp syncing when headers are synced in the background).
758async fn expect_validator_set<B, BE, R, AuthorityId: AuthorityIdBound>(
759	runtime: &R,
760	backend: &BE,
761	at_header: &B::Header,
762) -> Result<ValidatorSet<AuthorityId>, Error>
763where
764	B: Block,
765	BE: Backend<B>,
766	R: ProvideRuntimeApi<B>,
767	R::Api: BeefyApi<B, AuthorityId>,
768{
769	let blockchain = backend.blockchain();
770	// Walk up the chain looking for the validator set active at 'at_header'. Process both state and
771	// header digests.
772	debug!(
773		target: LOG_TARGET,
774		"🥩 Trying to find validator set active at header(number {:?}, hash {:?})",
775		at_header.number(),
776		at_header.hash()
777	);
778	let mut header = at_header.clone();
779	loop {
780		debug!(target: LOG_TARGET, "🥩 Looking for auth set change at block number: {:?}", *header.number());
781		if let Ok(Some(active)) = runtime.runtime_api().validator_set(header.hash()) {
782			return Ok(active);
783		} else {
784			match find_authorities_change::<B, AuthorityId>(&header) {
785				Some(active) => return Ok(active),
786				// Move up the chain. Ultimately we'll get it from chain genesis state, or error out
787				// there.
788				None => {
789					header = wait_for_parent_header(blockchain, header, HEADER_SYNC_DELAY)
790						.await
791						.map_err(|e| Error::Backend(e.to_string()))?
792				},
793			}
794		}
795	}
796}
797
798/// Scan the `header` digest log for a BEEFY validator set change. Return either the new
799/// validator set or `None` in case no validator set change has been signaled.
800pub(crate) fn find_authorities_change<B, AuthorityId>(
801	header: &B::Header,
802) -> Option<ValidatorSet<AuthorityId>>
803where
804	B: Block,
805	AuthorityId: AuthorityIdBound,
806{
807	let id = OpaqueDigestItemId::Consensus(&BEEFY_ENGINE_ID);
808
809	let filter = |log: ConsensusLog<AuthorityId>| match log {
810		ConsensusLog::AuthoritiesChange(validator_set) => Some(validator_set),
811		_ => None,
812	};
813	header.digest().convert_first(|l| l.try_to(id).and_then(filter))
814}