mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-31 02:51:01 +00:00
* Split FinalitySyncPipeline and SourceClient * Move some logic to finality_base * Add empty equivocation detection clients * Add equivocation reporting logic to the source client * Use convenience trait for SubstrateFinalitySyncPipeline * Define JustificationVerificationContext for GRANDPA * Equivocation source client: finality_verification_context() * Equivocation source client: synced_headers_finality_info() * reuse HeaderFinalityInfo * Define EquivocationsFinder * Fix spellcheck * Address review comments * Avoid equivocations lookup errors
This commit is contained in:
committed by
Bastian Köcher
parent
9bfad80664
commit
48cae06a77
@@ -15,8 +15,6 @@
|
||||
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use async_trait::async_trait;
|
||||
use relay_substrate_client::{AccountIdOf, AccountKeyPairOf};
|
||||
use sp_core::Pair;
|
||||
use structopt::StructOpt;
|
||||
use strum::{EnumString, EnumVariantNames, VariantNames};
|
||||
|
||||
@@ -76,10 +74,7 @@ pub enum RelayHeadersBridge {
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
trait HeadersRelayer: RelayToRelayHeadersCliBridge
|
||||
where
|
||||
AccountIdOf<Self::Target>: From<<AccountKeyPairOf<Self::Target> as Pair>::Public>,
|
||||
{
|
||||
trait HeadersRelayer: RelayToRelayHeadersCliBridge {
|
||||
/// Relay headers.
|
||||
async fn relay_headers(data: RelayHeaders) -> anyhow::Result<()> {
|
||||
let source_client = data.source.into_client::<Self::Source>().await?;
|
||||
|
||||
@@ -16,16 +16,17 @@
|
||||
|
||||
use crate::calls::UtilityCall;
|
||||
|
||||
use bp_header_chain::UnderlyingChainWithGrandpaProvider;
|
||||
use bp_header_chain::ChainWithGrandpa as ChainWithGrandpaBase;
|
||||
use bp_messages::MessageNonce;
|
||||
use bp_runtime::{
|
||||
Chain as ChainBase, ChainId, EncodedOrDecodedCall, HashOf, Parachain as ParachainBase,
|
||||
TransactionEra, TransactionEraOf, UnderlyingChainProvider,
|
||||
};
|
||||
use codec::{Codec, Encode};
|
||||
use codec::{Codec, Decode, Encode};
|
||||
use jsonrpsee::core::{DeserializeOwned, Serialize};
|
||||
use num_traits::Zero;
|
||||
use sc_transaction_pool_api::TransactionStatus;
|
||||
use scale_info::TypeInfo;
|
||||
use sp_core::{storage::StorageKey, Pair};
|
||||
use sp_runtime::{
|
||||
generic::SignedBlock,
|
||||
@@ -78,7 +79,7 @@ pub trait RelayChain: Chain {
|
||||
///
|
||||
/// Keep in mind that parachains are relying on relay chain GRANDPA, so they should not implement
|
||||
/// this trait.
|
||||
pub trait ChainWithGrandpa: Chain + UnderlyingChainWithGrandpaProvider {
|
||||
pub trait ChainWithGrandpa: Chain + ChainWithGrandpaBase {
|
||||
/// Name of the runtime API method that is returning the GRANDPA info associated with the
|
||||
/// headers accepted by the `submit_finality_proofs` extrinsic in the queried block.
|
||||
///
|
||||
@@ -87,7 +88,7 @@ pub trait ChainWithGrandpa: Chain + UnderlyingChainWithGrandpaProvider {
|
||||
const SYNCED_HEADERS_GRANDPA_INFO_METHOD: &'static str;
|
||||
|
||||
/// The type of the key owner proof used by the grandpa engine.
|
||||
type KeyOwnerProof;
|
||||
type KeyOwnerProof: Decode + TypeInfo + Send;
|
||||
}
|
||||
|
||||
/// Substrate-based parachain from minimal relay-client point of view.
|
||||
|
||||
@@ -22,8 +22,8 @@ use crate::{
|
||||
SubstrateAuthorClient, SubstrateChainClient, SubstrateFinalityClient,
|
||||
SubstrateFrameSystemClient, SubstrateStateClient, SubstrateSystemClient,
|
||||
},
|
||||
transaction_stall_timeout, AccountKeyPairOf, ConnectionParams, Error, HashOf, HeaderIdOf,
|
||||
Result, SignParam, TransactionTracker, UnsignedTransaction,
|
||||
transaction_stall_timeout, AccountKeyPairOf, ChainWithGrandpa, ConnectionParams, Error, HashOf,
|
||||
HeaderIdOf, Result, SignParam, TransactionTracker, UnsignedTransaction,
|
||||
};
|
||||
|
||||
use async_std::sync::{Arc, Mutex, RwLock};
|
||||
@@ -715,15 +715,16 @@ impl<C: Chain> Client<C> {
|
||||
Ok(Subscription(Mutex::new(receiver)))
|
||||
}
|
||||
|
||||
// TODO: remove warning after implementing
|
||||
// https://github.com/paritytech/parity-bridges-common/issues/39
|
||||
#[allow(dead_code)]
|
||||
async fn generate_grandpa_key_ownership_proof(
|
||||
/// Generates a proof of key ownership for the given authority in the given set.
|
||||
pub async fn generate_grandpa_key_ownership_proof(
|
||||
&self,
|
||||
at: HashOf<C>,
|
||||
set_id: sp_consensus_grandpa::SetId,
|
||||
authority_id: sp_consensus_grandpa::AuthorityId,
|
||||
) -> Result<Option<sp_consensus_grandpa::OpaqueKeyOwnershipProof>> {
|
||||
) -> Result<Option<sp_consensus_grandpa::OpaqueKeyOwnershipProof>>
|
||||
where
|
||||
C: ChainWithGrandpa,
|
||||
{
|
||||
self.typed_state_call(
|
||||
SUB_API_GRANDPA_GENERATE_KEY_OWNERSHIP_PROOF.into(),
|
||||
(set_id, authority_id),
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
[package]
|
||||
name = "equivocation-detector"
|
||||
version = "0.1.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
edition = "2021"
|
||||
license = "GPL-3.0-or-later WITH Classpath-exception-2.0"
|
||||
description = "Equivocation detector"
|
||||
|
||||
[dependencies]
|
||||
async-trait = "0.1"
|
||||
bp-header-chain = { path = "../../primitives/header-chain" }
|
||||
finality-relay = { path = "../finality" }
|
||||
relay-utils = { path = "../utils" }
|
||||
@@ -0,0 +1,70 @@
|
||||
// Copyright 2019-2023 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/>.
|
||||
|
||||
use async_trait::async_trait;
|
||||
use bp_header_chain::{FindEquivocations, HeaderFinalityInfo};
|
||||
use finality_relay::{FinalityPipeline, SourceClientBase};
|
||||
use relay_utils::{relay_loop::Client as RelayClient, TransactionTracker};
|
||||
|
||||
pub trait EquivocationDetectionPipeline: FinalityPipeline {
|
||||
/// Block number of the target chain.
|
||||
type TargetNumber: relay_utils::BlockNumberBase;
|
||||
/// The context needed for validating finality proofs.
|
||||
type FinalityVerificationContext;
|
||||
/// The type of the equivocation proof.
|
||||
type EquivocationProof;
|
||||
/// The equivocations finder.
|
||||
type EquivocationsFinder: FindEquivocations<
|
||||
Self::FinalityProof,
|
||||
Self::FinalityVerificationContext,
|
||||
Self::EquivocationProof,
|
||||
>;
|
||||
}
|
||||
|
||||
/// Source client used in equivocation detection loop.
|
||||
#[async_trait]
|
||||
pub trait SourceClient<P: EquivocationDetectionPipeline>: SourceClientBase<P> {
|
||||
/// Transaction tracker to track submitted transactions.
|
||||
type TransactionTracker: TransactionTracker;
|
||||
|
||||
/// Report equivocation.
|
||||
async fn report_equivocation(
|
||||
&self,
|
||||
at: P::Hash,
|
||||
equivocation: P::EquivocationProof,
|
||||
) -> Result<Self::TransactionTracker, Self::Error>;
|
||||
}
|
||||
|
||||
/// Target client used in equivocation detection loop.
|
||||
#[async_trait]
|
||||
pub trait TargetClient<P: EquivocationDetectionPipeline>: RelayClient {
|
||||
/// Get the data stored by the target at the specified block for validating source finality
|
||||
/// proofs.
|
||||
async fn finality_verification_context(
|
||||
&self,
|
||||
at: P::TargetNumber,
|
||||
) -> Result<P::FinalityVerificationContext, Self::Error>;
|
||||
|
||||
/// Get the finality info associated to the source headers synced with the target chain at the
|
||||
/// specified block.
|
||||
async fn synced_headers_finality_info(
|
||||
&self,
|
||||
at: P::TargetNumber,
|
||||
) -> Result<
|
||||
Vec<HeaderFinalityInfo<P::FinalityProof, P::FinalityVerificationContext>>,
|
||||
Self::Error,
|
||||
>;
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
// Copyright 2019-2023 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/>.
|
||||
|
||||
use async_trait::async_trait;
|
||||
use bp_header_chain::FinalityProof;
|
||||
use futures::Stream;
|
||||
use relay_utils::relay_loop::Client as RelayClient;
|
||||
use std::fmt::Debug;
|
||||
|
||||
/// Base finality pipeline.
|
||||
pub trait FinalityPipeline: 'static + Clone + Debug + Send + Sync {
|
||||
/// Name of the finality proofs source.
|
||||
const SOURCE_NAME: &'static str;
|
||||
/// Name of the finality proofs target.
|
||||
const TARGET_NAME: &'static str;
|
||||
|
||||
/// Synced headers are identified by this hash.
|
||||
type Hash: Eq + Clone + Copy + Send + Sync + Debug;
|
||||
/// Synced headers are identified by this number.
|
||||
type Number: relay_utils::BlockNumberBase;
|
||||
/// Finality proof type.
|
||||
type FinalityProof: FinalityProof<Self::Number>;
|
||||
}
|
||||
|
||||
/// Source client used in finality related loops.
|
||||
#[async_trait]
|
||||
pub trait SourceClientBase<P: FinalityPipeline>: RelayClient {
|
||||
/// Stream of new finality proofs. The stream is allowed to miss proofs for some
|
||||
/// headers, even if those headers are mandatory.
|
||||
type FinalityProofsStream: Stream<Item = P::FinalityProof> + Send + Unpin;
|
||||
|
||||
/// Subscribe to new finality proofs.
|
||||
async fn finality_proofs(&self) -> Result<Self::FinalityProofsStream, Self::Error>;
|
||||
}
|
||||
|
||||
/// Target client used in finality related loops.
|
||||
#[async_trait]
|
||||
pub trait TargetClientBase<P: FinalityPipeline>: RelayClient {}
|
||||
@@ -20,11 +20,13 @@
|
||||
//! assume that the persistent proof either exists, or will eventually become available.
|
||||
|
||||
use crate::{
|
||||
sync_loop_metrics::SyncLoopMetrics, FinalityProof, FinalitySyncPipeline, SourceHeader,
|
||||
sync_loop_metrics::SyncLoopMetrics, FinalityPipeline, FinalitySyncPipeline, SourceClientBase,
|
||||
SourceHeader,
|
||||
};
|
||||
|
||||
use async_trait::async_trait;
|
||||
use backoff::backoff::Backoff;
|
||||
use bp_header_chain::FinalityProof;
|
||||
use futures::{select, Future, FutureExt, Stream, StreamExt};
|
||||
use num_traits::{One, Saturating};
|
||||
use relay_utils::{
|
||||
@@ -66,11 +68,7 @@ pub struct FinalitySyncParams {
|
||||
|
||||
/// Source client used in finality synchronization loop.
|
||||
#[async_trait]
|
||||
pub trait SourceClient<P: FinalitySyncPipeline>: RelayClient {
|
||||
/// Stream of new finality proofs. The stream is allowed to miss proofs for some
|
||||
/// headers, even if those headers are mandatory.
|
||||
type FinalityProofsStream: Stream<Item = P::FinalityProof> + Send;
|
||||
|
||||
pub trait SourceClient<P: FinalitySyncPipeline>: SourceClientBase<P> {
|
||||
/// Get best finalized block number.
|
||||
async fn best_finalized_block_number(&self) -> Result<P::Number, Self::Error>;
|
||||
|
||||
@@ -79,9 +77,6 @@ pub trait SourceClient<P: FinalitySyncPipeline>: RelayClient {
|
||||
&self,
|
||||
number: P::Number,
|
||||
) -> Result<(P::Header, Option<P::FinalityProof>), Self::Error>;
|
||||
|
||||
/// Subscribe to new finality proofs.
|
||||
async fn finality_proofs(&self) -> Result<Self::FinalityProofsStream, Self::Error>;
|
||||
}
|
||||
|
||||
/// Target client used in finality synchronization loop.
|
||||
@@ -143,10 +138,10 @@ pub async fn run<P: FinalitySyncPipeline>(
|
||||
pub(crate) type UnjustifiedHeaders<H> = Vec<H>;
|
||||
/// Finality proofs container. Ordered by target header number.
|
||||
pub(crate) type FinalityProofs<P> =
|
||||
Vec<(<P as FinalitySyncPipeline>::Number, <P as FinalitySyncPipeline>::FinalityProof)>;
|
||||
Vec<(<P as FinalityPipeline>::Number, <P as FinalityPipeline>::FinalityProof)>;
|
||||
/// Reference to finality proofs container.
|
||||
pub(crate) type FinalityProofsRef<'a, P> =
|
||||
&'a [(<P as FinalitySyncPipeline>::Number, <P as FinalitySyncPipeline>::FinalityProof)];
|
||||
&'a [(<P as FinalityPipeline>::Number, <P as FinalityPipeline>::FinalityProof)];
|
||||
|
||||
/// Error that may happen inside finality synchronization loop.
|
||||
#[derive(Debug)]
|
||||
|
||||
@@ -26,11 +26,11 @@ use crate::{
|
||||
SourceClient, TargetClient,
|
||||
},
|
||||
sync_loop_metrics::SyncLoopMetrics,
|
||||
FinalityProof, FinalitySyncPipeline, SourceHeader,
|
||||
FinalityPipeline, FinalitySyncPipeline, SourceClientBase, SourceHeader,
|
||||
};
|
||||
|
||||
use async_trait::async_trait;
|
||||
use bp_header_chain::GrandpaConsensusLogReader;
|
||||
use bp_header_chain::{FinalityProof, GrandpaConsensusLogReader};
|
||||
use futures::{FutureExt, Stream, StreamExt};
|
||||
use parking_lot::Mutex;
|
||||
use relay_utils::{
|
||||
@@ -80,15 +80,18 @@ impl MaybeConnectionError for TestError {
|
||||
#[derive(Debug, Clone)]
|
||||
struct TestFinalitySyncPipeline;
|
||||
|
||||
impl FinalitySyncPipeline for TestFinalitySyncPipeline {
|
||||
impl FinalityPipeline for TestFinalitySyncPipeline {
|
||||
const SOURCE_NAME: &'static str = "TestSource";
|
||||
const TARGET_NAME: &'static str = "TestTarget";
|
||||
|
||||
type Hash = TestHash;
|
||||
type Number = TestNumber;
|
||||
type FinalityProof = TestFinalityProof;
|
||||
}
|
||||
|
||||
impl FinalitySyncPipeline for TestFinalitySyncPipeline {
|
||||
type ConsensusLogReader = GrandpaConsensusLogReader<TestNumber>;
|
||||
type Header = TestSourceHeader;
|
||||
type FinalityProof = TestFinalityProof;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
@@ -146,9 +149,18 @@ impl RelayClient for TestSourceClient {
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl SourceClient<TestFinalitySyncPipeline> for TestSourceClient {
|
||||
impl SourceClientBase<TestFinalitySyncPipeline> for TestSourceClient {
|
||||
type FinalityProofsStream = Pin<Box<dyn Stream<Item = TestFinalityProof> + 'static + Send>>;
|
||||
|
||||
async fn finality_proofs(&self) -> Result<Self::FinalityProofsStream, TestError> {
|
||||
let mut data = self.data.lock();
|
||||
(self.on_method_call)(&mut data);
|
||||
Ok(futures::stream::iter(data.source_proofs.clone()).boxed())
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl SourceClient<TestFinalitySyncPipeline> for TestSourceClient {
|
||||
async fn best_finalized_block_number(&self) -> Result<TestNumber, TestError> {
|
||||
let mut data = self.data.lock();
|
||||
(self.on_method_call)(&mut data);
|
||||
@@ -163,12 +175,6 @@ impl SourceClient<TestFinalitySyncPipeline> for TestSourceClient {
|
||||
(self.on_method_call)(&mut data);
|
||||
data.source_headers.get(&number).cloned().ok_or(TestError::NonConnection)
|
||||
}
|
||||
|
||||
async fn finality_proofs(&self) -> Result<Self::FinalityProofsStream, TestError> {
|
||||
let mut data = self.data.lock();
|
||||
(self.on_method_call)(&mut data);
|
||||
Ok(futures::stream::iter(data.source_proofs.clone()).boxed())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
|
||||
@@ -20,34 +20,25 @@
|
||||
//! to submit all source headers to the target node.
|
||||
|
||||
pub use crate::{
|
||||
base::{FinalityPipeline, SourceClientBase},
|
||||
finality_loop::{metrics_prefix, run, FinalitySyncParams, SourceClient, TargetClient},
|
||||
sync_loop_metrics::SyncLoopMetrics,
|
||||
};
|
||||
|
||||
use bp_header_chain::{ConsensusLogReader, FinalityProof};
|
||||
use bp_header_chain::ConsensusLogReader;
|
||||
use std::fmt::Debug;
|
||||
|
||||
mod base;
|
||||
mod finality_loop;
|
||||
mod finality_loop_tests;
|
||||
mod sync_loop_metrics;
|
||||
|
||||
/// Finality proofs synchronization pipeline.
|
||||
pub trait FinalitySyncPipeline: 'static + Clone + Debug + Send + Sync {
|
||||
/// Name of the finality proofs source.
|
||||
const SOURCE_NAME: &'static str;
|
||||
/// Name of the finality proofs target.
|
||||
const TARGET_NAME: &'static str;
|
||||
|
||||
/// Headers we're syncing are identified by this hash.
|
||||
type Hash: Eq + Clone + Copy + Send + Sync + Debug;
|
||||
/// Headers we're syncing are identified by this number.
|
||||
type Number: relay_utils::BlockNumberBase;
|
||||
pub trait FinalitySyncPipeline: FinalityPipeline {
|
||||
/// A reader that can extract the consensus log from the header digest and interpret it.
|
||||
type ConsensusLogReader: ConsensusLogReader;
|
||||
/// Type of header that we're syncing.
|
||||
type Header: SourceHeader<Self::Hash, Self::Number, Self::ConsensusLogReader>;
|
||||
/// Finality proof type.
|
||||
type FinalityProof: FinalityProof<Self::Number>;
|
||||
}
|
||||
|
||||
/// Header that we're receiving from source node.
|
||||
|
||||
@@ -24,6 +24,7 @@ bp-polkadot-core = { path = "../../primitives/polkadot-core" }
|
||||
bp-relayers = { path = "../../primitives/relayers" }
|
||||
bridge-runtime-common = { path = "../../bin/runtime-common" }
|
||||
|
||||
equivocation-detector = { path = "../equivocation" }
|
||||
finality-grandpa = { version = "0.16.2" }
|
||||
finality-relay = { path = "../finality" }
|
||||
parachains-relay = { path = "../parachains" }
|
||||
|
||||
@@ -17,30 +17,96 @@
|
||||
//! Types and functions intended to ease adding of new Substrate -> Substrate
|
||||
//! equivocation detection pipelines.
|
||||
|
||||
use crate::finality_base::SubstrateFinalityPipeline;
|
||||
mod source;
|
||||
mod target;
|
||||
|
||||
use crate::finality_base::{engine::Engine, SubstrateFinalityPipeline, SubstrateFinalityProof};
|
||||
|
||||
use async_trait::async_trait;
|
||||
use bp_runtime::{AccountIdOf, BlockNumberOf, HashOf};
|
||||
use equivocation_detector::EquivocationDetectionPipeline;
|
||||
use finality_relay::FinalityPipeline;
|
||||
use pallet_grandpa::{Call as GrandpaCall, Config as GrandpaConfig};
|
||||
use relay_substrate_client::{AccountKeyPairOf, CallOf, Chain, ChainWithTransactions};
|
||||
use sp_core::Pair;
|
||||
use sp_runtime::traits::{Block, Header};
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use crate::finality_base::engine::Engine;
|
||||
use async_trait::async_trait;
|
||||
use bp_runtime::{BlockNumberOf, HashOf};
|
||||
use pallet_grandpa::{Call as GrandpaCall, Config as GrandpaConfig};
|
||||
use relay_substrate_client::CallOf;
|
||||
use sp_runtime::traits::{Block, Header};
|
||||
/// Convenience trait that adds bounds to `SubstrateEquivocationDetectionPipeline`.
|
||||
pub trait BaseSubstrateEquivocationDetectionPipeline:
|
||||
SubstrateFinalityPipeline<SourceChain = Self::BoundedSourceChain>
|
||||
{
|
||||
/// Bounded `SubstrateFinalityPipeline::SourceChain`.
|
||||
type BoundedSourceChain: ChainWithTransactions<AccountId = Self::BoundedSourceChainAccountId>;
|
||||
|
||||
/// Bounded `AccountIdOf<SubstrateFinalityPipeline::SourceChain>`.
|
||||
type BoundedSourceChainAccountId: From<<AccountKeyPairOf<Self::BoundedSourceChain> as Pair>::Public>
|
||||
+ Send;
|
||||
}
|
||||
|
||||
impl<T> BaseSubstrateEquivocationDetectionPipeline for T
|
||||
where
|
||||
T: SubstrateFinalityPipeline,
|
||||
T::SourceChain: ChainWithTransactions,
|
||||
AccountIdOf<T::SourceChain>: From<<AccountKeyPairOf<Self::SourceChain> as Pair>::Public>,
|
||||
{
|
||||
type BoundedSourceChain = T::SourceChain;
|
||||
type BoundedSourceChainAccountId = AccountIdOf<T::SourceChain>;
|
||||
}
|
||||
|
||||
/// Substrate -> Substrate equivocation detection pipeline.
|
||||
#[async_trait]
|
||||
pub trait SubstrateEquivocationDetectionPipeline: SubstrateFinalityPipeline {
|
||||
pub trait SubstrateEquivocationDetectionPipeline:
|
||||
BaseSubstrateEquivocationDetectionPipeline
|
||||
{
|
||||
/// How the `report_equivocation` call is built ?
|
||||
type ReportEquivocationCallBuilder: ReportEquivocationCallBuilder<Self>;
|
||||
}
|
||||
|
||||
type FinalityProoffOf<P> = <<P as SubstrateFinalityPipeline>::FinalityEngine as Engine<
|
||||
<P as SubstrateFinalityPipeline>::SourceChain,
|
||||
>>::FinalityProof;
|
||||
type FinalityVerificationContextfOf<P> =
|
||||
<<P as SubstrateFinalityPipeline>::FinalityEngine as Engine<
|
||||
<P as SubstrateFinalityPipeline>::SourceChain,
|
||||
>>::FinalityVerificationContext;
|
||||
type EquivocationProofOf<P> = <<P as SubstrateFinalityPipeline>::FinalityEngine as Engine<
|
||||
<P as SubstrateFinalityPipeline>::SourceChain,
|
||||
>>::EquivocationProof;
|
||||
type EquivocationsFinderOf<P> = <<P as SubstrateFinalityPipeline>::FinalityEngine as Engine<
|
||||
<P as SubstrateFinalityPipeline>::SourceChain,
|
||||
>>::EquivocationsFinder;
|
||||
type KeyOwnerProofOf<P> = <<P as SubstrateFinalityPipeline>::FinalityEngine as Engine<
|
||||
<P as SubstrateFinalityPipeline>::SourceChain,
|
||||
>>::KeyOwnerProof;
|
||||
|
||||
/// Adapter that allows a `SubstrateEquivocationDetectionPipeline` to act as an
|
||||
/// `EquivocationDetectionPipeline`.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct EquivocationDetectionPipelineAdapter<P: SubstrateEquivocationDetectionPipeline> {
|
||||
_phantom: PhantomData<P>,
|
||||
}
|
||||
|
||||
impl<P: SubstrateEquivocationDetectionPipeline> FinalityPipeline
|
||||
for EquivocationDetectionPipelineAdapter<P>
|
||||
{
|
||||
const SOURCE_NAME: &'static str = P::SourceChain::NAME;
|
||||
const TARGET_NAME: &'static str = P::TargetChain::NAME;
|
||||
|
||||
type Hash = HashOf<P::SourceChain>;
|
||||
type Number = BlockNumberOf<P::SourceChain>;
|
||||
type FinalityProof = SubstrateFinalityProof<P>;
|
||||
}
|
||||
|
||||
impl<P: SubstrateEquivocationDetectionPipeline> EquivocationDetectionPipeline
|
||||
for EquivocationDetectionPipelineAdapter<P>
|
||||
{
|
||||
type TargetNumber = BlockNumberOf<P::TargetChain>;
|
||||
type FinalityVerificationContext = FinalityVerificationContextfOf<P>;
|
||||
type EquivocationProof = EquivocationProofOf<P>;
|
||||
type EquivocationsFinder = EquivocationsFinderOf<P>;
|
||||
}
|
||||
|
||||
/// Different ways of building `report_equivocation` calls.
|
||||
pub trait ReportEquivocationCallBuilder<P: SubstrateEquivocationDetectionPipeline> {
|
||||
/// Build a `report_equivocation` call to be executed on the source chain.
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
// Copyright 2019-2023 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/>.
|
||||
|
||||
//! Default generic implementation of equivocation source for basic Substrate client.
|
||||
|
||||
use crate::{
|
||||
equivocation::{
|
||||
EquivocationDetectionPipelineAdapter, EquivocationProofOf, ReportEquivocationCallBuilder,
|
||||
SubstrateEquivocationDetectionPipeline,
|
||||
},
|
||||
finality_base::{engine::Engine, finality_proofs, SubstrateFinalityProofsStream},
|
||||
TransactionParams,
|
||||
};
|
||||
|
||||
use async_trait::async_trait;
|
||||
use bp_runtime::{HashOf, TransactionEra};
|
||||
use equivocation_detector::SourceClient;
|
||||
use finality_relay::SourceClientBase;
|
||||
use relay_substrate_client::{
|
||||
AccountKeyPairOf, Client, Error, TransactionTracker, UnsignedTransaction,
|
||||
};
|
||||
use relay_utils::relay_loop::Client as RelayClient;
|
||||
|
||||
/// Substrate node as equivocation source.
|
||||
pub struct SubstrateEquivocationSource<P: SubstrateEquivocationDetectionPipeline> {
|
||||
client: Client<P::SourceChain>,
|
||||
transaction_params: TransactionParams<AccountKeyPairOf<P::SourceChain>>,
|
||||
}
|
||||
|
||||
impl<P: SubstrateEquivocationDetectionPipeline> SubstrateEquivocationSource<P> {}
|
||||
|
||||
impl<P: SubstrateEquivocationDetectionPipeline> Clone for SubstrateEquivocationSource<P> {
|
||||
fn clone(&self) -> Self {
|
||||
Self { client: self.client.clone(), transaction_params: self.transaction_params.clone() }
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<P: SubstrateEquivocationDetectionPipeline> RelayClient for SubstrateEquivocationSource<P> {
|
||||
type Error = Error;
|
||||
|
||||
async fn reconnect(&mut self) -> Result<(), Error> {
|
||||
self.client.reconnect().await
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<P: SubstrateEquivocationDetectionPipeline>
|
||||
SourceClientBase<EquivocationDetectionPipelineAdapter<P>> for SubstrateEquivocationSource<P>
|
||||
{
|
||||
type FinalityProofsStream = SubstrateFinalityProofsStream<P>;
|
||||
|
||||
async fn finality_proofs(&self) -> Result<Self::FinalityProofsStream, Error> {
|
||||
finality_proofs::<P>(&self.client).await
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<P: SubstrateEquivocationDetectionPipeline>
|
||||
SourceClient<EquivocationDetectionPipelineAdapter<P>> for SubstrateEquivocationSource<P>
|
||||
{
|
||||
type TransactionTracker = TransactionTracker<P::SourceChain, Client<P::SourceChain>>;
|
||||
|
||||
async fn report_equivocation(
|
||||
&self,
|
||||
at: HashOf<P::SourceChain>,
|
||||
equivocation: EquivocationProofOf<P>,
|
||||
) -> Result<Self::TransactionTracker, Self::Error> {
|
||||
let key_owner_proof =
|
||||
P::FinalityEngine::generate_source_key_ownership_proof(&self.client, at, &equivocation)
|
||||
.await?;
|
||||
|
||||
let mortality = self.transaction_params.mortality;
|
||||
let call = P::ReportEquivocationCallBuilder::build_report_equivocation_call(
|
||||
equivocation,
|
||||
key_owner_proof,
|
||||
);
|
||||
self.client
|
||||
.submit_and_watch_signed_extrinsic(
|
||||
&self.transaction_params.signer,
|
||||
move |best_block_id, transaction_nonce| {
|
||||
Ok(UnsignedTransaction::new(call.into(), transaction_nonce)
|
||||
.era(TransactionEra::new(best_block_id, mortality)))
|
||||
},
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
// Copyright 2019-2023 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/>.
|
||||
|
||||
//! Default generic implementation of equivocation source for basic Substrate client.
|
||||
|
||||
use crate::{
|
||||
equivocation::{EquivocationDetectionPipelineAdapter, SubstrateEquivocationDetectionPipeline},
|
||||
finality_base::engine::Engine,
|
||||
};
|
||||
|
||||
use crate::equivocation::{FinalityProoffOf, FinalityVerificationContextfOf};
|
||||
use async_trait::async_trait;
|
||||
use bp_header_chain::HeaderFinalityInfo;
|
||||
use bp_runtime::BlockNumberOf;
|
||||
use equivocation_detector::TargetClient;
|
||||
use relay_substrate_client::{Client, Error};
|
||||
use relay_utils::relay_loop::Client as RelayClient;
|
||||
use sp_runtime::traits::Header;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
/// Substrate node as equivocation source.
|
||||
pub struct SubstrateEquivocationTarget<P: SubstrateEquivocationDetectionPipeline> {
|
||||
client: Client<P::TargetChain>,
|
||||
|
||||
_phantom: PhantomData<P>,
|
||||
}
|
||||
|
||||
impl<P: SubstrateEquivocationDetectionPipeline> SubstrateEquivocationTarget<P> {}
|
||||
|
||||
impl<P: SubstrateEquivocationDetectionPipeline> Clone for SubstrateEquivocationTarget<P> {
|
||||
fn clone(&self) -> Self {
|
||||
Self { client: self.client.clone(), _phantom: Default::default() }
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<P: SubstrateEquivocationDetectionPipeline> RelayClient for SubstrateEquivocationTarget<P> {
|
||||
type Error = Error;
|
||||
|
||||
async fn reconnect(&mut self) -> Result<(), Error> {
|
||||
self.client.reconnect().await
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<P: SubstrateEquivocationDetectionPipeline>
|
||||
TargetClient<EquivocationDetectionPipelineAdapter<P>> for SubstrateEquivocationTarget<P>
|
||||
{
|
||||
async fn finality_verification_context(
|
||||
&self,
|
||||
at: BlockNumberOf<P::TargetChain>,
|
||||
) -> Result<FinalityVerificationContextfOf<P>, Self::Error> {
|
||||
P::FinalityEngine::finality_verification_context(
|
||||
&self.client,
|
||||
self.client.header_by_number(at).await?.hash(),
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn synced_headers_finality_info(
|
||||
&self,
|
||||
at: BlockNumberOf<P::TargetChain>,
|
||||
) -> Result<
|
||||
Vec<HeaderFinalityInfo<FinalityProoffOf<P>, FinalityVerificationContextfOf<P>>>,
|
||||
Self::Error,
|
||||
> {
|
||||
P::FinalityEngine::synced_headers_finality_info(
|
||||
&self.client,
|
||||
self.client.header_by_number(at).await?.hash(),
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
@@ -18,21 +18,18 @@
|
||||
//! finality proofs synchronization pipelines.
|
||||
|
||||
use crate::{
|
||||
finality::{
|
||||
source::{SubstrateFinalityProof, SubstrateFinalitySource},
|
||||
target::SubstrateFinalityTarget,
|
||||
},
|
||||
finality::{source::SubstrateFinalitySource, target::SubstrateFinalityTarget},
|
||||
finality_base::{engine::Engine, SubstrateFinalityPipeline, SubstrateFinalityProof},
|
||||
TransactionParams,
|
||||
};
|
||||
|
||||
use crate::finality_base::{engine::Engine, SubstrateFinalityPipeline};
|
||||
use async_trait::async_trait;
|
||||
use bp_header_chain::justification::GrandpaJustification;
|
||||
use finality_relay::FinalitySyncPipeline;
|
||||
use finality_relay::{FinalityPipeline, FinalitySyncPipeline};
|
||||
use pallet_bridge_grandpa::{Call as BridgeGrandpaCall, Config as BridgeGrandpaConfig};
|
||||
use relay_substrate_client::{
|
||||
transaction_stall_timeout, AccountIdOf, AccountKeyPairOf, BlockNumberOf, CallOf, Chain, Client,
|
||||
HashOf, HeaderOf, SyncHeader,
|
||||
transaction_stall_timeout, AccountIdOf, AccountKeyPairOf, BlockNumberOf, CallOf, Chain,
|
||||
ChainWithTransactions, Client, HashOf, HeaderOf, SyncHeader,
|
||||
};
|
||||
use relay_utils::metrics::MetricsParams;
|
||||
use sp_core::Pair;
|
||||
@@ -48,9 +45,31 @@ pub mod target;
|
||||
/// Substrate+GRANDPA based chains (good to know).
|
||||
pub(crate) const RECENT_FINALITY_PROOFS_LIMIT: usize = 4096;
|
||||
|
||||
/// Convenience trait that adds bounds to `SubstrateFinalitySyncPipeline`.
|
||||
pub trait BaseSubstrateFinalitySyncPipeline:
|
||||
SubstrateFinalityPipeline<TargetChain = Self::BoundedTargetChain>
|
||||
{
|
||||
/// Bounded `SubstrateFinalityPipeline::TargetChain`.
|
||||
type BoundedTargetChain: ChainWithTransactions<AccountId = Self::BoundedTargetChainAccountId>;
|
||||
|
||||
/// Bounded `AccountIdOf<SubstrateFinalityPipeline::TargetChain>`.
|
||||
type BoundedTargetChainAccountId: From<<AccountKeyPairOf<Self::BoundedTargetChain> as Pair>::Public>
|
||||
+ Send;
|
||||
}
|
||||
|
||||
impl<T> BaseSubstrateFinalitySyncPipeline for T
|
||||
where
|
||||
T: SubstrateFinalityPipeline,
|
||||
T::TargetChain: ChainWithTransactions,
|
||||
AccountIdOf<T::TargetChain>: From<<AccountKeyPairOf<Self::TargetChain> as Pair>::Public>,
|
||||
{
|
||||
type BoundedTargetChain = T::TargetChain;
|
||||
type BoundedTargetChainAccountId = AccountIdOf<T::TargetChain>;
|
||||
}
|
||||
|
||||
/// Substrate -> Substrate finality proofs synchronization pipeline.
|
||||
#[async_trait]
|
||||
pub trait SubstrateFinalitySyncPipeline: SubstrateFinalityPipeline {
|
||||
pub trait SubstrateFinalitySyncPipeline: BaseSubstrateFinalitySyncPipeline {
|
||||
/// How submit finality proof call is built?
|
||||
type SubmitFinalityProofCallBuilder: SubmitFinalityProofCallBuilder<Self>;
|
||||
|
||||
@@ -70,15 +89,18 @@ pub struct FinalitySyncPipelineAdapter<P: SubstrateFinalitySyncPipeline> {
|
||||
_phantom: PhantomData<P>,
|
||||
}
|
||||
|
||||
impl<P: SubstrateFinalitySyncPipeline> FinalitySyncPipeline for FinalitySyncPipelineAdapter<P> {
|
||||
impl<P: SubstrateFinalitySyncPipeline> FinalityPipeline for FinalitySyncPipelineAdapter<P> {
|
||||
const SOURCE_NAME: &'static str = P::SourceChain::NAME;
|
||||
const TARGET_NAME: &'static str = P::TargetChain::NAME;
|
||||
|
||||
type Hash = HashOf<P::SourceChain>;
|
||||
type Number = BlockNumberOf<P::SourceChain>;
|
||||
type FinalityProof = SubstrateFinalityProof<P>;
|
||||
}
|
||||
|
||||
impl<P: SubstrateFinalitySyncPipeline> FinalitySyncPipeline for FinalitySyncPipelineAdapter<P> {
|
||||
type ConsensusLogReader = <P::FinalityEngine as Engine<P::SourceChain>>::ConsensusLogReader;
|
||||
type Header = SyncHeader<HeaderOf<P::SourceChain>>;
|
||||
type FinalityProof = SubstrateFinalityProof<P>;
|
||||
}
|
||||
|
||||
/// Different ways of building `submit_finality_proof` calls.
|
||||
@@ -165,10 +187,7 @@ pub async fn run<P: SubstrateFinalitySyncPipeline>(
|
||||
only_mandatory_headers: bool,
|
||||
transaction_params: TransactionParams<AccountKeyPairOf<P::TargetChain>>,
|
||||
metrics_params: MetricsParams,
|
||||
) -> anyhow::Result<()>
|
||||
where
|
||||
AccountIdOf<P::TargetChain>: From<<AccountKeyPairOf<P::TargetChain> as Pair>::Public>,
|
||||
{
|
||||
) -> anyhow::Result<()> {
|
||||
log::info!(
|
||||
target: "bridge",
|
||||
"Starting {} -> {} finality proof relay",
|
||||
|
||||
@@ -18,38 +18,27 @@
|
||||
|
||||
use crate::{
|
||||
finality::{FinalitySyncPipelineAdapter, SubstrateFinalitySyncPipeline},
|
||||
finality_base::engine::Engine,
|
||||
finality_base::{
|
||||
engine::Engine, finality_proofs, SubstrateFinalityProof, SubstrateFinalityProofsStream,
|
||||
},
|
||||
};
|
||||
|
||||
use crate::finality_base::SubstrateFinalityPipeline;
|
||||
use async_std::sync::{Arc, Mutex};
|
||||
use async_trait::async_trait;
|
||||
use bp_header_chain::FinalityProof;
|
||||
use codec::Decode;
|
||||
use finality_relay::SourceClient;
|
||||
use finality_relay::{SourceClient, SourceClientBase};
|
||||
use futures::{
|
||||
select,
|
||||
stream::{try_unfold, unfold, Stream, StreamExt, TryStreamExt},
|
||||
stream::{try_unfold, Stream, StreamExt, TryStreamExt},
|
||||
};
|
||||
use num_traits::One;
|
||||
use relay_substrate_client::{
|
||||
BlockNumberOf, BlockWithJustification, Chain, Client, Error, HeaderOf,
|
||||
};
|
||||
use relay_substrate_client::{BlockNumberOf, BlockWithJustification, Client, Error, HeaderOf};
|
||||
use relay_utils::{relay_loop::Client as RelayClient, UniqueSaturatedInto};
|
||||
use std::pin::Pin;
|
||||
|
||||
/// Shared updatable reference to the maximal header number that we want to sync from the source.
|
||||
pub type RequiredHeaderNumberRef<C> = Arc<Mutex<<C as bp_runtime::Chain>::BlockNumber>>;
|
||||
|
||||
/// Substrate finality proofs stream.
|
||||
pub type SubstrateFinalityProofsStream<P> =
|
||||
Pin<Box<dyn Stream<Item = SubstrateFinalityProof<P>> + Send>>;
|
||||
|
||||
/// Substrate finality proof. Specific to the used `FinalityEngine`.
|
||||
pub type SubstrateFinalityProof<P> = <<P as SubstrateFinalityPipeline>::FinalityEngine as Engine<
|
||||
<P as SubstrateFinalityPipeline>::SourceChain,
|
||||
>>::FinalityProof;
|
||||
|
||||
/// Substrate node as finality source.
|
||||
pub struct SubstrateFinalitySource<P: SubstrateFinalitySyncPipeline> {
|
||||
client: Client<P::SourceChain>,
|
||||
@@ -204,11 +193,20 @@ impl<P: SubstrateFinalitySyncPipeline> RelayClient for SubstrateFinalitySource<P
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<P: SubstrateFinalitySyncPipeline> SourceClient<FinalitySyncPipelineAdapter<P>>
|
||||
impl<P: SubstrateFinalitySyncPipeline> SourceClientBase<FinalitySyncPipelineAdapter<P>>
|
||||
for SubstrateFinalitySource<P>
|
||||
{
|
||||
type FinalityProofsStream = SubstrateFinalityProofsStream<P>;
|
||||
|
||||
async fn finality_proofs(&self) -> Result<Self::FinalityProofsStream, Error> {
|
||||
finality_proofs::<P>(&self.client).await
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<P: SubstrateFinalitySyncPipeline> SourceClient<FinalitySyncPipelineAdapter<P>>
|
||||
for SubstrateFinalitySource<P>
|
||||
{
|
||||
async fn best_finalized_block_number(&self) -> Result<BlockNumberOf<P::SourceChain>, Error> {
|
||||
let mut finalized_header_number = self.on_chain_best_finalized_block_number().await?;
|
||||
// never return block number larger than requested. This way we'll never sync headers
|
||||
@@ -234,46 +232,6 @@ impl<P: SubstrateFinalitySyncPipeline> SourceClient<FinalitySyncPipelineAdapter<
|
||||
> {
|
||||
header_and_finality_proof::<P>(&self.client, number).await
|
||||
}
|
||||
|
||||
async fn finality_proofs(&self) -> Result<Self::FinalityProofsStream, Error> {
|
||||
Ok(unfold(
|
||||
P::FinalityEngine::source_finality_proofs(&self.client).await?,
|
||||
move |subscription| async move {
|
||||
loop {
|
||||
let log_error = |err| {
|
||||
log::error!(
|
||||
target: "bridge",
|
||||
"Failed to read justification target from the {} justifications stream: {:?}",
|
||||
P::SourceChain::NAME,
|
||||
err,
|
||||
);
|
||||
};
|
||||
|
||||
let next_justification = subscription
|
||||
.next()
|
||||
.await
|
||||
.map_err(|err| log_error(err.to_string()))
|
||||
.ok()??;
|
||||
|
||||
let decoded_justification =
|
||||
<P::FinalityEngine as Engine<P::SourceChain>>::FinalityProof::decode(
|
||||
&mut &next_justification[..],
|
||||
);
|
||||
|
||||
let justification = match decoded_justification {
|
||||
Ok(j) => j,
|
||||
Err(err) => {
|
||||
log_error(format!("decode failed with error {err:?}"));
|
||||
continue
|
||||
},
|
||||
};
|
||||
|
||||
return Some((justification, subscription))
|
||||
}
|
||||
},
|
||||
)
|
||||
.boxed())
|
||||
}
|
||||
}
|
||||
|
||||
async fn header_and_finality_proof<P: SubstrateFinalitySyncPipeline>(
|
||||
|
||||
@@ -18,21 +18,19 @@
|
||||
|
||||
use crate::{
|
||||
finality::{
|
||||
source::SubstrateFinalityProof, FinalitySyncPipelineAdapter,
|
||||
SubmitFinalityProofCallBuilder, SubstrateFinalitySyncPipeline,
|
||||
FinalitySyncPipelineAdapter, SubmitFinalityProofCallBuilder, SubstrateFinalitySyncPipeline,
|
||||
},
|
||||
finality_base::engine::Engine,
|
||||
finality_base::{engine::Engine, SubstrateFinalityProof},
|
||||
TransactionParams,
|
||||
};
|
||||
|
||||
use async_trait::async_trait;
|
||||
use finality_relay::TargetClient;
|
||||
use relay_substrate_client::{
|
||||
AccountIdOf, AccountKeyPairOf, Client, Error, HeaderIdOf, HeaderOf, SyncHeader, TransactionEra,
|
||||
AccountKeyPairOf, Client, Error, HeaderIdOf, HeaderOf, SyncHeader, TransactionEra,
|
||||
TransactionTracker, UnsignedTransaction,
|
||||
};
|
||||
use relay_utils::relay_loop::Client as RelayClient;
|
||||
use sp_core::Pair;
|
||||
|
||||
/// Substrate client as Substrate finality target.
|
||||
pub struct SubstrateFinalityTarget<P: SubstrateFinalitySyncPipeline> {
|
||||
@@ -86,8 +84,6 @@ impl<P: SubstrateFinalitySyncPipeline> RelayClient for SubstrateFinalityTarget<P
|
||||
#[async_trait]
|
||||
impl<P: SubstrateFinalitySyncPipeline> TargetClient<FinalitySyncPipelineAdapter<P>>
|
||||
for SubstrateFinalityTarget<P>
|
||||
where
|
||||
AccountIdOf<P::TargetChain>: From<<AccountKeyPairOf<P::TargetChain> as Pair>::Public>,
|
||||
{
|
||||
type TransactionTracker = TransactionTracker<P::TargetChain, Client<P::TargetChain>>;
|
||||
|
||||
@@ -116,7 +112,7 @@ where
|
||||
P::FinalityEngine::optimize_proof(&self.client, &header, &mut proof).await?;
|
||||
|
||||
// now we may submit optimized finality proof
|
||||
let transaction_params = self.transaction_params.clone();
|
||||
let mortality = self.transaction_params.mortality;
|
||||
let call =
|
||||
P::SubmitFinalityProofCallBuilder::build_submit_finality_proof_call(header, proof);
|
||||
self.client
|
||||
@@ -124,7 +120,7 @@ where
|
||||
&self.transaction_params.signer,
|
||||
move |best_block_id, transaction_nonce| {
|
||||
Ok(UnsignedTransaction::new(call.into(), transaction_nonce)
|
||||
.era(TransactionEra::new(best_block_id, transaction_params.mortality)))
|
||||
.era(TransactionEra::new(best_block_id, mortality)))
|
||||
},
|
||||
)
|
||||
.await
|
||||
|
||||
@@ -19,13 +19,15 @@
|
||||
use crate::error::Error;
|
||||
use async_trait::async_trait;
|
||||
use bp_header_chain::{
|
||||
justification::{verify_and_optimize_justification, GrandpaJustification},
|
||||
ChainWithGrandpa as ChainWithGrandpaBase, ConsensusLogReader, FinalityProof,
|
||||
GrandpaConsensusLogReader,
|
||||
justification::{
|
||||
verify_and_optimize_justification, GrandpaEquivocationsFinder, GrandpaJustification,
|
||||
JustificationVerificationContext,
|
||||
},
|
||||
AuthoritySet, ConsensusLogReader, FinalityProof, FindEquivocations, GrandpaConsensusLogReader,
|
||||
HeaderFinalityInfo, HeaderGrandpaInfo, StoredHeaderGrandpaInfo,
|
||||
};
|
||||
use bp_runtime::{BasicOperatingMode, HeaderIdProvider, OperatingMode};
|
||||
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,
|
||||
@@ -33,7 +35,7 @@ use relay_substrate_client::{
|
||||
};
|
||||
use sp_consensus_grandpa::{AuthorityList as GrandpaAuthoritiesSet, GRANDPA_ENGINE_ID};
|
||||
use sp_core::{storage::StorageKey, Bytes};
|
||||
use sp_runtime::{traits::Header, ConsensusEngineId};
|
||||
use sp_runtime::{scale_info::TypeInfo, traits::Header, ConsensusEngineId};
|
||||
use std::marker::PhantomData;
|
||||
|
||||
/// Finality engine, used by the Substrate chain.
|
||||
@@ -47,10 +49,18 @@ pub trait Engine<C: Chain>: Send {
|
||||
type FinalityClient: SubstrateFinalityClient<C>;
|
||||
/// Type of finality proofs, used by consensus engine.
|
||||
type FinalityProof: FinalityProof<BlockNumberOf<C>> + Decode + Encode;
|
||||
/// The context needed for verifying finality proofs.
|
||||
type FinalityVerificationContext;
|
||||
/// The type of the equivocation proof used by the consensus engine.
|
||||
type EquivocationProof;
|
||||
type EquivocationProof: Send + Sync;
|
||||
/// The equivocations finder.
|
||||
type EquivocationsFinder: FindEquivocations<
|
||||
Self::FinalityProof,
|
||||
Self::FinalityVerificationContext,
|
||||
Self::EquivocationProof,
|
||||
>;
|
||||
/// The type of the key owner proof used by the consensus engine.
|
||||
type KeyOwnerProof;
|
||||
type KeyOwnerProof: Send;
|
||||
/// Type of bridge pallet initialization data.
|
||||
type InitializationData: std::fmt::Debug + Send + Sync + 'static;
|
||||
/// Type of bridge pallet operating mode.
|
||||
@@ -105,6 +115,29 @@ pub trait Engine<C: Chain>: Send {
|
||||
async fn prepare_initialization_data(
|
||||
client: Client<C>,
|
||||
) -> Result<Self::InitializationData, Error<HashOf<C>, BlockNumberOf<C>>>;
|
||||
|
||||
/// Get the context needed for validating a finality proof.
|
||||
async fn finality_verification_context<TargetChain: Chain>(
|
||||
target_client: &Client<TargetChain>,
|
||||
at: HashOf<TargetChain>,
|
||||
) -> Result<Self::FinalityVerificationContext, SubstrateError>;
|
||||
|
||||
/// Returns the finality info associated to the source headers synced with the target
|
||||
/// at the provided block.
|
||||
async fn synced_headers_finality_info<TargetChain: Chain>(
|
||||
target_client: &Client<TargetChain>,
|
||||
at: TargetChain::Hash,
|
||||
) -> Result<
|
||||
Vec<HeaderFinalityInfo<Self::FinalityProof, Self::FinalityVerificationContext>>,
|
||||
SubstrateError,
|
||||
>;
|
||||
|
||||
/// Generate key ownership proof for the provided equivocation.
|
||||
async fn generate_source_key_ownership_proof(
|
||||
source_client: &Client<C>,
|
||||
at: C::Hash,
|
||||
equivocation: &Self::EquivocationProof,
|
||||
) -> Result<Self::KeyOwnerProof, SubstrateError>;
|
||||
}
|
||||
|
||||
/// GRANDPA finality engine.
|
||||
@@ -142,21 +175,19 @@ impl<C: ChainWithGrandpa> Engine<C> for Grandpa<C> {
|
||||
type ConsensusLogReader = GrandpaConsensusLogReader<<C::Header as Header>::Number>;
|
||||
type FinalityClient = SubstrateGrandpaFinalityClient;
|
||||
type FinalityProof = GrandpaJustification<HeaderOf<C>>;
|
||||
type FinalityVerificationContext = JustificationVerificationContext;
|
||||
type EquivocationProof = sp_consensus_grandpa::EquivocationProof<HashOf<C>, BlockNumberOf<C>>;
|
||||
type EquivocationsFinder = GrandpaEquivocationsFinder<C>;
|
||||
type KeyOwnerProof = C::KeyOwnerProof;
|
||||
type InitializationData = bp_header_chain::InitializationData<C::Header>;
|
||||
type OperatingMode = BasicOperatingMode;
|
||||
|
||||
fn is_initialized_key() -> StorageKey {
|
||||
bp_header_chain::storage_keys::best_finalized_key(
|
||||
C::ChainWithGrandpa::WITH_CHAIN_GRANDPA_PALLET_NAME,
|
||||
)
|
||||
bp_header_chain::storage_keys::best_finalized_key(C::WITH_CHAIN_GRANDPA_PALLET_NAME)
|
||||
}
|
||||
|
||||
fn pallet_operating_mode_key() -> StorageKey {
|
||||
bp_header_chain::storage_keys::pallet_operating_mode_key(
|
||||
C::ChainWithGrandpa::WITH_CHAIN_GRANDPA_PALLET_NAME,
|
||||
)
|
||||
bp_header_chain::storage_keys::pallet_operating_mode_key(C::WITH_CHAIN_GRANDPA_PALLET_NAME)
|
||||
}
|
||||
|
||||
async fn optimize_proof<TargetChain: Chain>(
|
||||
@@ -164,31 +195,18 @@ impl<C: ChainWithGrandpa> Engine<C> for Grandpa<C> {
|
||||
header: &C::Header,
|
||||
proof: &mut Self::FinalityProof,
|
||||
) -> Result<(), SubstrateError> {
|
||||
let current_authority_set_key = bp_header_chain::storage_keys::current_authority_set_key(
|
||||
C::ChainWithGrandpa::WITH_CHAIN_GRANDPA_PALLET_NAME,
|
||||
);
|
||||
let (authority_set, authority_set_id): (
|
||||
sp_consensus_grandpa::AuthorityList,
|
||||
sp_consensus_grandpa::SetId,
|
||||
) = target_client
|
||||
.storage_value(current_authority_set_key, None)
|
||||
.await?
|
||||
.map(Ok)
|
||||
.unwrap_or(Err(SubstrateError::Custom(format!(
|
||||
"{} `CurrentAuthoritySet` is missing from the {} storage",
|
||||
C::NAME,
|
||||
TargetChain::NAME,
|
||||
))))?;
|
||||
let authority_set =
|
||||
finality_grandpa::voter_set::VoterSet::new(authority_set).expect("TODO");
|
||||
let verification_context = Grandpa::<C>::finality_verification_context(
|
||||
target_client,
|
||||
target_client.best_header().await?.hash(),
|
||||
)
|
||||
.await?;
|
||||
// we're risking with race here - we have decided to submit justification some time ago and
|
||||
// actual authorities set (which we have read now) may have changed, so this
|
||||
// `optimize_justification` may fail. But if target chain is configured properly, it'll fail
|
||||
// anyway, after we submit transaction and failing earlier is better. So - it is fine
|
||||
verify_and_optimize_justification(
|
||||
(header.hash(), *header.number()),
|
||||
authority_set_id,
|
||||
&authority_set,
|
||||
&verification_context,
|
||||
proof,
|
||||
)
|
||||
.map_err(|e| {
|
||||
@@ -275,8 +293,6 @@ impl<C: ChainWithGrandpa> Engine<C> for Grandpa<C> {
|
||||
// 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: {}",
|
||||
@@ -286,8 +302,14 @@ impl<C: ChainWithGrandpa> Engine<C> for Grandpa<C> {
|
||||
|
||||
let is_valid_set_id = verify_and_optimize_justification(
|
||||
(initial_header_hash, initial_header_number),
|
||||
initial_authorities_set_id,
|
||||
&authorities_for_verification,
|
||||
&AuthoritySet {
|
||||
authorities: authorities_for_verification.clone(),
|
||||
set_id: initial_authorities_set_id,
|
||||
}
|
||||
.try_into()
|
||||
.map_err(|_| {
|
||||
Error::ReadInvalidAuthorities(C::NAME, authorities_for_verification.clone())
|
||||
})?,
|
||||
&mut justification.clone(),
|
||||
)
|
||||
.is_ok();
|
||||
@@ -317,4 +339,82 @@ impl<C: ChainWithGrandpa> Engine<C> for Grandpa<C> {
|
||||
operating_mode: BasicOperatingMode::Normal,
|
||||
})
|
||||
}
|
||||
|
||||
async fn finality_verification_context<TargetChain: Chain>(
|
||||
target_client: &Client<TargetChain>,
|
||||
at: HashOf<TargetChain>,
|
||||
) -> Result<Self::FinalityVerificationContext, SubstrateError> {
|
||||
let current_authority_set_key = bp_header_chain::storage_keys::current_authority_set_key(
|
||||
C::WITH_CHAIN_GRANDPA_PALLET_NAME,
|
||||
);
|
||||
let authority_set: AuthoritySet = target_client
|
||||
.storage_value(current_authority_set_key, Some(at))
|
||||
.await?
|
||||
.map(Ok)
|
||||
.unwrap_or(Err(SubstrateError::Custom(format!(
|
||||
"{} `CurrentAuthoritySet` is missing from the {} storage",
|
||||
C::NAME,
|
||||
TargetChain::NAME,
|
||||
))))?;
|
||||
|
||||
authority_set.try_into().map_err(|e| {
|
||||
SubstrateError::Custom(format!(
|
||||
"{} `CurrentAuthoritySet` from the {} storage is invalid: {e:?}",
|
||||
C::NAME,
|
||||
TargetChain::NAME,
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
async fn synced_headers_finality_info<TargetChain: Chain>(
|
||||
target_client: &Client<TargetChain>,
|
||||
at: TargetChain::Hash,
|
||||
) -> Result<Vec<HeaderGrandpaInfo<HeaderOf<C>>>, SubstrateError> {
|
||||
let stored_headers_grandpa_info: Vec<StoredHeaderGrandpaInfo<HeaderOf<C>>> = target_client
|
||||
.typed_state_call(C::SYNCED_HEADERS_GRANDPA_INFO_METHOD.to_string(), (), Some(at))
|
||||
.await?;
|
||||
|
||||
let mut headers_grandpa_info = vec![];
|
||||
for stored_header_grandpa_info in stored_headers_grandpa_info {
|
||||
headers_grandpa_info.push(stored_header_grandpa_info.try_into().map_err(|e| {
|
||||
SubstrateError::Custom(format!(
|
||||
"{} `AuthoritySet` synced to {} is invalid: {e:?} ",
|
||||
C::NAME,
|
||||
TargetChain::NAME,
|
||||
))
|
||||
})?);
|
||||
}
|
||||
|
||||
Ok(headers_grandpa_info)
|
||||
}
|
||||
|
||||
async fn generate_source_key_ownership_proof(
|
||||
source_client: &Client<C>,
|
||||
at: C::Hash,
|
||||
equivocation: &Self::EquivocationProof,
|
||||
) -> Result<Self::KeyOwnerProof, SubstrateError> {
|
||||
let set_id = equivocation.set_id();
|
||||
let offender = equivocation.offender();
|
||||
|
||||
let opaque_key_owner_proof = source_client
|
||||
.generate_grandpa_key_ownership_proof(at, set_id, offender.clone())
|
||||
.await?
|
||||
.ok_or(SubstrateError::Custom(format!(
|
||||
"Couldn't get GRANDPA key ownership proof from {} at block: {at} \
|
||||
for offender: {:?}, set_id: {set_id} ",
|
||||
C::NAME,
|
||||
offender.clone(),
|
||||
)))?;
|
||||
|
||||
let key_owner_proof =
|
||||
opaque_key_owner_proof.decode().ok_or(SubstrateError::Custom(format!(
|
||||
"Couldn't decode GRANDPA `OpaqueKeyOwnnershipProof` from {} at block: {at}
|
||||
to `{:?}` for offender: {:?}, set_id: {set_id}, at block: {at}",
|
||||
C::NAME,
|
||||
<C::KeyOwnerProof as TypeInfo>::type_info().path,
|
||||
offender.clone(),
|
||||
)))?;
|
||||
|
||||
Ok(key_owner_proof)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,9 +19,12 @@
|
||||
|
||||
pub mod engine;
|
||||
|
||||
use crate::finality_base::engine::Engine;
|
||||
use async_trait::async_trait;
|
||||
use relay_substrate_client::{Chain, ChainWithTransactions};
|
||||
use std::fmt::Debug;
|
||||
use codec::Decode;
|
||||
use futures::{stream::unfold, Stream, StreamExt};
|
||||
use relay_substrate_client::{Chain, Client, Error};
|
||||
use std::{fmt::Debug, pin::Pin};
|
||||
|
||||
/// Substrate -> Substrate finality related pipeline.
|
||||
#[async_trait]
|
||||
@@ -29,7 +32,56 @@ pub trait SubstrateFinalityPipeline: 'static + Clone + Debug + Send + Sync {
|
||||
/// Headers of this chain are submitted to the `TargetChain`.
|
||||
type SourceChain: Chain;
|
||||
/// Headers of the `SourceChain` are submitted to this chain.
|
||||
type TargetChain: ChainWithTransactions;
|
||||
type TargetChain: Chain;
|
||||
/// Finality engine.
|
||||
type FinalityEngine: engine::Engine<Self::SourceChain>;
|
||||
type FinalityEngine: Engine<Self::SourceChain>;
|
||||
}
|
||||
|
||||
/// Substrate finality proof. Specific to the used `FinalityEngine`.
|
||||
pub type SubstrateFinalityProof<P> = <<P as SubstrateFinalityPipeline>::FinalityEngine as Engine<
|
||||
<P as SubstrateFinalityPipeline>::SourceChain,
|
||||
>>::FinalityProof;
|
||||
|
||||
/// Substrate finality proofs stream.
|
||||
pub type SubstrateFinalityProofsStream<P> =
|
||||
Pin<Box<dyn Stream<Item = SubstrateFinalityProof<P>> + Send>>;
|
||||
|
||||
/// Subscribe to new finality proofs.
|
||||
pub async fn finality_proofs<P: SubstrateFinalityPipeline>(
|
||||
client: &Client<P::SourceChain>,
|
||||
) -> Result<SubstrateFinalityProofsStream<P>, Error> {
|
||||
Ok(unfold(
|
||||
P::FinalityEngine::source_finality_proofs(client).await?,
|
||||
move |subscription| async move {
|
||||
loop {
|
||||
let log_error = |err| {
|
||||
log::error!(
|
||||
target: "bridge",
|
||||
"Failed to read justification target from the {} justifications stream: {:?}",
|
||||
P::SourceChain::NAME,
|
||||
err,
|
||||
);
|
||||
};
|
||||
|
||||
let next_justification =
|
||||
subscription.next().await.map_err(|err| log_error(err.to_string())).ok()??;
|
||||
|
||||
let decoded_justification =
|
||||
<P::FinalityEngine as Engine<P::SourceChain>>::FinalityProof::decode(
|
||||
&mut &next_justification[..],
|
||||
);
|
||||
|
||||
let justification = match decoded_justification {
|
||||
Ok(j) => j,
|
||||
Err(err) => {
|
||||
log_error(format!("decode failed with error {err:?}"));
|
||||
continue
|
||||
},
|
||||
};
|
||||
|
||||
return Some((justification, subscription))
|
||||
}
|
||||
},
|
||||
)
|
||||
.boxed())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user