Refactor finality relay helpers (#1220)

* refactor finality relay helper definitions

* add missing doc

* removed commented code

* fmt

* disable rustfmt for macro

* move best_finalized method const to relay chain def
This commit is contained in:
Svyatoslav Nikolsky
2021-12-03 14:43:33 +03:00
committed by Bastian Köcher
parent f84590817b
commit e675b13042
37 changed files with 475 additions and 823 deletions
-8
View File
@@ -731,10 +731,6 @@ impl_runtime_apis! {
let header = BridgeRialtoGrandpa::best_finalized();
(header.number, header.hash())
}
fn is_known_header(hash: bp_rialto::Hash) -> bool {
BridgeRialtoGrandpa::is_known_header(hash)
}
}
impl bp_westend::WestendFinalityApi<Block> for Runtime {
@@ -742,10 +738,6 @@ impl_runtime_apis! {
let header = BridgeWestendGrandpa::best_finalized();
(header.number, header.hash())
}
fn is_known_header(hash: bp_westend::Hash) -> bool {
BridgeWestendGrandpa::is_known_header(hash)
}
}
impl bp_rialto::ToRialtoOutboundLaneApi<Block, Balance, ToRialtoMessagePayload> for Runtime {
-4
View File
@@ -662,10 +662,6 @@ impl_runtime_apis! {
let header = BridgeMillauGrandpa::best_finalized();
(header.number, header.hash())
}
fn is_known_header(hash: bp_millau::Hash) -> bool {
BridgeMillauGrandpa::is_known_header(hash)
}
}
impl sp_transaction_pool::runtime_api::TaggedTransactionQueue<Block> for Runtime {
@@ -90,8 +90,6 @@ pub const POLKADOT_TO_KUSAMA_CONVERSION_RATE_PARAMETER_NAME: &str =
/// Name of the `KusamaFinalityApi::best_finalized` runtime method.
pub const BEST_FINALIZED_KUSAMA_HEADER_METHOD: &str = "KusamaFinalityApi_best_finalized";
/// Name of the `KusamaFinalityApi::is_known_header` runtime method.
pub const IS_KNOWN_KUSAMA_HEADER_METHOD: &str = "KusamaFinalityApi_is_known_header";
/// Name of the `ToKusamaOutboundLaneApi::estimate_message_delivery_and_dispatch_fee` runtime
/// method.
@@ -124,8 +122,6 @@ sp_api::decl_runtime_apis! {
pub trait KusamaFinalityApi {
/// Returns number and hash of the best finalized header known to the bridge module.
fn best_finalized() -> (BlockNumber, Hash);
/// Returns true if the header is known to the runtime.
fn is_known_header(hash: Hash) -> bool;
}
/// Outbound message lane API for messages that are sent to Kusama chain.
@@ -298,8 +298,6 @@ sp_api::decl_runtime_apis! {
pub trait MillauFinalityApi {
/// Returns number and hash of the best finalized header known to the bridge module.
fn best_finalized() -> (BlockNumber, Hash);
/// Returns true if the header is known to the runtime.
fn is_known_header(hash: Hash) -> bool;
}
/// Outbound message lane API for messages that are sent to Millau chain.
@@ -90,8 +90,6 @@ pub const KUSAMA_TO_POLKADOT_CONVERSION_RATE_PARAMETER_NAME: &str =
/// Name of the `PolkadotFinalityApi::best_finalized` runtime method.
pub const BEST_FINALIZED_POLKADOT_HEADER_METHOD: &str = "PolkadotFinalityApi_best_finalized";
/// Name of the `PolkadotFinalityApi::is_known_header` runtime method.
pub const IS_KNOWN_POLKADOT_HEADER_METHOD: &str = "PolkadotFinalityApi_is_known_header";
/// Name of the `ToPolkadotOutboundLaneApi::estimate_message_delivery_and_dispatch_fee` runtime
/// method.
@@ -124,8 +122,6 @@ sp_api::decl_runtime_apis! {
pub trait PolkadotFinalityApi {
/// Returns number and hash of the best finalized header known to the bridge module.
fn best_finalized() -> (BlockNumber, Hash);
/// Returns true if the header is known to the runtime.
fn is_known_header(hash: Hash) -> bool;
}
/// Outbound message lane API for messages that are sent to Polkadot chain.
@@ -271,8 +271,6 @@ sp_api::decl_runtime_apis! {
pub trait RialtoFinalityApi {
/// Returns number and hash of the best finalized header known to the bridge module.
fn best_finalized() -> (BlockNumber, Hash);
/// Returns true if the header is known to the runtime.
fn is_known_header(hash: Hash) -> bool;
}
/// Outbound message lane API for messages that are sent to Rialto chain.
@@ -79,8 +79,6 @@ pub const WITH_WOCOCO_MESSAGES_PALLET_NAME: &str = "BridgeWococoMessages";
/// Name of the `RococoFinalityApi::best_finalized` runtime method.
pub const BEST_FINALIZED_ROCOCO_HEADER_METHOD: &str = "RococoFinalityApi_best_finalized";
/// Name of the `RococoFinalityApi::is_known_header` runtime method.
pub const IS_KNOWN_ROCOCO_HEADER_METHOD: &str = "RococoFinalityApi_is_known_header";
/// Name of the `ToRococoOutboundLaneApi::estimate_message_delivery_and_dispatch_fee` runtime
/// method.
@@ -125,8 +123,6 @@ sp_api::decl_runtime_apis! {
pub trait RococoFinalityApi {
/// Returns number and hash of the best finalized header known to the bridge module.
fn best_finalized() -> (BlockNumber, Hash);
/// Returns true if the header is known to the runtime.
fn is_known_header(hash: Hash) -> bool;
}
/// Outbound message lane API for messages that are sent to Rococo chain.
@@ -90,8 +90,6 @@ pub fn derive_account_from_rococo_id(id: bp_runtime::SourceAccount<AccountId>) -
/// Name of the `WestendFinalityApi::best_finalized` runtime method.
pub const BEST_FINALIZED_WESTEND_HEADER_METHOD: &str = "WestendFinalityApi_best_finalized";
/// Name of the `WestendFinalityApi::is_known_header` runtime method.
pub const IS_KNOWN_WESTEND_HEADER_METHOD: &str = "WestendFinalityApi_is_known_header";
/// Name of the `ToWestendOutboundLaneApi::estimate_message_delivery_and_dispatch_fee` runtime
/// method.
@@ -131,8 +129,6 @@ sp_api::decl_runtime_apis! {
pub trait WestendFinalityApi {
/// Returns number and hash of the best finalized header known to the bridge module.
fn best_finalized() -> (BlockNumber, Hash);
/// Returns true if the header is known to the runtime.
fn is_known_header(hash: Hash) -> bool;
}
/// Outbound message lane API for messages that are sent to Westend chain.
@@ -44,8 +44,6 @@ pub const WITH_ROCOCO_MESSAGES_PALLET_NAME: &str = "BridgeRococoMessages";
/// Name of the `WococoFinalityApi::best_finalized` runtime method.
pub const BEST_FINALIZED_WOCOCO_HEADER_METHOD: &str = "WococoFinalityApi_best_finalized";
/// Name of the `WococoFinalityApi::is_known_header` runtime method.
pub const IS_KNOWN_WOCOCO_HEADER_METHOD: &str = "WococoFinalityApi_is_known_header";
/// Name of the `ToWococoOutboundLaneApi::estimate_message_delivery_and_dispatch_fee` runtime
/// method.
@@ -78,8 +76,6 @@ sp_api::decl_runtime_apis! {
pub trait WococoFinalityApi {
/// Returns number and hash of the best finalized header known to the bridge module.
fn best_finalized() -> (BlockNumber, Hash);
/// Returns true if the header is known to the runtime.
fn is_known_header(hash: Hash) -> bool;
}
/// Outbound message lane API for messages that are sent to Wococo chain.
+2 -1
View File
@@ -15,7 +15,7 @@
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
use frame_support::Parameter;
use num_traits::{AsPrimitive, Bounded, CheckedSub, SaturatingAdd, Zero};
use num_traits::{AsPrimitive, Bounded, CheckedSub, Saturating, SaturatingAdd, Zero};
use sp_runtime::{
traits::{
AtLeast32Bit, AtLeast32BitUnsigned, Hash as HashT, Header as HeaderT, MaybeDisplay,
@@ -46,6 +46,7 @@ pub trait Chain: Send + Sync + 'static {
+ MaybeMallocSizeOf
+ AsPrimitive<usize>
+ Default
+ Saturating
// original `sp_runtime::traits::Header::BlockNumber` doesn't have this trait, but
// `sp_runtime::generic::Era` requires block number -> `u64` conversion.
+ Into<u64>;
@@ -16,17 +16,8 @@
//! Kusama-to-Polkadot headers sync entrypoint.
use codec::Encode;
use sp_core::{Bytes, Pair};
use bp_header_chain::justification::GrandpaJustification;
use relay_kusama_client::{Kusama, SyncHeader as KusamaSyncHeader};
use relay_polkadot_client::{Polkadot, SigningParams as PolkadotSigningParams};
use relay_substrate_client::{Client, TransactionSignScheme, UnsignedTransaction};
use relay_utils::metrics::MetricsParams;
use substrate_relay_helper::finality_pipeline::{
SubstrateFinalitySyncPipeline, SubstrateFinalityToSubstrate,
};
use sp_core::Pair;
use substrate_relay_helper::finality_pipeline::{SubstrateFinalitySyncPipeline, TransactionParams};
/// Maximal saturating difference between `balance(now)` and `balance(now-24h)` to treat
/// relay as gone wild.
@@ -35,77 +26,37 @@ use substrate_relay_helper::finality_pipeline::{
/// DOT, but let's round up to 30 DOT here.
pub(crate) const MAXIMAL_BALANCE_DECREASE_PER_DAY: bp_polkadot::Balance = 30_000_000_000;
/// Kusama-to-Polkadot finality sync pipeline.
pub(crate) type FinalityPipelineKusamaFinalityToPolkadot =
SubstrateFinalityToSubstrate<Kusama, Polkadot, PolkadotSigningParams>;
/// Description of Kusama -> Polkadot finalized headers bridge.
#[derive(Clone, Debug)]
pub(crate) struct KusamaFinalityToPolkadot {
finality_pipeline: FinalityPipelineKusamaFinalityToPolkadot,
}
impl KusamaFinalityToPolkadot {
pub fn new(target_client: Client<Polkadot>, target_sign: PolkadotSigningParams) -> Self {
Self {
finality_pipeline: FinalityPipelineKusamaFinalityToPolkadot::new(
target_client,
target_sign,
),
}
}
}
pub struct KusamaFinalityToPolkadot;
substrate_relay_helper::generate_mocked_submit_finality_proof_call_builder!(
KusamaFinalityToPolkadot,
KusamaFinalityToPolkadotCallBuilder,
relay_polkadot_client::runtime::Call::BridgeKusamaGrandpa,
relay_polkadot_client::runtime::BridgeKusamaGrandpaCall::submit_finality_proof
);
impl SubstrateFinalitySyncPipeline for KusamaFinalityToPolkadot {
type FinalitySyncPipeline = FinalityPipelineKusamaFinalityToPolkadot;
type SourceChain = relay_kusama_client::Kusama;
type TargetChain = relay_polkadot_client::Polkadot;
const BEST_FINALIZED_SOURCE_HEADER_ID_AT_TARGET: &'static str =
bp_kusama::BEST_FINALIZED_KUSAMA_HEADER_METHOD;
type SubmitFinalityProofCallBuilder = KusamaFinalityToPolkadotCallBuilder;
type TransactionSignScheme = relay_polkadot_client::Polkadot;
type TargetChain = Polkadot;
fn customize_metrics(params: MetricsParams) -> anyhow::Result<MetricsParams> {
crate::chains::add_polkadot_kusama_price_metrics::<Self::FinalitySyncPipeline>(params)
}
fn start_relay_guards(&self) {
fn start_relay_guards(
target_client: &relay_substrate_client::Client<relay_polkadot_client::Polkadot>,
transaction_params: &TransactionParams<sp_core::sr25519::Pair>,
) {
relay_substrate_client::guard::abort_on_spec_version_change(
self.finality_pipeline.target_client.clone(),
target_client.clone(),
bp_polkadot::VERSION.spec_version,
);
relay_substrate_client::guard::abort_when_account_balance_decreased(
self.finality_pipeline.target_client.clone(),
self.transactions_author(),
target_client.clone(),
transaction_params.transactions_signer.public().into(),
MAXIMAL_BALANCE_DECREASE_PER_DAY,
);
}
fn transactions_author(&self) -> bp_polkadot::AccountId {
(*self.finality_pipeline.target_sign.public().as_array_ref()).into()
}
fn make_submit_finality_proof_transaction(
&self,
era: bp_runtime::TransactionEraOf<Polkadot>,
transaction_nonce: bp_runtime::IndexOf<Polkadot>,
header: KusamaSyncHeader,
proof: GrandpaJustification<bp_kusama::Header>,
) -> Bytes {
let call = relay_polkadot_client::runtime::Call::BridgeKusamaGrandpa(
relay_polkadot_client::runtime::BridgeKusamaGrandpaCall::submit_finality_proof(
Box::new(header.into_inner()),
proof,
),
);
let genesis_hash = *self.finality_pipeline.target_client.genesis_hash();
let transaction = Polkadot::sign_transaction(
genesis_hash,
&self.finality_pipeline.target_sign,
era,
UnsignedTransaction::new(call, transaction_nonce),
);
Bytes(transaction.encode())
}
}
#[cfg(test)]
@@ -16,65 +16,22 @@
//! Millau-to-Rialto headers sync entrypoint.
use codec::Encode;
use sp_core::{Bytes, Pair};
use bp_header_chain::justification::GrandpaJustification;
use relay_millau_client::{Millau, SyncHeader as MillauSyncHeader};
use relay_rialto_client::{Rialto, SigningParams as RialtoSigningParams};
use relay_substrate_client::{Client, IndexOf, TransactionSignScheme, UnsignedTransaction};
use substrate_relay_helper::finality_pipeline::{
SubstrateFinalitySyncPipeline, SubstrateFinalityToSubstrate,
DirectSubmitFinalityProofCallBuilder, SubstrateFinalitySyncPipeline,
};
/// Millau-to-Rialto finality sync pipeline.
pub(crate) type FinalityPipelineMillauToRialto =
SubstrateFinalityToSubstrate<Millau, Rialto, RialtoSigningParams>;
/// Description of Millau -> Rialto finalized headers bridge.
#[derive(Clone, Debug)]
pub(crate) struct MillauFinalityToRialto {
finality_pipeline: FinalityPipelineMillauToRialto,
}
impl MillauFinalityToRialto {
pub fn new(target_client: Client<Rialto>, target_sign: RialtoSigningParams) -> Self {
Self { finality_pipeline: FinalityPipelineMillauToRialto::new(target_client, target_sign) }
}
}
pub struct MillauFinalityToRialto;
impl SubstrateFinalitySyncPipeline for MillauFinalityToRialto {
type FinalitySyncPipeline = FinalityPipelineMillauToRialto;
type SourceChain = relay_millau_client::Millau;
type TargetChain = relay_rialto_client::Rialto;
const BEST_FINALIZED_SOURCE_HEADER_ID_AT_TARGET: &'static str =
bp_millau::BEST_FINALIZED_MILLAU_HEADER_METHOD;
type TargetChain = Rialto;
fn transactions_author(&self) -> bp_rialto::AccountId {
(*self.finality_pipeline.target_sign.public().as_array_ref()).into()
}
fn make_submit_finality_proof_transaction(
&self,
era: bp_runtime::TransactionEraOf<Rialto>,
transaction_nonce: IndexOf<Rialto>,
header: MillauSyncHeader,
proof: GrandpaJustification<bp_millau::Header>,
) -> Bytes {
let call = rialto_runtime::BridgeGrandpaMillauCall::submit_finality_proof {
finality_target: Box::new(header.into_inner()),
justification: proof,
}
.into();
let genesis_hash = *self.finality_pipeline.target_client.genesis_hash();
let transaction = Rialto::sign_transaction(
genesis_hash,
&self.finality_pipeline.target_sign,
era,
UnsignedTransaction::new(call, transaction_nonce),
);
Bytes(transaction.encode())
}
type SubmitFinalityProofCallBuilder = DirectSubmitFinalityProofCallBuilder<
Self,
rialto_runtime::Runtime,
rialto_runtime::MillauGrandpaInstance,
>;
type TransactionSignScheme = relay_rialto_client::Rialto;
}
@@ -39,18 +39,6 @@ mod rococo;
mod westend;
mod wococo;
use relay_utils::metrics::{MetricsParams, StandaloneMetric};
pub(crate) fn add_polkadot_kusama_price_metrics<T: finality_relay::FinalitySyncPipeline>(
params: MetricsParams,
) -> anyhow::Result<MetricsParams> {
substrate_relay_helper::helpers::token_price_metric(polkadot::TOKEN_ID)?
.register_and_spawn(&params.registry)?;
substrate_relay_helper::helpers::token_price_metric(kusama::TOKEN_ID)?
.register_and_spawn(&params.registry)?;
Ok(params)
}
#[cfg(test)]
mod tests {
use crate::cli::{encode_call, send_message};
@@ -16,17 +16,8 @@
//! Polkadot-to-Kusama headers sync entrypoint.
use codec::Encode;
use sp_core::{Bytes, Pair};
use bp_header_chain::justification::GrandpaJustification;
use relay_kusama_client::{Kusama, SigningParams as KusamaSigningParams};
use relay_polkadot_client::{Polkadot, SyncHeader as PolkadotSyncHeader};
use relay_substrate_client::{Client, TransactionSignScheme, UnsignedTransaction};
use relay_utils::metrics::MetricsParams;
use substrate_relay_helper::finality_pipeline::{
SubstrateFinalitySyncPipeline, SubstrateFinalityToSubstrate,
};
use sp_core::Pair;
use substrate_relay_helper::finality_pipeline::{SubstrateFinalitySyncPipeline, TransactionParams};
/// Maximal saturating difference between `balance(now)` and `balance(now-24h)` to treat
/// relay as gone wild.
@@ -35,77 +26,37 @@ use substrate_relay_helper::finality_pipeline::{
/// KSM, but let's round up to 0.1 KSM here.
pub(crate) const MAXIMAL_BALANCE_DECREASE_PER_DAY: bp_polkadot::Balance = 100_000_000_000;
/// Polkadot-to-Kusama finality sync pipeline.
pub(crate) type FinalityPipelinePolkadotFinalityToKusama =
SubstrateFinalityToSubstrate<Polkadot, Kusama, KusamaSigningParams>;
/// Description of Polkadot -> Kusama finalized headers bridge.
#[derive(Clone, Debug)]
pub(crate) struct PolkadotFinalityToKusama {
finality_pipeline: FinalityPipelinePolkadotFinalityToKusama,
}
impl PolkadotFinalityToKusama {
pub fn new(target_client: Client<Kusama>, target_sign: KusamaSigningParams) -> Self {
Self {
finality_pipeline: FinalityPipelinePolkadotFinalityToKusama::new(
target_client,
target_sign,
),
}
}
}
pub struct PolkadotFinalityToKusama;
substrate_relay_helper::generate_mocked_submit_finality_proof_call_builder!(
PolkadotFinalityToKusama,
PolkadotFinalityToKusamaCallBuilder,
relay_kusama_client::runtime::Call::BridgePolkadotGrandpa,
relay_kusama_client::runtime::BridgePolkadotGrandpaCall::submit_finality_proof
);
impl SubstrateFinalitySyncPipeline for PolkadotFinalityToKusama {
type FinalitySyncPipeline = FinalityPipelinePolkadotFinalityToKusama;
type SourceChain = relay_polkadot_client::Polkadot;
type TargetChain = relay_kusama_client::Kusama;
const BEST_FINALIZED_SOURCE_HEADER_ID_AT_TARGET: &'static str =
bp_polkadot::BEST_FINALIZED_POLKADOT_HEADER_METHOD;
type SubmitFinalityProofCallBuilder = PolkadotFinalityToKusamaCallBuilder;
type TransactionSignScheme = relay_kusama_client::Kusama;
type TargetChain = Kusama;
fn customize_metrics(params: MetricsParams) -> anyhow::Result<MetricsParams> {
crate::chains::add_polkadot_kusama_price_metrics::<Self::FinalitySyncPipeline>(params)
}
fn start_relay_guards(&self) {
fn start_relay_guards(
target_client: &relay_substrate_client::Client<relay_kusama_client::Kusama>,
transaction_params: &TransactionParams<sp_core::sr25519::Pair>,
) {
relay_substrate_client::guard::abort_on_spec_version_change(
self.finality_pipeline.target_client.clone(),
target_client.clone(),
bp_kusama::VERSION.spec_version,
);
relay_substrate_client::guard::abort_when_account_balance_decreased(
self.finality_pipeline.target_client.clone(),
self.transactions_author(),
target_client.clone(),
transaction_params.transactions_signer.public().into(),
MAXIMAL_BALANCE_DECREASE_PER_DAY,
);
}
fn transactions_author(&self) -> bp_kusama::AccountId {
(*self.finality_pipeline.target_sign.public().as_array_ref()).into()
}
fn make_submit_finality_proof_transaction(
&self,
era: bp_runtime::TransactionEraOf<Kusama>,
transaction_nonce: bp_runtime::IndexOf<Kusama>,
header: PolkadotSyncHeader,
proof: GrandpaJustification<bp_polkadot::Header>,
) -> Bytes {
let call = relay_kusama_client::runtime::Call::BridgePolkadotGrandpa(
relay_kusama_client::runtime::BridgePolkadotGrandpaCall::submit_finality_proof(
Box::new(header.into_inner()),
proof,
),
);
let genesis_hash = *self.finality_pipeline.target_client.genesis_hash();
let transaction = Kusama::sign_transaction(
genesis_hash,
&self.finality_pipeline.target_sign,
era,
UnsignedTransaction::new(call, transaction_nonce),
);
Bytes(transaction.encode())
}
}
#[cfg(test)]
@@ -16,73 +16,22 @@
//! Rialto-to-Millau headers sync entrypoint.
use codec::Encode;
use sp_core::{Bytes, Pair};
use bp_header_chain::justification::GrandpaJustification;
use relay_millau_client::{Millau, SigningParams as MillauSigningParams};
use relay_rialto_client::{Rialto, SyncHeader as RialtoSyncHeader};
use relay_substrate_client::{Client, IndexOf, TransactionSignScheme, UnsignedTransaction};
use substrate_relay_helper::finality_pipeline::{
SubstrateFinalitySyncPipeline, SubstrateFinalityToSubstrate,
DirectSubmitFinalityProofCallBuilder, SubstrateFinalitySyncPipeline,
};
/// Rialto-to-Millau finality sync pipeline.
pub(crate) type FinalityPipelineRialtoFinalityToMillau =
SubstrateFinalityToSubstrate<Rialto, Millau, MillauSigningParams>;
/// Description of Millau -> Rialto finalized headers bridge.
#[derive(Clone, Debug)]
pub struct RialtoFinalityToMillau {
finality_pipeline: FinalityPipelineRialtoFinalityToMillau,
}
impl RialtoFinalityToMillau {
pub fn new(target_client: Client<Millau>, target_sign: MillauSigningParams) -> Self {
Self {
finality_pipeline: FinalityPipelineRialtoFinalityToMillau::new(
target_client,
target_sign,
),
}
}
}
pub struct RialtoFinalityToMillau;
impl SubstrateFinalitySyncPipeline for RialtoFinalityToMillau {
type FinalitySyncPipeline = FinalityPipelineRialtoFinalityToMillau;
type SourceChain = relay_rialto_client::Rialto;
type TargetChain = relay_millau_client::Millau;
const BEST_FINALIZED_SOURCE_HEADER_ID_AT_TARGET: &'static str =
bp_rialto::BEST_FINALIZED_RIALTO_HEADER_METHOD;
type TargetChain = Millau;
fn transactions_author(&self) -> bp_millau::AccountId {
(*self.finality_pipeline.target_sign.public().as_array_ref()).into()
}
fn make_submit_finality_proof_transaction(
&self,
era: bp_runtime::TransactionEraOf<Millau>,
transaction_nonce: IndexOf<Millau>,
header: RialtoSyncHeader,
proof: GrandpaJustification<bp_rialto::Header>,
) -> Bytes {
let call = millau_runtime::BridgeGrandpaCall::<
type SubmitFinalityProofCallBuilder = DirectSubmitFinalityProofCallBuilder<
Self,
millau_runtime::Runtime,
millau_runtime::RialtoGrandpaInstance,
>::submit_finality_proof {
finality_target: Box::new(header.into_inner()),
justification: proof,
}
.into();
let genesis_hash = *self.finality_pipeline.target_client.genesis_hash();
let transaction = Millau::sign_transaction(
genesis_hash,
&self.finality_pipeline.target_sign,
era,
UnsignedTransaction::new(call, transaction_nonce),
);
Bytes(transaction.encode())
}
>;
type TransactionSignScheme = relay_millau_client::Millau;
}
@@ -16,89 +16,40 @@
//! Rococo-to-Wococo headers sync entrypoint.
use codec::Encode;
use sp_core::{Bytes, Pair};
use bp_header_chain::justification::GrandpaJustification;
use relay_rococo_client::{Rococo, SyncHeader as RococoSyncHeader};
use relay_substrate_client::{Client, IndexOf, TransactionSignScheme, UnsignedTransaction};
use relay_utils::metrics::MetricsParams;
use relay_wococo_client::{SigningParams as WococoSigningParams, Wococo};
use substrate_relay_helper::finality_pipeline::{
SubstrateFinalitySyncPipeline, SubstrateFinalityToSubstrate,
};
use crate::chains::wococo_headers_to_rococo::MAXIMAL_BALANCE_DECREASE_PER_DAY;
/// Rococo-to-Wococo finality sync pipeline.
pub(crate) type FinalityPipelineRococoFinalityToWococo =
SubstrateFinalityToSubstrate<Rococo, Wococo, WococoSigningParams>;
use sp_core::Pair;
use substrate_relay_helper::finality_pipeline::{SubstrateFinalitySyncPipeline, TransactionParams};
/// Description of Rococo -> Wococo finalized headers bridge.
#[derive(Clone, Debug)]
pub(crate) struct RococoFinalityToWococo {
finality_pipeline: FinalityPipelineRococoFinalityToWococo,
}
impl RococoFinalityToWococo {
pub fn new(target_client: Client<Wococo>, target_sign: WococoSigningParams) -> Self {
Self {
finality_pipeline: FinalityPipelineRococoFinalityToWococo::new(
target_client,
target_sign,
),
}
}
}
pub struct RococoFinalityToWococo;
substrate_relay_helper::generate_mocked_submit_finality_proof_call_builder!(
RococoFinalityToWococo,
RococoFinalityToWococoCallBuilder,
relay_wococo_client::runtime::Call::BridgeGrandpaRococo,
relay_wococo_client::runtime::BridgeGrandpaRococoCall::submit_finality_proof
);
impl SubstrateFinalitySyncPipeline for RococoFinalityToWococo {
type FinalitySyncPipeline = FinalityPipelineRococoFinalityToWococo;
type SourceChain = relay_rococo_client::Rococo;
type TargetChain = relay_wococo_client::Wococo;
const BEST_FINALIZED_SOURCE_HEADER_ID_AT_TARGET: &'static str =
bp_rococo::BEST_FINALIZED_ROCOCO_HEADER_METHOD;
type SubmitFinalityProofCallBuilder = RococoFinalityToWococoCallBuilder;
type TransactionSignScheme = relay_wococo_client::Wococo;
type TargetChain = Wococo;
fn customize_metrics(params: MetricsParams) -> anyhow::Result<MetricsParams> {
crate::chains::add_polkadot_kusama_price_metrics::<Self::FinalitySyncPipeline>(params)
}
fn start_relay_guards(&self) {
fn start_relay_guards(
target_client: &relay_substrate_client::Client<relay_wococo_client::Wococo>,
transaction_params: &TransactionParams<sp_core::sr25519::Pair>,
) {
relay_substrate_client::guard::abort_on_spec_version_change(
self.finality_pipeline.target_client.clone(),
target_client.clone(),
bp_wococo::VERSION.spec_version,
);
relay_substrate_client::guard::abort_when_account_balance_decreased(
self.finality_pipeline.target_client.clone(),
self.transactions_author(),
target_client.clone(),
transaction_params.transactions_signer.public().into(),
MAXIMAL_BALANCE_DECREASE_PER_DAY,
);
}
fn transactions_author(&self) -> bp_wococo::AccountId {
(*self.finality_pipeline.target_sign.public().as_array_ref()).into()
}
fn make_submit_finality_proof_transaction(
&self,
era: bp_runtime::TransactionEraOf<Wococo>,
transaction_nonce: IndexOf<Wococo>,
header: RococoSyncHeader,
proof: GrandpaJustification<bp_rococo::Header>,
) -> Bytes {
let call = relay_wococo_client::runtime::Call::BridgeGrandpaRococo(
relay_wococo_client::runtime::BridgeGrandpaRococoCall::submit_finality_proof(
Box::new(header.into_inner()),
proof,
),
);
let genesis_hash = *self.finality_pipeline.target_client.genesis_hash();
let transaction = Wococo::sign_transaction(
genesis_hash,
&self.finality_pipeline.target_sign,
era,
UnsignedTransaction::new(call, transaction_nonce),
);
Bytes(transaction.encode())
}
}
@@ -16,78 +16,22 @@
//! Westend-to-Millau headers sync entrypoint.
use codec::Encode;
use sp_core::{Bytes, Pair};
use bp_header_chain::justification::GrandpaJustification;
use relay_millau_client::{Millau, SigningParams as MillauSigningParams};
use relay_substrate_client::{Client, IndexOf, TransactionSignScheme, UnsignedTransaction};
use relay_utils::metrics::MetricsParams;
use relay_westend_client::{SyncHeader as WestendSyncHeader, Westend};
use substrate_relay_helper::finality_pipeline::{
SubstrateFinalitySyncPipeline, SubstrateFinalityToSubstrate,
DirectSubmitFinalityProofCallBuilder, SubstrateFinalitySyncPipeline,
};
/// Westend-to-Millau finality sync pipeline.
pub(crate) type FinalityPipelineWestendFinalityToMillau =
SubstrateFinalityToSubstrate<Westend, Millau, MillauSigningParams>;
/// Description of Westend -> Millau finalized headers bridge.
#[derive(Clone, Debug)]
pub(crate) struct WestendFinalityToMillau {
finality_pipeline: FinalityPipelineWestendFinalityToMillau,
}
impl WestendFinalityToMillau {
pub fn new(target_client: Client<Millau>, target_sign: MillauSigningParams) -> Self {
Self {
finality_pipeline: FinalityPipelineWestendFinalityToMillau::new(
target_client,
target_sign,
),
}
}
}
pub struct WestendFinalityToMillau;
impl SubstrateFinalitySyncPipeline for WestendFinalityToMillau {
type FinalitySyncPipeline = FinalityPipelineWestendFinalityToMillau;
type SourceChain = relay_westend_client::Westend;
type TargetChain = relay_millau_client::Millau;
const BEST_FINALIZED_SOURCE_HEADER_ID_AT_TARGET: &'static str =
bp_westend::BEST_FINALIZED_WESTEND_HEADER_METHOD;
type TargetChain = Millau;
fn customize_metrics(params: MetricsParams) -> anyhow::Result<MetricsParams> {
crate::chains::add_polkadot_kusama_price_metrics::<Self::FinalitySyncPipeline>(params)
}
fn transactions_author(&self) -> bp_millau::AccountId {
(*self.finality_pipeline.target_sign.public().as_array_ref()).into()
}
fn make_submit_finality_proof_transaction(
&self,
era: bp_runtime::TransactionEraOf<Millau>,
transaction_nonce: IndexOf<Millau>,
header: WestendSyncHeader,
proof: GrandpaJustification<bp_westend::Header>,
) -> Bytes {
let call = millau_runtime::BridgeGrandpaCall::<
type SubmitFinalityProofCallBuilder = DirectSubmitFinalityProofCallBuilder<
Self,
millau_runtime::Runtime,
millau_runtime::WestendGrandpaInstance,
>::submit_finality_proof {
finality_target: Box::new(header.into_inner()),
justification: proof,
}
.into();
let genesis_hash = *self.finality_pipeline.target_client.genesis_hash();
let transaction = Millau::sign_transaction(
genesis_hash,
&self.finality_pipeline.target_sign,
era,
UnsignedTransaction::new(call, transaction_nonce),
);
Bytes(transaction.encode())
}
>;
type TransactionSignScheme = relay_millau_client::Millau;
}
@@ -16,17 +16,8 @@
//! Wococo-to-Rococo headers sync entrypoint.
use codec::Encode;
use sp_core::{Bytes, Pair};
use bp_header_chain::justification::GrandpaJustification;
use relay_rococo_client::{Rococo, SigningParams as RococoSigningParams};
use relay_substrate_client::{Client, IndexOf, TransactionSignScheme, UnsignedTransaction};
use relay_utils::metrics::MetricsParams;
use relay_wococo_client::{SyncHeader as WococoSyncHeader, Wococo};
use substrate_relay_helper::finality_pipeline::{
SubstrateFinalitySyncPipeline, SubstrateFinalityToSubstrate,
};
use sp_core::Pair;
use substrate_relay_helper::finality_pipeline::{SubstrateFinalitySyncPipeline, TransactionParams};
/// Maximal saturating difference between `balance(now)` and `balance(now-24h)` to treat
/// relay as gone wild.
@@ -35,77 +26,37 @@ use substrate_relay_helper::finality_pipeline::{
/// Note that this is in plancks, so this corresponds to `1500 UNITS`.
pub(crate) const MAXIMAL_BALANCE_DECREASE_PER_DAY: bp_rococo::Balance = 1_500_000_000_000_000;
/// Wococo-to-Rococo finality sync pipeline.
pub(crate) type FinalityPipelineWococoFinalityToRococo =
SubstrateFinalityToSubstrate<Wococo, Rococo, RococoSigningParams>;
/// Description of Wococo -> Rococo finalized headers bridge.
#[derive(Clone, Debug)]
pub(crate) struct WococoFinalityToRococo {
finality_pipeline: FinalityPipelineWococoFinalityToRococo,
}
impl WococoFinalityToRococo {
pub fn new(target_client: Client<Rococo>, target_sign: RococoSigningParams) -> Self {
Self {
finality_pipeline: FinalityPipelineWococoFinalityToRococo::new(
target_client,
target_sign,
),
}
}
}
pub struct WococoFinalityToRococo;
substrate_relay_helper::generate_mocked_submit_finality_proof_call_builder!(
WococoFinalityToRococo,
WococoFinalityToRococoCallBuilder,
relay_rococo_client::runtime::Call::BridgeGrandpaWococo,
relay_rococo_client::runtime::BridgeGrandpaWococoCall::submit_finality_proof
);
impl SubstrateFinalitySyncPipeline for WococoFinalityToRococo {
type FinalitySyncPipeline = FinalityPipelineWococoFinalityToRococo;
type SourceChain = relay_wococo_client::Wococo;
type TargetChain = relay_rococo_client::Rococo;
const BEST_FINALIZED_SOURCE_HEADER_ID_AT_TARGET: &'static str =
bp_wococo::BEST_FINALIZED_WOCOCO_HEADER_METHOD;
type SubmitFinalityProofCallBuilder = WococoFinalityToRococoCallBuilder;
type TransactionSignScheme = relay_rococo_client::Rococo;
type TargetChain = Rococo;
fn customize_metrics(params: MetricsParams) -> anyhow::Result<MetricsParams> {
crate::chains::add_polkadot_kusama_price_metrics::<Self::FinalitySyncPipeline>(params)
}
fn start_relay_guards(&self) {
fn start_relay_guards(
target_client: &relay_substrate_client::Client<relay_rococo_client::Rococo>,
transaction_params: &TransactionParams<sp_core::sr25519::Pair>,
) {
relay_substrate_client::guard::abort_on_spec_version_change(
self.finality_pipeline.target_client.clone(),
target_client.clone(),
bp_rococo::VERSION.spec_version,
);
relay_substrate_client::guard::abort_when_account_balance_decreased(
self.finality_pipeline.target_client.clone(),
self.transactions_author(),
target_client.clone(),
transaction_params.transactions_signer.public().into(),
MAXIMAL_BALANCE_DECREASE_PER_DAY,
);
}
fn transactions_author(&self) -> bp_rococo::AccountId {
(*self.finality_pipeline.target_sign.public().as_array_ref()).into()
}
fn make_submit_finality_proof_transaction(
&self,
era: bp_runtime::TransactionEraOf<Rococo>,
transaction_nonce: IndexOf<Rococo>,
header: WococoSyncHeader,
proof: GrandpaJustification<bp_wococo::Header>,
) -> Bytes {
let call = relay_rococo_client::runtime::Call::BridgeGrandpaWococo(
relay_rococo_client::runtime::BridgeGrandpaWococoCall::submit_finality_proof(
Box::new(header.into_inner()),
proof,
),
);
let genesis_hash = *self.finality_pipeline.target_client.genesis_hash();
let transaction = Rococo::sign_transaction(
genesis_hash,
&self.finality_pipeline.target_sign,
era,
UnsignedTransaction::new(call, transaction_nonce),
);
Bytes(transaction.encode())
}
}
#[cfg(test)]
@@ -121,18 +121,22 @@ impl RelayHeaders {
let target_client = self.target.to_client::<Target>().await?;
let target_transactions_mortality = self.target_sign.target_transactions_mortality;
let target_sign = self.target_sign.to_keypair::<Target>()?;
let metrics_params = Finality::customize_metrics(self.prometheus_params.into())?;
let metrics_params: relay_utils::metrics::MetricsParams = self.prometheus_params.into();
GlobalMetrics::new()?.register_and_spawn(&metrics_params.registry)?;
let finality = Finality::new(target_client.clone(), target_sign);
finality.start_relay_guards();
let target_transactions_params =
substrate_relay_helper::finality_pipeline::TransactionParams {
transactions_signer: target_sign,
transactions_mortality: target_transactions_mortality,
};
Finality::start_relay_guards(&target_client, &target_transactions_params);
substrate_relay_helper::finality_pipeline::run(
finality,
substrate_relay_helper::finality_pipeline::run::<Finality>(
source_client,
target_client,
self.only_mandatory_headers,
target_transactions_mortality,
target_transactions_params,
metrics_params,
)
.await
@@ -489,25 +489,35 @@ impl RelayHeadersAndMessages {
}
// start on-demand header relays
let left_to_right_finality =
LeftToRightFinality::new(right_client.clone(), right_sign.clone());
let right_to_left_finality =
RightToLeftFinality::new(left_client.clone(), left_sign.clone());
left_to_right_finality.start_relay_guards();
right_to_left_finality.start_relay_guards();
let left_to_right_on_demand_headers = OnDemandHeadersRelay::new(
let left_to_right_transaction_params =
substrate_relay_helper::finality_pipeline::TransactionParams {
transactions_mortality: right_transactions_mortality,
transactions_signer: right_sign.clone(),
};
let right_to_left_transaction_params =
substrate_relay_helper::finality_pipeline::TransactionParams {
transactions_mortality: left_transactions_mortality,
transactions_signer: left_sign.clone(),
};
LeftToRightFinality::start_relay_guards(
&right_client,
&left_to_right_transaction_params,
);
RightToLeftFinality::start_relay_guards(
&left_client,
&right_to_left_transaction_params,
);
let left_to_right_on_demand_headers = OnDemandHeadersRelay::new::<LeftToRightFinality>(
left_client.clone(),
right_client.clone(),
right_transactions_mortality,
left_to_right_finality,
left_to_right_transaction_params,
MAX_MISSING_LEFT_HEADERS_AT_RIGHT,
params.shared.only_mandatory_headers,
);
let right_to_left_on_demand_headers = OnDemandHeadersRelay::new(
let right_to_left_on_demand_headers = OnDemandHeadersRelay::new::<RightToLeftFinality>(
right_client.clone(),
left_client.clone(),
left_transactions_mortality,
right_to_left_finality,
right_to_left_transaction_params,
MAX_MISSING_RIGHT_HEADERS_AT_LEFT,
params.shared.only_mandatory_headers,
);
+2
View File
@@ -48,6 +48,8 @@ impl ChainBase for Kusama {
impl Chain for Kusama {
const NAME: &'static str = "Kusama";
const BEST_FINALIZED_HEADER_ID_METHOD: &'static str =
bp_kusama::BEST_FINALIZED_KUSAMA_HEADER_METHOD;
const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(6);
const STORAGE_PROOF_OVERHEAD: u32 = bp_kusama::EXTRA_STORAGE_PROOF_SIZE;
const MAXIMAL_ENCODED_ACCOUNT_ID_SIZE: u32 = bp_kusama::MAXIMAL_ENCODED_ACCOUNT_ID_SIZE;
+2
View File
@@ -46,6 +46,8 @@ impl ChainBase for Millau {
impl Chain for Millau {
const NAME: &'static str = "Millau";
const BEST_FINALIZED_HEADER_ID_METHOD: &'static str =
bp_millau::BEST_FINALIZED_MILLAU_HEADER_METHOD;
const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(5);
const STORAGE_PROOF_OVERHEAD: u32 = bp_millau::EXTRA_STORAGE_PROOF_SIZE;
const MAXIMAL_ENCODED_ACCOUNT_ID_SIZE: u32 = bp_millau::MAXIMAL_ENCODED_ACCOUNT_ID_SIZE;
@@ -48,6 +48,8 @@ impl ChainBase for Polkadot {
impl Chain for Polkadot {
const NAME: &'static str = "Polkadot";
const BEST_FINALIZED_HEADER_ID_METHOD: &'static str =
bp_polkadot::BEST_FINALIZED_POLKADOT_HEADER_METHOD;
const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(6);
const STORAGE_PROOF_OVERHEAD: u32 = bp_polkadot::EXTRA_STORAGE_PROOF_SIZE;
const MAXIMAL_ENCODED_ACCOUNT_ID_SIZE: u32 = bp_polkadot::MAXIMAL_ENCODED_ACCOUNT_ID_SIZE;
@@ -41,6 +41,8 @@ impl ChainBase for RialtoParachain {
impl Chain for RialtoParachain {
const NAME: &'static str = "RialtoParachain";
// should be fixed/changed in https://github.com/paritytech/parity-bridges-common/pull/1199
const BEST_FINALIZED_HEADER_ID_METHOD: &'static str = "<UNIMPLEMENTED>";
const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(5);
const STORAGE_PROOF_OVERHEAD: u32 = bp_rialto::EXTRA_STORAGE_PROOF_SIZE;
const MAXIMAL_ENCODED_ACCOUNT_ID_SIZE: u32 = bp_rialto::MAXIMAL_ENCODED_ACCOUNT_ID_SIZE;
+2
View File
@@ -46,6 +46,8 @@ impl ChainBase for Rialto {
impl Chain for Rialto {
const NAME: &'static str = "Rialto";
const BEST_FINALIZED_HEADER_ID_METHOD: &'static str =
bp_rialto::BEST_FINALIZED_RIALTO_HEADER_METHOD;
const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(5);
const STORAGE_PROOF_OVERHEAD: u32 = bp_rialto::EXTRA_STORAGE_PROOF_SIZE;
const MAXIMAL_ENCODED_ACCOUNT_ID_SIZE: u32 = bp_rialto::MAXIMAL_ENCODED_ACCOUNT_ID_SIZE;
+2
View File
@@ -51,6 +51,8 @@ impl ChainBase for Rococo {
impl Chain for Rococo {
const NAME: &'static str = "Rococo";
const BEST_FINALIZED_HEADER_ID_METHOD: &'static str =
bp_rococo::BEST_FINALIZED_ROCOCO_HEADER_METHOD;
const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(6);
const STORAGE_PROOF_OVERHEAD: u32 = bp_rococo::EXTRA_STORAGE_PROOF_SIZE;
const MAXIMAL_ENCODED_ACCOUNT_ID_SIZE: u32 = bp_rococo::MAXIMAL_ENCODED_ACCOUNT_ID_SIZE;
+11 -1
View File
@@ -32,6 +32,13 @@ use std::{fmt::Debug, time::Duration};
pub trait Chain: ChainBase + Clone {
/// Chain name.
const NAME: &'static str;
/// Name of the runtime API method that is returning best known finalized header number
/// and hash (as tuple).
///
/// Keep in mind that this method is normally provided by the other chain, which is
/// bridged with this chain.
const BEST_FINALIZED_HEADER_ID_METHOD: &'static str;
/// Average block interval.
///
/// How often blocks are produced on that chain. It's suggested to set this value
@@ -45,7 +52,7 @@ pub trait Chain: ChainBase + Clone {
/// Block type.
type SignedBlock: Member + Serialize + DeserializeOwned + BlockWithJustification<Self::Header>;
/// The aggregated `Call` type.
type Call: Clone + Dispatchable + Debug;
type Call: Clone + Dispatchable + Debug + Send;
/// Type that is used by the chain, to convert from weight to fee.
type WeightToFee: WeightToFeePolynomial<Balance = Self::Balance>;
@@ -102,6 +109,9 @@ impl<C: Chain> UnsignedTransaction<C> {
}
}
/// Account key pair used by transactions signing scheme.
pub type AccountKeyPairOf<S> = <S as TransactionSignScheme>::AccountKeyPair;
/// Substrate-based chain transactions signing scheme.
pub trait TransactionSignScheme {
/// Chain that this scheme is to be used.
@@ -206,6 +206,7 @@ mod tests {
impl Chain for TestChain {
const NAME: &'static str = "Test";
const BEST_FINALIZED_HEADER_ID_METHOD: &'static str = "BestTestHeader";
const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_millis(1);
const STORAGE_PROOF_OVERHEAD: u32 = 0;
const MAXIMAL_ENCODED_ACCOUNT_ID_SIZE: u32 = 0;
+2 -3
View File
@@ -24,7 +24,6 @@ mod error;
mod rpc;
mod sync_header;
pub mod finality_source;
pub mod guard;
pub mod metrics;
@@ -32,8 +31,8 @@ use std::time::Duration;
pub use crate::{
chain::{
BlockWithJustification, CallOf, Chain, ChainWithBalances, TransactionSignScheme,
TransactionStatusOf, UnsignedTransaction, WeightToFeeOf,
AccountKeyPairOf, BlockWithJustification, CallOf, Chain, ChainWithBalances,
TransactionSignScheme, TransactionStatusOf, UnsignedTransaction, WeightToFeeOf,
},
client::{Client, OpaqueGrandpaAuthoritiesSet, Subscription},
error::{Error, Result},
+2
View File
@@ -44,6 +44,8 @@ impl ChainBase for Westend {
impl Chain for Westend {
const NAME: &'static str = "Westend";
const BEST_FINALIZED_HEADER_ID_METHOD: &'static str =
bp_westend::BEST_FINALIZED_WESTEND_HEADER_METHOD;
const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(6);
const STORAGE_PROOF_OVERHEAD: u32 = bp_westend::EXTRA_STORAGE_PROOF_SIZE;
const MAXIMAL_ENCODED_ACCOUNT_ID_SIZE: u32 = bp_westend::MAXIMAL_ENCODED_ACCOUNT_ID_SIZE;
+2
View File
@@ -51,6 +51,8 @@ impl ChainBase for Wococo {
impl Chain for Wococo {
const NAME: &'static str = "Wococo";
const BEST_FINALIZED_HEADER_ID_METHOD: &'static str =
bp_wococo::BEST_FINALIZED_WOCOCO_HEADER_METHOD;
const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(6);
const STORAGE_PROOF_OVERHEAD: u32 = bp_wococo::EXTRA_STORAGE_PROOF_SIZE;
const MAXIMAL_ENCODED_ACCOUNT_ID_SIZE: u32 = bp_wococo::MAXIMAL_ENCODED_ACCOUNT_ID_SIZE;
@@ -27,6 +27,7 @@ relay-utils = { path = "../utils" }
messages-relay = { path = "../messages" }
relay-substrate-client = { path = "../client-substrate" }
pallet-bridge-grandpa = { path = "../../modules/grandpa" }
pallet-bridge-messages = { path = "../../modules/messages" }
bp-runtime = { path = "../../primitives/runtime" }
@@ -14,18 +14,20 @@
// You should have received a copy of the GNU General Public License
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
//! Substrate-to-Substrate headers sync entrypoint.
//! Types and functions intended to ease adding of new Substrate -> Substrate
//! finality proofs synchronization pipelines.
use crate::{finality_target::SubstrateFinalityTarget, STALL_TIMEOUT};
use crate::{finality_source::SubstrateFinalitySource, finality_target::SubstrateFinalityTarget};
use bp_header_chain::justification::GrandpaJustification;
use bp_runtime::AccountIdOf;
use finality_relay::{FinalitySyncParams, FinalitySyncPipeline};
use finality_relay::FinalitySyncPipeline;
use pallet_bridge_grandpa::{Call as BridgeGrandpaCall, Config as BridgeGrandpaConfig};
use relay_substrate_client::{
finality_source::FinalitySource, BlockNumberOf, Chain, Client, HashOf, SyncHeader,
transaction_stall_timeout, AccountIdOf, AccountKeyPairOf, BlockNumberOf, CallOf, Chain, Client,
HashOf, HeaderOf, SyncHeader, TransactionSignScheme,
};
use relay_utils::{metrics::MetricsParams, BlockNumberBase};
use sp_core::Bytes;
use relay_utils::metrics::MetricsParams;
use sp_core::Pair;
use std::{fmt::Debug, marker::PhantomData};
/// Default limit of recent finality proofs.
@@ -34,130 +36,152 @@ use std::{fmt::Debug, marker::PhantomData};
/// Substrate+GRANDPA based chains (good to know).
pub(crate) const RECENT_FINALITY_PROOFS_LIMIT: usize = 4096;
/// Headers sync pipeline for Substrate <-> Substrate relays.
/// Submit-finality-proofs transaction creation parameters.
#[derive(Clone, Debug)]
pub struct TransactionParams<TS> {
/// Transactions author.
pub transactions_signer: TS,
/// Transactions mortality.
pub transactions_mortality: Option<u32>,
}
/// Substrate -> Substrate finality proofs synchronization pipeline.
pub trait SubstrateFinalitySyncPipeline: 'static + Clone + Debug + Send + Sync {
/// Pipeline for syncing finalized Source chain headers to Target chain.
type FinalitySyncPipeline: FinalitySyncPipeline;
/// Name of the runtime method that returns id of best finalized source header at target chain.
const BEST_FINALIZED_SOURCE_HEADER_ID_AT_TARGET: &'static str;
/// Chain with GRANDPA bridge pallet.
/// Headers of this chain are submitted to the `TargetChain`.
type SourceChain: Chain;
/// Headers of the `SourceChain` are submitted to this chain.
type TargetChain: Chain;
/// Customize metrics exposed by headers sync loop.
fn customize_metrics(params: MetricsParams) -> anyhow::Result<MetricsParams> {
Ok(params)
}
/// How submit finality proof call is built?
type SubmitFinalityProofCallBuilder: SubmitFinalityProofCallBuilder<Self>;
/// Scheme used to sign target chain transactions.
type TransactionSignScheme: TransactionSignScheme;
/// Start finality relay guards.
///
/// Different finality bridges may have different set of guards - e.g. on ephemeral chains we
/// don't need a version guards, on test chains we don't care that much about relayer account
/// balance, ... So the implementation is left to the specific bridges.
fn start_relay_guards(&self) {}
/// Returns id of account that we're using to sign transactions at target chain.
fn transactions_author(&self) -> AccountIdOf<Self::TargetChain>;
/// Make submit header transaction.
fn make_submit_finality_proof_transaction(
&self,
era: bp_runtime::TransactionEraOf<Self::TargetChain>,
transaction_nonce: bp_runtime::IndexOf<Self::TargetChain>,
header: <Self::FinalitySyncPipeline as FinalitySyncPipeline>::Header,
proof: <Self::FinalitySyncPipeline as FinalitySyncPipeline>::FinalityProof,
) -> Bytes;
}
/// Substrate-to-Substrate finality proof pipeline.
#[derive(Clone)]
pub struct SubstrateFinalityToSubstrate<SourceChain, TargetChain: Chain, TargetSign> {
/// Client for the target chain.
pub target_client: Client<TargetChain>,
/// Data required to sign target chain transactions.
pub target_sign: TargetSign,
/// Unused generic arguments dump.
_marker: PhantomData<SourceChain>,
}
impl<SourceChain, TargetChain: Chain, TargetSign> Debug
for SubstrateFinalityToSubstrate<SourceChain, TargetChain, TargetSign>
{
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
f.debug_struct("SubstrateFinalityToSubstrate")
.field("target_client", &self.target_client)
.finish()
/// Add relay guards if required.
fn start_relay_guards(
_target_client: &Client<Self::TargetChain>,
_transaction_params: &TransactionParams<AccountKeyPairOf<Self::TransactionSignScheme>>,
) {
}
}
impl<SourceChain, TargetChain: Chain, TargetSign>
SubstrateFinalityToSubstrate<SourceChain, TargetChain, TargetSign>
{
/// Create new Substrate-to-Substrate headers pipeline.
pub fn new(target_client: Client<TargetChain>, target_sign: TargetSign) -> Self {
SubstrateFinalityToSubstrate { target_client, target_sign, _marker: Default::default() }
}
/// Adapter that allows all `SubstrateFinalitySyncPipeline` to act as `FinalitySyncPipeline`.
#[derive(Clone, Debug)]
pub struct FinalitySyncPipelineAdapter<P: SubstrateFinalitySyncPipeline> {
_phantom: PhantomData<P>,
}
impl<SourceChain, TargetChain, TargetSign> FinalitySyncPipeline
for SubstrateFinalityToSubstrate<SourceChain, TargetChain, TargetSign>
impl<P: SubstrateFinalitySyncPipeline> FinalitySyncPipeline for FinalitySyncPipelineAdapter<P> {
const SOURCE_NAME: &'static str = P::SourceChain::NAME;
const TARGET_NAME: &'static str = P::TargetChain::NAME;
type Hash = HashOf<P::SourceChain>;
type Number = BlockNumberOf<P::SourceChain>;
type Header = relay_substrate_client::SyncHeader<HeaderOf<P::SourceChain>>;
type FinalityProof = GrandpaJustification<HeaderOf<P::SourceChain>>;
}
/// Different ways of building `submit_finality_proof` calls.
pub trait SubmitFinalityProofCallBuilder<P: SubstrateFinalitySyncPipeline> {
/// Given source chain header and its finality proofs, build call of `submit_finality_proof`
/// function of bridge GRANDPA module at the target chain.
fn build_submit_finality_proof_call(
header: SyncHeader<HeaderOf<P::SourceChain>>,
proof: GrandpaJustification<HeaderOf<P::SourceChain>>,
) -> CallOf<P::TargetChain>;
}
/// Building `submit_finality_proof` call when you have direct access to the target
/// chain runtime.
pub struct DirectSubmitFinalityProofCallBuilder<P, R, I> {
_phantom: PhantomData<(P, R, I)>,
}
impl<P, R, I> SubmitFinalityProofCallBuilder<P> for DirectSubmitFinalityProofCallBuilder<P, R, I>
where
SourceChain: Clone + Chain + Debug,
BlockNumberOf<SourceChain>: BlockNumberBase,
TargetChain: Clone + Chain + Debug,
TargetSign: 'static + Clone + Send + Sync,
P: SubstrateFinalitySyncPipeline,
R: BridgeGrandpaConfig<I>,
I: 'static,
R::BridgedChain: bp_runtime::Chain<Header = HeaderOf<P::SourceChain>>,
CallOf<P::TargetChain>: From<BridgeGrandpaCall<R, I>>,
{
const SOURCE_NAME: &'static str = SourceChain::NAME;
const TARGET_NAME: &'static str = TargetChain::NAME;
type Hash = HashOf<SourceChain>;
type Number = BlockNumberOf<SourceChain>;
type Header = SyncHeader<SourceChain::Header>;
type FinalityProof = GrandpaJustification<SourceChain::Header>;
fn build_submit_finality_proof_call(
header: SyncHeader<HeaderOf<P::SourceChain>>,
proof: GrandpaJustification<HeaderOf<P::SourceChain>>,
) -> CallOf<P::TargetChain> {
BridgeGrandpaCall::<R, I>::submit_finality_proof {
finality_target: Box::new(header.into_inner()),
justification: proof,
}
.into()
}
}
/// Run Substrate-to-Substrate finality sync.
pub async fn run<SourceChain, TargetChain, P>(
pipeline: P,
source_client: Client<SourceChain>,
target_client: Client<TargetChain>,
/// Macro that generates `SubmitFinalityProofCallBuilder` implementation for the case when
/// you only have an access to the mocked version of target chain runtime. In this case you
/// should provide "name" of the call variant for the bridge GRANDPA calls and the "name" of
/// the variant for the `submit_finality_proof` call within that first option.
#[rustfmt::skip]
#[macro_export]
macro_rules! generate_mocked_submit_finality_proof_call_builder {
($pipeline:ident, $mocked_builder:ident, $bridge_grandpa:path, $submit_finality_proof:path) => {
pub struct $mocked_builder;
impl $crate::finality_pipeline::SubmitFinalityProofCallBuilder<$pipeline>
for $mocked_builder
{
fn build_submit_finality_proof_call(
header: relay_substrate_client::SyncHeader<
relay_substrate_client::HeaderOf<
<$pipeline as $crate::finality_pipeline::SubstrateFinalitySyncPipeline>::SourceChain
>
>,
proof: bp_header_chain::justification::GrandpaJustification<
relay_substrate_client::HeaderOf<
<$pipeline as $crate::finality_pipeline::SubstrateFinalitySyncPipeline>::SourceChain
>
>,
) -> relay_substrate_client::CallOf<
<$pipeline as $crate::finality_pipeline::SubstrateFinalitySyncPipeline>::TargetChain
> {
$bridge_grandpa($submit_finality_proof(Box::new(header.into_inner()), proof))
}
}
};
}
/// Run Substrate-to-Substrate finality sync loop.
pub async fn run<P: SubstrateFinalitySyncPipeline>(
source_client: Client<P::SourceChain>,
target_client: Client<P::TargetChain>,
only_mandatory_headers: bool,
transactions_mortality: Option<u32>,
transaction_params: TransactionParams<AccountKeyPairOf<P::TransactionSignScheme>>,
metrics_params: MetricsParams,
) -> anyhow::Result<()>
where
P: SubstrateFinalitySyncPipeline<TargetChain = TargetChain>,
P::FinalitySyncPipeline: FinalitySyncPipeline<
Hash = HashOf<SourceChain>,
Number = BlockNumberOf<SourceChain>,
Header = SyncHeader<SourceChain::Header>,
FinalityProof = GrandpaJustification<SourceChain::Header>,
>,
SourceChain: Clone + Chain,
BlockNumberOf<SourceChain>: BlockNumberBase,
TargetChain: Clone + Chain,
AccountIdOf<P::TargetChain>: From<<AccountKeyPairOf<P::TransactionSignScheme> as Pair>::Public>,
P::TransactionSignScheme: TransactionSignScheme<Chain = P::TargetChain>,
{
log::info!(
target: "bridge",
"Starting {} -> {} finality proof relay",
SourceChain::NAME,
TargetChain::NAME,
P::SourceChain::NAME,
P::TargetChain::NAME,
);
finality_relay::run(
FinalitySource::new(source_client, None),
SubstrateFinalityTarget::new(target_client, pipeline, transactions_mortality),
FinalitySyncParams {
SubstrateFinalitySource::<P>::new(source_client, None),
SubstrateFinalityTarget::<P>::new(target_client, transaction_params.clone()),
finality_relay::FinalitySyncParams {
tick: std::cmp::max(
SourceChain::AVERAGE_BLOCK_INTERVAL,
TargetChain::AVERAGE_BLOCK_INTERVAL,
P::SourceChain::AVERAGE_BLOCK_INTERVAL,
P::TargetChain::AVERAGE_BLOCK_INTERVAL,
),
recent_finality_proofs_limit: RECENT_FINALITY_PROOFS_LIMIT,
stall_timeout: relay_substrate_client::transaction_stall_timeout(
transactions_mortality,
TargetChain::AVERAGE_BLOCK_INTERVAL,
STALL_TIMEOUT,
stall_timeout: transaction_stall_timeout(
transaction_params.transactions_mortality,
P::TargetChain::AVERAGE_BLOCK_INTERVAL,
crate::STALL_TIMEOUT,
),
only_mandatory_headers,
},
@@ -16,49 +16,59 @@
//! Default generic implementation of finality source for basic Substrate client.
use crate::{
chain::{BlockWithJustification, Chain},
client::Client,
error::Error,
sync_header::SyncHeader,
};
use crate::finality_pipeline::{FinalitySyncPipelineAdapter, SubstrateFinalitySyncPipeline};
use async_std::sync::{Arc, Mutex};
use async_trait::async_trait;
use bp_header_chain::justification::GrandpaJustification;
use codec::Decode;
use finality_relay::{FinalitySyncPipeline, SourceClient, SourceHeader};
use finality_relay::SourceClient;
use futures::stream::{unfold, Stream, StreamExt};
use relay_substrate_client::{
BlockNumberOf, BlockWithJustification, Chain, Client, Error, HeaderOf,
};
use relay_utils::relay_loop::Client as RelayClient;
use sp_runtime::traits::Header as HeaderT;
use std::{marker::PhantomData, pin::Pin};
use std::pin::Pin;
/// Shared updatable reference to the maximal header number that we want to sync from the source.
pub type RequiredHeaderNumberRef<C> = Arc<Mutex<<C as bp_runtime::Chain>::BlockNumber>>;
/// Substrate finality proofs stream.
pub type SubstrateFinalityProofsStream<P> = Pin<
Box<
dyn Stream<
Item = GrandpaJustification<
HeaderOf<<P as SubstrateFinalitySyncPipeline>::SourceChain>,
>,
> + Send,
>,
>;
/// Substrate node as finality source.
pub struct FinalitySource<C: Chain, P> {
client: Client<C>,
maximal_header_number: Option<RequiredHeaderNumberRef<C>>,
_phantom: PhantomData<P>,
pub struct SubstrateFinalitySource<P: SubstrateFinalitySyncPipeline> {
client: Client<P::SourceChain>,
maximal_header_number: Option<RequiredHeaderNumberRef<P::SourceChain>>,
}
impl<C: Chain, P> FinalitySource<C, P> {
impl<P: SubstrateFinalitySyncPipeline> SubstrateFinalitySource<P> {
/// Create new headers source using given client.
pub fn new(
client: Client<C>,
maximal_header_number: Option<RequiredHeaderNumberRef<C>>,
client: Client<P::SourceChain>,
maximal_header_number: Option<RequiredHeaderNumberRef<P::SourceChain>>,
) -> Self {
FinalitySource { client, maximal_header_number, _phantom: Default::default() }
SubstrateFinalitySource { client, maximal_header_number }
}
/// Returns reference to the underlying RPC client.
pub fn client(&self) -> &Client<C> {
pub fn client(&self) -> &Client<P::SourceChain> {
&self.client
}
/// Returns best finalized block number.
pub async fn on_chain_best_finalized_block_number(&self) -> Result<C::BlockNumber, Error> {
pub async fn on_chain_best_finalized_block_number(
&self,
) -> Result<BlockNumberOf<P::SourceChain>, Error> {
// we **CAN** continue to relay finality proofs if source node is out of sync, because
// target node may be missing proofs that are already available at the source
let finalized_header_hash = self.client.best_finalized_header_hash().await?;
@@ -67,18 +77,17 @@ impl<C: Chain, P> FinalitySource<C, P> {
}
}
impl<C: Chain, P> Clone for FinalitySource<C, P> {
impl<P: SubstrateFinalitySyncPipeline> Clone for SubstrateFinalitySource<P> {
fn clone(&self) -> Self {
FinalitySource {
SubstrateFinalitySource {
client: self.client.clone(),
maximal_header_number: self.maximal_header_number.clone(),
_phantom: Default::default(),
}
}
}
#[async_trait]
impl<C: Chain, P: FinalitySyncPipeline> RelayClient for FinalitySource<C, P> {
impl<P: SubstrateFinalitySyncPipeline> RelayClient for SubstrateFinalitySource<P> {
type Error = Error;
async fn reconnect(&mut self) -> Result<(), Error> {
@@ -87,21 +96,12 @@ impl<C: Chain, P: FinalitySyncPipeline> RelayClient for FinalitySource<C, P> {
}
#[async_trait]
impl<C, P> SourceClient<P> for FinalitySource<C, P>
where
C: Chain,
C::BlockNumber: relay_utils::BlockNumberBase,
P: FinalitySyncPipeline<
Hash = C::Hash,
Number = C::BlockNumber,
Header = SyncHeader<C::Header>,
FinalityProof = GrandpaJustification<C::Header>,
>,
P::Header: SourceHeader<C::BlockNumber>,
impl<P: SubstrateFinalitySyncPipeline> SourceClient<FinalitySyncPipelineAdapter<P>>
for SubstrateFinalitySource<P>
{
type FinalityProofsStream = Pin<Box<dyn Stream<Item = GrandpaJustification<C::Header>> + Send>>;
type FinalityProofsStream = SubstrateFinalityProofsStream<P>;
async fn best_finalized_block_number(&self) -> Result<P::Number, Error> {
async fn best_finalized_block_number(&self) -> Result<BlockNumberOf<P::SourceChain>, Error> {
let mut finalized_header_number = self.on_chain_best_finalized_block_number().await?;
// never return block number larger than requested. This way we'll never sync headers
// past `maximal_header_number`
@@ -116,15 +116,23 @@ where
async fn header_and_finality_proof(
&self,
number: P::Number,
) -> Result<(P::Header, Option<P::FinalityProof>), Error> {
number: BlockNumberOf<P::SourceChain>,
) -> Result<
(
relay_substrate_client::SyncHeader<HeaderOf<P::SourceChain>>,
Option<GrandpaJustification<HeaderOf<P::SourceChain>>>,
),
Error,
> {
let header_hash = self.client.block_hash_by_number(number).await?;
let signed_block = self.client.get_block(Some(header_hash)).await?;
let justification = signed_block
.justification()
.map(|raw_justification| {
GrandpaJustification::<C::Header>::decode(&mut raw_justification.as_slice())
GrandpaJustification::<HeaderOf<P::SourceChain>>::decode(
&mut raw_justification.as_slice(),
)
})
.transpose()
.map_err(Error::ResponseParseFailed)?;
@@ -141,7 +149,7 @@ where
log::error!(
target: "bridge",
"Failed to read justification target from the {} justifications stream: {:?}",
P::SOURCE_NAME,
P::SourceChain::NAME,
err,
);
};
@@ -153,7 +161,9 @@ where
.ok()??;
let decoded_justification =
GrandpaJustification::<C::Header>::decode(&mut &next_justification[..]);
GrandpaJustification::<HeaderOf<P::SourceChain>>::decode(
&mut &next_justification[..],
);
let justification = match decoded_justification {
Ok(j) => j,
@@ -15,70 +15,78 @@
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
//! Substrate client as Substrate finality proof target. The chain we connect to should have
//! runtime that implements `<BridgedChainName>FinalityApi` to allow bridging with
//! <BridgedName> chain.
//! bridge GRANDPA pallet deployed and provide `<BridgedChainName>FinalityApi` to allow bridging
//! with <BridgedName> chain.
use crate::finality_pipeline::SubstrateFinalitySyncPipeline;
use crate::finality_pipeline::{
FinalitySyncPipelineAdapter, SubmitFinalityProofCallBuilder, SubstrateFinalitySyncPipeline,
TransactionParams,
};
use async_trait::async_trait;
use codec::Decode;
use finality_relay::{FinalitySyncPipeline, TargetClient};
use relay_substrate_client::{Chain, Client, Error as SubstrateError};
use bp_header_chain::justification::GrandpaJustification;
use codec::Encode;
use finality_relay::TargetClient;
use relay_substrate_client::{
AccountIdOf, AccountKeyPairOf, BlockNumberOf, Chain, Client, Error, HashOf, HeaderOf,
SyncHeader, TransactionEra, TransactionSignScheme, UnsignedTransaction,
};
use relay_utils::relay_loop::Client as RelayClient;
use sp_core::{Bytes, Pair};
/// Substrate client as Substrate finality target.
pub struct SubstrateFinalityTarget<C: Chain, P> {
client: Client<C>,
pipeline: P,
transactions_mortality: Option<u32>,
pub struct SubstrateFinalityTarget<P: SubstrateFinalitySyncPipeline> {
client: Client<P::TargetChain>,
transaction_params: TransactionParams<AccountKeyPairOf<P::TransactionSignScheme>>,
}
impl<C: Chain, P> SubstrateFinalityTarget<C, P> {
impl<P: SubstrateFinalitySyncPipeline> SubstrateFinalityTarget<P> {
/// Create new Substrate headers target.
pub fn new(client: Client<C>, pipeline: P, transactions_mortality: Option<u32>) -> Self {
SubstrateFinalityTarget { client, pipeline, transactions_mortality }
pub fn new(
client: Client<P::TargetChain>,
transaction_params: TransactionParams<AccountKeyPairOf<P::TransactionSignScheme>>,
) -> Self {
SubstrateFinalityTarget { client, transaction_params }
}
}
impl<C: Chain, P: SubstrateFinalitySyncPipeline> Clone for SubstrateFinalityTarget<C, P> {
impl<P: SubstrateFinalitySyncPipeline> Clone for SubstrateFinalityTarget<P> {
fn clone(&self) -> Self {
SubstrateFinalityTarget {
client: self.client.clone(),
pipeline: self.pipeline.clone(),
transactions_mortality: self.transactions_mortality,
transaction_params: self.transaction_params.clone(),
}
}
}
#[async_trait]
impl<C: Chain, P: SubstrateFinalitySyncPipeline> RelayClient for SubstrateFinalityTarget<C, P> {
type Error = SubstrateError;
impl<P: SubstrateFinalitySyncPipeline> RelayClient for SubstrateFinalityTarget<P> {
type Error = Error;
async fn reconnect(&mut self) -> Result<(), SubstrateError> {
async fn reconnect(&mut self) -> Result<(), Error> {
self.client.reconnect().await
}
}
#[async_trait]
impl<C, P> TargetClient<P::FinalitySyncPipeline> for SubstrateFinalityTarget<C, P>
impl<P: SubstrateFinalitySyncPipeline> TargetClient<FinalitySyncPipelineAdapter<P>>
for SubstrateFinalityTarget<P>
where
C: Chain,
P: SubstrateFinalitySyncPipeline<TargetChain = C>,
<P::FinalitySyncPipeline as FinalitySyncPipeline>::Number: Decode,
<P::FinalitySyncPipeline as FinalitySyncPipeline>::Hash: Decode,
AccountIdOf<P::TargetChain>: From<<AccountKeyPairOf<P::TransactionSignScheme> as Pair>::Public>,
P::TransactionSignScheme: TransactionSignScheme<Chain = P::TargetChain>,
{
async fn best_finalized_source_block_number(
&self,
) -> Result<<P::FinalitySyncPipeline as FinalitySyncPipeline>::Number, SubstrateError> {
) -> Result<BlockNumberOf<P::SourceChain>, Error> {
// we can't continue to relay finality if target node is out of sync, because
// it may have already received (some of) headers that we're going to relay
self.client.ensure_synced().await?;
Ok(crate::messages_source::read_client_state::<
C,
<P::FinalitySyncPipeline as FinalitySyncPipeline>::Hash,
<P::FinalitySyncPipeline as FinalitySyncPipeline>::Number,
>(&self.client, P::BEST_FINALIZED_SOURCE_HEADER_ID_AT_TARGET)
P::TargetChain,
HashOf<P::SourceChain>,
BlockNumberOf<P::SourceChain>,
>(&self.client, P::SourceChain::BEST_FINALIZED_HEADER_ID_METHOD)
.await?
.best_finalized_peer_at_best_self
.0)
@@ -86,24 +94,28 @@ where
async fn submit_finality_proof(
&self,
header: <P::FinalitySyncPipeline as FinalitySyncPipeline>::Header,
proof: <P::FinalitySyncPipeline as FinalitySyncPipeline>::FinalityProof,
) -> Result<(), SubstrateError> {
let transactions_author = self.pipeline.transactions_author();
let pipeline = self.pipeline.clone();
let transactions_mortality = self.transactions_mortality;
header: SyncHeader<HeaderOf<P::SourceChain>>,
proof: GrandpaJustification<HeaderOf<P::SourceChain>>,
) -> Result<(), Error> {
let genesis_hash = *self.client.genesis_hash();
let transaction_params = self.transaction_params.clone();
let call =
P::SubmitFinalityProofCallBuilder::build_submit_finality_proof_call(header, proof);
self.client
.submit_signed_extrinsic(
transactions_author,
self.transaction_params.transactions_signer.public().into(),
move |best_block_id, transaction_nonce| {
pipeline.make_submit_finality_proof_transaction(
relay_substrate_client::TransactionEra::new(
Bytes(
P::TransactionSignScheme::sign_transaction(
genesis_hash,
&transaction_params.transactions_signer,
TransactionEra::new(
best_block_id,
transactions_mortality,
transaction_params.transactions_mortality,
),
transaction_nonce,
header,
proof,
UnsignedTransaction::new(call, transaction_nonce),
)
.encode(),
)
},
)
@@ -23,6 +23,7 @@ use std::time::Duration;
pub mod conversion_rate_update;
pub mod error;
pub mod finality_pipeline;
pub mod finality_source;
pub mod finality_target;
pub mod headers_initialize;
pub mod helpers;
@@ -16,29 +16,24 @@
//! On-demand Substrate -> Substrate headers relay.
use std::fmt::Debug;
use async_std::sync::{Arc, Mutex};
use futures::{select, FutureExt};
use num_traits::{CheckedSub, One, Zero};
use finality_relay::{
FinalitySyncParams, FinalitySyncPipeline, SourceClient as FinalitySourceClient, SourceHeader,
TargetClient as FinalityTargetClient,
};
use finality_relay::{FinalitySyncParams, SourceHeader, TargetClient as FinalityTargetClient};
use relay_substrate_client::{
finality_source::{FinalitySource as SubstrateFinalitySource, RequiredHeaderNumberRef},
Chain, Client, HeaderIdOf, SyncHeader,
AccountIdOf, AccountKeyPairOf, BlockNumberOf, Chain, Client, HeaderIdOf, HeaderOf, SyncHeader,
TransactionSignScheme,
};
use relay_utils::{
metrics::MetricsParams, relay_loop::Client as RelayClient, BlockNumberBase, FailedClient,
MaybeConnectionError,
metrics::MetricsParams, relay_loop::Client as RelayClient, FailedClient, MaybeConnectionError,
};
use crate::{
finality_pipeline::{
SubstrateFinalitySyncPipeline, SubstrateFinalityToSubstrate, RECENT_FINALITY_PROOFS_LIMIT,
SubstrateFinalitySyncPipeline, TransactionParams, RECENT_FINALITY_PROOFS_LIMIT,
},
finality_source::{RequiredHeaderNumberRef, SubstrateFinalitySource},
finality_target::SubstrateFinalityTarget,
STALL_TIMEOUT,
};
@@ -58,40 +53,28 @@ pub struct OnDemandHeadersRelay<SourceChain: Chain> {
impl<SourceChain: Chain> OnDemandHeadersRelay<SourceChain> {
/// Create new on-demand headers relay.
pub fn new<TargetChain: Chain, TargetSign, P>(
source_client: Client<SourceChain>,
target_client: Client<TargetChain>,
target_transactions_mortality: Option<u32>,
pipeline: P,
maximal_headers_difference: SourceChain::BlockNumber,
pub fn new<P: SubstrateFinalitySyncPipeline<SourceChain = SourceChain>>(
source_client: Client<P::SourceChain>,
target_client: Client<P::TargetChain>,
target_transaction_params: TransactionParams<AccountKeyPairOf<P::TransactionSignScheme>>,
maximal_headers_difference: BlockNumberOf<P::SourceChain>,
only_mandatory_headers: bool,
) -> Self
where
SourceChain: Chain + Debug,
SourceChain::BlockNumber: BlockNumberBase,
TargetChain: Chain + Debug,
TargetChain::BlockNumber: BlockNumberBase,
TargetSign: Clone + Send + Sync + 'static,
P: SubstrateFinalitySyncPipeline<
FinalitySyncPipeline = SubstrateFinalityToSubstrate<
SourceChain,
TargetChain,
TargetSign,
>,
TargetChain = TargetChain,
>,
AccountIdOf<P::TargetChain>:
From<<AccountKeyPairOf<P::TransactionSignScheme> as sp_core::Pair>::Public>,
P::TransactionSignScheme: TransactionSignScheme<Chain = P::TargetChain>,
{
let required_header_number = Arc::new(Mutex::new(Zero::zero()));
let this = OnDemandHeadersRelay {
relay_task_name: on_demand_headers_relay_name::<SourceChain, TargetChain>(),
relay_task_name: on_demand_headers_relay_name::<P::SourceChain, P::TargetChain>(),
required_header_number: required_header_number.clone(),
};
async_std::task::spawn(async move {
background_task(
background_task::<P>(
source_client,
target_client,
target_transactions_mortality,
pipeline,
target_transaction_params,
maximal_headers_difference,
only_mandatory_headers,
required_header_number,
@@ -120,35 +103,25 @@ impl<SourceChain: Chain> OnDemandHeadersRelay<SourceChain> {
}
/// Background task that is responsible for starting headers relay.
async fn background_task<SourceChain, TargetChain, TargetSign, P>(
source_client: Client<SourceChain>,
target_client: Client<TargetChain>,
target_transactions_mortality: Option<u32>,
pipeline: P,
maximal_headers_difference: SourceChain::BlockNumber,
async fn background_task<P: SubstrateFinalitySyncPipeline>(
source_client: Client<P::SourceChain>,
target_client: Client<P::TargetChain>,
target_transaction_params: TransactionParams<AccountKeyPairOf<P::TransactionSignScheme>>,
maximal_headers_difference: BlockNumberOf<P::SourceChain>,
only_mandatory_headers: bool,
required_header_number: RequiredHeaderNumberRef<SourceChain>,
required_header_number: RequiredHeaderNumberRef<P::SourceChain>,
) where
SourceChain: Chain + Debug,
SourceChain::BlockNumber: BlockNumberBase,
TargetChain: Chain + Debug,
TargetChain::BlockNumber: BlockNumberBase,
TargetSign: Clone + Send + Sync + 'static,
P: SubstrateFinalitySyncPipeline<
FinalitySyncPipeline = SubstrateFinalityToSubstrate<SourceChain, TargetChain, TargetSign>,
TargetChain = TargetChain,
>,
AccountIdOf<P::TargetChain>:
From<<AccountKeyPairOf<P::TransactionSignScheme> as sp_core::Pair>::Public>,
P::TransactionSignScheme: TransactionSignScheme<Chain = P::TargetChain>,
{
let relay_task_name = on_demand_headers_relay_name::<SourceChain, TargetChain>();
let mut finality_source = SubstrateFinalitySource::<
_,
SubstrateFinalityToSubstrate<SourceChain, TargetChain, TargetSign>,
>::new(source_client.clone(), Some(required_header_number.clone()));
let mut finality_target = SubstrateFinalityTarget::new(
target_client.clone(),
pipeline.clone(),
target_transactions_mortality,
let relay_task_name = on_demand_headers_relay_name::<P::SourceChain, P::TargetChain>();
let mut finality_source = SubstrateFinalitySource::<P>::new(
source_client.clone(),
Some(required_header_number.clone()),
);
let mut finality_target =
SubstrateFinalityTarget::new(target_client.clone(), target_transaction_params);
let mut latest_non_mandatory_at_source = Zero::zero();
let mut restart_relay = true;
@@ -157,7 +130,7 @@ async fn background_task<SourceChain, TargetChain, TargetSign, P>(
loop {
select! {
_ = async_std::task::sleep(TargetChain::AVERAGE_BLOCK_INTERVAL).fuse() => {},
_ = async_std::task::sleep(P::TargetChain::AVERAGE_BLOCK_INTERVAL).fuse() => {},
_ = finality_relay_task => {
// this should never happen in practice given the current code
restart_relay = true;
@@ -179,12 +152,8 @@ async fn background_task<SourceChain, TargetChain, TargetSign, P>(
}
// read best finalized source header number from target
let best_finalized_source_header_at_target = best_finalized_source_header_at_target::<
SourceChain,
_,
_,
>(&finality_target, &relay_task_name)
.await;
let best_finalized_source_header_at_target =
best_finalized_source_header_at_target::<P>(&finality_target, &relay_task_name).await;
if matches!(best_finalized_source_header_at_target, Err(ref e) if e.is_connection_error()) {
relay_utils::relay_loop::reconnect_failed_client(
FailedClient::Target,
@@ -199,7 +168,7 @@ async fn background_task<SourceChain, TargetChain, TargetSign, P>(
// submit mandatory header if some headers are missing
let best_finalized_source_header_at_target_fmt =
format!("{:?}", best_finalized_source_header_at_target);
let mandatory_scan_range = mandatory_headers_scan_range::<SourceChain>(
let mandatory_scan_range = mandatory_headers_scan_range::<P::SourceChain>(
best_finalized_source_header_at_source.ok(),
best_finalized_source_header_at_target.ok(),
maximal_headers_difference,
@@ -247,8 +216,8 @@ async fn background_task<SourceChain, TargetChain, TargetSign, P>(
finality_target.clone(),
FinalitySyncParams {
tick: std::cmp::max(
SourceChain::AVERAGE_BLOCK_INTERVAL,
TargetChain::AVERAGE_BLOCK_INTERVAL,
P::SourceChain::AVERAGE_BLOCK_INTERVAL,
P::TargetChain::AVERAGE_BLOCK_INTERVAL,
),
recent_finality_proofs_limit: RECENT_FINALITY_PROOFS_LIMIT,
stall_timeout: STALL_TIMEOUT,
@@ -316,17 +285,13 @@ async fn mandatory_headers_scan_range<C: Chain>(
/// it.
///
/// Returns `true` if header was found and (asked to be) relayed and `false` otherwise.
async fn relay_mandatory_header_from_range<SourceChain: Chain, P>(
finality_source: &SubstrateFinalitySource<SourceChain, P>,
required_header_number: &RequiredHeaderNumberRef<SourceChain>,
async fn relay_mandatory_header_from_range<P: SubstrateFinalitySyncPipeline>(
finality_source: &SubstrateFinalitySource<P>,
required_header_number: &RequiredHeaderNumberRef<P::SourceChain>,
best_finalized_source_header_at_target: String,
range: (SourceChain::BlockNumber, SourceChain::BlockNumber),
range: (BlockNumberOf<P::SourceChain>, BlockNumberOf<P::SourceChain>),
relay_task_name: &str,
) -> Result<bool, relay_substrate_client::Error>
where
SubstrateFinalitySource<SourceChain, P>: FinalitySourceClient<P>,
P: FinalitySyncPipeline<Number = SourceChain::BlockNumber>,
{
) -> Result<bool, relay_substrate_client::Error> {
// search for mandatory header first
let mandatory_source_header_number =
find_mandatory_header_in_range(finality_source, range).await?;
@@ -347,7 +312,7 @@ where
log::trace!(
target: "bridge",
"Too many {} headers missing at target in {} relay ({} vs {}). Going to sync up to the mandatory {}",
SourceChain::NAME,
P::SourceChain::NAME,
relay_task_name,
best_finalized_source_header_at_target,
range.1,
@@ -361,14 +326,10 @@ where
/// Read best finalized source block number from source client.
///
/// Returns `None` if we have failed to read the number.
async fn best_finalized_source_header_at_source<SourceChain: Chain, P>(
finality_source: &SubstrateFinalitySource<SourceChain, P>,
async fn best_finalized_source_header_at_source<P: SubstrateFinalitySyncPipeline>(
finality_source: &SubstrateFinalitySource<P>,
relay_task_name: &str,
) -> Result<SourceChain::BlockNumber, relay_substrate_client::Error>
where
SubstrateFinalitySource<SourceChain, P>: FinalitySourceClient<P>,
P: FinalitySyncPipeline<Number = SourceChain::BlockNumber>,
{
) -> Result<BlockNumberOf<P::SourceChain>, relay_substrate_client::Error> {
finality_source.on_chain_best_finalized_block_number().await.map_err(|error| {
log::error!(
target: "bridge",
@@ -384,14 +345,14 @@ where
/// Read best finalized source block number from target client.
///
/// Returns `None` if we have failed to read the number.
async fn best_finalized_source_header_at_target<SourceChain: Chain, TargetChain: Chain, P>(
finality_target: &SubstrateFinalityTarget<TargetChain, P>,
async fn best_finalized_source_header_at_target<P: SubstrateFinalitySyncPipeline>(
finality_target: &SubstrateFinalityTarget<P>,
relay_task_name: &str,
) -> Result<SourceChain::BlockNumber, <SubstrateFinalityTarget<TargetChain, P> as RelayClient>::Error>
) -> Result<BlockNumberOf<P::SourceChain>, <SubstrateFinalityTarget<P> as RelayClient>::Error>
where
SubstrateFinalityTarget<TargetChain, P>: FinalityTargetClient<P::FinalitySyncPipeline>,
P: SubstrateFinalitySyncPipeline,
P::FinalitySyncPipeline: FinalitySyncPipeline<Number = SourceChain::BlockNumber>,
AccountIdOf<P::TargetChain>:
From<<AccountKeyPairOf<P::TransactionSignScheme> as sp_core::Pair>::Public>,
P::TransactionSignScheme: TransactionSignScheme<Chain = P::TargetChain>,
{
finality_target.best_finalized_source_block_number().await.map_err(|error| {
log::error!(
@@ -408,17 +369,13 @@ where
/// Read first mandatory header in given inclusive range.
///
/// Returns `Ok(None)` if there were no mandatory headers in the range.
async fn find_mandatory_header_in_range<SourceChain: Chain, P>(
finality_source: &SubstrateFinalitySource<SourceChain, P>,
range: (SourceChain::BlockNumber, SourceChain::BlockNumber),
) -> Result<Option<SourceChain::BlockNumber>, relay_substrate_client::Error>
where
SubstrateFinalitySource<SourceChain, P>: FinalitySourceClient<P>,
P: FinalitySyncPipeline<Number = SourceChain::BlockNumber>,
{
async fn find_mandatory_header_in_range<P: SubstrateFinalitySyncPipeline>(
finality_source: &SubstrateFinalitySource<P>,
range: (BlockNumberOf<P::SourceChain>, BlockNumberOf<P::SourceChain>),
) -> Result<Option<BlockNumberOf<P::SourceChain>>, relay_substrate_client::Error> {
let mut current = range.0;
while current <= range.1 {
let header: SyncHeader<SourceChain::Header> =
let header: SyncHeader<HeaderOf<P::SourceChain>> =
finality_source.client().header_by_number(current).await?.into();
if header.is_mandatory() {
return Ok(Some(current))