diff --git a/Cargo.lock b/Cargo.lock index c971ebcba9c6..7e31150836d4 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", @@ -13703,10 +13705,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", @@ -17035,6 +17039,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 7c9b9e05d62c..9ee81f80d66a 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; type FullGrandpaBlockImport = grandpa::GrandpaBlockImport; #[cfg(feature = "full-node")] -type FullBeefyBlockImport = - beefy::import::BeefyBlockImport; +type FullBeefyBlockImport = + beefy::import::BeefyBlockImport; #[cfg(feature = "full-node")] struct Basics { @@ -486,11 +487,14 @@ fn new_partial( babe::BabeBlockImport< Block, FullClient, - FullBeefyBlockImport>, + FullBeefyBlockImport< + FullGrandpaBlockImport, + ecdsa_crypto::AuthorityId, + >, >, grandpa::LinkHalf, babe::BabeLink, - beefy::BeefyVoterLinks, + beefy::BeefyVoterLinks, ), 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:: { 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 5af5e63b1753..1900b595d671 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 4455efd3b533..2daa246102fc 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 { } /// Dependencies for BEEFY -pub struct BeefyDeps { +pub struct BeefyDeps { /// Receives notifications about finality proof events from BEEFY. - pub beefy_finality_proof_stream: BeefyVersionedFinalityProofStream, + pub beefy_finality_proof_stream: BeefyVersionedFinalityProofStream, /// Receives notifications about best block events from BEEFY. pub beefy_best_block_stream: BeefyBestBlockStream, /// Executor to drive the subscription manager in the BEEFY RPC handler. @@ -72,7 +74,7 @@ pub struct BeefyDeps { } /// Full client dependencies -pub struct FullDeps { +pub struct FullDeps { /// The client instance to use. pub client: Arc, /// Transaction pool instance. @@ -88,14 +90,14 @@ pub struct FullDeps { /// GRANDPA specific dependencies. pub grandpa: GrandpaDeps, /// BEEFY specific dependencies. - pub beefy: BeefyDeps, + pub beefy: BeefyDeps, /// Backend used by the node. pub backend: Arc, } /// Instantiate all RPC extensions. -pub fn create_full( - FullDeps { client, pool, select_chain, chain_spec, deny_unsafe, babe, grandpa, beefy, backend } : FullDeps, +pub fn create_full( + FullDeps { client, pool, select_chain, chain_spec, deny_unsafe, babe, grandpa, beefy, backend } : FullDeps, ) -> Result> where C: ProvideRuntimeApi @@ -114,6 +116,8 @@ where SC: SelectChain + 'static, B: sc_client_api::Backend + Send + Sync + 'static, B::State: sc_client_api::StateBackend>, + AuthorityId: AuthorityIdBound, + ::Signature: Send + Sync, { use frame_rpc_system::{System, SystemApiServer}; use mmr_rpc::{Mmr, MmrApiServer}; @@ -171,7 +175,7 @@ where )?; io.merge( - Beefy::::new( + Beefy::::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 84903bd9b872..e57ca04f3b74 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; type FullSelectChain = sc_consensus::LongestChain; type FullGrandpaBlockImport = grandpa::GrandpaBlockImport; -type FullBeefyBlockImport = - beefy::import::BeefyBlockImport; +type FullBeefyBlockImport = beefy::import::BeefyBlockImport< + Block, + FullBackend, + FullClient, + InnerBlockImport, + beefy_primitives::ecdsa_crypto::AuthorityId, +>; /// The transaction pool type definition. pub type TransactionPool = sc_transaction_pool::FullPool; @@ -180,7 +188,7 @@ pub fn new_partial( >, grandpa::LinkHalf, sc_consensus_babe::BabeLink, - beefy::BeefyVoterLinks, + beefy::BeefyVoterLinks, ), grandpa::SharedVoterState, Option, @@ -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_finality_proof_stream: beefy_rpc_links .from_voter_justif_stream .clone(), @@ -683,7 +691,7 @@ pub fn new_full_base::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 894dbf0da85c..6ae80eb57859 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 4646524a25ba..52cd7f9561d2 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 { } /// Dependencies for BEEFY -pub struct BeefyDeps { +pub struct BeefyDeps { /// Receives notifications about finality proof events from BEEFY. - pub beefy_finality_proof_stream: BeefyVersionedFinalityProofStream, + pub beefy_finality_proof_stream: BeefyVersionedFinalityProofStream, /// Receives notifications about best block events from BEEFY. pub beefy_best_block_stream: BeefyBestBlockStream, /// Executor to drive the subscription manager in the BEEFY RPC handler. @@ -86,7 +88,7 @@ pub struct BeefyDeps { } /// Full client dependencies. -pub struct FullDeps { +pub struct FullDeps { /// The client instance to use. pub client: Arc, /// Transaction pool instance. @@ -102,7 +104,7 @@ pub struct FullDeps { /// GRANDPA specific dependencies. pub grandpa: GrandpaDeps, /// BEEFY specific dependencies. - pub beefy: BeefyDeps, + pub beefy: BeefyDeps, /// Shared statement store reference. pub statement_store: Arc, /// The backend used by the node. @@ -112,7 +114,7 @@ pub struct FullDeps { } /// Instantiate all Full RPC extensions. -pub fn create_full( +pub fn create_full( FullDeps { client, pool, @@ -125,7 +127,7 @@ pub fn create_full( statement_store, backend, mixnet_api, - }: FullDeps, + }: FullDeps, ) -> Result, Box> where C: ProvideRuntimeApi @@ -145,6 +147,8 @@ where SC: SelectChain + 'static, B: sc_client_api::Backend + Send + Sync + 'static, B::State: sc_client_api::backend::StateBackend>, + AuthorityId: AuthorityIdBound, + ::Signature: Send + Sync, { use mmr_rpc::{Mmr, MmrApiServer}; use pallet_transaction_payment_rpc::{TransactionPayment, TransactionPaymentApiServer}; @@ -223,7 +227,7 @@ where } io.merge( - Beefy::::new( + Beefy::::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 193acbe52a1e..cd183f6bc8b0 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 07e46dbda156..84f90622b5c1 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 f01baee2d6ec..66102eeb35c8 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 { } /// Implements the BeefyApi RPC trait for interacting with BEEFY. -pub struct Beefy { - finality_proof_stream: BeefyVersionedFinalityProofStream, +pub struct Beefy { + finality_proof_stream: BeefyVersionedFinalityProofStream, beefy_best_block: Arc>>, executor: SubscriptionTaskExecutor, } -impl Beefy +impl Beefy where Block: BlockT, + AuthorityId: AuthorityIdBound, { /// Creates a new Beefy Rpc handler instance. pub fn new( - finality_proof_stream: BeefyVersionedFinalityProofStream, + finality_proof_stream: BeefyVersionedFinalityProofStream, best_block_stream: BeefyBestBlockStream, executor: SubscriptionTaskExecutor, ) -> Result { @@ -129,16 +132,18 @@ where } #[async_trait] -impl BeefyApiServer - for Beefy +impl BeefyApiServer + for Beefy where Block: BlockT, + AuthorityId: AuthorityIdBound, + ::Signature: Send + Sync, { fn subscribe_justifications(&self, pending: PendingSubscriptionSink) { let stream = self .finality_proof_stream .subscribe(100_000) - .map(|vfp| notification::EncodedVersionedFinalityProof::new::(vfp)); + .map(|vfp| notification::EncodedVersionedFinalityProof::new::(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>, BeefyVersionedFinalityProofSender) { + fn setup_io_handler() -> ( + RpcModule>, + BeefyVersionedFinalityProofSender, + ) { let (_, stream) = BeefyBestBlockStream::::channel(); setup_io_handler_with_best_block_stream(stream) } fn setup_io_handler_with_best_block_stream( best_block_stream: BeefyBestBlockStream, - ) -> (RpcModule>, BeefyVersionedFinalityProofSender) { + ) -> ( + RpcModule>, + BeefyVersionedFinalityProofSender, + ) { let (finality_proof_sender, finality_proof_stream) = - BeefyVersionedFinalityProofStream::::channel(); + BeefyVersionedFinalityProofStream::::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 { + fn create_finality_proof() -> BeefyVersionedFinalityProof { let payload = Payload::from_single_entry(known_payloads::MMR_ROOT_ID, "Hello World!".encode()); - BeefyVersionedFinalityProof::::V1(SignedCommitment { + BeefyVersionedFinalityProof::::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::().await.unwrap().unwrap(); - let recv_finality_proof: BeefyVersionedFinalityProof = + let recv_finality_proof: BeefyVersionedFinalityProof = 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 690c511b999a..d4339058a694 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( - finality_proof: sc_consensus_beefy::justification::BeefyVersionedFinalityProof, + pub fn new( + 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 534f668ae69c..1922494ad112 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(backend: &BE) -> Result<(), Er } /// Write voter state. -pub(crate) fn write_voter_state( +pub(crate) fn write_voter_state( backend: &BE, - state: &PersistedState, -) -> Result<(), Error> { + state: &PersistedState, +) -> 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(backend: &BE, key: &[u8]) -> Result, Error> { - match backend.get_aux(key).map_err(|e| Error::Backend(e.to_string()))? { +fn load_decode(backend: &BE, key: &[u8]) -> ClientResult> { + 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(backend: &BE) -> Result>, Error> +pub(crate) fn load_persistent( + backend: &BE, +) -> ClientResult>> where B: BlockT, BE: Backend, @@ -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>(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>(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>(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 947fe13856f4..95cac250b7c5 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 { +pub(crate) enum GossipMessage { /// BEEFY message with commitment and single signature. - Vote(VoteMessage, AuthorityId, Signature>), + Vote(VoteMessage, AuthorityId, ::Signature>), /// BEEFY justification with commitment and signatures. - FinalityProof(BeefyVersionedFinalityProof), + FinalityProof(BeefyVersionedFinalityProof), } -impl GossipMessage { +impl GossipMessage { /// Return inner vote if this message is a Vote. - pub fn unwrap_vote(self) -> Option, AuthorityId, Signature>> { + pub fn unwrap_vote( + self, + ) -> Option, AuthorityId, ::Signature>> + { match self { GossipMessage::Vote(vote) => Some(vote), GossipMessage::FinalityProof(_) => None, @@ -89,7 +90,7 @@ impl GossipMessage { } /// Return inner finality proof if this message is a FinalityProof. - pub fn unwrap_finality_proof(self) -> Option> { + pub fn unwrap_finality_proof(self) -> Option> { 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, pub end: NumberFor, pub validator_set: &'a ValidatorSet, } #[derive(Clone, Debug)] -struct FilterInner { +struct FilterInner { pub start: NumberFor, pub end: NumberFor, pub validator_set: ValidatorSet, } -struct Filter { +struct Filter { // specifies live rounds - inner: Option>, + inner: Option>, // cache of seen valid justifications in active rounds rounds_with_valid_proofs: BTreeSet>, } -impl Filter { +impl Filter { 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) { + fn update(&mut self, cfg: GossipFilterCfg) { 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 Filter { /// rejected/expired. /// ///All messaging is handled in a single BEEFY global topic. -pub(crate) struct GossipValidator +pub(crate) struct GossipValidator where B: Block, { votes_topic: B::Hash, justifs_topic: B::Hash, - gossip_filter: RwLock>, + gossip_filter: RwLock>, next_rebroadcast: Mutex, known_peers: Arc>>, network: Arc, } -impl GossipValidator +impl GossipValidator where B: Block, + AuthorityId: AuthorityIdBound, { pub(crate) fn new(known_peers: Arc>>, network: Arc) -> 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) { + pub(crate) fn update_filter(&self, filter: GossipFilterCfg) { debug!( target: LOG_TARGET, "🥩 New gossip filter: start {:?}, end {:?}, validator set id {:?}", @@ -260,10 +262,11 @@ where } } -impl GossipValidator +impl GossipValidator 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, AuthorityId, Signature>, + vote: VoteMessage, AuthorityId, ::Signature>, sender: &PeerId, ) -> Action { 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, + proof: BeefyVersionedFinalityProof, sender: &PeerId, ) -> Action { - let (round, set_id) = proof_block_num_and_set_id::(&proof); + let (round, set_id) = proof_block_num_and_set_id::(&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::(round, validator_set, &proof) + verify_with_validator_set::(round, validator_set, &proof) { debug!( target: LOG_TARGET, @@ -369,9 +372,10 @@ where } } -impl Validator for GossipValidator +impl Validator for GossipValidator where B: Block, + AuthorityId: AuthorityIdBound, N: NetworkPeers + Send + Sync, { fn peer_disconnected(&self, _context: &mut dyn ValidatorContext, who: &PeerId) { @@ -385,7 +389,7 @@ where mut data: &[u8], ) -> ValidationResult { let raw = data; - let action = match GossipMessage::::decode_all(&mut data) { + let action = match GossipMessage::::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 bool + 'a> { let filter = self.gossip_filter.read(); - Box::new(move |_topic, mut data| match GossipMessage::::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::(&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::::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::(&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::::decode_all(&mut data) { + match GossipMessage::::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::(&proof); + let (round, set_id) = proof_block_num_and_set_id::(&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( - who: &Keyring, + who: &Keyring, commitment: &Commitment, - ) -> Signature { + ) -> ecdsa_crypto::Signature { let store = MemoryKeystore::new(); store.ecdsa_generate_new(BEEFY_KEY_TYPE, Some(&who.to_seed())).unwrap(); - let beefy_keystore: BeefyKeystore = Some(store.into()).into(); + let beefy_keystore: BeefyKeystore = Some(store.into()).into(); beefy_keystore.sign(&who.public(), &commitment.encode()).unwrap() } - fn dummy_vote(block_number: u64) -> VoteMessage { + fn dummy_vote( + block_number: u64, + ) -> VoteMessage { 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, - ) -> BeefyVersionedFinalityProof { + validator_set: &ValidatorSet, + ) -> BeefyVersionedFinalityProof { 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::::from_public(validator).unwrap(), + &Keyring::::from_public(validator).unwrap(), &commitment, )) }) .collect(); - BeefyVersionedFinalityProof::::V1(SignedCommitment { commitment, signatures }) + BeefyVersionedFinalityProof::::V1(SignedCommitment { + commitment, + signatures, + }) } #[test] fn should_validate_messages() { - let keys = vec![Keyring::::Alice.public()]; - let validator_set = ValidatorSet::::new(keys.clone(), 0).unwrap(); + let keys = vec![Keyring::::Alice.public()]; + let validator_set = + ValidatorSet::::new(keys.clone(), 0).unwrap(); let (network, mut report_stream) = TestNetwork::new(); - let gv = GossipValidator::::new( + let gv = GossipValidator::::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::::Vote(vote.clone()).encode(); + let encoded = + GossipMessage::::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::::Vote(bad_vote).encode(); + let bad_vote = GossipMessage::::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::::FinalityProof(proof).encode(); + let encoded_proof = + GossipMessage::::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::::FinalityProof(proof).encode(); + let encoded_proof = + GossipMessage::::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::::FinalityProof(proof).encode(); + let encoded_proof = + GossipMessage::::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::::new(keys, 1).unwrap(); + let bad_validator_set = ValidatorSet::::new(keys, 1).unwrap(); let proof = dummy_proof(20, &bad_validator_set); - let encoded_proof = GossipMessage::::FinalityProof(proof).encode(); + let encoded_proof = + GossipMessage::::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::::new(vec![Keyring::Bob.public()], 0).unwrap(); + ValidatorSet::::new(vec![Keyring::Bob.public()], 0).unwrap(); let proof = dummy_proof(21, &bad_validator_set); - let encoded_proof = GossipMessage::::FinalityProof(proof).encode(); + let encoded_proof = + GossipMessage::::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::::new(keys.clone(), 0).unwrap(); - let gv = GossipValidator::::new( + let validator_set = + ValidatorSet::::new(keys.clone(), 0).unwrap(); + let gv = GossipValidator::::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::::Vote(vote).encode(); + let mut encoded_vote = + GossipMessage::::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::::FinalityProof(proof).encode(); + let mut encoded_proof = + GossipMessage::::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::::Vote(vote).encode(); + let mut encoded_vote = + GossipMessage::::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::::FinalityProof(proof).encode(); + let mut encoded_proof = + GossipMessage::::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::::new(keys.clone(), 1).unwrap(); + let bad_validator_set = + ValidatorSet::::new(keys.clone(), 1).unwrap(); let proof = dummy_proof(2, &bad_validator_set); - let mut encoded_proof = GossipMessage::::FinalityProof(proof).encode(); + let mut encoded_proof = + GossipMessage::::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::::Vote(vote).encode(); + let mut encoded_vote = + GossipMessage::::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::::FinalityProof(proof).encode(); + let mut encoded_proof = + GossipMessage::::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::::Vote(vote).encode(); + let mut encoded_vote = + GossipMessage::::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::::FinalityProof(proof).encode(); + let mut encoded_proof = + GossipMessage::::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::::Vote(vote).encode(); + let mut encoded_vote = + GossipMessage::::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::::FinalityProof(proof).encode(); + let mut encoded_proof = + GossipMessage::::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::::new(keys.clone(), 0).unwrap(); - let gv = GossipValidator::::new( + let validator_set = + ValidatorSet::::new(keys.clone(), 0).unwrap(); + let gv = GossipValidator::::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 a4486e523c30..8bb5d848b4fa 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 = /// 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 = - NotificationSender>; +pub type BeefyVersionedFinalityProofSender = + NotificationSender>; /// 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 = - NotificationStream, BeefyVersionedFinalityProofTracingKey>; +pub type BeefyVersionedFinalityProofStream = NotificationStream< + BeefyVersionedFinalityProof, + 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 7893066a01e0..350e7a271bc3 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 IncomingRequest { 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 2ab072960900..4d40656375ec 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, ProtocolName), RequestFailure>; type ResponseReceiver = oneshot::Receiver; #[derive(Clone, Debug)] -struct RequestInfo { +struct RequestInfo { block: NumberFor, active_set: ValidatorSet, } -enum State { +enum State { Idle, - AwaitingResponse(PeerId, RequestInfo, ResponseReceiver), + AwaitingResponse(PeerId, RequestInfo, ResponseReceiver), } /// Possible engine responses. -pub(crate) enum ResponseInfo { +pub(crate) enum ResponseInfo { /// No peer response available yet. Pending, /// Valid justification provided alongside peer reputation changes. - ValidProof(BeefyVersionedFinalityProof, PeerReport), + ValidProof(BeefyVersionedFinalityProof, PeerReport), /// No justification yet, only peer reputation changes. PeerReport(PeerReport), } -pub struct OnDemandJustificationsEngine { +pub struct OnDemandJustificationsEngine { network: Arc, protocol_name: ProtocolName, live_peers: Arc>>, peers_cache: VecDeque, - state: State, + state: State, metrics: Option, } -impl OnDemandJustificationsEngine { +impl OnDemandJustificationsEngine { pub fn new( network: Arc, protocol_name: ProtocolName, @@ -106,13 +106,13 @@ impl OnDemandJustificationsEngine { 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) { + fn request_from_peer(&mut self, peer: PeerId, req_info: RequestInfo) { debug!( target: BEEFY_SYNC_LOG_TARGET, "🥩 requesting justif #{:?} from peer {:?}", req_info.block, peer, @@ -140,7 +140,7 @@ impl OnDemandJustificationsEngine { pub fn request(&mut self, block: NumberFor, active_set: ValidatorSet) { // 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 OnDemandJustificationsEngine { fn process_response( &mut self, peer: &PeerId, - req_info: &RequestInfo, + req_info: &RequestInfo, response: Result, - ) -> Result, Error> { + ) -> Result, Error> { response .map_err(|e| { debug!( @@ -207,7 +207,7 @@ impl OnDemandJustificationsEngine { } }) .and_then(|(encoded, _)| { - decode_and_verify_finality_proof::( + decode_and_verify_finality_proof::( &encoded[..], req_info.block, &req_info.active_set, @@ -227,11 +227,11 @@ impl OnDemandJustificationsEngine { }) } - pub(crate) async fn next(&mut self) -> ResponseInfo { + pub(crate) async fn next(&mut self) -> ResponseInfo { 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 b4773f940193..9cd09cb99332 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 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 a2b4c8f945d1..073fee0bdbdb 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 { +pub struct Fisherman { backend: Arc, runtime: Arc, key_store: Arc>, @@ -47,9 +47,11 @@ pub struct Fisherman { _phantom: PhantomData, } -impl, RuntimeApi: ProvideRuntimeApi> Fisherman +impl, RuntimeApi: ProvideRuntimeApi, AuthorityId> + Fisherman where RuntimeApi::Api: BeefyApi, + AuthorityId: AuthorityIdBound, { pub fn new( backend: Arc, @@ -64,7 +66,7 @@ where at: BlockId, offender_ids: impl Iterator, validator_set_id: ValidatorSetId, - ) -> Result>, Error> { + ) -> Result>, 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, AuthorityId, Signature>, - active_rounds: &Rounds, + proof: DoubleVotingProof< + NumberFor, + AuthorityId, + ::Signature, + >, + active_rounds: &Rounds, ) -> 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 ed8ed68c4e8d..c01fb3db4845 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 { +pub struct BeefyBlockImport { backend: Arc, runtime: Arc, inner: I, - justification_sender: BeefyVersionedFinalityProofSender, + justification_sender: BeefyVersionedFinalityProofSender, metrics: Option, } -impl Clone for BeefyBlockImport { +impl Clone + for BeefyBlockImport +{ fn clone(&self) -> Self { BeefyBlockImport { backend: self.backend.clone(), @@ -65,32 +67,35 @@ impl Clone for BeefyBlockImport BeefyBlockImport { +impl + BeefyBlockImport +{ /// Create a new BeefyBlockImport. pub fn new( backend: Arc, runtime: Arc, inner: I, - justification_sender: BeefyVersionedFinalityProofSender, + justification_sender: BeefyVersionedFinalityProofSender, metrics: Option, - ) -> BeefyBlockImport { + ) -> BeefyBlockImport { BeefyBlockImport { backend, runtime, inner, justification_sender, metrics } } } -impl BeefyBlockImport +impl BeefyBlockImport where Block: BlockT, BE: Backend, Runtime: ProvideRuntimeApi, Runtime::Api: BeefyApi + Send, + AuthorityId: AuthorityIdBound, { fn decode_and_verify( &self, encoded: &EncodedJustification, number: NumberFor, hash: ::Hash, - ) -> Result, ConsensusError> { + ) -> Result, 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::(&encoded[..], number, &validator_set) + decode_and_verify_finality_proof::(&encoded[..], number, &validator_set) .map_err(|(err, _)| err) } } #[async_trait::async_trait] -impl BlockImport for BeefyBlockImport +impl BlockImport + for BeefyBlockImport where Block: BlockT, BE: Backend, I: BlockImport + Send + Sync, Runtime: ProvideRuntimeApi + Send + Sync, Runtime::Api: BeefyApi, + 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 886368c9d7cb..9ff7c3cf54f6 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 . 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 = VersionedFinalityProof, Signature>; +pub type BeefyVersionedFinalityProof = + VersionedFinalityProof, ::Signature>; -pub(crate) fn proof_block_num_and_set_id( - proof: &BeefyVersionedFinalityProof, +pub(crate) fn proof_block_num_and_set_id( + proof: &BeefyVersionedFinalityProof, ) -> (NumberFor, ValidatorSetId) { match proof { VersionedFinalityProof::V1(sc) => @@ -37,23 +39,26 @@ pub(crate) fn proof_block_num_and_set_id( } /// Decode and verify a Beefy FinalityProof. -pub(crate) fn decode_and_verify_finality_proof( +pub(crate) fn decode_and_verify_finality_proof( encoded: &[u8], target_number: NumberFor, validator_set: &ValidatorSet, -) -> Result, (ConsensusError, u32)> { - let proof = >::decode_all(&mut &*encoded) +) -> Result, (ConsensusError, u32)> { + let proof = >::decode_all(&mut &*encoded) .map_err(|_| (ConsensusError::InvalidJustification, 0))?; - verify_with_validator_set::(target_number, validator_set, &proof)?; + verify_with_validator_set::(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, validator_set: &'a ValidatorSet, - proof: &'a BeefyVersionedFinalityProof, -) -> Result>, (ConsensusError, u32)> { + proof: &'a BeefyVersionedFinalityProof, +) -> Result< + Vec::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, - validator_set: &ValidatorSet, - keys: &[Keyring], - ) -> BeefyVersionedFinalityProof { + validator_set: &ValidatorSet, + keys: &[Keyring], + ) -> BeefyVersionedFinalityProof { 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_num, &validator_set, &good_proof).unwrap(); + verify_with_validator_set::( + 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_num + 1, &validator_set, &good_proof) { + match verify_with_validator_set::( + 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_num, &other, &good_proof) { + match verify_with_validator_set::( + 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_num + 1, &validator_set, &bad_proof.into()) { + match verify_with_validator_set::( + 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_num, &validator_set, &bad_proof.into()) { + match verify_with_validator_set::( + 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::::Dave.sign(&bad_signed_commitment.commitment.encode())); - match verify_with_validator_set::(block_num, &validator_set, &bad_proof.into()) { + *bad_signed_commitment.signatures.first_mut().unwrap() = Some( + Keyring::::Dave + .sign(&bad_signed_commitment.commitment.encode()), + ); + match verify_with_validator_set::( + 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 = proof.into(); + let versioned_proof: BeefyVersionedFinalityProof = + proof.into(); let encoded = versioned_proof.encode(); // should successfully decode and verify - let verified = - decode_and_verify_finality_proof::(&encoded, block_num, &validator_set).unwrap(); + let verified = decode_and_verify_finality_proof::( + &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 9582c2661c30..8daf3440c7d2 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 . +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 BeefyKeystore { } } -impl From> for BeefyKeystore -where - ::Signature: Send + Sync, -{ +impl From> for BeefyKeystore { fn from(store: Option) -> BeefyKeystore { BeefyKeystore(store, PhantomData) } diff --git a/substrate/client/consensus/beefy/src/lib.rs b/substrate/client/consensus/beefy/src/lib.rs index 0e49839f0fd2..4cb014b00d5b 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 { +pub struct BeefyVoterLinks { // BlockImport -> Voter links /// Stream of BEEFY signed commitments from block import to voter. - pub from_block_import_justif_stream: BeefyVersionedFinalityProofStream, + pub from_block_import_justif_stream: BeefyVersionedFinalityProofStream, // Voter -> RPC links /// Sends BEEFY signed commitments from voter to RPC. - pub to_rpc_justif_sender: BeefyVersionedFinalityProofSender, + pub to_rpc_justif_sender: BeefyVersionedFinalityProofSender, /// Sends BEEFY best block hashes from voter to RPC. pub to_rpc_best_block_sender: BeefyBestBlockSender, } /// Links used by the BEEFY RPC layer, from the BEEFY background voter. #[derive(Clone)] -pub struct BeefyRPCLinks { +pub struct BeefyRPCLinks { /// Stream of signed commitments coming from the voter. - pub from_voter_justif_stream: BeefyVersionedFinalityProofStream, + pub from_voter_justif_stream: BeefyVersionedFinalityProofStream, /// Stream of BEEFY best block hashes coming from the voter. pub from_voter_best_beefy_stream: BeefyBestBlockStream, } /// Make block importer and link half necessary to tie the background voter to it. -pub fn beefy_block_import_and_links( +pub fn beefy_block_import_and_links( wrapped_block_import: I, backend: Arc, runtime: Arc, prometheus_registry: Option, -) -> (BeefyBlockImport, BeefyVoterLinks, BeefyRPCLinks) +) -> ( + BeefyBlockImport, + BeefyVoterLinks, + BeefyRPCLinks, +) where B: Block, BE: Backend, I: BlockImport + Send + Sync, RuntimeApi: ProvideRuntimeApi + Send + Sync, RuntimeApi::Api: BeefyApi, + AuthorityId: AuthorityIdBound, { // Voter -> RPC links let (to_rpc_justif_sender, from_voter_justif_stream) = - BeefyVersionedFinalityProofStream::::channel(); + BeefyVersionedFinalityProofStream::::channel(); let (to_rpc_best_block_sender, from_voter_best_beefy_stream) = BeefyBestBlockStream::::channel(); // BlockImport -> Voter links let (to_voter_justif_sender, from_block_import_justif_stream) = - BeefyVersionedFinalityProofStream::::channel(); + BeefyVersionedFinalityProofStream::::channel(); let metrics = register_metrics(prometheus_registry); // BlockImport @@ -201,7 +205,7 @@ pub struct BeefyNetworkParams { } /// BEEFY gadget initialization parameters. -pub struct BeefyParams { +pub struct BeefyParams { /// BEEFY client pub client: Arc, /// Client Backend @@ -219,7 +223,7 @@ pub struct BeefyParams { /// Prometheus metric registry pub prometheus_registry: Option, /// Links between the block importer, the background voter and the RPC layer. - pub links: BeefyVoterLinks, + pub links: BeefyVoterLinks, /// Handler for incoming BEEFY justifications requests from a remote peer. pub on_demand_justifications_handler: BeefyJustifsRequestHandler, /// Whether running under "Authority" role. @@ -228,10 +232,10 @@ pub struct BeefyParams { /// 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 { +pub(crate) struct BeefyComms { pub gossip_engine: GossipEngine, - pub gossip_validator: Arc>, - pub on_demand_justifications: OnDemandJustificationsEngine, + pub gossip_validator: Arc>, + pub on_demand_justifications: OnDemandJustificationsEngine, } /// Helper builder object for building [worker::BeefyWorker]. @@ -240,22 +244,23 @@ pub(crate) struct BeefyComms { /// 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 { +pub(crate) struct BeefyWorkerBuilder { // utilities backend: Arc, runtime: Arc, key_store: BeefyKeystore, // voter metrics metrics: Option, - persisted_state: PersistedState, + persisted_state: PersistedState, } -impl BeefyWorkerBuilder +impl BeefyWorkerBuilder where B: Block + codec::Codec, BE: Backend, R: ProvideRuntimeApi, R::Api: BeefyApi, + 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, metrics: Option, min_block_delta: u32, - gossip_validator: Arc>, + gossip_validator: Arc>, finality_notifications: &mut Fuse>, is_authority: bool, ) -> Result { @@ -301,11 +306,11 @@ where self, payload_provider: P, sync: Arc, - comms: BeefyComms, - links: BeefyVoterLinks, - pending_justifications: BTreeMap, BeefyVersionedFinalityProof>, + comms: BeefyComms, + links: BeefyVoterLinks, + pending_justifications: BTreeMap, BeefyVersionedFinalityProof>, is_authority: bool, - ) -> BeefyWorker { + ) -> BeefyWorker { let key_store = Arc::new(self.key_store); BeefyWorker { backend: self.backend.clone(), @@ -334,7 +339,7 @@ where min_block_delta: u32, backend: Arc, runtime: Arc, - ) -> Result, Error> { + ) -> Result, 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::(&header) { + if let Some(active) = find_authorities_change::(&header) { debug!( target: LOG_TARGET, "🥩 Marking block {:?} as BEEFY Mandatory.", @@ -431,7 +436,7 @@ where key_store: &BeefyKeystore, metrics: &Option, is_authority: bool, - ) -> Result, Error> { + ) -> Result, 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::(&header) { + if let Some(active) = find_authorities_change::(&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( - beefy_params: BeefyParams, +pub async fn start_beefy_gadget( + beefy_params: BeefyParams, ) where B: Block, BE: Backend, @@ -493,6 +498,7 @@ pub async fn start_beefy_gadget( R::Api: BeefyApi, N: GossipNetwork + NetworkRequest + Send + Sync + 'static, S: GossipSyncing + SyncOracle + 'static, + AuthorityId: AuthorityIdBound, { let BeefyParams { client, @@ -598,15 +604,17 @@ pub async fn start_beefy_gadget( 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( +async fn wait_for_runtime_pallet( runtime: &R, finality: &mut Fuse>, ) -> Result<(NumberFor, ::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( +async fn expect_validator_set( 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::(&header) { + match find_authorities_change::(&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(header: &B::Header) -> Option> +pub(crate) fn find_authorities_change( + header: &B::Header, +) -> Option> 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 5dae80cb1830..31cfe4c10c2e 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, +#[derive(Debug, Decode, Encode, PartialEq)] +pub(crate) struct RoundTracker { + votes: BTreeMap::Signature>, +} + +impl Default for RoundTracker { + fn default() -> Self { + Self { votes: Default::default() } + } } -impl RoundTracker { - fn add_vote(&mut self, vote: (AuthorityId, Signature)) -> bool { +impl RoundTracker { + fn add_vote( + &mut self, + vote: (AuthorityId, ::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 { +pub enum VoteImportResult { Ok, - RoundConcluded(SignedCommitment, Signature>), - DoubleVoting(DoubleVotingProof, AuthorityId, Signature>), + RoundConcluded(SignedCommitment, ::Signature>), + DoubleVoting( + DoubleVotingProof, AuthorityId, ::Signature>, + ), Invalid, Stale, } @@ -71,19 +83,22 @@ pub enum VoteImportResult { /// /// 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 { - rounds: BTreeMap>, RoundTracker>, - previous_votes: - BTreeMap<(AuthorityId, NumberFor), VoteMessage, AuthorityId, Signature>>, +pub(crate) struct Rounds { + rounds: BTreeMap>, RoundTracker>, + previous_votes: BTreeMap< + (AuthorityId, NumberFor), + VoteMessage, AuthorityId, ::Signature>, + >, session_start: NumberFor, validator_set: ValidatorSet, mandatory_done: bool, best_done: Option>, } -impl Rounds +impl Rounds where B: Block, + AuthorityId: AuthorityIdBound, { pub(crate) fn new( session_start: NumberFor, @@ -121,14 +136,14 @@ where pub(crate) fn add_vote( &mut self, - vote: VoteMessage, AuthorityId, Signature>, - ) -> VoteImportResult { + vote: VoteMessage, AuthorityId, ::Signature>, + ) -> VoteImportResult { 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>, RoundTracker), - ) -> SignedCommitment, Signature> { + round: (Commitment>, RoundTracker), + ) -> SignedCommitment, ::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 Rounds + impl Rounds 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::::Bob.sign(b"I am committed")); + let mut rt = RoundTracker::::default(); + let bob_vote = ( + Keyring::::Bob.public(), + Keyring::::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::::Alice.sign(b"I am committed")); + let alice_vote = ( + Keyring::::Alice.public(), + Keyring::::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::::new( + let validators = ValidatorSet::::new( vec![Keyring::Alice.public(), Keyring::Bob.public(), Keyring::Charlie.public()], 42, ) .unwrap(); let session_start = 1u64.into(); - let rounds = Rounds::::new(session_start, validators); + let rounds = Rounds::::new(session_start, validators); assert_eq!(42, rounds.validator_set_id()); assert_eq!(1, rounds.session_start()); assert_eq!( &vec![ - Keyring::::Alice.public(), - Keyring::::Bob.public(), - Keyring::::Charlie.public() + Keyring::::Alice.public(), + Keyring::::Bob.public(), + Keyring::::Charlie.public() ], rounds.validators() ); @@ -285,7 +305,7 @@ mod tests { fn add_and_conclude_votes() { sp_tracing::try_init_simple(); - let validators = ValidatorSet::::new( + let validators = ValidatorSet::::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::::new(session_start, validators); + let mut rounds = Rounds::::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::::Alice.sign(b"I am committed"), + signature: Keyring::::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::::Dave.sign(b"I am committed"); + vote.signature = Keyring::::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::::Bob.sign(b"I am committed"); + vote.signature = Keyring::::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::::Charlie.sign(b"I am committed"); + vote.signature = Keyring::::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::::Alice.sign(b"I am committed")), - Some(Keyring::::Bob.sign(b"I am committed")), - Some(Keyring::::Charlie.sign(b"I am committed")), + Some(Keyring::::Alice.sign(b"I am committed")), + Some(Keyring::::Bob.sign(b"I am committed")), + Some(Keyring::::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::::Eve.sign(b"I am committed"); + vote.signature = Keyring::::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::::new( + let validators = ValidatorSet::::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::::new(session_start, validators); + let mut rounds = Rounds::::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::::Alice.sign(b"I am committed"), + signature: Keyring::::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::::new( + let validators = ValidatorSet::::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::::new(session_start, validators); + let mut rounds = Rounds::::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::::Alice.sign(b"I am committed"), + signature: Keyring::::Alice.sign(b"I am committed"), }; let mut bob_vote = VoteMessage { id: Keyring::Bob.public(), commitment: commitment.clone(), - signature: Keyring::::Bob.sign(b"I am committed"), + signature: Keyring::::Bob.sign(b"I am committed"), }; let mut charlie_vote = VoteMessage { id: Keyring::Charlie.public(), commitment, - signature: Keyring::::Charlie.sign(b"I am committed"), + signature: Keyring::::Charlie.sign(b"I am committed"), }; let expected_signatures = vec![ - Some(Keyring::::Alice.sign(b"I am committed")), - Some(Keyring::::Bob.sign(b"I am committed")), - Some(Keyring::::Charlie.sign(b"I am committed")), + Some(Keyring::::Alice.sign(b"I am committed")), + Some(Keyring::::Bob.sign(b"I am committed")), + Some(Keyring::::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::::new( + let validators = ValidatorSet::::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::::new(session_start, validators); + let mut rounds = Rounds::::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::::Alice.sign(b"I am committed"), + signature: Keyring::::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 2bb145d660df..681e11a0c531 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, + AuthorityId, >; pub(crate) type BeefyValidatorSet = ValidatorSet; @@ -107,8 +109,8 @@ impl BuildStorage for Genesis { #[derive(Default)] pub(crate) struct PeerData { - pub(crate) beefy_rpc_links: Mutex>>, - pub(crate) beefy_voter_links: Mutex>>, + pub(crate) beefy_rpc_links: Mutex>>, + pub(crate) beefy_voter_links: Mutex>>, pub(crate) beefy_justif_req_handler: Mutex>>, } @@ -371,7 +373,7 @@ async fn voter_init_setup( net: &mut BeefyTestNet, finality: &mut futures::stream::Fuse>, api: &TestApi, -) -> Result, Error> { +) -> Result, 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) {} assert_send(&task); @@ -472,8 +474,10 @@ pub(crate) fn get_beefy_streams( net: &mut BeefyTestNet, // peer index and key peers: impl Iterator)>, -) -> (Vec>, Vec>>) -{ +) -> ( + Vec>, + Vec>>, +) { 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>>, + streams: Vec>>, net: &Arc>, 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:: { + charlie_gossip_validator.update_filter(GossipFilterCfg:: { 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::::FinalityProof(proof); + let gossip_proof = GossipMessage::::FinalityProof(proof); let encoded_proof = gossip_proof.encode(); charlie_gossip_engine.gossip_message(proofs_topic::(), 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::::Vote(vote_message).encode(); + let encoded_vote = + GossipMessage::::Vote(vote_message).encode(); charlie_gossip_engine.gossip_message(votes_topic::(), encoded_vote, true); // Expect #2 is finalized. @@ -1538,12 +1543,15 @@ async fn gossipped_finality_proofs() { charlie_gossip_engine .messages_for(proofs_topic::()) .filter_map(|notification| async move { - GossipMessage::::decode(&mut ¬ification.message[..]).ok().and_then( - |message| match message { - GossipMessage::::Vote(_) => unreachable!(), - GossipMessage::::FinalityProof(proof) => Some(proof), - }, + GossipMessage::::decode( + &mut ¬ification.message[..], ) + .ok() + .and_then(|message| match message { + GossipMessage::::Vote(_) => unreachable!(), + GossipMessage::::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::(&proof); + let (round, _) = proof_block_num_and_set_id::(&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 cfbb3d63aea4..3ce4da7ecd56 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 { +pub(crate) struct VoterOracle { /// 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 { /// 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>, + sessions: VecDeque>, /// 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: ::Header, /// Best block a BEEFY voting round has been concluded for. best_beefy_block: NumberFor, + _phantom: PhantomData AuthorityId>, } -impl VoterOracle { +impl VoterOracle +where + AuthorityId: AuthorityIdBound, +{ /// Verify provided `sessions` satisfies requirements, then build `VoterOracle`. pub fn checked_new( - sessions: VecDeque>, + sessions: VecDeque>, min_block_delta: u32, grandpa_header: ::Header, best_beefy: NumberFor, @@ -105,24 +111,24 @@ impl VoterOracle { 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 VoterOracle { 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 VoterOracle { // 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, Error> { + fn active_rounds(&self) -> Result<&Rounds, 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, Error> { + fn active_rounds_mut(&mut self) -> Result<&mut Rounds, Error> { self.sessions.front_mut().ok_or(Error::UninitSession) } @@ -183,7 +190,7 @@ impl VoterOracle { } /// Add new observed session to the Oracle. - pub fn add_session(&mut self, rounds: Rounds) { + pub fn add_session(&mut self, rounds: Rounds) { 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 VoterOracle { /// /// Note: Any changes here should also bump aux-db schema version. #[derive(Debug, Decode, Encode, PartialEq)] -pub(crate) struct PersistedState { +pub(crate) struct PersistedState { /// Best block we voted on. best_voted: NumberFor, /// Chooses which incoming votes to accept and which votes to generate. /// Keeps track of voting seen for current and future rounds. - voting_oracle: VoterOracle, + voting_oracle: VoterOracle, /// Pallet-beefy genesis block - block number when BEEFY consensus started for this chain. pallet_genesis: NumberFor, } -impl PersistedState { +impl PersistedState { pub fn checked_new( grandpa_header: ::Header, best_beefy: NumberFor, - sessions: VecDeque>, + sessions: VecDeque>, min_block_delta: u32, pallet_genesis: NumberFor, ) -> Option { @@ -314,11 +321,11 @@ impl PersistedState { self.voting_oracle.best_grandpa_block_header = best_grandpa; } - pub fn voting_oracle(&self) -> &VoterOracle { + pub fn voting_oracle(&self) -> &VoterOracle { &self.voting_oracle } - pub(crate) fn gossip_filter_config(&self) -> Result, Error> { + pub(crate) fn gossip_filter_config(&self) -> Result, 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 PersistedState { } /// A BEEFY worker/voter that follows the BEEFY protocol -pub(crate) struct BeefyWorker { +pub(crate) struct BeefyWorker { // utilities pub backend: Arc, pub runtime: Arc, pub key_store: Arc>, pub payload_provider: P, pub sync: Arc, - pub fisherman: Arc>, + pub fisherman: Arc>, // communication (created once, but returned and reused if worker is restarted/reinitialized) - pub comms: BeefyComms, + pub comms: BeefyComms, // channels /// Links between the block importer, the background voter and the RPC layer. - pub links: BeefyVoterLinks, + pub links: BeefyVoterLinks, // voter state /// Buffer holding justifications for future processing. - pub pending_justifications: BTreeMap, BeefyVersionedFinalityProof>, + pub pending_justifications: BTreeMap, BeefyVersionedFinalityProof>, /// Persisted voter state. - pub persisted_state: PersistedState, + pub persisted_state: PersistedState, /// BEEFY voter metrics pub metrics: Option, /// Node runs under "Authority" role. pub is_authority: bool, } -impl BeefyWorker +impl BeefyWorker where B: Block + Codec, BE: Backend, @@ -408,17 +415,18 @@ where S: SyncOracle, R: ProvideRuntimeApi, R::Api: BeefyApi, + AuthorityId: AuthorityIdBound, { fn best_grandpa_block(&self) -> NumberFor { *self.persisted_state.voting_oracle.best_grandpa_block_header.number() } - fn voting_oracle(&self) -> &VoterOracle { + fn voting_oracle(&self) -> &VoterOracle { &self.persisted_state.voting_oracle } #[cfg(test)] - fn active_rounds(&mut self) -> Result<&Rounds, Error> { + fn active_rounds(&mut self) -> Result<&Rounds, 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::(&header) { + if let Some(new_validator_set) = find_authorities_change::(&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, AuthorityId, Signature>, - ) -> Result<(), Error> { + vote: VoteMessage, AuthorityId, ::Signature>, + ) -> Result<(), Error> + where + ::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::::FinalityProof(finality_proof); + let gossip_proof = + GossipMessage::::FinalityProof(finality_proof); let encoded_proof = gossip_proof.encode(); self.comms.gossip_engine.gossip_message( proofs_topic::(), @@ -528,7 +541,7 @@ where /// Expects `justification` to be valid. fn triage_incoming_justif( &mut self, - justification: BeefyVersionedFinalityProof, + justification: BeefyVersionedFinalityProof, ) -> Result<(), Error> { let signed_commitment = match justification { VersionedFinalityProof::V1(ref sc) => sc, @@ -560,8 +573,8 @@ where fn handle_vote( &mut self, - vote: VoteMessage, AuthorityId, Signature>, - ) -> Result>, Error> { + vote: VoteMessage, AuthorityId, ::Signature>, + ) -> Result>, 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) -> Result<(), Error> { + fn finalize( + &mut self, + finality_proof: BeefyVersionedFinalityProof, + ) -> 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::::FinalityProof(finality_proof).encode(); + let encoded_proof = + GossipMessage::::FinalityProof(finality_proof).encode(); self.comms .gossip_engine .gossip_message(proofs_topic::(), encoded_proof, true); } else { metric_inc!(self.metrics, beefy_votes_sent); debug!(target: LOG_TARGET, "🥩 Sent vote message: {:?}", vote); - let encoded_vote = GossipMessage::::Vote(vote).encode(); + let encoded_vote = GossipMessage::::Vote(vote).encode(); self.comms.gossip_engine.gossip_message(votes_topic::(), 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>>, + block_import_justif: &mut Fuse< + NotificationReceiver>, + >, finality_notifications: &mut Fuse>, - ) -> (Error, BeefyComms) { + ) -> (Error, BeefyComms) { info!( target: LOG_TARGET, "🥩 run BEEFY worker, best grandpa: #{:?}.", @@ -839,9 +858,10 @@ where .gossip_engine .messages_for(votes_topic::()) .filter_map(|notification| async move { - let vote = GossipMessage::::decode_all(&mut ¬ification.message[..]) - .ok() - .and_then(|message| message.unwrap_vote()); + let vote = + GossipMessage::::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::()) .filter_map(|notification| async move { - let proof = GossipMessage::::decode_all(&mut ¬ification.message[..]) - .ok() - .and_then(|message| message.unwrap_finality_proof()); + let proof = + GossipMessage::::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, AuthorityId, Signature>, + proof: DoubleVotingProof< + NumberFor, + AuthorityId, + ::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 PersistedState { - pub fn active_round(&self) -> Result<&Rounds, Error> { + impl PersistedState { + pub fn active_round(&self) -> Result<&Rounds, Error> { self.voting_oracle.active_rounds() } @@ -1033,17 +1058,17 @@ pub(crate) mod tests { } } - impl VoterOracle { - pub fn sessions(&self) -> &VecDeque> { + impl VoterOracle { + pub fn sessions(&self) -> &VecDeque> { &self.sessions } } fn create_beefy_worker( peer: &mut BeefyPeer, - key: &Keyring, + key: &Keyring, min_block_delta: u32, - genesis_validator_set: ValidatorSet, + genesis_validator_set: ValidatorSet, ) -> BeefyWorker< Block, Backend, @@ -1051,15 +1076,16 @@ pub(crate) mod tests { TestApi, Arc>, TestNetwork, + ecdsa_crypto::AuthorityId, > { let keystore = create_beefy_keystore(key); let (to_rpc_justif_sender, from_voter_justif_stream) = - BeefyVersionedFinalityProofStream::::channel(); + BeefyVersionedFinalityProofStream::::channel(); let (to_rpc_best_block_sender, from_voter_best_beefy_stream) = BeefyBestBlockStream::::channel(); let (_, from_block_import_justif_stream) = - BeefyVersionedFinalityProofStream::::channel(); + BeefyVersionedFinalityProofStream::::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> = Arc::new(Some(keystore).into()); + let key_store: Arc> = + 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:: { + let mut oracle = VoterOracle:: { 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, + let voting_target_with = |oracle: &mut VoterOracle, best_beefy: NumberFor, best_grandpa: NumberFor| -> Option> { @@ -1295,18 +1323,20 @@ pub(crate) mod tests { Default::default(), Digest::default(), ); - let mut oracle = VoterOracle:: { + let mut oracle = VoterOracle:: { 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, - best_grandpa: NumberFor| - -> Result<(NumberFor, NumberFor), Error> { - oracle.best_grandpa_block_header.number = best_grandpa; - oracle.accepted_interval() - }; + let accepted_interval_with = + |oracle: &mut VoterOracle, + best_grandpa: NumberFor| + -> Result<(NumberFor, NumberFor), 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::(&header).is_none()); + assert!(find_authorities_change::(&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::::AuthoritiesChange(validator_set.clone()).encode(), + ConsensusLog::::AuthoritiesChange(validator_set.clone()) + .encode(), )); // verify validator set is correctly extracted from digest - let extracted = find_authorities_change::(&header); + let extracted = find_authorities_change::(&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 f70434beab33..913184402aef 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 + + BeefyAuthorityId { + /// 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 d8610ecfa5b6..1403e4745ff1 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();