diff --git a/bridges/bin/rialto-runtime/src/lib.rs b/bridges/bin/rialto-runtime/src/lib.rs index 6b207dbdb0..f0e1e77154 100644 --- a/bridges/bin/rialto-runtime/src/lib.rs +++ b/bridges/bin/rialto-runtime/src/lib.rs @@ -522,7 +522,7 @@ impl_runtime_apis! { } } - impl bp_eth_poa::RialtoHeaderApi for Runtime { + impl bp_eth_poa::RialtoPoAHeaderApi for Runtime { fn best_block() -> (u64, bp_eth_poa::H256) { let best_block = BridgeRialto::best_block(); (best_block.number, best_block.hash) diff --git a/bridges/primitives/ethereum-poa/src/lib.rs b/bridges/primitives/ethereum-poa/src/lib.rs index 5229522b02..63d2dda207 100644 --- a/bridges/primitives/ethereum-poa/src/lib.rs +++ b/bridges/primitives/ethereum-poa/src/lib.rs @@ -549,7 +549,7 @@ pub fn step_validator(header_validators: &[T], header_step: u64) -> &T { sp_api::decl_runtime_apis! { /// API for querying information about headers from the Rialto Bridge Pallet - pub trait RialtoHeaderApi { + pub trait RialtoPoAHeaderApi { /// Returns number and hash of the best block known to the bridge module. /// /// The caller should only submit an `import_header` transaction that makes diff --git a/bridges/relays/ethereum/Cargo.toml b/bridges/relays/ethereum/Cargo.toml index 08f1b147aa..15feebd70f 100644 --- a/bridges/relays/ethereum/Cargo.toml +++ b/bridges/relays/ethereum/Cargo.toml @@ -27,17 +27,14 @@ messages-relay = { path = "../messages-relay" } num-traits = "0.2" parity-crypto = { version = "0.6", features = ["publickey"] } relay-ethereum-client = { path = "../ethereum-client" } +relay-rialto-client = { path = "../rialto-client" } +relay-substrate-client = { path = "../substrate-client" } +rialto-runtime = { path = "../../bin/rialto-runtime" } relay-utils = { path = "../utils" } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0.57" time = "0.2" -[dependencies.jsonrpsee] -git = "https://github.com/svyatonik/jsonrpsee.git" -branch = "shared-client-in-rpc-api" -default-features = false -features = ["http"] - # Substrate Based Dependencies [dependencies.frame-system] version = "2.0.0" @@ -73,7 +70,3 @@ git = "https://github.com/paritytech/substrate.git" version = "0.8.0" tag = 'v2.0.0' git = "https://github.com/paritytech/substrate.git" - -[dependencies.rialto-runtime] -version = "0.1.0" -path = "../../bin/rialto-runtime" diff --git a/bridges/relays/ethereum/src/cli.yml b/bridges/relays/ethereum/src/cli.yml index 35c8c2e6eb..a665e76776 100644 --- a/bridges/relays/ethereum/src/cli.yml +++ b/bridges/relays/ethereum/src/cli.yml @@ -85,7 +85,6 @@ subcommands: help: Hex-encoded secret to use when transactions are submitted to the Ethereum node. - sub-host: *sub-host - sub-port: *sub-port - - sub-pallet-instance: *sub-pallet-instance - no-prometheus: *no-prometheus - prometheus-host: *prometheus-host - prometheus-port: *prometheus-port @@ -103,7 +102,6 @@ subcommands: takes_value: true - sub-host: *sub-host - sub-port: *sub-port - - sub-pallet-instance: *sub-pallet-instance - sub-authorities-set-id: long: sub-authorities-set-id value_name: SUB_AUTHORITIES_SET_ID diff --git a/bridges/relays/ethereum/src/ethereum_client.rs b/bridges/relays/ethereum/src/ethereum_client.rs index d9357c560b..19d8819985 100644 --- a/bridges/relays/ethereum/src/ethereum_client.rs +++ b/bridges/relays/ethereum/src/ethereum_client.rs @@ -15,7 +15,7 @@ // along with Parity Bridges Common. If not, see . use crate::rpc_errors::RpcError; -use crate::substrate_types::{GrandpaJustification, Hash as SubstrateHash, QueuedSubstrateHeader, SubstrateHeaderId}; +use crate::substrate_sync_loop::QueuedRialtoHeader; use async_trait::async_trait; use codec::{Decode, Encode}; @@ -26,7 +26,9 @@ use relay_ethereum_client::{ types::{Address, CallRequest, HeaderId as EthereumHeaderId, Receipt, H256, U256}, Client as EthereumClient, Error as EthereumNodeError, SigningParams as EthereumSigningParams, }; +use relay_rialto_client::HeaderId as RialtoHeaderId; use relay_utils::{HeaderId, MaybeConnectionError}; +use sp_runtime::Justification; use std::collections::HashSet; // to encode/decode contract calls @@ -39,34 +41,34 @@ type RpcResult = std::result::Result; #[async_trait] pub trait EthereumHighLevelRpc { /// Returns best Substrate block that PoA chain knows of. - async fn best_substrate_block(&self, contract_address: Address) -> RpcResult; + async fn best_substrate_block(&self, contract_address: Address) -> RpcResult; /// Returns true if Substrate header is known to Ethereum node. async fn substrate_header_known( &self, contract_address: Address, - id: SubstrateHeaderId, - ) -> RpcResult<(SubstrateHeaderId, bool)>; + id: RialtoHeaderId, + ) -> RpcResult<(RialtoHeaderId, bool)>; /// Submits Substrate headers to Ethereum contract. async fn submit_substrate_headers( &self, params: EthereumSigningParams, contract_address: Address, - headers: Vec, - ) -> SubmittedHeaders; + headers: Vec, + ) -> SubmittedHeaders; /// Returns ids of incomplete Substrate headers. - async fn incomplete_substrate_headers(&self, contract_address: Address) -> RpcResult>; + async fn incomplete_substrate_headers(&self, contract_address: Address) -> RpcResult>; /// Complete Substrate header. async fn complete_substrate_header( &self, params: EthereumSigningParams, contract_address: Address, - id: SubstrateHeaderId, - justification: GrandpaJustification, - ) -> RpcResult; + id: RialtoHeaderId, + justification: Justification, + ) -> RpcResult; /// Submit ethereum transaction. async fn submit_ethereum_transaction( @@ -88,7 +90,7 @@ pub trait EthereumHighLevelRpc { #[async_trait] impl EthereumHighLevelRpc for EthereumClient { - async fn best_substrate_block(&self, contract_address: Address) -> RpcResult { + async fn best_substrate_block(&self, contract_address: Address) -> RpcResult { let (encoded_call, call_decoder) = bridge_contract::functions::best_known_header::call(); let call_request = CallRequest { to: Some(contract_address), @@ -98,7 +100,7 @@ impl EthereumHighLevelRpc for EthereumClient { let call_result = self.eth_call(call_request).await?; let (number, raw_hash) = call_decoder.decode(&call_result.0)?; - let hash = SubstrateHash::decode(&mut &raw_hash[..])?; + let hash = rialto_runtime::Hash::decode(&mut &raw_hash[..])?; if number != number.low_u32().into() { return Err(RpcError::Ethereum(EthereumNodeError::InvalidSubstrateBlockNumber)); @@ -110,8 +112,8 @@ impl EthereumHighLevelRpc for EthereumClient { async fn substrate_header_known( &self, contract_address: Address, - id: SubstrateHeaderId, - ) -> RpcResult<(SubstrateHeaderId, bool)> { + id: RialtoHeaderId, + ) -> RpcResult<(RialtoHeaderId, bool)> { let (encoded_call, call_decoder) = bridge_contract::functions::is_known_header::call(id.1); let call_request = CallRequest { to: Some(contract_address), @@ -129,8 +131,8 @@ impl EthereumHighLevelRpc for EthereumClient { &self, params: EthereumSigningParams, contract_address: Address, - headers: Vec, - ) -> SubmittedHeaders { + headers: Vec, + ) -> SubmittedHeaders { // read nonce of signer let address: Address = params.signer.address().as_fixed_bytes().into(); let nonce = match self.account_nonce(address).await { @@ -159,7 +161,7 @@ impl EthereumHighLevelRpc for EthereumClient { .await } - async fn incomplete_substrate_headers(&self, contract_address: Address) -> RpcResult> { + async fn incomplete_substrate_headers(&self, contract_address: Address) -> RpcResult> { let (encoded_call, call_decoder) = bridge_contract::functions::incomplete_headers::call(); let call_request = CallRequest { to: Some(contract_address), @@ -190,9 +192,9 @@ impl EthereumHighLevelRpc for EthereumClient { &self, params: EthereumSigningParams, contract_address: Address, - id: SubstrateHeaderId, - justification: GrandpaJustification, - ) -> RpcResult { + id: RialtoHeaderId, + justification: Justification, + ) -> RpcResult { let _ = self .submit_ethereum_transaction( ¶ms, @@ -243,21 +245,21 @@ pub const HEADERS_BATCH: usize = 4; #[derive(Debug)] #[cfg_attr(test, derive(Clone))] pub struct HeadersBatch { - pub header1: QueuedSubstrateHeader, - pub header2: Option, - pub header3: Option, - pub header4: Option, + pub header1: QueuedRialtoHeader, + pub header2: Option, + pub header3: Option, + pub header4: Option, } impl HeadersBatch { /// Create new headers from given header & ids collections. /// /// This method will pop `HEADERS_BATCH` items from both collections - /// and construct `Headers` object and a vector of `SubstrateheaderId`s. + /// and construct `Headers` object and a vector of `RialtoHeaderId`s. pub fn pop_from( - headers: &mut Vec, - ids: &mut Vec, - ) -> Result<(Self, Vec), ()> { + headers: &mut Vec, + ids: &mut Vec, + ) -> Result<(Self, Vec), ()> { if headers.len() != ids.len() { log::error!(target: "bridge", "Collection size mismatch ({} vs {})", headers.len(), ids.len()); return Err(()); @@ -287,7 +289,7 @@ impl HeadersBatch { /// Returns unified array of headers. /// /// The first element is always `Some`. - fn headers(&self) -> [Option<&QueuedSubstrateHeader>; HEADERS_BATCH] { + fn headers(&self) -> [Option<&QueuedRialtoHeader>; HEADERS_BATCH] { [ Some(&self.header1), self.header2.as_ref(), @@ -298,7 +300,7 @@ impl HeadersBatch { /// Encodes all headers. If header is not present an empty vector will be returned. pub fn encode(&self) -> [Vec; HEADERS_BATCH] { - let encode = |h: &QueuedSubstrateHeader| h.header().encode(); + let encode = |h: &QueuedRialtoHeader| h.header().encode(); let headers = self.headers(); [ headers[0].map(encode).unwrap_or_default(), @@ -309,7 +311,7 @@ impl HeadersBatch { } /// Returns number of contained headers. pub fn len(&self) -> usize { - let is_set = |h: &Option<&QueuedSubstrateHeader>| if h.is_some() { 1 } else { 0 }; + let is_set = |h: &Option<&QueuedRialtoHeader>| if h.is_some() { 1 } else { 0 }; self.headers().iter().map(is_set).sum() } @@ -396,8 +398,8 @@ impl HeadersSubmitter for EthereumHeadersSubmitter { /// Submit multiple Substrate headers. async fn submit_substrate_headers( mut header_submitter: impl HeadersSubmitter, - mut headers: Vec, -) -> SubmittedHeaders { + mut headers: Vec, +) -> SubmittedHeaders { let mut submitted_headers = SubmittedHeaders::default(); let mut ids = headers.iter().map(|header| header.id()).rev().collect::>(); @@ -424,8 +426,8 @@ async fn submit_substrate_headers( /// Submit 4 Substrate headers in single PoA transaction. async fn submit_substrate_headers_batch( header_submitter: &mut impl HeadersSubmitter, - submitted_headers: &mut SubmittedHeaders, - mut ids: Vec, + submitted_headers: &mut SubmittedHeaders, + mut ids: Vec, mut headers: HeadersBatch, ) -> Option { debug_assert_eq!(ids.len(), headers.len(),); @@ -486,12 +488,11 @@ async fn submit_substrate_headers_batch( #[cfg(test)] mod tests { use super::*; - use crate::substrate_types::{Header as SubstrateHeader, Number as SubstrateBlockNumber}; use sp_runtime::traits::Header; struct TestHeadersSubmitter { - incomplete: Vec, - failed: Vec, + incomplete: Vec, + failed: Vec, } #[async_trait] @@ -513,9 +514,9 @@ mod tests { } } - fn header(number: SubstrateBlockNumber) -> QueuedSubstrateHeader { - QueuedSubstrateHeader::new( - SubstrateHeader::new( + fn header(number: rialto_runtime::BlockNumber) -> QueuedRialtoHeader { + QueuedRialtoHeader::new( + rialto_runtime::Header::new( number, Default::default(), Default::default(), diff --git a/bridges/relays/ethereum/src/ethereum_deploy_contract.rs b/bridges/relays/ethereum/src/ethereum_deploy_contract.rs index 17f12ba45e..25f8c873e5 100644 --- a/bridges/relays/ethereum/src/ethereum_deploy_contract.rs +++ b/bridges/relays/ethereum/src/ethereum_deploy_contract.rs @@ -15,16 +15,17 @@ // along with Parity Bridges Common. If not, see . use crate::ethereum_client::{bridge_contract, EthereumHighLevelRpc}; -use crate::instances::BridgeInstance; -use crate::rpc::SubstrateRpc; -use crate::substrate_client::{SubstrateConnectionParams, SubstrateRpcClient}; -use crate::substrate_types::{Hash as SubstrateHash, Header as SubstrateHeader, SubstrateHeaderId}; +use crate::rpc_errors::RpcError; use codec::{Decode, Encode}; use num_traits::Zero; use relay_ethereum_client::{ Client as EthereumClient, ConnectionParams as EthereumConnectionParams, SigningParams as EthereumSigningParams, }; +use relay_rialto_client::{HeaderId as RialtoHeaderId, Rialto}; +use relay_substrate_client::{ + Client as SubstrateClient, ConnectionParams as SubstrateConnectionParams, OpaqueGrandpaAuthoritiesSet, +}; use relay_utils::HeaderId; /// Ethereum synchronization parameters. @@ -44,8 +45,6 @@ pub struct EthereumDeployContractParams { pub sub_initial_authorities_set: Option>, /// Initial header. pub sub_initial_header: Option>, - /// Instance of the bridge pallet being synchronized. - pub instance: Box, } /// Deploy Bridge contract on Ethereum chain. @@ -56,7 +55,6 @@ pub fn run(params: EthereumDeployContractParams) { eth_params, eth_sign, sub_params, - instance, sub_initial_authorities_set_id, sub_initial_authorities_set, sub_initial_header, @@ -65,7 +63,7 @@ pub fn run(params: EthereumDeployContractParams) { let result = local_pool.run_until(async move { let eth_client = EthereumClient::new(eth_params); - let sub_client = SubstrateRpcClient::new(sub_params, instance).await?; + let sub_client = SubstrateClient::::new(sub_params).await.map_err(RpcError::Substrate)?; let (initial_header_id, initial_header) = prepare_initial_header(&sub_client, sub_initial_header).await?; let initial_set_id = sub_initial_authorities_set_id.unwrap_or(0); @@ -102,11 +100,11 @@ pub fn run(params: EthereumDeployContractParams) { /// Prepare initial header. async fn prepare_initial_header( - sub_client: &SubstrateRpcClient, + sub_client: &SubstrateClient, sub_initial_header: Option>, -) -> Result<(SubstrateHeaderId, Vec), String> { +) -> Result<(RialtoHeaderId, Vec), String> { match sub_initial_header { - Some(raw_initial_header) => match SubstrateHeader::decode(&mut &raw_initial_header[..]) { + Some(raw_initial_header) => match rialto_runtime::Header::decode(&mut &raw_initial_header[..]) { Ok(initial_header) => Ok(( HeaderId(initial_header.number, initial_header.hash()), raw_initial_header, @@ -124,10 +122,10 @@ async fn prepare_initial_header( /// Prepare initial GRANDPA authorities set. async fn prepare_initial_authorities_set( - sub_client: &SubstrateRpcClient, - sub_initial_header_hash: SubstrateHash, + sub_client: &SubstrateClient, + sub_initial_header_hash: rialto_runtime::Hash, sub_initial_authorities_set: Option>, -) -> Result, String> { +) -> Result { let initial_authorities_set = match sub_initial_authorities_set { Some(initial_authorities_set) => Ok(initial_authorities_set), None => sub_client.grandpa_authorities_set(sub_initial_header_hash).await, diff --git a/bridges/relays/ethereum/src/ethereum_exchange.rs b/bridges/relays/ethereum/src/ethereum_exchange.rs index 59c9a42ca6..c8b8a7a7e8 100644 --- a/bridges/relays/ethereum/src/ethereum_exchange.rs +++ b/bridges/relays/ethereum/src/ethereum_exchange.rs @@ -17,11 +17,8 @@ //! Relaying proofs of PoA -> Substrate exchange transactions. use crate::instances::BridgeInstance; -use crate::rpc::SubstrateRpc; +use crate::rialto_client::{SubmitEthereumExchangeTransactionProof, SubstrateHighLevelRpc}; use crate::rpc_errors::RpcError; -use crate::substrate_client::{ - SubmitEthereumExchangeTransactionProof, SubstrateConnectionParams, SubstrateRpcClient, SubstrateSigningParams, -}; use crate::substrate_types::into_substrate_ethereum_receipt; use async_trait::async_trait; @@ -38,9 +35,11 @@ use relay_ethereum_client::{ }, Client as EthereumClient, ConnectionParams as EthereumConnectionParams, }; +use relay_rialto_client::{Rialto, SigningParams as RialtoSigningParams}; +use relay_substrate_client::{Client as SubstrateClient, ConnectionParams as SubstrateConnectionParams}; use relay_utils::{metrics::MetricsParams, HeaderId}; use rialto_runtime::exchange::EthereumTransactionInclusionProof; -use std::time::Duration; +use std::{sync::Arc, time::Duration}; /// Interval at which we ask Ethereum node for updates. const ETHEREUM_TICK_INTERVAL: Duration = Duration::from_secs(10); @@ -64,13 +63,13 @@ pub struct EthereumExchangeParams { /// Substrate connection params. pub sub_params: SubstrateConnectionParams, /// Substrate signing params. - pub sub_sign: SubstrateSigningParams, + pub sub_sign: RialtoSigningParams, /// Relay working mode. pub mode: ExchangeRelayMode, /// Metrics parameters. pub metrics_params: Option, /// Instance of the bridge pallet being synchronized. - pub instance: Box, + pub instance: Arc, } /// Ethereum to Substrate exchange pipeline. @@ -201,8 +200,9 @@ impl SourceClient for EthereumTransactionsSource { /// Substrate node as transactions proof target. struct SubstrateTransactionsTarget { - client: SubstrateRpcClient, - sign_params: SubstrateSigningParams, + client: SubstrateClient, + sign_params: RialtoSigningParams, + bridge_instance: Arc, } #[async_trait] @@ -254,8 +254,10 @@ impl TargetClient for SubstrateTransactionsTarget { } async fn submit_transaction_proof(&self, proof: EthereumTransactionInclusionProof) -> Result<(), Self::Error> { - let sign_params = self.sign_params.clone(); - self.client.submit_exchange_transaction_proof(sign_params, proof).await + let (sign_params, bridge_instance) = (self.sign_params.clone(), self.bridge_instance.clone()); + self.client + .submit_exchange_transaction_proof(sign_params, bridge_instance, proof) + .await } } @@ -283,12 +285,15 @@ fn run_single_transaction_relay(params: EthereumExchangeParams, eth_tx_hash: H25 let result = local_pool.run_until(async move { let eth_client = EthereumClient::new(eth_params); - let sub_client = SubstrateRpcClient::new(sub_params, instance).await?; + let sub_client = SubstrateClient::::new(sub_params) + .await + .map_err(RpcError::Substrate)?; let source = EthereumTransactionsSource { client: eth_client }; let target = SubstrateTransactionsTarget { client: sub_client, sign_params: sub_sign, + bridge_instance: instance, }; relay_single_transaction_proof(&source, &target, eth_tx_hash).await @@ -326,7 +331,7 @@ fn run_auto_transactions_relay_loop(params: EthereumExchangeParams, eth_start_wi let do_run_loop = move || -> Result<(), String> { let eth_client = EthereumClient::new(eth_params); - let sub_client = async_std::task::block_on(SubstrateRpcClient::new(sub_params, instance)) + let sub_client = async_std::task::block_on(SubstrateClient::::new(sub_params)) .map_err(|err| format!("Error starting Substrate client: {:?}", err))?; let eth_start_with_block_number = match eth_start_with_block_number { @@ -349,6 +354,7 @@ fn run_auto_transactions_relay_loop(params: EthereumExchangeParams, eth_start_wi SubstrateTransactionsTarget { client: sub_client, sign_params: sub_sign, + bridge_instance: instance, }, metrics_params, futures::future::pending(), diff --git a/bridges/relays/ethereum/src/ethereum_sync_loop.rs b/bridges/relays/ethereum/src/ethereum_sync_loop.rs index d5a4ec9b8e..c9045c6974 100644 --- a/bridges/relays/ethereum/src/ethereum_sync_loop.rs +++ b/bridges/relays/ethereum/src/ethereum_sync_loop.rs @@ -14,15 +14,12 @@ // You should have received a copy of the GNU General Public License // along with Parity Bridges Common. If not, see . -//! Ethereum PoA -> Substrate synchronization. +//! Ethereum PoA -> Rialto-Substrate synchronization. use crate::ethereum_client::EthereumHighLevelRpc; use crate::instances::BridgeInstance; -use crate::rpc::SubstrateRpc; +use crate::rialto_client::{SubmitEthereumHeaders, SubstrateHighLevelRpc}; use crate::rpc_errors::RpcError; -use crate::substrate_client::{ - SubmitEthereumHeaders, SubstrateConnectionParams, SubstrateRpcClient, SubstrateSigningParams, -}; use crate::substrate_types::{into_substrate_ethereum_header, into_substrate_ethereum_receipts}; use async_trait::async_trait; @@ -36,10 +33,12 @@ use relay_ethereum_client::{ types::{HeaderHash, HeaderId as EthereumHeaderId, Receipt, SyncHeader as Header}, Client as EthereumClient, ConnectionParams as EthereumConnectionParams, }; +use relay_rialto_client::{Rialto, SigningParams as RialtoSigningParams}; +use relay_substrate_client::{Client as SubstrateClient, ConnectionParams as SubstrateConnectionParams}; use relay_utils::metrics::MetricsParams; use std::fmt::Debug; -use std::{collections::HashSet, time::Duration}; +use std::{collections::HashSet, sync::Arc, time::Duration}; pub mod consts { use super::*; @@ -70,13 +69,13 @@ pub struct EthereumSyncParams { /// Substrate connection params. pub sub_params: SubstrateConnectionParams, /// Substrate signing params. - pub sub_sign: SubstrateSigningParams, + pub sub_sign: RialtoSigningParams, /// Synchronization parameters. pub sync_params: HeadersSyncParams, /// Metrics parameters. pub metrics_params: Option, /// Instance of the bridge pallet being synchronized. - pub instance: Box, + pub instance: Arc, } /// Ethereum synchronization pipeline. @@ -158,19 +157,27 @@ impl SourceClient for EthereumHeadersSource { struct SubstrateHeadersTarget { /// Substrate node client. - client: SubstrateRpcClient, + client: SubstrateClient, /// Whether we want to submit signed (true), or unsigned (false) transactions. sign_transactions: bool, /// Substrate signing params. - sign_params: SubstrateSigningParams, + sign_params: RialtoSigningParams, + /// Bridge instance used in Ethereum to Substrate sync. + bridge_instance: Arc, } impl SubstrateHeadersTarget { - fn new(client: SubstrateRpcClient, sign_transactions: bool, sign_params: SubstrateSigningParams) -> Self { + fn new( + client: SubstrateClient, + sign_transactions: bool, + sign_params: RialtoSigningParams, + bridge_instance: Arc, + ) -> Self { Self { client, sign_transactions, sign_params, + bridge_instance, } } } @@ -191,9 +198,13 @@ impl TargetClient for SubstrateHeadersTarget { &self, headers: Vec, ) -> SubmittedHeaders { - let (sign_params, sign_transactions) = (self.sign_params.clone(), self.sign_transactions); + let (sign_params, bridge_instance, sign_transactions) = ( + self.sign_params.clone(), + self.bridge_instance.clone(), + self.sign_transactions, + ); self.client - .submit_ethereum_headers(sign_params, headers, sign_transactions) + .submit_ethereum_headers(sign_params, bridge_instance, headers, sign_transactions) .await } @@ -228,7 +239,7 @@ pub fn run(params: EthereumSyncParams) -> Result<(), RpcError> { } = params; let eth_client = EthereumClient::new(eth_params); - let sub_client = async_std::task::block_on(async { SubstrateRpcClient::new(sub_params, instance).await })?; + let sub_client = async_std::task::block_on(async { SubstrateClient::::new(sub_params).await })?; let sign_sub_transactions = match sync_params.target_tx_mode { TargetTransactionMode::Signed | TargetTransactionMode::Backup => true, @@ -236,7 +247,7 @@ pub fn run(params: EthereumSyncParams) -> Result<(), RpcError> { }; let source = EthereumHeadersSource::new(eth_client); - let target = SubstrateHeadersTarget::new(sub_client, sign_sub_transactions, sub_sign); + let target = SubstrateHeadersTarget::new(sub_client, sign_sub_transactions, sub_sign, instance); headers_relay::sync_loop::run( source, diff --git a/bridges/relays/ethereum/src/instances.rs b/bridges/relays/ethereum/src/instances.rs index d2f06c2504..bfdccd0ba3 100644 --- a/bridges/relays/ethereum/src/instances.rs +++ b/bridges/relays/ethereum/src/instances.rs @@ -44,9 +44,9 @@ pub trait BridgeInstance: Send + Sync + std::fmt::Debug { /// Corresponds to the Rialto instance used in the bridge runtime. #[derive(Default, Clone, Debug)] -pub struct Rialto; +pub struct RialtoPoA; -impl BridgeInstance for Rialto { +impl BridgeInstance for RialtoPoA { fn build_signed_header_call(&self, headers: Vec) -> Call { let pallet_call = rialto_runtime::BridgeEthPoACall::import_signed_headers( headers diff --git a/bridges/relays/ethereum/src/main.rs b/bridges/relays/ethereum/src/main.rs index 14400c630f..9b290df43c 100644 --- a/bridges/relays/ethereum/src/main.rs +++ b/bridges/relays/ethereum/src/main.rs @@ -22,9 +22,8 @@ mod ethereum_exchange; mod ethereum_exchange_submit; mod ethereum_sync_loop; mod instances; -mod rpc; +mod rialto_client; mod rpc_errors; -mod substrate_client; mod substrate_sync_loop; mod substrate_types; @@ -34,16 +33,17 @@ use ethereum_exchange_submit::EthereumExchangeSubmitParams; use ethereum_sync_loop::EthereumSyncParams; use headers_relay::sync::TargetTransactionMode; use hex_literal::hex; -use instances::{BridgeInstance, Kovan, Rialto}; +use instances::{BridgeInstance, Kovan, RialtoPoA}; use parity_crypto::publickey::{KeyPair, Secret}; use relay_utils::metrics::MetricsParams; use sp_core::crypto::Pair; -use substrate_client::{SubstrateConnectionParams, SubstrateSigningParams}; use substrate_sync_loop::SubstrateSyncParams; use headers_relay::sync::HeadersSyncParams; use relay_ethereum_client::{ConnectionParams as EthereumConnectionParams, SigningParams as EthereumSigningParams}; -use std::io::Write; +use relay_rialto_client::SigningParams as RialtoSigningParams; +use relay_substrate_client::ConnectionParams as SubstrateConnectionParams; +use std::{io::Write, sync::Arc}; fn main() { initialize(); @@ -197,8 +197,8 @@ fn substrate_connection_params(matches: &clap::ArgMatches) -> Result Result { - let mut params = SubstrateSigningParams::default(); +fn rialto_signing_params(matches: &clap::ArgMatches) -> Result { + let mut params = RialtoSigningParams::default(); if let Some(sub_signer) = matches.value_of("sub-signer") { let sub_signer_password = matches.value_of("sub-signer-password"); params.signer = sp_core::sr25519::Pair::from_string(sub_signer, sub_signer_password) @@ -235,7 +235,7 @@ fn ethereum_sync_params(matches: &clap::ArgMatches) -> Result Result Result Result Result, S Ok(Some(metrics_params)) } -fn instance_params(matches: &clap::ArgMatches) -> Result, String> { - let instance: Box = if let Some(instance) = matches.value_of("sub-pallet-instance") { +fn instance_params(matches: &clap::ArgMatches) -> Result, String> { + let instance = if let Some(instance) = matches.value_of("sub-pallet-instance") { match instance.to_lowercase().as_str() { - "rialto" => Box::new(Rialto::default()), - "kovan" => Box::new(Kovan::default()), + "rialto" => Arc::new(RialtoPoA) as Arc, + "kovan" => Arc::new(Kovan), _ => return Err("Unsupported bridge pallet instance".to_string()), } } else { diff --git a/bridges/relays/ethereum/src/rialto_client.rs b/bridges/relays/ethereum/src/rialto_client.rs new file mode 100644 index 0000000000..861ef8efeb --- /dev/null +++ b/bridges/relays/ethereum/src/rialto_client.rs @@ -0,0 +1,269 @@ +// 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 . + +use crate::ethereum_sync_loop::QueuedEthereumHeader; +use crate::instances::BridgeInstance; +use crate::rpc_errors::RpcError; + +use async_trait::async_trait; +use bp_eth_poa::AuraHeader as SubstrateEthereumHeader; +use codec::{Decode, Encode}; +use headers_relay::sync_types::SubmittedHeaders; +use relay_ethereum_client::types::HeaderId as EthereumHeaderId; +use relay_rialto_client::{Rialto, SigningParams as RialtoSigningParams}; +use relay_substrate_client::{Client as SubstrateClient, TransactionSignScheme}; +use relay_utils::HeaderId; +use sp_core::{crypto::Pair, Bytes}; +use std::{collections::VecDeque, sync::Arc}; + +const ETH_API_IMPORT_REQUIRES_RECEIPTS: &str = "RialtoPoAHeaderApi_is_import_requires_receipts"; +const ETH_API_IS_KNOWN_BLOCK: &str = "RialtoPoAHeaderApi_is_known_block"; +const ETH_API_BEST_BLOCK: &str = "RialtoPoAHeaderApi_best_block"; +const ETH_API_BEST_FINALIZED_BLOCK: &str = "RialtoPoAHeaderApi_finalized_block"; +const EXCH_API_FILTER_TRANSACTION_PROOF: &str = "RialtoCurrencyExchangeApi_filter_transaction_proof"; + +type RpcResult = std::result::Result; + +/// A trait which contains methods that work by using multiple low-level RPCs, or more complicated +/// interactions involving, for example, an Ethereum bridge module. +#[async_trait] +pub trait SubstrateHighLevelRpc { + /// Returns best Ethereum block that Substrate runtime knows of. + async fn best_ethereum_block(&self) -> RpcResult; + /// Returns best finalized Ethereum block that Substrate runtime knows of. + async fn best_ethereum_finalized_block(&self) -> RpcResult; + /// Returns whether or not transactions receipts are required for Ethereum header submission. + async fn ethereum_receipts_required(&self, header: SubstrateEthereumHeader) -> RpcResult; + /// Returns whether or not the given Ethereum header is known to the Substrate runtime. + async fn ethereum_header_known(&self, header_id: EthereumHeaderId) -> RpcResult; +} + +#[async_trait] +impl SubstrateHighLevelRpc for SubstrateClient { + async fn best_ethereum_block(&self) -> RpcResult { + let call = ETH_API_BEST_BLOCK.to_string(); + let data = Bytes(Vec::new()); + + let encoded_response = self.state_call(call, data, None).await?; + let decoded_response: (u64, bp_eth_poa::H256) = Decode::decode(&mut &encoded_response.0[..])?; + + let best_header_id = HeaderId(decoded_response.0, decoded_response.1); + Ok(best_header_id) + } + + async fn best_ethereum_finalized_block(&self) -> RpcResult { + let call = ETH_API_BEST_FINALIZED_BLOCK.to_string(); + let data = Bytes(Vec::new()); + + let encoded_response = self.state_call(call, data, None).await?; + let decoded_response: (u64, bp_eth_poa::H256) = Decode::decode(&mut &encoded_response.0[..])?; + + let best_header_id = HeaderId(decoded_response.0, decoded_response.1); + Ok(best_header_id) + } + + async fn ethereum_receipts_required(&self, header: SubstrateEthereumHeader) -> RpcResult { + let call = ETH_API_IMPORT_REQUIRES_RECEIPTS.to_string(); + let data = Bytes(header.encode()); + + let encoded_response = self.state_call(call, data, None).await?; + let receipts_required: bool = Decode::decode(&mut &encoded_response.0[..])?; + + Ok(receipts_required) + } + + // The Substrate module could prune old headers. So this function could return false even + // if header is synced. And we'll mark corresponding Ethereum header as Orphan. + // + // But when we read the best header from Substrate next time, we will know that + // there's a better header. This Orphan will either be marked as synced, or + // eventually pruned. + async fn ethereum_header_known(&self, header_id: EthereumHeaderId) -> RpcResult { + let call = ETH_API_IS_KNOWN_BLOCK.to_string(); + let data = Bytes(header_id.1.encode()); + + let encoded_response = self.state_call(call, data, None).await?; + let is_known_block: bool = Decode::decode(&mut &encoded_response.0[..])?; + + Ok(is_known_block) + } +} + +/// A trait for RPC calls which are used to submit Ethereum headers to a Substrate +/// runtime. These are typically calls which use a combination of other low-level RPC +/// calls. +#[async_trait] +pub trait SubmitEthereumHeaders { + /// Submits Ethereum header to Substrate runtime. + async fn submit_ethereum_headers( + &self, + params: RialtoSigningParams, + instance: Arc, + headers: Vec, + sign_transactions: bool, + ) -> SubmittedHeaders; + + /// Submits signed Ethereum header to Substrate runtime. + async fn submit_signed_ethereum_headers( + &self, + params: RialtoSigningParams, + instance: Arc, + headers: Vec, + ) -> SubmittedHeaders; + + /// Submits unsigned Ethereum header to Substrate runtime. + async fn submit_unsigned_ethereum_headers( + &self, + instance: Arc, + headers: Vec, + ) -> SubmittedHeaders; +} + +#[async_trait] +impl SubmitEthereumHeaders for SubstrateClient { + async fn submit_ethereum_headers( + &self, + params: RialtoSigningParams, + instance: Arc, + headers: Vec, + sign_transactions: bool, + ) -> SubmittedHeaders { + if sign_transactions { + self.submit_signed_ethereum_headers(params, instance, headers).await + } else { + self.submit_unsigned_ethereum_headers(instance, headers).await + } + } + + async fn submit_signed_ethereum_headers( + &self, + params: RialtoSigningParams, + instance: Arc, + headers: Vec, + ) -> SubmittedHeaders { + let ids = headers.iter().map(|header| header.id()).collect(); + let submission_result = async { + let account_id = params.signer.public().as_array_ref().clone().into(); + let nonce = self.next_account_index(account_id).await?; + + let call = instance.build_signed_header_call(headers); + let transaction = Rialto::sign_transaction(self, ¶ms.signer, nonce, call); + + let _ = self.submit_extrinsic(Bytes(transaction.encode())).await?; + Ok(()) + } + .await; + + match submission_result { + Ok(_) => SubmittedHeaders { + submitted: ids, + incomplete: Vec::new(), + rejected: Vec::new(), + fatal_error: None, + }, + Err(error) => SubmittedHeaders { + submitted: Vec::new(), + incomplete: Vec::new(), + rejected: ids, + fatal_error: Some(error), + }, + } + } + + async fn submit_unsigned_ethereum_headers( + &self, + instance: Arc, + headers: Vec, + ) -> SubmittedHeaders { + let mut ids = headers.iter().map(|header| header.id()).collect::>(); + let mut submitted_headers = SubmittedHeaders::default(); + + for header in headers { + let id = ids.pop_front().expect("both collections have same size; qed"); + + let call = instance.build_unsigned_header_call(header); + let transaction = create_unsigned_submit_transaction(call); + + match self.submit_extrinsic(Bytes(transaction.encode())).await { + Ok(_) => submitted_headers.submitted.push(id), + Err(error) => { + submitted_headers.rejected.push(id); + submitted_headers.rejected.extend(ids); + submitted_headers.fatal_error = Some(error.into()); + break; + } + } + } + + submitted_headers + } +} + +/// A trait for RPC calls which are used to submit proof of Ethereum exchange transaction to a +/// Substrate runtime. These are typically calls which use a combination of other low-level RPC +/// calls. +#[async_trait] +pub trait SubmitEthereumExchangeTransactionProof { + /// Pre-verify Ethereum exchange transaction proof. + async fn verify_exchange_transaction_proof( + &self, + proof: rialto_runtime::exchange::EthereumTransactionInclusionProof, + ) -> RpcResult; + /// Submits Ethereum exchange transaction proof to Substrate runtime. + async fn submit_exchange_transaction_proof( + &self, + params: RialtoSigningParams, + instance: Arc, + proof: rialto_runtime::exchange::EthereumTransactionInclusionProof, + ) -> RpcResult<()>; +} + +#[async_trait] +impl SubmitEthereumExchangeTransactionProof for SubstrateClient { + async fn verify_exchange_transaction_proof( + &self, + proof: rialto_runtime::exchange::EthereumTransactionInclusionProof, + ) -> RpcResult { + let call = EXCH_API_FILTER_TRANSACTION_PROOF.to_string(); + let data = Bytes(proof.encode()); + + let encoded_response = self.state_call(call, data, None).await?; + let is_allowed: bool = Decode::decode(&mut &encoded_response.0[..])?; + + Ok(is_allowed) + } + + async fn submit_exchange_transaction_proof( + &self, + params: RialtoSigningParams, + instance: Arc, + proof: rialto_runtime::exchange::EthereumTransactionInclusionProof, + ) -> RpcResult<()> { + let account_id = params.signer.public().as_array_ref().clone().into(); + let nonce = self.next_account_index(account_id).await?; + + let call = instance.build_currency_exchange_call(proof); + let transaction = Rialto::sign_transaction(self, ¶ms.signer, nonce, call); + + let _ = self.submit_extrinsic(Bytes(transaction.encode())).await?; + Ok(()) + } +} + +/// Create unsigned Substrate transaction for submitting Ethereum header. +fn create_unsigned_submit_transaction(call: rialto_runtime::Call) -> rialto_runtime::UncheckedExtrinsic { + rialto_runtime::UncheckedExtrinsic::new_unsigned(call) +} diff --git a/bridges/relays/ethereum/src/rpc.rs b/bridges/relays/ethereum/src/rpc.rs deleted file mode 100644 index ee463e38ce..0000000000 --- a/bridges/relays/ethereum/src/rpc.rs +++ /dev/null @@ -1,86 +0,0 @@ -// 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 . - -//! RPC Module - -#![warn(missing_docs)] -// The compiler doesn't think we're using the -// code from rpc_api! -#![allow(dead_code)] -#![allow(unused_variables)] -use std::result; - -use crate::rpc_errors::RpcError; -use crate::substrate_types::{ - Hash as SubstrateHash, Header as SubstrateHeader, Number as SubBlockNumber, SignedBlock as SubstrateBlock, -}; - -use async_trait::async_trait; -use bp_eth_poa::AuraHeader as SubstrateEthereumHeader; -use relay_ethereum_client::types::{Bytes, HeaderId as EthereumHeaderId}; - -type Result = result::Result; -type GrandpaAuthorityList = Vec; - -jsonrpsee::rpc_api! { - pub(crate) Substrate { - #[rpc(method = "chain_getHeader", positional_params)] - fn chain_get_header(block_hash: Option) -> SubstrateHeader; - #[rpc(method = "chain_getBlock", positional_params)] - fn chain_get_block(block_hash: Option) -> SubstrateBlock; - #[rpc(method = "chain_getBlockHash", positional_params)] - fn chain_get_block_hash(block_number: Option) -> SubstrateHash; - #[rpc(method = "system_accountNextIndex", positional_params)] - fn system_account_next_index(account_id: node_primitives::AccountId) -> node_primitives::Index; - #[rpc(method = "author_submitExtrinsic", positional_params)] - fn author_submit_extrinsic(extrinsic: Bytes) -> SubstrateHash; - #[rpc(method = "state_call", positional_params)] - fn state_call(method: String, data: Bytes, at_block: Option) -> Bytes; - } -} - -/// The API for the supported Substrate RPC methods. -#[async_trait] -pub trait SubstrateRpc { - /// Returns the best Substrate header. - async fn best_header(&self) -> Result; - /// Get a Substrate block from its hash. - async fn get_block(&self, block_hash: Option) -> Result; - /// Get a Substrate header by its hash. - async fn header_by_hash(&self, hash: SubstrateHash) -> Result; - /// Get a Substrate block hash by its number. - async fn block_hash_by_number(&self, number: SubBlockNumber) -> Result; - /// Get a Substrate header by its number. - async fn header_by_number(&self, block_number: SubBlockNumber) -> Result; - /// Get the nonce of the given Substrate account. - /// - /// Note: It's the caller's responsibility to make sure `account` is a valid ss58 address. - async fn next_account_index(&self, account: node_primitives::AccountId) -> Result; - /// Returns best Ethereum block that Substrate runtime knows of. - async fn best_ethereum_block(&self) -> Result; - /// Returns best finalized Ethereum block that Substrate runtime knows of. - async fn best_ethereum_finalized_block(&self) -> Result; - /// Returns whether or not transactions receipts are required for Ethereum header submission. - async fn ethereum_receipts_required(&self, header: SubstrateEthereumHeader) -> Result; - /// Returns whether or not the given Ethereum header is known to the Substrate runtime. - async fn ethereum_header_known(&self, header_id: EthereumHeaderId) -> Result; - /// Submit an extrinsic for inclusion in a block. - /// - /// Note: The given transaction does not need be SCALE encoded beforehand. - async fn submit_extrinsic(&self, transaction: Bytes) -> Result; - /// Get the GRANDPA authority set at given block. - async fn grandpa_authorities_set(&self, block: SubstrateHash) -> Result; -} diff --git a/bridges/relays/ethereum/src/rpc_errors.rs b/bridges/relays/ethereum/src/rpc_errors.rs index 5e01031968..9f7f14cf9a 100644 --- a/bridges/relays/ethereum/src/rpc_errors.rs +++ b/bridges/relays/ethereum/src/rpc_errors.rs @@ -14,8 +14,8 @@ // You should have received a copy of the GNU General Public License // along with Parity Bridges Common. If not, see . -use jsonrpsee::client::RequestError; use relay_ethereum_client::Error as EthereumNodeError; +use relay_substrate_client::Error as SubstrateNodeError; use relay_utils::MaybeConnectionError; /// Contains common errors that can occur when @@ -29,9 +29,6 @@ pub enum RpcError { Ethereum(EthereumNodeError), /// An error occured when interacting with a Substrate node. Substrate(SubstrateNodeError), - /// An error that can occur when making an HTTP request to - /// an JSON-RPC client. - Request(RequestError), } impl From for String { @@ -40,7 +37,6 @@ impl From for String { RpcError::Serialization(e) => e.to_string(), RpcError::Ethereum(e) => e.to_string(), RpcError::Substrate(e) => e.to_string(), - RpcError::Request(e) => e.to_string(), } } } @@ -63,12 +59,6 @@ impl From for RpcError { } } -impl From for RpcError { - fn from(err: RequestError) -> Self { - Self::Request(err) - } -} - impl From for RpcError { fn from(err: ethabi::Error) -> Self { Self::Ethereum(EthereumNodeError::ResponseParseFailed(format!("{}", err))) @@ -78,8 +68,8 @@ impl From for RpcError { impl MaybeConnectionError for RpcError { fn is_connection_error(&self) -> bool { match self { - RpcError::Request(RequestError::TransportError(_)) => true, RpcError::Ethereum(ref error) => error.is_connection_error(), + RpcError::Substrate(ref error) => error.is_connection_error(), _ => false, } } @@ -87,22 +77,6 @@ impl MaybeConnectionError for RpcError { impl From for RpcError { fn from(err: codec::Error) -> Self { - Self::Substrate(SubstrateNodeError::Decoding(err)) - } -} - -/// Errors that can occur only when interacting with -/// a Substrate node through RPC. -#[derive(Debug)] -pub enum SubstrateNodeError { - /// The response from the client could not be SCALE decoded. - Decoding(codec::Error), -} - -impl ToString for SubstrateNodeError { - fn to_string(&self) -> String { - match self { - Self::Decoding(e) => e.what().to_string(), - } + Self::Substrate(SubstrateNodeError::ResponseParseFailed(err)) } } diff --git a/bridges/relays/ethereum/src/substrate_client.rs b/bridges/relays/ethereum/src/substrate_client.rs deleted file mode 100644 index 4b1765aa2b..0000000000 --- a/bridges/relays/ethereum/src/substrate_client.rs +++ /dev/null @@ -1,410 +0,0 @@ -// 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 . - -use crate::ethereum_sync_loop::QueuedEthereumHeader; -use crate::instances::BridgeInstance; -use crate::rpc::{Substrate, SubstrateRpc}; -use crate::rpc_errors::RpcError; -use crate::substrate_types::{Hash, Header as SubstrateHeader, Number, SignedBlock as SignedSubstrateBlock}; - -use async_trait::async_trait; -use bp_eth_poa::AuraHeader as SubstrateEthereumHeader; -use codec::{Decode, Encode}; -use headers_relay::sync_types::SubmittedHeaders; -use jsonrpsee::raw::RawClient; -use jsonrpsee::transport::http::HttpTransportClient; -use jsonrpsee::Client; -use num_traits::Zero; -use relay_ethereum_client::types::{Bytes, HeaderId as EthereumHeaderId, H256}; -use relay_utils::HeaderId; -use sp_core::crypto::Pair; -use sp_runtime::traits::IdentifyAccount; -use std::collections::VecDeque; - -const ETH_API_IMPORT_REQUIRES_RECEIPTS: &str = "RialtoHeaderApi_is_import_requires_receipts"; -const ETH_API_IS_KNOWN_BLOCK: &str = "RialtoHeaderApi_is_known_block"; -const ETH_API_BEST_BLOCK: &str = "RialtoHeaderApi_best_block"; -const ETH_API_BEST_FINALIZED_BLOCK: &str = "RialtoHeaderApi_finalized_block"; -const EXCH_API_FILTER_TRANSACTION_PROOF: &str = "RialtoCurrencyExchangeApi_filter_transaction_proof"; -const SUB_API_GRANDPA_AUTHORITIES: &str = "GrandpaApi_grandpa_authorities"; - -type RpcResult = std::result::Result; -type GrandpaAuthorityList = Vec; - -/// Substrate connection params. -#[derive(Debug, Clone)] -pub struct SubstrateConnectionParams { - /// Substrate RPC host. - pub host: String, - /// Substrate RPC port. - pub port: u16, -} - -impl Default for SubstrateConnectionParams { - fn default() -> Self { - SubstrateConnectionParams { - host: "localhost".into(), - port: 9933, - } - } -} - -/// Substrate signing params. -#[derive(Clone)] -pub struct SubstrateSigningParams { - /// Substrate transactions signer. - pub signer: sp_core::sr25519::Pair, -} - -impl std::fmt::Debug for SubstrateSigningParams { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "{}", self.signer.public()) - } -} - -impl Default for SubstrateSigningParams { - fn default() -> Self { - SubstrateSigningParams { - signer: sp_keyring::AccountKeyring::Alice.pair(), - } - } -} - -/// Substrate client type. -pub struct SubstrateRpcClient { - /// Substrate RPC client. - client: Client, - /// Genesis block hash. - genesis_hash: H256, - /// Instance of the bridge pallet being synchronized. - instance: Box, -} - -impl SubstrateRpcClient { - /// Returns client that is able to call RPCs on Substrate node. - pub async fn new(params: SubstrateConnectionParams, instance: Box) -> RpcResult { - let uri = format!("http://{}:{}", params.host, params.port); - let transport = HttpTransportClient::new(&uri); - let raw_client = RawClient::new(transport); - let client: Client = raw_client.into(); - - let number: Number = Zero::zero(); - let genesis_hash = Substrate::chain_get_block_hash(&client, number).await?; - - Ok(Self { - client, - genesis_hash, - instance, - }) - } -} - -#[async_trait] -impl SubstrateRpc for SubstrateRpcClient { - async fn best_header(&self) -> RpcResult { - Ok(Substrate::chain_get_header(&self.client, None).await?) - } - - async fn get_block(&self, block_hash: Option) -> RpcResult { - Ok(Substrate::chain_get_block(&self.client, block_hash).await?) - } - - async fn header_by_hash(&self, block_hash: Hash) -> RpcResult { - Ok(Substrate::chain_get_header(&self.client, block_hash).await?) - } - - async fn block_hash_by_number(&self, number: Number) -> RpcResult { - Ok(Substrate::chain_get_block_hash(&self.client, number).await?) - } - - async fn header_by_number(&self, block_number: Number) -> RpcResult { - let block_hash = Self::block_hash_by_number(self, block_number).await?; - Ok(Self::header_by_hash(self, block_hash).await?) - } - - async fn next_account_index(&self, account: node_primitives::AccountId) -> RpcResult { - Ok(Substrate::system_account_next_index(&self.client, account).await?) - } - - async fn best_ethereum_block(&self) -> RpcResult { - let call = ETH_API_BEST_BLOCK.to_string(); - let data = Bytes(Vec::new()); - - let encoded_response = Substrate::state_call(&self.client, call, data, None).await?; - let decoded_response: (u64, bp_eth_poa::H256) = Decode::decode(&mut &encoded_response.0[..])?; - - let best_header_id = HeaderId(decoded_response.0, decoded_response.1); - Ok(best_header_id) - } - - async fn best_ethereum_finalized_block(&self) -> RpcResult { - let call = ETH_API_BEST_FINALIZED_BLOCK.to_string(); - let data = Bytes(Vec::new()); - - let encoded_response = Substrate::state_call(&self.client, call, data, None).await?; - let decoded_response: (u64, bp_eth_poa::H256) = Decode::decode(&mut &encoded_response.0[..])?; - - let best_header_id = HeaderId(decoded_response.0, decoded_response.1); - Ok(best_header_id) - } - - async fn ethereum_receipts_required(&self, header: SubstrateEthereumHeader) -> RpcResult { - let call = ETH_API_IMPORT_REQUIRES_RECEIPTS.to_string(); - let data = Bytes(header.encode()); - - let encoded_response = Substrate::state_call(&self.client, call, data, None).await?; - let receipts_required: bool = Decode::decode(&mut &encoded_response.0[..])?; - - Ok(receipts_required) - } - - // The Substrate module could prune old headers. So this function could return false even - // if header is synced. And we'll mark corresponding Ethereum header as Orphan. - // - // But when we read the best header from Substrate next time, we will know that - // there's a better header. This Orphan will either be marked as synced, or - // eventually pruned. - async fn ethereum_header_known(&self, header_id: EthereumHeaderId) -> RpcResult { - let call = ETH_API_IS_KNOWN_BLOCK.to_string(); - let data = Bytes(header_id.1.encode()); - - let encoded_response = Substrate::state_call(&self.client, call, data, None).await?; - let is_known_block: bool = Decode::decode(&mut &encoded_response.0[..])?; - - Ok(is_known_block) - } - - async fn submit_extrinsic(&self, transaction: Bytes) -> RpcResult { - let tx_hash = Substrate::author_submit_extrinsic(&self.client, transaction).await?; - log::trace!(target: "bridge", "Sent transaction to Substrate node: {:?}", tx_hash); - Ok(tx_hash) - } - - async fn grandpa_authorities_set(&self, block: Hash) -> RpcResult { - let call = SUB_API_GRANDPA_AUTHORITIES.to_string(); - let data = Bytes(Vec::new()); - - let encoded_response = Substrate::state_call(&self.client, call, data, Some(block)).await?; - let authority_list = encoded_response.0; - - Ok(authority_list) - } -} - -/// A trait for RPC calls which are used to submit Ethereum headers to a Substrate -/// runtime. These are typically calls which use a combination of other low-level RPC -/// calls. -#[async_trait] -pub trait SubmitEthereumHeaders: SubstrateRpc { - /// Submits Ethereum header to Substrate runtime. - async fn submit_ethereum_headers( - &self, - params: SubstrateSigningParams, - headers: Vec, - sign_transactions: bool, - ) -> SubmittedHeaders; - - /// Submits signed Ethereum header to Substrate runtime. - async fn submit_signed_ethereum_headers( - &self, - params: SubstrateSigningParams, - headers: Vec, - ) -> SubmittedHeaders; - - /// Submits unsigned Ethereum header to Substrate runtime. - async fn submit_unsigned_ethereum_headers( - &self, - headers: Vec, - ) -> SubmittedHeaders; -} - -#[async_trait] -impl SubmitEthereumHeaders for SubstrateRpcClient { - async fn submit_ethereum_headers( - &self, - params: SubstrateSigningParams, - headers: Vec, - sign_transactions: bool, - ) -> SubmittedHeaders { - if sign_transactions { - self.submit_signed_ethereum_headers(params, headers).await - } else { - self.submit_unsigned_ethereum_headers(headers).await - } - } - - async fn submit_signed_ethereum_headers( - &self, - params: SubstrateSigningParams, - headers: Vec, - ) -> SubmittedHeaders { - let ids = headers.iter().map(|header| header.id()).collect(); - let submission_result = async { - let account_id = params.signer.public().as_array_ref().clone().into(); - let nonce = self.next_account_index(account_id).await?; - - let call = self.instance.build_signed_header_call(headers); - let transaction = create_signed_submit_transaction(call, ¶ms.signer, nonce, self.genesis_hash); - let _ = self.submit_extrinsic(Bytes(transaction.encode())).await?; - Ok(()) - } - .await; - - match submission_result { - Ok(_) => SubmittedHeaders { - submitted: ids, - incomplete: Vec::new(), - rejected: Vec::new(), - fatal_error: None, - }, - Err(error) => SubmittedHeaders { - submitted: Vec::new(), - incomplete: Vec::new(), - rejected: ids, - fatal_error: Some(error), - }, - } - } - - async fn submit_unsigned_ethereum_headers( - &self, - headers: Vec, - ) -> SubmittedHeaders { - let mut ids = headers.iter().map(|header| header.id()).collect::>(); - let mut submitted_headers = SubmittedHeaders::default(); - - for header in headers { - let id = ids.pop_front().expect("both collections have same size; qed"); - - let call = self.instance.build_unsigned_header_call(header); - let transaction = create_unsigned_submit_transaction(call); - - match self.submit_extrinsic(Bytes(transaction.encode())).await { - Ok(_) => submitted_headers.submitted.push(id), - Err(error) => { - submitted_headers.rejected.push(id); - submitted_headers.rejected.extend(ids); - submitted_headers.fatal_error = Some(error); - break; - } - } - } - - submitted_headers - } -} - -/// A trait for RPC calls which are used to submit proof of Ethereum exchange transaction to a -/// Substrate runtime. These are typically calls which use a combination of other low-level RPC -/// calls. -#[async_trait] -pub trait SubmitEthereumExchangeTransactionProof: SubstrateRpc { - /// Pre-verify Ethereum exchange transaction proof. - async fn verify_exchange_transaction_proof( - &self, - proof: rialto_runtime::exchange::EthereumTransactionInclusionProof, - ) -> RpcResult; - /// Submits Ethereum exchange transaction proof to Substrate runtime. - async fn submit_exchange_transaction_proof( - &self, - params: SubstrateSigningParams, - proof: rialto_runtime::exchange::EthereumTransactionInclusionProof, - ) -> RpcResult<()>; -} - -#[async_trait] -impl SubmitEthereumExchangeTransactionProof for SubstrateRpcClient { - async fn verify_exchange_transaction_proof( - &self, - proof: rialto_runtime::exchange::EthereumTransactionInclusionProof, - ) -> RpcResult { - let call = EXCH_API_FILTER_TRANSACTION_PROOF.to_string(); - let data = Bytes(proof.encode()); - - let encoded_response = Substrate::state_call(&self.client, call, data, None).await?; - let is_allowed: bool = Decode::decode(&mut &encoded_response.0[..])?; - - Ok(is_allowed) - } - - async fn submit_exchange_transaction_proof( - &self, - params: SubstrateSigningParams, - proof: rialto_runtime::exchange::EthereumTransactionInclusionProof, - ) -> RpcResult<()> { - let account_id = params.signer.public().as_array_ref().clone().into(); - let nonce = self.next_account_index(account_id).await?; - - let call = self.instance.build_currency_exchange_call(proof); - let transaction = create_signed_transaction(call, ¶ms.signer, nonce, self.genesis_hash); - - let _ = self.submit_extrinsic(Bytes(transaction.encode())).await?; - Ok(()) - } -} - -/// Create signed Substrate transaction for submitting Ethereum headers. -fn create_signed_submit_transaction( - signed_call: rialto_runtime::Call, - signer: &sp_core::sr25519::Pair, - index: node_primitives::Index, - genesis_hash: H256, -) -> rialto_runtime::UncheckedExtrinsic { - create_signed_transaction(signed_call, signer, index, genesis_hash) -} - -/// Create unsigned Substrate transaction for submitting Ethereum header. -fn create_unsigned_submit_transaction(call: rialto_runtime::Call) -> rialto_runtime::UncheckedExtrinsic { - rialto_runtime::UncheckedExtrinsic::new_unsigned(call) -} - -/// Create signed Substrate transaction. -fn create_signed_transaction( - function: rialto_runtime::Call, - signer: &sp_core::sr25519::Pair, - index: node_primitives::Index, - genesis_hash: H256, -) -> rialto_runtime::UncheckedExtrinsic { - let extra = |i: node_primitives::Index, f: node_primitives::Balance| { - ( - frame_system::CheckSpecVersion::::new(), - frame_system::CheckTxVersion::::new(), - frame_system::CheckGenesis::::new(), - frame_system::CheckEra::::from(sp_runtime::generic::Era::Immortal), - frame_system::CheckNonce::::from(i), - frame_system::CheckWeight::::new(), - pallet_transaction_payment::ChargeTransactionPayment::::from(f), - ) - }; - let raw_payload = rialto_runtime::SignedPayload::from_raw( - function, - extra(index, 0), - ( - rialto_runtime::VERSION.spec_version, - rialto_runtime::VERSION.transaction_version, - genesis_hash, - genesis_hash, - (), - (), - (), - ), - ); - let signature = raw_payload.using_encoded(|payload| signer.sign(payload)); - let signer: sp_runtime::MultiSigner = signer.public().into(); - let (function, extra, _) = raw_payload.deconstruct(); - - rialto_runtime::UncheckedExtrinsic::new_signed(function, signer.into_account(), signature.into(), extra) -} diff --git a/bridges/relays/ethereum/src/substrate_sync_loop.rs b/bridges/relays/ethereum/src/substrate_sync_loop.rs index 8c3e0fb91d..a3f525f111 100644 --- a/bridges/relays/ethereum/src/substrate_sync_loop.rs +++ b/bridges/relays/ethereum/src/substrate_sync_loop.rs @@ -14,29 +14,26 @@ // You should have received a copy of the GNU General Public License // along with Parity Bridges Common. If not, see . -//! Substrate -> Ethereum synchronization. +//! Rialto-Substrate -> Ethereum PoA synchronization. use crate::ethereum_client::EthereumHighLevelRpc; -use crate::instances::BridgeInstance; -use crate::rpc::SubstrateRpc; use crate::rpc_errors::RpcError; -use crate::substrate_client::{SubstrateConnectionParams, SubstrateRpcClient}; -use crate::substrate_types::{ - GrandpaJustification, Hash, Number, QueuedSubstrateHeader, SubstrateHeaderId, SubstrateHeadersSyncPipeline, - SubstrateSyncHeader as Header, -}; use async_trait::async_trait; +use codec::Encode; use headers_relay::{ sync::HeadersSyncParams, sync_loop::{SourceClient, TargetClient}, - sync_types::{SourceHeader, SubmittedHeaders}, + sync_types::{HeadersSyncPipeline, QueuedHeader, SourceHeader, SubmittedHeaders}, }; use relay_ethereum_client::{ types::Address, Client as EthereumClient, ConnectionParams as EthereumConnectionParams, 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_utils::metrics::MetricsParams; +use sp_runtime::Justification; use std::fmt::Debug; use std::{collections::HashSet, time::Duration}; @@ -71,18 +68,39 @@ pub struct SubstrateSyncParams { pub sync_params: HeadersSyncParams, /// Metrics parameters. pub metrics_params: Option, - /// Instance of the bridge pallet being synchronized. - pub instance: Box, } +/// Substrate synchronization pipeline. +#[derive(Clone, Copy, Debug)] +#[cfg_attr(test, derive(PartialEq))] +pub struct SubstrateHeadersSyncPipeline; + +impl HeadersSyncPipeline for SubstrateHeadersSyncPipeline { + const SOURCE_NAME: &'static str = "Substrate"; + const TARGET_NAME: &'static str = "Ethereum"; + + type Hash = rialto_runtime::Hash; + type Number = rialto_runtime::BlockNumber; + type Header = RialtoSyncHeader; + type Extra = (); + type Completion = Justification; + + fn estimate_size(source: &QueuedHeader) -> usize { + source.header().encode().len() + } +} + +/// Queued substrate header ID. +pub type QueuedRialtoHeader = QueuedHeader; + /// Substrate client as headers source. struct SubstrateHeadersSource { /// Substrate node client. - client: SubstrateRpcClient, + client: SubstrateClient, } impl SubstrateHeadersSource { - fn new(client: SubstrateRpcClient) -> Self { + fn new(client: SubstrateClient) -> Self { Self { client } } } @@ -91,22 +109,30 @@ impl SubstrateHeadersSource { impl SourceClient for SubstrateHeadersSource { type Error = RpcError; - async fn best_block_number(&self) -> Result { + async fn best_block_number(&self) -> Result { Ok(self.client.best_header().await?.number) } - async fn header_by_hash(&self, hash: Hash) -> Result { - self.client.header_by_hash(hash).await.map(Into::into) + 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: Number) -> Result { - self.client.header_by_number(number).await.map(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: SubstrateHeaderId, - ) -> Result<(SubstrateHeaderId, Option), Self::Error> { + 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; @@ -116,9 +142,9 @@ impl SourceClient for SubstrateHeadersSource { async fn header_extra( &self, - id: SubstrateHeaderId, - _header: QueuedSubstrateHeader, - ) -> Result<(SubstrateHeaderId, ()), Self::Error> { + id: RialtoHeaderId, + _header: QueuedRialtoHeader, + ) -> Result<(RialtoHeaderId, ()), Self::Error> { Ok((id, ())) } } @@ -147,38 +173,35 @@ impl EthereumHeadersTarget { impl TargetClient for EthereumHeadersTarget { type Error = RpcError; - async fn best_header_id(&self) -> Result { + async fn best_header_id(&self) -> Result { self.client.best_substrate_block(self.contract).await } - async fn is_known_header(&self, id: SubstrateHeaderId) -> Result<(SubstrateHeaderId, bool), Self::Error> { + async fn is_known_header(&self, id: RialtoHeaderId) -> Result<(RialtoHeaderId, bool), Self::Error> { self.client.substrate_header_known(self.contract, id).await } - async fn submit_headers( - &self, - headers: Vec, - ) -> SubmittedHeaders { + async fn submit_headers(&self, headers: Vec) -> SubmittedHeaders { self.client .submit_substrate_headers(self.sign_params.clone(), self.contract, headers) .await } - async fn incomplete_headers_ids(&self) -> Result, Self::Error> { + async fn incomplete_headers_ids(&self) -> Result, Self::Error> { self.client.incomplete_substrate_headers(self.contract).await } async fn complete_header( &self, - id: SubstrateHeaderId, - completion: GrandpaJustification, - ) -> Result { + id: RialtoHeaderId, + completion: Justification, + ) -> Result { self.client .complete_substrate_header(self.sign_params.clone(), self.contract, id, completion) .await } - async fn requires_extra(&self, header: QueuedSubstrateHeader) -> Result<(SubstrateHeaderId, bool), Self::Error> { + async fn requires_extra(&self, header: QueuedRialtoHeader) -> Result<(RialtoHeaderId, bool), Self::Error> { Ok((header.header().id(), false)) } } @@ -192,11 +215,10 @@ pub fn run(params: SubstrateSyncParams) -> Result<(), RpcError> { eth_contract_address, sync_params, metrics_params, - instance, } = params; let eth_client = EthereumClient::new(eth_params); - let sub_client = async_std::task::block_on(async { SubstrateRpcClient::new(sub_params, instance).await })?; + let sub_client = async_std::task::block_on(async { SubstrateClient::::new(sub_params).await })?; let target = EthereumHeadersTarget::new(eth_client, eth_contract_address, eth_sign); let source = SubstrateHeadersSource::new(sub_client); diff --git a/bridges/relays/ethereum/src/substrate_types.rs b/bridges/relays/ethereum/src/substrate_types.rs index 793358be03..b88f383139 100644 --- a/bridges/relays/ethereum/src/substrate_types.rs +++ b/bridges/relays/ethereum/src/substrate_types.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2020 Parity Technologies (UK) Ltd. +// Copyright 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 @@ -14,86 +14,16 @@ // You should have received a copy of the GNU General Public License // along with Parity Bridges Common. If not, see . -use codec::Encode; -use headers_relay::sync_types::{HeadersSyncPipeline, QueuedHeader, SourceHeader}; -use relay_utils::HeaderId; +//! Converting between Ethereum headers and bridge module types. -pub use bp_eth_poa::{ - Address, AuraHeader as SubstrateEthereumHeader, Bloom, Bytes, LogEntry as SubstrateEthereumLogEntry, - Receipt as SubstrateEthereumReceipt, TransactionOutcome as SubstrateEthereumTransactionOutcome, H256, U256, +use bp_eth_poa::{ + AuraHeader as SubstrateEthereumHeader, LogEntry as SubstrateEthereumLogEntry, Receipt as SubstrateEthereumReceipt, + TransactionOutcome as SubstrateEthereumTransactionOutcome, }; use relay_ethereum_client::types::{ Header as EthereumHeader, Receipt as EthereumReceipt, HEADER_ID_PROOF as ETHEREUM_HEADER_ID_PROOF, }; -/// Substrate header hash. -pub type Hash = rialto_runtime::Hash; - -/// Substrate header number. -pub type Number = rialto_runtime::BlockNumber; - -/// Substrate header type. -pub type Header = rialto_runtime::Header; - -/// Substrate header type used in headers sync. -#[derive(Clone, Debug, PartialEq)] -pub struct SubstrateSyncHeader(Header); - -impl std::ops::Deref for SubstrateSyncHeader { - type Target = Header; - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -/// Substrate signed block type. -pub type SignedBlock = rialto_runtime::SignedBlock; - -/// GRANDPA justification. -pub type GrandpaJustification = Vec; - -/// Substrate header ID. -pub type SubstrateHeaderId = HeaderId; - -/// Queued substrate header ID. -pub type QueuedSubstrateHeader = QueuedHeader; - -/// Substrate synchronization pipeline. -#[derive(Clone, Copy, Debug)] -#[cfg_attr(test, derive(PartialEq))] -pub struct SubstrateHeadersSyncPipeline; - -impl HeadersSyncPipeline for SubstrateHeadersSyncPipeline { - const SOURCE_NAME: &'static str = "Substrate"; - const TARGET_NAME: &'static str = "Ethereum"; - - type Hash = rialto_runtime::Hash; - type Number = rialto_runtime::BlockNumber; - type Header = SubstrateSyncHeader; - type Extra = (); - type Completion = GrandpaJustification; - - fn estimate_size(source: &QueuedHeader) -> usize { - source.header().encode().len() - } -} - -impl From
for SubstrateSyncHeader { - fn from(header: Header) -> Self { - Self(header) - } -} - -impl SourceHeader for SubstrateSyncHeader { - fn id(&self) -> SubstrateHeaderId { - HeaderId(self.number, self.hash()) - } - - fn parent_id(&self) -> SubstrateHeaderId { - HeaderId(self.number - 1, self.parent_hash) - } -} - /// Convert Ethereum header into Ethereum header for Substrate. pub fn into_substrate_ethereum_header(header: &EthereumHeader) -> SubstrateEthereumHeader { SubstrateEthereumHeader { diff --git a/bridges/relays/millau-client/Cargo.toml b/bridges/relays/millau-client/Cargo.toml new file mode 100644 index 0000000000..cd142200a0 --- /dev/null +++ b/bridges/relays/millau-client/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "relay-millau-client" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2018" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +millau-runtime = { path = "../../bin/millau-runtime" } +substrate-client = { path = "../substrate-client" } diff --git a/bridges/relays/millau-client/src/lib.rs b/bridges/relays/millau-client/src/lib.rs new file mode 100644 index 0000000000..c05fad91ac --- /dev/null +++ b/bridges/relays/millau-client/src/lib.rs @@ -0,0 +1,33 @@ +// 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 . + +//! Types used to connect to the Millau-Substrate chain. + +use relay_substrate_client::Chain; + +/// Millau chain definition. +#[derive(Debug, Clone, Copy)] +pub struct Millau; + +impl Chain for Millau { + type BlockNumber = millau_runtime::BlockNumber; + type Hash = millau_runtime::Hash; + type Header = millau_runtime::Header; + type AccountId = millau_runtime::AccountId; + type Index = millau_runtime::Index; + type SignedBlock = millau_runtime::SignedBlock; + type Call = rialto_runtime::Call; +} diff --git a/bridges/relays/rialto-client/Cargo.toml b/bridges/relays/rialto-client/Cargo.toml new file mode 100644 index 0000000000..5f5fef38c3 --- /dev/null +++ b/bridges/relays/rialto-client/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "relay-rialto-client" +version = "0.1.0" +authors = ["Parity Technologies "] +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 + +rialto-runtime = { path = "../../bin/rialto-runtime" } + +# Substrate Dependencies + +frame-system = { version = "2.0.0", tag = 'v2.0.0', git = "https://github.com/paritytech/substrate.git" } +pallet-transaction-payment = { version = "2.0.0", tag = 'v2.0.0', git = "https://github.com/paritytech/substrate.git" } +sp-core = { version = "2.0.0", tag = 'v2.0.0', git = "https://github.com/paritytech/substrate.git" } +sp-keyring = { 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/rialto-client/src/lib.rs b/bridges/relays/rialto-client/src/lib.rs new file mode 100644 index 0000000000..4db86ae9e3 --- /dev/null +++ b/bridges/relays/rialto-client/src/lib.rs @@ -0,0 +1,132 @@ +// 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 . + +//! Types used to connect to the Rialto-Substrate chain. + +use codec::Encode; +use headers_relay::sync_types::SourceHeader; +use relay_substrate_client::{Chain, Client, TransactionSignScheme}; +use sp_core::Pair; +use sp_runtime::{ + generic::SignedPayload, + traits::{Header as HeaderT, IdentifyAccount}, +}; + +/// Rialto header id. +pub type HeaderId = relay_utils::HeaderId; + +/// Rialto chain definition +#[derive(Debug, Clone, Copy)] +pub struct Rialto; + +impl Chain for Rialto { + type BlockNumber = rialto_runtime::BlockNumber; + type Hash = rialto_runtime::Hash; + type Header = rialto_runtime::Header; + type AccountId = rialto_runtime::AccountId; + type Index = rialto_runtime::Index; + type SignedBlock = rialto_runtime::SignedBlock; + type Call = rialto_runtime::Call; +} + +impl TransactionSignScheme for Rialto { + type Chain = Rialto; + type AccountKeyPair = sp_core::sr25519::Pair; + type SignedTransaction = rialto_runtime::UncheckedExtrinsic; + + fn sign_transaction( + client: &Client, + signer: &Self::AccountKeyPair, + signer_nonce: ::Index, + call: ::Call, + ) -> Self::SignedTransaction { + let raw_payload = SignedPayload::from_raw( + call, + ( + frame_system::CheckSpecVersion::::new(), + frame_system::CheckTxVersion::::new(), + frame_system::CheckGenesis::::new(), + frame_system::CheckEra::::from(sp_runtime::generic::Era::Immortal), + frame_system::CheckNonce::::from(signer_nonce), + frame_system::CheckWeight::::new(), + pallet_transaction_payment::ChargeTransactionPayment::::from(0), + ), + ( + rialto_runtime::VERSION.spec_version, + rialto_runtime::VERSION.transaction_version, + *client.genesis_hash(), + *client.genesis_hash(), + (), + (), + (), + ), + ); + let signature = raw_payload.using_encoded(|payload| signer.sign(payload)); + let signer: sp_runtime::MultiSigner = signer.public().into(); + let (call, extra, _) = raw_payload.deconstruct(); + + rialto_runtime::UncheckedExtrinsic::new_signed(call, signer.into_account(), signature.into(), extra) + } +} + +/// Rialto signing params. +#[derive(Clone)] +pub struct SigningParams { + /// Substrate transactions signer. + pub signer: sp_core::sr25519::Pair, +} + +impl std::fmt::Debug for SigningParams { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{}", self.signer.public()) + } +} + +impl Default for SigningParams { + fn default() -> Self { + SigningParams { + signer: sp_keyring::AccountKeyring::Alice.pair(), + } + } +} + +/// Rialto header type used in headers sync. +#[derive(Clone, Debug, PartialEq)] +pub struct SyncHeader(rialto_runtime::Header); + +impl std::ops::Deref for SyncHeader { + type Target = rialto_runtime::Header; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl From for SyncHeader { + fn from(header: rialto_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() - 1, *self.parent_hash()) + } +} diff --git a/bridges/relays/substrate-client/Cargo.toml b/bridges/relays/substrate-client/Cargo.toml new file mode 100644 index 0000000000..fc92f568fc --- /dev/null +++ b/bridges/relays/substrate-client/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "relay-substrate-client" +version = "0.1.0" +authors = ["Parity Technologies "] +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" } +jsonrpsee = { git = "https://github.com/svyatonik/jsonrpsee.git", branch = "shared-client-in-rpc-api", default-features = false, features = ["http"] } +log = "0.4.11" +num-traits = "0.2" +relay-utils = { path = "../utils" } + +# Substrate Dependencies + +frame-support = { version = "2.0.0", tag = 'v2.0.0', git = "https://github.com/paritytech/substrate.git" } +frame-system = { version = "2.0.0", tag = 'v2.0.0', git = "https://github.com/paritytech/substrate.git" } +sp-core = { 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" } +sp-std = { version = "2.0.0", tag = 'v2.0.0', git = "https://github.com/paritytech/substrate.git" } diff --git a/bridges/relays/substrate-client/src/chain.rs b/bridges/relays/substrate-client/src/chain.rs new file mode 100644 index 0000000000..94126d811b --- /dev/null +++ b/bridges/relays/substrate-client/src/chain.rs @@ -0,0 +1,87 @@ +// 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 . + +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_std::fmt::Debug; + +/// Substrate-based chain from minimal relay-client point of view. +pub trait Chain { + /// The block number type used by the runtime. + type BlockNumber: Parameter + + Member + + MaybeSerializeDeserialize + + Debug + + MaybeDisplay + + AtLeast32BitUnsigned + + Default + + Bounded + + Copy + + sp_std::hash::Hash + + sp_std::str::FromStr + + MaybeMallocSizeOf; + /// The output of the `Hashing` function. + type Hash: Parameter + + Member + + MaybeSerializeDeserialize + + Debug + + MaybeDisplay + + SimpleBitOps + + Ord + + Default + + Copy + + CheckEqual + + sp_std::hash::Hash + + AsRef<[u8]> + + AsMut<[u8]> + + MaybeMallocSizeOf; + /// The block header. + type Header: Parameter + HeaderT; + /// The user account identifier type for the runtime. + type AccountId: Parameter + Member + MaybeSerializeDeserialize + Debug + MaybeDisplay + Ord + Default; + /// Account index (aka nonce) type. This stores the number of previous transactions associated + /// with a sender account. + type Index: Parameter + Member + MaybeSerialize + Debug + Default + MaybeDisplay + AtLeast32Bit + Copy; + /// Block type. + type SignedBlock: Member + Serialize + DeserializeOwned; + /// The aggregated `Call` type. + type Call: Dispatchable + Debug; +} + +/// Substrate-based chain transactions signing scheme. +pub trait TransactionSignScheme { + /// Chain that this scheme is to be used. + type Chain: Chain; + /// Type of key pairs used to sign transactions. + type AccountKeyPair: Pair; + /// Signed transaction. + type SignedTransaction; + + /// Create transaction for given runtime call, signed by given account. + fn sign_transaction( + client: &Client, + signer: &Self::AccountKeyPair, + signer_nonce: ::Index, + call: ::Call, + ) -> Self::SignedTransaction; +} diff --git a/bridges/relays/substrate-client/src/client.rs b/bridges/relays/substrate-client/src/client.rs new file mode 100644 index 0000000000..5d744a8920 --- /dev/null +++ b/bridges/relays/substrate-client/src/client.rs @@ -0,0 +1,135 @@ +// 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 node client. + +use crate::chain::Chain; +use crate::rpc::Substrate; +use crate::{ConnectionParams, Result}; + +use jsonrpsee::common::DeserializeOwned; +use jsonrpsee::raw::RawClient; +use jsonrpsee::transport::http::HttpTransportClient; +use jsonrpsee::Client as RpcClient; +use num_traits::Zero; +use sp_core::Bytes; + +const SUB_API_GRANDPA_AUTHORITIES: &str = "GrandpaApi_grandpa_authorities"; + +/// Opaque GRANDPA authorities set. +pub type OpaqueGrandpaAuthoritiesSet = Vec; + +/// Substrate client type. +pub struct Client { + /// Substrate RPC client. + client: RpcClient, + /// Genesis block hash. + genesis_hash: C::Hash, +} + +impl std::fmt::Debug for Client { + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + fmt.debug_struct("Client") + .field("genesis_hash", &self.genesis_hash) + .finish() + } +} + +impl Client { + /// Returns client that is able to call RPCs on Substrate node. + pub async fn new(params: ConnectionParams) -> Result { + let uri = format!("http://{}:{}", params.host, params.port); + let transport = HttpTransportClient::new(&uri); + let raw_client = RawClient::new(transport); + let client: RpcClient = raw_client.into(); + + let number: C::BlockNumber = Zero::zero(); + let genesis_hash = Substrate::::chain_get_block_hash(&client, number).await?; + + Ok(Self { client, genesis_hash }) + } +} + +impl Client +where + C::Header: DeserializeOwned, + C::Index: DeserializeOwned, +{ + /// Return hash of the genesis block. + pub fn genesis_hash(&self) -> &C::Hash { + &self.genesis_hash + } + + /// Returns the best Substrate header. + pub async fn best_header(&self) -> Result { + Ok(Substrate::::chain_get_header(&self.client, None).await?) + } + + /// Get a Substrate block from its hash. + pub async fn get_block(&self, block_hash: Option) -> Result { + Ok(Substrate::::chain_get_block(&self.client, block_hash).await?) + } + + /// Get a Substrate header by its hash. + pub async fn header_by_hash(&self, block_hash: C::Hash) -> Result { + Ok(Substrate::::chain_get_header(&self.client, block_hash).await?) + } + + /// Get a Substrate block hash by its number. + pub async fn block_hash_by_number(&self, number: C::BlockNumber) -> Result { + Ok(Substrate::::chain_get_block_hash(&self.client, number).await?) + } + + /// Get a Substrate header by its number. + pub async fn header_by_number(&self, block_number: C::BlockNumber) -> Result { + let block_hash = Self::block_hash_by_number(self, block_number).await?; + Ok(Self::header_by_hash(self, block_hash).await?) + } + + /// Get the nonce of the given Substrate account. + /// + /// Note: It's the caller's responsibility to make sure `account` is a valid ss58 address. + pub async fn next_account_index(&self, account: C::AccountId) -> Result { + Ok(Substrate::::system_account_next_index(&self.client, account).await?) + } + + /// Submit an extrinsic for inclusion in a block. + /// + /// Note: The given transaction does not need be SCALE encoded beforehand. + pub async fn submit_extrinsic(&self, transaction: Bytes) -> Result { + let tx_hash = Substrate::::author_submit_extrinsic(&self.client, transaction).await?; + log::trace!(target: "bridge", "Sent transaction to Substrate node: {:?}", tx_hash); + Ok(tx_hash) + } + + /// Get the GRANDPA authority set at given block. + pub async fn grandpa_authorities_set(&self, block: C::Hash) -> Result { + let call = SUB_API_GRANDPA_AUTHORITIES.to_string(); + let data = Bytes(Vec::new()); + + let encoded_response = Substrate::::state_call(&self.client, call, data, Some(block)).await?; + let authority_list = encoded_response.0; + + Ok(authority_list) + } + + /// Execute runtime call at given block. + pub async fn state_call(&self, method: String, data: Bytes, at_block: Option) -> Result { + Substrate::::state_call(&self.client, method, data, at_block) + .await + .map_err(Into::into) + } +} diff --git a/bridges/relays/substrate-client/src/error.rs b/bridges/relays/substrate-client/src/error.rs new file mode 100644 index 0000000000..78b519c7b0 --- /dev/null +++ b/bridges/relays/substrate-client/src/error.rs @@ -0,0 +1,55 @@ +// 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 node RPC errors. + +use jsonrpsee::client::RequestError; +use relay_utils::MaybeConnectionError; + +/// Result type used by Substrate client. +pub type Result = std::result::Result; + +/// Errors that can occur only when interacting with +/// a Substrate node through RPC. +#[derive(Debug)] +pub enum Error { + /// An error that can occur when making an HTTP request to + /// an JSON-RPC client. + Request(RequestError), + /// The response from the client could not be SCALE decoded. + ResponseParseFailed(codec::Error), +} + +impl From for Error { + fn from(error: RequestError) -> Self { + Error::Request(error) + } +} + +impl MaybeConnectionError for Error { + fn is_connection_error(&self) -> bool { + matches!(*self, Error::Request(RequestError::TransportError(_))) + } +} + +impl ToString for Error { + fn to_string(&self) -> String { + match self { + Self::Request(e) => e.to_string(), + Self::ResponseParseFailed(e) => e.what().to_string(), + } + } +} diff --git a/bridges/relays/substrate-client/src/lib.rs b/bridges/relays/substrate-client/src/lib.rs new file mode 100644 index 0000000000..6bf38afdb8 --- /dev/null +++ b/bridges/relays/substrate-client/src/lib.rs @@ -0,0 +1,46 @@ +// 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 . + +//! Tools to interact with (Open) Ethereum node using RPC methods. + +#![warn(missing_docs)] + +mod chain; +mod client; +mod error; +mod rpc; + +pub use crate::chain::{Chain, TransactionSignScheme}; +pub use crate::client::{Client, OpaqueGrandpaAuthoritiesSet}; +pub use crate::error::{Error, Result}; + +/// Substrate connection params. +#[derive(Debug, Clone)] +pub struct ConnectionParams { + /// Substrate RPC host. + pub host: String, + /// Substrate RPC port. + pub port: u16, +} + +impl Default for ConnectionParams { + fn default() -> Self { + ConnectionParams { + host: "localhost".into(), + port: 9933, + } + } +} diff --git a/bridges/relays/substrate-client/src/rpc.rs b/bridges/relays/substrate-client/src/rpc.rs new file mode 100644 index 0000000000..60141e0df6 --- /dev/null +++ b/bridges/relays/substrate-client/src/rpc.rs @@ -0,0 +1,43 @@ +// 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 . + +//! The most generic Substrate node RPC interface. + +// The compiler doesn't think we're using the +// code from rpc_api! +#![allow(dead_code)] +#![allow(unused_variables)] + +use crate::chain::Chain; + +use sp_core::Bytes; + +jsonrpsee::rpc_api! { + pub(crate) Substrate { + #[rpc(method = "chain_getHeader", positional_params)] + fn chain_get_header(block_hash: Option) -> C::Header; + #[rpc(method = "chain_getBlock", positional_params)] + fn chain_get_block(block_hash: Option) -> C::SignedBlock; + #[rpc(method = "chain_getBlockHash", positional_params)] + fn chain_get_block_hash(block_number: Option) -> C::Hash; + #[rpc(method = "system_accountNextIndex", positional_params)] + fn system_account_next_index(account_id: C::AccountId) -> C::Index; + #[rpc(method = "author_submitExtrinsic", positional_params)] + fn author_submit_extrinsic(extrinsic: Bytes) -> C::Hash; + #[rpc(method = "state_call", positional_params)] + fn state_call(method: String, data: Bytes, at_block: Option) -> Bytes; + } +}