mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-30 19:51:02 +00:00
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:
committed by
Bastian Köcher
parent
6a4144e8f2
commit
b3c8852bcf
@@ -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<Self::SourceChain>;
|
||||
type SubmitFinalityProofCallBuilder = KusamaFinalityToPolkadotCallBuilder;
|
||||
type TransactionSignScheme = Polkadot;
|
||||
|
||||
@@ -55,7 +59,7 @@ impl SubstrateFinalitySyncPipeline for KusamaFinalityToPolkadot {
|
||||
transaction_params: &TransactionParams<sp_core::sr25519::Pair>,
|
||||
enable_version_guard: bool,
|
||||
) -> relay_substrate_client::Result<()> {
|
||||
substrate_relay_helper::finality_guards::start::<Polkadot, Polkadot>(
|
||||
substrate_relay_helper::finality::guards::start::<Polkadot, Polkadot>(
|
||||
target_client,
|
||||
transaction_params,
|
||||
enable_version_guard,
|
||||
|
||||
@@ -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<Self::SourceChain>;
|
||||
type SubmitFinalityProofCallBuilder = DirectSubmitGrandpaFinalityProofCallBuilder<
|
||||
Self,
|
||||
rialto_runtime::Runtime,
|
||||
rialto_runtime::MillauGrandpaInstance,
|
||||
|
||||
@@ -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<Self::SourceChain>;
|
||||
type SubmitFinalityProofCallBuilder = PolkadotFinalityToKusamaCallBuilder;
|
||||
type TransactionSignScheme = Kusama;
|
||||
|
||||
@@ -55,7 +59,7 @@ impl SubstrateFinalitySyncPipeline for PolkadotFinalityToKusama {
|
||||
transaction_params: &TransactionParams<sp_core::sr25519::Pair>,
|
||||
enable_version_guard: bool,
|
||||
) -> relay_substrate_client::Result<()> {
|
||||
substrate_relay_helper::finality_guards::start::<Kusama, Kusama>(
|
||||
substrate_relay_helper::finality::guards::start::<Kusama, Kusama>(
|
||||
target_client,
|
||||
transaction_params,
|
||||
enable_version_guard,
|
||||
|
||||
@@ -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<Self::SourceChain>;
|
||||
type SubmitFinalityProofCallBuilder = DirectSubmitGrandpaFinalityProofCallBuilder<
|
||||
Self,
|
||||
millau_runtime::Runtime,
|
||||
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 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<Self::SourceChain>;
|
||||
type SubmitFinalityProofCallBuilder = RococoFinalityToWococoCallBuilder;
|
||||
type TransactionSignScheme = Wococo;
|
||||
|
||||
@@ -45,7 +49,7 @@ impl SubstrateFinalitySyncPipeline for RococoFinalityToWococo {
|
||||
transaction_params: &TransactionParams<sp_core::sr25519::Pair>,
|
||||
enable_version_guard: bool,
|
||||
) -> relay_substrate_client::Result<()> {
|
||||
substrate_relay_helper::finality_guards::start::<Wococo, Wococo>(
|
||||
substrate_relay_helper::finality::guards::start::<Wococo, Wococo>(
|
||||
target_client,
|
||||
transaction_params,
|
||||
enable_version_guard,
|
||||
|
||||
@@ -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<Self::SourceChain>;
|
||||
type SubmitFinalityProofCallBuilder = DirectSubmitGrandpaFinalityProofCallBuilder<
|
||||
Self,
|
||||
millau_runtime::Runtime,
|
||||
millau_runtime::WestendGrandpaInstance,
|
||||
|
||||
@@ -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<Self::SourceChain>;
|
||||
type SubmitFinalityProofCallBuilder = WococoFinalityToRococoCallBuilder;
|
||||
type TransactionSignScheme = Rococo;
|
||||
|
||||
@@ -50,7 +54,7 @@ impl SubstrateFinalitySyncPipeline for WococoFinalityToRococo {
|
||||
transaction_params: &TransactionParams<sp_core::sr25519::Pair>,
|
||||
enable_version_guard: bool,
|
||||
) -> relay_substrate_client::Result<()> {
|
||||
substrate_relay_helper::finality_guards::start::<Rococo, Rococo>(
|
||||
substrate_relay_helper::finality::guards::start::<Rococo, Rococo>(
|
||||
target_client,
|
||||
transaction_params,
|
||||
enable_version_guard,
|
||||
|
||||
@@ -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<Source>;
|
||||
|
||||
fn encode_init_bridge(
|
||||
init_data: InitializationData<<Source as ChainBase>::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<Source>;
|
||||
|
||||
fn encode_init_bridge(
|
||||
init_data: InitializationData<<Source as ChainBase>::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<Source>;
|
||||
|
||||
fn encode_init_bridge(
|
||||
init_data: InitializationData<<Source as ChainBase>::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<Source>;
|
||||
|
||||
fn encode_init_bridge(
|
||||
init_data: InitializationData<<Source as ChainBase>::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<Source>;
|
||||
|
||||
fn encode_init_bridge(
|
||||
init_data: InitializationData<<Source as ChainBase>::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<Source>;
|
||||
|
||||
fn encode_init_bridge(
|
||||
init_data: InitializationData<<Source as ChainBase>::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<Source>;
|
||||
|
||||
fn encode_init_bridge(
|
||||
init_data: InitializationData<<Source as ChainBase>::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::<Engine, _, _, _>(
|
||||
source_client,
|
||||
target_client.clone(),
|
||||
target_sign.public().into(),
|
||||
|
||||
@@ -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<P> = (
|
||||
SyncHeader<HeaderOf<<P as SubstrateFinalitySyncPipeline>::SourceChain>>,
|
||||
GrandpaJustification<HeaderOf<<P as SubstrateFinalitySyncPipeline>::SourceChain>>,
|
||||
SubstrateFinalityProof<P>,
|
||||
);
|
||||
/// Vector of mandatory headers and their finality proofs.
|
||||
type HeadersAndProofs<P> = Vec<HeaderAndProof<P>>;
|
||||
@@ -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};
|
||||
|
||||
@@ -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::<Finality>(
|
||||
substrate_relay_helper::finality::run::<Finality>(
|
||||
source_client,
|
||||
target_client,
|
||||
self.only_mandatory_headers,
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
|
||||
@@ -704,8 +704,8 @@ impl<C: Chain> Client<C> {
|
||||
.await
|
||||
}
|
||||
|
||||
/// Return new justifications stream.
|
||||
pub async fn subscribe_justifications(&self) -> Result<Subscription<Bytes>> {
|
||||
/// Return new GRANDPA justifications stream.
|
||||
pub async fn subscribe_grandpa_justifications(&self) -> Result<Subscription<Bytes>> {
|
||||
let subscription = self
|
||||
.jsonrpsee_execute(move |client| async move {
|
||||
Ok(client
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -72,7 +72,7 @@ impl<C: Chain> StorageProofOverheadMetric<C> {
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
+27
-12
@@ -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<Self::SourceChain>;
|
||||
/// How submit finality proof call is built?
|
||||
type SubmitFinalityProofCallBuilder: SubmitFinalityProofCallBuilder<Self>;
|
||||
/// Scheme used to sign target chain transactions.
|
||||
@@ -76,7 +88,7 @@ impl<P: SubstrateFinalitySyncPipeline> FinalitySyncPipeline for FinalitySyncPipe
|
||||
type Hash = HashOf<P::SourceChain>;
|
||||
type Number = BlockNumberOf<P::SourceChain>;
|
||||
type Header = relay_substrate_client::SyncHeader<HeaderOf<P::SourceChain>>;
|
||||
type FinalityProof = GrandpaJustification<HeaderOf<P::SourceChain>>;
|
||||
type FinalityProof = SubstrateFinalityProof<P>;
|
||||
}
|
||||
|
||||
/// 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.
|
||||
fn build_submit_finality_proof_call(
|
||||
header: SyncHeader<HeaderOf<P::SourceChain>>,
|
||||
proof: GrandpaJustification<HeaderOf<P::SourceChain>>,
|
||||
proof: SubstrateFinalityProof<P>,
|
||||
) -> CallOf<P::TargetChain>;
|
||||
}
|
||||
|
||||
/// Building `submit_finality_proof` call when you have direct access to the target
|
||||
/// chain runtime.
|
||||
pub struct DirectSubmitFinalityProofCallBuilder<P, R, I> {
|
||||
pub struct DirectSubmitGrandpaFinalityProofCallBuilder<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
|
||||
P: SubstrateFinalitySyncPipeline,
|
||||
R: BridgeGrandpaConfig<I>,
|
||||
I: 'static,
|
||||
R::BridgedChain: bp_runtime::Chain<Header = HeaderOf<P::SourceChain>>,
|
||||
CallOf<P::TargetChain>: From<BridgeGrandpaCall<R, I>>,
|
||||
P::FinalityEngine:
|
||||
Engine<P::SourceChain, FinalityProof = GrandpaJustification<HeaderOf<P::SourceChain>>>,
|
||||
{
|
||||
fn build_submit_finality_proof_call(
|
||||
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) => {
|
||||
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))
|
||||
}
|
||||
+14
-11
@@ -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<C> = Arc<Mutex<<C as bp_runtime::Chain>::BlockN
|
||||
pub type SubstrateFinalityProofsStream<P> = Pin<
|
||||
Box<
|
||||
dyn Stream<
|
||||
Item = GrandpaJustification<
|
||||
HeaderOf<<P as SubstrateFinalitySyncPipeline>::SourceChain>,
|
||||
>,
|
||||
Item = <<P as SubstrateFinalitySyncPipeline>::FinalityEngine as Engine<
|
||||
<P as SubstrateFinalitySyncPipeline>::SourceChain,
|
||||
>>::FinalityProof,
|
||||
> + 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.
|
||||
pub struct SubstrateFinalitySource<P: SubstrateFinalitySyncPipeline> {
|
||||
client: Client<P::SourceChain>,
|
||||
@@ -120,7 +125,7 @@ impl<P: SubstrateFinalitySyncPipeline> SourceClient<FinalitySyncPipelineAdapter<
|
||||
) -> Result<
|
||||
(
|
||||
relay_substrate_client::SyncHeader<HeaderOf<P::SourceChain>>,
|
||||
Option<GrandpaJustification<HeaderOf<P::SourceChain>>>,
|
||||
Option<SubstrateFinalityProof<P>>,
|
||||
),
|
||||
Error,
|
||||
> {
|
||||
@@ -130,9 +135,7 @@ impl<P: SubstrateFinalitySyncPipeline> SourceClient<FinalitySyncPipelineAdapter<
|
||||
let justification = signed_block
|
||||
.justification()
|
||||
.map(|raw_justification| {
|
||||
GrandpaJustification::<HeaderOf<P::SourceChain>>::decode(
|
||||
&mut raw_justification.as_slice(),
|
||||
)
|
||||
SubstrateFinalityProof::<P>::decode(&mut raw_justification.as_slice())
|
||||
})
|
||||
.transpose()
|
||||
.map_err(Error::ResponseParseFailed)?;
|
||||
@@ -142,7 +145,7 @@ impl<P: SubstrateFinalitySyncPipeline> SourceClient<FinalitySyncPipelineAdapter<
|
||||
|
||||
async fn finality_proofs(&self) -> Result<Self::FinalityProofsStream, Error> {
|
||||
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<P: SubstrateFinalitySyncPipeline> SourceClient<FinalitySyncPipelineAdapter<
|
||||
.ok()??;
|
||||
|
||||
let decoded_justification =
|
||||
GrandpaJustification::<HeaderOf<P::SourceChain>>::decode(
|
||||
<P::FinalityEngine as Engine<P::SourceChain>>::FinalityProof::decode(
|
||||
&mut &next_justification[..],
|
||||
);
|
||||
|
||||
+10
-15
@@ -14,24 +14,22 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Substrate client as Substrate finality proof target. The chain we connect to should have
|
||||
//! bridge GRANDPA pallet deployed and provide `<BridgedChainName>FinalityApi` to allow bridging
|
||||
//! with <BridgedName> 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<P: SubstrateFinalitySyncPipeline> SubstrateFinalityTarget<P> {
|
||||
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::<P::TargetChain, P::SourceChain>(
|
||||
@@ -109,7 +104,7 @@ where
|
||||
async fn submit_finality_proof(
|
||||
&self,
|
||||
header: SyncHeader<HeaderOf<P::SourceChain>>,
|
||||
proof: GrandpaJustification<HeaderOf<P::SourceChain>>,
|
||||
proof: SubstrateFinalityProof<P>,
|
||||
) -> Result<(), Error> {
|
||||
let genesis_hash = *self.client.genesis_hash();
|
||||
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 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;
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user