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 ¬ification.message[..]).ok().and_then( - |message| match message { - GossipMessage::<Block>::Vote(_) => unreachable!(), - GossipMessage::<Block>::FinalityProof(proof) => Some(proof), - }, + GossipMessage::<Block, ecdsa_crypto::AuthorityId>::decode( + &mut ¬ification.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 ¬ification.message[..]) - .ok() - .and_then(|message| message.unwrap_vote()); + let vote = + GossipMessage::<B, AuthorityId>::decode_all(&mut ¬ification.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 ¬ification.message[..]) - .ok() - .and_then(|message| message.unwrap_finality_proof()); + let proof = + GossipMessage::<B, AuthorityId>::decode_all(&mut ¬ification.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();