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 <adrian@parity.io>

Co-authored-by: Adrian Catangiu <adrian@parity.io>
This commit is contained in:
Svyatoslav Nikolsky
2022-04-14 12:05:57 +03:00
committed by Bastian Köcher
parent 6a4144e8f2
commit b3c8852bcf
23 changed files with 469 additions and 359 deletions
@@ -18,7 +18,10 @@
use async_trait::async_trait; use async_trait::async_trait;
use relay_polkadot_client::Polkadot; 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 /// Maximal saturating difference between `balance(now)` and `balance(now-24h)` to treat
/// relay as gone wild. /// relay as gone wild.
@@ -47,6 +50,7 @@ impl SubstrateFinalitySyncPipeline for KusamaFinalityToPolkadot {
type SourceChain = relay_kusama_client::Kusama; type SourceChain = relay_kusama_client::Kusama;
type TargetChain = Polkadot; type TargetChain = Polkadot;
type FinalityEngine = GrandpaFinalityEngine<Self::SourceChain>;
type SubmitFinalityProofCallBuilder = KusamaFinalityToPolkadotCallBuilder; type SubmitFinalityProofCallBuilder = KusamaFinalityToPolkadotCallBuilder;
type TransactionSignScheme = Polkadot; type TransactionSignScheme = Polkadot;
@@ -55,7 +59,7 @@ impl SubstrateFinalitySyncPipeline for KusamaFinalityToPolkadot {
transaction_params: &TransactionParams<sp_core::sr25519::Pair>, transaction_params: &TransactionParams<sp_core::sr25519::Pair>,
enable_version_guard: bool, enable_version_guard: bool,
) -> relay_substrate_client::Result<()> { ) -> relay_substrate_client::Result<()> {
substrate_relay_helper::finality_guards::start::<Polkadot, Polkadot>( substrate_relay_helper::finality::guards::start::<Polkadot, Polkadot>(
target_client, target_client,
transaction_params, transaction_params,
enable_version_guard, enable_version_guard,
@@ -16,8 +16,9 @@
//! Millau-to-Rialto headers sync entrypoint. //! Millau-to-Rialto headers sync entrypoint.
use substrate_relay_helper::finality_pipeline::{ use substrate_relay_helper::finality::{
DirectSubmitFinalityProofCallBuilder, SubstrateFinalitySyncPipeline, engine::Grandpa as GrandpaFinalityEngine, DirectSubmitGrandpaFinalityProofCallBuilder,
SubstrateFinalitySyncPipeline,
}; };
/// Description of Millau -> Rialto finalized headers bridge. /// Description of Millau -> Rialto finalized headers bridge.
@@ -28,7 +29,8 @@ impl SubstrateFinalitySyncPipeline for MillauFinalityToRialto {
type SourceChain = relay_millau_client::Millau; type SourceChain = relay_millau_client::Millau;
type TargetChain = relay_rialto_client::Rialto; type TargetChain = relay_rialto_client::Rialto;
type SubmitFinalityProofCallBuilder = DirectSubmitFinalityProofCallBuilder< type FinalityEngine = GrandpaFinalityEngine<Self::SourceChain>;
type SubmitFinalityProofCallBuilder = DirectSubmitGrandpaFinalityProofCallBuilder<
Self, Self,
rialto_runtime::Runtime, rialto_runtime::Runtime,
rialto_runtime::MillauGrandpaInstance, rialto_runtime::MillauGrandpaInstance,
@@ -18,7 +18,10 @@
use async_trait::async_trait; use async_trait::async_trait;
use relay_kusama_client::Kusama; 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 /// Maximal saturating difference between `balance(now)` and `balance(now-24h)` to treat
/// relay as gone wild. /// relay as gone wild.
@@ -47,6 +50,7 @@ impl SubstrateFinalitySyncPipeline for PolkadotFinalityToKusama {
type SourceChain = relay_polkadot_client::Polkadot; type SourceChain = relay_polkadot_client::Polkadot;
type TargetChain = Kusama; type TargetChain = Kusama;
type FinalityEngine = GrandpaFinalityEngine<Self::SourceChain>;
type SubmitFinalityProofCallBuilder = PolkadotFinalityToKusamaCallBuilder; type SubmitFinalityProofCallBuilder = PolkadotFinalityToKusamaCallBuilder;
type TransactionSignScheme = Kusama; type TransactionSignScheme = Kusama;
@@ -55,7 +59,7 @@ impl SubstrateFinalitySyncPipeline for PolkadotFinalityToKusama {
transaction_params: &TransactionParams<sp_core::sr25519::Pair>, transaction_params: &TransactionParams<sp_core::sr25519::Pair>,
enable_version_guard: bool, enable_version_guard: bool,
) -> relay_substrate_client::Result<()> { ) -> relay_substrate_client::Result<()> {
substrate_relay_helper::finality_guards::start::<Kusama, Kusama>( substrate_relay_helper::finality::guards::start::<Kusama, Kusama>(
target_client, target_client,
transaction_params, transaction_params,
enable_version_guard, enable_version_guard,
@@ -16,8 +16,9 @@
//! Rialto-to-Millau headers sync entrypoint. //! Rialto-to-Millau headers sync entrypoint.
use substrate_relay_helper::finality_pipeline::{ use substrate_relay_helper::finality::{
DirectSubmitFinalityProofCallBuilder, SubstrateFinalitySyncPipeline, engine::Grandpa as GrandpaFinalityEngine, DirectSubmitGrandpaFinalityProofCallBuilder,
SubstrateFinalitySyncPipeline,
}; };
/// Description of Millau -> Rialto finalized headers bridge. /// Description of Millau -> Rialto finalized headers bridge.
@@ -28,7 +29,8 @@ impl SubstrateFinalitySyncPipeline for RialtoFinalityToMillau {
type SourceChain = relay_rialto_client::Rialto; type SourceChain = relay_rialto_client::Rialto;
type TargetChain = relay_millau_client::Millau; type TargetChain = relay_millau_client::Millau;
type SubmitFinalityProofCallBuilder = DirectSubmitFinalityProofCallBuilder< type FinalityEngine = GrandpaFinalityEngine<Self::SourceChain>;
type SubmitFinalityProofCallBuilder = DirectSubmitGrandpaFinalityProofCallBuilder<
Self, Self,
millau_runtime::Runtime, millau_runtime::Runtime,
millau_runtime::RialtoGrandpaInstance, millau_runtime::RialtoGrandpaInstance,
@@ -20,7 +20,10 @@ use crate::chains::wococo_headers_to_rococo::MAXIMAL_BALANCE_DECREASE_PER_DAY;
use async_trait::async_trait; use async_trait::async_trait;
use relay_wococo_client::Wococo; 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. /// Description of Rococo -> Wococo finalized headers bridge.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@@ -37,6 +40,7 @@ impl SubstrateFinalitySyncPipeline for RococoFinalityToWococo {
type SourceChain = relay_rococo_client::Rococo; type SourceChain = relay_rococo_client::Rococo;
type TargetChain = Wococo; type TargetChain = Wococo;
type FinalityEngine = GrandpaFinalityEngine<Self::SourceChain>;
type SubmitFinalityProofCallBuilder = RococoFinalityToWococoCallBuilder; type SubmitFinalityProofCallBuilder = RococoFinalityToWococoCallBuilder;
type TransactionSignScheme = Wococo; type TransactionSignScheme = Wococo;
@@ -45,7 +49,7 @@ impl SubstrateFinalitySyncPipeline for RococoFinalityToWococo {
transaction_params: &TransactionParams<sp_core::sr25519::Pair>, transaction_params: &TransactionParams<sp_core::sr25519::Pair>,
enable_version_guard: bool, enable_version_guard: bool,
) -> relay_substrate_client::Result<()> { ) -> relay_substrate_client::Result<()> {
substrate_relay_helper::finality_guards::start::<Wococo, Wococo>( substrate_relay_helper::finality::guards::start::<Wococo, Wococo>(
target_client, target_client,
transaction_params, transaction_params,
enable_version_guard, enable_version_guard,
@@ -16,8 +16,9 @@
//! Westend-to-Millau headers sync entrypoint. //! Westend-to-Millau headers sync entrypoint.
use substrate_relay_helper::finality_pipeline::{ use substrate_relay_helper::finality::{
DirectSubmitFinalityProofCallBuilder, SubstrateFinalitySyncPipeline, engine::Grandpa as GrandpaFinalityEngine, DirectSubmitGrandpaFinalityProofCallBuilder,
SubstrateFinalitySyncPipeline,
}; };
/// Description of Westend -> Millau finalized headers bridge. /// Description of Westend -> Millau finalized headers bridge.
@@ -28,7 +29,8 @@ impl SubstrateFinalitySyncPipeline for WestendFinalityToMillau {
type SourceChain = relay_westend_client::Westend; type SourceChain = relay_westend_client::Westend;
type TargetChain = relay_millau_client::Millau; type TargetChain = relay_millau_client::Millau;
type SubmitFinalityProofCallBuilder = DirectSubmitFinalityProofCallBuilder< type FinalityEngine = GrandpaFinalityEngine<Self::SourceChain>;
type SubmitFinalityProofCallBuilder = DirectSubmitGrandpaFinalityProofCallBuilder<
Self, Self,
millau_runtime::Runtime, millau_runtime::Runtime,
millau_runtime::WestendGrandpaInstance, millau_runtime::WestendGrandpaInstance,
@@ -18,7 +18,10 @@
use async_trait::async_trait; use async_trait::async_trait;
use relay_rococo_client::Rococo; 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 /// Maximal saturating difference between `balance(now)` and `balance(now-24h)` to treat
/// relay as gone wild. /// relay as gone wild.
@@ -42,6 +45,7 @@ impl SubstrateFinalitySyncPipeline for WococoFinalityToRococo {
type SourceChain = relay_wococo_client::Wococo; type SourceChain = relay_wococo_client::Wococo;
type TargetChain = Rococo; type TargetChain = Rococo;
type FinalityEngine = GrandpaFinalityEngine<Self::SourceChain>;
type SubmitFinalityProofCallBuilder = WococoFinalityToRococoCallBuilder; type SubmitFinalityProofCallBuilder = WococoFinalityToRococoCallBuilder;
type TransactionSignScheme = Rococo; type TransactionSignScheme = Rococo;
@@ -50,7 +54,7 @@ impl SubstrateFinalitySyncPipeline for WococoFinalityToRococo {
transaction_params: &TransactionParams<sp_core::sr25519::Pair>, transaction_params: &TransactionParams<sp_core::sr25519::Pair>,
enable_version_guard: bool, enable_version_guard: bool,
) -> relay_substrate_client::Result<()> { ) -> relay_substrate_client::Result<()> {
substrate_relay_helper::finality_guards::start::<Rococo, Rococo>( substrate_relay_helper::finality::guards::start::<Rococo, Rococo>(
target_client, target_client,
transaction_params, transaction_params,
enable_version_guard, enable_version_guard,
@@ -22,6 +22,7 @@ use relay_substrate_client::{Chain, SignParam, TransactionSignScheme, UnsignedTr
use sp_core::{Bytes, Pair}; use sp_core::{Bytes, Pair};
use structopt::StructOpt; use structopt::StructOpt;
use strum::{EnumString, EnumVariantNames, VariantNames}; use strum::{EnumString, EnumVariantNames, VariantNames};
use substrate_relay_helper::finality::engine::Grandpa as GrandpaFinalityEngine;
/// Initialize bridge pallet. /// Initialize bridge pallet.
#[derive(StructOpt)] #[derive(StructOpt)]
@@ -56,6 +57,7 @@ macro_rules! select_bridge {
InitBridgeName::MillauToRialto => { InitBridgeName::MillauToRialto => {
type Source = relay_millau_client::Millau; type Source = relay_millau_client::Millau;
type Target = relay_rialto_client::Rialto; type Target = relay_rialto_client::Rialto;
type Engine = GrandpaFinalityEngine<Source>;
fn encode_init_bridge( fn encode_init_bridge(
init_data: InitializationData<<Source as ChainBase>::Header>, init_data: InitializationData<<Source as ChainBase>::Header>,
@@ -74,6 +76,7 @@ macro_rules! select_bridge {
InitBridgeName::RialtoToMillau => { InitBridgeName::RialtoToMillau => {
type Source = relay_rialto_client::Rialto; type Source = relay_rialto_client::Rialto;
type Target = relay_millau_client::Millau; type Target = relay_millau_client::Millau;
type Engine = GrandpaFinalityEngine<Source>;
fn encode_init_bridge( fn encode_init_bridge(
init_data: InitializationData<<Source as ChainBase>::Header>, init_data: InitializationData<<Source as ChainBase>::Header>,
@@ -92,6 +95,7 @@ macro_rules! select_bridge {
InitBridgeName::WestendToMillau => { InitBridgeName::WestendToMillau => {
type Source = relay_westend_client::Westend; type Source = relay_westend_client::Westend;
type Target = relay_millau_client::Millau; type Target = relay_millau_client::Millau;
type Engine = GrandpaFinalityEngine<Source>;
fn encode_init_bridge( fn encode_init_bridge(
init_data: InitializationData<<Source as ChainBase>::Header>, init_data: InitializationData<<Source as ChainBase>::Header>,
@@ -114,6 +118,7 @@ macro_rules! select_bridge {
InitBridgeName::RococoToWococo => { InitBridgeName::RococoToWococo => {
type Source = relay_rococo_client::Rococo; type Source = relay_rococo_client::Rococo;
type Target = relay_wococo_client::Wococo; type Target = relay_wococo_client::Wococo;
type Engine = GrandpaFinalityEngine<Source>;
fn encode_init_bridge( fn encode_init_bridge(
init_data: InitializationData<<Source as ChainBase>::Header>, init_data: InitializationData<<Source as ChainBase>::Header>,
@@ -130,6 +135,7 @@ macro_rules! select_bridge {
InitBridgeName::WococoToRococo => { InitBridgeName::WococoToRococo => {
type Source = relay_wococo_client::Wococo; type Source = relay_wococo_client::Wococo;
type Target = relay_rococo_client::Rococo; type Target = relay_rococo_client::Rococo;
type Engine = GrandpaFinalityEngine<Source>;
fn encode_init_bridge( fn encode_init_bridge(
init_data: InitializationData<<Source as ChainBase>::Header>, init_data: InitializationData<<Source as ChainBase>::Header>,
@@ -146,6 +152,7 @@ macro_rules! select_bridge {
InitBridgeName::KusamaToPolkadot => { InitBridgeName::KusamaToPolkadot => {
type Source = relay_kusama_client::Kusama; type Source = relay_kusama_client::Kusama;
type Target = relay_polkadot_client::Polkadot; type Target = relay_polkadot_client::Polkadot;
type Engine = GrandpaFinalityEngine<Source>;
fn encode_init_bridge( fn encode_init_bridge(
init_data: InitializationData<<Source as ChainBase>::Header>, init_data: InitializationData<<Source as ChainBase>::Header>,
@@ -162,6 +169,7 @@ macro_rules! select_bridge {
InitBridgeName::PolkadotToKusama => { InitBridgeName::PolkadotToKusama => {
type Source = relay_polkadot_client::Polkadot; type Source = relay_polkadot_client::Polkadot;
type Target = relay_kusama_client::Kusama; type Target = relay_kusama_client::Kusama;
type Engine = GrandpaFinalityEngine<Source>;
fn encode_init_bridge( fn encode_init_bridge(
init_data: InitializationData<<Source as ChainBase>::Header>, init_data: InitializationData<<Source as ChainBase>::Header>,
@@ -189,7 +197,7 @@ impl InitBridge {
let (spec_version, transaction_version) = let (spec_version, transaction_version) =
target_client.simple_runtime_version().await?; target_client.simple_runtime_version().await?;
substrate_relay_helper::headers_initialize::initialize( substrate_relay_helper::finality::initialize::initialize::<Engine, _, _, _>(
source_client, source_client,
target_client.clone(), target_client.clone(),
target_sign.public().into(), target_sign.public().into(),
@@ -24,7 +24,6 @@ use crate::{
TargetConnectionParams, TargetSigningParams, TargetConnectionParams, TargetSigningParams,
}, },
}; };
use bp_header_chain::justification::GrandpaJustification;
use bp_runtime::Chain; use bp_runtime::Chain;
use codec::Encode; use codec::Encode;
use finality_relay::{SourceClient, SourceHeader}; use finality_relay::{SourceClient, SourceHeader};
@@ -40,8 +39,12 @@ use std::convert::{TryFrom, TryInto};
use structopt::StructOpt; use structopt::StructOpt;
use strum::{EnumString, EnumVariantNames, VariantNames}; use strum::{EnumString, EnumVariantNames, VariantNames};
use substrate_relay_helper::{ use substrate_relay_helper::{
finality_pipeline::SubstrateFinalitySyncPipeline, finality_source::SubstrateFinalitySource, finality::{
finality_target::SubstrateFinalityTarget, messages_source::read_client_state, source::{SubstrateFinalityProof, SubstrateFinalitySource},
target::SubstrateFinalityTarget,
SubstrateFinalitySyncPipeline,
},
messages_source::read_client_state,
TransactionParams, TransactionParams,
}; };
@@ -299,7 +302,7 @@ impl ReinitBridge {
/// Mandatory header and its finality proof. /// Mandatory header and its finality proof.
type HeaderAndProof<P> = ( type HeaderAndProof<P> = (
SyncHeader<HeaderOf<<P as SubstrateFinalitySyncPipeline>::SourceChain>>, SyncHeader<HeaderOf<<P as SubstrateFinalitySyncPipeline>::SourceChain>>,
GrandpaJustification<HeaderOf<<P as SubstrateFinalitySyncPipeline>::SourceChain>>, SubstrateFinalityProof<P>,
); );
/// Vector of mandatory headers and their finality proofs. /// Vector of mandatory headers and their finality proofs.
type HeadersAndProofs<P> = Vec<HeaderAndProof<P>>; type HeadersAndProofs<P> = Vec<HeaderAndProof<P>>;
@@ -425,6 +428,7 @@ fn make_mandatory_headers_batches<
mod tests { mod tests {
use super::*; use super::*;
use crate::cli::{RuntimeVersionType, SourceRuntimeVersionParams, TargetRuntimeVersionParams}; use crate::cli::{RuntimeVersionType, SourceRuntimeVersionParams, TargetRuntimeVersionParams};
use bp_header_chain::justification::GrandpaJustification;
use bp_test_utils::{make_default_justification, test_header}; use bp_test_utils::{make_default_justification, test_header};
use relay_polkadot_client::Polkadot; use relay_polkadot_client::Polkadot;
use sp_runtime::{traits::Header as _, DigestItem}; use sp_runtime::{traits::Header as _, DigestItem};
@@ -18,7 +18,7 @@ use structopt::StructOpt;
use strum::{EnumString, EnumVariantNames, VariantNames}; use strum::{EnumString, EnumVariantNames, VariantNames};
use relay_utils::metrics::{GlobalMetrics, StandaloneMetric}; use relay_utils::metrics::{GlobalMetrics, StandaloneMetric};
use substrate_relay_helper::finality_pipeline::SubstrateFinalitySyncPipeline; use substrate_relay_helper::finality::SubstrateFinalitySyncPipeline;
use crate::cli::{ use crate::cli::{
PrometheusParams, SourceConnectionParams, TargetConnectionParams, TargetSigningParams, PrometheusParams, SourceConnectionParams, TargetConnectionParams, TargetSigningParams,
@@ -136,7 +136,7 @@ impl RelayHeaders {
) )
.await?; .await?;
substrate_relay_helper::finality_pipeline::run::<Finality>( substrate_relay_helper::finality::run::<Finality>(
source_client, source_client,
target_client, target_client,
self.only_mandatory_headers, self.only_mandatory_headers,
@@ -35,7 +35,7 @@ use relay_substrate_client::{
use relay_utils::metrics::MetricsParams; use relay_utils::metrics::MetricsParams;
use sp_core::{Bytes, Pair}; use sp_core::{Bytes, Pair};
use substrate_relay_helper::{ use substrate_relay_helper::{
finality_pipeline::SubstrateFinalitySyncPipeline, messages_lane::MessagesRelayParams, finality::SubstrateFinalitySyncPipeline, messages_lane::MessagesRelayParams,
on_demand_headers::OnDemandHeadersRelay, TransactionParams, on_demand_headers::OnDemandHeadersRelay, TransactionParams,
}; };
@@ -704,8 +704,8 @@ impl<C: Chain> Client<C> {
.await .await
} }
/// Return new justifications stream. /// Return new GRANDPA justifications stream.
pub async fn subscribe_justifications(&self) -> Result<Subscription<Bytes>> { pub async fn subscribe_grandpa_justifications(&self) -> Result<Subscription<Bytes>> {
let subscription = self let subscription = self
.jsonrpsee_execute(move |client| async move { .jsonrpsee_execute(move |client| async move {
Ok(client Ok(client
+3 -3
View File
@@ -45,9 +45,9 @@ pub enum Error {
/// Account does not exist on the chain. /// Account does not exist on the chain.
#[error("Account does not exist on the chain.")] #[error("Account does not exist on the chain.")]
AccountDoesNotExist, AccountDoesNotExist,
/// Runtime storage is missing mandatory ":code:" entry. /// Runtime storage is missing some mandatory value.
#[error("Mandatory :code: entry is missing from runtime storage.")] #[error("Mandatory storage value is missing from the runtime storage.")]
MissingMandatoryCodeEntry, MissingMandatoryStorageValue,
/// The client we're connected to is not synced, so we can't rely on its state. /// The client we're connected to is not synced, so we can't rely on its state.
#[error("Substrate client is not synced {0}.")] #[error("Substrate client is not synced {0}.")]
ClientNotSynced(Health), ClientNotSynced(Health),
@@ -72,7 +72,7 @@ impl<C: Chain> StorageProofOverheadMetric<C> {
let maybe_encoded_storage_value = let maybe_encoded_storage_value =
storage_value_reader.read_value(CODE).map_err(Error::StorageProofError)?; storage_value_reader.read_value(CODE).map_err(Error::StorageProofError)?;
let encoded_storage_value_size = 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) Ok(storage_proof_size - encoded_storage_value_size)
} }
@@ -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 <http://www.gnu.org/licenses/>.
//! 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<C: Chain> {
/// Unique consensus engine identifier.
const ID: ConsensusEngineId;
/// Type of finality proofs, used by consensus engine.
type FinalityProof: FinalityProof<BlockNumberOf<C>> + 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<C>) -> Result<Subscription<Bytes>, SubstrateError>;
/// Prepare initialization data for the finality bridge pallet.
async fn prepare_initialization_data(
client: Client<C>,
) -> Result<Self::InitializationData, Error<HashOf<C>, BlockNumberOf<C>>>;
}
/// GRANDPA finality engine.
pub struct Grandpa<C>(PhantomData<C>);
impl<C: ChainWithGrandpa> Grandpa<C> {
/// Read header by hash from the source client.
async fn source_header(
source_client: &Client<C>,
header_hash: C::Hash,
) -> Result<C::Header, Error<HashOf<C>, BlockNumberOf<C>>> {
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<C>,
header_hash: C::Hash,
) -> Result<GrandpaAuthoritiesSet, Error<HashOf<C>, BlockNumberOf<C>>> {
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<C: ChainWithGrandpa> Engine<C> for Grandpa<C> {
const ID: ConsensusEngineId = sp_finality_grandpa::GRANDPA_ENGINE_ID;
type FinalityProof = GrandpaJustification<HeaderOf<C>>;
type InitializationData = bp_header_chain::InitializationData<C::Header>;
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<C>) -> Result<Subscription<Bytes>, SubstrateError> {
client.subscribe_grandpa_justifications().await
}
/// Prepare initialization data for the GRANDPA verifier pallet.
async fn prepare_initialization_data(
source_client: Client<C>,
) -> Result<Self::InitializationData, Error<HashOf<C>, BlockNumberOf<C>>> {
// 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<C::Header> =
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::<C::Header>(
(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,
})
}
}
@@ -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 <http://www.gnu.org/licenses/>.
//! 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<E: Engine<SourceChain>, SourceChain: Chain, TargetChain: Chain, F>(
source_client: Client<SourceChain>,
target_client: Client<TargetChain>,
target_transactions_signer: TargetChain::AccountId,
prepare_initialize_transaction: F,
) where
F: FnOnce(TargetChain::Index, E::InitializationData) -> Result<Bytes, SubstrateError>
+ Send
+ 'static,
{
let result = do_initialize::<E, _, _, _>(
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<E: Engine<SourceChain>, SourceChain: Chain, TargetChain: Chain, F>(
source_client: Client<SourceChain>,
target_client: Client<TargetChain>,
target_transactions_signer: TargetChain::AccountId,
prepare_initialize_transaction: F,
) -> Result<
Option<TargetChain::Hash>,
Error<SourceChain::Hash, <SourceChain::Header as HeaderT>::Number>,
>
where
F: FnOnce(TargetChain::Index, E::InitializationData) -> Result<Bytes, SubstrateError>
+ Send
+ 'static,
{
let is_initialized = is_initialized::<E, SourceChain, TargetChain>(&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<E: Engine<SourceChain>, SourceChain: Chain, TargetChain: Chain>(
target_client: &Client<TargetChain>,
) -> Result<bool, Error<HashOf<SourceChain>, BlockNumberOf<SourceChain>>> {
Ok(target_client
.raw_storage_value(E::is_initialized_key(), None)
.await
.map_err(|err| Error::RetrieveBestFinalizedHeaderHash(SourceChain::NAME, err))?
.is_some())
}
@@ -18,7 +18,11 @@
//! finality proofs synchronization pipelines. //! finality proofs synchronization pipelines.
use crate::{ use crate::{
finality_source::SubstrateFinalitySource, finality_target::SubstrateFinalityTarget, finality::{
engine::Engine,
source::{SubstrateFinalityProof, SubstrateFinalitySource},
target::SubstrateFinalityTarget,
},
TransactionParams, TransactionParams,
}; };
@@ -27,13 +31,19 @@ use bp_header_chain::justification::GrandpaJustification;
use finality_relay::FinalitySyncPipeline; use finality_relay::FinalitySyncPipeline;
use pallet_bridge_grandpa::{Call as BridgeGrandpaCall, Config as BridgeGrandpaConfig}; use pallet_bridge_grandpa::{Call as BridgeGrandpaCall, Config as BridgeGrandpaConfig};
use relay_substrate_client::{ use relay_substrate_client::{
transaction_stall_timeout, AccountIdOf, AccountKeyPairOf, BlockNumberOf, CallOf, Chain, transaction_stall_timeout, AccountIdOf, AccountKeyPairOf, BlockNumberOf, CallOf, Chain, Client,
ChainWithGrandpa, Client, HashOf, HeaderOf, SyncHeader, TransactionSignScheme, HashOf, HeaderOf, SyncHeader, TransactionSignScheme,
}; };
use relay_utils::metrics::MetricsParams; use relay_utils::metrics::MetricsParams;
use sp_core::Pair; use sp_core::Pair;
use std::{fmt::Debug, marker::PhantomData}; 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. /// Default limit of recent finality proofs.
/// ///
/// Finality delay of 4096 blocks is unlikely to happen in practice in /// 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] #[async_trait]
pub trait SubstrateFinalitySyncPipeline: 'static + Clone + Debug + Send + Sync { pub trait SubstrateFinalitySyncPipeline: 'static + Clone + Debug + Send + Sync {
/// Headers of this chain are submitted to the `TargetChain`. /// Headers of this chain are submitted to the `TargetChain`.
type SourceChain: ChainWithGrandpa; type SourceChain: Chain;
/// Headers of the `SourceChain` are submitted to this chain. /// Headers of the `SourceChain` are submitted to this chain.
type TargetChain: Chain; type TargetChain: Chain;
/// Finality engine.
type FinalityEngine: Engine<Self::SourceChain>;
/// How submit finality proof call is built? /// How submit finality proof call is built?
type SubmitFinalityProofCallBuilder: SubmitFinalityProofCallBuilder<Self>; type SubmitFinalityProofCallBuilder: SubmitFinalityProofCallBuilder<Self>;
/// Scheme used to sign target chain transactions. /// Scheme used to sign target chain transactions.
@@ -76,7 +88,7 @@ impl<P: SubstrateFinalitySyncPipeline> FinalitySyncPipeline for FinalitySyncPipe
type Hash = HashOf<P::SourceChain>; type Hash = HashOf<P::SourceChain>;
type Number = BlockNumberOf<P::SourceChain>; type Number = BlockNumberOf<P::SourceChain>;
type Header = relay_substrate_client::SyncHeader<HeaderOf<P::SourceChain>>; type Header = relay_substrate_client::SyncHeader<HeaderOf<P::SourceChain>>;
type FinalityProof = GrandpaJustification<HeaderOf<P::SourceChain>>; type FinalityProof = SubstrateFinalityProof<P>;
} }
/// Different ways of building `submit_finality_proof` calls. /// Different ways of building `submit_finality_proof` calls.
@@ -85,23 +97,26 @@ pub trait SubmitFinalityProofCallBuilder<P: SubstrateFinalitySyncPipeline> {
/// function of bridge GRANDPA module at the target chain. /// function of bridge GRANDPA module at the target chain.
fn build_submit_finality_proof_call( fn build_submit_finality_proof_call(
header: SyncHeader<HeaderOf<P::SourceChain>>, header: SyncHeader<HeaderOf<P::SourceChain>>,
proof: GrandpaJustification<HeaderOf<P::SourceChain>>, proof: SubstrateFinalityProof<P>,
) -> CallOf<P::TargetChain>; ) -> CallOf<P::TargetChain>;
} }
/// Building `submit_finality_proof` call when you have direct access to the target /// Building `submit_finality_proof` call when you have direct access to the target
/// chain runtime. /// chain runtime.
pub struct DirectSubmitFinalityProofCallBuilder<P, R, I> { pub struct DirectSubmitGrandpaFinalityProofCallBuilder<P, R, I> {
_phantom: PhantomData<(P, R, I)>, _phantom: PhantomData<(P, R, I)>,
} }
impl<P, R, I> SubmitFinalityProofCallBuilder<P> for DirectSubmitFinalityProofCallBuilder<P, R, I> impl<P, R, I> SubmitFinalityProofCallBuilder<P>
for DirectSubmitGrandpaFinalityProofCallBuilder<P, R, I>
where where
P: SubstrateFinalitySyncPipeline, P: SubstrateFinalitySyncPipeline,
R: BridgeGrandpaConfig<I>, R: BridgeGrandpaConfig<I>,
I: 'static, I: 'static,
R::BridgedChain: bp_runtime::Chain<Header = HeaderOf<P::SourceChain>>, R::BridgedChain: bp_runtime::Chain<Header = HeaderOf<P::SourceChain>>,
CallOf<P::TargetChain>: From<BridgeGrandpaCall<R, I>>, CallOf<P::TargetChain>: From<BridgeGrandpaCall<R, I>>,
P::FinalityEngine:
Engine<P::SourceChain, FinalityProof = GrandpaJustification<HeaderOf<P::SourceChain>>>,
{ {
fn build_submit_finality_proof_call( fn build_submit_finality_proof_call(
header: SyncHeader<HeaderOf<P::SourceChain>>, header: SyncHeader<HeaderOf<P::SourceChain>>,
@@ -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) => { ($pipeline:ident, $mocked_builder:ident, $bridge_grandpa:path, $submit_finality_proof:path) => {
pub struct $mocked_builder; pub struct $mocked_builder;
impl $crate::finality_pipeline::SubmitFinalityProofCallBuilder<$pipeline> impl $crate::finality::SubmitFinalityProofCallBuilder<$pipeline>
for $mocked_builder for $mocked_builder
{ {
fn build_submit_finality_proof_call( fn build_submit_finality_proof_call(
header: relay_substrate_client::SyncHeader< header: relay_substrate_client::SyncHeader<
relay_substrate_client::HeaderOf< relay_substrate_client::HeaderOf<
<$pipeline as $crate::finality_pipeline::SubstrateFinalitySyncPipeline>::SourceChain <$pipeline as $crate::finality::SubstrateFinalitySyncPipeline>::SourceChain
> >
>, >,
proof: bp_header_chain::justification::GrandpaJustification< proof: bp_header_chain::justification::GrandpaJustification<
relay_substrate_client::HeaderOf< relay_substrate_client::HeaderOf<
<$pipeline as $crate::finality_pipeline::SubstrateFinalitySyncPipeline>::SourceChain <$pipeline as $crate::finality::SubstrateFinalitySyncPipeline>::SourceChain
> >
>, >,
) -> relay_substrate_client::CallOf< ) -> 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)) $bridge_grandpa($submit_finality_proof(Box::new(header.into_inner()), proof))
} }
@@ -16,11 +16,10 @@
//! Default generic implementation of finality source for basic Substrate client. //! 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_std::sync::{Arc, Mutex};
use async_trait::async_trait; use async_trait::async_trait;
use bp_header_chain::justification::GrandpaJustification;
use codec::Decode; use codec::Decode;
use finality_relay::SourceClient; use finality_relay::SourceClient;
use futures::stream::{unfold, Stream, StreamExt}; use futures::stream::{unfold, Stream, StreamExt};
@@ -38,13 +37,19 @@ pub type RequiredHeaderNumberRef<C> = Arc<Mutex<<C as bp_runtime::Chain>::BlockN
pub type SubstrateFinalityProofsStream<P> = Pin< pub type SubstrateFinalityProofsStream<P> = Pin<
Box< Box<
dyn Stream< dyn Stream<
Item = GrandpaJustification< Item = <<P as SubstrateFinalitySyncPipeline>::FinalityEngine as Engine<
HeaderOf<<P as SubstrateFinalitySyncPipeline>::SourceChain>, <P as SubstrateFinalitySyncPipeline>::SourceChain,
>, >>::FinalityProof,
> + Send, > + Send,
>, >,
>; >;
/// Substrate finality proof. Specific to the used `FinalityEngine`.
pub type SubstrateFinalityProof<P> =
<<P as SubstrateFinalitySyncPipeline>::FinalityEngine as Engine<
<P as SubstrateFinalitySyncPipeline>::SourceChain,
>>::FinalityProof;
/// Substrate node as finality source. /// Substrate node as finality source.
pub struct SubstrateFinalitySource<P: SubstrateFinalitySyncPipeline> { pub struct SubstrateFinalitySource<P: SubstrateFinalitySyncPipeline> {
client: Client<P::SourceChain>, client: Client<P::SourceChain>,
@@ -120,7 +125,7 @@ impl<P: SubstrateFinalitySyncPipeline> SourceClient<FinalitySyncPipelineAdapter<
) -> Result< ) -> Result<
( (
relay_substrate_client::SyncHeader<HeaderOf<P::SourceChain>>, relay_substrate_client::SyncHeader<HeaderOf<P::SourceChain>>,
Option<GrandpaJustification<HeaderOf<P::SourceChain>>>, Option<SubstrateFinalityProof<P>>,
), ),
Error, Error,
> { > {
@@ -130,9 +135,7 @@ impl<P: SubstrateFinalitySyncPipeline> SourceClient<FinalitySyncPipelineAdapter<
let justification = signed_block let justification = signed_block
.justification() .justification()
.map(|raw_justification| { .map(|raw_justification| {
GrandpaJustification::<HeaderOf<P::SourceChain>>::decode( SubstrateFinalityProof::<P>::decode(&mut raw_justification.as_slice())
&mut raw_justification.as_slice(),
)
}) })
.transpose() .transpose()
.map_err(Error::ResponseParseFailed)?; .map_err(Error::ResponseParseFailed)?;
@@ -142,7 +145,7 @@ impl<P: SubstrateFinalitySyncPipeline> SourceClient<FinalitySyncPipelineAdapter<
async fn finality_proofs(&self) -> Result<Self::FinalityProofsStream, Error> { async fn finality_proofs(&self) -> Result<Self::FinalityProofsStream, Error> {
Ok(unfold( Ok(unfold(
self.client.clone().subscribe_justifications().await?, P::FinalityEngine::finality_proofs(self.client.clone()).await?,
move |subscription| async move { move |subscription| async move {
loop { loop {
let log_error = |err| { let log_error = |err| {
@@ -161,7 +164,7 @@ impl<P: SubstrateFinalitySyncPipeline> SourceClient<FinalitySyncPipelineAdapter<
.ok()??; .ok()??;
let decoded_justification = let decoded_justification =
GrandpaJustification::<HeaderOf<P::SourceChain>>::decode( <P::FinalityEngine as Engine<P::SourceChain>>::FinalityProof::decode(
&mut &next_justification[..], &mut &next_justification[..],
); );
@@ -14,24 +14,22 @@
// You should have received a copy of the GNU General Public License // 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/>. // 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 //! Substrate client as Substrate finality proof target.
//! bridge GRANDPA pallet deployed and provide `<BridgedChainName>FinalityApi` to allow bridging
//! with <BridgedName> chain.
use crate::{ use crate::{
finality_pipeline::{ finality::{
FinalitySyncPipelineAdapter, SubmitFinalityProofCallBuilder, SubstrateFinalitySyncPipeline, engine::Engine, source::SubstrateFinalityProof, FinalitySyncPipelineAdapter,
SubmitFinalityProofCallBuilder, SubstrateFinalitySyncPipeline,
}, },
TransactionParams, TransactionParams,
}; };
use async_trait::async_trait; use async_trait::async_trait;
use bp_header_chain::{justification::GrandpaJustification, storage_keys::is_halted_key};
use codec::Encode; use codec::Encode;
use finality_relay::TargetClient; use finality_relay::TargetClient;
use relay_substrate_client::{ use relay_substrate_client::{
AccountIdOf, AccountKeyPairOf, Chain, ChainWithGrandpa, Client, Error, HeaderIdOf, HeaderOf, AccountIdOf, AccountKeyPairOf, Chain, Client, Error, HeaderIdOf, HeaderOf, SignParam,
SignParam, SyncHeader, TransactionEra, TransactionSignScheme, UnsignedTransaction, SyncHeader, TransactionEra, TransactionSignScheme, UnsignedTransaction,
}; };
use relay_utils::relay_loop::Client as RelayClient; use relay_utils::relay_loop::Client as RelayClient;
use sp_core::{Bytes, Pair}; use sp_core::{Bytes, Pair};
@@ -51,12 +49,9 @@ impl<P: SubstrateFinalitySyncPipeline> SubstrateFinalityTarget<P> {
SubstrateFinalityTarget { client, transaction_params } 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> { pub async fn ensure_pallet_active(&self) -> Result<(), Error> {
let is_halted = self let is_halted = self.client.storage_value(P::FinalityEngine::is_halted_key(), None).await?;
.client
.storage_value(is_halted_key(P::SourceChain::WITH_CHAIN_GRANDPA_PALLET_NAME), None)
.await?;
if is_halted.unwrap_or(false) { if is_halted.unwrap_or(false) {
Err(Error::BridgePalletIsHalted) Err(Error::BridgePalletIsHalted)
} else { } else {
@@ -94,7 +89,7 @@ where
// we can't continue to relay finality if target node is out of sync, because // 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 // it may have already received (some of) headers that we're going to relay
self.client.ensure_synced().await?; 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?; self.ensure_pallet_active().await?;
Ok(crate::messages_source::read_client_state::<P::TargetChain, P::SourceChain>( Ok(crate::messages_source::read_client_state::<P::TargetChain, P::SourceChain>(
@@ -109,7 +104,7 @@ where
async fn submit_finality_proof( async fn submit_finality_proof(
&self, &self,
header: SyncHeader<HeaderOf<P::SourceChain>>, header: SyncHeader<HeaderOf<P::SourceChain>>,
proof: GrandpaJustification<HeaderOf<P::SourceChain>>, proof: SubstrateFinalityProof<P>,
) -> Result<(), Error> { ) -> Result<(), Error> {
let genesis_hash = *self.client.genesis_hash(); let genesis_hash = *self.client.genesis_hash();
let transaction_params = self.transaction_params.clone(); let transaction_params = self.transaction_params.clone();
@@ -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 <http://www.gnu.org/licenses/>.
//! 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<SourceChain: ChainWithGrandpa, TargetChain: Chain>(
source_client: Client<SourceChain>,
target_client: Client<TargetChain>,
target_transactions_signer: TargetChain::AccountId,
prepare_initialize_transaction: impl FnOnce(
TargetChain::Index,
InitializationData<SourceChain::Header>,
) -> Result<Bytes, SubstrateError>
+ 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: ChainWithGrandpa, TargetChain: Chain>(
source_client: Client<SourceChain>,
target_client: Client<TargetChain>,
target_transactions_signer: TargetChain::AccountId,
prepare_initialize_transaction: impl FnOnce(
TargetChain::Index,
InitializationData<SourceChain::Header>,
) -> Result<Bytes, SubstrateError>
+ Send
+ 'static,
) -> Result<
Option<TargetChain::Hash>,
Error<SourceChain::Hash, <SourceChain::Header as HeaderT>::Number>,
> {
let is_initialized = is_initialized::<SourceChain, TargetChain>(&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<SourceChain: ChainWithGrandpa, TargetChain: Chain>(
target_client: &Client<TargetChain>,
) -> Result<bool, Error<HashOf<SourceChain>, BlockNumberOf<SourceChain>>> {
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<SourceChain: Chain>(
source_client: Client<SourceChain>,
) -> Result<
InitializationData<SourceChain::Header>,
Error<SourceChain::Hash, <SourceChain::Header as HeaderT>::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<SourceChain::Header> =
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::<SourceChain::Header>(
(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<SourceChain: Chain>(
source_client: &Client<SourceChain>,
header_hash: SourceChain::Hash,
) -> Result<SourceChain::Header, Error<SourceChain::Hash, <SourceChain::Header as HeaderT>::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<SourceChain: Chain>(
source_client: &Client<SourceChain>,
header_hash: SourceChain::Hash,
) -> Result<GrandpaAuthoritiesSet, Error<SourceChain::Hash, <SourceChain::Header as HeaderT>::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))
}
@@ -22,11 +22,7 @@ use std::time::Duration;
pub mod conversion_rate_update; pub mod conversion_rate_update;
pub mod error; pub mod error;
pub mod finality_guards; pub mod finality;
pub mod finality_pipeline;
pub mod finality_source;
pub mod finality_target;
pub mod headers_initialize;
pub mod helpers; pub mod helpers;
pub mod messages_lane; pub mod messages_lane;
pub mod messages_metrics; pub mod messages_metrics;
@@ -30,9 +30,11 @@ use relay_utils::{
}; };
use crate::{ use crate::{
finality_pipeline::{SubstrateFinalitySyncPipeline, RECENT_FINALITY_PROOFS_LIMIT}, finality::{
finality_source::{RequiredHeaderNumberRef, SubstrateFinalitySource}, source::{RequiredHeaderNumberRef, SubstrateFinalitySource},
finality_target::SubstrateFinalityTarget, target::SubstrateFinalityTarget,
SubstrateFinalitySyncPipeline, RECENT_FINALITY_PROOFS_LIMIT,
},
TransactionParams, STALL_TIMEOUT, TransactionParams, STALL_TIMEOUT,
}; };