From 42add7c61406f1fe0c7af98762d384682fc6dda3 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Thu, 14 Apr 2022 12:05:57 +0300 Subject: [PATCH] FinalityEngine in substrate relay (#1374) * introduce FinalityEngine in relay code * add FinalityEngine to relay * spelling * fix test compilation * Update relays/lib-substrate-relay/src/finality/source.rs Co-authored-by: Adrian Catangiu Co-authored-by: Adrian Catangiu --- .../src/chains/kusama_headers_to_polkadot.rs | 8 +- .../src/chains/millau_headers_to_rialto.rs | 8 +- .../src/chains/polkadot_headers_to_kusama.rs | 8 +- .../src/chains/rialto_headers_to_millau.rs | 8 +- .../src/chains/rococo_headers_to_wococo.rs | 8 +- .../src/chains/westend_headers_to_millau.rs | 8 +- .../src/chains/wococo_headers_to_rococo.rs | 8 +- .../bin-substrate/src/cli/init_bridge.rs | 10 +- .../bin-substrate/src/cli/reinit_bridge.rs | 12 +- .../bin-substrate/src/cli/relay_headers.rs | 4 +- .../src/cli/relay_headers_and_messages.rs | 2 +- bridges/relays/client-substrate/src/client.rs | 4 +- bridges/relays/client-substrate/src/error.rs | 6 +- .../src/metrics/storage_proof_overhead.rs | 2 +- .../src/finality/engine.rs | 226 ++++++++++++++ .../guards.rs} | 0 .../src/finality/initialize.rs | 121 ++++++++ .../{finality_pipeline.rs => finality/mod.rs} | 39 ++- .../source.rs} | 25 +- .../target.rs} | 25 +- .../src/headers_initialize.rs | 282 ------------------ bridges/relays/lib-substrate-relay/src/lib.rs | 6 +- .../src/on_demand_headers.rs | 8 +- 23 files changed, 469 insertions(+), 359 deletions(-) create mode 100644 bridges/relays/lib-substrate-relay/src/finality/engine.rs rename bridges/relays/lib-substrate-relay/src/{finality_guards.rs => finality/guards.rs} (100%) create mode 100644 bridges/relays/lib-substrate-relay/src/finality/initialize.rs rename bridges/relays/lib-substrate-relay/src/{finality_pipeline.rs => finality/mod.rs} (85%) rename bridges/relays/lib-substrate-relay/src/{finality_source.rs => finality/source.rs} (87%) rename bridges/relays/lib-substrate-relay/src/{finality_target.rs => finality/target.rs} (81%) delete mode 100644 bridges/relays/lib-substrate-relay/src/headers_initialize.rs diff --git a/bridges/relays/bin-substrate/src/chains/kusama_headers_to_polkadot.rs b/bridges/relays/bin-substrate/src/chains/kusama_headers_to_polkadot.rs index 0c0ba2272c7e9..97d2057209878 100644 --- a/bridges/relays/bin-substrate/src/chains/kusama_headers_to_polkadot.rs +++ b/bridges/relays/bin-substrate/src/chains/kusama_headers_to_polkadot.rs @@ -18,7 +18,10 @@ use async_trait::async_trait; use relay_polkadot_client::Polkadot; -use substrate_relay_helper::{finality_pipeline::SubstrateFinalitySyncPipeline, TransactionParams}; +use substrate_relay_helper::{ + finality::{engine::Grandpa as GrandpaFinalityEngine, SubstrateFinalitySyncPipeline}, + TransactionParams, +}; /// Maximal saturating difference between `balance(now)` and `balance(now-24h)` to treat /// relay as gone wild. @@ -47,6 +50,7 @@ impl SubstrateFinalitySyncPipeline for KusamaFinalityToPolkadot { type SourceChain = relay_kusama_client::Kusama; type TargetChain = Polkadot; + type FinalityEngine = GrandpaFinalityEngine; type SubmitFinalityProofCallBuilder = KusamaFinalityToPolkadotCallBuilder; type TransactionSignScheme = Polkadot; @@ -55,7 +59,7 @@ impl SubstrateFinalitySyncPipeline for KusamaFinalityToPolkadot { transaction_params: &TransactionParams, enable_version_guard: bool, ) -> relay_substrate_client::Result<()> { - substrate_relay_helper::finality_guards::start::( + substrate_relay_helper::finality::guards::start::( target_client, transaction_params, enable_version_guard, diff --git a/bridges/relays/bin-substrate/src/chains/millau_headers_to_rialto.rs b/bridges/relays/bin-substrate/src/chains/millau_headers_to_rialto.rs index 584f0a9bb1d8b..2fefc2e74c968 100644 --- a/bridges/relays/bin-substrate/src/chains/millau_headers_to_rialto.rs +++ b/bridges/relays/bin-substrate/src/chains/millau_headers_to_rialto.rs @@ -16,8 +16,9 @@ //! Millau-to-Rialto headers sync entrypoint. -use substrate_relay_helper::finality_pipeline::{ - DirectSubmitFinalityProofCallBuilder, SubstrateFinalitySyncPipeline, +use substrate_relay_helper::finality::{ + engine::Grandpa as GrandpaFinalityEngine, DirectSubmitGrandpaFinalityProofCallBuilder, + SubstrateFinalitySyncPipeline, }; /// Description of Millau -> Rialto finalized headers bridge. @@ -28,7 +29,8 @@ impl SubstrateFinalitySyncPipeline for MillauFinalityToRialto { type SourceChain = relay_millau_client::Millau; type TargetChain = relay_rialto_client::Rialto; - type SubmitFinalityProofCallBuilder = DirectSubmitFinalityProofCallBuilder< + type FinalityEngine = GrandpaFinalityEngine; + type SubmitFinalityProofCallBuilder = DirectSubmitGrandpaFinalityProofCallBuilder< Self, rialto_runtime::Runtime, rialto_runtime::MillauGrandpaInstance, diff --git a/bridges/relays/bin-substrate/src/chains/polkadot_headers_to_kusama.rs b/bridges/relays/bin-substrate/src/chains/polkadot_headers_to_kusama.rs index 6d118b07caa5b..a8b6ce07ab6c9 100644 --- a/bridges/relays/bin-substrate/src/chains/polkadot_headers_to_kusama.rs +++ b/bridges/relays/bin-substrate/src/chains/polkadot_headers_to_kusama.rs @@ -18,7 +18,10 @@ use async_trait::async_trait; use relay_kusama_client::Kusama; -use substrate_relay_helper::{finality_pipeline::SubstrateFinalitySyncPipeline, TransactionParams}; +use substrate_relay_helper::{ + finality::{engine::Grandpa as GrandpaFinalityEngine, SubstrateFinalitySyncPipeline}, + TransactionParams, +}; /// Maximal saturating difference between `balance(now)` and `balance(now-24h)` to treat /// relay as gone wild. @@ -47,6 +50,7 @@ impl SubstrateFinalitySyncPipeline for PolkadotFinalityToKusama { type SourceChain = relay_polkadot_client::Polkadot; type TargetChain = Kusama; + type FinalityEngine = GrandpaFinalityEngine; type SubmitFinalityProofCallBuilder = PolkadotFinalityToKusamaCallBuilder; type TransactionSignScheme = Kusama; @@ -55,7 +59,7 @@ impl SubstrateFinalitySyncPipeline for PolkadotFinalityToKusama { transaction_params: &TransactionParams, enable_version_guard: bool, ) -> relay_substrate_client::Result<()> { - substrate_relay_helper::finality_guards::start::( + substrate_relay_helper::finality::guards::start::( target_client, transaction_params, enable_version_guard, diff --git a/bridges/relays/bin-substrate/src/chains/rialto_headers_to_millau.rs b/bridges/relays/bin-substrate/src/chains/rialto_headers_to_millau.rs index a433f3562a703..a890405703e6a 100644 --- a/bridges/relays/bin-substrate/src/chains/rialto_headers_to_millau.rs +++ b/bridges/relays/bin-substrate/src/chains/rialto_headers_to_millau.rs @@ -16,8 +16,9 @@ //! Rialto-to-Millau headers sync entrypoint. -use substrate_relay_helper::finality_pipeline::{ - DirectSubmitFinalityProofCallBuilder, SubstrateFinalitySyncPipeline, +use substrate_relay_helper::finality::{ + engine::Grandpa as GrandpaFinalityEngine, DirectSubmitGrandpaFinalityProofCallBuilder, + SubstrateFinalitySyncPipeline, }; /// Description of Millau -> Rialto finalized headers bridge. @@ -28,7 +29,8 @@ impl SubstrateFinalitySyncPipeline for RialtoFinalityToMillau { type SourceChain = relay_rialto_client::Rialto; type TargetChain = relay_millau_client::Millau; - type SubmitFinalityProofCallBuilder = DirectSubmitFinalityProofCallBuilder< + type FinalityEngine = GrandpaFinalityEngine; + type SubmitFinalityProofCallBuilder = DirectSubmitGrandpaFinalityProofCallBuilder< Self, millau_runtime::Runtime, millau_runtime::RialtoGrandpaInstance, diff --git a/bridges/relays/bin-substrate/src/chains/rococo_headers_to_wococo.rs b/bridges/relays/bin-substrate/src/chains/rococo_headers_to_wococo.rs index bb66a7422d370..5e2b523000cf4 100644 --- a/bridges/relays/bin-substrate/src/chains/rococo_headers_to_wococo.rs +++ b/bridges/relays/bin-substrate/src/chains/rococo_headers_to_wococo.rs @@ -20,7 +20,10 @@ use crate::chains::wococo_headers_to_rococo::MAXIMAL_BALANCE_DECREASE_PER_DAY; use async_trait::async_trait; use relay_wococo_client::Wococo; -use substrate_relay_helper::{finality_pipeline::SubstrateFinalitySyncPipeline, TransactionParams}; +use substrate_relay_helper::{ + finality::{engine::Grandpa as GrandpaFinalityEngine, SubstrateFinalitySyncPipeline}, + TransactionParams, +}; /// Description of Rococo -> Wococo finalized headers bridge. #[derive(Clone, Debug)] @@ -37,6 +40,7 @@ impl SubstrateFinalitySyncPipeline for RococoFinalityToWococo { type SourceChain = relay_rococo_client::Rococo; type TargetChain = Wococo; + type FinalityEngine = GrandpaFinalityEngine; type SubmitFinalityProofCallBuilder = RococoFinalityToWococoCallBuilder; type TransactionSignScheme = Wococo; @@ -45,7 +49,7 @@ impl SubstrateFinalitySyncPipeline for RococoFinalityToWococo { transaction_params: &TransactionParams, enable_version_guard: bool, ) -> relay_substrate_client::Result<()> { - substrate_relay_helper::finality_guards::start::( + substrate_relay_helper::finality::guards::start::( target_client, transaction_params, enable_version_guard, diff --git a/bridges/relays/bin-substrate/src/chains/westend_headers_to_millau.rs b/bridges/relays/bin-substrate/src/chains/westend_headers_to_millau.rs index 2ec20a027ff5f..3ed67dd260a6b 100644 --- a/bridges/relays/bin-substrate/src/chains/westend_headers_to_millau.rs +++ b/bridges/relays/bin-substrate/src/chains/westend_headers_to_millau.rs @@ -16,8 +16,9 @@ //! Westend-to-Millau headers sync entrypoint. -use substrate_relay_helper::finality_pipeline::{ - DirectSubmitFinalityProofCallBuilder, SubstrateFinalitySyncPipeline, +use substrate_relay_helper::finality::{ + engine::Grandpa as GrandpaFinalityEngine, DirectSubmitGrandpaFinalityProofCallBuilder, + SubstrateFinalitySyncPipeline, }; /// Description of Westend -> Millau finalized headers bridge. @@ -28,7 +29,8 @@ impl SubstrateFinalitySyncPipeline for WestendFinalityToMillau { type SourceChain = relay_westend_client::Westend; type TargetChain = relay_millau_client::Millau; - type SubmitFinalityProofCallBuilder = DirectSubmitFinalityProofCallBuilder< + type FinalityEngine = GrandpaFinalityEngine; + type SubmitFinalityProofCallBuilder = DirectSubmitGrandpaFinalityProofCallBuilder< Self, millau_runtime::Runtime, millau_runtime::WestendGrandpaInstance, diff --git a/bridges/relays/bin-substrate/src/chains/wococo_headers_to_rococo.rs b/bridges/relays/bin-substrate/src/chains/wococo_headers_to_rococo.rs index a7bff5951882e..58413a9d0d561 100644 --- a/bridges/relays/bin-substrate/src/chains/wococo_headers_to_rococo.rs +++ b/bridges/relays/bin-substrate/src/chains/wococo_headers_to_rococo.rs @@ -18,7 +18,10 @@ use async_trait::async_trait; use relay_rococo_client::Rococo; -use substrate_relay_helper::{finality_pipeline::SubstrateFinalitySyncPipeline, TransactionParams}; +use substrate_relay_helper::{ + finality::{engine::Grandpa as GrandpaFinalityEngine, SubstrateFinalitySyncPipeline}, + TransactionParams, +}; /// Maximal saturating difference between `balance(now)` and `balance(now-24h)` to treat /// relay as gone wild. @@ -42,6 +45,7 @@ impl SubstrateFinalitySyncPipeline for WococoFinalityToRococo { type SourceChain = relay_wococo_client::Wococo; type TargetChain = Rococo; + type FinalityEngine = GrandpaFinalityEngine; type SubmitFinalityProofCallBuilder = WococoFinalityToRococoCallBuilder; type TransactionSignScheme = Rococo; @@ -50,7 +54,7 @@ impl SubstrateFinalitySyncPipeline for WococoFinalityToRococo { transaction_params: &TransactionParams, enable_version_guard: bool, ) -> relay_substrate_client::Result<()> { - substrate_relay_helper::finality_guards::start::( + substrate_relay_helper::finality::guards::start::( target_client, transaction_params, enable_version_guard, diff --git a/bridges/relays/bin-substrate/src/cli/init_bridge.rs b/bridges/relays/bin-substrate/src/cli/init_bridge.rs index a0129ce9baa46..add80c58538dd 100644 --- a/bridges/relays/bin-substrate/src/cli/init_bridge.rs +++ b/bridges/relays/bin-substrate/src/cli/init_bridge.rs @@ -22,6 +22,7 @@ use relay_substrate_client::{Chain, SignParam, TransactionSignScheme, UnsignedTr use sp_core::{Bytes, Pair}; use structopt::StructOpt; use strum::{EnumString, EnumVariantNames, VariantNames}; +use substrate_relay_helper::finality::engine::Grandpa as GrandpaFinalityEngine; /// Initialize bridge pallet. #[derive(StructOpt)] @@ -56,6 +57,7 @@ macro_rules! select_bridge { InitBridgeName::MillauToRialto => { type Source = relay_millau_client::Millau; type Target = relay_rialto_client::Rialto; + type Engine = GrandpaFinalityEngine; fn encode_init_bridge( init_data: InitializationData<::Header>, @@ -74,6 +76,7 @@ macro_rules! select_bridge { InitBridgeName::RialtoToMillau => { type Source = relay_rialto_client::Rialto; type Target = relay_millau_client::Millau; + type Engine = GrandpaFinalityEngine; fn encode_init_bridge( init_data: InitializationData<::Header>, @@ -92,6 +95,7 @@ macro_rules! select_bridge { InitBridgeName::WestendToMillau => { type Source = relay_westend_client::Westend; type Target = relay_millau_client::Millau; + type Engine = GrandpaFinalityEngine; fn encode_init_bridge( init_data: InitializationData<::Header>, @@ -114,6 +118,7 @@ macro_rules! select_bridge { InitBridgeName::RococoToWococo => { type Source = relay_rococo_client::Rococo; type Target = relay_wococo_client::Wococo; + type Engine = GrandpaFinalityEngine; fn encode_init_bridge( init_data: InitializationData<::Header>, @@ -130,6 +135,7 @@ macro_rules! select_bridge { InitBridgeName::WococoToRococo => { type Source = relay_wococo_client::Wococo; type Target = relay_rococo_client::Rococo; + type Engine = GrandpaFinalityEngine; fn encode_init_bridge( init_data: InitializationData<::Header>, @@ -146,6 +152,7 @@ macro_rules! select_bridge { InitBridgeName::KusamaToPolkadot => { type Source = relay_kusama_client::Kusama; type Target = relay_polkadot_client::Polkadot; + type Engine = GrandpaFinalityEngine; fn encode_init_bridge( init_data: InitializationData<::Header>, @@ -162,6 +169,7 @@ macro_rules! select_bridge { InitBridgeName::PolkadotToKusama => { type Source = relay_polkadot_client::Polkadot; type Target = relay_kusama_client::Kusama; + type Engine = GrandpaFinalityEngine; fn encode_init_bridge( init_data: InitializationData<::Header>, @@ -189,7 +197,7 @@ impl InitBridge { let (spec_version, transaction_version) = target_client.simple_runtime_version().await?; - substrate_relay_helper::headers_initialize::initialize( + substrate_relay_helper::finality::initialize::initialize::( source_client, target_client.clone(), target_sign.public().into(), diff --git a/bridges/relays/bin-substrate/src/cli/reinit_bridge.rs b/bridges/relays/bin-substrate/src/cli/reinit_bridge.rs index a6897aaf0ab1e..dc902055d20bd 100644 --- a/bridges/relays/bin-substrate/src/cli/reinit_bridge.rs +++ b/bridges/relays/bin-substrate/src/cli/reinit_bridge.rs @@ -24,7 +24,6 @@ use crate::{ TargetConnectionParams, TargetSigningParams, }, }; -use bp_header_chain::justification::GrandpaJustification; use bp_runtime::Chain; use codec::Encode; use finality_relay::{SourceClient, SourceHeader}; @@ -40,8 +39,12 @@ use std::convert::{TryFrom, TryInto}; use structopt::StructOpt; use strum::{EnumString, EnumVariantNames, VariantNames}; use substrate_relay_helper::{ - finality_pipeline::SubstrateFinalitySyncPipeline, finality_source::SubstrateFinalitySource, - finality_target::SubstrateFinalityTarget, messages_source::read_client_state, + finality::{ + source::{SubstrateFinalityProof, SubstrateFinalitySource}, + target::SubstrateFinalityTarget, + SubstrateFinalitySyncPipeline, + }, + messages_source::read_client_state, TransactionParams, }; @@ -299,7 +302,7 @@ impl ReinitBridge { /// Mandatory header and its finality proof. type HeaderAndProof

= ( SyncHeader::SourceChain>>, - GrandpaJustification::SourceChain>>, + SubstrateFinalityProof

, ); /// Vector of mandatory headers and their finality proofs. type HeadersAndProofs

= Vec>; @@ -425,6 +428,7 @@ fn make_mandatory_headers_batches< mod tests { use super::*; use crate::cli::{RuntimeVersionType, SourceRuntimeVersionParams, TargetRuntimeVersionParams}; + use bp_header_chain::justification::GrandpaJustification; use bp_test_utils::{make_default_justification, test_header}; use relay_polkadot_client::Polkadot; use sp_runtime::{traits::Header as _, DigestItem}; diff --git a/bridges/relays/bin-substrate/src/cli/relay_headers.rs b/bridges/relays/bin-substrate/src/cli/relay_headers.rs index 45034aba4b5e8..3a353ed4ab03c 100644 --- a/bridges/relays/bin-substrate/src/cli/relay_headers.rs +++ b/bridges/relays/bin-substrate/src/cli/relay_headers.rs @@ -18,7 +18,7 @@ use structopt::StructOpt; use strum::{EnumString, EnumVariantNames, VariantNames}; use relay_utils::metrics::{GlobalMetrics, StandaloneMetric}; -use substrate_relay_helper::finality_pipeline::SubstrateFinalitySyncPipeline; +use substrate_relay_helper::finality::SubstrateFinalitySyncPipeline; use crate::cli::{ PrometheusParams, SourceConnectionParams, TargetConnectionParams, TargetSigningParams, @@ -136,7 +136,7 @@ impl RelayHeaders { ) .await?; - substrate_relay_helper::finality_pipeline::run::( + substrate_relay_helper::finality::run::( source_client, target_client, self.only_mandatory_headers, diff --git a/bridges/relays/bin-substrate/src/cli/relay_headers_and_messages.rs b/bridges/relays/bin-substrate/src/cli/relay_headers_and_messages.rs index d071d1f8910f2..537ae1e101864 100644 --- a/bridges/relays/bin-substrate/src/cli/relay_headers_and_messages.rs +++ b/bridges/relays/bin-substrate/src/cli/relay_headers_and_messages.rs @@ -35,7 +35,7 @@ use relay_substrate_client::{ use relay_utils::metrics::MetricsParams; use sp_core::{Bytes, Pair}; use substrate_relay_helper::{ - finality_pipeline::SubstrateFinalitySyncPipeline, messages_lane::MessagesRelayParams, + finality::SubstrateFinalitySyncPipeline, messages_lane::MessagesRelayParams, on_demand_headers::OnDemandHeadersRelay, TransactionParams, }; diff --git a/bridges/relays/client-substrate/src/client.rs b/bridges/relays/client-substrate/src/client.rs index 1e48bc3339668..a0426b99f95ae 100644 --- a/bridges/relays/client-substrate/src/client.rs +++ b/bridges/relays/client-substrate/src/client.rs @@ -704,8 +704,8 @@ impl Client { .await } - /// Return new justifications stream. - pub async fn subscribe_justifications(&self) -> Result> { + /// Return new GRANDPA justifications stream. + pub async fn subscribe_grandpa_justifications(&self) -> Result> { let subscription = self .jsonrpsee_execute(move |client| async move { Ok(client diff --git a/bridges/relays/client-substrate/src/error.rs b/bridges/relays/client-substrate/src/error.rs index e698f2596c5fa..797af5cc5d225 100644 --- a/bridges/relays/client-substrate/src/error.rs +++ b/bridges/relays/client-substrate/src/error.rs @@ -45,9 +45,9 @@ pub enum Error { /// Account does not exist on the chain. #[error("Account does not exist on the chain.")] AccountDoesNotExist, - /// Runtime storage is missing mandatory ":code:" entry. - #[error("Mandatory :code: entry is missing from runtime storage.")] - MissingMandatoryCodeEntry, + /// Runtime storage is missing some mandatory value. + #[error("Mandatory storage value is missing from the runtime storage.")] + MissingMandatoryStorageValue, /// The client we're connected to is not synced, so we can't rely on its state. #[error("Substrate client is not synced {0}.")] ClientNotSynced(Health), diff --git a/bridges/relays/client-substrate/src/metrics/storage_proof_overhead.rs b/bridges/relays/client-substrate/src/metrics/storage_proof_overhead.rs index f1c770ed228e7..42793fe7c5468 100644 --- a/bridges/relays/client-substrate/src/metrics/storage_proof_overhead.rs +++ b/bridges/relays/client-substrate/src/metrics/storage_proof_overhead.rs @@ -72,7 +72,7 @@ impl StorageProofOverheadMetric { let maybe_encoded_storage_value = storage_value_reader.read_value(CODE).map_err(Error::StorageProofError)?; let encoded_storage_value_size = - maybe_encoded_storage_value.ok_or(Error::MissingMandatoryCodeEntry)?.len(); + maybe_encoded_storage_value.ok_or(Error::MissingMandatoryStorageValue)?.len(); Ok(storage_proof_size - encoded_storage_value_size) } diff --git a/bridges/relays/lib-substrate-relay/src/finality/engine.rs b/bridges/relays/lib-substrate-relay/src/finality/engine.rs new file mode 100644 index 0000000000000..0b20a3222d232 --- /dev/null +++ b/bridges/relays/lib-substrate-relay/src/finality/engine.rs @@ -0,0 +1,226 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Bridges Common is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Bridges Common. If not, see . + +//! Support of different finality engines, available in Substrate. + +use crate::error::Error; +use async_trait::async_trait; +use bp_header_chain::{ + find_grandpa_authorities_scheduled_change, + justification::{verify_justification, GrandpaJustification}, + FinalityProof, +}; +use codec::{Decode, Encode}; +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, +}; +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. +#[async_trait] +pub trait Engine { + /// Unique consensus engine identifier. + const ID: ConsensusEngineId; + /// Type of finality proofs, used by consensus engine. + type FinalityProof: FinalityProof> + Decode + Encode; + /// Type of bridge pallet initialization data. + type InitializationData: std::fmt::Debug + Send + Sync + 'static; + + /// Returns storage key at the bridged (target) chain that corresponds to the `bool` value, + /// which is true when the bridge pallet is halted. + fn is_halted_key() -> StorageKey; + /// Returns storage at the bridged (target) chain that corresponds to some value that is + /// missing from the storage until bridge pallet is initialized. + /// + /// 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>; + /// Prepare initialization data for the finality bridge pallet. + async fn prepare_initialization_data( + client: Client, + ) -> Result, BlockNumberOf>>; +} + +/// GRANDPA finality engine. +pub struct Grandpa(PhantomData); + +impl Grandpa { + /// Read header by hash from the source client. + async fn source_header( + source_client: &Client, + header_hash: C::Hash, + ) -> Result, BlockNumberOf>> { + source_client + .header_by_hash(header_hash) + .await + .map_err(|err| Error::RetrieveHeader(C::NAME, header_hash, err)) + } + + /// Read GRANDPA authorities set at given header. + async fn source_authorities_set( + source_client: &Client, + header_hash: C::Hash, + ) -> Result, BlockNumberOf>> { + let raw_authorities_set = source_client + .grandpa_authorities_set(header_hash) + .await + .map_err(|err| Error::RetrieveAuthorities(C::NAME, header_hash, err))?; + GrandpaAuthoritiesSet::decode(&mut &raw_authorities_set[..]) + .map_err(|err| Error::DecodeAuthorities(C::NAME, header_hash, err)) + } +} + +#[async_trait] +impl Engine for Grandpa { + const ID: ConsensusEngineId = sp_finality_grandpa::GRANDPA_ENGINE_ID; + type FinalityProof = GrandpaJustification>; + type InitializationData = bp_header_chain::InitializationData; + + fn is_halted_key() -> StorageKey { + bp_header_chain::storage_keys::is_halted_key(C::WITH_CHAIN_GRANDPA_PALLET_NAME) + } + + fn is_initialized_key() -> StorageKey { + bp_header_chain::storage_keys::best_finalized_hash_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, + ) -> Result, BlockNumberOf>> { + // In ideal world we just need to get best finalized header and then to read GRANDPA + // authorities set (`pallet_grandpa::CurrentSetId` + `GrandpaApi::grandpa_authorities()`) at + // this header. + // + // 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() + .await + .map_err(|err| Error::Subscribe(C::NAME, err))?; + // Read next justification - the header that it finalizes will be used as initial header. + let justification = justifications + .next() + .await + .map_err(|e| Error::ReadJustification(C::NAME, e)) + .and_then(|justification| { + justification.ok_or(Error::ReadJustificationStreamEnded(C::NAME)) + })?; + + // Read initial header. + let justification: GrandpaJustification = + Decode::decode(&mut &justification.0[..]) + .map_err(|err| Error::DecodeJustification(C::NAME, err))?; + + let (initial_header_hash, initial_header_number) = + (justification.commit.target_hash, justification.commit.target_number); + + let initial_header = Self::source_header(&source_client, initial_header_hash).await?; + log::trace!(target: "bridge", "Selected {} initial header: {}/{}", + C::NAME, + initial_header_number, + initial_header_hash, + ); + + // Read GRANDPA authorities set at initial header. + let initial_authorities_set = + Self::source_authorities_set(&source_client, initial_header_hash).await?; + log::trace!(target: "bridge", "Selected {} initial authorities set: {:?}", + C::NAME, + initial_authorities_set, + ); + + // If initial header changes the GRANDPA authorities set, then we need previous authorities + // to verify justification. + let mut authorities_for_verification = initial_authorities_set.clone(); + let scheduled_change = find_grandpa_authorities_scheduled_change(&initial_header); + assert!( + scheduled_change.as_ref().map(|c| c.delay.is_zero()).unwrap_or(true), + "GRANDPA authorities change at {} scheduled to happen in {:?} blocks. We expect\ + regular hange to have zero delay", + initial_header_hash, + scheduled_change.as_ref().map(|c| c.delay), + ); + let schedules_change = scheduled_change.is_some(); + if schedules_change { + authorities_for_verification = + Self::source_authorities_set(&source_client, *initial_header.parent_hash()).await?; + log::trace!( + target: "bridge", + "Selected {} header is scheduling GRANDPA authorities set changes. Using previous set: {:?}", + C::NAME, + authorities_for_verification, + ); + } + + // Now let's try to guess authorities set id by verifying justification. + let mut initial_authorities_set_id = 0; + let mut min_possible_block_number = C::BlockNumber::zero(); + let authorities_for_verification = VoterSet::new(authorities_for_verification.clone()) + .ok_or(Error::ReadInvalidAuthorities(C::NAME, authorities_for_verification))?; + loop { + log::trace!( + target: "bridge", "Trying {} GRANDPA authorities set id: {}", + C::NAME, + initial_authorities_set_id, + ); + + let is_valid_set_id = verify_justification::( + (initial_header_hash, initial_header_number), + initial_authorities_set_id, + &authorities_for_verification, + &justification, + ) + .is_ok(); + + if is_valid_set_id { + break + } + + initial_authorities_set_id += 1; + min_possible_block_number += One::one(); + if min_possible_block_number > initial_header_number { + // there can't be more authorities set changes than headers => if we have reached + // `initial_block_number` and still have not found correct value of + // `initial_authorities_set_id`, then something else is broken => fail + return Err(Error::GuessInitialAuthorities(C::NAME, initial_header_number)) + } + } + + Ok(bp_header_chain::InitializationData { + header: Box::new(initial_header), + authority_list: initial_authorities_set, + set_id: if schedules_change { + initial_authorities_set_id + 1 + } else { + initial_authorities_set_id + }, + is_halted: false, + }) + } +} diff --git a/bridges/relays/lib-substrate-relay/src/finality_guards.rs b/bridges/relays/lib-substrate-relay/src/finality/guards.rs similarity index 100% rename from bridges/relays/lib-substrate-relay/src/finality_guards.rs rename to bridges/relays/lib-substrate-relay/src/finality/guards.rs diff --git a/bridges/relays/lib-substrate-relay/src/finality/initialize.rs b/bridges/relays/lib-substrate-relay/src/finality/initialize.rs new file mode 100644 index 0000000000000..58a0fc499fb02 --- /dev/null +++ b/bridges/relays/lib-substrate-relay/src/finality/initialize.rs @@ -0,0 +1,121 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Bridges Common is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Bridges Common. If not, see . + +//! Initialize Substrate -> Substrate finality bridge. +//! +//! Initialization is a transaction that calls `initialize()` function of the +//! finality pallet (GRANDPA/BEEFY/...). This transaction brings initial header +//! and authorities set from source to target chain. The finality sync starts +//! with this header. + +use crate::{error::Error, finality::engine::Engine}; + +use relay_substrate_client::{BlockNumberOf, Chain, Client, Error as SubstrateError, HashOf}; +use sp_core::Bytes; +use sp_runtime::traits::Header as HeaderT; + +/// Submit headers-bridge initialization transaction. +pub async fn initialize, SourceChain: Chain, TargetChain: Chain, F>( + source_client: Client, + target_client: Client, + target_transactions_signer: TargetChain::AccountId, + prepare_initialize_transaction: F, +) where + F: FnOnce(TargetChain::Index, E::InitializationData) -> Result + + Send + + 'static, +{ + let result = do_initialize::( + source_client, + target_client, + target_transactions_signer, + prepare_initialize_transaction, + ) + .await; + + match result { + Ok(Some(tx_hash)) => log::info!( + target: "bridge", + "Successfully submitted {}-headers bridge initialization transaction to {}: {:?}", + SourceChain::NAME, + TargetChain::NAME, + tx_hash, + ), + Ok(None) => (), + Err(err) => log::error!( + target: "bridge", + "Failed to submit {}-headers bridge initialization transaction to {}: {:?}", + SourceChain::NAME, + TargetChain::NAME, + err, + ), + } +} + +/// Craft and submit initialization transaction, returning any error that may occur. +async fn do_initialize, SourceChain: Chain, TargetChain: Chain, F>( + source_client: Client, + target_client: Client, + target_transactions_signer: TargetChain::AccountId, + prepare_initialize_transaction: F, +) -> Result< + Option, + Error::Number>, +> +where + F: FnOnce(TargetChain::Index, E::InitializationData) -> Result + + Send + + 'static, +{ + let is_initialized = is_initialized::(&target_client).await?; + if is_initialized { + log::info!( + target: "bridge", + "{}-headers bridge at {} is already initialized. Skipping", + SourceChain::NAME, + TargetChain::NAME, + ); + return Ok(None) + } + + let initialization_data = E::prepare_initialization_data(source_client).await?; + log::info!( + target: "bridge", + "Prepared initialization data for {}-headers bridge at {}: {:?}", + SourceChain::NAME, + TargetChain::NAME, + initialization_data, + ); + + let initialization_tx_hash = target_client + .submit_signed_extrinsic(target_transactions_signer, move |_, transaction_nonce| { + prepare_initialize_transaction(transaction_nonce, initialization_data) + }) + .await + .map_err(|err| Error::SubmitTransaction(TargetChain::NAME, err))?; + Ok(Some(initialization_tx_hash)) +} + +/// Returns `Ok(true)` if bridge has already been initialized. +async fn is_initialized, SourceChain: Chain, TargetChain: Chain>( + target_client: &Client, +) -> Result, BlockNumberOf>> { + Ok(target_client + .raw_storage_value(E::is_initialized_key(), None) + .await + .map_err(|err| Error::RetrieveBestFinalizedHeaderHash(SourceChain::NAME, err))? + .is_some()) +} diff --git a/bridges/relays/lib-substrate-relay/src/finality_pipeline.rs b/bridges/relays/lib-substrate-relay/src/finality/mod.rs similarity index 85% rename from bridges/relays/lib-substrate-relay/src/finality_pipeline.rs rename to bridges/relays/lib-substrate-relay/src/finality/mod.rs index 3daf8d11440eb..736937ba0724d 100644 --- a/bridges/relays/lib-substrate-relay/src/finality_pipeline.rs +++ b/bridges/relays/lib-substrate-relay/src/finality/mod.rs @@ -18,7 +18,11 @@ //! finality proofs synchronization pipelines. use crate::{ - finality_source::SubstrateFinalitySource, finality_target::SubstrateFinalityTarget, + finality::{ + engine::Engine, + source::{SubstrateFinalityProof, SubstrateFinalitySource}, + target::SubstrateFinalityTarget, + }, TransactionParams, }; @@ -27,13 +31,19 @@ use bp_header_chain::justification::GrandpaJustification; use finality_relay::FinalitySyncPipeline; use pallet_bridge_grandpa::{Call as BridgeGrandpaCall, Config as BridgeGrandpaConfig}; use relay_substrate_client::{ - transaction_stall_timeout, AccountIdOf, AccountKeyPairOf, BlockNumberOf, CallOf, Chain, - ChainWithGrandpa, Client, HashOf, HeaderOf, SyncHeader, TransactionSignScheme, + transaction_stall_timeout, AccountIdOf, AccountKeyPairOf, BlockNumberOf, CallOf, Chain, Client, + HashOf, HeaderOf, SyncHeader, TransactionSignScheme, }; use relay_utils::metrics::MetricsParams; use sp_core::Pair; use std::{fmt::Debug, marker::PhantomData}; +pub mod engine; +pub mod guards; +pub mod initialize; +pub mod source; +pub mod target; + /// Default limit of recent finality proofs. /// /// Finality delay of 4096 blocks is unlikely to happen in practice in @@ -44,10 +54,12 @@ pub(crate) const RECENT_FINALITY_PROOFS_LIMIT: usize = 4096; #[async_trait] pub trait SubstrateFinalitySyncPipeline: 'static + Clone + Debug + Send + Sync { /// Headers of this chain are submitted to the `TargetChain`. - type SourceChain: ChainWithGrandpa; + type SourceChain: Chain; /// Headers of the `SourceChain` are submitted to this chain. type TargetChain: Chain; + /// Finality engine. + type FinalityEngine: Engine; /// How submit finality proof call is built? type SubmitFinalityProofCallBuilder: SubmitFinalityProofCallBuilder; /// Scheme used to sign target chain transactions. @@ -76,7 +88,7 @@ impl FinalitySyncPipeline for FinalitySyncPipe type Hash = HashOf; type Number = BlockNumberOf; type Header = relay_substrate_client::SyncHeader>; - type FinalityProof = GrandpaJustification>; + type FinalityProof = SubstrateFinalityProof

; } /// Different ways of building `submit_finality_proof` calls. @@ -85,23 +97,26 @@ pub trait SubmitFinalityProofCallBuilder { /// function of bridge GRANDPA module at the target chain. fn build_submit_finality_proof_call( header: SyncHeader>, - proof: GrandpaJustification>, + proof: SubstrateFinalityProof

, ) -> CallOf; } /// Building `submit_finality_proof` call when you have direct access to the target /// chain runtime. -pub struct DirectSubmitFinalityProofCallBuilder { +pub struct DirectSubmitGrandpaFinalityProofCallBuilder { _phantom: PhantomData<(P, R, I)>, } -impl SubmitFinalityProofCallBuilder

for DirectSubmitFinalityProofCallBuilder +impl SubmitFinalityProofCallBuilder

+ for DirectSubmitGrandpaFinalityProofCallBuilder where P: SubstrateFinalitySyncPipeline, R: BridgeGrandpaConfig, I: 'static, R::BridgedChain: bp_runtime::Chain

>, CallOf: From>, + P::FinalityEngine: + Engine>>, { fn build_submit_finality_proof_call( header: SyncHeader>, @@ -125,22 +140,22 @@ 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> + impl $crate::finality::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 + <$pipeline as $crate::finality::SubstrateFinalitySyncPipeline>::SourceChain > >, proof: bp_header_chain::justification::GrandpaJustification< relay_substrate_client::HeaderOf< - <$pipeline as $crate::finality_pipeline::SubstrateFinalitySyncPipeline>::SourceChain + <$pipeline as $crate::finality::SubstrateFinalitySyncPipeline>::SourceChain > >, ) -> relay_substrate_client::CallOf< - <$pipeline as $crate::finality_pipeline::SubstrateFinalitySyncPipeline>::TargetChain + <$pipeline as $crate::finality::SubstrateFinalitySyncPipeline>::TargetChain > { $bridge_grandpa($submit_finality_proof(Box::new(header.into_inner()), proof)) } diff --git a/bridges/relays/lib-substrate-relay/src/finality_source.rs b/bridges/relays/lib-substrate-relay/src/finality/source.rs similarity index 87% rename from bridges/relays/lib-substrate-relay/src/finality_source.rs rename to bridges/relays/lib-substrate-relay/src/finality/source.rs index 804d3212930d4..d0acb7717682f 100644 --- a/bridges/relays/lib-substrate-relay/src/finality_source.rs +++ b/bridges/relays/lib-substrate-relay/src/finality/source.rs @@ -16,11 +16,10 @@ //! Default generic implementation of finality source for basic Substrate client. -use crate::finality_pipeline::{FinalitySyncPipelineAdapter, SubstrateFinalitySyncPipeline}; +use crate::finality::{engine::Engine, 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::SourceClient; use futures::stream::{unfold, Stream, StreamExt}; @@ -38,13 +37,19 @@ pub type RequiredHeaderNumberRef = Arc::BlockN pub type SubstrateFinalityProofsStream

= Pin< Box< dyn Stream< - Item = GrandpaJustification< - HeaderOf<

::SourceChain>, - >, + Item = <

::FinalityEngine as Engine< +

::SourceChain, + >>::FinalityProof, > + Send, >, >; +/// Substrate finality proof. Specific to the used `FinalityEngine`. +pub type SubstrateFinalityProof

= + <

::FinalityEngine as Engine< +

::SourceChain, + >>::FinalityProof; + /// Substrate node as finality source. pub struct SubstrateFinalitySource { client: Client, @@ -120,7 +125,7 @@ impl SourceClient Result< ( relay_substrate_client::SyncHeader>, - Option>>, + Option>, ), Error, > { @@ -130,9 +135,7 @@ impl SourceClient>::decode( - &mut raw_justification.as_slice(), - ) + SubstrateFinalityProof::

::decode(&mut raw_justification.as_slice()) }) .transpose() .map_err(Error::ResponseParseFailed)?; @@ -142,7 +145,7 @@ impl SourceClient Result { Ok(unfold( - self.client.clone().subscribe_justifications().await?, + P::FinalityEngine::finality_proofs(self.client.clone()).await?, move |subscription| async move { loop { let log_error = |err| { @@ -161,7 +164,7 @@ impl SourceClient>::decode( + >::FinalityProof::decode( &mut &next_justification[..], ); diff --git a/bridges/relays/lib-substrate-relay/src/finality_target.rs b/bridges/relays/lib-substrate-relay/src/finality/target.rs similarity index 81% rename from bridges/relays/lib-substrate-relay/src/finality_target.rs rename to bridges/relays/lib-substrate-relay/src/finality/target.rs index 4c5814171049a..6ca6be631aab8 100644 --- a/bridges/relays/lib-substrate-relay/src/finality_target.rs +++ b/bridges/relays/lib-substrate-relay/src/finality/target.rs @@ -14,24 +14,22 @@ // You should have received a copy of the GNU General Public License // along with Parity Bridges Common. If not, see . -//! Substrate client as Substrate finality proof target. The chain we connect to should have -//! bridge GRANDPA pallet deployed and provide `FinalityApi` to allow bridging -//! with chain. +//! Substrate client as Substrate finality proof target. use crate::{ - finality_pipeline::{ - FinalitySyncPipelineAdapter, SubmitFinalityProofCallBuilder, SubstrateFinalitySyncPipeline, + finality::{ + engine::Engine, source::SubstrateFinalityProof, FinalitySyncPipelineAdapter, + SubmitFinalityProofCallBuilder, SubstrateFinalitySyncPipeline, }, TransactionParams, }; use async_trait::async_trait; -use bp_header_chain::{justification::GrandpaJustification, storage_keys::is_halted_key}; use codec::Encode; use finality_relay::TargetClient; use relay_substrate_client::{ - AccountIdOf, AccountKeyPairOf, Chain, ChainWithGrandpa, Client, Error, HeaderIdOf, HeaderOf, - SignParam, SyncHeader, TransactionEra, TransactionSignScheme, UnsignedTransaction, + AccountIdOf, AccountKeyPairOf, Chain, Client, Error, HeaderIdOf, HeaderOf, SignParam, + SyncHeader, TransactionEra, TransactionSignScheme, UnsignedTransaction, }; use relay_utils::relay_loop::Client as RelayClient; use sp_core::{Bytes, Pair}; @@ -51,12 +49,9 @@ impl SubstrateFinalityTarget

{ SubstrateFinalityTarget { client, transaction_params } } - /// Ensure that the GRANDPA pallet at target chain is active. + /// Ensure that the bridge pallet at target chain is active. pub async fn ensure_pallet_active(&self) -> Result<(), Error> { - let is_halted = self - .client - .storage_value(is_halted_key(P::SourceChain::WITH_CHAIN_GRANDPA_PALLET_NAME), None) - .await?; + let is_halted = self.client.storage_value(P::FinalityEngine::is_halted_key(), None).await?; if is_halted.unwrap_or(false) { Err(Error::BridgePalletIsHalted) } else { @@ -94,7 +89,7 @@ where // 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?; - // we can't relay finality if GRANDPA pallet at target chain is halted + // we can't relay finality if bridge pallet at target chain is halted self.ensure_pallet_active().await?; Ok(crate::messages_source::read_client_state::( @@ -109,7 +104,7 @@ where async fn submit_finality_proof( &self, header: SyncHeader>, - proof: GrandpaJustification>, + proof: SubstrateFinalityProof

, ) -> Result<(), Error> { let genesis_hash = *self.client.genesis_hash(); let transaction_params = self.transaction_params.clone(); diff --git a/bridges/relays/lib-substrate-relay/src/headers_initialize.rs b/bridges/relays/lib-substrate-relay/src/headers_initialize.rs deleted file mode 100644 index 0e1371c53c815..0000000000000 --- a/bridges/relays/lib-substrate-relay/src/headers_initialize.rs +++ /dev/null @@ -1,282 +0,0 @@ -// Copyright 2019-2021 Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Initialize Substrate -> Substrate headers bridge. -//! -//! Initialization is a transaction that calls `initialize()` function of the -//! `pallet-bridge-grandpa` pallet. This transaction brings initial header -//! and authorities set from source to target chain. The headers sync starts -//! with this header. - -use crate::error::Error; - -use bp_header_chain::{ - find_grandpa_authorities_scheduled_change, - justification::{verify_justification, GrandpaJustification}, - InitializationData, -}; -use codec::Decode; -use finality_grandpa::voter_set::VoterSet; -use num_traits::{One, Zero}; -use relay_substrate_client::{ - BlockNumberOf, Chain, ChainWithGrandpa, Client, Error as SubstrateError, HashOf, -}; -use sp_core::Bytes; -use sp_finality_grandpa::AuthorityList as GrandpaAuthoritiesSet; -use sp_runtime::traits::Header as HeaderT; - -/// Submit headers-bridge initialization transaction. -pub async fn initialize( - source_client: Client, - target_client: Client, - target_transactions_signer: TargetChain::AccountId, - prepare_initialize_transaction: impl FnOnce( - TargetChain::Index, - InitializationData, - ) -> Result - + Send - + 'static, -) { - let result = do_initialize( - source_client, - target_client, - target_transactions_signer, - prepare_initialize_transaction, - ) - .await; - - match result { - Ok(Some(tx_hash)) => log::info!( - target: "bridge", - "Successfully submitted {}-headers bridge initialization transaction to {}: {:?}", - SourceChain::NAME, - TargetChain::NAME, - tx_hash, - ), - Ok(None) => (), - Err(err) => log::error!( - target: "bridge", - "Failed to submit {}-headers bridge initialization transaction to {}: {:?}", - SourceChain::NAME, - TargetChain::NAME, - err, - ), - } -} - -/// Craft and submit initialization transaction, returning any error that may occur. -async fn do_initialize( - source_client: Client, - target_client: Client, - target_transactions_signer: TargetChain::AccountId, - prepare_initialize_transaction: impl FnOnce( - TargetChain::Index, - InitializationData, - ) -> Result - + Send - + 'static, -) -> Result< - Option, - Error::Number>, -> { - let is_initialized = is_initialized::(&target_client).await?; - if is_initialized { - log::info!( - target: "bridge", - "{}-headers bridge at {} is already initialized. Skipping", - SourceChain::NAME, - TargetChain::NAME, - ); - return Ok(None) - } - - let initialization_data = prepare_initialization_data(source_client).await?; - log::info!( - target: "bridge", - "Prepared initialization data for {}-headers bridge at {}: {:?}", - SourceChain::NAME, - TargetChain::NAME, - initialization_data, - ); - - let initialization_tx_hash = target_client - .submit_signed_extrinsic(target_transactions_signer, move |_, transaction_nonce| { - prepare_initialize_transaction(transaction_nonce, initialization_data) - }) - .await - .map_err(|err| Error::SubmitTransaction(TargetChain::NAME, err))?; - Ok(Some(initialization_tx_hash)) -} - -/// Returns `Ok(true)` if bridge has already been initialized. -async fn is_initialized( - target_client: &Client, -) -> Result, BlockNumberOf>> { - Ok(target_client - .raw_storage_value( - bp_header_chain::storage_keys::best_finalized_hash_key( - SourceChain::WITH_CHAIN_GRANDPA_PALLET_NAME, - ), - None, - ) - .await - .map_err(|err| Error::RetrieveBestFinalizedHeaderHash(SourceChain::NAME, err))? - .is_some()) -} - -/// Prepare initialization data for the GRANDPA verifier pallet. -async fn prepare_initialization_data( - source_client: Client, -) -> Result< - InitializationData, - Error::Number>, -> { - // In ideal world we just need to get best finalized header and then to read GRANDPA authorities - // set (`pallet_grandpa::CurrentSetId` + `GrandpaApi::grandpa_authorities()`) at this header. - // - // 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_justifications() - .await - .map_err(|err| Error::Subscribe(SourceChain::NAME, err))?; - // Read next justification - the header that it finalizes will be used as initial header. - let justification = justifications - .next() - .await - .map_err(|e| Error::ReadJustification(SourceChain::NAME, e)) - .and_then(|justification| { - justification.ok_or(Error::ReadJustificationStreamEnded(SourceChain::NAME)) - })?; - - // Read initial header. - let justification: GrandpaJustification = - Decode::decode(&mut &justification.0[..]) - .map_err(|err| Error::DecodeJustification(SourceChain::NAME, err))?; - - let (initial_header_hash, initial_header_number) = - (justification.commit.target_hash, justification.commit.target_number); - - let initial_header = source_header(&source_client, initial_header_hash).await?; - log::trace!(target: "bridge", "Selected {} initial header: {}/{}", - SourceChain::NAME, - initial_header_number, - initial_header_hash, - ); - - // Read GRANDPA authorities set at initial header. - let initial_authorities_set = - source_authorities_set(&source_client, initial_header_hash).await?; - log::trace!(target: "bridge", "Selected {} initial authorities set: {:?}", - SourceChain::NAME, - initial_authorities_set, - ); - - // If initial header changes the GRANDPA authorities set, then we need previous authorities - // to verify justification. - let mut authorities_for_verification = initial_authorities_set.clone(); - let scheduled_change = find_grandpa_authorities_scheduled_change(&initial_header); - assert!( - scheduled_change.as_ref().map(|c| c.delay.is_zero()).unwrap_or(true), - "GRANDPA authorities change at {} scheduled to happen in {:?} blocks. We expect\ - regular hange to have zero delay", - initial_header_hash, - scheduled_change.as_ref().map(|c| c.delay), - ); - let schedules_change = scheduled_change.is_some(); - if schedules_change { - authorities_for_verification = - source_authorities_set(&source_client, *initial_header.parent_hash()).await?; - log::trace!( - target: "bridge", - "Selected {} header is scheduling GRANDPA authorities set changes. Using previous set: {:?}", - SourceChain::NAME, - authorities_for_verification, - ); - } - - // Now let's try to guess authorities set id by verifying justification. - let mut initial_authorities_set_id = 0; - let mut min_possible_block_number = SourceChain::BlockNumber::zero(); - let authorities_for_verification = VoterSet::new(authorities_for_verification.clone()) - .ok_or(Error::ReadInvalidAuthorities(SourceChain::NAME, authorities_for_verification))?; - loop { - log::trace!( - target: "bridge", "Trying {} GRANDPA authorities set id: {}", - SourceChain::NAME, - initial_authorities_set_id, - ); - - let is_valid_set_id = verify_justification::( - (initial_header_hash, initial_header_number), - initial_authorities_set_id, - &authorities_for_verification, - &justification, - ) - .is_ok(); - - if is_valid_set_id { - break - } - - initial_authorities_set_id += 1; - min_possible_block_number += One::one(); - if min_possible_block_number > initial_header_number { - // there can't be more authorities set changes than headers => if we have reached - // `initial_block_number` and still have not found correct value of - // `initial_authorities_set_id`, then something else is broken => fail - return Err(Error::GuessInitialAuthorities(SourceChain::NAME, initial_header_number)) - } - } - - Ok(InitializationData { - header: Box::new(initial_header), - authority_list: initial_authorities_set, - set_id: if schedules_change { - initial_authorities_set_id + 1 - } else { - initial_authorities_set_id - }, - is_halted: false, - }) -} - -/// Read header by hash from the source client. -async fn source_header( - source_client: &Client, - header_hash: SourceChain::Hash, -) -> Result::Number>> -{ - source_client - .header_by_hash(header_hash) - .await - .map_err(|err| Error::RetrieveHeader(SourceChain::NAME, header_hash, err)) -} - -/// Read GRANDPA authorities set at given header. -async fn source_authorities_set( - source_client: &Client, - header_hash: SourceChain::Hash, -) -> Result::Number>> -{ - let raw_authorities_set = source_client - .grandpa_authorities_set(header_hash) - .await - .map_err(|err| Error::RetrieveAuthorities(SourceChain::NAME, header_hash, err))?; - GrandpaAuthoritiesSet::decode(&mut &raw_authorities_set[..]) - .map_err(|err| Error::DecodeAuthorities(SourceChain::NAME, header_hash, err)) -} diff --git a/bridges/relays/lib-substrate-relay/src/lib.rs b/bridges/relays/lib-substrate-relay/src/lib.rs index 27d91147c2ddc..bd51925f5273b 100644 --- a/bridges/relays/lib-substrate-relay/src/lib.rs +++ b/bridges/relays/lib-substrate-relay/src/lib.rs @@ -22,11 +22,7 @@ use std::time::Duration; pub mod conversion_rate_update; pub mod error; -pub mod finality_guards; -pub mod finality_pipeline; -pub mod finality_source; -pub mod finality_target; -pub mod headers_initialize; +pub mod finality; pub mod helpers; pub mod messages_lane; pub mod messages_metrics; diff --git a/bridges/relays/lib-substrate-relay/src/on_demand_headers.rs b/bridges/relays/lib-substrate-relay/src/on_demand_headers.rs index c1401a28a6dd8..915e04f08778e 100644 --- a/bridges/relays/lib-substrate-relay/src/on_demand_headers.rs +++ b/bridges/relays/lib-substrate-relay/src/on_demand_headers.rs @@ -30,9 +30,11 @@ use relay_utils::{ }; use crate::{ - finality_pipeline::{SubstrateFinalitySyncPipeline, RECENT_FINALITY_PROOFS_LIMIT}, - finality_source::{RequiredHeaderNumberRef, SubstrateFinalitySource}, - finality_target::SubstrateFinalityTarget, + finality::{ + source::{RequiredHeaderNumberRef, SubstrateFinalitySource}, + target::SubstrateFinalityTarget, + SubstrateFinalitySyncPipeline, RECENT_FINALITY_PROOFS_LIMIT, + }, TransactionParams, STALL_TIMEOUT, };