From 1d3666bc4e64c9c712093ac74d10596e08c0d124 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Wed, 30 Sep 2020 14:03:57 +0300 Subject: [PATCH] Substrate relay stub (#376) * substrate-relay: initial commit * MillauHeaderApi and RialtoHeaderApi * post-merge fixes + TODOs + compilation --- bridges/bin/millau-runtime/Cargo.toml | 2 + bridges/bin/millau-runtime/src/lib.rs | 4 +- bridges/bin/rialto-runtime/Cargo.toml | 4 + bridges/bin/rialto-runtime/src/lib.rs | 21 ++- bridges/primitives/millau/Cargo.toml | 22 +++ bridges/primitives/millau/src/lib.rs | 49 +++++++ bridges/primitives/rialto/Cargo.toml | 22 +++ bridges/primitives/rialto/src/lib.rs | 49 +++++++ .../ethereum/src/substrate_sync_loop.rs | 63 +-------- .../relays/headers-relay/src/sync_types.rs | 8 +- bridges/relays/millau-client/Cargo.toml | 13 +- bridges/relays/millau-client/src/lib.rs | 36 ++++- bridges/relays/rialto-client/src/lib.rs | 9 ++ bridges/relays/substrate-client/Cargo.toml | 1 + bridges/relays/substrate-client/src/chain.rs | 33 ++++- bridges/relays/substrate-client/src/error.rs | 6 + .../substrate-client/src/headers_source.rs | 99 ++++++++++++++ bridges/relays/substrate-client/src/lib.rs | 4 +- bridges/relays/substrate/Cargo.toml | 22 +++ bridges/relays/substrate/src/cli.rs | 65 +++++++++ bridges/relays/substrate/src/main.rs | 66 +++++++++ .../substrate/src/millau_headers_to_rialto.rs | 125 ++++++++++++++++++ 22 files changed, 652 insertions(+), 71 deletions(-) create mode 100644 bridges/primitives/millau/Cargo.toml create mode 100644 bridges/primitives/millau/src/lib.rs create mode 100644 bridges/primitives/rialto/Cargo.toml create mode 100644 bridges/primitives/rialto/src/lib.rs create mode 100644 bridges/relays/substrate-client/src/headers_source.rs create mode 100644 bridges/relays/substrate/Cargo.toml create mode 100644 bridges/relays/substrate/src/cli.rs create mode 100644 bridges/relays/substrate/src/main.rs create mode 100644 bridges/relays/substrate/src/millau_headers_to_rialto.rs diff --git a/bridges/bin/millau-runtime/Cargo.toml b/bridges/bin/millau-runtime/Cargo.toml index e766a58d790fd..fd24613039d84 100644 --- a/bridges/bin/millau-runtime/Cargo.toml +++ b/bridges/bin/millau-runtime/Cargo.toml @@ -8,6 +8,7 @@ repository = "https://github.com/paritytech/parity-bridges-common/" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] +bp-millau = { path = "../../primitives/millau", default-features = false } hex-literal = "0.3" [dependencies.codec] @@ -217,6 +218,7 @@ git = "https://github.com/paritytech/substrate/" default = ["std"] std = [ "bp-message-lane/std", + "bp-millau/std", "codec/std", "frame-benchmarking/std", "frame-executive/std", diff --git a/bridges/bin/millau-runtime/src/lib.rs b/bridges/bin/millau-runtime/src/lib.rs index 394cb0d1c82b0..1adab7e6a2ec4 100644 --- a/bridges/bin/millau-runtime/src/lib.rs +++ b/bridges/bin/millau-runtime/src/lib.rs @@ -61,7 +61,7 @@ pub use sp_runtime::BuildStorage; pub use sp_runtime::{Perbill, Permill}; /// An index to a block. -pub type BlockNumber = u32; +pub type BlockNumber = bp_millau::BlockNumber; /// Alias to 512-bit hash when used in the context of a transaction signature on the chain. pub type Signature = MultiSignature; @@ -81,7 +81,7 @@ pub type Balance = u128; pub type Index = u32; /// A hash of some data used by the chain. -pub type Hash = sp_core::H256; +pub type Hash = bp_millau::Hash; /// Digest item type. pub type DigestItem = generic::DigestItem; diff --git a/bridges/bin/rialto-runtime/Cargo.toml b/bridges/bin/rialto-runtime/Cargo.toml index 5ea4c9fc90238..495948486cbe2 100644 --- a/bridges/bin/rialto-runtime/Cargo.toml +++ b/bridges/bin/rialto-runtime/Cargo.toml @@ -8,6 +8,8 @@ repository = "https://github.com/paritytech/parity-bridges-common/" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] +bp-millau = { path = "../../primitives/millau", default-features = false } +bp-rialto = { path = "../../primitives/rialto", default-features = false } hex-literal = "0.3" [dependencies.codec] @@ -256,6 +258,8 @@ std = [ "bp-currency-exchange/std", "bp-eth-poa/std", "bp-message-lane/std", + "bp-millau/std", + "bp-rialto/std", "codec/std", "frame-benchmarking/std", "frame-executive/std", diff --git a/bridges/bin/rialto-runtime/src/lib.rs b/bridges/bin/rialto-runtime/src/lib.rs index f0e1e77154b7d..2d0c6fbb0c663 100644 --- a/bridges/bin/rialto-runtime/src/lib.rs +++ b/bridges/bin/rialto-runtime/src/lib.rs @@ -70,7 +70,7 @@ pub use sp_runtime::BuildStorage; pub use sp_runtime::{Perbill, Permill}; /// An index to a block. -pub type BlockNumber = u32; +pub type BlockNumber = bp_rialto::BlockNumber; /// Alias to 512-bit hash when used in the context of a transaction signature on the chain. pub type Signature = MultiSignature; @@ -90,7 +90,7 @@ pub type Balance = u128; pub type Index = u32; /// A hash of some data used by the chain. -pub type Hash = sp_core::H256; +pub type Hash = bp_rialto::Hash; /// Digest item type. pub type DigestItem = generic::DigestItem; @@ -574,6 +574,23 @@ impl_runtime_apis! { } } + impl bp_millau::MillauHeaderApi for Runtime { + fn best_block() -> (bp_millau::BlockNumber, bp_millau::Hash) { + unimplemented!("https://github.com/paritytech/parity-bridges-common/issues/368") + } + + fn finalized_block() -> (bp_millau::BlockNumber, bp_millau::Hash) { + unimplemented!("https://github.com/paritytech/parity-bridges-common/issues/368") + } + + fn incomplete_headers() -> Vec<(bp_millau::BlockNumber, bp_millau::Hash)> { + unimplemented!("https://github.com/paritytech/parity-bridges-common/issues/368") + } + + fn is_known_block(_hash: bp_millau::Hash) -> bool { + unimplemented!("https://github.com/paritytech/parity-bridges-common/issues/368") + } + } impl sp_transaction_pool::runtime_api::TaggedTransactionQueue for Runtime { fn validate_transaction( diff --git a/bridges/primitives/millau/Cargo.toml b/bridges/primitives/millau/Cargo.toml new file mode 100644 index 0000000000000..8e787996f501a --- /dev/null +++ b/bridges/primitives/millau/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "bp-millau" +description = "Primitives of Millau runtime." +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2018" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] + +# Substrate Based Dependencies +sp-api = { version = "2.0.0", git = "https://github.com/paritytech/substrate.git", tag = 'v2.0.0', default-features = false } +sp-core = { version = "2.0.0", git = "https://github.com/paritytech/substrate.git", tag = 'v2.0.0', default-features = false } +sp-std = { version = "2.0.0", git = "https://github.com/paritytech/substrate.git", tag = 'v2.0.0', default-features = false } + +[features] +default = ["std"] +std = [ + "sp-api/std", + "sp-core/std", + "sp-std/std", +] diff --git a/bridges/primitives/millau/src/lib.rs b/bridges/primitives/millau/src/lib.rs new file mode 100644 index 0000000000000..214497d1e8580 --- /dev/null +++ b/bridges/primitives/millau/src/lib.rs @@ -0,0 +1,49 @@ +// Copyright 2019-2020 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 . + +#![cfg_attr(not(feature = "std"), no_std)] +// RuntimeApi generated functions +#![allow(clippy::too_many_arguments)] +// Runtime-generated DecodeLimit::decode_all_With_depth_limit +#![allow(clippy::unnecessary_mut_passed)] + +use sp_std::prelude::*; + +/// Block number type used in Millau. +pub type BlockNumber = u32; + +/// Hash type used in Millau. +pub type Hash = sp_core::H256; + +sp_api::decl_runtime_apis! { + /// API for querying information about Millau headers from the Bridge Pallet instance. + /// + /// This API is implemented by runtimes that are bridging with Millau chain, not the + /// Millau runtime itself. + pub trait MillauHeaderApi { + /// Returns number and hash of the best block known to the bridge module. + /// + /// The caller should only submit an `import_header` transaction that makes + /// (or leads to making) other header the best one. + fn best_block() -> (BlockNumber, Hash); + /// Returns number and hash of the best finalized block known to the bridge module. + fn finalized_block() -> (BlockNumber, Hash); + /// Returns numbers and hashes of headers that require finality proofs. + fn incomplete_headers() -> Vec<(BlockNumber, Hash)>; + /// Returns true if header is known to the runtime. + fn is_known_block(hash: Hash) -> bool; + } +} diff --git a/bridges/primitives/rialto/Cargo.toml b/bridges/primitives/rialto/Cargo.toml new file mode 100644 index 0000000000000..42f56fdae00d8 --- /dev/null +++ b/bridges/primitives/rialto/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "bp-rialto" +description = "Primitives of Rialto runtime." +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2018" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] + +# Substrate Based Dependencies +sp-api = { version = "2.0.0", git = "https://github.com/paritytech/substrate.git", tag = 'v2.0.0', default-features = false } +sp-core = { version = "2.0.0", git = "https://github.com/paritytech/substrate.git", tag = 'v2.0.0', default-features = false } +sp-std = { version = "2.0.0", git = "https://github.com/paritytech/substrate.git", tag = 'v2.0.0', default-features = false } + +[features] +default = ["std"] +std = [ + "sp-api/std", + "sp-core/std", + "sp-std/std", +] diff --git a/bridges/primitives/rialto/src/lib.rs b/bridges/primitives/rialto/src/lib.rs new file mode 100644 index 0000000000000..f23e09c511499 --- /dev/null +++ b/bridges/primitives/rialto/src/lib.rs @@ -0,0 +1,49 @@ +// Copyright 2019-2020 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 . + +#![cfg_attr(not(feature = "std"), no_std)] +// RuntimeApi generated functions +#![allow(clippy::too_many_arguments)] +// Runtime-generated DecodeLimit::decode_all_With_depth_limit +#![allow(clippy::unnecessary_mut_passed)] + +use sp_std::prelude::*; + +/// Block number type used in Rialto. +pub type BlockNumber = u32; + +/// Hash type used in Rialto. +pub type Hash = sp_core::H256; + +sp_api::decl_runtime_apis! { + /// API for querying information about Rialto headers from the Bridge Pallet instance. + /// + /// This API is implemented by runtimes that are bridging with Rialto chain, not the + /// Rialto runtime itself. + pub trait RialtoHeaderApi { + /// Returns number and hash of the best block known to the bridge module. + /// + /// The caller should only submit an `import_header` transaction that makes + /// (or leads to making) other header the best one. + fn best_block() -> (BlockNumber, Hash); + /// Returns number and hash of the best finalized block known to the bridge module. + fn finalized_block() -> (BlockNumber, Hash); + /// Returns numbers and hashes of headers that require finality proofs. + fn incomplete_headers() -> Vec<(BlockNumber, Hash)>; + /// Returns true if header is known to the runtime. + fn is_known_block(hash: Hash) -> bool; + } +} diff --git a/bridges/relays/ethereum/src/substrate_sync_loop.rs b/bridges/relays/ethereum/src/substrate_sync_loop.rs index a3f525f11100e..80d611a89c4fc 100644 --- a/bridges/relays/ethereum/src/substrate_sync_loop.rs +++ b/bridges/relays/ethereum/src/substrate_sync_loop.rs @@ -23,7 +23,7 @@ use async_trait::async_trait; use codec::Encode; use headers_relay::{ sync::HeadersSyncParams, - sync_loop::{SourceClient, TargetClient}, + sync_loop::TargetClient, sync_types::{HeadersSyncPipeline, QueuedHeader, SourceHeader, SubmittedHeaders}, }; use relay_ethereum_client::{ @@ -31,7 +31,9 @@ use relay_ethereum_client::{ SigningParams as EthereumSigningParams, }; use relay_rialto_client::{HeaderId as RialtoHeaderId, Rialto, SyncHeader as RialtoSyncHeader}; -use relay_substrate_client::{Client as SubstrateClient, ConnectionParams as SubstrateConnectionParams}; +use relay_substrate_client::{ + headers_source::HeadersSource, Client as SubstrateClient, ConnectionParams as SubstrateConnectionParams, +}; use relay_utils::metrics::MetricsParams; use sp_runtime::Justification; @@ -93,61 +95,8 @@ impl HeadersSyncPipeline for SubstrateHeadersSyncPipeline { /// Queued substrate header ID. pub type QueuedRialtoHeader = QueuedHeader; -/// Substrate client as headers source. -struct SubstrateHeadersSource { - /// Substrate node client. - client: SubstrateClient, -} - -impl SubstrateHeadersSource { - fn new(client: SubstrateClient) -> Self { - Self { client } - } -} - -#[async_trait] -impl SourceClient for SubstrateHeadersSource { - type Error = RpcError; - - async fn best_block_number(&self) -> Result { - Ok(self.client.best_header().await?.number) - } - - async fn header_by_hash(&self, hash: rialto_runtime::Hash) -> Result { - self.client - .header_by_hash(hash) - .await - .map(Into::into) - .map_err(Into::into) - } - - async fn header_by_number(&self, number: rialto_runtime::BlockNumber) -> Result { - self.client - .header_by_number(number) - .await - .map(Into::into) - .map_err(Into::into) - } - - async fn header_completion( - &self, - id: RialtoHeaderId, - ) -> Result<(RialtoHeaderId, Option), Self::Error> { - let hash = id.1; - let signed_block = self.client.get_block(Some(hash)).await?; - let grandpa_justification = signed_block.justification; - - Ok((id, grandpa_justification)) - } - - async fn header_extra( - &self, - id: RialtoHeaderId, - _header: QueuedRialtoHeader, - ) -> Result<(RialtoHeaderId, ()), Self::Error> { - Ok((id, ())) - } -} +/// Rialto node as headers source. +type SubstrateHeadersSource = HeadersSource; /// Ethereum client as Substrate headers target. struct EthereumHeadersTarget { diff --git a/bridges/relays/headers-relay/src/sync_types.rs b/bridges/relays/headers-relay/src/sync_types.rs index 21eec10c1e0d3..0dcb712c91800 100644 --- a/bridges/relays/headers-relay/src/sync_types.rs +++ b/bridges/relays/headers-relay/src/sync_types.rs @@ -43,19 +43,21 @@ pub enum HeaderStatus { } /// Headers synchronization pipeline. -pub trait HeadersSyncPipeline: Clone + Copy { +pub trait HeadersSyncPipeline: Clone + Copy + Send + Sync { /// Name of the headers source. const SOURCE_NAME: &'static str; /// Name of the headers target. const TARGET_NAME: &'static str; /// Headers we're syncing are identified by this hash. - type Hash: Eq + Clone + Copy + std::fmt::Debug + std::fmt::Display + std::hash::Hash; + type Hash: Eq + Clone + Copy + Send + Sync + std::fmt::Debug + std::fmt::Display + std::hash::Hash; /// Headers we're syncing are identified by this number. type Number: From + Ord + Clone + Copy + + Send + + Sync + std::fmt::Debug + std::fmt::Display + std::hash::Hash @@ -66,7 +68,7 @@ pub trait HeadersSyncPipeline: Clone + Copy { + num_traits::One + Into; /// Type of header that we're syncing. - type Header: Clone + std::fmt::Debug + PartialEq + SourceHeader; + type Header: Clone + std::fmt::Debug + PartialEq + SourceHeader + Send + Sync; /// Type of extra data for the header that we're receiving from the source node: /// 1) extra data is required for some headers; /// 2) target node may answer if it'll require extra data before header is submitted; diff --git a/bridges/relays/millau-client/Cargo.toml b/bridges/relays/millau-client/Cargo.toml index cd142200a0913..35b029ed3b7c8 100644 --- a/bridges/relays/millau-client/Cargo.toml +++ b/bridges/relays/millau-client/Cargo.toml @@ -6,5 +6,16 @@ edition = "2018" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] +codec = { package = "parity-scale-codec", version = "1.3.4" } +headers-relay = { path = "../headers-relay" } +relay-substrate-client = { path = "../substrate-client" } +relay-utils = { path = "../utils" } + +# Supported Chains + millau-runtime = { path = "../../bin/millau-runtime" } -substrate-client = { path = "../substrate-client" } + +# Substrate Dependencies + +frame-system = { version = "2.0.0", tag = 'v2.0.0', git = "https://github.com/paritytech/substrate.git" } +sp-runtime = { version = "2.0.0", tag = 'v2.0.0', git = "https://github.com/paritytech/substrate.git" } diff --git a/bridges/relays/millau-client/src/lib.rs b/bridges/relays/millau-client/src/lib.rs index c05fad91ac0de..a901be18eedb4 100644 --- a/bridges/relays/millau-client/src/lib.rs +++ b/bridges/relays/millau-client/src/lib.rs @@ -18,6 +18,12 @@ use relay_substrate_client::Chain; +use headers_relay::sync_types::SourceHeader; +use sp_runtime::traits::Header as HeaderT; + +/// Millau header id. +pub type HeaderId = relay_utils::HeaderId; + /// Millau chain definition. #[derive(Debug, Clone, Copy)] pub struct Millau; @@ -29,5 +35,33 @@ impl Chain for Millau { type AccountId = millau_runtime::AccountId; type Index = millau_runtime::Index; type SignedBlock = millau_runtime::SignedBlock; - type Call = rialto_runtime::Call; + type Call = millau_runtime::Call; +} + +/// Millau header type used in headers sync. +#[derive(Clone, Debug, PartialEq)] +pub struct SyncHeader(millau_runtime::Header); + +impl std::ops::Deref for SyncHeader { + type Target = millau_runtime::Header; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl From for SyncHeader { + fn from(header: millau_runtime::Header) -> Self { + Self(header) + } +} + +impl SourceHeader for SyncHeader { + fn id(&self) -> HeaderId { + relay_utils::HeaderId(*self.number(), self.hash()) + } + + fn parent_id(&self) -> HeaderId { + relay_utils::HeaderId(*self.number(), *self.parent_hash()) + } } diff --git a/bridges/relays/rialto-client/src/lib.rs b/bridges/relays/rialto-client/src/lib.rs index 4db86ae9e3c55..2ad3526995ae3 100644 --- a/bridges/relays/rialto-client/src/lib.rs +++ b/bridges/relays/rialto-client/src/lib.rs @@ -89,6 +89,15 @@ pub struct SigningParams { pub signer: sp_core::sr25519::Pair, } +impl SigningParams { + /// Create signing params from SURI and password. + pub fn from_suri(suri: &str, password: Option<&str>) -> Result { + Ok(SigningParams { + signer: sp_core::sr25519::Pair::from_string(suri, password)?, + }) + } +} + impl std::fmt::Debug for SigningParams { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "{}", self.signer.public()) diff --git a/bridges/relays/substrate-client/Cargo.toml b/bridges/relays/substrate-client/Cargo.toml index fc92f568fcb57..ae04b9dbeae70 100644 --- a/bridges/relays/substrate-client/Cargo.toml +++ b/bridges/relays/substrate-client/Cargo.toml @@ -6,6 +6,7 @@ edition = "2018" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] +async-trait = "0.1.40" codec = { package = "parity-scale-codec", version = "1.3.4" } headers-relay = { path = "../headers-relay" } jsonrpsee = { git = "https://github.com/svyatonik/jsonrpsee.git", branch = "shared-client-in-rpc-api", default-features = false, features = ["http"] } diff --git a/bridges/relays/substrate-client/src/chain.rs b/bridges/relays/substrate-client/src/chain.rs index 94126d811bc9c..174bbcb33d625 100644 --- a/bridges/relays/substrate-client/src/chain.rs +++ b/bridges/relays/substrate-client/src/chain.rs @@ -19,9 +19,13 @@ use crate::client::Client; use frame_support::Parameter; use jsonrpsee::common::{DeserializeOwned, Serialize}; use sp_core::Pair; -use sp_runtime::traits::{ - AtLeast32Bit, AtLeast32BitUnsigned, Bounded, CheckEqual, Dispatchable, Header as HeaderT, MaybeDisplay, - MaybeMallocSizeOf, MaybeSerialize, MaybeSerializeDeserialize, Member, SimpleBitOps, +use sp_runtime::{ + generic::SignedBlock, + traits::{ + AtLeast32Bit, AtLeast32BitUnsigned, Bounded, CheckEqual, Dispatchable, Header as HeaderT, MaybeDisplay, + MaybeMallocSizeOf, MaybeSerialize, MaybeSerializeDeserialize, Member, SimpleBitOps, + }, + Justification, }; use sp_std::fmt::Debug; @@ -63,11 +67,17 @@ pub trait Chain { /// with a sender account. type Index: Parameter + Member + MaybeSerialize + Debug + Default + MaybeDisplay + AtLeast32Bit + Copy; /// Block type. - type SignedBlock: Member + Serialize + DeserializeOwned; + type SignedBlock: Member + Serialize + DeserializeOwned + BlockWithJustification; /// The aggregated `Call` type. type Call: Dispatchable + Debug; } +/// Block with justification. +pub trait BlockWithJustification { + /// Return block justification, if known. + fn justification(&self) -> Option<&Justification>; +} + /// Substrate-based chain transactions signing scheme. pub trait TransactionSignScheme { /// Chain that this scheme is to be used. @@ -85,3 +95,18 @@ pub trait TransactionSignScheme { call: ::Call, ) -> Self::SignedTransaction; } + +/// Header type used by the chain. +pub type HeaderOf = ::Header; + +/// Hash type used by the chain. +pub type HashOf = ::Hash; + +/// Block number used by the chain. +pub type BlockNumberOf = ::BlockNumber; + +impl BlockWithJustification for SignedBlock { + fn justification(&self) -> Option<&Justification> { + self.justification.as_ref() + } +} diff --git a/bridges/relays/substrate-client/src/error.rs b/bridges/relays/substrate-client/src/error.rs index 78b519c7b0ad0..6f8c206d7dac3 100644 --- a/bridges/relays/substrate-client/src/error.rs +++ b/bridges/relays/substrate-client/src/error.rs @@ -45,6 +45,12 @@ impl MaybeConnectionError for Error { } } +impl From for String { + fn from(error: Error) -> String { + error.to_string() + } +} + impl ToString for Error { fn to_string(&self) -> String { match self { diff --git a/bridges/relays/substrate-client/src/headers_source.rs b/bridges/relays/substrate-client/src/headers_source.rs new file mode 100644 index 0000000000000..14be6e0e65cbb --- /dev/null +++ b/bridges/relays/substrate-client/src/headers_source.rs @@ -0,0 +1,99 @@ +// Copyright 2019-2020 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 . + +//! Default generic implementation of headers source for basic Substrate client. + +use crate::chain::{BlockWithJustification, Chain}; +use crate::client::Client; +use crate::error::Error; + +use async_trait::async_trait; +use headers_relay::{ + sync_loop::SourceClient, + sync_types::{HeaderIdOf, HeadersSyncPipeline, QueuedHeader, SourceHeader}, +}; +use jsonrpsee::common::DeserializeOwned; +use num_traits::Saturating; +use sp_runtime::{traits::Header as HeaderT, Justification}; +use std::marker::PhantomData; + +/// Substrate node as headers source. +pub struct HeadersSource { + client: Client, + _phantom: PhantomData

, +} + +impl HeadersSource { + /// Create new headers source using given client. + pub fn new(client: Client) -> Self { + HeadersSource { + client, + _phantom: Default::default(), + } + } +} + +#[async_trait] +impl SourceClient

for HeadersSource +where + C: Chain, + C::BlockNumber: Into + Saturating, + C::Header: DeserializeOwned + Into, + C::Index: DeserializeOwned, + P: HeadersSyncPipeline, + P::Header: SourceHeader, +{ + type Error = Error; + + async fn best_block_number(&self) -> Result { + Ok(*self.client.best_header().await?.number()) + } + + async fn header_by_hash(&self, hash: P::Hash) -> Result { + self.client + .header_by_hash(hash) + .await + .map(Into::into) + .map_err(Into::into) + } + + async fn header_by_number(&self, number: P::Number) -> Result { + self.client + .header_by_number(number) + .await + .map(Into::into) + .map_err(Into::into) + } + + async fn header_completion( + &self, + id: HeaderIdOf

, + ) -> Result<(HeaderIdOf

, Option), Self::Error> { + let hash = id.1; + let signed_block = self.client.get_block(Some(hash)).await?; + let grandpa_justification = signed_block.justification().cloned(); + + Ok((id, grandpa_justification)) + } + + async fn header_extra( + &self, + id: HeaderIdOf

, + _header: QueuedHeader

, + ) -> Result<(HeaderIdOf

, ()), Self::Error> { + Ok((id, ())) + } +} diff --git a/bridges/relays/substrate-client/src/lib.rs b/bridges/relays/substrate-client/src/lib.rs index 6bf38afdb8d3c..7e44653db8add 100644 --- a/bridges/relays/substrate-client/src/lib.rs +++ b/bridges/relays/substrate-client/src/lib.rs @@ -23,7 +23,9 @@ mod client; mod error; mod rpc; -pub use crate::chain::{Chain, TransactionSignScheme}; +pub mod headers_source; + +pub use crate::chain::{BlockNumberOf, BlockWithJustification, Chain, HashOf, HeaderOf, TransactionSignScheme}; pub use crate::client::{Client, OpaqueGrandpaAuthoritiesSet}; pub use crate::error::{Error, Result}; diff --git a/bridges/relays/substrate/Cargo.toml b/bridges/relays/substrate/Cargo.toml new file mode 100644 index 0000000000000..22809fc3c7ba4 --- /dev/null +++ b/bridges/relays/substrate/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "substrate-relay" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2018" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +async-std = "1.6.2" +async-trait = "0.1.40" +bp-rialto = { path = "../../primitives/rialto" } +codec = { package = "parity-scale-codec", version = "1.3.4" } +futures = "0.3.5" +headers-relay = { path = "../headers-relay" } +log = "0.4.11" +messages-relay = { path = "../messages-relay" } +paste = "1.0" +relay-millau-client = { path = "../millau-client" } +relay-rialto-client = { path = "../rialto-client" } +relay-substrate-client = { path = "../substrate-client" } +sp-runtime = { version = "2.0.0", tag = 'v2.0.0', git = "https://github.com/paritytech/substrate.git" } +structopt = "0.3" diff --git a/bridges/relays/substrate/src/cli.rs b/bridges/relays/substrate/src/cli.rs new file mode 100644 index 0000000000000..939863b5ca0a2 --- /dev/null +++ b/bridges/relays/substrate/src/cli.rs @@ -0,0 +1,65 @@ +// Copyright 2019-2020 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 . + +//! Deal with CLI args of substrate-to-substrate relay. + +use structopt::StructOpt; + +/// Parse relay CLI args. +pub fn parse_args() -> Command { + Command::from_args() +} + +/// Substrate-to-Substrate relay CLI args. +#[derive(StructOpt)] +#[structopt(about = "Substrate-to-Substrate relay")] +pub enum Command { + MillauHeadersToRialto { + #[structopt(flatten)] + millau: MillauConnectionParams, + #[structopt(flatten)] + rialto: RialtoConnectionParams, + #[structopt(flatten)] + rialto_sign: RialtoSigningParams, + }, +} + +macro_rules! declare_chain_options { + ($chain:ident, $chain_prefix:ident) => { + paste::item! { + #[doc = $chain " connection params."] + #[derive(StructOpt)] + pub struct [<$chain ConnectionParams>] { + #[doc = "Connect to " $chain " node at given host."] + pub [<$chain_prefix _host>]: String, + #[doc = "Connect to " $chain " node at given port."] + pub [<$chain_prefix _port>]: u16, + } + + #[doc = $chain " signing params."] + #[derive(StructOpt)] + pub struct [<$chain SigningParams>] { + #[doc = "The SURI of secret key to use when transactions are submitted to the " $chain " node."] + pub [<$chain_prefix _signer>]: String, + #[doc = "The password for the SURI of secret key to use when transactions are submitted to the " $chain " node."] + pub [<$chain_prefix _signer_password>]: Option, + } + } + }; +} + +declare_chain_options!(Rialto, rialto); +declare_chain_options!(Millau, millau); diff --git a/bridges/relays/substrate/src/main.rs b/bridges/relays/substrate/src/main.rs new file mode 100644 index 0000000000000..a42d721759221 --- /dev/null +++ b/bridges/relays/substrate/src/main.rs @@ -0,0 +1,66 @@ +// Copyright 2019-2020 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 . + +//! Substrate-to-substrate relay entrypoint. + +#![warn(missing_docs)] + +use relay_rialto_client::SigningParams as RialtoSigningParams; +use relay_substrate_client::ConnectionParams; + +/// Millau node client. +pub type MillauClient = relay_substrate_client::Client; +/// Rialto node client. +pub type RialtoClient = relay_substrate_client::Client; + +mod cli; +mod millau_headers_to_rialto; + +fn main() { + let result = async_std::task::block_on(run_command(cli::parse_args())); + if let Err(error) = result { + log::error!(target: "bridge", "Failed to start relay: {}", error); + } +} + +async fn run_command(command: cli::Command) -> Result<(), String> { + match command { + cli::Command::MillauHeadersToRialto { + millau, + rialto, + rialto_sign, + } => { + let millau_client = MillauClient::new(ConnectionParams { + host: millau.millau_host, + port: millau.millau_port, + }) + .await?; + let rialto_client = RialtoClient::new(ConnectionParams { + host: rialto.rialto_host, + port: rialto.rialto_port, + }) + .await?; + let rialto_sign = RialtoSigningParams::from_suri( + &rialto_sign.rialto_signer, + rialto_sign.rialto_signer_password.as_deref(), + ) + .map_err(|e| format!("Failed to parse rialto-signer: {:?}", e))?; + millau_headers_to_rialto::run(millau_client, rialto_client, rialto_sign) + } + } + + Ok(()) +} diff --git a/bridges/relays/substrate/src/millau_headers_to_rialto.rs b/bridges/relays/substrate/src/millau_headers_to_rialto.rs new file mode 100644 index 0000000000000..d927a9e133f42 --- /dev/null +++ b/bridges/relays/substrate/src/millau_headers_to_rialto.rs @@ -0,0 +1,125 @@ +// Copyright 2019-2020 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 . + +//! Millau-to-Rialto headers sync entrypoint. + +use crate::{MillauClient, RialtoClient}; + +use async_trait::async_trait; +use codec::Encode; +use headers_relay::{ + sync::{HeadersSyncParams, TargetTransactionMode}, + sync_loop::TargetClient, + sync_types::{HeadersSyncPipeline, QueuedHeader, SubmittedHeaders}, +}; +use relay_millau_client::{HeaderId as MillauHeaderId, Millau, SyncHeader as MillauSyncHeader}; +use relay_rialto_client::SigningParams as RialtoSigningParams; +use relay_substrate_client::{headers_source::HeadersSource, BlockNumberOf, Error as SubstrateError, HashOf}; +use sp_runtime::Justification; +use std::{collections::HashSet, time::Duration}; + +/// Millau-to-Rialto headers pipeline. +#[derive(Debug, Clone, Copy)] +struct MillauHeadersToRialto; + +impl HeadersSyncPipeline for MillauHeadersToRialto { + const SOURCE_NAME: &'static str = "Millau"; + const TARGET_NAME: &'static str = "Rialto"; + + type Hash = HashOf; + type Number = BlockNumberOf; + type Header = MillauSyncHeader; + type Extra = (); + type Completion = Justification; + + fn estimate_size(source: &QueuedHeader) -> usize { + source.header().encode().len() + } +} + +/// Millau header in-the-queue. +type QueuedMillauHeader = QueuedHeader; + +/// Millau node as headers source. +type MillauSourceClient = HeadersSource; + +/// Rialto node as headers target. +struct RialtoTargetClient { + _client: RialtoClient, + _sign: RialtoSigningParams, +} + +#[async_trait] +impl TargetClient for RialtoTargetClient { + type Error = SubstrateError; + + async fn best_header_id(&self) -> Result { + unimplemented!("https://github.com/paritytech/parity-bridges-common/issues/209") + } + + async fn is_known_header(&self, _id: MillauHeaderId) -> Result<(MillauHeaderId, bool), Self::Error> { + unimplemented!("https://github.com/paritytech/parity-bridges-common/issues/209") + } + + async fn submit_headers(&self, _headers: Vec) -> SubmittedHeaders { + unimplemented!("https://github.com/paritytech/parity-bridges-common/issues/209") + } + + async fn incomplete_headers_ids(&self) -> Result, Self::Error> { + unimplemented!("https://github.com/paritytech/parity-bridges-common/issues/209") + } + + #[allow(clippy::unit_arg)] + async fn complete_header( + &self, + _id: MillauHeaderId, + _completion: Justification, + ) -> Result { + unimplemented!("https://github.com/paritytech/parity-bridges-common/issues/209") + } + + async fn requires_extra(&self, _header: QueuedMillauHeader) -> Result<(MillauHeaderId, bool), Self::Error> { + unimplemented!("https://github.com/paritytech/parity-bridges-common/issues/209") + } +} + +/// Run Millau-to-Rialto headers sync. +pub fn run(millau_client: MillauClient, rialto_client: RialtoClient, rialto_sign: RialtoSigningParams) { + let millau_tick = Duration::from_secs(5); + let rialto_tick = Duration::from_secs(5); + let sync_params = HeadersSyncParams { + max_future_headers_to_download: 32, + max_headers_in_submitted_status: 1024, + max_headers_in_single_submit: 8, + max_headers_size_in_single_submit: 1024 * 1024, + prune_depth: 256, + target_tx_mode: TargetTransactionMode::Signed, + }; + let metrics_params = None; + + headers_relay::sync_loop::run( + MillauSourceClient::new(millau_client), + millau_tick, + RialtoTargetClient { + _client: rialto_client, + _sign: rialto_sign, + }, + rialto_tick, + sync_params, + metrics_params, + futures::future::pending(), + ); +}