diff --git a/Cargo.lock b/Cargo.lock
index e8732f64efa07f63de43c4013a48744084b748a7..781dba880cbe5c735bdf0b2c1087a81be60ff04b 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -8912,10 +8912,12 @@ dependencies = [
  "sc-sync-state-rpc",
  "sc-transaction-pool-api",
  "sp-api",
+ "sp-application-crypto",
  "sp-block-builder",
  "sp-blockchain",
  "sp-consensus",
  "sp-consensus-babe",
+ "sp-consensus-beefy",
  "sp-keystore",
  "sp-runtime",
  "sp-statement-store",
@@ -13704,10 +13706,12 @@ dependencies = [
  "sc-sync-state-rpc",
  "sc-transaction-pool-api",
  "sp-api",
+ "sp-application-crypto",
  "sp-block-builder",
  "sp-blockchain",
  "sp-consensus",
  "sp-consensus-babe",
+ "sp-consensus-beefy",
  "sp-keystore",
  "sp-runtime",
  "substrate-frame-rpc-system",
@@ -17036,6 +17040,7 @@ dependencies = [
  "sc-rpc",
  "serde",
  "serde_json",
+ "sp-application-crypto",
  "sp-consensus-beefy",
  "sp-core",
  "sp-runtime",
diff --git a/polkadot/node/service/src/lib.rs b/polkadot/node/service/src/lib.rs
index 7c9b9e05d62c3c72c7a86976ac54490fba426ca5..9ee81f80d66a545fbc704068b62cdebf01c06a08 100644
--- a/polkadot/node/service/src/lib.rs
+++ b/polkadot/node/service/src/lib.rs
@@ -88,6 +88,7 @@ use telemetry::TelemetryWorker;
 #[cfg(feature = "full-node")]
 use telemetry::{Telemetry, TelemetryWorkerHandle};
 
+use beefy_primitives::ecdsa_crypto;
 pub use chain_spec::{GenericChainSpec, RococoChainSpec, WestendChainSpec};
 pub use consensus_common::{Proposal, SelectChain};
 use frame_benchmarking_cli::SUBSTRATE_REFERENCE_HARDWARE;
@@ -394,8 +395,8 @@ type FullSelectChain = relay_chain_selection::SelectRelayChain<FullBackend>;
 type FullGrandpaBlockImport<ChainSelection = FullSelectChain> =
 	grandpa::GrandpaBlockImport<FullBackend, Block, FullClient, ChainSelection>;
 #[cfg(feature = "full-node")]
-type FullBeefyBlockImport<InnerBlockImport> =
-	beefy::import::BeefyBlockImport<Block, FullBackend, FullClient, InnerBlockImport>;
+type FullBeefyBlockImport<InnerBlockImport, AuthorityId> =
+	beefy::import::BeefyBlockImport<Block, FullBackend, FullClient, InnerBlockImport, AuthorityId>;
 
 #[cfg(feature = "full-node")]
 struct Basics {
@@ -486,11 +487,14 @@ fn new_partial<ChainSelection>(
 				babe::BabeBlockImport<
 					Block,
 					FullClient,
-					FullBeefyBlockImport<FullGrandpaBlockImport<ChainSelection>>,
+					FullBeefyBlockImport<
+						FullGrandpaBlockImport<ChainSelection>,
+						ecdsa_crypto::AuthorityId,
+					>,
 				>,
 				grandpa::LinkHalf<Block, FullClient, ChainSelection>,
 				babe::BabeLink<Block>,
-				beefy::BeefyVoterLinks<Block>,
+				beefy::BeefyVoterLinks<Block, ecdsa_crypto::AuthorityId>,
 			),
 			grandpa::SharedVoterState,
 			sp_consensus_babe::SlotDuration,
@@ -601,7 +605,7 @@ where
 					subscription_executor: subscription_executor.clone(),
 					finality_provider: finality_proof_provider.clone(),
 				},
-				beefy: polkadot_rpc::BeefyDeps {
+				beefy: polkadot_rpc::BeefyDeps::<ecdsa_crypto::AuthorityId> {
 					beefy_finality_proof_stream: beefy_rpc_links.from_voter_justif_stream.clone(),
 					beefy_best_block_stream: beefy_rpc_links.from_voter_best_beefy_stream.clone(),
 					subscription_executor,
@@ -1293,7 +1297,9 @@ pub fn new_full<
 			is_authority: role.is_authority(),
 		};
 
-		let gadget = beefy::start_beefy_gadget::<_, _, _, _, _, _, _>(beefy_params);
+		let gadget = beefy::start_beefy_gadget::<_, _, _, _, _, _, _, ecdsa_crypto::AuthorityId>(
+			beefy_params,
+		);
 
 		// BEEFY is part of consensus, if it fails we'll bring the node down with it to make sure it
 		// is noticed.
diff --git a/polkadot/rpc/Cargo.toml b/polkadot/rpc/Cargo.toml
index 5af5e63b175380f7e695deb973ea48df52c326e4..1900b595d671540ee3518bd1a7bd0fc061c073f2 100644
--- a/polkadot/rpc/Cargo.toml
+++ b/polkadot/rpc/Cargo.toml
@@ -17,8 +17,10 @@ sp-blockchain = { path = "../../substrate/primitives/blockchain" }
 sp-keystore = { path = "../../substrate/primitives/keystore" }
 sp-runtime = { path = "../../substrate/primitives/runtime" }
 sp-api = { path = "../../substrate/primitives/api" }
+sp-application-crypto = { path = "../../substrate/primitives/application-crypto" }
 sp-consensus = { path = "../../substrate/primitives/consensus/common" }
 sp-consensus-babe = { path = "../../substrate/primitives/consensus/babe" }
+sp-consensus-beefy = { path = "../../substrate/primitives/consensus/beefy" }
 sc-chain-spec = { path = "../../substrate/client/chain-spec" }
 sc-rpc = { path = "../../substrate/client/rpc" }
 sc-rpc-spec-v2 = { path = "../../substrate/client/rpc-spec-v2" }
diff --git a/polkadot/rpc/src/lib.rs b/polkadot/rpc/src/lib.rs
index 4455efd3b5337be85fb975f368af9475b20b0b89..2daa246102fc24f08d0e04c9d5640aa69aced086 100644
--- a/polkadot/rpc/src/lib.rs
+++ b/polkadot/rpc/src/lib.rs
@@ -29,10 +29,12 @@ use sc_consensus_beefy::communication::notification::{
 use sc_consensus_grandpa::FinalityProofProvider;
 pub use sc_rpc::{DenyUnsafe, SubscriptionTaskExecutor};
 use sp_api::ProvideRuntimeApi;
+use sp_application_crypto::RuntimeAppPublic;
 use sp_block_builder::BlockBuilder;
 use sp_blockchain::{Error as BlockChainError, HeaderBackend, HeaderMetadata};
 use sp_consensus::SelectChain;
 use sp_consensus_babe::BabeApi;
+use sp_consensus_beefy::AuthorityIdBound;
 use sp_keystore::KeystorePtr;
 use txpool_api::TransactionPool;
 
@@ -62,9 +64,9 @@ pub struct GrandpaDeps<B> {
 }
 
 /// Dependencies for BEEFY
-pub struct BeefyDeps {
+pub struct BeefyDeps<AuthorityId: AuthorityIdBound> {
 	/// Receives notifications about finality proof events from BEEFY.
-	pub beefy_finality_proof_stream: BeefyVersionedFinalityProofStream<Block>,
+	pub beefy_finality_proof_stream: BeefyVersionedFinalityProofStream<Block, AuthorityId>,
 	/// Receives notifications about best block events from BEEFY.
 	pub beefy_best_block_stream: BeefyBestBlockStream<Block>,
 	/// Executor to drive the subscription manager in the BEEFY RPC handler.
@@ -72,7 +74,7 @@ pub struct BeefyDeps {
 }
 
 /// Full client dependencies
-pub struct FullDeps<C, P, SC, B> {
+pub struct FullDeps<C, P, SC, B, AuthorityId: AuthorityIdBound> {
 	/// The client instance to use.
 	pub client: Arc<C>,
 	/// Transaction pool instance.
@@ -88,14 +90,14 @@ pub struct FullDeps<C, P, SC, B> {
 	/// GRANDPA specific dependencies.
 	pub grandpa: GrandpaDeps<B>,
 	/// BEEFY specific dependencies.
-	pub beefy: BeefyDeps,
+	pub beefy: BeefyDeps<AuthorityId>,
 	/// Backend used by the node.
 	pub backend: Arc<B>,
 }
 
 /// Instantiate all RPC extensions.
-pub fn create_full<C, P, SC, B>(
-	FullDeps { client, pool, select_chain, chain_spec, deny_unsafe, babe, grandpa, beefy, backend } : FullDeps<C, P, SC, B>,
+pub fn create_full<C, P, SC, B, AuthorityId>(
+	FullDeps { client, pool, select_chain, chain_spec, deny_unsafe, babe, grandpa, beefy, backend } : FullDeps<C, P, SC, B, AuthorityId>,
 ) -> Result<RpcExtension, Box<dyn std::error::Error + Send + Sync>>
 where
 	C: ProvideRuntimeApi<Block>
@@ -114,6 +116,8 @@ where
 	SC: SelectChain<Block> + 'static,
 	B: sc_client_api::Backend<Block> + Send + Sync + 'static,
 	B::State: sc_client_api::StateBackend<sp_runtime::traits::HashingFor<Block>>,
+	AuthorityId: AuthorityIdBound,
+	<AuthorityId as RuntimeAppPublic>::Signature: Send + Sync,
 {
 	use frame_rpc_system::{System, SystemApiServer};
 	use mmr_rpc::{Mmr, MmrApiServer};
@@ -171,7 +175,7 @@ where
 	)?;
 
 	io.merge(
-		Beefy::<Block>::new(
+		Beefy::<Block, AuthorityId>::new(
 			beefy.beefy_finality_proof_stream,
 			beefy.beefy_best_block_stream,
 			beefy.subscription_executor,
diff --git a/substrate/bin/node/cli/src/service.rs b/substrate/bin/node/cli/src/service.rs
index 84903bd9b872322c75346a74195aa916d5cc4843..e57ca04f3b743ff3fab2f492e90a2d92cfdf8fd5 100644
--- a/substrate/bin/node/cli/src/service.rs
+++ b/substrate/bin/node/cli/src/service.rs
@@ -20,7 +20,10 @@
 
 //! Service implementation. Specialized wrapper over substrate service.
 
-use polkadot_sdk::{sc_consensus_beefy as beefy, sc_consensus_grandpa as grandpa, *};
+use polkadot_sdk::{
+	sc_consensus_beefy as beefy, sc_consensus_grandpa as grandpa,
+	sp_consensus_beefy as beefy_primitives, *,
+};
 
 use crate::Cli;
 use codec::Encode;
@@ -67,8 +70,13 @@ type FullBackend = sc_service::TFullBackend<Block>;
 type FullSelectChain = sc_consensus::LongestChain<FullBackend, Block>;
 type FullGrandpaBlockImport =
 	grandpa::GrandpaBlockImport<FullBackend, Block, FullClient, FullSelectChain>;
-type FullBeefyBlockImport<InnerBlockImport> =
-	beefy::import::BeefyBlockImport<Block, FullBackend, FullClient, InnerBlockImport>;
+type FullBeefyBlockImport<InnerBlockImport> = beefy::import::BeefyBlockImport<
+	Block,
+	FullBackend,
+	FullClient,
+	InnerBlockImport,
+	beefy_primitives::ecdsa_crypto::AuthorityId,
+>;
 
 /// The transaction pool type definition.
 pub type TransactionPool = sc_transaction_pool::FullPool<Block, FullClient>;
@@ -180,7 +188,7 @@ pub fn new_partial(
 				>,
 				grandpa::LinkHalf<Block, FullClient, FullSelectChain>,
 				sc_consensus_babe::BabeLink<Block>,
-				beefy::BeefyVoterLinks<Block>,
+				beefy::BeefyVoterLinks<Block, beefy_primitives::ecdsa_crypto::AuthorityId>,
 			),
 			grandpa::SharedVoterState,
 			Option<Telemetry>,
@@ -328,7 +336,7 @@ pub fn new_partial(
 						subscription_executor: subscription_executor.clone(),
 						finality_provider: finality_proof_provider.clone(),
 					},
-					beefy: node_rpc::BeefyDeps {
+					beefy: node_rpc::BeefyDeps::<beefy_primitives::ecdsa_crypto::AuthorityId> {
 						beefy_finality_proof_stream: beefy_rpc_links
 							.from_voter_justif_stream
 							.clone(),
@@ -683,7 +691,7 @@ pub fn new_full_base<N: NetworkBackend<Block, <Block as BlockT>::Hash>>(
 		is_authority: role.is_authority(),
 	};
 
-	let beefy_gadget = beefy::start_beefy_gadget::<_, _, _, _, _, _, _>(beefy_params);
+	let beefy_gadget = beefy::start_beefy_gadget::<_, _, _, _, _, _, _, _>(beefy_params);
 	// BEEFY is part of consensus, if it fails we'll bring the node down with it to make sure it
 	// is noticed.
 	task_manager
diff --git a/substrate/bin/node/rpc/Cargo.toml b/substrate/bin/node/rpc/Cargo.toml
index 894dbf0da85ca56d412087adf01fa12c7983ae7a..6ae80eb578596490753d903d253c01af2660ef4f 100644
--- a/substrate/bin/node/rpc/Cargo.toml
+++ b/substrate/bin/node/rpc/Cargo.toml
@@ -26,6 +26,7 @@ sc-consensus-babe = { path = "../../../client/consensus/babe" }
 sc-consensus-babe-rpc = { path = "../../../client/consensus/babe/rpc" }
 sc-consensus-beefy = { path = "../../../client/consensus/beefy" }
 sc-consensus-beefy-rpc = { path = "../../../client/consensus/beefy/rpc" }
+sp-consensus-beefy = { path = "../../../primitives/consensus/beefy" }
 sc-consensus-grandpa = { path = "../../../client/consensus/grandpa" }
 sc-consensus-grandpa-rpc = { path = "../../../client/consensus/grandpa/rpc" }
 sc-mixnet = { path = "../../../client/mixnet" }
@@ -41,6 +42,7 @@ sp-consensus = { path = "../../../primitives/consensus/common" }
 sp-consensus-babe = { path = "../../../primitives/consensus/babe" }
 sp-keystore = { path = "../../../primitives/keystore" }
 sp-runtime = { path = "../../../primitives/runtime" }
+sp-application-crypto = { path = "../../../primitives/application-crypto" }
 sp-statement-store = { path = "../../../primitives/statement-store" }
 substrate-frame-rpc-system = { path = "../../../utils/frame/rpc/system" }
 substrate-state-trie-migration-rpc = { path = "../../../utils/frame/rpc/state-trie-migration-rpc" }
diff --git a/substrate/bin/node/rpc/src/lib.rs b/substrate/bin/node/rpc/src/lib.rs
index 4646524a25babfd7cb1276326d4704abdcc65622..52cd7f9561d2a9e8287e85e1fdc7dc80f4bf5a3f 100644
--- a/substrate/bin/node/rpc/src/lib.rs
+++ b/substrate/bin/node/rpc/src/lib.rs
@@ -47,10 +47,12 @@ pub use sc_rpc::SubscriptionTaskExecutor;
 pub use sc_rpc_api::DenyUnsafe;
 use sc_transaction_pool_api::TransactionPool;
 use sp_api::ProvideRuntimeApi;
+use sp_application_crypto::RuntimeAppPublic;
 use sp_block_builder::BlockBuilder;
 use sp_blockchain::{Error as BlockChainError, HeaderBackend, HeaderMetadata};
 use sp_consensus::SelectChain;
 use sp_consensus_babe::BabeApi;
+use sp_consensus_beefy::AuthorityIdBound;
 use sp_keystore::KeystorePtr;
 
 /// Extra dependencies for BABE.
@@ -76,9 +78,9 @@ pub struct GrandpaDeps<B> {
 }
 
 /// Dependencies for BEEFY
-pub struct BeefyDeps {
+pub struct BeefyDeps<AuthorityId: AuthorityIdBound> {
 	/// Receives notifications about finality proof events from BEEFY.
-	pub beefy_finality_proof_stream: BeefyVersionedFinalityProofStream<Block>,
+	pub beefy_finality_proof_stream: BeefyVersionedFinalityProofStream<Block, AuthorityId>,
 	/// Receives notifications about best block events from BEEFY.
 	pub beefy_best_block_stream: BeefyBestBlockStream<Block>,
 	/// Executor to drive the subscription manager in the BEEFY RPC handler.
@@ -86,7 +88,7 @@ pub struct BeefyDeps {
 }
 
 /// Full client dependencies.
-pub struct FullDeps<C, P, SC, B> {
+pub struct FullDeps<C, P, SC, B, AuthorityId: AuthorityIdBound> {
 	/// The client instance to use.
 	pub client: Arc<C>,
 	/// Transaction pool instance.
@@ -102,7 +104,7 @@ pub struct FullDeps<C, P, SC, B> {
 	/// GRANDPA specific dependencies.
 	pub grandpa: GrandpaDeps<B>,
 	/// BEEFY specific dependencies.
-	pub beefy: BeefyDeps,
+	pub beefy: BeefyDeps<AuthorityId>,
 	/// Shared statement store reference.
 	pub statement_store: Arc<dyn sp_statement_store::StatementStore>,
 	/// The backend used by the node.
@@ -112,7 +114,7 @@ pub struct FullDeps<C, P, SC, B> {
 }
 
 /// Instantiate all Full RPC extensions.
-pub fn create_full<C, P, SC, B>(
+pub fn create_full<C, P, SC, B, AuthorityId>(
 	FullDeps {
 		client,
 		pool,
@@ -125,7 +127,7 @@ pub fn create_full<C, P, SC, B>(
 		statement_store,
 		backend,
 		mixnet_api,
-	}: FullDeps<C, P, SC, B>,
+	}: FullDeps<C, P, SC, B, AuthorityId>,
 ) -> Result<RpcModule<()>, Box<dyn std::error::Error + Send + Sync>>
 where
 	C: ProvideRuntimeApi<Block>
@@ -145,6 +147,8 @@ where
 	SC: SelectChain<Block> + 'static,
 	B: sc_client_api::Backend<Block> + Send + Sync + 'static,
 	B::State: sc_client_api::backend::StateBackend<sp_runtime::traits::HashingFor<Block>>,
+	AuthorityId: AuthorityIdBound,
+	<AuthorityId as RuntimeAppPublic>::Signature: Send + Sync,
 {
 	use mmr_rpc::{Mmr, MmrApiServer};
 	use pallet_transaction_payment_rpc::{TransactionPayment, TransactionPaymentApiServer};
@@ -223,7 +227,7 @@ where
 	}
 
 	io.merge(
-		Beefy::<Block>::new(
+		Beefy::<Block, AuthorityId>::new(
 			beefy.beefy_finality_proof_stream,
 			beefy.beefy_best_block_stream,
 			beefy.subscription_executor,
diff --git a/substrate/client/consensus/beefy/Cargo.toml b/substrate/client/consensus/beefy/Cargo.toml
index 193acbe52a1e8b3bc7752b984296bb9c1456d483..cd183f6bc8b0562a334d59f86fa58699c63a7a73 100644
--- a/substrate/client/consensus/beefy/Cargo.toml
+++ b/substrate/client/consensus/beefy/Cargo.toml
@@ -42,7 +42,6 @@ sp-keystore = { path = "../../../primitives/keystore" }
 sp-runtime = { path = "../../../primitives/runtime" }
 tokio = "1.37"
 
-
 [dev-dependencies]
 serde = { workspace = true, default-features = true }
 tempfile = "3.1.0"
diff --git a/substrate/client/consensus/beefy/rpc/Cargo.toml b/substrate/client/consensus/beefy/rpc/Cargo.toml
index 07e46dbda156a79a77a60e8340e46a323e133fd2..84f90622b5c14f8b7cba19749c877a6ce53ec226 100644
--- a/substrate/client/consensus/beefy/rpc/Cargo.toml
+++ b/substrate/client/consensus/beefy/rpc/Cargo.toml
@@ -24,6 +24,7 @@ sp-consensus-beefy = { path = "../../../../primitives/consensus/beefy" }
 sc-rpc = { path = "../../../rpc" }
 sp-core = { path = "../../../../primitives/core" }
 sp-runtime = { path = "../../../../primitives/runtime" }
+sp-application-crypto = { path = "../../../../primitives/application-crypto" }
 
 [dev-dependencies]
 serde_json = { workspace = true, default-features = true }
diff --git a/substrate/client/consensus/beefy/rpc/src/lib.rs b/substrate/client/consensus/beefy/rpc/src/lib.rs
index f01baee2d6ece9a9d1dd36e1524a1d004e9b0401..66102eeb35c897a7bff4e50a12da1b90754eeeaa 100644
--- a/substrate/client/consensus/beefy/rpc/src/lib.rs
+++ b/substrate/client/consensus/beefy/rpc/src/lib.rs
@@ -21,9 +21,11 @@
 #![warn(missing_docs)]
 
 use parking_lot::RwLock;
+use sp_consensus_beefy::AuthorityIdBound;
 use std::sync::Arc;
 
 use sc_rpc::{utils::pipe_from_stream, SubscriptionTaskExecutor};
+use sp_application_crypto::RuntimeAppPublic;
 use sp_runtime::traits::Block as BlockT;
 
 use futures::{task::SpawnError, FutureExt, StreamExt};
@@ -98,19 +100,20 @@ pub trait BeefyApi<Notification, Hash> {
 }
 
 /// Implements the BeefyApi RPC trait for interacting with BEEFY.
-pub struct Beefy<Block: BlockT> {
-	finality_proof_stream: BeefyVersionedFinalityProofStream<Block>,
+pub struct Beefy<Block: BlockT, AuthorityId: AuthorityIdBound> {
+	finality_proof_stream: BeefyVersionedFinalityProofStream<Block, AuthorityId>,
 	beefy_best_block: Arc<RwLock<Option<Block::Hash>>>,
 	executor: SubscriptionTaskExecutor,
 }
 
-impl<Block> Beefy<Block>
+impl<Block, AuthorityId> Beefy<Block, AuthorityId>
 where
 	Block: BlockT,
+	AuthorityId: AuthorityIdBound,
 {
 	/// Creates a new Beefy Rpc handler instance.
 	pub fn new(
-		finality_proof_stream: BeefyVersionedFinalityProofStream<Block>,
+		finality_proof_stream: BeefyVersionedFinalityProofStream<Block, AuthorityId>,
 		best_block_stream: BeefyBestBlockStream<Block>,
 		executor: SubscriptionTaskExecutor,
 	) -> Result<Self, Error> {
@@ -129,16 +132,18 @@ where
 }
 
 #[async_trait]
-impl<Block> BeefyApiServer<notification::EncodedVersionedFinalityProof, Block::Hash>
-	for Beefy<Block>
+impl<Block, AuthorityId> BeefyApiServer<notification::EncodedVersionedFinalityProof, Block::Hash>
+	for Beefy<Block, AuthorityId>
 where
 	Block: BlockT,
+	AuthorityId: AuthorityIdBound,
+	<AuthorityId as RuntimeAppPublic>::Signature: Send + Sync,
 {
 	fn subscribe_justifications(&self, pending: PendingSubscriptionSink) {
 		let stream = self
 			.finality_proof_stream
 			.subscribe(100_000)
-			.map(|vfp| notification::EncodedVersionedFinalityProof::new::<Block>(vfp));
+			.map(|vfp| notification::EncodedVersionedFinalityProof::new::<Block, AuthorityId>(vfp));
 
 		sc_rpc::utils::spawn_subscription_task(&self.executor, pipe_from_stream(pending, stream));
 	}
@@ -158,20 +163,26 @@ mod tests {
 		communication::notification::BeefyVersionedFinalityProofSender,
 		justification::BeefyVersionedFinalityProof,
 	};
-	use sp_consensus_beefy::{known_payloads, Payload, SignedCommitment};
+	use sp_consensus_beefy::{ecdsa_crypto, known_payloads, Payload, SignedCommitment};
 	use sp_runtime::traits::{BlakeTwo256, Hash};
 	use substrate_test_runtime_client::runtime::Block;
 
-	fn setup_io_handler() -> (RpcModule<Beefy<Block>>, BeefyVersionedFinalityProofSender<Block>) {
+	fn setup_io_handler() -> (
+		RpcModule<Beefy<Block, ecdsa_crypto::AuthorityId>>,
+		BeefyVersionedFinalityProofSender<Block, ecdsa_crypto::AuthorityId>,
+	) {
 		let (_, stream) = BeefyBestBlockStream::<Block>::channel();
 		setup_io_handler_with_best_block_stream(stream)
 	}
 
 	fn setup_io_handler_with_best_block_stream(
 		best_block_stream: BeefyBestBlockStream<Block>,
-	) -> (RpcModule<Beefy<Block>>, BeefyVersionedFinalityProofSender<Block>) {
+	) -> (
+		RpcModule<Beefy<Block, ecdsa_crypto::AuthorityId>>,
+		BeefyVersionedFinalityProofSender<Block, ecdsa_crypto::AuthorityId>,
+	) {
 		let (finality_proof_sender, finality_proof_stream) =
-			BeefyVersionedFinalityProofStream::<Block>::channel();
+			BeefyVersionedFinalityProofStream::<Block, ecdsa_crypto::AuthorityId>::channel();
 
 		let handler =
 			Beefy::new(finality_proof_stream, best_block_stream, sc_rpc::testing::test_executor())
@@ -250,10 +261,10 @@ mod tests {
 		assert_eq!(response, expected);
 	}
 
-	fn create_finality_proof() -> BeefyVersionedFinalityProof<Block> {
+	fn create_finality_proof() -> BeefyVersionedFinalityProof<Block, ecdsa_crypto::AuthorityId> {
 		let payload =
 			Payload::from_single_entry(known_payloads::MMR_ROOT_ID, "Hello World!".encode());
-		BeefyVersionedFinalityProof::<Block>::V1(SignedCommitment {
+		BeefyVersionedFinalityProof::<Block, ecdsa_crypto::AuthorityId>::V1(SignedCommitment {
 			commitment: sp_consensus_beefy::Commitment {
 				payload,
 				block_number: 5,
@@ -280,7 +291,7 @@ mod tests {
 
 		// Inspect what we received
 		let (bytes, recv_sub_id) = sub.next::<sp_core::Bytes>().await.unwrap().unwrap();
-		let recv_finality_proof: BeefyVersionedFinalityProof<Block> =
+		let recv_finality_proof: BeefyVersionedFinalityProof<Block, ecdsa_crypto::AuthorityId> =
 			Decode::decode(&mut &bytes[..]).unwrap();
 		assert_eq!(&recv_sub_id, sub.subscription_id());
 		assert_eq!(recv_finality_proof, finality_proof);
diff --git a/substrate/client/consensus/beefy/rpc/src/notification.rs b/substrate/client/consensus/beefy/rpc/src/notification.rs
index 690c511b999ac1b89627cd1e67dc2ce8e2343862..d4339058a6940df75959fbd7b9ded13a318bd5db 100644
--- a/substrate/client/consensus/beefy/rpc/src/notification.rs
+++ b/substrate/client/consensus/beefy/rpc/src/notification.rs
@@ -19,6 +19,7 @@
 use codec::Encode;
 use serde::{Deserialize, Serialize};
 
+use sp_consensus_beefy::AuthorityIdBound;
 use sp_runtime::traits::Block as BlockT;
 
 /// An encoded finality proof proving that the given header has been finalized.
@@ -28,11 +29,15 @@ use sp_runtime::traits::Block as BlockT;
 pub struct EncodedVersionedFinalityProof(sp_core::Bytes);
 
 impl EncodedVersionedFinalityProof {
-	pub fn new<Block>(
-		finality_proof: sc_consensus_beefy::justification::BeefyVersionedFinalityProof<Block>,
+	pub fn new<Block, AuthorityId>(
+		finality_proof: sc_consensus_beefy::justification::BeefyVersionedFinalityProof<
+			Block,
+			AuthorityId,
+		>,
 	) -> Self
 	where
 		Block: BlockT,
+		AuthorityId: AuthorityIdBound,
 	{
 		EncodedVersionedFinalityProof(finality_proof.encode().into())
 	}
diff --git a/substrate/client/consensus/beefy/src/aux_schema.rs b/substrate/client/consensus/beefy/src/aux_schema.rs
index 534f668ae69c2996064bef086e2958b23f48caf0..1922494ad11207ae4f46931a5dcf673b3154001b 100644
--- a/substrate/client/consensus/beefy/src/aux_schema.rs
+++ b/substrate/client/consensus/beefy/src/aux_schema.rs
@@ -20,8 +20,10 @@
 
 use crate::{error::Error, worker::PersistedState, LOG_TARGET};
 use codec::{Decode, Encode};
-use log::{debug, trace};
+use log::{debug, trace, warn};
 use sc_client_api::{backend::AuxStore, Backend};
+use sp_blockchain::{Error as ClientError, Result as ClientResult};
+use sp_consensus_beefy::AuthorityIdBound;
 use sp_runtime::traits::Block as BlockT;
 
 const VERSION_KEY: &[u8] = b"beefy_auxschema_version";
@@ -36,26 +38,27 @@ pub(crate) fn write_current_version<BE: AuxStore>(backend: &BE) -> Result<(), Er
 }
 
 /// Write voter state.
-pub(crate) fn write_voter_state<B: BlockT, BE: AuxStore>(
+pub(crate) fn write_voter_state<B: BlockT, BE: AuxStore, AuthorityId: AuthorityIdBound>(
 	backend: &BE,
-	state: &PersistedState<B>,
-) -> Result<(), Error> {
+	state: &PersistedState<B, AuthorityId>,
+) -> ClientResult<()> {
 	trace!(target: LOG_TARGET, "🥩 persisting {:?}", state);
 	AuxStore::insert_aux(backend, &[(WORKER_STATE_KEY, state.encode().as_slice())], &[])
-		.map_err(|e| Error::Backend(e.to_string()))
 }
 
-fn load_decode<BE: AuxStore, T: Decode>(backend: &BE, key: &[u8]) -> Result<Option<T>, Error> {
-	match backend.get_aux(key).map_err(|e| Error::Backend(e.to_string()))? {
+fn load_decode<BE: AuxStore, T: Decode>(backend: &BE, key: &[u8]) -> ClientResult<Option<T>> {
+	match backend.get_aux(key)? {
 		None => Ok(None),
 		Some(t) => T::decode(&mut &t[..])
-			.map_err(|e| Error::Backend(format!("BEEFY DB is corrupted: {}", e)))
+			.map_err(|e| ClientError::Backend(format!("BEEFY DB is corrupted: {}", e)))
 			.map(Some),
 	}
 }
 
 /// Load or initialize persistent data from backend.
-pub(crate) fn load_persistent<B, BE>(backend: &BE) -> Result<Option<PersistedState<B>>, Error>
+pub(crate) fn load_persistent<B, BE, AuthorityId: AuthorityIdBound>(
+	backend: &BE,
+) -> ClientResult<Option<PersistedState<B, AuthorityId>>>
 where
 	B: BlockT,
 	BE: Backend<B>,
@@ -64,9 +67,14 @@ where
 
 	match version {
 		None => (),
-		Some(1) | Some(2) | Some(3) => (), // versions 1, 2 & 3 are obsolete and should be ignored
-		Some(4) => return load_decode::<_, PersistedState<B>>(backend, WORKER_STATE_KEY),
-		other => return Err(Error::Backend(format!("Unsupported BEEFY DB version: {:?}", other))),
+
+		Some(v) if 1 <= v && v <= 3 =>
+		// versions 1, 2 & 3 are obsolete and should be ignored
+			warn!(target: LOG_TARGET,  "🥩 backend contains a BEEFY state of an obsolete version {v}. ignoring..."),
+		Some(4) =>
+			return load_decode::<_, PersistedState<B, AuthorityId>>(backend, WORKER_STATE_KEY),
+		other =>
+			return Err(ClientError::Backend(format!("Unsupported BEEFY DB version: {:?}", other))),
 	}
 
 	// No persistent state found in DB.
@@ -78,6 +86,7 @@ pub(crate) mod tests {
 	use super::*;
 	use crate::tests::BeefyTestNet;
 	use sc_network_test::TestNetFactory;
+	use sp_consensus_beefy::ecdsa_crypto;
 
 	// also used in tests.rs
 	pub fn verify_persisted_version<B: BlockT, BE: Backend<B>>(backend: &BE) -> bool {
@@ -91,7 +100,7 @@ pub(crate) mod tests {
 		let backend = net.peer(0).client().as_backend();
 
 		// version not available in db -> None
-		assert_eq!(load_persistent(&*backend).unwrap(), None);
+		assert_eq!(load_persistent::<_, _, ecdsa_crypto::AuthorityId>(&*backend).unwrap(), None);
 
 		// populate version in db
 		write_current_version(&*backend).unwrap();
@@ -99,7 +108,7 @@ pub(crate) mod tests {
 		assert_eq!(load_decode(&*backend, VERSION_KEY).unwrap(), Some(CURRENT_VERSION));
 
 		// version is available in db but state isn't -> None
-		assert_eq!(load_persistent(&*backend).unwrap(), None);
+		assert_eq!(load_persistent::<_, _, ecdsa_crypto::AuthorityId>(&*backend).unwrap(), None);
 
 		// full `PersistedState` load is tested in `tests.rs`.
 	}
diff --git a/substrate/client/consensus/beefy/src/communication/gossip.rs b/substrate/client/consensus/beefy/src/communication/gossip.rs
index 947fe13856f476042858b1a5698aa5333b1d287b..95cac250b7c595e7a1f8875096b1f37d061a367f 100644
--- a/substrate/client/consensus/beefy/src/communication/gossip.rs
+++ b/substrate/client/consensus/beefy/src/communication/gossip.rs
@@ -36,10 +36,8 @@ use crate::{
 	keystore::BeefyKeystore,
 	LOG_TARGET,
 };
-use sp_consensus_beefy::{
-	ecdsa_crypto::{AuthorityId, Signature},
-	ValidatorSet, ValidatorSetId, VoteMessage,
-};
+use sp_application_crypto::RuntimeAppPublic;
+use sp_consensus_beefy::{AuthorityIdBound, ValidatorSet, ValidatorSetId, VoteMessage};
 
 // Timeout for rebroadcasting messages.
 #[cfg(not(test))]
@@ -72,16 +70,19 @@ enum Consider {
 
 /// BEEFY gossip message type that gets encoded and sent on the network.
 #[derive(Debug, Encode, Decode)]
-pub(crate) enum GossipMessage<B: Block> {
+pub(crate) enum GossipMessage<B: Block, AuthorityId: AuthorityIdBound> {
 	/// BEEFY message with commitment and single signature.
-	Vote(VoteMessage<NumberFor<B>, AuthorityId, Signature>),
+	Vote(VoteMessage<NumberFor<B>, AuthorityId, <AuthorityId as RuntimeAppPublic>::Signature>),
 	/// BEEFY justification with commitment and signatures.
-	FinalityProof(BeefyVersionedFinalityProof<B>),
+	FinalityProof(BeefyVersionedFinalityProof<B, AuthorityId>),
 }
 
-impl<B: Block> GossipMessage<B> {
+impl<B: Block, AuthorityId: AuthorityIdBound> GossipMessage<B, AuthorityId> {
 	/// Return inner vote if this message is a Vote.
-	pub fn unwrap_vote(self) -> Option<VoteMessage<NumberFor<B>, AuthorityId, Signature>> {
+	pub fn unwrap_vote(
+		self,
+	) -> Option<VoteMessage<NumberFor<B>, AuthorityId, <AuthorityId as RuntimeAppPublic>::Signature>>
+	{
 		match self {
 			GossipMessage::Vote(vote) => Some(vote),
 			GossipMessage::FinalityProof(_) => None,
@@ -89,7 +90,7 @@ impl<B: Block> GossipMessage<B> {
 	}
 
 	/// Return inner finality proof if this message is a FinalityProof.
-	pub fn unwrap_finality_proof(self) -> Option<BeefyVersionedFinalityProof<B>> {
+	pub fn unwrap_finality_proof(self) -> Option<BeefyVersionedFinalityProof<B, AuthorityId>> {
 		match self {
 			GossipMessage::Vote(_) => None,
 			GossipMessage::FinalityProof(proof) => Some(proof),
@@ -114,33 +115,33 @@ where
 }
 
 #[derive(Clone, Debug)]
-pub(crate) struct GossipFilterCfg<'a, B: Block> {
+pub(crate) struct GossipFilterCfg<'a, B: Block, AuthorityId: AuthorityIdBound> {
 	pub start: NumberFor<B>,
 	pub end: NumberFor<B>,
 	pub validator_set: &'a ValidatorSet<AuthorityId>,
 }
 
 #[derive(Clone, Debug)]
-struct FilterInner<B: Block> {
+struct FilterInner<B: Block, AuthorityId: AuthorityIdBound> {
 	pub start: NumberFor<B>,
 	pub end: NumberFor<B>,
 	pub validator_set: ValidatorSet<AuthorityId>,
 }
 
-struct Filter<B: Block> {
+struct Filter<B: Block, AuthorityId: AuthorityIdBound> {
 	// specifies live rounds
-	inner: Option<FilterInner<B>>,
+	inner: Option<FilterInner<B, AuthorityId>>,
 	// cache of seen valid justifications in active rounds
 	rounds_with_valid_proofs: BTreeSet<NumberFor<B>>,
 }
 
-impl<B: Block> Filter<B> {
+impl<B: Block, AuthorityId: AuthorityIdBound> Filter<B, AuthorityId> {
 	pub fn new() -> Self {
 		Self { inner: None, rounds_with_valid_proofs: BTreeSet::new() }
 	}
 
 	/// Update filter to new `start` and `set_id`.
-	fn update(&mut self, cfg: GossipFilterCfg<B>) {
+	fn update(&mut self, cfg: GossipFilterCfg<B, AuthorityId>) {
 		self.rounds_with_valid_proofs
 			.retain(|&round| round >= cfg.start && round <= cfg.end);
 		// only clone+overwrite big validator_set if set_id changed
@@ -220,21 +221,22 @@ impl<B: Block> Filter<B> {
 /// rejected/expired.
 ///
 ///All messaging is handled in a single BEEFY global topic.
-pub(crate) struct GossipValidator<B, N>
+pub(crate) struct GossipValidator<B, N, AuthorityId: AuthorityIdBound>
 where
 	B: Block,
 {
 	votes_topic: B::Hash,
 	justifs_topic: B::Hash,
-	gossip_filter: RwLock<Filter<B>>,
+	gossip_filter: RwLock<Filter<B, AuthorityId>>,
 	next_rebroadcast: Mutex<Instant>,
 	known_peers: Arc<Mutex<KnownPeers<B>>>,
 	network: Arc<N>,
 }
 
-impl<B, N> GossipValidator<B, N>
+impl<B, N, AuthorityId> GossipValidator<B, N, AuthorityId>
 where
 	B: Block,
+	AuthorityId: AuthorityIdBound,
 {
 	pub(crate) fn new(known_peers: Arc<Mutex<KnownPeers<B>>>, network: Arc<N>) -> Self {
 		Self {
@@ -250,7 +252,7 @@ where
 	/// Update gossip validator filter.
 	///
 	/// Only votes for `set_id` and rounds `start <= round <= end` will be accepted.
-	pub(crate) fn update_filter(&self, filter: GossipFilterCfg<B>) {
+	pub(crate) fn update_filter(&self, filter: GossipFilterCfg<B, AuthorityId>) {
 		debug!(
 			target: LOG_TARGET,
 			"🥩 New gossip filter: start {:?}, end {:?}, validator set id {:?}",
@@ -260,10 +262,11 @@ where
 	}
 }
 
-impl<B, N> GossipValidator<B, N>
+impl<B, N, AuthorityId> GossipValidator<B, N, AuthorityId>
 where
 	B: Block,
 	N: NetworkPeers,
+	AuthorityId: AuthorityIdBound,
 {
 	fn report(&self, who: PeerId, cost_benefit: ReputationChange) {
 		self.network.report_peer(who, cost_benefit);
@@ -271,7 +274,7 @@ where
 
 	fn validate_vote(
 		&self,
-		vote: VoteMessage<NumberFor<B>, AuthorityId, Signature>,
+		vote: VoteMessage<NumberFor<B>, AuthorityId, <AuthorityId as RuntimeAppPublic>::Signature>,
 		sender: &PeerId,
 	) -> Action<B::Hash> {
 		let round = vote.commitment.block_number;
@@ -299,7 +302,7 @@ where
 				.unwrap_or(false)
 			{
 				debug!(target: LOG_TARGET, "Message from voter not in validator set: {}", vote.id);
-				return Action::Discard(cost::UNKNOWN_VOTER)
+				return Action::Discard(cost::UNKNOWN_VOTER);
 			}
 		}
 
@@ -316,10 +319,10 @@ where
 
 	fn validate_finality_proof(
 		&self,
-		proof: BeefyVersionedFinalityProof<B>,
+		proof: BeefyVersionedFinalityProof<B, AuthorityId>,
 		sender: &PeerId,
 	) -> Action<B::Hash> {
-		let (round, set_id) = proof_block_num_and_set_id::<B>(&proof);
+		let (round, set_id) = proof_block_num_and_set_id::<B, AuthorityId>(&proof);
 		self.known_peers.lock().note_vote_for(*sender, round);
 
 		let action = {
@@ -336,7 +339,7 @@ where
 			}
 
 			if guard.is_already_proven(round) {
-				return Action::Discard(benefit::NOT_INTERESTED)
+				return Action::Discard(benefit::NOT_INTERESTED);
 			}
 
 			// Verify justification signatures.
@@ -344,7 +347,7 @@ where
 				.validator_set()
 				.map(|validator_set| {
 					if let Err((_, signatures_checked)) =
-						verify_with_validator_set::<B>(round, validator_set, &proof)
+						verify_with_validator_set::<B, AuthorityId>(round, validator_set, &proof)
 					{
 						debug!(
 							target: LOG_TARGET,
@@ -369,9 +372,10 @@ where
 	}
 }
 
-impl<B, N> Validator<B> for GossipValidator<B, N>
+impl<B, N, AuthorityId> Validator<B> for GossipValidator<B, N, AuthorityId>
 where
 	B: Block,
+	AuthorityId: AuthorityIdBound,
 	N: NetworkPeers + Send + Sync,
 {
 	fn peer_disconnected(&self, _context: &mut dyn ValidatorContext<B>, who: &PeerId) {
@@ -385,7 +389,7 @@ where
 		mut data: &[u8],
 	) -> ValidationResult<B::Hash> {
 		let raw = data;
-		let action = match GossipMessage::<B>::decode_all(&mut data) {
+		let action = match GossipMessage::<B, AuthorityId>::decode_all(&mut data) {
 			Ok(GossipMessage::Vote(msg)) => self.validate_vote(msg, sender),
 			Ok(GossipMessage::FinalityProof(proof)) => self.validate_finality_proof(proof, sender),
 			Err(e) => {
@@ -414,26 +418,28 @@ where
 
 	fn message_expired<'a>(&'a self) -> Box<dyn FnMut(B::Hash, &[u8]) -> bool + 'a> {
 		let filter = self.gossip_filter.read();
-		Box::new(move |_topic, mut data| match GossipMessage::<B>::decode_all(&mut data) {
-			Ok(GossipMessage::Vote(msg)) => {
-				let round = msg.commitment.block_number;
-				let set_id = msg.commitment.validator_set_id;
-				let expired = filter.consider_vote(round, set_id) != Consider::Accept;
-				trace!(target: LOG_TARGET, "🥩 Vote for round #{} expired: {}", round, expired);
-				expired
-			},
-			Ok(GossipMessage::FinalityProof(proof)) => {
-				let (round, set_id) = proof_block_num_and_set_id::<B>(&proof);
-				let expired = filter.consider_finality_proof(round, set_id) != Consider::Accept;
-				trace!(
-					target: LOG_TARGET,
-					"🥩 Finality proof for round #{} expired: {}",
-					round,
+		Box::new(move |_topic, mut data| {
+			match GossipMessage::<B, AuthorityId>::decode_all(&mut data) {
+				Ok(GossipMessage::Vote(msg)) => {
+					let round = msg.commitment.block_number;
+					let set_id = msg.commitment.validator_set_id;
+					let expired = filter.consider_vote(round, set_id) != Consider::Accept;
+					trace!(target: LOG_TARGET, "🥩 Vote for round #{} expired: {}", round, expired);
 					expired
-				);
-				expired
-			},
-			Err(_) => true,
+				},
+				Ok(GossipMessage::FinalityProof(proof)) => {
+					let (round, set_id) = proof_block_num_and_set_id::<B, AuthorityId>(&proof);
+					let expired = filter.consider_finality_proof(round, set_id) != Consider::Accept;
+					trace!(
+						target: LOG_TARGET,
+						"🥩 Finality proof for round #{} expired: {}",
+						round,
+						expired
+					);
+					expired
+				},
+				Err(_) => true,
+			}
 		})
 	}
 
@@ -455,10 +461,10 @@ where
 		let filter = self.gossip_filter.read();
 		Box::new(move |_who, intent, _topic, mut data| {
 			if let MessageIntent::PeriodicRebroadcast = intent {
-				return do_rebroadcast
+				return do_rebroadcast;
 			}
 
-			match GossipMessage::<B>::decode_all(&mut data) {
+			match GossipMessage::<B, AuthorityId>::decode_all(&mut data) {
 				Ok(GossipMessage::Vote(msg)) => {
 					let round = msg.commitment.block_number;
 					let set_id = msg.commitment.validator_set_id;
@@ -467,7 +473,7 @@ where
 					allowed
 				},
 				Ok(GossipMessage::FinalityProof(proof)) => {
-					let (round, set_id) = proof_block_num_and_set_id::<B>(&proof);
+					let (round, set_id) = proof_block_num_and_set_id::<B, AuthorityId>(&proof);
 					let allowed = filter.consider_finality_proof(round, set_id) == Consider::Accept;
 					trace!(
 						target: LOG_TARGET,
@@ -490,8 +496,8 @@ pub(crate) mod tests {
 	use sc_network_test::Block;
 	use sp_application_crypto::key_types::BEEFY as BEEFY_KEY_TYPE;
 	use sp_consensus_beefy::{
-		ecdsa_crypto::Signature, known_payloads, test_utils::Keyring, Commitment, MmrRootHash,
-		Payload, SignedCommitment, VoteMessage,
+		ecdsa_crypto, known_payloads, test_utils::Keyring, Commitment, MmrRootHash, Payload,
+		SignedCommitment, VoteMessage,
 	};
 	use sp_keystore::{testing::MemoryKeystore, Keystore};
 
@@ -607,16 +613,18 @@ pub(crate) mod tests {
 	}
 
 	pub fn sign_commitment<BN: Encode>(
-		who: &Keyring<AuthorityId>,
+		who: &Keyring<ecdsa_crypto::AuthorityId>,
 		commitment: &Commitment<BN>,
-	) -> Signature {
+	) -> ecdsa_crypto::Signature {
 		let store = MemoryKeystore::new();
 		store.ecdsa_generate_new(BEEFY_KEY_TYPE, Some(&who.to_seed())).unwrap();
-		let beefy_keystore: BeefyKeystore<AuthorityId> = Some(store.into()).into();
+		let beefy_keystore: BeefyKeystore<ecdsa_crypto::AuthorityId> = Some(store.into()).into();
 		beefy_keystore.sign(&who.public(), &commitment.encode()).unwrap()
 	}
 
-	fn dummy_vote(block_number: u64) -> VoteMessage<u64, AuthorityId, Signature> {
+	fn dummy_vote(
+		block_number: u64,
+	) -> VoteMessage<u64, ecdsa_crypto::AuthorityId, ecdsa_crypto::Signature> {
 		let payload = Payload::from_single_entry(
 			known_payloads::MMR_ROOT_ID,
 			MmrRootHash::default().encode(),
@@ -629,8 +637,8 @@ pub(crate) mod tests {
 
 	pub fn dummy_proof(
 		block_number: u64,
-		validator_set: &ValidatorSet<AuthorityId>,
-	) -> BeefyVersionedFinalityProof<Block> {
+		validator_set: &ValidatorSet<ecdsa_crypto::AuthorityId>,
+	) -> BeefyVersionedFinalityProof<Block, ecdsa_crypto::AuthorityId> {
 		let payload = Payload::from_single_entry(
 			known_payloads::MMR_ROOT_ID,
 			MmrRootHash::default().encode(),
@@ -639,25 +647,29 @@ pub(crate) mod tests {
 		let signatures = validator_set
 			.validators()
 			.iter()
-			.map(|validator: &AuthorityId| {
+			.map(|validator: &ecdsa_crypto::AuthorityId| {
 				Some(sign_commitment(
-					&Keyring::<AuthorityId>::from_public(validator).unwrap(),
+					&Keyring::<ecdsa_crypto::AuthorityId>::from_public(validator).unwrap(),
 					&commitment,
 				))
 			})
 			.collect();
 
-		BeefyVersionedFinalityProof::<Block>::V1(SignedCommitment { commitment, signatures })
+		BeefyVersionedFinalityProof::<Block, ecdsa_crypto::AuthorityId>::V1(SignedCommitment {
+			commitment,
+			signatures,
+		})
 	}
 
 	#[test]
 	fn should_validate_messages() {
-		let keys = vec![Keyring::<AuthorityId>::Alice.public()];
-		let validator_set = ValidatorSet::<AuthorityId>::new(keys.clone(), 0).unwrap();
+		let keys = vec![Keyring::<ecdsa_crypto::AuthorityId>::Alice.public()];
+		let validator_set =
+			ValidatorSet::<ecdsa_crypto::AuthorityId>::new(keys.clone(), 0).unwrap();
 
 		let (network, mut report_stream) = TestNetwork::new();
 
-		let gv = GossipValidator::<Block, _>::new(
+		let gv = GossipValidator::<Block, _, ecdsa_crypto::AuthorityId>::new(
 			Arc::new(Mutex::new(KnownPeers::new())),
 			Arc::new(network),
 		);
@@ -678,7 +690,8 @@ pub(crate) mod tests {
 		// verify votes validation
 
 		let vote = dummy_vote(3);
-		let encoded = GossipMessage::<Block>::Vote(vote.clone()).encode();
+		let encoded =
+			GossipMessage::<Block, ecdsa_crypto::AuthorityId>::Vote(vote.clone()).encode();
 
 		// filter not initialized
 		let res = gv.validate(&mut context, &sender, &encoded);
@@ -696,7 +709,7 @@ pub(crate) mod tests {
 		// reject vote, voter not in validator set
 		let mut bad_vote = vote.clone();
 		bad_vote.id = Keyring::Bob.public();
-		let bad_vote = GossipMessage::<Block>::Vote(bad_vote).encode();
+		let bad_vote = GossipMessage::<Block, ecdsa_crypto::AuthorityId>::Vote(bad_vote).encode();
 		let res = gv.validate(&mut context, &sender, &bad_vote);
 		assert!(matches!(res, ValidationResult::Discard));
 		expected_report.cost_benefit = cost::UNKNOWN_VOTER;
@@ -726,7 +739,8 @@ pub(crate) mod tests {
 
 		// reject old proof
 		let proof = dummy_proof(5, &validator_set);
-		let encoded_proof = GossipMessage::<Block>::FinalityProof(proof).encode();
+		let encoded_proof =
+			GossipMessage::<Block, ecdsa_crypto::AuthorityId>::FinalityProof(proof).encode();
 		let res = gv.validate(&mut context, &sender, &encoded_proof);
 		assert!(matches!(res, ValidationResult::Discard));
 		expected_report.cost_benefit = cost::OUTDATED_MESSAGE;
@@ -734,7 +748,8 @@ pub(crate) mod tests {
 
 		// accept next proof with good set_id
 		let proof = dummy_proof(7, &validator_set);
-		let encoded_proof = GossipMessage::<Block>::FinalityProof(proof).encode();
+		let encoded_proof =
+			GossipMessage::<Block, ecdsa_crypto::AuthorityId>::FinalityProof(proof).encode();
 		let res = gv.validate(&mut context, &sender, &encoded_proof);
 		assert!(matches!(res, ValidationResult::ProcessAndKeep(_)));
 		expected_report.cost_benefit = benefit::VALIDATED_PROOF;
@@ -742,16 +757,18 @@ pub(crate) mod tests {
 
 		// accept future proof with good set_id
 		let proof = dummy_proof(20, &validator_set);
-		let encoded_proof = GossipMessage::<Block>::FinalityProof(proof).encode();
+		let encoded_proof =
+			GossipMessage::<Block, ecdsa_crypto::AuthorityId>::FinalityProof(proof).encode();
 		let res = gv.validate(&mut context, &sender, &encoded_proof);
 		assert!(matches!(res, ValidationResult::ProcessAndKeep(_)));
 		expected_report.cost_benefit = benefit::VALIDATED_PROOF;
 		assert_eq!(report_stream.try_next().unwrap().unwrap(), expected_report);
 
 		// reject proof, future set_id
-		let bad_validator_set = ValidatorSet::<AuthorityId>::new(keys, 1).unwrap();
+		let bad_validator_set = ValidatorSet::<ecdsa_crypto::AuthorityId>::new(keys, 1).unwrap();
 		let proof = dummy_proof(20, &bad_validator_set);
-		let encoded_proof = GossipMessage::<Block>::FinalityProof(proof).encode();
+		let encoded_proof =
+			GossipMessage::<Block, ecdsa_crypto::AuthorityId>::FinalityProof(proof).encode();
 		let res = gv.validate(&mut context, &sender, &encoded_proof);
 		assert!(matches!(res, ValidationResult::Discard));
 		expected_report.cost_benefit = cost::FUTURE_MESSAGE;
@@ -759,9 +776,10 @@ pub(crate) mod tests {
 
 		// reject proof, bad signatures (Bob instead of Alice)
 		let bad_validator_set =
-			ValidatorSet::<AuthorityId>::new(vec![Keyring::Bob.public()], 0).unwrap();
+			ValidatorSet::<ecdsa_crypto::AuthorityId>::new(vec![Keyring::Bob.public()], 0).unwrap();
 		let proof = dummy_proof(21, &bad_validator_set);
-		let encoded_proof = GossipMessage::<Block>::FinalityProof(proof).encode();
+		let encoded_proof =
+			GossipMessage::<Block, ecdsa_crypto::AuthorityId>::FinalityProof(proof).encode();
 		let res = gv.validate(&mut context, &sender, &encoded_proof);
 		assert!(matches!(res, ValidationResult::Discard));
 		expected_report.cost_benefit = cost::INVALID_PROOF;
@@ -772,8 +790,9 @@ pub(crate) mod tests {
 	#[test]
 	fn messages_allowed_and_expired() {
 		let keys = vec![Keyring::Alice.public()];
-		let validator_set = ValidatorSet::<AuthorityId>::new(keys.clone(), 0).unwrap();
-		let gv = GossipValidator::<Block, _>::new(
+		let validator_set =
+			ValidatorSet::<ecdsa_crypto::AuthorityId>::new(keys.clone(), 0).unwrap();
+		let gv = GossipValidator::<Block, _, ecdsa_crypto::AuthorityId>::new(
 			Arc::new(Mutex::new(KnownPeers::new())),
 			Arc::new(TestNetwork::new().0),
 		);
@@ -793,58 +812,70 @@ pub(crate) mod tests {
 
 		// inactive round 1 -> expired
 		let vote = dummy_vote(1);
-		let mut encoded_vote = GossipMessage::<Block>::Vote(vote).encode();
+		let mut encoded_vote =
+			GossipMessage::<Block, ecdsa_crypto::AuthorityId>::Vote(vote).encode();
 		assert!(!allowed(&sender, intent, &topic, &mut encoded_vote));
 		assert!(expired(topic, &mut encoded_vote));
 		let proof = dummy_proof(1, &validator_set);
-		let mut encoded_proof = GossipMessage::<Block>::FinalityProof(proof).encode();
+		let mut encoded_proof =
+			GossipMessage::<Block, ecdsa_crypto::AuthorityId>::FinalityProof(proof).encode();
 		assert!(!allowed(&sender, intent, &topic, &mut encoded_proof));
 		assert!(expired(topic, &mut encoded_proof));
 
 		// active round 2 -> !expired - concluded but still gossiped
 		let vote = dummy_vote(2);
-		let mut encoded_vote = GossipMessage::<Block>::Vote(vote).encode();
+		let mut encoded_vote =
+			GossipMessage::<Block, ecdsa_crypto::AuthorityId>::Vote(vote).encode();
 		assert!(allowed(&sender, intent, &topic, &mut encoded_vote));
 		assert!(!expired(topic, &mut encoded_vote));
 		let proof = dummy_proof(2, &validator_set);
-		let mut encoded_proof = GossipMessage::<Block>::FinalityProof(proof).encode();
+		let mut encoded_proof =
+			GossipMessage::<Block, ecdsa_crypto::AuthorityId>::FinalityProof(proof).encode();
 		assert!(allowed(&sender, intent, &topic, &mut encoded_proof));
 		assert!(!expired(topic, &mut encoded_proof));
 		// using wrong set_id -> !allowed, expired
-		let bad_validator_set = ValidatorSet::<AuthorityId>::new(keys.clone(), 1).unwrap();
+		let bad_validator_set =
+			ValidatorSet::<ecdsa_crypto::AuthorityId>::new(keys.clone(), 1).unwrap();
 		let proof = dummy_proof(2, &bad_validator_set);
-		let mut encoded_proof = GossipMessage::<Block>::FinalityProof(proof).encode();
+		let mut encoded_proof =
+			GossipMessage::<Block, ecdsa_crypto::AuthorityId>::FinalityProof(proof).encode();
 		assert!(!allowed(&sender, intent, &topic, &mut encoded_proof));
 		assert!(expired(topic, &mut encoded_proof));
 
 		// in progress round 3 -> !expired
 		let vote = dummy_vote(3);
-		let mut encoded_vote = GossipMessage::<Block>::Vote(vote).encode();
+		let mut encoded_vote =
+			GossipMessage::<Block, ecdsa_crypto::AuthorityId>::Vote(vote).encode();
 		assert!(allowed(&sender, intent, &topic, &mut encoded_vote));
 		assert!(!expired(topic, &mut encoded_vote));
 		let proof = dummy_proof(3, &validator_set);
-		let mut encoded_proof = GossipMessage::<Block>::FinalityProof(proof).encode();
+		let mut encoded_proof =
+			GossipMessage::<Block, ecdsa_crypto::AuthorityId>::FinalityProof(proof).encode();
 		assert!(allowed(&sender, intent, &topic, &mut encoded_proof));
 		assert!(!expired(topic, &mut encoded_proof));
 
 		// unseen round 4 -> !expired
 		let vote = dummy_vote(4);
-		let mut encoded_vote = GossipMessage::<Block>::Vote(vote).encode();
+		let mut encoded_vote =
+			GossipMessage::<Block, ecdsa_crypto::AuthorityId>::Vote(vote).encode();
 		assert!(allowed(&sender, intent, &topic, &mut encoded_vote));
 		assert!(!expired(topic, &mut encoded_vote));
 		let proof = dummy_proof(4, &validator_set);
-		let mut encoded_proof = GossipMessage::<Block>::FinalityProof(proof).encode();
+		let mut encoded_proof =
+			GossipMessage::<Block, ecdsa_crypto::AuthorityId>::FinalityProof(proof).encode();
 		assert!(allowed(&sender, intent, &topic, &mut encoded_proof));
 		assert!(!expired(topic, &mut encoded_proof));
 
 		// future round 11 -> expired
 		let vote = dummy_vote(11);
-		let mut encoded_vote = GossipMessage::<Block>::Vote(vote).encode();
+		let mut encoded_vote =
+			GossipMessage::<Block, ecdsa_crypto::AuthorityId>::Vote(vote).encode();
 		assert!(!allowed(&sender, intent, &topic, &mut encoded_vote));
 		assert!(expired(topic, &mut encoded_vote));
 		// future proofs allowed while same set_id -> allowed
 		let proof = dummy_proof(11, &validator_set);
-		let mut encoded_proof = GossipMessage::<Block>::FinalityProof(proof).encode();
+		let mut encoded_proof =
+			GossipMessage::<Block, ecdsa_crypto::AuthorityId>::FinalityProof(proof).encode();
 		assert!(allowed(&sender, intent, &topic, &mut encoded_proof));
 		assert!(!expired(topic, &mut encoded_proof));
 	}
@@ -852,8 +883,9 @@ pub(crate) mod tests {
 	#[test]
 	fn messages_rebroadcast() {
 		let keys = vec![Keyring::Alice.public()];
-		let validator_set = ValidatorSet::<AuthorityId>::new(keys.clone(), 0).unwrap();
-		let gv = GossipValidator::<Block, _>::new(
+		let validator_set =
+			ValidatorSet::<ecdsa_crypto::AuthorityId>::new(keys.clone(), 0).unwrap();
+		let gv = GossipValidator::<Block, _, ecdsa_crypto::AuthorityId>::new(
 			Arc::new(Mutex::new(KnownPeers::new())),
 			Arc::new(TestNetwork::new().0),
 		);
diff --git a/substrate/client/consensus/beefy/src/communication/notification.rs b/substrate/client/consensus/beefy/src/communication/notification.rs
index a4486e523c301f6b64af3936a5bdba3456303ad2..8bb5d848b4faab9ee5a25032b32db0015324d4bc 100644
--- a/substrate/client/consensus/beefy/src/communication/notification.rs
+++ b/substrate/client/consensus/beefy/src/communication/notification.rs
@@ -32,13 +32,15 @@ pub type BeefyBestBlockStream<Block> =
 
 /// The sending half of the notifications channel(s) used to send notifications
 /// about versioned finality proof generated at the end of a BEEFY round.
-pub type BeefyVersionedFinalityProofSender<Block> =
-	NotificationSender<BeefyVersionedFinalityProof<Block>>;
+pub type BeefyVersionedFinalityProofSender<Block, AuthorityId> =
+	NotificationSender<BeefyVersionedFinalityProof<Block, AuthorityId>>;
 
 /// The receiving half of a notifications channel used to receive notifications
 /// about versioned finality proof generated at the end of a BEEFY round.
-pub type BeefyVersionedFinalityProofStream<Block> =
-	NotificationStream<BeefyVersionedFinalityProof<Block>, BeefyVersionedFinalityProofTracingKey>;
+pub type BeefyVersionedFinalityProofStream<Block, AuthorityId> = NotificationStream<
+	BeefyVersionedFinalityProof<Block, AuthorityId>,
+	BeefyVersionedFinalityProofTracingKey,
+>;
 
 /// Provides tracing key for BEEFY best block stream.
 #[derive(Clone)]
diff --git a/substrate/client/consensus/beefy/src/communication/request_response/incoming_requests_handler.rs b/substrate/client/consensus/beefy/src/communication/request_response/incoming_requests_handler.rs
index 7893066a01e06c967893bdedbd25ad3c2da9c3bd..350e7a271bc3921a91380baae0cf2e14e1d6debd 100644
--- a/substrate/client/consensus/beefy/src/communication/request_response/incoming_requests_handler.rs
+++ b/substrate/client/consensus/beefy/src/communication/request_response/incoming_requests_handler.rs
@@ -87,9 +87,9 @@ impl<B: Block> IncomingRequest<B> {
 					sent_feedback: None,
 				};
 				if let Err(_) = pending_response.send(response) {
-					return Err(Error::DecodingErrorNoReputationChange(peer, err))
+					return Err(Error::DecodingErrorNoReputationChange(peer, err));
 				}
-				return Err(Error::DecodingError(peer, err))
+				return Err(Error::DecodingError(peer, err));
 			},
 		};
 		Ok(Self::new(peer, payload, pending_response))
diff --git a/substrate/client/consensus/beefy/src/communication/request_response/outgoing_requests_engine.rs b/substrate/client/consensus/beefy/src/communication/request_response/outgoing_requests_engine.rs
index 2ab072960900817155b42434f69f29236fcda54b..4d40656375ec8231f5fa0b92839c73d2f187a7b4 100644
--- a/substrate/client/consensus/beefy/src/communication/request_response/outgoing_requests_engine.rs
+++ b/substrate/client/consensus/beefy/src/communication/request_response/outgoing_requests_engine.rs
@@ -27,7 +27,7 @@ use sc_network::{
 	NetworkRequest, ProtocolName,
 };
 use sc_network_types::PeerId;
-use sp_consensus_beefy::{ecdsa_crypto::AuthorityId, ValidatorSet};
+use sp_consensus_beefy::{AuthorityIdBound, ValidatorSet};
 use sp_runtime::traits::{Block, NumberFor};
 use std::{collections::VecDeque, result::Result, sync::Arc};
 
@@ -49,38 +49,38 @@ type Response = Result<(Vec<u8>, ProtocolName), RequestFailure>;
 type ResponseReceiver = oneshot::Receiver<Response>;
 
 #[derive(Clone, Debug)]
-struct RequestInfo<B: Block> {
+struct RequestInfo<B: Block, AuthorityId: AuthorityIdBound> {
 	block: NumberFor<B>,
 	active_set: ValidatorSet<AuthorityId>,
 }
 
-enum State<B: Block> {
+enum State<B: Block, AuthorityId: AuthorityIdBound> {
 	Idle,
-	AwaitingResponse(PeerId, RequestInfo<B>, ResponseReceiver),
+	AwaitingResponse(PeerId, RequestInfo<B, AuthorityId>, ResponseReceiver),
 }
 
 /// Possible engine responses.
-pub(crate) enum ResponseInfo<B: Block> {
+pub(crate) enum ResponseInfo<B: Block, AuthorityId: AuthorityIdBound> {
 	/// No peer response available yet.
 	Pending,
 	/// Valid justification provided alongside peer reputation changes.
-	ValidProof(BeefyVersionedFinalityProof<B>, PeerReport),
+	ValidProof(BeefyVersionedFinalityProof<B, AuthorityId>, PeerReport),
 	/// No justification yet, only peer reputation changes.
 	PeerReport(PeerReport),
 }
 
-pub struct OnDemandJustificationsEngine<B: Block> {
+pub struct OnDemandJustificationsEngine<B: Block, AuthorityId: AuthorityIdBound> {
 	network: Arc<dyn NetworkRequest + Send + Sync>,
 	protocol_name: ProtocolName,
 
 	live_peers: Arc<Mutex<KnownPeers<B>>>,
 	peers_cache: VecDeque<PeerId>,
 
-	state: State<B>,
+	state: State<B, AuthorityId>,
 	metrics: Option<OnDemandOutgoingRequestsMetrics>,
 }
 
-impl<B: Block> OnDemandJustificationsEngine<B> {
+impl<B: Block, AuthorityId: AuthorityIdBound> OnDemandJustificationsEngine<B, AuthorityId> {
 	pub fn new(
 		network: Arc<dyn NetworkRequest + Send + Sync>,
 		protocol_name: ProtocolName,
@@ -106,13 +106,13 @@ impl<B: Block> OnDemandJustificationsEngine<B> {
 		let live = self.live_peers.lock();
 		while let Some(peer) = self.peers_cache.pop_front() {
 			if live.contains(&peer) {
-				return Some(peer)
+				return Some(peer);
 			}
 		}
 		None
 	}
 
-	fn request_from_peer(&mut self, peer: PeerId, req_info: RequestInfo<B>) {
+	fn request_from_peer(&mut self, peer: PeerId, req_info: RequestInfo<B, AuthorityId>) {
 		debug!(
 			target: BEEFY_SYNC_LOG_TARGET,
 			"🥩 requesting justif #{:?} from peer {:?}", req_info.block, peer,
@@ -140,7 +140,7 @@ impl<B: Block> OnDemandJustificationsEngine<B> {
 	pub fn request(&mut self, block: NumberFor<B>, active_set: ValidatorSet<AuthorityId>) {
 		// ignore new requests while there's already one pending
 		if matches!(self.state, State::AwaitingResponse(_, _, _)) {
-			return
+			return;
 		}
 		self.reset_peers_cache_for_block(block);
 
@@ -174,9 +174,9 @@ impl<B: Block> OnDemandJustificationsEngine<B> {
 	fn process_response(
 		&mut self,
 		peer: &PeerId,
-		req_info: &RequestInfo<B>,
+		req_info: &RequestInfo<B, AuthorityId>,
 		response: Result<Response, Canceled>,
-	) -> Result<BeefyVersionedFinalityProof<B>, Error> {
+	) -> Result<BeefyVersionedFinalityProof<B, AuthorityId>, Error> {
 		response
 			.map_err(|e| {
 				debug!(
@@ -207,7 +207,7 @@ impl<B: Block> OnDemandJustificationsEngine<B> {
 				}
 			})
 			.and_then(|(encoded, _)| {
-				decode_and_verify_finality_proof::<B>(
+				decode_and_verify_finality_proof::<B, AuthorityId>(
 					&encoded[..],
 					req_info.block,
 					&req_info.active_set,
@@ -227,11 +227,11 @@ impl<B: Block> OnDemandJustificationsEngine<B> {
 			})
 	}
 
-	pub(crate) async fn next(&mut self) -> ResponseInfo<B> {
+	pub(crate) async fn next(&mut self) -> ResponseInfo<B, AuthorityId> {
 		let (peer, req_info, resp) = match &mut self.state {
 			State::Idle => {
 				futures::future::pending::<()>().await;
-				return ResponseInfo::Pending
+				return ResponseInfo::Pending;
 			},
 			State::AwaitingResponse(peer, req_info, receiver) => {
 				let resp = receiver.await;
diff --git a/substrate/client/consensus/beefy/src/error.rs b/substrate/client/consensus/beefy/src/error.rs
index b4773f940193e07d4f6f6962b69096c91805bbeb..9cd09cb99332a28f3f1ec731be3c5af6504c91e0 100644
--- a/substrate/client/consensus/beefy/src/error.rs
+++ b/substrate/client/consensus/beefy/src/error.rs
@@ -20,6 +20,7 @@
 //!
 //! Used for BEEFY gadget internal error handling only
 
+use sp_blockchain::Error as ClientError;
 use std::fmt::Debug;
 
 #[derive(Debug, thiserror::Error)]
@@ -48,6 +49,12 @@ pub enum Error {
 	VotesGossipStreamTerminated,
 }
 
+impl From<ClientError> for Error {
+	fn from(e: ClientError) -> Self {
+		Self::Backend(e.to_string())
+	}
+}
+
 #[cfg(test)]
 impl PartialEq for Error {
 	fn eq(&self, other: &Self) -> bool {
diff --git a/substrate/client/consensus/beefy/src/fisherman.rs b/substrate/client/consensus/beefy/src/fisherman.rs
index a2b4c8f945d1c224c45e522bc7e7b0461a3c00ce..073fee0bdbdbecb3ee6d869a3e727d3ec62b9c9b 100644
--- a/substrate/client/consensus/beefy/src/fisherman.rs
+++ b/substrate/client/consensus/beefy/src/fisherman.rs
@@ -20,11 +20,11 @@ use crate::{error::Error, keystore::BeefyKeystore, round::Rounds, LOG_TARGET};
 use log::{debug, error, warn};
 use sc_client_api::Backend;
 use sp_api::ProvideRuntimeApi;
+use sp_application_crypto::RuntimeAppPublic;
 use sp_blockchain::HeaderBackend;
 use sp_consensus_beefy::{
-	check_equivocation_proof,
-	ecdsa_crypto::{AuthorityId, Signature},
-	BeefyApi, BeefySignatureHasher, DoubleVotingProof, OpaqueKeyOwnershipProof, ValidatorSetId,
+	check_equivocation_proof, AuthorityIdBound, BeefyApi, BeefySignatureHasher, DoubleVotingProof,
+	OpaqueKeyOwnershipProof, ValidatorSetId,
 };
 use sp_runtime::{
 	generic::BlockId,
@@ -33,13 +33,13 @@ use sp_runtime::{
 use std::{marker::PhantomData, sync::Arc};
 
 /// Helper struct containing the id and the key ownership proof for a validator.
-pub struct ProvedValidator<'a> {
+pub struct ProvedValidator<'a, AuthorityId: AuthorityIdBound> {
 	pub id: &'a AuthorityId,
 	pub key_owner_proof: OpaqueKeyOwnershipProof,
 }
 
 /// Helper used to check and report equivocations.
-pub struct Fisherman<B, BE, RuntimeApi> {
+pub struct Fisherman<B, BE, RuntimeApi, AuthorityId: AuthorityIdBound> {
 	backend: Arc<BE>,
 	runtime: Arc<RuntimeApi>,
 	key_store: Arc<BeefyKeystore<AuthorityId>>,
@@ -47,9 +47,11 @@ pub struct Fisherman<B, BE, RuntimeApi> {
 	_phantom: PhantomData<B>,
 }
 
-impl<B: Block, BE: Backend<B>, RuntimeApi: ProvideRuntimeApi<B>> Fisherman<B, BE, RuntimeApi>
+impl<B: Block, BE: Backend<B>, RuntimeApi: ProvideRuntimeApi<B>, AuthorityId>
+	Fisherman<B, BE, RuntimeApi, AuthorityId>
 where
 	RuntimeApi::Api: BeefyApi<B, AuthorityId>,
+	AuthorityId: AuthorityIdBound,
 {
 	pub fn new(
 		backend: Arc<BE>,
@@ -64,7 +66,7 @@ where
 		at: BlockId<B>,
 		offender_ids: impl Iterator<Item = &'a AuthorityId>,
 		validator_set_id: ValidatorSetId,
-	) -> Result<Vec<ProvedValidator<'a>>, Error> {
+	) -> Result<Vec<ProvedValidator<'a, AuthorityId>>, Error> {
 		let hash = match at {
 			BlockId::Hash(hash) => hash,
 			BlockId::Number(number) => self
@@ -119,8 +121,12 @@ where
 	/// isn't necessarily the best block if there are pending authority set changes.
 	pub fn report_double_voting(
 		&self,
-		proof: DoubleVotingProof<NumberFor<B>, AuthorityId, Signature>,
-		active_rounds: &Rounds<B>,
+		proof: DoubleVotingProof<
+			NumberFor<B>,
+			AuthorityId,
+			<AuthorityId as RuntimeAppPublic>::Signature,
+		>,
+		active_rounds: &Rounds<B, AuthorityId>,
 	) -> Result<(), Error> {
 		let (validators, validator_set_id) =
 			(active_rounds.validators(), active_rounds.validator_set_id());
@@ -128,13 +134,13 @@ where
 
 		if !check_equivocation_proof::<_, _, BeefySignatureHasher>(&proof) {
 			debug!(target: LOG_TARGET, "🥩 Skipping report for bad equivocation {:?}", proof);
-			return Ok(())
+			return Ok(());
 		}
 
 		if let Some(local_id) = self.key_store.authority_id(validators) {
 			if offender_id == &local_id {
 				warn!(target: LOG_TARGET, "🥩 Skipping report for own equivocation");
-				return Ok(())
+				return Ok(());
 			}
 		}
 
diff --git a/substrate/client/consensus/beefy/src/import.rs b/substrate/client/consensus/beefy/src/import.rs
index ed8ed68c4e8d0d378728ba87d1ffe726f6c4c11a..c01fb3db4845eb9e413e0dbe781ae395a3b31217 100644
--- a/substrate/client/consensus/beefy/src/import.rs
+++ b/substrate/client/consensus/beefy/src/import.rs
@@ -22,7 +22,7 @@ use log::debug;
 
 use sp_api::ProvideRuntimeApi;
 use sp_consensus::Error as ConsensusError;
-use sp_consensus_beefy::{ecdsa_crypto::AuthorityId, BeefyApi, BEEFY_ENGINE_ID};
+use sp_consensus_beefy::{AuthorityIdBound, BeefyApi, BEEFY_ENGINE_ID};
 use sp_runtime::{
 	traits::{Block as BlockT, Header as HeaderT, NumberFor},
 	EncodedJustification,
@@ -45,15 +45,17 @@ use crate::{
 /// Wraps a `inner: BlockImport` and ultimately defers to it.
 ///
 /// When using BEEFY, the block import worker should be using this block import object.
-pub struct BeefyBlockImport<Block: BlockT, Backend, RuntimeApi, I> {
+pub struct BeefyBlockImport<Block: BlockT, Backend, RuntimeApi, I, AuthorityId: AuthorityIdBound> {
 	backend: Arc<Backend>,
 	runtime: Arc<RuntimeApi>,
 	inner: I,
-	justification_sender: BeefyVersionedFinalityProofSender<Block>,
+	justification_sender: BeefyVersionedFinalityProofSender<Block, AuthorityId>,
 	metrics: Option<BlockImportMetrics>,
 }
 
-impl<Block: BlockT, BE, Runtime, I: Clone> Clone for BeefyBlockImport<Block, BE, Runtime, I> {
+impl<Block: BlockT, BE, Runtime, I: Clone, AuthorityId: AuthorityIdBound> Clone
+	for BeefyBlockImport<Block, BE, Runtime, I, AuthorityId>
+{
 	fn clone(&self) -> Self {
 		BeefyBlockImport {
 			backend: self.backend.clone(),
@@ -65,32 +67,35 @@ impl<Block: BlockT, BE, Runtime, I: Clone> Clone for BeefyBlockImport<Block, BE,
 	}
 }
 
-impl<Block: BlockT, BE, Runtime, I> BeefyBlockImport<Block, BE, Runtime, I> {
+impl<Block: BlockT, BE, Runtime, I, AuthorityId: AuthorityIdBound>
+	BeefyBlockImport<Block, BE, Runtime, I, AuthorityId>
+{
 	/// Create a new BeefyBlockImport.
 	pub fn new(
 		backend: Arc<BE>,
 		runtime: Arc<Runtime>,
 		inner: I,
-		justification_sender: BeefyVersionedFinalityProofSender<Block>,
+		justification_sender: BeefyVersionedFinalityProofSender<Block, AuthorityId>,
 		metrics: Option<BlockImportMetrics>,
-	) -> BeefyBlockImport<Block, BE, Runtime, I> {
+	) -> BeefyBlockImport<Block, BE, Runtime, I, AuthorityId> {
 		BeefyBlockImport { backend, runtime, inner, justification_sender, metrics }
 	}
 }
 
-impl<Block, BE, Runtime, I> BeefyBlockImport<Block, BE, Runtime, I>
+impl<Block, BE, Runtime, I, AuthorityId> BeefyBlockImport<Block, BE, Runtime, I, AuthorityId>
 where
 	Block: BlockT,
 	BE: Backend<Block>,
 	Runtime: ProvideRuntimeApi<Block>,
 	Runtime::Api: BeefyApi<Block, AuthorityId> + Send,
+	AuthorityId: AuthorityIdBound,
 {
 	fn decode_and_verify(
 		&self,
 		encoded: &EncodedJustification,
 		number: NumberFor<Block>,
 		hash: <Block as BlockT>::Hash,
-	) -> Result<BeefyVersionedFinalityProof<Block>, ConsensusError> {
+	) -> Result<BeefyVersionedFinalityProof<Block, AuthorityId>, ConsensusError> {
 		use ConsensusError::ClientImport as ImportError;
 		let beefy_genesis = self
 			.runtime
@@ -99,7 +104,7 @@ where
 			.map_err(|e| ImportError(e.to_string()))?
 			.ok_or_else(|| ImportError("Unknown BEEFY genesis".to_string()))?;
 		if number < beefy_genesis {
-			return Err(ImportError("BEEFY genesis is set for future block".to_string()))
+			return Err(ImportError("BEEFY genesis is set for future block".to_string()));
 		}
 		let validator_set = self
 			.runtime
@@ -108,19 +113,21 @@ where
 			.map_err(|e| ImportError(e.to_string()))?
 			.ok_or_else(|| ImportError("Unknown validator set".to_string()))?;
 
-		decode_and_verify_finality_proof::<Block>(&encoded[..], number, &validator_set)
+		decode_and_verify_finality_proof::<Block, AuthorityId>(&encoded[..], number, &validator_set)
 			.map_err(|(err, _)| err)
 	}
 }
 
 #[async_trait::async_trait]
-impl<Block, BE, Runtime, I> BlockImport<Block> for BeefyBlockImport<Block, BE, Runtime, I>
+impl<Block, BE, Runtime, I, AuthorityId> BlockImport<Block>
+	for BeefyBlockImport<Block, BE, Runtime, I, AuthorityId>
 where
 	Block: BlockT,
 	BE: Backend<Block>,
 	I: BlockImport<Block, Error = ConsensusError> + Send + Sync,
 	Runtime: ProvideRuntimeApi<Block> + Send + Sync,
 	Runtime::Api: BeefyApi<Block, AuthorityId>,
+	AuthorityId: AuthorityIdBound,
 {
 	type Error = ConsensusError;
 
@@ -148,7 +155,7 @@ where
 				// The block is imported as part of some chain sync.
 				// The voter doesn't need to process it now.
 				// It will be detected and processed as part of the voter state init.
-				return Ok(inner_import_result)
+				return Ok(inner_import_result);
 			},
 		}
 
diff --git a/substrate/client/consensus/beefy/src/justification.rs b/substrate/client/consensus/beefy/src/justification.rs
index 886368c9d7cb096c0178819e1bad23f06c82f3fb..9ff7c3cf54f687c8f937ff0cecd9aea821c2ddaa 100644
--- a/substrate/client/consensus/beefy/src/justification.rs
+++ b/substrate/client/consensus/beefy/src/justification.rs
@@ -17,18 +17,20 @@
 // along with this program. If not, see <https://www.gnu.org/licenses/>.
 
 use codec::DecodeAll;
+use sp_application_crypto::RuntimeAppPublic;
 use sp_consensus::Error as ConsensusError;
 use sp_consensus_beefy::{
-	ecdsa_crypto::{AuthorityId, Signature},
-	BeefySignatureHasher, KnownSignature, ValidatorSet, ValidatorSetId, VersionedFinalityProof,
+	AuthorityIdBound, BeefySignatureHasher, KnownSignature, ValidatorSet, ValidatorSetId,
+	VersionedFinalityProof,
 };
 use sp_runtime::traits::{Block as BlockT, NumberFor};
 
 /// A finality proof with matching BEEFY authorities' signatures.
-pub type BeefyVersionedFinalityProof<Block> = VersionedFinalityProof<NumberFor<Block>, Signature>;
+pub type BeefyVersionedFinalityProof<Block, AuthorityId> =
+	VersionedFinalityProof<NumberFor<Block>, <AuthorityId as RuntimeAppPublic>::Signature>;
 
-pub(crate) fn proof_block_num_and_set_id<Block: BlockT>(
-	proof: &BeefyVersionedFinalityProof<Block>,
+pub(crate) fn proof_block_num_and_set_id<Block: BlockT, AuthorityId: AuthorityIdBound>(
+	proof: &BeefyVersionedFinalityProof<Block, AuthorityId>,
 ) -> (NumberFor<Block>, ValidatorSetId) {
 	match proof {
 		VersionedFinalityProof::V1(sc) =>
@@ -37,23 +39,26 @@ pub(crate) fn proof_block_num_and_set_id<Block: BlockT>(
 }
 
 /// Decode and verify a Beefy FinalityProof.
-pub(crate) fn decode_and_verify_finality_proof<Block: BlockT>(
+pub(crate) fn decode_and_verify_finality_proof<Block: BlockT, AuthorityId: AuthorityIdBound>(
 	encoded: &[u8],
 	target_number: NumberFor<Block>,
 	validator_set: &ValidatorSet<AuthorityId>,
-) -> Result<BeefyVersionedFinalityProof<Block>, (ConsensusError, u32)> {
-	let proof = <BeefyVersionedFinalityProof<Block>>::decode_all(&mut &*encoded)
+) -> Result<BeefyVersionedFinalityProof<Block, AuthorityId>, (ConsensusError, u32)> {
+	let proof = <BeefyVersionedFinalityProof<Block, AuthorityId>>::decode_all(&mut &*encoded)
 		.map_err(|_| (ConsensusError::InvalidJustification, 0))?;
-	verify_with_validator_set::<Block>(target_number, validator_set, &proof)?;
+	verify_with_validator_set::<Block, AuthorityId>(target_number, validator_set, &proof)?;
 	Ok(proof)
 }
 
 /// Verify the Beefy finality proof against the validator set at the block it was generated.
-pub(crate) fn verify_with_validator_set<'a, Block: BlockT>(
+pub(crate) fn verify_with_validator_set<'a, Block: BlockT, AuthorityId: AuthorityIdBound>(
 	target_number: NumberFor<Block>,
 	validator_set: &'a ValidatorSet<AuthorityId>,
-	proof: &'a BeefyVersionedFinalityProof<Block>,
-) -> Result<Vec<KnownSignature<&'a AuthorityId, &'a Signature>>, (ConsensusError, u32)> {
+	proof: &'a BeefyVersionedFinalityProof<Block, AuthorityId>,
+) -> Result<
+	Vec<KnownSignature<&'a AuthorityId, &'a <AuthorityId as RuntimeAppPublic>::Signature>>,
+	(ConsensusError, u32),
+> {
 	match proof {
 		VersionedFinalityProof::V1(signed_commitment) => {
 			let signatories = signed_commitment
@@ -78,7 +83,7 @@ pub(crate) fn verify_with_validator_set<'a, Block: BlockT>(
 pub(crate) mod tests {
 	use codec::Encode;
 	use sp_consensus_beefy::{
-		known_payloads, test_utils::Keyring, Commitment, Payload, SignedCommitment,
+		ecdsa_crypto, known_payloads, test_utils::Keyring, Commitment, Payload, SignedCommitment,
 		VersionedFinalityProof,
 	};
 	use substrate_test_runtime_client::runtime::Block;
@@ -88,9 +93,9 @@ pub(crate) mod tests {
 
 	pub(crate) fn new_finality_proof(
 		block_num: NumberFor<Block>,
-		validator_set: &ValidatorSet<AuthorityId>,
-		keys: &[Keyring<AuthorityId>],
-	) -> BeefyVersionedFinalityProof<Block> {
+		validator_set: &ValidatorSet<ecdsa_crypto::AuthorityId>,
+		keys: &[Keyring<ecdsa_crypto::AuthorityId>],
+	) -> BeefyVersionedFinalityProof<Block, ecdsa_crypto::AuthorityId> {
 		let commitment = Commitment {
 			payload: Payload::from_single_entry(known_payloads::MMR_ROOT_ID, vec![]),
 			block_number: block_num,
@@ -112,11 +117,20 @@ pub(crate) mod tests {
 
 		let good_proof = proof.clone().into();
 		// should verify successfully
-		verify_with_validator_set::<Block>(block_num, &validator_set, &good_proof).unwrap();
+		verify_with_validator_set::<Block, ecdsa_crypto::AuthorityId>(
+			block_num,
+			&validator_set,
+			&good_proof,
+		)
+		.unwrap();
 
 		// wrong block number -> should fail verification
 		let good_proof = proof.clone().into();
-		match verify_with_validator_set::<Block>(block_num + 1, &validator_set, &good_proof) {
+		match verify_with_validator_set::<Block, ecdsa_crypto::AuthorityId>(
+			block_num + 1,
+			&validator_set,
+			&good_proof,
+		) {
 			Err((ConsensusError::InvalidJustification, 0)) => (),
 			e => assert!(false, "Got unexpected {:?}", e),
 		};
@@ -124,7 +138,11 @@ pub(crate) mod tests {
 		// wrong validator set id -> should fail verification
 		let good_proof = proof.clone().into();
 		let other = ValidatorSet::new(make_beefy_ids(keys), 1).unwrap();
-		match verify_with_validator_set::<Block>(block_num, &other, &good_proof) {
+		match verify_with_validator_set::<Block, ecdsa_crypto::AuthorityId>(
+			block_num,
+			&other,
+			&good_proof,
+		) {
 			Err((ConsensusError::InvalidJustification, 0)) => (),
 			e => assert!(false, "Got unexpected {:?}", e),
 		};
@@ -136,7 +154,11 @@ pub(crate) mod tests {
 			VersionedFinalityProof::V1(ref mut sc) => sc,
 		};
 		bad_signed_commitment.signatures.pop().flatten().unwrap();
-		match verify_with_validator_set::<Block>(block_num + 1, &validator_set, &bad_proof.into()) {
+		match verify_with_validator_set::<Block, ecdsa_crypto::AuthorityId>(
+			block_num + 1,
+			&validator_set,
+			&bad_proof.into(),
+		) {
 			Err((ConsensusError::InvalidJustification, 0)) => (),
 			e => assert!(false, "Got unexpected {:?}", e),
 		};
@@ -148,7 +170,11 @@ pub(crate) mod tests {
 		};
 		// remove a signature (but same length)
 		*bad_signed_commitment.signatures.first_mut().unwrap() = None;
-		match verify_with_validator_set::<Block>(block_num, &validator_set, &bad_proof.into()) {
+		match verify_with_validator_set::<Block, ecdsa_crypto::AuthorityId>(
+			block_num,
+			&validator_set,
+			&bad_proof.into(),
+		) {
 			Err((ConsensusError::InvalidJustification, 2)) => (),
 			e => assert!(false, "Got unexpected {:?}", e),
 		};
@@ -159,9 +185,15 @@ pub(crate) mod tests {
 			VersionedFinalityProof::V1(ref mut sc) => sc,
 		};
 		// change a signature to a different key
-		*bad_signed_commitment.signatures.first_mut().unwrap() =
-			Some(Keyring::<AuthorityId>::Dave.sign(&bad_signed_commitment.commitment.encode()));
-		match verify_with_validator_set::<Block>(block_num, &validator_set, &bad_proof.into()) {
+		*bad_signed_commitment.signatures.first_mut().unwrap() = Some(
+			Keyring::<ecdsa_crypto::AuthorityId>::Dave
+				.sign(&bad_signed_commitment.commitment.encode()),
+		);
+		match verify_with_validator_set::<Block, ecdsa_crypto::AuthorityId>(
+			block_num,
+			&validator_set,
+			&bad_proof.into(),
+		) {
 			Err((ConsensusError::InvalidJustification, 3)) => (),
 			e => assert!(false, "Got unexpected {:?}", e),
 		};
@@ -175,12 +207,17 @@ pub(crate) mod tests {
 
 		// build valid justification
 		let proof = new_finality_proof(block_num, &validator_set, keys);
-		let versioned_proof: BeefyVersionedFinalityProof<Block> = proof.into();
+		let versioned_proof: BeefyVersionedFinalityProof<Block, ecdsa_crypto::AuthorityId> =
+			proof.into();
 		let encoded = versioned_proof.encode();
 
 		// should successfully decode and verify
-		let verified =
-			decode_and_verify_finality_proof::<Block>(&encoded, block_num, &validator_set).unwrap();
+		let verified = decode_and_verify_finality_proof::<Block, ecdsa_crypto::AuthorityId>(
+			&encoded,
+			block_num,
+			&validator_set,
+		)
+		.unwrap();
 		assert_eq!(verified, versioned_proof);
 	}
 }
diff --git a/substrate/client/consensus/beefy/src/keystore.rs b/substrate/client/consensus/beefy/src/keystore.rs
index 9582c2661c30b431a5934e2bcc592b4a56faf3b3..8daf3440c7d2c745cc0ef85bc3d7436a6d91e1f4 100644
--- a/substrate/client/consensus/beefy/src/keystore.rs
+++ b/substrate/client/consensus/beefy/src/keystore.rs
@@ -15,19 +15,19 @@
 
 // You should have received a copy of the GNU General Public License
 // along with this program. If not, see <https://www.gnu.org/licenses/>.
+use codec::Decode;
+use log::warn;
 
 use sp_application_crypto::{key_types::BEEFY as BEEFY_KEY_TYPE, AppCrypto, RuntimeAppPublic};
-use sp_consensus_beefy::{AuthorityIdBound, BeefyAuthorityId, BeefySignatureHasher};
-use sp_core::ecdsa;
 #[cfg(feature = "bls-experimental")]
 use sp_core::ecdsa_bls377;
-use sp_crypto_hashing::keccak_256;
-use sp_keystore::KeystorePtr;
+use sp_core::{ecdsa, keccak_256};
 
-use codec::Decode;
-use log::warn;
+use sp_keystore::KeystorePtr;
 use std::marker::PhantomData;
 
+use sp_consensus_beefy::{AuthorityIdBound, BeefyAuthorityId, BeefySignatureHasher};
+
 use crate::{error, LOG_TARGET};
 
 /// A BEEFY specific keystore implemented as a `Newtype`. This is basically a
@@ -175,10 +175,7 @@ impl<AuthorityId: AuthorityIdBound> BeefyKeystore<AuthorityId> {
 	}
 }
 
-impl<AuthorityId: AuthorityIdBound> From<Option<KeystorePtr>> for BeefyKeystore<AuthorityId>
-where
-	<AuthorityId as RuntimeAppPublic>::Signature: Send + Sync,
-{
+impl<AuthorityId: AuthorityIdBound> From<Option<KeystorePtr>> for BeefyKeystore<AuthorityId> {
 	fn from(store: Option<KeystorePtr>) -> BeefyKeystore<AuthorityId> {
 		BeefyKeystore(store, PhantomData)
 	}
diff --git a/substrate/client/consensus/beefy/src/lib.rs b/substrate/client/consensus/beefy/src/lib.rs
index 0e49839f0fd2dae76e26738823ea9e5af775acb7..4cb014b00d5bedf36c7ce74b5649f28bd0d55f60 100644
--- a/substrate/client/consensus/beefy/src/lib.rs
+++ b/substrate/client/consensus/beefy/src/lib.rs
@@ -43,8 +43,7 @@ use sp_api::ProvideRuntimeApi;
 use sp_blockchain::{Backend as BlockchainBackend, HeaderBackend};
 use sp_consensus::{Error as ConsensusError, SyncOracle};
 use sp_consensus_beefy::{
-	ecdsa_crypto::AuthorityId, BeefyApi, ConsensusLog, PayloadProvider, ValidatorSet,
-	BEEFY_ENGINE_ID,
+	AuthorityIdBound, BeefyApi, ConsensusLog, PayloadProvider, ValidatorSet, BEEFY_ENGINE_ID,
 };
 use sp_keystore::KeystorePtr;
 use sp_runtime::traits::{Block, Header as HeaderT, NumberFor, Zero};
@@ -118,50 +117,55 @@ where
 /// Links between the block importer, the background voter and the RPC layer,
 /// to be used by the voter.
 #[derive(Clone)]
-pub struct BeefyVoterLinks<B: Block> {
+pub struct BeefyVoterLinks<B: Block, AuthorityId: AuthorityIdBound> {
 	// BlockImport -> Voter links
 	/// Stream of BEEFY signed commitments from block import to voter.
-	pub from_block_import_justif_stream: BeefyVersionedFinalityProofStream<B>,
+	pub from_block_import_justif_stream: BeefyVersionedFinalityProofStream<B, AuthorityId>,
 
 	// Voter -> RPC links
 	/// Sends BEEFY signed commitments from voter to RPC.
-	pub to_rpc_justif_sender: BeefyVersionedFinalityProofSender<B>,
+	pub to_rpc_justif_sender: BeefyVersionedFinalityProofSender<B, AuthorityId>,
 	/// Sends BEEFY best block hashes from voter to RPC.
 	pub to_rpc_best_block_sender: BeefyBestBlockSender<B>,
 }
 
 /// Links used by the BEEFY RPC layer, from the BEEFY background voter.
 #[derive(Clone)]
-pub struct BeefyRPCLinks<B: Block> {
+pub struct BeefyRPCLinks<B: Block, AuthorityId: AuthorityIdBound> {
 	/// Stream of signed commitments coming from the voter.
-	pub from_voter_justif_stream: BeefyVersionedFinalityProofStream<B>,
+	pub from_voter_justif_stream: BeefyVersionedFinalityProofStream<B, AuthorityId>,
 	/// Stream of BEEFY best block hashes coming from the voter.
 	pub from_voter_best_beefy_stream: BeefyBestBlockStream<B>,
 }
 
 /// Make block importer and link half necessary to tie the background voter to it.
-pub fn beefy_block_import_and_links<B, BE, RuntimeApi, I>(
+pub fn beefy_block_import_and_links<B, BE, RuntimeApi, I, AuthorityId: AuthorityIdBound>(
 	wrapped_block_import: I,
 	backend: Arc<BE>,
 	runtime: Arc<RuntimeApi>,
 	prometheus_registry: Option<Registry>,
-) -> (BeefyBlockImport<B, BE, RuntimeApi, I>, BeefyVoterLinks<B>, BeefyRPCLinks<B>)
+) -> (
+	BeefyBlockImport<B, BE, RuntimeApi, I, AuthorityId>,
+	BeefyVoterLinks<B, AuthorityId>,
+	BeefyRPCLinks<B, AuthorityId>,
+)
 where
 	B: Block,
 	BE: Backend<B>,
 	I: BlockImport<B, Error = ConsensusError> + Send + Sync,
 	RuntimeApi: ProvideRuntimeApi<B> + Send + Sync,
 	RuntimeApi::Api: BeefyApi<B, AuthorityId>,
+	AuthorityId: AuthorityIdBound,
 {
 	// Voter -> RPC links
 	let (to_rpc_justif_sender, from_voter_justif_stream) =
-		BeefyVersionedFinalityProofStream::<B>::channel();
+		BeefyVersionedFinalityProofStream::<B, AuthorityId>::channel();
 	let (to_rpc_best_block_sender, from_voter_best_beefy_stream) =
 		BeefyBestBlockStream::<B>::channel();
 
 	// BlockImport -> Voter links
 	let (to_voter_justif_sender, from_block_import_justif_stream) =
-		BeefyVersionedFinalityProofStream::<B>::channel();
+		BeefyVersionedFinalityProofStream::<B, AuthorityId>::channel();
 	let metrics = register_metrics(prometheus_registry);
 
 	// BlockImport
@@ -201,7 +205,7 @@ pub struct BeefyNetworkParams<B: Block, N, S> {
 }
 
 /// BEEFY gadget initialization parameters.
-pub struct BeefyParams<B: Block, BE, C, N, P, R, S> {
+pub struct BeefyParams<B: Block, BE, C, N, P, R, S, AuthorityId: AuthorityIdBound> {
 	/// BEEFY client
 	pub client: Arc<C>,
 	/// Client Backend
@@ -219,7 +223,7 @@ pub struct BeefyParams<B: Block, BE, C, N, P, R, S> {
 	/// Prometheus metric registry
 	pub prometheus_registry: Option<Registry>,
 	/// Links between the block importer, the background voter and the RPC layer.
-	pub links: BeefyVoterLinks<B>,
+	pub links: BeefyVoterLinks<B, AuthorityId>,
 	/// Handler for incoming BEEFY justifications requests from a remote peer.
 	pub on_demand_justifications_handler: BeefyJustifsRequestHandler<B, C>,
 	/// Whether running under "Authority" role.
@@ -228,10 +232,10 @@ pub struct BeefyParams<B: Block, BE, C, N, P, R, S> {
 /// Helper object holding BEEFY worker communication/gossip components.
 ///
 /// These are created once, but will be reused if worker is restarted/reinitialized.
-pub(crate) struct BeefyComms<B: Block, N> {
+pub(crate) struct BeefyComms<B: Block, N, AuthorityId: AuthorityIdBound> {
 	pub gossip_engine: GossipEngine<B>,
-	pub gossip_validator: Arc<GossipValidator<B, N>>,
-	pub on_demand_justifications: OnDemandJustificationsEngine<B>,
+	pub gossip_validator: Arc<GossipValidator<B, N, AuthorityId>>,
+	pub on_demand_justifications: OnDemandJustificationsEngine<B, AuthorityId>,
 }
 
 /// Helper builder object for building [worker::BeefyWorker].
@@ -240,22 +244,23 @@ pub(crate) struct BeefyComms<B: Block, N> {
 /// for certain chain and backend conditions, and while sleeping we still need to pump the
 /// GossipEngine. Once initialization is done, the GossipEngine (and other pieces) are added to get
 /// the complete [worker::BeefyWorker] object.
-pub(crate) struct BeefyWorkerBuilder<B: Block, BE, RuntimeApi> {
+pub(crate) struct BeefyWorkerBuilder<B: Block, BE, RuntimeApi, AuthorityId: AuthorityIdBound> {
 	// utilities
 	backend: Arc<BE>,
 	runtime: Arc<RuntimeApi>,
 	key_store: BeefyKeystore<AuthorityId>,
 	// voter metrics
 	metrics: Option<VoterMetrics>,
-	persisted_state: PersistedState<B>,
+	persisted_state: PersistedState<B, AuthorityId>,
 }
 
-impl<B, BE, R> BeefyWorkerBuilder<B, BE, R>
+impl<B, BE, R, AuthorityId> BeefyWorkerBuilder<B, BE, R, AuthorityId>
 where
 	B: Block + codec::Codec,
 	BE: Backend<B>,
 	R: ProvideRuntimeApi<B>,
 	R::Api: BeefyApi<B, AuthorityId>,
+	AuthorityId: AuthorityIdBound,
 {
 	/// This will wait for the chain to enable BEEFY (if not yet enabled) and also wait for the
 	/// backend to sync all headers required by the voter to build a contiguous chain of mandatory
@@ -269,7 +274,7 @@ where
 		key_store: BeefyKeystore<AuthorityId>,
 		metrics: Option<VoterMetrics>,
 		min_block_delta: u32,
-		gossip_validator: Arc<GossipValidator<B, N>>,
+		gossip_validator: Arc<GossipValidator<B, N, AuthorityId>>,
 		finality_notifications: &mut Fuse<FinalityNotifications<B>>,
 		is_authority: bool,
 	) -> Result<Self, Error> {
@@ -301,11 +306,11 @@ where
 		self,
 		payload_provider: P,
 		sync: Arc<S>,
-		comms: BeefyComms<B, N>,
-		links: BeefyVoterLinks<B>,
-		pending_justifications: BTreeMap<NumberFor<B>, BeefyVersionedFinalityProof<B>>,
+		comms: BeefyComms<B, N, AuthorityId>,
+		links: BeefyVoterLinks<B, AuthorityId>,
+		pending_justifications: BTreeMap<NumberFor<B>, BeefyVersionedFinalityProof<B, AuthorityId>>,
 		is_authority: bool,
-	) -> BeefyWorker<B, BE, P, R, S, N> {
+	) -> BeefyWorker<B, BE, P, R, S, N, AuthorityId> {
 		let key_store = Arc::new(self.key_store);
 		BeefyWorker {
 			backend: self.backend.clone(),
@@ -334,7 +339,7 @@ where
 		min_block_delta: u32,
 		backend: Arc<BE>,
 		runtime: Arc<R>,
-	) -> Result<PersistedState<B>, Error> {
+	) -> Result<PersistedState<B, AuthorityId>, Error> {
 		let blockchain = backend.blockchain();
 
 		let beefy_genesis = runtime
@@ -378,7 +383,7 @@ where
 					beefy_genesis,
 				)
 				.ok_or_else(|| Error::Backend("Invalid BEEFY chain".into()))?;
-				break state
+				break state;
 			}
 
 			if *header.number() == beefy_genesis {
@@ -401,10 +406,10 @@ where
 					min_block_delta,
 					beefy_genesis,
 				)
-				.ok_or_else(|| Error::Backend("Invalid BEEFY chain".into()))?
+				.ok_or_else(|| Error::Backend("Invalid BEEFY chain".into()))?;
 			}
 
-			if let Some(active) = find_authorities_change::<B>(&header) {
+			if let Some(active) = find_authorities_change::<B, AuthorityId>(&header) {
 				debug!(
 					target: LOG_TARGET,
 					"🥩 Marking block {:?} as BEEFY Mandatory.",
@@ -431,7 +436,7 @@ where
 		key_store: &BeefyKeystore<AuthorityId>,
 		metrics: &Option<VoterMetrics>,
 		is_authority: bool,
-	) -> Result<PersistedState<B>, Error> {
+	) -> Result<PersistedState<B, AuthorityId>, Error> {
 		// Initialize voter state from AUX DB if compatible.
 		if let Some(mut state) = crate::aux_schema::load_persistent(backend.as_ref())?
 			// Verify state pallet genesis matches runtime.
@@ -448,7 +453,7 @@ where
 			let mut header = best_grandpa.clone();
 			while *header.number() > state.best_beefy() {
 				if state.voting_oracle().can_add_session(*header.number()) {
-					if let Some(active) = find_authorities_change::<B>(&header) {
+					if let Some(active) = find_authorities_change::<B, AuthorityId>(&header) {
 						new_sessions.push((active, *header.number()));
 					}
 				}
@@ -471,7 +476,7 @@ where
 					is_authority,
 				);
 			}
-			return Ok(state)
+			return Ok(state);
 		}
 
 		// No valid voter-state persisted, re-initialize from pallet genesis.
@@ -482,8 +487,8 @@ where
 /// Start the BEEFY gadget.
 ///
 /// This is a thin shim around running and awaiting a BEEFY worker.
-pub async fn start_beefy_gadget<B, BE, C, N, P, R, S>(
-	beefy_params: BeefyParams<B, BE, C, N, P, R, S>,
+pub async fn start_beefy_gadget<B, BE, C, N, P, R, S, AuthorityId>(
+	beefy_params: BeefyParams<B, BE, C, N, P, R, S, AuthorityId>,
 ) where
 	B: Block,
 	BE: Backend<B>,
@@ -493,6 +498,7 @@ pub async fn start_beefy_gadget<B, BE, C, N, P, R, S>(
 	R::Api: BeefyApi<B, AuthorityId>,
 	N: GossipNetwork<B> + NetworkRequest + Send + Sync + 'static,
 	S: GossipSyncing<B> + SyncOracle + 'static,
+	AuthorityId: AuthorityIdBound,
 {
 	let BeefyParams {
 		client,
@@ -598,15 +604,17 @@ pub async fn start_beefy_gadget<B, BE, C, N, P, R, S>(
 			futures::future::Either::Left(((error::Error::ConsensusReset, reuse_comms), _)) => {
 				error!(target: LOG_TARGET, "🥩 Error: {:?}. Restarting voter.", error::Error::ConsensusReset);
 				beefy_comms = reuse_comms;
-				continue
+				continue;
 			},
 			// On other errors, bring down / finish the task.
-			futures::future::Either::Left(((worker_err, _), _)) =>
-				error!(target: LOG_TARGET, "🥩 Error: {:?}. Terminating.", worker_err),
-			futures::future::Either::Right((odj_handler_err, _)) =>
-				error!(target: LOG_TARGET, "🥩 Error: {:?}. Terminating.", odj_handler_err),
+			futures::future::Either::Left(((worker_err, _), _)) => {
+				error!(target: LOG_TARGET, "🥩 Error: {:?}. Terminating.", worker_err)
+			},
+			futures::future::Either::Right((odj_handler_err, _)) => {
+				error!(target: LOG_TARGET, "🥩 Error: {:?}. Terminating.", odj_handler_err)
+			},
 		};
-		return
+		return;
 	}
 }
 
@@ -651,7 +659,7 @@ where
 
 /// Wait for BEEFY runtime pallet to be available, return active validator set.
 /// Should be called only once during worker initialization.
-async fn wait_for_runtime_pallet<B, R>(
+async fn wait_for_runtime_pallet<B, R, AuthorityId: AuthorityIdBound>(
 	runtime: &R,
 	finality: &mut Fuse<FinalityNotifications<B>>,
 ) -> Result<(NumberFor<B>, <B as Block>::Header), Error>
@@ -676,7 +684,7 @@ where
 					"🥩 BEEFY pallet available: block {:?} beefy genesis {:?}",
 					notif.header.number(), start
 				);
-				return Ok((start, notif.header))
+				return Ok((start, notif.header));
 			}
 		}
 	}
@@ -687,7 +695,7 @@ where
 ///
 /// Note: function will `async::sleep()` when walking back the chain if some needed header hasn't
 /// been synced yet (as it happens when warp syncing when headers are synced in the background).
-async fn expect_validator_set<B, BE, R>(
+async fn expect_validator_set<B, BE, R, AuthorityId: AuthorityIdBound>(
 	runtime: &R,
 	backend: &BE,
 	at_header: &B::Header,
@@ -711,9 +719,9 @@ where
 	loop {
 		debug!(target: LOG_TARGET, "🥩 Looking for auth set change at block number: {:?}", *header.number());
 		if let Ok(Some(active)) = runtime.runtime_api().validator_set(header.hash()) {
-			return Ok(active)
+			return Ok(active);
 		} else {
-			match find_authorities_change::<B>(&header) {
+			match find_authorities_change::<B, AuthorityId>(&header) {
 				Some(active) => return Ok(active),
 				// Move up the chain. Ultimately we'll get it from chain genesis state, or error out
 				// there.
@@ -728,9 +736,12 @@ where
 
 /// Scan the `header` digest log for a BEEFY validator set change. Return either the new
 /// validator set or `None` in case no validator set change has been signaled.
-pub(crate) fn find_authorities_change<B>(header: &B::Header) -> Option<ValidatorSet<AuthorityId>>
+pub(crate) fn find_authorities_change<B, AuthorityId>(
+	header: &B::Header,
+) -> Option<ValidatorSet<AuthorityId>>
 where
 	B: Block,
+	AuthorityId: AuthorityIdBound,
 {
 	let id = OpaqueDigestItemId::Consensus(&BEEFY_ENGINE_ID);
 
diff --git a/substrate/client/consensus/beefy/src/round.rs b/substrate/client/consensus/beefy/src/round.rs
index 5dae80cb1830ddc2d6402625865af6e965046897..31cfe4c10c2e7d3e7112c353aff1c0572a7bd52b 100644
--- a/substrate/client/consensus/beefy/src/round.rs
+++ b/substrate/client/consensus/beefy/src/round.rs
@@ -20,9 +20,10 @@ use crate::LOG_TARGET;
 
 use codec::{Decode, Encode};
 use log::{debug, info};
+use sp_application_crypto::RuntimeAppPublic;
 use sp_consensus_beefy::{
-	ecdsa_crypto::{AuthorityId, Signature},
-	Commitment, DoubleVotingProof, SignedCommitment, ValidatorSet, ValidatorSetId, VoteMessage,
+	AuthorityIdBound, Commitment, DoubleVotingProof, SignedCommitment, ValidatorSet,
+	ValidatorSetId, VoteMessage,
 };
 use sp_runtime::traits::{Block, NumberFor};
 use std::collections::BTreeMap;
@@ -31,15 +32,24 @@ use std::collections::BTreeMap;
 /// whether the local `self` validator has voted/signed.
 ///
 /// Does not do any validation on votes or signatures, layers above need to handle that (gossip).
-#[derive(Debug, Decode, Default, Encode, PartialEq)]
-pub(crate) struct RoundTracker {
-	votes: BTreeMap<AuthorityId, Signature>,
+#[derive(Debug, Decode, Encode, PartialEq)]
+pub(crate) struct RoundTracker<AuthorityId: AuthorityIdBound> {
+	votes: BTreeMap<AuthorityId, <AuthorityId as RuntimeAppPublic>::Signature>,
+}
+
+impl<AuthorityId: AuthorityIdBound> Default for RoundTracker<AuthorityId> {
+	fn default() -> Self {
+		Self { votes: Default::default() }
+	}
 }
 
-impl RoundTracker {
-	fn add_vote(&mut self, vote: (AuthorityId, Signature)) -> bool {
+impl<AuthorityId: AuthorityIdBound> RoundTracker<AuthorityId> {
+	fn add_vote(
+		&mut self,
+		vote: (AuthorityId, <AuthorityId as RuntimeAppPublic>::Signature),
+	) -> bool {
 		if self.votes.contains_key(&vote.0) {
-			return false
+			return false;
 		}
 
 		self.votes.insert(vote.0, vote.1);
@@ -58,10 +68,12 @@ pub fn threshold(authorities: usize) -> usize {
 }
 
 #[derive(Debug, PartialEq)]
-pub enum VoteImportResult<B: Block> {
+pub enum VoteImportResult<B: Block, AuthorityId: AuthorityIdBound> {
 	Ok,
-	RoundConcluded(SignedCommitment<NumberFor<B>, Signature>),
-	DoubleVoting(DoubleVotingProof<NumberFor<B>, AuthorityId, Signature>),
+	RoundConcluded(SignedCommitment<NumberFor<B>, <AuthorityId as RuntimeAppPublic>::Signature>),
+	DoubleVoting(
+		DoubleVotingProof<NumberFor<B>, AuthorityId, <AuthorityId as RuntimeAppPublic>::Signature>,
+	),
 	Invalid,
 	Stale,
 }
@@ -71,19 +83,22 @@ pub enum VoteImportResult<B: Block> {
 ///
 /// Does not do any validation on votes or signatures, layers above need to handle that (gossip).
 #[derive(Debug, Decode, Encode, PartialEq)]
-pub(crate) struct Rounds<B: Block> {
-	rounds: BTreeMap<Commitment<NumberFor<B>>, RoundTracker>,
-	previous_votes:
-		BTreeMap<(AuthorityId, NumberFor<B>), VoteMessage<NumberFor<B>, AuthorityId, Signature>>,
+pub(crate) struct Rounds<B: Block, AuthorityId: AuthorityIdBound> {
+	rounds: BTreeMap<Commitment<NumberFor<B>>, RoundTracker<AuthorityId>>,
+	previous_votes: BTreeMap<
+		(AuthorityId, NumberFor<B>),
+		VoteMessage<NumberFor<B>, AuthorityId, <AuthorityId as RuntimeAppPublic>::Signature>,
+	>,
 	session_start: NumberFor<B>,
 	validator_set: ValidatorSet<AuthorityId>,
 	mandatory_done: bool,
 	best_done: Option<NumberFor<B>>,
 }
 
-impl<B> Rounds<B>
+impl<B, AuthorityId> Rounds<B, AuthorityId>
 where
 	B: Block,
+	AuthorityId: AuthorityIdBound,
 {
 	pub(crate) fn new(
 		session_start: NumberFor<B>,
@@ -121,14 +136,14 @@ where
 
 	pub(crate) fn add_vote(
 		&mut self,
-		vote: VoteMessage<NumberFor<B>, AuthorityId, Signature>,
-	) -> VoteImportResult<B> {
+		vote: VoteMessage<NumberFor<B>, AuthorityId, <AuthorityId as RuntimeAppPublic>::Signature>,
+	) -> VoteImportResult<B, AuthorityId> {
 		let num = vote.commitment.block_number;
 		let vote_key = (vote.id.clone(), num);
 
 		if num < self.session_start || Some(num) <= self.best_done {
 			debug!(target: LOG_TARGET, "🥩 received vote for old stale round {:?}, ignoring", num);
-			return VoteImportResult::Stale
+			return VoteImportResult::Stale;
 		} else if vote.commitment.validator_set_id != self.validator_set_id() {
 			debug!(
 				target: LOG_TARGET,
@@ -136,14 +151,14 @@ where
 				self.validator_set_id(),
 				vote,
 			);
-			return VoteImportResult::Invalid
+			return VoteImportResult::Invalid;
 		} else if !self.validators().iter().any(|id| &vote.id == id) {
 			debug!(
 				target: LOG_TARGET,
 				"🥩 received vote {:?} from validator that is not in the validator set, ignoring",
 				vote
 			);
-			return VoteImportResult::Invalid
+			return VoteImportResult::Invalid;
 		}
 
 		if let Some(previous_vote) = self.previous_votes.get(&vote_key) {
@@ -156,7 +171,7 @@ where
 				return VoteImportResult::DoubleVoting(DoubleVotingProof {
 					first: previous_vote.clone(),
 					second: vote,
-				})
+				});
 			}
 		} else {
 			// this is the first vote sent by `id` for `num`, all good
@@ -169,7 +184,7 @@ where
 			round.is_done(threshold(self.validator_set.len()))
 		{
 			if let Some(round) = self.rounds.remove_entry(&vote.commitment) {
-				return VoteImportResult::RoundConcluded(self.signed_commitment(round))
+				return VoteImportResult::RoundConcluded(self.signed_commitment(round));
 			}
 		}
 		VoteImportResult::Ok
@@ -177,8 +192,8 @@ where
 
 	fn signed_commitment(
 		&mut self,
-		round: (Commitment<NumberFor<B>>, RoundTracker),
-	) -> SignedCommitment<NumberFor<B>, Signature> {
+		round: (Commitment<NumberFor<B>>, RoundTracker<AuthorityId>),
+	) -> SignedCommitment<NumberFor<B>, <AuthorityId as RuntimeAppPublic>::Signature> {
 		let votes = round.1.votes;
 		let signatures = self
 			.validators()
@@ -207,14 +222,14 @@ mod tests {
 	use sc_network_test::Block;
 
 	use sp_consensus_beefy::{
-		known_payloads::MMR_ROOT_ID, test_utils::Keyring, Commitment, DoubleVotingProof, Payload,
-		SignedCommitment, ValidatorSet, VoteMessage,
+		ecdsa_crypto, known_payloads::MMR_ROOT_ID, test_utils::Keyring, Commitment,
+		DoubleVotingProof, Payload, SignedCommitment, ValidatorSet, VoteMessage,
 	};
 
-	use super::{threshold, AuthorityId, Block as BlockT, RoundTracker, Rounds};
+	use super::{threshold, Block as BlockT, RoundTracker, Rounds};
 	use crate::round::VoteImportResult;
 
-	impl<B> Rounds<B>
+	impl<B> Rounds<B, ecdsa_crypto::AuthorityId>
 	where
 		B: BlockT,
 	{
@@ -225,8 +240,11 @@ mod tests {
 
 	#[test]
 	fn round_tracker() {
-		let mut rt = RoundTracker::default();
-		let bob_vote = (Keyring::Bob.public(), Keyring::<AuthorityId>::Bob.sign(b"I am committed"));
+		let mut rt = RoundTracker::<ecdsa_crypto::AuthorityId>::default();
+		let bob_vote = (
+			Keyring::<ecdsa_crypto::AuthorityId>::Bob.public(),
+			Keyring::<ecdsa_crypto::AuthorityId>::Bob.sign(b"I am committed"),
+		);
 		let threshold = 2;
 
 		// adding new vote allowed
@@ -237,8 +255,10 @@ mod tests {
 		// vote is not done
 		assert!(!rt.is_done(threshold));
 
-		let alice_vote =
-			(Keyring::Alice.public(), Keyring::<AuthorityId>::Alice.sign(b"I am committed"));
+		let alice_vote = (
+			Keyring::<ecdsa_crypto::AuthorityId>::Alice.public(),
+			Keyring::<ecdsa_crypto::AuthorityId>::Alice.sign(b"I am committed"),
+		);
 		// adding new vote (self vote this time) allowed
 		assert!(rt.add_vote(alice_vote));
 
@@ -260,22 +280,22 @@ mod tests {
 	fn new_rounds() {
 		sp_tracing::try_init_simple();
 
-		let validators = ValidatorSet::<AuthorityId>::new(
+		let validators = ValidatorSet::<ecdsa_crypto::AuthorityId>::new(
 			vec![Keyring::Alice.public(), Keyring::Bob.public(), Keyring::Charlie.public()],
 			42,
 		)
 		.unwrap();
 
 		let session_start = 1u64.into();
-		let rounds = Rounds::<Block>::new(session_start, validators);
+		let rounds = Rounds::<Block, ecdsa_crypto::AuthorityId>::new(session_start, validators);
 
 		assert_eq!(42, rounds.validator_set_id());
 		assert_eq!(1, rounds.session_start());
 		assert_eq!(
 			&vec![
-				Keyring::<AuthorityId>::Alice.public(),
-				Keyring::<AuthorityId>::Bob.public(),
-				Keyring::<AuthorityId>::Charlie.public()
+				Keyring::<ecdsa_crypto::AuthorityId>::Alice.public(),
+				Keyring::<ecdsa_crypto::AuthorityId>::Bob.public(),
+				Keyring::<ecdsa_crypto::AuthorityId>::Charlie.public()
 			],
 			rounds.validators()
 		);
@@ -285,7 +305,7 @@ mod tests {
 	fn add_and_conclude_votes() {
 		sp_tracing::try_init_simple();
 
-		let validators = ValidatorSet::<AuthorityId>::new(
+		let validators = ValidatorSet::<ecdsa_crypto::AuthorityId>::new(
 			vec![
 				Keyring::Alice.public(),
 				Keyring::Bob.public(),
@@ -298,7 +318,7 @@ mod tests {
 		let validator_set_id = validators.id();
 
 		let session_start = 1u64.into();
-		let mut rounds = Rounds::<Block>::new(session_start, validators);
+		let mut rounds = Rounds::<Block, ecdsa_crypto::AuthorityId>::new(session_start, validators);
 
 		let payload = Payload::from_single_entry(MMR_ROOT_ID, vec![]);
 		let block_number = 1;
@@ -306,7 +326,7 @@ mod tests {
 		let mut vote = VoteMessage {
 			id: Keyring::Alice.public(),
 			commitment: commitment.clone(),
-			signature: Keyring::<AuthorityId>::Alice.sign(b"I am committed"),
+			signature: Keyring::<ecdsa_crypto::AuthorityId>::Alice.sign(b"I am committed"),
 		};
 		// add 1st good vote
 		assert_eq!(rounds.add_vote(vote.clone()), VoteImportResult::Ok);
@@ -315,26 +335,26 @@ mod tests {
 		assert_eq!(rounds.add_vote(vote.clone()), VoteImportResult::Ok);
 
 		vote.id = Keyring::Dave.public();
-		vote.signature = Keyring::<AuthorityId>::Dave.sign(b"I am committed");
+		vote.signature = Keyring::<ecdsa_crypto::AuthorityId>::Dave.sign(b"I am committed");
 		// invalid vote (Dave is not a validator)
 		assert_eq!(rounds.add_vote(vote.clone()), VoteImportResult::Invalid);
 
 		vote.id = Keyring::Bob.public();
-		vote.signature = Keyring::<AuthorityId>::Bob.sign(b"I am committed");
+		vote.signature = Keyring::<ecdsa_crypto::AuthorityId>::Bob.sign(b"I am committed");
 		// add 2nd good vote
 		assert_eq!(rounds.add_vote(vote.clone()), VoteImportResult::Ok);
 
 		vote.id = Keyring::Charlie.public();
-		vote.signature = Keyring::<AuthorityId>::Charlie.sign(b"I am committed");
+		vote.signature = Keyring::<ecdsa_crypto::AuthorityId>::Charlie.sign(b"I am committed");
 		// add 3rd good vote -> round concluded -> signatures present
 		assert_eq!(
 			rounds.add_vote(vote.clone()),
 			VoteImportResult::RoundConcluded(SignedCommitment {
 				commitment,
 				signatures: vec![
-					Some(Keyring::<AuthorityId>::Alice.sign(b"I am committed")),
-					Some(Keyring::<AuthorityId>::Bob.sign(b"I am committed")),
-					Some(Keyring::<AuthorityId>::Charlie.sign(b"I am committed")),
+					Some(Keyring::<ecdsa_crypto::AuthorityId>::Alice.sign(b"I am committed")),
+					Some(Keyring::<ecdsa_crypto::AuthorityId>::Bob.sign(b"I am committed")),
+					Some(Keyring::<ecdsa_crypto::AuthorityId>::Charlie.sign(b"I am committed")),
 					None,
 				]
 			})
@@ -342,7 +362,7 @@ mod tests {
 		rounds.conclude(block_number);
 
 		vote.id = Keyring::Eve.public();
-		vote.signature = Keyring::<AuthorityId>::Eve.sign(b"I am committed");
+		vote.signature = Keyring::<ecdsa_crypto::AuthorityId>::Eve.sign(b"I am committed");
 		// Eve is a validator, but round was concluded, adding vote disallowed
 		assert_eq!(rounds.add_vote(vote), VoteImportResult::Stale);
 	}
@@ -351,7 +371,7 @@ mod tests {
 	fn old_rounds_not_accepted() {
 		sp_tracing::try_init_simple();
 
-		let validators = ValidatorSet::<AuthorityId>::new(
+		let validators = ValidatorSet::<ecdsa_crypto::AuthorityId>::new(
 			vec![Keyring::Alice.public(), Keyring::Bob.public(), Keyring::Charlie.public()],
 			42,
 		)
@@ -360,7 +380,7 @@ mod tests {
 
 		// active rounds starts at block 10
 		let session_start = 10u64.into();
-		let mut rounds = Rounds::<Block>::new(session_start, validators);
+		let mut rounds = Rounds::<Block, ecdsa_crypto::AuthorityId>::new(session_start, validators);
 
 		// vote on round 9
 		let block_number = 9;
@@ -369,7 +389,7 @@ mod tests {
 		let mut vote = VoteMessage {
 			id: Keyring::Alice.public(),
 			commitment,
-			signature: Keyring::<AuthorityId>::Alice.sign(b"I am committed"),
+			signature: Keyring::<ecdsa_crypto::AuthorityId>::Alice.sign(b"I am committed"),
 		};
 		// add vote for previous session, should fail
 		assert_eq!(rounds.add_vote(vote.clone()), VoteImportResult::Stale);
@@ -397,7 +417,7 @@ mod tests {
 	fn multiple_rounds() {
 		sp_tracing::try_init_simple();
 
-		let validators = ValidatorSet::<AuthorityId>::new(
+		let validators = ValidatorSet::<ecdsa_crypto::AuthorityId>::new(
 			vec![Keyring::Alice.public(), Keyring::Bob.public(), Keyring::Charlie.public()],
 			Default::default(),
 		)
@@ -405,29 +425,29 @@ mod tests {
 		let validator_set_id = validators.id();
 
 		let session_start = 1u64.into();
-		let mut rounds = Rounds::<Block>::new(session_start, validators);
+		let mut rounds = Rounds::<Block, ecdsa_crypto::AuthorityId>::new(session_start, validators);
 
 		let payload = Payload::from_single_entry(MMR_ROOT_ID, vec![]);
 		let commitment = Commitment { block_number: 1, payload, validator_set_id };
 		let mut alice_vote = VoteMessage {
 			id: Keyring::Alice.public(),
 			commitment: commitment.clone(),
-			signature: Keyring::<AuthorityId>::Alice.sign(b"I am committed"),
+			signature: Keyring::<ecdsa_crypto::AuthorityId>::Alice.sign(b"I am committed"),
 		};
 		let mut bob_vote = VoteMessage {
 			id: Keyring::Bob.public(),
 			commitment: commitment.clone(),
-			signature: Keyring::<AuthorityId>::Bob.sign(b"I am committed"),
+			signature: Keyring::<ecdsa_crypto::AuthorityId>::Bob.sign(b"I am committed"),
 		};
 		let mut charlie_vote = VoteMessage {
 			id: Keyring::Charlie.public(),
 			commitment,
-			signature: Keyring::<AuthorityId>::Charlie.sign(b"I am committed"),
+			signature: Keyring::<ecdsa_crypto::AuthorityId>::Charlie.sign(b"I am committed"),
 		};
 		let expected_signatures = vec![
-			Some(Keyring::<AuthorityId>::Alice.sign(b"I am committed")),
-			Some(Keyring::<AuthorityId>::Bob.sign(b"I am committed")),
-			Some(Keyring::<AuthorityId>::Charlie.sign(b"I am committed")),
+			Some(Keyring::<ecdsa_crypto::AuthorityId>::Alice.sign(b"I am committed")),
+			Some(Keyring::<ecdsa_crypto::AuthorityId>::Bob.sign(b"I am committed")),
+			Some(Keyring::<ecdsa_crypto::AuthorityId>::Charlie.sign(b"I am committed")),
 		];
 
 		// round 1 - only 2 out of 3 vote
@@ -472,14 +492,14 @@ mod tests {
 	fn should_provide_equivocation_proof() {
 		sp_tracing::try_init_simple();
 
-		let validators = ValidatorSet::<AuthorityId>::new(
+		let validators = ValidatorSet::<ecdsa_crypto::AuthorityId>::new(
 			vec![Keyring::Alice.public(), Keyring::Bob.public()],
 			Default::default(),
 		)
 		.unwrap();
 		let validator_set_id = validators.id();
 		let session_start = 1u64.into();
-		let mut rounds = Rounds::<Block>::new(session_start, validators);
+		let mut rounds = Rounds::<Block, ecdsa_crypto::AuthorityId>::new(session_start, validators);
 
 		let payload1 = Payload::from_single_entry(MMR_ROOT_ID, vec![1, 1, 1, 1]);
 		let payload2 = Payload::from_single_entry(MMR_ROOT_ID, vec![2, 2, 2, 2]);
@@ -489,7 +509,7 @@ mod tests {
 		let alice_vote1 = VoteMessage {
 			id: Keyring::Alice.public(),
 			commitment: commitment1,
-			signature: Keyring::<AuthorityId>::Alice.sign(b"I am committed"),
+			signature: Keyring::<ecdsa_crypto::AuthorityId>::Alice.sign(b"I am committed"),
 		};
 		let mut alice_vote2 = alice_vote1.clone();
 		alice_vote2.commitment = commitment2;
diff --git a/substrate/client/consensus/beefy/src/tests.rs b/substrate/client/consensus/beefy/src/tests.rs
index 2bb145d660df061561efd2196b2193b5ef70b1df..681e11a0c5310f7bfa67aab824e861ef447f3941 100644
--- a/substrate/client/consensus/beefy/src/tests.rs
+++ b/substrate/client/consensus/beefy/src/tests.rs
@@ -55,6 +55,7 @@ use sp_api::{ApiRef, ProvideRuntimeApi};
 use sp_application_crypto::key_types::BEEFY as BEEFY_KEY_TYPE;
 use sp_consensus::BlockOrigin;
 use sp_consensus_beefy::{
+	ecdsa_crypto,
 	ecdsa_crypto::{AuthorityId, Signature},
 	known_payloads,
 	mmr::{find_mmr_root_digest, MmrRootProvider},
@@ -89,6 +90,7 @@ type BeefyBlockImport = crate::BeefyBlockImport<
 	substrate_test_runtime_client::Backend,
 	TestApi,
 	BlockImportAdapter<PeersClient>,
+	AuthorityId,
 >;
 
 pub(crate) type BeefyValidatorSet = ValidatorSet<AuthorityId>;
@@ -107,8 +109,8 @@ impl BuildStorage for Genesis {
 
 #[derive(Default)]
 pub(crate) struct PeerData {
-	pub(crate) beefy_rpc_links: Mutex<Option<BeefyRPCLinks<Block>>>,
-	pub(crate) beefy_voter_links: Mutex<Option<BeefyVoterLinks<Block>>>,
+	pub(crate) beefy_rpc_links: Mutex<Option<BeefyRPCLinks<Block, AuthorityId>>>,
+	pub(crate) beefy_voter_links: Mutex<Option<BeefyVoterLinks<Block, AuthorityId>>>,
 	pub(crate) beefy_justif_req_handler:
 		Mutex<Option<BeefyJustifsRequestHandler<Block, PeersFullClient>>>,
 }
@@ -371,7 +373,7 @@ async fn voter_init_setup(
 	net: &mut BeefyTestNet,
 	finality: &mut futures::stream::Fuse<FinalityNotifications<Block>>,
 	api: &TestApi,
-) -> Result<PersistedState<Block>, Error> {
+) -> Result<PersistedState<Block, ecdsa_crypto::AuthorityId>, Error> {
 	let backend = net.peer(0).client().as_backend();
 	let (beefy_genesis, best_grandpa) = wait_for_runtime_pallet(api, finality).await.unwrap();
 	let key_store = None.into();
@@ -446,7 +448,7 @@ where
 			on_demand_justifications_handler: on_demand_justif_handler,
 			is_authority: true,
 		};
-		let task = crate::start_beefy_gadget::<_, _, _, _, _, _, _>(beefy_params);
+		let task = crate::start_beefy_gadget::<_, _, _, _, _, _, _, _>(beefy_params);
 
 		fn assert_send<T: Send>(_: &T) {}
 		assert_send(&task);
@@ -472,8 +474,10 @@ pub(crate) fn get_beefy_streams(
 	net: &mut BeefyTestNet,
 	// peer index and key
 	peers: impl Iterator<Item = (usize, BeefyKeyring<AuthorityId>)>,
-) -> (Vec<NotificationReceiver<H256>>, Vec<NotificationReceiver<BeefyVersionedFinalityProof<Block>>>)
-{
+) -> (
+	Vec<NotificationReceiver<H256>>,
+	Vec<NotificationReceiver<BeefyVersionedFinalityProof<Block, AuthorityId>>>,
+) {
 	let mut best_block_streams = Vec::new();
 	let mut versioned_finality_proof_streams = Vec::new();
 	peers.for_each(|(index, _)| {
@@ -511,7 +515,7 @@ async fn wait_for_best_beefy_blocks(
 }
 
 async fn wait_for_beefy_signed_commitments(
-	streams: Vec<NotificationReceiver<BeefyVersionedFinalityProof<Block>>>,
+	streams: Vec<NotificationReceiver<BeefyVersionedFinalityProof<Block, AuthorityId>>>,
 	net: &Arc<Mutex<BeefyTestNet>>,
 	expected_commitment_block_nums: &[u64],
 ) {
@@ -1417,7 +1421,7 @@ async fn beefy_reports_equivocations() {
 	for wait_ms in [250, 500, 1250, 3000] {
 		run_for(Duration::from_millis(wait_ms), &net).await;
 		if !api_alice.reported_equivocations.as_ref().unwrap().lock().is_empty() {
-			break
+			break;
 		}
 	}
 
@@ -1457,7 +1461,7 @@ async fn gossipped_finality_proofs() {
 	// Charlie will run just the gossip engine and not the full voter.
 	let gossip_validator = GossipValidator::new(known_peers, Arc::new(TestNetwork::new().0));
 	let charlie_gossip_validator = Arc::new(gossip_validator);
-	charlie_gossip_validator.update_filter(GossipFilterCfg::<Block> {
+	charlie_gossip_validator.update_filter(GossipFilterCfg::<Block, ecdsa_crypto::AuthorityId> {
 		start: 1,
 		end: 10,
 		validator_set: &validator_set,
@@ -1501,7 +1505,7 @@ async fn gossipped_finality_proofs() {
 	let (best_blocks, versioned_finality_proof) = get_beefy_streams(&mut net.lock(), peers.clone());
 	// Charlie gossips finality proof for #1 -> Alice and Bob also finalize.
 	let proof = crate::communication::gossip::tests::dummy_proof(1, &validator_set);
-	let gossip_proof = GossipMessage::<Block>::FinalityProof(proof);
+	let gossip_proof = GossipMessage::<Block, ecdsa_crypto::AuthorityId>::FinalityProof(proof);
 	let encoded_proof = gossip_proof.encode();
 	charlie_gossip_engine.gossip_message(proofs_topic::<Block>(), encoded_proof, true);
 	// Expect #1 is finalized.
@@ -1526,7 +1530,8 @@ async fn gossipped_finality_proofs() {
 	let commitment = Commitment { payload, block_number, validator_set_id: validator_set.id() };
 	let signature = sign_commitment(&BeefyKeyring::Charlie, &commitment);
 	let vote_message = VoteMessage { commitment, id: BeefyKeyring::Charlie.public(), signature };
-	let encoded_vote = GossipMessage::<Block>::Vote(vote_message).encode();
+	let encoded_vote =
+		GossipMessage::<Block, ecdsa_crypto::AuthorityId>::Vote(vote_message).encode();
 	charlie_gossip_engine.gossip_message(votes_topic::<Block>(), encoded_vote, true);
 
 	// Expect #2 is finalized.
@@ -1538,12 +1543,15 @@ async fn gossipped_finality_proofs() {
 		charlie_gossip_engine
 			.messages_for(proofs_topic::<Block>())
 			.filter_map(|notification| async move {
-				GossipMessage::<Block>::decode(&mut &notification.message[..]).ok().and_then(
-					|message| match message {
-						GossipMessage::<Block>::Vote(_) => unreachable!(),
-						GossipMessage::<Block>::FinalityProof(proof) => Some(proof),
-					},
+				GossipMessage::<Block, ecdsa_crypto::AuthorityId>::decode(
+					&mut &notification.message[..],
 				)
+				.ok()
+				.and_then(|message| match message {
+					GossipMessage::<Block, ecdsa_crypto::AuthorityId>::Vote(_) => unreachable!(),
+					GossipMessage::<Block, ecdsa_crypto::AuthorityId>::FinalityProof(proof) =>
+						Some(proof),
+				})
 			})
 			.fuse(),
 	);
@@ -1561,7 +1569,7 @@ async fn gossipped_finality_proofs() {
 			// verify finality proof has been gossipped
 			proof = charlie_gossip_proofs.next() => {
 				let proof = proof.unwrap();
-				let (round, _) = proof_block_num_and_set_id::<Block>(&proof);
+				let (round, _) = proof_block_num_and_set_id::<Block, ecdsa_crypto::AuthorityId>(&proof);
 				match round {
 					1 => continue, // finality proof generated by Charlie in the previous round
 					2 => break,	// finality proof generated by Alice or Bob and gossiped to Charlie
diff --git a/substrate/client/consensus/beefy/src/worker.rs b/substrate/client/consensus/beefy/src/worker.rs
index cfbb3d63aea446d5b6a9a20a0eb9b2cf8c391e79..3ce4da7ecd56adea78b92a905546c4fa619403d7 100644
--- a/substrate/client/consensus/beefy/src/worker.rs
+++ b/substrate/client/consensus/beefy/src/worker.rs
@@ -31,6 +31,8 @@ use crate::{
 	round::{Rounds, VoteImportResult},
 	BeefyComms, BeefyVoterLinks, LOG_TARGET,
 };
+use sp_application_crypto::RuntimeAppPublic;
+
 use codec::{Codec, Decode, DecodeAll, Encode};
 use futures::{stream::Fuse, FutureExt, StreamExt};
 use log::{debug, error, info, trace, warn};
@@ -40,9 +42,8 @@ use sp_api::ProvideRuntimeApi;
 use sp_arithmetic::traits::{AtLeast32Bit, Saturating};
 use sp_consensus::SyncOracle;
 use sp_consensus_beefy::{
-	ecdsa_crypto::{AuthorityId, Signature},
-	BeefyApi, Commitment, DoubleVotingProof, PayloadProvider, ValidatorSet, VersionedFinalityProof,
-	VoteMessage, BEEFY_ENGINE_ID,
+	AuthorityIdBound, BeefyApi, Commitment, DoubleVotingProof, PayloadProvider, ValidatorSet,
+	VersionedFinalityProof, VoteMessage, BEEFY_ENGINE_ID,
 };
 use sp_runtime::{
 	generic::BlockId,
@@ -52,6 +53,7 @@ use sp_runtime::{
 use std::{
 	collections::{BTreeMap, VecDeque},
 	fmt::Debug,
+	marker::PhantomData,
 	sync::Arc,
 };
 
@@ -72,7 +74,7 @@ pub(crate) enum RoundAction {
 /// Note: this is part of `PersistedState` so any changes here should also bump
 /// aux-db schema version.
 #[derive(Debug, Decode, Encode, PartialEq)]
-pub(crate) struct VoterOracle<B: Block> {
+pub(crate) struct VoterOracle<B: Block, AuthorityId: AuthorityIdBound> {
 	/// Queue of known sessions. Keeps track of voting rounds (block numbers) within each session.
 	///
 	/// There are three voter states corresponding to three queue states:
@@ -82,19 +84,23 @@ pub(crate) struct VoterOracle<B: Block> {
 	/// 3. lagging behind GRANDPA: queue has [1, N] elements, where all `mandatory_done == false`.
 	///    In this state, every time a session gets its mandatory block BEEFY finalized, it's
 	///    popped off the queue, eventually getting to state `2. up-to-date`.
-	sessions: VecDeque<Rounds<B>>,
+	sessions: VecDeque<Rounds<B, AuthorityId>>,
 	/// Min delta in block numbers between two blocks, BEEFY should vote on.
 	min_block_delta: u32,
 	/// Best block we received a GRANDPA finality for.
 	best_grandpa_block_header: <B as Block>::Header,
 	/// Best block a BEEFY voting round has been concluded for.
 	best_beefy_block: NumberFor<B>,
+	_phantom: PhantomData<fn() -> AuthorityId>,
 }
 
-impl<B: Block> VoterOracle<B> {
+impl<B: Block, AuthorityId> VoterOracle<B, AuthorityId>
+where
+	AuthorityId: AuthorityIdBound,
+{
 	/// Verify provided `sessions` satisfies requirements, then build `VoterOracle`.
 	pub fn checked_new(
-		sessions: VecDeque<Rounds<B>>,
+		sessions: VecDeque<Rounds<B, AuthorityId>>,
 		min_block_delta: u32,
 		grandpa_header: <B as Block>::Header,
 		best_beefy: NumberFor<B>,
@@ -105,24 +111,24 @@ impl<B: Block> VoterOracle<B> {
 		let mut validate = || -> bool {
 			let best_grandpa = *grandpa_header.number();
 			if sessions.is_empty() || best_beefy > best_grandpa {
-				return false
+				return false;
 			}
 			for (idx, session) in sessions.iter().enumerate() {
 				let start = session.session_start();
 				if session.validators().is_empty() {
-					return false
+					return false;
 				}
 				if start > best_grandpa || start <= prev_start {
-					return false
+					return false;
 				}
 				#[cfg(not(test))]
 				if let Some(prev_id) = prev_validator_id {
 					if session.validator_set_id() <= prev_id {
-						return false
+						return false;
 					}
 				}
 				if idx != 0 && session.mandatory_done() {
-					return false
+					return false;
 				}
 				prev_start = session.session_start();
 				prev_validator_id = Some(session.validator_set_id());
@@ -136,6 +142,7 @@ impl<B: Block> VoterOracle<B> {
 				min_block_delta: min_block_delta.max(1),
 				best_grandpa_block_header: grandpa_header,
 				best_beefy_block: best_beefy,
+				_phantom: PhantomData,
 			})
 		} else {
 			error!(
@@ -151,13 +158,13 @@ impl<B: Block> VoterOracle<B> {
 
 	// Return reference to rounds pertaining to first session in the queue.
 	// Voting will always happen at the head of the queue.
-	fn active_rounds(&self) -> Result<&Rounds<B>, Error> {
+	fn active_rounds(&self) -> Result<&Rounds<B, AuthorityId>, Error> {
 		self.sessions.front().ok_or(Error::UninitSession)
 	}
 
 	// Return mutable reference to rounds pertaining to first session in the queue.
 	// Voting will always happen at the head of the queue.
-	fn active_rounds_mut(&mut self) -> Result<&mut Rounds<B>, Error> {
+	fn active_rounds_mut(&mut self) -> Result<&mut Rounds<B, AuthorityId>, Error> {
 		self.sessions.front_mut().ok_or(Error::UninitSession)
 	}
 
@@ -183,7 +190,7 @@ impl<B: Block> VoterOracle<B> {
 	}
 
 	/// Add new observed session to the Oracle.
-	pub fn add_session(&mut self, rounds: Rounds<B>) {
+	pub fn add_session(&mut self, rounds: Rounds<B, AuthorityId>) {
 		self.sessions.push_back(rounds);
 		// Once we add a new session we can drop/prune previous session if it's been finalized.
 		self.try_prune();
@@ -267,21 +274,21 @@ impl<B: Block> VoterOracle<B> {
 ///
 /// Note: Any changes here should also bump aux-db schema version.
 #[derive(Debug, Decode, Encode, PartialEq)]
-pub(crate) struct PersistedState<B: Block> {
+pub(crate) struct PersistedState<B: Block, AuthorityId: AuthorityIdBound> {
 	/// Best block we voted on.
 	best_voted: NumberFor<B>,
 	/// Chooses which incoming votes to accept and which votes to generate.
 	/// Keeps track of voting seen for current and future rounds.
-	voting_oracle: VoterOracle<B>,
+	voting_oracle: VoterOracle<B, AuthorityId>,
 	/// Pallet-beefy genesis block - block number when BEEFY consensus started for this chain.
 	pallet_genesis: NumberFor<B>,
 }
 
-impl<B: Block> PersistedState<B> {
+impl<B: Block, AuthorityId: AuthorityIdBound> PersistedState<B, AuthorityId> {
 	pub fn checked_new(
 		grandpa_header: <B as Block>::Header,
 		best_beefy: NumberFor<B>,
-		sessions: VecDeque<Rounds<B>>,
+		sessions: VecDeque<Rounds<B, AuthorityId>>,
 		min_block_delta: u32,
 		pallet_genesis: NumberFor<B>,
 	) -> Option<Self> {
@@ -314,11 +321,11 @@ impl<B: Block> PersistedState<B> {
 		self.voting_oracle.best_grandpa_block_header = best_grandpa;
 	}
 
-	pub fn voting_oracle(&self) -> &VoterOracle<B> {
+	pub fn voting_oracle(&self) -> &VoterOracle<B, AuthorityId> {
 		&self.voting_oracle
 	}
 
-	pub(crate) fn gossip_filter_config(&self) -> Result<GossipFilterCfg<B>, Error> {
+	pub(crate) fn gossip_filter_config(&self) -> Result<GossipFilterCfg<B, AuthorityId>, Error> {
 		let (start, end) = self.voting_oracle.accepted_interval()?;
 		let validator_set = self.voting_oracle.current_validator_set()?;
 		Ok(GossipFilterCfg { start, end, validator_set })
@@ -373,34 +380,34 @@ impl<B: Block> PersistedState<B> {
 }
 
 /// A BEEFY worker/voter that follows the BEEFY protocol
-pub(crate) struct BeefyWorker<B: Block, BE, P, RuntimeApi, S, N> {
+pub(crate) struct BeefyWorker<B: Block, BE, P, RuntimeApi, S, N, AuthorityId: AuthorityIdBound> {
 	// utilities
 	pub backend: Arc<BE>,
 	pub runtime: Arc<RuntimeApi>,
 	pub key_store: Arc<BeefyKeystore<AuthorityId>>,
 	pub payload_provider: P,
 	pub sync: Arc<S>,
-	pub fisherman: Arc<Fisherman<B, BE, RuntimeApi>>,
+	pub fisherman: Arc<Fisherman<B, BE, RuntimeApi, AuthorityId>>,
 
 	// communication (created once, but returned and reused if worker is restarted/reinitialized)
-	pub comms: BeefyComms<B, N>,
+	pub comms: BeefyComms<B, N, AuthorityId>,
 
 	// channels
 	/// Links between the block importer, the background voter and the RPC layer.
-	pub links: BeefyVoterLinks<B>,
+	pub links: BeefyVoterLinks<B, AuthorityId>,
 
 	// voter state
 	/// Buffer holding justifications for future processing.
-	pub pending_justifications: BTreeMap<NumberFor<B>, BeefyVersionedFinalityProof<B>>,
+	pub pending_justifications: BTreeMap<NumberFor<B>, BeefyVersionedFinalityProof<B, AuthorityId>>,
 	/// Persisted voter state.
-	pub persisted_state: PersistedState<B>,
+	pub persisted_state: PersistedState<B, AuthorityId>,
 	/// BEEFY voter metrics
 	pub metrics: Option<VoterMetrics>,
 	/// Node runs under "Authority" role.
 	pub is_authority: bool,
 }
 
-impl<B, BE, P, R, S, N> BeefyWorker<B, BE, P, R, S, N>
+impl<B, BE, P, R, S, N, AuthorityId> BeefyWorker<B, BE, P, R, S, N, AuthorityId>
 where
 	B: Block + Codec,
 	BE: Backend<B>,
@@ -408,17 +415,18 @@ where
 	S: SyncOracle,
 	R: ProvideRuntimeApi<B>,
 	R::Api: BeefyApi<B, AuthorityId>,
+	AuthorityId: AuthorityIdBound,
 {
 	fn best_grandpa_block(&self) -> NumberFor<B> {
 		*self.persisted_state.voting_oracle.best_grandpa_block_header.number()
 	}
 
-	fn voting_oracle(&self) -> &VoterOracle<B> {
+	fn voting_oracle(&self) -> &VoterOracle<B, AuthorityId> {
 		&self.persisted_state.voting_oracle
 	}
 
 	#[cfg(test)]
-	fn active_rounds(&mut self) -> Result<&Rounds<B>, Error> {
+	fn active_rounds(&mut self) -> Result<&Rounds<B, AuthorityId>, Error> {
 		self.persisted_state.voting_oracle.active_rounds()
 	}
 
@@ -476,7 +484,8 @@ where
 				})
 				.chain(std::iter::once(header.clone()))
 			{
-				if let Some(new_validator_set) = find_authorities_change::<B>(&header) {
+				if let Some(new_validator_set) = find_authorities_change::<B, AuthorityId>(&header)
+				{
 					self.init_session_at(new_validator_set, *header.number());
 					new_session_added = true;
 				}
@@ -503,13 +512,17 @@ where
 	/// Based on [VoterOracle] this vote is either processed here or discarded.
 	fn triage_incoming_vote(
 		&mut self,
-		vote: VoteMessage<NumberFor<B>, AuthorityId, Signature>,
-	) -> Result<(), Error> {
+		vote: VoteMessage<NumberFor<B>, AuthorityId, <AuthorityId as RuntimeAppPublic>::Signature>,
+	) -> Result<(), Error>
+	where
+		<AuthorityId as RuntimeAppPublic>::Signature: Encode + Decode,
+	{
 		let block_num = vote.commitment.block_number;
 		match self.voting_oracle().triage_round(block_num)? {
 			RoundAction::Process =>
 				if let Some(finality_proof) = self.handle_vote(vote)? {
-					let gossip_proof = GossipMessage::<B>::FinalityProof(finality_proof);
+					let gossip_proof =
+						GossipMessage::<B, AuthorityId>::FinalityProof(finality_proof);
 					let encoded_proof = gossip_proof.encode();
 					self.comms.gossip_engine.gossip_message(
 						proofs_topic::<B>(),
@@ -528,7 +541,7 @@ where
 	/// Expects `justification` to be valid.
 	fn triage_incoming_justif(
 		&mut self,
-		justification: BeefyVersionedFinalityProof<B>,
+		justification: BeefyVersionedFinalityProof<B, AuthorityId>,
 	) -> Result<(), Error> {
 		let signed_commitment = match justification {
 			VersionedFinalityProof::V1(ref sc) => sc,
@@ -560,8 +573,8 @@ where
 
 	fn handle_vote(
 		&mut self,
-		vote: VoteMessage<NumberFor<B>, AuthorityId, Signature>,
-	) -> Result<Option<BeefyVersionedFinalityProof<B>>, Error> {
+		vote: VoteMessage<NumberFor<B>, AuthorityId, <AuthorityId as RuntimeAppPublic>::Signature>,
+	) -> Result<Option<BeefyVersionedFinalityProof<B, AuthorityId>>, Error> {
 		let rounds = self.persisted_state.voting_oracle.active_rounds_mut()?;
 
 		let block_number = vote.commitment.block_number;
@@ -576,7 +589,7 @@ where
 				// New state is persisted after finalization.
 				self.finalize(finality_proof.clone())?;
 				metric_inc!(self.metrics, beefy_good_votes_processed);
-				return Ok(Some(finality_proof))
+				return Ok(Some(finality_proof));
 			},
 			VoteImportResult::Ok => {
 				// Persist state after handling mandatory block vote.
@@ -608,14 +621,17 @@ where
 	/// 4. Send best block hash and `finality_proof` to RPC worker.
 	///
 	/// Expects `finality proof` to be valid and for a block > current-best-beefy.
-	fn finalize(&mut self, finality_proof: BeefyVersionedFinalityProof<B>) -> Result<(), Error> {
+	fn finalize(
+		&mut self,
+		finality_proof: BeefyVersionedFinalityProof<B, AuthorityId>,
+	) -> Result<(), Error> {
 		let block_num = match finality_proof {
 			VersionedFinalityProof::V1(ref sc) => sc.commitment.block_number,
 		};
 
 		if block_num <= self.persisted_state.voting_oracle.best_beefy_block {
 			// we've already finalized this round before, short-circuit.
-			return Ok(())
+			return Ok(());
 		}
 
 		// Finalize inner round and update voting_oracle state.
@@ -740,7 +756,7 @@ where
 			hash
 		} else {
 			warn!(target: LOG_TARGET, "🥩 No MMR root digest found for: {:?}", target_hash);
-			return Ok(())
+			return Ok(());
 		};
 
 		let rounds = self.persisted_state.voting_oracle.active_rounds_mut()?;
@@ -754,7 +770,7 @@ where
 				target: LOG_TARGET,
 				"🥩 Missing validator id - can't vote for: {:?}", target_hash
 			);
-			return Ok(())
+			return Ok(());
 		};
 
 		let commitment = Commitment { payload, block_number: target_number, validator_set_id };
@@ -764,7 +780,7 @@ where
 			Ok(sig) => sig,
 			Err(err) => {
 				warn!(target: LOG_TARGET, "🥩 Error signing commitment: {:?}", err);
-				return Ok(())
+				return Ok(());
 			},
 		};
 
@@ -780,14 +796,15 @@ where
 			error!(target: LOG_TARGET, "🥩 Error handling self vote: {}", err);
 			err
 		})? {
-			let encoded_proof = GossipMessage::<B>::FinalityProof(finality_proof).encode();
+			let encoded_proof =
+				GossipMessage::<B, AuthorityId>::FinalityProof(finality_proof).encode();
 			self.comms
 				.gossip_engine
 				.gossip_message(proofs_topic::<B>(), encoded_proof, true);
 		} else {
 			metric_inc!(self.metrics, beefy_votes_sent);
 			debug!(target: LOG_TARGET, "🥩 Sent vote message: {:?}", vote);
-			let encoded_vote = GossipMessage::<B>::Vote(vote).encode();
+			let encoded_vote = GossipMessage::<B, AuthorityId>::Vote(vote).encode();
 			self.comms.gossip_engine.gossip_message(votes_topic::<B>(), encoded_vote, false);
 		}
 
@@ -825,9 +842,11 @@ where
 	/// Should never end, returns `Error` otherwise.
 	pub(crate) async fn run(
 		mut self,
-		block_import_justif: &mut Fuse<NotificationReceiver<BeefyVersionedFinalityProof<B>>>,
+		block_import_justif: &mut Fuse<
+			NotificationReceiver<BeefyVersionedFinalityProof<B, AuthorityId>>,
+		>,
 		finality_notifications: &mut Fuse<FinalityNotifications<B>>,
-	) -> (Error, BeefyComms<B, N>) {
+	) -> (Error, BeefyComms<B, N, AuthorityId>) {
 		info!(
 			target: LOG_TARGET,
 			"🥩 run BEEFY worker, best grandpa: #{:?}.",
@@ -839,9 +858,10 @@ where
 				.gossip_engine
 				.messages_for(votes_topic::<B>())
 				.filter_map(|notification| async move {
-					let vote = GossipMessage::<B>::decode_all(&mut &notification.message[..])
-						.ok()
-						.and_then(|message| message.unwrap_vote());
+					let vote =
+						GossipMessage::<B, AuthorityId>::decode_all(&mut &notification.message[..])
+							.ok()
+							.and_then(|message| message.unwrap_vote());
 					trace!(target: LOG_TARGET, "🥩 Got vote message: {:?}", vote);
 					vote
 				})
@@ -852,9 +872,10 @@ where
 				.gossip_engine
 				.messages_for(proofs_topic::<B>())
 				.filter_map(|notification| async move {
-					let proof = GossipMessage::<B>::decode_all(&mut &notification.message[..])
-						.ok()
-						.and_then(|message| message.unwrap_finality_proof());
+					let proof =
+						GossipMessage::<B, AuthorityId>::decode_all(&mut &notification.message[..])
+							.ok()
+							.and_then(|message| message.unwrap_finality_proof());
 					trace!(target: LOG_TARGET, "🥩 Got gossip proof message: {:?}", proof);
 					proof
 				})
@@ -945,7 +966,11 @@ where
 	/// Report the given equivocation to the BEEFY runtime module.
 	fn report_double_voting(
 		&self,
-		proof: DoubleVotingProof<NumberFor<B>, AuthorityId, Signature>,
+		proof: DoubleVotingProof<
+			NumberFor<B>,
+			AuthorityId,
+			<AuthorityId as RuntimeAppPublic>::Signature,
+		>,
 	) -> Result<(), Error> {
 		let rounds = self.persisted_state.voting_oracle.active_rounds()?;
 		self.fisherman.report_double_voting(proof, rounds)
@@ -1011,7 +1036,7 @@ pub(crate) mod tests {
 	use sc_network_test::TestNetFactory;
 	use sp_blockchain::Backend as BlockchainBackendT;
 	use sp_consensus_beefy::{
-		known_payloads,
+		ecdsa_crypto, known_payloads,
 		known_payloads::MMR_ROOT_ID,
 		mmr::MmrRootProvider,
 		test_utils::{generate_equivocation_proof, Keyring},
@@ -1023,8 +1048,8 @@ pub(crate) mod tests {
 		Backend,
 	};
 
-	impl<B: super::Block> PersistedState<B> {
-		pub fn active_round(&self) -> Result<&Rounds<B>, Error> {
+	impl<B: super::Block, AuthorityId: AuthorityIdBound> PersistedState<B, AuthorityId> {
+		pub fn active_round(&self) -> Result<&Rounds<B, AuthorityId>, Error> {
 			self.voting_oracle.active_rounds()
 		}
 
@@ -1033,17 +1058,17 @@ pub(crate) mod tests {
 		}
 	}
 
-	impl<B: super::Block> VoterOracle<B> {
-		pub fn sessions(&self) -> &VecDeque<Rounds<B>> {
+	impl<B: super::Block> VoterOracle<B, ecdsa_crypto::AuthorityId> {
+		pub fn sessions(&self) -> &VecDeque<Rounds<B, ecdsa_crypto::AuthorityId>> {
 			&self.sessions
 		}
 	}
 
 	fn create_beefy_worker(
 		peer: &mut BeefyPeer,
-		key: &Keyring<AuthorityId>,
+		key: &Keyring<ecdsa_crypto::AuthorityId>,
 		min_block_delta: u32,
-		genesis_validator_set: ValidatorSet<AuthorityId>,
+		genesis_validator_set: ValidatorSet<ecdsa_crypto::AuthorityId>,
 	) -> BeefyWorker<
 		Block,
 		Backend,
@@ -1051,15 +1076,16 @@ pub(crate) mod tests {
 		TestApi,
 		Arc<SyncingService<Block>>,
 		TestNetwork,
+		ecdsa_crypto::AuthorityId,
 	> {
 		let keystore = create_beefy_keystore(key);
 
 		let (to_rpc_justif_sender, from_voter_justif_stream) =
-			BeefyVersionedFinalityProofStream::<Block>::channel();
+			BeefyVersionedFinalityProofStream::<Block, ecdsa_crypto::AuthorityId>::channel();
 		let (to_rpc_best_block_sender, from_voter_best_beefy_stream) =
 			BeefyBestBlockStream::<Block>::channel();
 		let (_, from_block_import_justif_stream) =
-			BeefyVersionedFinalityProofStream::<Block>::channel();
+			BeefyVersionedFinalityProofStream::<Block, ecdsa_crypto::AuthorityId>::channel();
 
 		let beefy_rpc_links =
 			BeefyRPCLinks { from_voter_justif_stream, from_voter_best_beefy_stream };
@@ -1115,7 +1141,8 @@ pub(crate) mod tests {
 		.unwrap();
 		let payload_provider = MmrRootProvider::new(api.clone());
 		let comms = BeefyComms { gossip_engine, gossip_validator, on_demand_justifications };
-		let key_store: Arc<BeefyKeystore<AuthorityId>> = Arc::new(Some(keystore).into());
+		let key_store: Arc<BeefyKeystore<ecdsa_crypto::AuthorityId>> =
+			Arc::new(Some(keystore).into());
 		BeefyWorker {
 			backend: backend.clone(),
 			runtime: api.clone(),
@@ -1233,13 +1260,14 @@ pub(crate) mod tests {
 			Default::default(),
 			Digest::default(),
 		);
-		let mut oracle = VoterOracle::<Block> {
+		let mut oracle = VoterOracle::<Block, ecdsa_crypto::AuthorityId> {
 			best_beefy_block: 0,
 			best_grandpa_block_header: header,
 			min_block_delta: 1,
 			sessions: VecDeque::new(),
+			_phantom: PhantomData,
 		};
-		let voting_target_with = |oracle: &mut VoterOracle<Block>,
+		let voting_target_with = |oracle: &mut VoterOracle<Block, ecdsa_crypto::AuthorityId>,
 		                          best_beefy: NumberFor<Block>,
 		                          best_grandpa: NumberFor<Block>|
 		 -> Option<NumberFor<Block>> {
@@ -1295,18 +1323,20 @@ pub(crate) mod tests {
 			Default::default(),
 			Digest::default(),
 		);
-		let mut oracle = VoterOracle::<Block> {
+		let mut oracle = VoterOracle::<Block, ecdsa_crypto::AuthorityId> {
 			best_beefy_block: 0,
 			best_grandpa_block_header: header,
 			min_block_delta: 1,
 			sessions: VecDeque::new(),
+			_phantom: PhantomData,
 		};
-		let accepted_interval_with = |oracle: &mut VoterOracle<Block>,
-		                              best_grandpa: NumberFor<Block>|
-		 -> Result<(NumberFor<Block>, NumberFor<Block>), Error> {
-			oracle.best_grandpa_block_header.number = best_grandpa;
-			oracle.accepted_interval()
-		};
+		let accepted_interval_with =
+			|oracle: &mut VoterOracle<Block, ecdsa_crypto::AuthorityId>,
+			 best_grandpa: NumberFor<Block>|
+			 -> Result<(NumberFor<Block>, NumberFor<Block>), Error> {
+				oracle.best_grandpa_block_header.number = best_grandpa;
+				oracle.accepted_interval()
+			};
 
 		// rounds not initialized -> should accept votes: `None`
 		assert!(accepted_interval_with(&mut oracle, 1).is_err());
@@ -1377,18 +1407,19 @@ pub(crate) mod tests {
 		);
 
 		// verify empty digest shows nothing
-		assert!(find_authorities_change::<Block>(&header).is_none());
+		assert!(find_authorities_change::<Block, ecdsa_crypto::AuthorityId>(&header).is_none());
 
 		let peers = &[Keyring::One, Keyring::Two];
 		let id = 42;
 		let validator_set = ValidatorSet::new(make_beefy_ids(peers), id).unwrap();
 		header.digest_mut().push(DigestItem::Consensus(
 			BEEFY_ENGINE_ID,
-			ConsensusLog::<AuthorityId>::AuthoritiesChange(validator_set.clone()).encode(),
+			ConsensusLog::<ecdsa_crypto::AuthorityId>::AuthoritiesChange(validator_set.clone())
+				.encode(),
 		));
 
 		// verify validator set is correctly extracted from digest
-		let extracted = find_authorities_change::<Block>(&header);
+		let extracted = find_authorities_change::<Block, ecdsa_crypto::AuthorityId>(&header);
 		assert_eq!(extracted, Some(validator_set));
 	}
 
diff --git a/substrate/primitives/consensus/beefy/src/lib.rs b/substrate/primitives/consensus/beefy/src/lib.rs
index f70434beab33c5942d2548d89ad4c889f04ce0f5..913184402aef7bf9d1ee906faea935262e177a93 100644
--- a/substrate/primitives/consensus/beefy/src/lib.rs
+++ b/substrate/primitives/consensus/beefy/src/lib.rs
@@ -50,7 +50,7 @@ use alloc::vec::Vec;
 use codec::{Codec, Decode, Encode};
 use core::fmt::{Debug, Display};
 use scale_info::TypeInfo;
-use sp_application_crypto::{AppCrypto, AppPublic, ByteArray, RuntimeAppPublic};
+use sp_application_crypto::{AppPublic, RuntimeAppPublic};
 use sp_core::H256;
 use sp_runtime::{
 	traits::{Hash, Keccak256, NumberFor},
@@ -76,17 +76,13 @@ pub type BeefySignatureHasher = sp_runtime::traits::Keccak256;
 /// A trait bound which lists all traits which are required to be implemented by
 /// a BEEFY AuthorityId type in order to be able to be used in BEEFY Keystore
 pub trait AuthorityIdBound:
-	Codec
-	+ Debug
-	+ Clone
-	+ AsRef<[u8]>
-	+ ByteArray
+	Ord
 	+ AppPublic
-	+ AppCrypto
-	+ RuntimeAppPublic
 	+ Display
-	+ BeefyAuthorityId<BeefySignatureHasher>
+	+ BeefyAuthorityId<BeefySignatureHasher, Signature = Self::BoundedSignature>
 {
+	/// Necessary bounds on the Signature associated with the AuthorityId
+	type BoundedSignature: Debug + Eq + PartialEq + Clone + TypeInfo + Codec + Send + Sync;
 }
 
 /// BEEFY cryptographic types for ECDSA crypto
@@ -127,7 +123,9 @@ pub mod ecdsa_crypto {
 			}
 		}
 	}
-	impl AuthorityIdBound for AuthorityId {}
+	impl AuthorityIdBound for AuthorityId {
+		type BoundedSignature = Signature;
+	}
 }
 
 /// BEEFY cryptographic types for BLS crypto
@@ -168,7 +166,9 @@ pub mod bls_crypto {
 			BlsPair::verify(signature.as_inner_ref(), msg, self.as_inner_ref())
 		}
 	}
-	impl AuthorityIdBound for AuthorityId {}
+	impl AuthorityIdBound for AuthorityId {
+		type BoundedSignature = Signature;
+	}
 }
 
 /// BEEFY cryptographic types for (ECDSA,BLS) crypto pair
@@ -216,7 +216,9 @@ pub mod ecdsa_bls_crypto {
 		}
 	}
 
-	impl AuthorityIdBound for AuthorityId {}
+	impl AuthorityIdBound for AuthorityId {
+		type BoundedSignature = Signature;
+	}
 }
 
 /// The `ConsensusEngineId` of BEEFY.
diff --git a/substrate/primitives/keystore/src/testing.rs b/substrate/primitives/keystore/src/testing.rs
index d8610ecfa5b60d8891b6690e00ce6efb55342983..1403e4745ff11120606295ef3c69cbb116843e8b 100644
--- a/substrate/primitives/keystore/src/testing.rs
+++ b/substrate/primitives/keystore/src/testing.rs
@@ -516,7 +516,7 @@ mod tests {
 		let suri = "//Alice";
 		let pair = ecdsa_bls377::Pair::from_string(suri, None).unwrap();
 
-		let msg = b"this should be a normal unhashed message not ";
+		let msg = b"this should be a normal unhashed message not a hash of a message because bls scheme comes with its own hashing";
 
 		// insert key, sign again
 		store.insert(ECDSA_BLS377, suri, pair.public().as_ref()).unwrap();