diff --git a/bridges/relays/client-substrate/src/client.rs b/bridges/relays/client-substrate/src/client.rs index 067d3d89d245..a713b3ba1212 100644 --- a/bridges/relays/client-substrate/src/client.rs +++ b/bridges/relays/client-substrate/src/client.rs @@ -19,8 +19,8 @@ use crate::{ chain::{Chain, ChainWithBalances}, rpc::{ - SubstrateAuthorClient, SubstrateChainClient, SubstrateFrameSystemClient, - SubstrateGrandpaClient, SubstrateStateClient, SubstrateSystemClient, + SubstrateAuthorClient, SubstrateChainClient, SubstrateFinalityClient, + SubstrateFrameSystemClient, SubstrateStateClient, SubstrateSystemClient, SubstrateTransactionPaymentClient, }, transaction_stall_timeout, ConnectionParams, Error, HashOf, HeaderIdOf, Result, SignParam, @@ -642,11 +642,13 @@ impl Client { .await } - /// Return new GRANDPA justifications stream. - pub async fn subscribe_grandpa_justifications(&self) -> Result> { + /// Return new finality justifications stream. + pub async fn subscribe_finality_justifications>( + &self, + ) -> Result> { let subscription = self .jsonrpsee_execute(move |client| async move { - Ok(SubstrateGrandpaClient::::subscribe_justifications(&*client).await?) + Ok(FC::subscribe_justifications(&client).await?) }) .await?; let (sender, receiver) = futures::channel::mpsc::channel(MAX_SUBSCRIPTION_CAPACITY); diff --git a/bridges/relays/client-substrate/src/lib.rs b/bridges/relays/client-substrate/src/lib.rs index bd38f3a928f8..9e6c73d83caf 100644 --- a/bridges/relays/client-substrate/src/lib.rs +++ b/bridges/relays/client-substrate/src/lib.rs @@ -39,6 +39,7 @@ pub use crate::{ }, client::{ChainRuntimeVersion, Client, OpaqueGrandpaAuthoritiesSet, Subscription}, error::{Error, Result}, + rpc::{SubstrateBeefyFinalityClient, SubstrateFinalityClient, SubstrateGrandpaFinalityClient}, sync_header::SyncHeader, transaction_tracker::TransactionTracker, }; diff --git a/bridges/relays/client-substrate/src/rpc.rs b/bridges/relays/client-substrate/src/rpc.rs index fdba424dd90f..083b1dea761a 100644 --- a/bridges/relays/client-substrate/src/rpc.rs +++ b/bridges/relays/client-substrate/src/rpc.rs @@ -16,9 +16,15 @@ //! The most generic Substrate node RPC interface. -use crate::{Chain, TransactionStatusOf}; +use async_trait::async_trait; -use jsonrpsee::{core::RpcResult, proc_macros::rpc}; +use crate::{Chain, ChainWithGrandpa, TransactionStatusOf}; + +use jsonrpsee::{ + core::{client::Subscription, RpcResult}, + proc_macros::rpc, + ws_client::WsClient, +}; use pallet_transaction_payment_rpc_runtime_api::FeeDetails; use sc_rpc_api::{state::ReadProof, system::Health}; use sp_core::{ @@ -100,14 +106,49 @@ pub(crate) trait SubstrateState { ) -> RpcResult>; } +/// RPC methods that we are using for a certain finality gadget. +#[async_trait] +pub trait SubstrateFinalityClient { + /// Subscribe to finality justifications. + async fn subscribe_justifications(client: &WsClient) -> RpcResult>; +} + /// RPC methods of Substrate `grandpa` namespace, that we are using. -#[rpc(client, client_bounds(C: Chain), namespace = "grandpa")] +#[rpc(client, client_bounds(C: ChainWithGrandpa), namespace = "grandpa")] pub(crate) trait SubstrateGrandpa { /// Subscribe to GRANDPA justifications. #[subscription(name = "subscribeJustifications", unsubscribe = "unsubscribeJustifications", item = Bytes)] fn subscribe_justifications(&self); } +/// RPC finality methods of Substrate `grandpa` namespace, that we are using. +pub struct SubstrateGrandpaFinalityClient; +#[async_trait] +impl SubstrateFinalityClient for SubstrateGrandpaFinalityClient { + async fn subscribe_justifications(client: &WsClient) -> RpcResult> { + SubstrateGrandpaClient::::subscribe_justifications(client).await + } +} + +// TODO: Use `ChainWithBeefy` instead of `Chain` after #1606 is merged +/// RPC methods of Substrate `beefy` namespace, that we are using. +#[rpc(client, client_bounds(C: Chain), namespace = "beefy")] +pub(crate) trait SubstrateBeefy { + /// Subscribe to BEEFY justifications. + #[subscription(name = "subscribeJustifications", unsubscribe = "unsubscribeJustifications", item = Bytes)] + fn subscribe_justifications(&self); +} + +/// RPC finality methods of Substrate `beefy` namespace, that we are using. +pub struct SubstrateBeefyFinalityClient; +// TODO: Use `ChainWithBeefy` instead of `Chain` after #1606 is merged +#[async_trait] +impl SubstrateFinalityClient for SubstrateBeefyFinalityClient { + async fn subscribe_justifications(client: &WsClient) -> RpcResult> { + SubstrateBeefyClient::::subscribe_justifications(client).await + } +} + /// RPC methods of Substrate `system` frame pallet, that we are using. #[rpc(client, client_bounds(C: Chain), namespace = "system")] pub(crate) trait SubstrateFrameSystem { diff --git a/bridges/relays/lib-substrate-relay/src/finality/engine.rs b/bridges/relays/lib-substrate-relay/src/finality/engine.rs index b2b72e4f2c39..83ea074e93d0 100644 --- a/bridges/relays/lib-substrate-relay/src/finality/engine.rs +++ b/bridges/relays/lib-substrate-relay/src/finality/engine.rs @@ -29,18 +29,20 @@ use finality_grandpa::voter_set::VoterSet; use num_traits::{One, Zero}; use relay_substrate_client::{ BlockNumberOf, Chain, ChainWithGrandpa, Client, Error as SubstrateError, HashOf, HeaderOf, - Subscription, + Subscription, SubstrateFinalityClient, SubstrateGrandpaFinalityClient, }; use sp_core::{storage::StorageKey, Bytes}; use sp_finality_grandpa::AuthorityList as GrandpaAuthoritiesSet; use sp_runtime::{traits::Header, ConsensusEngineId}; use std::marker::PhantomData; -/// Finality enfine, used by the Substrate chain. +/// Finality engine, used by the Substrate chain. #[async_trait] pub trait Engine: Send { /// Unique consensus engine identifier. const ID: ConsensusEngineId; + /// Type of Finality RPC client used by this engine. + type FinalityClient: SubstrateFinalityClient; /// Type of finality proofs, used by consensus engine. type FinalityProof: FinalityProof> + Decode + Encode; /// Type of bridge pallet initialization data. @@ -57,7 +59,9 @@ pub trait Engine: Send { /// Note that we don't care about type of the value - just if it present or not. fn is_initialized_key() -> StorageKey; /// A method to subscribe to encoded finality proofs, given source client. - async fn finality_proofs(client: Client) -> Result, SubstrateError>; + async fn finality_proofs(client: &Client) -> Result, SubstrateError> { + client.subscribe_finality_justifications::().await + } /// Prepare initialization data for the finality bridge pallet. async fn prepare_initialization_data( client: Client, @@ -117,6 +121,7 @@ impl Grandpa { #[async_trait] impl Engine for Grandpa { const ID: ConsensusEngineId = sp_finality_grandpa::GRANDPA_ENGINE_ID; + type FinalityClient = SubstrateGrandpaFinalityClient; type FinalityProof = GrandpaJustification>; type InitializationData = bp_header_chain::InitializationData; type OperatingMode = BasicOperatingMode; @@ -129,10 +134,6 @@ impl Engine for Grandpa { bp_header_chain::storage_keys::best_finalized_key(C::WITH_CHAIN_GRANDPA_PALLET_NAME) } - async fn finality_proofs(client: Client) -> Result, SubstrateError> { - client.subscribe_grandpa_justifications().await - } - /// Prepare initialization data for the GRANDPA verifier pallet. async fn prepare_initialization_data( source_client: Client, @@ -144,8 +145,7 @@ impl Engine for Grandpa { // But now there are problems with this approach - `CurrentSetId` may return invalid value. // So here we're waiting for the next justification, read the authorities set and then try // to figure out the set id with bruteforce. - let justifications = source_client - .subscribe_grandpa_justifications() + let justifications = Self::finality_proofs(&source_client) .await .map_err(|err| Error::Subscribe(C::NAME, err))?; // Read next justification - the header that it finalizes will be used as initial header. diff --git a/bridges/relays/lib-substrate-relay/src/finality/source.rs b/bridges/relays/lib-substrate-relay/src/finality/source.rs index c8360bbddbc5..199de3681379 100644 --- a/bridges/relays/lib-substrate-relay/src/finality/source.rs +++ b/bridges/relays/lib-substrate-relay/src/finality/source.rs @@ -142,7 +142,7 @@ impl SourceClient Result { Ok(unfold( - P::FinalityEngine::finality_proofs(self.client.clone()).await?, + P::FinalityEngine::finality_proofs(&self.client).await?, move |subscription| async move { loop { let log_error = |err| {