Extract minimal Rialto-Sub and Millau-Sub clients (#365)

* extracting sub clients

* fmt + lost docs

* revert enum BridgeInstance

* apply suggestions from review

* explicite debug impl

* remove unused imports from Millau

* fix typo

* fix instance + API name

* Update relays/ethereum/src/ethereum_sync_loop.rs

Co-authored-by: Hernando Castano <HCastano@users.noreply.github.com>

* separate crates for millau and rialto client

* cargo fmt

* fix

* fmt

* remove no_std support

* fix compilation again

* Update relays/substrate-client/Cargo.toml

* Update relay clients to Substrate 2.0

Co-authored-by: Hernando Castano <HCastano@users.noreply.github.com>
Co-authored-by: Hernando Castano <castano.ha@gmail.com>
This commit is contained in:
Svyatoslav Nikolsky
2020-09-28 23:49:15 +03:00
committed by Bastian Köcher
parent 7f7d62d813
commit 52c1913fff
26 changed files with 1042 additions and 751 deletions
+1 -1
View File
@@ -522,7 +522,7 @@ impl_runtime_apis! {
} }
} }
impl bp_eth_poa::RialtoHeaderApi<Block> for Runtime { impl bp_eth_poa::RialtoPoAHeaderApi<Block> for Runtime {
fn best_block() -> (u64, bp_eth_poa::H256) { fn best_block() -> (u64, bp_eth_poa::H256) {
let best_block = BridgeRialto::best_block(); let best_block = BridgeRialto::best_block();
(best_block.number, best_block.hash) (best_block.number, best_block.hash)
+1 -1
View File
@@ -549,7 +549,7 @@ pub fn step_validator<T>(header_validators: &[T], header_step: u64) -> &T {
sp_api::decl_runtime_apis! { sp_api::decl_runtime_apis! {
/// API for querying information about headers from the Rialto Bridge Pallet /// 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. /// Returns number and hash of the best block known to the bridge module.
/// ///
/// The caller should only submit an `import_header` transaction that makes /// The caller should only submit an `import_header` transaction that makes
+3 -10
View File
@@ -27,17 +27,14 @@ messages-relay = { path = "../messages-relay" }
num-traits = "0.2" num-traits = "0.2"
parity-crypto = { version = "0.6", features = ["publickey"] } parity-crypto = { version = "0.6", features = ["publickey"] }
relay-ethereum-client = { path = "../ethereum-client" } 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" } relay-utils = { path = "../utils" }
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0.57" serde_json = "1.0.57"
time = "0.2" 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 # Substrate Based Dependencies
[dependencies.frame-system] [dependencies.frame-system]
version = "2.0.0" version = "2.0.0"
@@ -73,7 +70,3 @@ git = "https://github.com/paritytech/substrate.git"
version = "0.8.0" version = "0.8.0"
tag = 'v2.0.0' tag = 'v2.0.0'
git = "https://github.com/paritytech/substrate.git" git = "https://github.com/paritytech/substrate.git"
[dependencies.rialto-runtime]
version = "0.1.0"
path = "../../bin/rialto-runtime"
-2
View File
@@ -85,7 +85,6 @@ subcommands:
help: Hex-encoded secret to use when transactions are submitted to the Ethereum node. help: Hex-encoded secret to use when transactions are submitted to the Ethereum node.
- sub-host: *sub-host - sub-host: *sub-host
- sub-port: *sub-port - sub-port: *sub-port
- sub-pallet-instance: *sub-pallet-instance
- no-prometheus: *no-prometheus - no-prometheus: *no-prometheus
- prometheus-host: *prometheus-host - prometheus-host: *prometheus-host
- prometheus-port: *prometheus-port - prometheus-port: *prometheus-port
@@ -103,7 +102,6 @@ subcommands:
takes_value: true takes_value: true
- sub-host: *sub-host - sub-host: *sub-host
- sub-port: *sub-port - sub-port: *sub-port
- sub-pallet-instance: *sub-pallet-instance
- sub-authorities-set-id: - sub-authorities-set-id:
long: sub-authorities-set-id long: sub-authorities-set-id
value_name: SUB_AUTHORITIES_SET_ID value_name: SUB_AUTHORITIES_SET_ID
+42 -41
View File
@@ -15,7 +15,7 @@
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>. // along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
use crate::rpc_errors::RpcError; 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 async_trait::async_trait;
use codec::{Decode, Encode}; use codec::{Decode, Encode};
@@ -26,7 +26,9 @@ use relay_ethereum_client::{
types::{Address, CallRequest, HeaderId as EthereumHeaderId, Receipt, H256, U256}, types::{Address, CallRequest, HeaderId as EthereumHeaderId, Receipt, H256, U256},
Client as EthereumClient, Error as EthereumNodeError, SigningParams as EthereumSigningParams, Client as EthereumClient, Error as EthereumNodeError, SigningParams as EthereumSigningParams,
}; };
use relay_rialto_client::HeaderId as RialtoHeaderId;
use relay_utils::{HeaderId, MaybeConnectionError}; use relay_utils::{HeaderId, MaybeConnectionError};
use sp_runtime::Justification;
use std::collections::HashSet; use std::collections::HashSet;
// to encode/decode contract calls // to encode/decode contract calls
@@ -39,34 +41,34 @@ type RpcResult<T> = std::result::Result<T, RpcError>;
#[async_trait] #[async_trait]
pub trait EthereumHighLevelRpc { pub trait EthereumHighLevelRpc {
/// Returns best Substrate block that PoA chain knows of. /// Returns best Substrate block that PoA chain knows of.
async fn best_substrate_block(&self, contract_address: Address) -> RpcResult<SubstrateHeaderId>; async fn best_substrate_block(&self, contract_address: Address) -> RpcResult<RialtoHeaderId>;
/// Returns true if Substrate header is known to Ethereum node. /// Returns true if Substrate header is known to Ethereum node.
async fn substrate_header_known( async fn substrate_header_known(
&self, &self,
contract_address: Address, contract_address: Address,
id: SubstrateHeaderId, id: RialtoHeaderId,
) -> RpcResult<(SubstrateHeaderId, bool)>; ) -> RpcResult<(RialtoHeaderId, bool)>;
/// Submits Substrate headers to Ethereum contract. /// Submits Substrate headers to Ethereum contract.
async fn submit_substrate_headers( async fn submit_substrate_headers(
&self, &self,
params: EthereumSigningParams, params: EthereumSigningParams,
contract_address: Address, contract_address: Address,
headers: Vec<QueuedSubstrateHeader>, headers: Vec<QueuedRialtoHeader>,
) -> SubmittedHeaders<SubstrateHeaderId, RpcError>; ) -> SubmittedHeaders<RialtoHeaderId, RpcError>;
/// Returns ids of incomplete Substrate headers. /// Returns ids of incomplete Substrate headers.
async fn incomplete_substrate_headers(&self, contract_address: Address) -> RpcResult<HashSet<SubstrateHeaderId>>; async fn incomplete_substrate_headers(&self, contract_address: Address) -> RpcResult<HashSet<RialtoHeaderId>>;
/// Complete Substrate header. /// Complete Substrate header.
async fn complete_substrate_header( async fn complete_substrate_header(
&self, &self,
params: EthereumSigningParams, params: EthereumSigningParams,
contract_address: Address, contract_address: Address,
id: SubstrateHeaderId, id: RialtoHeaderId,
justification: GrandpaJustification, justification: Justification,
) -> RpcResult<SubstrateHeaderId>; ) -> RpcResult<RialtoHeaderId>;
/// Submit ethereum transaction. /// Submit ethereum transaction.
async fn submit_ethereum_transaction( async fn submit_ethereum_transaction(
@@ -88,7 +90,7 @@ pub trait EthereumHighLevelRpc {
#[async_trait] #[async_trait]
impl EthereumHighLevelRpc for EthereumClient { impl EthereumHighLevelRpc for EthereumClient {
async fn best_substrate_block(&self, contract_address: Address) -> RpcResult<SubstrateHeaderId> { async fn best_substrate_block(&self, contract_address: Address) -> RpcResult<RialtoHeaderId> {
let (encoded_call, call_decoder) = bridge_contract::functions::best_known_header::call(); let (encoded_call, call_decoder) = bridge_contract::functions::best_known_header::call();
let call_request = CallRequest { let call_request = CallRequest {
to: Some(contract_address), to: Some(contract_address),
@@ -98,7 +100,7 @@ impl EthereumHighLevelRpc for EthereumClient {
let call_result = self.eth_call(call_request).await?; let call_result = self.eth_call(call_request).await?;
let (number, raw_hash) = call_decoder.decode(&call_result.0)?; 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() { if number != number.low_u32().into() {
return Err(RpcError::Ethereum(EthereumNodeError::InvalidSubstrateBlockNumber)); return Err(RpcError::Ethereum(EthereumNodeError::InvalidSubstrateBlockNumber));
@@ -110,8 +112,8 @@ impl EthereumHighLevelRpc for EthereumClient {
async fn substrate_header_known( async fn substrate_header_known(
&self, &self,
contract_address: Address, contract_address: Address,
id: SubstrateHeaderId, id: RialtoHeaderId,
) -> RpcResult<(SubstrateHeaderId, bool)> { ) -> RpcResult<(RialtoHeaderId, bool)> {
let (encoded_call, call_decoder) = bridge_contract::functions::is_known_header::call(id.1); let (encoded_call, call_decoder) = bridge_contract::functions::is_known_header::call(id.1);
let call_request = CallRequest { let call_request = CallRequest {
to: Some(contract_address), to: Some(contract_address),
@@ -129,8 +131,8 @@ impl EthereumHighLevelRpc for EthereumClient {
&self, &self,
params: EthereumSigningParams, params: EthereumSigningParams,
contract_address: Address, contract_address: Address,
headers: Vec<QueuedSubstrateHeader>, headers: Vec<QueuedRialtoHeader>,
) -> SubmittedHeaders<SubstrateHeaderId, RpcError> { ) -> SubmittedHeaders<RialtoHeaderId, RpcError> {
// read nonce of signer // read nonce of signer
let address: Address = params.signer.address().as_fixed_bytes().into(); let address: Address = params.signer.address().as_fixed_bytes().into();
let nonce = match self.account_nonce(address).await { let nonce = match self.account_nonce(address).await {
@@ -159,7 +161,7 @@ impl EthereumHighLevelRpc for EthereumClient {
.await .await
} }
async fn incomplete_substrate_headers(&self, contract_address: Address) -> RpcResult<HashSet<SubstrateHeaderId>> { async fn incomplete_substrate_headers(&self, contract_address: Address) -> RpcResult<HashSet<RialtoHeaderId>> {
let (encoded_call, call_decoder) = bridge_contract::functions::incomplete_headers::call(); let (encoded_call, call_decoder) = bridge_contract::functions::incomplete_headers::call();
let call_request = CallRequest { let call_request = CallRequest {
to: Some(contract_address), to: Some(contract_address),
@@ -190,9 +192,9 @@ impl EthereumHighLevelRpc for EthereumClient {
&self, &self,
params: EthereumSigningParams, params: EthereumSigningParams,
contract_address: Address, contract_address: Address,
id: SubstrateHeaderId, id: RialtoHeaderId,
justification: GrandpaJustification, justification: Justification,
) -> RpcResult<SubstrateHeaderId> { ) -> RpcResult<RialtoHeaderId> {
let _ = self let _ = self
.submit_ethereum_transaction( .submit_ethereum_transaction(
&params, &params,
@@ -243,21 +245,21 @@ pub const HEADERS_BATCH: usize = 4;
#[derive(Debug)] #[derive(Debug)]
#[cfg_attr(test, derive(Clone))] #[cfg_attr(test, derive(Clone))]
pub struct HeadersBatch { pub struct HeadersBatch {
pub header1: QueuedSubstrateHeader, pub header1: QueuedRialtoHeader,
pub header2: Option<QueuedSubstrateHeader>, pub header2: Option<QueuedRialtoHeader>,
pub header3: Option<QueuedSubstrateHeader>, pub header3: Option<QueuedRialtoHeader>,
pub header4: Option<QueuedSubstrateHeader>, pub header4: Option<QueuedRialtoHeader>,
} }
impl HeadersBatch { impl HeadersBatch {
/// Create new headers from given header & ids collections. /// Create new headers from given header & ids collections.
/// ///
/// This method will pop `HEADERS_BATCH` items from both 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( pub fn pop_from(
headers: &mut Vec<QueuedSubstrateHeader>, headers: &mut Vec<QueuedRialtoHeader>,
ids: &mut Vec<SubstrateHeaderId>, ids: &mut Vec<RialtoHeaderId>,
) -> Result<(Self, Vec<SubstrateHeaderId>), ()> { ) -> Result<(Self, Vec<RialtoHeaderId>), ()> {
if headers.len() != ids.len() { if headers.len() != ids.len() {
log::error!(target: "bridge", "Collection size mismatch ({} vs {})", headers.len(), ids.len()); log::error!(target: "bridge", "Collection size mismatch ({} vs {})", headers.len(), ids.len());
return Err(()); return Err(());
@@ -287,7 +289,7 @@ impl HeadersBatch {
/// Returns unified array of headers. /// Returns unified array of headers.
/// ///
/// The first element is always `Some`. /// The first element is always `Some`.
fn headers(&self) -> [Option<&QueuedSubstrateHeader>; HEADERS_BATCH] { fn headers(&self) -> [Option<&QueuedRialtoHeader>; HEADERS_BATCH] {
[ [
Some(&self.header1), Some(&self.header1),
self.header2.as_ref(), self.header2.as_ref(),
@@ -298,7 +300,7 @@ impl HeadersBatch {
/// Encodes all headers. If header is not present an empty vector will be returned. /// Encodes all headers. If header is not present an empty vector will be returned.
pub fn encode(&self) -> [Vec<u8>; HEADERS_BATCH] { pub fn encode(&self) -> [Vec<u8>; HEADERS_BATCH] {
let encode = |h: &QueuedSubstrateHeader| h.header().encode(); let encode = |h: &QueuedRialtoHeader| h.header().encode();
let headers = self.headers(); let headers = self.headers();
[ [
headers[0].map(encode).unwrap_or_default(), headers[0].map(encode).unwrap_or_default(),
@@ -309,7 +311,7 @@ impl HeadersBatch {
} }
/// Returns number of contained headers. /// Returns number of contained headers.
pub fn len(&self) -> usize { 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() self.headers().iter().map(is_set).sum()
} }
@@ -396,8 +398,8 @@ impl HeadersSubmitter for EthereumHeadersSubmitter {
/// Submit multiple Substrate headers. /// Submit multiple Substrate headers.
async fn submit_substrate_headers( async fn submit_substrate_headers(
mut header_submitter: impl HeadersSubmitter, mut header_submitter: impl HeadersSubmitter,
mut headers: Vec<QueuedSubstrateHeader>, mut headers: Vec<QueuedRialtoHeader>,
) -> SubmittedHeaders<SubstrateHeaderId, RpcError> { ) -> SubmittedHeaders<RialtoHeaderId, RpcError> {
let mut submitted_headers = SubmittedHeaders::default(); let mut submitted_headers = SubmittedHeaders::default();
let mut ids = headers.iter().map(|header| header.id()).rev().collect::<Vec<_>>(); let mut ids = headers.iter().map(|header| header.id()).rev().collect::<Vec<_>>();
@@ -424,8 +426,8 @@ async fn submit_substrate_headers(
/// Submit 4 Substrate headers in single PoA transaction. /// Submit 4 Substrate headers in single PoA transaction.
async fn submit_substrate_headers_batch( async fn submit_substrate_headers_batch(
header_submitter: &mut impl HeadersSubmitter, header_submitter: &mut impl HeadersSubmitter,
submitted_headers: &mut SubmittedHeaders<SubstrateHeaderId, RpcError>, submitted_headers: &mut SubmittedHeaders<RialtoHeaderId, RpcError>,
mut ids: Vec<SubstrateHeaderId>, mut ids: Vec<RialtoHeaderId>,
mut headers: HeadersBatch, mut headers: HeadersBatch,
) -> Option<RpcError> { ) -> Option<RpcError> {
debug_assert_eq!(ids.len(), headers.len(),); debug_assert_eq!(ids.len(), headers.len(),);
@@ -486,12 +488,11 @@ async fn submit_substrate_headers_batch(
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::substrate_types::{Header as SubstrateHeader, Number as SubstrateBlockNumber};
use sp_runtime::traits::Header; use sp_runtime::traits::Header;
struct TestHeadersSubmitter { struct TestHeadersSubmitter {
incomplete: Vec<SubstrateHeaderId>, incomplete: Vec<RialtoHeaderId>,
failed: Vec<SubstrateHeaderId>, failed: Vec<RialtoHeaderId>,
} }
#[async_trait] #[async_trait]
@@ -513,9 +514,9 @@ mod tests {
} }
} }
fn header(number: SubstrateBlockNumber) -> QueuedSubstrateHeader { fn header(number: rialto_runtime::BlockNumber) -> QueuedRialtoHeader {
QueuedSubstrateHeader::new( QueuedRialtoHeader::new(
SubstrateHeader::new( rialto_runtime::Header::new(
number, number,
Default::default(), Default::default(),
Default::default(), Default::default(),
@@ -15,16 +15,17 @@
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>. // along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
use crate::ethereum_client::{bridge_contract, EthereumHighLevelRpc}; use crate::ethereum_client::{bridge_contract, EthereumHighLevelRpc};
use crate::instances::BridgeInstance; use crate::rpc_errors::RpcError;
use crate::rpc::SubstrateRpc;
use crate::substrate_client::{SubstrateConnectionParams, SubstrateRpcClient};
use crate::substrate_types::{Hash as SubstrateHash, Header as SubstrateHeader, SubstrateHeaderId};
use codec::{Decode, Encode}; use codec::{Decode, Encode};
use num_traits::Zero; use num_traits::Zero;
use relay_ethereum_client::{ use relay_ethereum_client::{
Client as EthereumClient, ConnectionParams as EthereumConnectionParams, SigningParams as EthereumSigningParams, 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; use relay_utils::HeaderId;
/// Ethereum synchronization parameters. /// Ethereum synchronization parameters.
@@ -44,8 +45,6 @@ pub struct EthereumDeployContractParams {
pub sub_initial_authorities_set: Option<Vec<u8>>, pub sub_initial_authorities_set: Option<Vec<u8>>,
/// Initial header. /// Initial header.
pub sub_initial_header: Option<Vec<u8>>, pub sub_initial_header: Option<Vec<u8>>,
/// Instance of the bridge pallet being synchronized.
pub instance: Box<dyn BridgeInstance>,
} }
/// Deploy Bridge contract on Ethereum chain. /// Deploy Bridge contract on Ethereum chain.
@@ -56,7 +55,6 @@ pub fn run(params: EthereumDeployContractParams) {
eth_params, eth_params,
eth_sign, eth_sign,
sub_params, sub_params,
instance,
sub_initial_authorities_set_id, sub_initial_authorities_set_id,
sub_initial_authorities_set, sub_initial_authorities_set,
sub_initial_header, sub_initial_header,
@@ -65,7 +63,7 @@ pub fn run(params: EthereumDeployContractParams) {
let result = local_pool.run_until(async move { let result = local_pool.run_until(async move {
let eth_client = EthereumClient::new(eth_params); let eth_client = EthereumClient::new(eth_params);
let sub_client = SubstrateRpcClient::new(sub_params, instance).await?; let sub_client = SubstrateClient::<Rialto>::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_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); let initial_set_id = sub_initial_authorities_set_id.unwrap_or(0);
@@ -102,11 +100,11 @@ pub fn run(params: EthereumDeployContractParams) {
/// Prepare initial header. /// Prepare initial header.
async fn prepare_initial_header( async fn prepare_initial_header(
sub_client: &SubstrateRpcClient, sub_client: &SubstrateClient<Rialto>,
sub_initial_header: Option<Vec<u8>>, sub_initial_header: Option<Vec<u8>>,
) -> Result<(SubstrateHeaderId, Vec<u8>), String> { ) -> Result<(RialtoHeaderId, Vec<u8>), String> {
match sub_initial_header { 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(( Ok(initial_header) => Ok((
HeaderId(initial_header.number, initial_header.hash()), HeaderId(initial_header.number, initial_header.hash()),
raw_initial_header, raw_initial_header,
@@ -124,10 +122,10 @@ async fn prepare_initial_header(
/// Prepare initial GRANDPA authorities set. /// Prepare initial GRANDPA authorities set.
async fn prepare_initial_authorities_set( async fn prepare_initial_authorities_set(
sub_client: &SubstrateRpcClient, sub_client: &SubstrateClient<Rialto>,
sub_initial_header_hash: SubstrateHash, sub_initial_header_hash: rialto_runtime::Hash,
sub_initial_authorities_set: Option<Vec<u8>>, sub_initial_authorities_set: Option<Vec<u8>>,
) -> Result<Vec<u8>, String> { ) -> Result<OpaqueGrandpaAuthoritiesSet, String> {
let initial_authorities_set = match sub_initial_authorities_set { let initial_authorities_set = match sub_initial_authorities_set {
Some(initial_authorities_set) => Ok(initial_authorities_set), Some(initial_authorities_set) => Ok(initial_authorities_set),
None => sub_client.grandpa_authorities_set(sub_initial_header_hash).await, None => sub_client.grandpa_authorities_set(sub_initial_header_hash).await,
@@ -17,11 +17,8 @@
//! Relaying proofs of PoA -> Substrate exchange transactions. //! Relaying proofs of PoA -> Substrate exchange transactions.
use crate::instances::BridgeInstance; use crate::instances::BridgeInstance;
use crate::rpc::SubstrateRpc; use crate::rialto_client::{SubmitEthereumExchangeTransactionProof, SubstrateHighLevelRpc};
use crate::rpc_errors::RpcError; use crate::rpc_errors::RpcError;
use crate::substrate_client::{
SubmitEthereumExchangeTransactionProof, SubstrateConnectionParams, SubstrateRpcClient, SubstrateSigningParams,
};
use crate::substrate_types::into_substrate_ethereum_receipt; use crate::substrate_types::into_substrate_ethereum_receipt;
use async_trait::async_trait; use async_trait::async_trait;
@@ -38,9 +35,11 @@ use relay_ethereum_client::{
}, },
Client as EthereumClient, ConnectionParams as EthereumConnectionParams, 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 relay_utils::{metrics::MetricsParams, HeaderId};
use rialto_runtime::exchange::EthereumTransactionInclusionProof; use rialto_runtime::exchange::EthereumTransactionInclusionProof;
use std::time::Duration; use std::{sync::Arc, time::Duration};
/// Interval at which we ask Ethereum node for updates. /// Interval at which we ask Ethereum node for updates.
const ETHEREUM_TICK_INTERVAL: Duration = Duration::from_secs(10); const ETHEREUM_TICK_INTERVAL: Duration = Duration::from_secs(10);
@@ -64,13 +63,13 @@ pub struct EthereumExchangeParams {
/// Substrate connection params. /// Substrate connection params.
pub sub_params: SubstrateConnectionParams, pub sub_params: SubstrateConnectionParams,
/// Substrate signing params. /// Substrate signing params.
pub sub_sign: SubstrateSigningParams, pub sub_sign: RialtoSigningParams,
/// Relay working mode. /// Relay working mode.
pub mode: ExchangeRelayMode, pub mode: ExchangeRelayMode,
/// Metrics parameters. /// Metrics parameters.
pub metrics_params: Option<MetricsParams>, pub metrics_params: Option<MetricsParams>,
/// Instance of the bridge pallet being synchronized. /// Instance of the bridge pallet being synchronized.
pub instance: Box<dyn BridgeInstance>, pub instance: Arc<dyn BridgeInstance>,
} }
/// Ethereum to Substrate exchange pipeline. /// Ethereum to Substrate exchange pipeline.
@@ -201,8 +200,9 @@ impl SourceClient<EthereumToSubstrateExchange> for EthereumTransactionsSource {
/// Substrate node as transactions proof target. /// Substrate node as transactions proof target.
struct SubstrateTransactionsTarget { struct SubstrateTransactionsTarget {
client: SubstrateRpcClient, client: SubstrateClient<Rialto>,
sign_params: SubstrateSigningParams, sign_params: RialtoSigningParams,
bridge_instance: Arc<dyn BridgeInstance>,
} }
#[async_trait] #[async_trait]
@@ -254,8 +254,10 @@ impl TargetClient<EthereumToSubstrateExchange> for SubstrateTransactionsTarget {
} }
async fn submit_transaction_proof(&self, proof: EthereumTransactionInclusionProof) -> Result<(), Self::Error> { async fn submit_transaction_proof(&self, proof: EthereumTransactionInclusionProof) -> Result<(), Self::Error> {
let sign_params = self.sign_params.clone(); let (sign_params, bridge_instance) = (self.sign_params.clone(), self.bridge_instance.clone());
self.client.submit_exchange_transaction_proof(sign_params, proof).await 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 result = local_pool.run_until(async move {
let eth_client = EthereumClient::new(eth_params); let eth_client = EthereumClient::new(eth_params);
let sub_client = SubstrateRpcClient::new(sub_params, instance).await?; let sub_client = SubstrateClient::<Rialto>::new(sub_params)
.await
.map_err(RpcError::Substrate)?;
let source = EthereumTransactionsSource { client: eth_client }; let source = EthereumTransactionsSource { client: eth_client };
let target = SubstrateTransactionsTarget { let target = SubstrateTransactionsTarget {
client: sub_client, client: sub_client,
sign_params: sub_sign, sign_params: sub_sign,
bridge_instance: instance,
}; };
relay_single_transaction_proof(&source, &target, eth_tx_hash).await 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 do_run_loop = move || -> Result<(), String> {
let eth_client = EthereumClient::new(eth_params); 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::<Rialto>::new(sub_params))
.map_err(|err| format!("Error starting Substrate client: {:?}", err))?; .map_err(|err| format!("Error starting Substrate client: {:?}", err))?;
let eth_start_with_block_number = match eth_start_with_block_number { 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 { SubstrateTransactionsTarget {
client: sub_client, client: sub_client,
sign_params: sub_sign, sign_params: sub_sign,
bridge_instance: instance,
}, },
metrics_params, metrics_params,
futures::future::pending(), futures::future::pending(),
@@ -14,15 +14,12 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>. // along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
//! Ethereum PoA -> Substrate synchronization. //! Ethereum PoA -> Rialto-Substrate synchronization.
use crate::ethereum_client::EthereumHighLevelRpc; use crate::ethereum_client::EthereumHighLevelRpc;
use crate::instances::BridgeInstance; use crate::instances::BridgeInstance;
use crate::rpc::SubstrateRpc; use crate::rialto_client::{SubmitEthereumHeaders, SubstrateHighLevelRpc};
use crate::rpc_errors::RpcError; 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 crate::substrate_types::{into_substrate_ethereum_header, into_substrate_ethereum_receipts};
use async_trait::async_trait; use async_trait::async_trait;
@@ -36,10 +33,12 @@ use relay_ethereum_client::{
types::{HeaderHash, HeaderId as EthereumHeaderId, Receipt, SyncHeader as Header}, types::{HeaderHash, HeaderId as EthereumHeaderId, Receipt, SyncHeader as Header},
Client as EthereumClient, ConnectionParams as EthereumConnectionParams, 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 relay_utils::metrics::MetricsParams;
use std::fmt::Debug; use std::fmt::Debug;
use std::{collections::HashSet, time::Duration}; use std::{collections::HashSet, sync::Arc, time::Duration};
pub mod consts { pub mod consts {
use super::*; use super::*;
@@ -70,13 +69,13 @@ pub struct EthereumSyncParams {
/// Substrate connection params. /// Substrate connection params.
pub sub_params: SubstrateConnectionParams, pub sub_params: SubstrateConnectionParams,
/// Substrate signing params. /// Substrate signing params.
pub sub_sign: SubstrateSigningParams, pub sub_sign: RialtoSigningParams,
/// Synchronization parameters. /// Synchronization parameters.
pub sync_params: HeadersSyncParams, pub sync_params: HeadersSyncParams,
/// Metrics parameters. /// Metrics parameters.
pub metrics_params: Option<MetricsParams>, pub metrics_params: Option<MetricsParams>,
/// Instance of the bridge pallet being synchronized. /// Instance of the bridge pallet being synchronized.
pub instance: Box<dyn BridgeInstance>, pub instance: Arc<dyn BridgeInstance>,
} }
/// Ethereum synchronization pipeline. /// Ethereum synchronization pipeline.
@@ -158,19 +157,27 @@ impl SourceClient<EthereumHeadersSyncPipeline> for EthereumHeadersSource {
struct SubstrateHeadersTarget { struct SubstrateHeadersTarget {
/// Substrate node client. /// Substrate node client.
client: SubstrateRpcClient, client: SubstrateClient<Rialto>,
/// Whether we want to submit signed (true), or unsigned (false) transactions. /// Whether we want to submit signed (true), or unsigned (false) transactions.
sign_transactions: bool, sign_transactions: bool,
/// Substrate signing params. /// Substrate signing params.
sign_params: SubstrateSigningParams, sign_params: RialtoSigningParams,
/// Bridge instance used in Ethereum to Substrate sync.
bridge_instance: Arc<dyn BridgeInstance>,
} }
impl SubstrateHeadersTarget { impl SubstrateHeadersTarget {
fn new(client: SubstrateRpcClient, sign_transactions: bool, sign_params: SubstrateSigningParams) -> Self { fn new(
client: SubstrateClient<Rialto>,
sign_transactions: bool,
sign_params: RialtoSigningParams,
bridge_instance: Arc<dyn BridgeInstance>,
) -> Self {
Self { Self {
client, client,
sign_transactions, sign_transactions,
sign_params, sign_params,
bridge_instance,
} }
} }
} }
@@ -191,9 +198,13 @@ impl TargetClient<EthereumHeadersSyncPipeline> for SubstrateHeadersTarget {
&self, &self,
headers: Vec<QueuedEthereumHeader>, headers: Vec<QueuedEthereumHeader>,
) -> SubmittedHeaders<EthereumHeaderId, Self::Error> { ) -> SubmittedHeaders<EthereumHeaderId, Self::Error> {
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 self.client
.submit_ethereum_headers(sign_params, headers, sign_transactions) .submit_ethereum_headers(sign_params, bridge_instance, headers, sign_transactions)
.await .await
} }
@@ -228,7 +239,7 @@ pub fn run(params: EthereumSyncParams) -> Result<(), RpcError> {
} = params; } = params;
let eth_client = EthereumClient::new(eth_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::<Rialto>::new(sub_params).await })?;
let sign_sub_transactions = match sync_params.target_tx_mode { let sign_sub_transactions = match sync_params.target_tx_mode {
TargetTransactionMode::Signed | TargetTransactionMode::Backup => true, TargetTransactionMode::Signed | TargetTransactionMode::Backup => true,
@@ -236,7 +247,7 @@ pub fn run(params: EthereumSyncParams) -> Result<(), RpcError> {
}; };
let source = EthereumHeadersSource::new(eth_client); 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( headers_relay::sync_loop::run(
source, source,
+2 -2
View File
@@ -44,9 +44,9 @@ pub trait BridgeInstance: Send + Sync + std::fmt::Debug {
/// Corresponds to the Rialto instance used in the bridge runtime. /// Corresponds to the Rialto instance used in the bridge runtime.
#[derive(Default, Clone, Debug)] #[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<QueuedEthereumHeader>) -> Call { fn build_signed_header_call(&self, headers: Vec<QueuedEthereumHeader>) -> Call {
let pallet_call = rialto_runtime::BridgeEthPoACall::import_signed_headers( let pallet_call = rialto_runtime::BridgeEthPoACall::import_signed_headers(
headers headers
+14 -16
View File
@@ -22,9 +22,8 @@ mod ethereum_exchange;
mod ethereum_exchange_submit; mod ethereum_exchange_submit;
mod ethereum_sync_loop; mod ethereum_sync_loop;
mod instances; mod instances;
mod rpc; mod rialto_client;
mod rpc_errors; mod rpc_errors;
mod substrate_client;
mod substrate_sync_loop; mod substrate_sync_loop;
mod substrate_types; mod substrate_types;
@@ -34,16 +33,17 @@ use ethereum_exchange_submit::EthereumExchangeSubmitParams;
use ethereum_sync_loop::EthereumSyncParams; use ethereum_sync_loop::EthereumSyncParams;
use headers_relay::sync::TargetTransactionMode; use headers_relay::sync::TargetTransactionMode;
use hex_literal::hex; use hex_literal::hex;
use instances::{BridgeInstance, Kovan, Rialto}; use instances::{BridgeInstance, Kovan, RialtoPoA};
use parity_crypto::publickey::{KeyPair, Secret}; use parity_crypto::publickey::{KeyPair, Secret};
use relay_utils::metrics::MetricsParams; use relay_utils::metrics::MetricsParams;
use sp_core::crypto::Pair; use sp_core::crypto::Pair;
use substrate_client::{SubstrateConnectionParams, SubstrateSigningParams};
use substrate_sync_loop::SubstrateSyncParams; use substrate_sync_loop::SubstrateSyncParams;
use headers_relay::sync::HeadersSyncParams; use headers_relay::sync::HeadersSyncParams;
use relay_ethereum_client::{ConnectionParams as EthereumConnectionParams, SigningParams as EthereumSigningParams}; 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() { fn main() {
initialize(); initialize();
@@ -197,8 +197,8 @@ fn substrate_connection_params(matches: &clap::ArgMatches) -> Result<SubstrateCo
Ok(params) Ok(params)
} }
fn substrate_signing_params(matches: &clap::ArgMatches) -> Result<SubstrateSigningParams, String> { fn rialto_signing_params(matches: &clap::ArgMatches) -> Result<RialtoSigningParams, String> {
let mut params = SubstrateSigningParams::default(); let mut params = RialtoSigningParams::default();
if let Some(sub_signer) = matches.value_of("sub-signer") { if let Some(sub_signer) = matches.value_of("sub-signer") {
let sub_signer_password = matches.value_of("sub-signer-password"); let sub_signer_password = matches.value_of("sub-signer-password");
params.signer = sp_core::sr25519::Pair::from_string(sub_signer, 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<EthereumSyncParams
let params = EthereumSyncParams { let params = EthereumSyncParams {
eth_params: ethereum_connection_params(matches)?, eth_params: ethereum_connection_params(matches)?,
sub_params: substrate_connection_params(matches)?, sub_params: substrate_connection_params(matches)?,
sub_sign: substrate_signing_params(matches)?, sub_sign: rialto_signing_params(matches)?,
metrics_params: metrics_params(matches)?, metrics_params: metrics_params(matches)?,
instance: instance_params(matches)?, instance: instance_params(matches)?,
sync_params, sync_params,
@@ -263,11 +263,10 @@ fn substrate_sync_params(matches: &clap::ArgMatches) -> Result<SubstrateSyncPara
eth_params: ethereum_connection_params(matches)?, eth_params: ethereum_connection_params(matches)?,
eth_sign: ethereum_signing_params(matches)?, eth_sign: ethereum_signing_params(matches)?,
metrics_params: metrics_params(matches)?, metrics_params: metrics_params(matches)?,
instance: instance_params(matches)?,
sync_params: HeadersSyncParams { sync_params: HeadersSyncParams {
max_future_headers_to_download: MAX_FUTURE_HEADERS_TO_DOWNLOAD, max_future_headers_to_download: MAX_FUTURE_HEADERS_TO_DOWNLOAD,
max_headers_in_submitted_status: MAX_SUBMITTED_HEADERS, max_headers_in_submitted_status: MAX_SUBMITTED_HEADERS,
max_headers_in_single_submit: 4, max_headers_in_single_submit: MAX_SUBMITTED_HEADERS,
max_headers_size_in_single_submit: std::usize::MAX, max_headers_size_in_single_submit: std::usize::MAX,
prune_depth: PRUNE_DEPTH, prune_depth: PRUNE_DEPTH,
target_tx_mode: TargetTransactionMode::Signed, target_tx_mode: TargetTransactionMode::Signed,
@@ -299,7 +298,6 @@ fn ethereum_deploy_contract_params(matches: &clap::ArgMatches) -> Result<Ethereu
eth_params: ethereum_connection_params(matches)?, eth_params: ethereum_connection_params(matches)?,
eth_sign: ethereum_signing_params(matches)?, eth_sign: ethereum_signing_params(matches)?,
sub_params: substrate_connection_params(matches)?, sub_params: substrate_connection_params(matches)?,
instance: instance_params(matches)?,
sub_initial_authorities_set_id, sub_initial_authorities_set_id,
sub_initial_authorities_set, sub_initial_authorities_set,
sub_initial_header, sub_initial_header,
@@ -384,7 +382,7 @@ fn ethereum_exchange_params(matches: &clap::ArgMatches) -> Result<EthereumExchan
let params = EthereumExchangeParams { let params = EthereumExchangeParams {
eth_params: ethereum_connection_params(matches)?, eth_params: ethereum_connection_params(matches)?,
sub_params: substrate_connection_params(matches)?, sub_params: substrate_connection_params(matches)?,
sub_sign: substrate_signing_params(matches)?, sub_sign: rialto_signing_params(matches)?,
metrics_params: metrics_params(matches)?, metrics_params: metrics_params(matches)?,
instance: instance_params(matches)?, instance: instance_params(matches)?,
mode, mode,
@@ -414,11 +412,11 @@ fn metrics_params(matches: &clap::ArgMatches) -> Result<Option<MetricsParams>, S
Ok(Some(metrics_params)) Ok(Some(metrics_params))
} }
fn instance_params(matches: &clap::ArgMatches) -> Result<Box<dyn BridgeInstance>, String> { fn instance_params(matches: &clap::ArgMatches) -> Result<Arc<dyn BridgeInstance>, String> {
let instance: Box<dyn BridgeInstance> = if let Some(instance) = matches.value_of("sub-pallet-instance") { let instance = if let Some(instance) = matches.value_of("sub-pallet-instance") {
match instance.to_lowercase().as_str() { match instance.to_lowercase().as_str() {
"rialto" => Box::new(Rialto::default()), "rialto" => Arc::new(RialtoPoA) as Arc<dyn BridgeInstance>,
"kovan" => Box::new(Kovan::default()), "kovan" => Arc::new(Kovan),
_ => return Err("Unsupported bridge pallet instance".to_string()), _ => return Err("Unsupported bridge pallet instance".to_string()),
} }
} else { } else {
@@ -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 <http://www.gnu.org/licenses/>.
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<T> = std::result::Result<T, RpcError>;
/// 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<EthereumHeaderId>;
/// Returns best finalized Ethereum block that Substrate runtime knows of.
async fn best_ethereum_finalized_block(&self) -> RpcResult<EthereumHeaderId>;
/// Returns whether or not transactions receipts are required for Ethereum header submission.
async fn ethereum_receipts_required(&self, header: SubstrateEthereumHeader) -> RpcResult<bool>;
/// Returns whether or not the given Ethereum header is known to the Substrate runtime.
async fn ethereum_header_known(&self, header_id: EthereumHeaderId) -> RpcResult<bool>;
}
#[async_trait]
impl SubstrateHighLevelRpc for SubstrateClient<Rialto> {
async fn best_ethereum_block(&self) -> RpcResult<EthereumHeaderId> {
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<EthereumHeaderId> {
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<bool> {
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<bool> {
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<dyn BridgeInstance>,
headers: Vec<QueuedEthereumHeader>,
sign_transactions: bool,
) -> SubmittedHeaders<EthereumHeaderId, RpcError>;
/// Submits signed Ethereum header to Substrate runtime.
async fn submit_signed_ethereum_headers(
&self,
params: RialtoSigningParams,
instance: Arc<dyn BridgeInstance>,
headers: Vec<QueuedEthereumHeader>,
) -> SubmittedHeaders<EthereumHeaderId, RpcError>;
/// Submits unsigned Ethereum header to Substrate runtime.
async fn submit_unsigned_ethereum_headers(
&self,
instance: Arc<dyn BridgeInstance>,
headers: Vec<QueuedEthereumHeader>,
) -> SubmittedHeaders<EthereumHeaderId, RpcError>;
}
#[async_trait]
impl SubmitEthereumHeaders for SubstrateClient<Rialto> {
async fn submit_ethereum_headers(
&self,
params: RialtoSigningParams,
instance: Arc<dyn BridgeInstance>,
headers: Vec<QueuedEthereumHeader>,
sign_transactions: bool,
) -> SubmittedHeaders<EthereumHeaderId, RpcError> {
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<dyn BridgeInstance>,
headers: Vec<QueuedEthereumHeader>,
) -> SubmittedHeaders<EthereumHeaderId, RpcError> {
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, &params.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<dyn BridgeInstance>,
headers: Vec<QueuedEthereumHeader>,
) -> SubmittedHeaders<EthereumHeaderId, RpcError> {
let mut ids = headers.iter().map(|header| header.id()).collect::<VecDeque<_>>();
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<bool>;
/// Submits Ethereum exchange transaction proof to Substrate runtime.
async fn submit_exchange_transaction_proof(
&self,
params: RialtoSigningParams,
instance: Arc<dyn BridgeInstance>,
proof: rialto_runtime::exchange::EthereumTransactionInclusionProof,
) -> RpcResult<()>;
}
#[async_trait]
impl SubmitEthereumExchangeTransactionProof for SubstrateClient<Rialto> {
async fn verify_exchange_transaction_proof(
&self,
proof: rialto_runtime::exchange::EthereumTransactionInclusionProof,
) -> RpcResult<bool> {
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<dyn BridgeInstance>,
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, &params.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)
}
-86
View File
@@ -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 <http://www.gnu.org/licenses/>.
//! 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<T> = result::Result<T, RpcError>;
type GrandpaAuthorityList = Vec<u8>;
jsonrpsee::rpc_api! {
pub(crate) Substrate {
#[rpc(method = "chain_getHeader", positional_params)]
fn chain_get_header(block_hash: Option<SubstrateHash>) -> SubstrateHeader;
#[rpc(method = "chain_getBlock", positional_params)]
fn chain_get_block(block_hash: Option<SubstrateHash>) -> SubstrateBlock;
#[rpc(method = "chain_getBlockHash", positional_params)]
fn chain_get_block_hash(block_number: Option<SubBlockNumber>) -> 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<SubstrateHash>) -> 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<SubstrateHeader>;
/// Get a Substrate block from its hash.
async fn get_block(&self, block_hash: Option<SubstrateHash>) -> Result<SubstrateBlock>;
/// Get a Substrate header by its hash.
async fn header_by_hash(&self, hash: SubstrateHash) -> Result<SubstrateHeader>;
/// Get a Substrate block hash by its number.
async fn block_hash_by_number(&self, number: SubBlockNumber) -> Result<SubstrateHash>;
/// Get a Substrate header by its number.
async fn header_by_number(&self, block_number: SubBlockNumber) -> Result<SubstrateHeader>;
/// 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<node_primitives::Index>;
/// Returns best Ethereum block that Substrate runtime knows of.
async fn best_ethereum_block(&self) -> Result<EthereumHeaderId>;
/// Returns best finalized Ethereum block that Substrate runtime knows of.
async fn best_ethereum_finalized_block(&self) -> Result<EthereumHeaderId>;
/// Returns whether or not transactions receipts are required for Ethereum header submission.
async fn ethereum_receipts_required(&self, header: SubstrateEthereumHeader) -> Result<bool>;
/// Returns whether or not the given Ethereum header is known to the Substrate runtime.
async fn ethereum_header_known(&self, header_id: EthereumHeaderId) -> Result<bool>;
/// 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<SubstrateHash>;
/// Get the GRANDPA authority set at given block.
async fn grandpa_authorities_set(&self, block: SubstrateHash) -> Result<GrandpaAuthorityList>;
}
+3 -29
View File
@@ -14,8 +14,8 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>. // along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
use jsonrpsee::client::RequestError;
use relay_ethereum_client::Error as EthereumNodeError; use relay_ethereum_client::Error as EthereumNodeError;
use relay_substrate_client::Error as SubstrateNodeError;
use relay_utils::MaybeConnectionError; use relay_utils::MaybeConnectionError;
/// Contains common errors that can occur when /// Contains common errors that can occur when
@@ -29,9 +29,6 @@ pub enum RpcError {
Ethereum(EthereumNodeError), Ethereum(EthereumNodeError),
/// An error occured when interacting with a Substrate node. /// An error occured when interacting with a Substrate node.
Substrate(SubstrateNodeError), Substrate(SubstrateNodeError),
/// An error that can occur when making an HTTP request to
/// an JSON-RPC client.
Request(RequestError),
} }
impl From<RpcError> for String { impl From<RpcError> for String {
@@ -40,7 +37,6 @@ impl From<RpcError> for String {
RpcError::Serialization(e) => e.to_string(), RpcError::Serialization(e) => e.to_string(),
RpcError::Ethereum(e) => e.to_string(), RpcError::Ethereum(e) => e.to_string(),
RpcError::Substrate(e) => e.to_string(), RpcError::Substrate(e) => e.to_string(),
RpcError::Request(e) => e.to_string(),
} }
} }
} }
@@ -63,12 +59,6 @@ impl From<SubstrateNodeError> for RpcError {
} }
} }
impl From<RequestError> for RpcError {
fn from(err: RequestError) -> Self {
Self::Request(err)
}
}
impl From<ethabi::Error> for RpcError { impl From<ethabi::Error> for RpcError {
fn from(err: ethabi::Error) -> Self { fn from(err: ethabi::Error) -> Self {
Self::Ethereum(EthereumNodeError::ResponseParseFailed(format!("{}", err))) Self::Ethereum(EthereumNodeError::ResponseParseFailed(format!("{}", err)))
@@ -78,8 +68,8 @@ impl From<ethabi::Error> for RpcError {
impl MaybeConnectionError for RpcError { impl MaybeConnectionError for RpcError {
fn is_connection_error(&self) -> bool { fn is_connection_error(&self) -> bool {
match self { match self {
RpcError::Request(RequestError::TransportError(_)) => true,
RpcError::Ethereum(ref error) => error.is_connection_error(), RpcError::Ethereum(ref error) => error.is_connection_error(),
RpcError::Substrate(ref error) => error.is_connection_error(),
_ => false, _ => false,
} }
} }
@@ -87,22 +77,6 @@ impl MaybeConnectionError for RpcError {
impl From<codec::Error> for RpcError { impl From<codec::Error> for RpcError {
fn from(err: codec::Error) -> Self { fn from(err: codec::Error) -> Self {
Self::Substrate(SubstrateNodeError::Decoding(err)) Self::Substrate(SubstrateNodeError::ResponseParseFailed(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(),
}
} }
} }
@@ -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 <http://www.gnu.org/licenses/>.
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<T> = std::result::Result<T, RpcError>;
type GrandpaAuthorityList = Vec<u8>;
/// 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<dyn BridgeInstance>,
}
impl SubstrateRpcClient {
/// Returns client that is able to call RPCs on Substrate node.
pub async fn new(params: SubstrateConnectionParams, instance: Box<dyn BridgeInstance>) -> RpcResult<Self> {
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<SubstrateHeader> {
Ok(Substrate::chain_get_header(&self.client, None).await?)
}
async fn get_block(&self, block_hash: Option<Hash>) -> RpcResult<SignedSubstrateBlock> {
Ok(Substrate::chain_get_block(&self.client, block_hash).await?)
}
async fn header_by_hash(&self, block_hash: Hash) -> RpcResult<SubstrateHeader> {
Ok(Substrate::chain_get_header(&self.client, block_hash).await?)
}
async fn block_hash_by_number(&self, number: Number) -> RpcResult<Hash> {
Ok(Substrate::chain_get_block_hash(&self.client, number).await?)
}
async fn header_by_number(&self, block_number: Number) -> RpcResult<SubstrateHeader> {
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<node_primitives::Index> {
Ok(Substrate::system_account_next_index(&self.client, account).await?)
}
async fn best_ethereum_block(&self) -> RpcResult<EthereumHeaderId> {
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<EthereumHeaderId> {
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<bool> {
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<bool> {
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<Hash> {
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<GrandpaAuthorityList> {
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<QueuedEthereumHeader>,
sign_transactions: bool,
) -> SubmittedHeaders<EthereumHeaderId, RpcError>;
/// Submits signed Ethereum header to Substrate runtime.
async fn submit_signed_ethereum_headers(
&self,
params: SubstrateSigningParams,
headers: Vec<QueuedEthereumHeader>,
) -> SubmittedHeaders<EthereumHeaderId, RpcError>;
/// Submits unsigned Ethereum header to Substrate runtime.
async fn submit_unsigned_ethereum_headers(
&self,
headers: Vec<QueuedEthereumHeader>,
) -> SubmittedHeaders<EthereumHeaderId, RpcError>;
}
#[async_trait]
impl SubmitEthereumHeaders for SubstrateRpcClient {
async fn submit_ethereum_headers(
&self,
params: SubstrateSigningParams,
headers: Vec<QueuedEthereumHeader>,
sign_transactions: bool,
) -> SubmittedHeaders<EthereumHeaderId, RpcError> {
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<QueuedEthereumHeader>,
) -> SubmittedHeaders<EthereumHeaderId, RpcError> {
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, &params.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<QueuedEthereumHeader>,
) -> SubmittedHeaders<EthereumHeaderId, RpcError> {
let mut ids = headers.iter().map(|header| header.id()).collect::<VecDeque<_>>();
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<bool>;
/// 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<bool> {
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, &params.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::<rialto_runtime::Runtime>::new(),
frame_system::CheckTxVersion::<rialto_runtime::Runtime>::new(),
frame_system::CheckGenesis::<rialto_runtime::Runtime>::new(),
frame_system::CheckEra::<rialto_runtime::Runtime>::from(sp_runtime::generic::Era::Immortal),
frame_system::CheckNonce::<rialto_runtime::Runtime>::from(i),
frame_system::CheckWeight::<rialto_runtime::Runtime>::new(),
pallet_transaction_payment::ChargeTransactionPayment::<rialto_runtime::Runtime>::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)
}
@@ -14,29 +14,26 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>. // along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
//! Substrate -> Ethereum synchronization. //! Rialto-Substrate -> Ethereum PoA synchronization.
use crate::ethereum_client::EthereumHighLevelRpc; use crate::ethereum_client::EthereumHighLevelRpc;
use crate::instances::BridgeInstance;
use crate::rpc::SubstrateRpc;
use crate::rpc_errors::RpcError; 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 async_trait::async_trait;
use codec::Encode;
use headers_relay::{ use headers_relay::{
sync::HeadersSyncParams, sync::HeadersSyncParams,
sync_loop::{SourceClient, TargetClient}, sync_loop::{SourceClient, TargetClient},
sync_types::{SourceHeader, SubmittedHeaders}, sync_types::{HeadersSyncPipeline, QueuedHeader, SourceHeader, SubmittedHeaders},
}; };
use relay_ethereum_client::{ use relay_ethereum_client::{
types::Address, Client as EthereumClient, ConnectionParams as EthereumConnectionParams, types::Address, Client as EthereumClient, ConnectionParams as EthereumConnectionParams,
SigningParams as EthereumSigningParams, 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 relay_utils::metrics::MetricsParams;
use sp_runtime::Justification;
use std::fmt::Debug; use std::fmt::Debug;
use std::{collections::HashSet, time::Duration}; use std::{collections::HashSet, time::Duration};
@@ -71,18 +68,39 @@ pub struct SubstrateSyncParams {
pub sync_params: HeadersSyncParams, pub sync_params: HeadersSyncParams,
/// Metrics parameters. /// Metrics parameters.
pub metrics_params: Option<MetricsParams>, pub metrics_params: Option<MetricsParams>,
/// Instance of the bridge pallet being synchronized.
pub instance: Box<dyn BridgeInstance>,
} }
/// 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<Self>) -> usize {
source.header().encode().len()
}
}
/// Queued substrate header ID.
pub type QueuedRialtoHeader = QueuedHeader<SubstrateHeadersSyncPipeline>;
/// Substrate client as headers source. /// Substrate client as headers source.
struct SubstrateHeadersSource { struct SubstrateHeadersSource {
/// Substrate node client. /// Substrate node client.
client: SubstrateRpcClient, client: SubstrateClient<Rialto>,
} }
impl SubstrateHeadersSource { impl SubstrateHeadersSource {
fn new(client: SubstrateRpcClient) -> Self { fn new(client: SubstrateClient<Rialto>) -> Self {
Self { client } Self { client }
} }
} }
@@ -91,22 +109,30 @@ impl SubstrateHeadersSource {
impl SourceClient<SubstrateHeadersSyncPipeline> for SubstrateHeadersSource { impl SourceClient<SubstrateHeadersSyncPipeline> for SubstrateHeadersSource {
type Error = RpcError; type Error = RpcError;
async fn best_block_number(&self) -> Result<Number, Self::Error> { async fn best_block_number(&self) -> Result<rialto_runtime::BlockNumber, Self::Error> {
Ok(self.client.best_header().await?.number) Ok(self.client.best_header().await?.number)
} }
async fn header_by_hash(&self, hash: Hash) -> Result<Header, Self::Error> { async fn header_by_hash(&self, hash: rialto_runtime::Hash) -> Result<RialtoSyncHeader, Self::Error> {
self.client.header_by_hash(hash).await.map(Into::into) self.client
.header_by_hash(hash)
.await
.map(Into::into)
.map_err(Into::into)
} }
async fn header_by_number(&self, number: Number) -> Result<Header, Self::Error> { async fn header_by_number(&self, number: rialto_runtime::BlockNumber) -> Result<RialtoSyncHeader, Self::Error> {
self.client.header_by_number(number).await.map(Into::into) self.client
.header_by_number(number)
.await
.map(Into::into)
.map_err(Into::into)
} }
async fn header_completion( async fn header_completion(
&self, &self,
id: SubstrateHeaderId, id: RialtoHeaderId,
) -> Result<(SubstrateHeaderId, Option<GrandpaJustification>), Self::Error> { ) -> Result<(RialtoHeaderId, Option<Justification>), Self::Error> {
let hash = id.1; let hash = id.1;
let signed_block = self.client.get_block(Some(hash)).await?; let signed_block = self.client.get_block(Some(hash)).await?;
let grandpa_justification = signed_block.justification; let grandpa_justification = signed_block.justification;
@@ -116,9 +142,9 @@ impl SourceClient<SubstrateHeadersSyncPipeline> for SubstrateHeadersSource {
async fn header_extra( async fn header_extra(
&self, &self,
id: SubstrateHeaderId, id: RialtoHeaderId,
_header: QueuedSubstrateHeader, _header: QueuedRialtoHeader,
) -> Result<(SubstrateHeaderId, ()), Self::Error> { ) -> Result<(RialtoHeaderId, ()), Self::Error> {
Ok((id, ())) Ok((id, ()))
} }
} }
@@ -147,38 +173,35 @@ impl EthereumHeadersTarget {
impl TargetClient<SubstrateHeadersSyncPipeline> for EthereumHeadersTarget { impl TargetClient<SubstrateHeadersSyncPipeline> for EthereumHeadersTarget {
type Error = RpcError; type Error = RpcError;
async fn best_header_id(&self) -> Result<SubstrateHeaderId, Self::Error> { async fn best_header_id(&self) -> Result<RialtoHeaderId, Self::Error> {
self.client.best_substrate_block(self.contract).await 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 self.client.substrate_header_known(self.contract, id).await
} }
async fn submit_headers( async fn submit_headers(&self, headers: Vec<QueuedRialtoHeader>) -> SubmittedHeaders<RialtoHeaderId, Self::Error> {
&self,
headers: Vec<QueuedSubstrateHeader>,
) -> SubmittedHeaders<SubstrateHeaderId, Self::Error> {
self.client self.client
.submit_substrate_headers(self.sign_params.clone(), self.contract, headers) .submit_substrate_headers(self.sign_params.clone(), self.contract, headers)
.await .await
} }
async fn incomplete_headers_ids(&self) -> Result<HashSet<SubstrateHeaderId>, Self::Error> { async fn incomplete_headers_ids(&self) -> Result<HashSet<RialtoHeaderId>, Self::Error> {
self.client.incomplete_substrate_headers(self.contract).await self.client.incomplete_substrate_headers(self.contract).await
} }
async fn complete_header( async fn complete_header(
&self, &self,
id: SubstrateHeaderId, id: RialtoHeaderId,
completion: GrandpaJustification, completion: Justification,
) -> Result<SubstrateHeaderId, Self::Error> { ) -> Result<RialtoHeaderId, Self::Error> {
self.client self.client
.complete_substrate_header(self.sign_params.clone(), self.contract, id, completion) .complete_substrate_header(self.sign_params.clone(), self.contract, id, completion)
.await .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)) Ok((header.header().id(), false))
} }
} }
@@ -192,11 +215,10 @@ pub fn run(params: SubstrateSyncParams) -> Result<(), RpcError> {
eth_contract_address, eth_contract_address,
sync_params, sync_params,
metrics_params, metrics_params,
instance,
} = params; } = params;
let eth_client = EthereumClient::new(eth_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::<Rialto>::new(sub_params).await })?;
let target = EthereumHeadersTarget::new(eth_client, eth_contract_address, eth_sign); let target = EthereumHeadersTarget::new(eth_client, eth_contract_address, eth_sign);
let source = SubstrateHeadersSource::new(sub_client); let source = SubstrateHeadersSource::new(sub_client);
+5 -75
View File
@@ -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. // This file is part of Parity Bridges Common.
// Parity Bridges Common is free software: you can redistribute it and/or modify // 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 // You should have received a copy of the GNU General Public License
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>. // along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
use codec::Encode; //! Converting between Ethereum headers and bridge module types.
use headers_relay::sync_types::{HeadersSyncPipeline, QueuedHeader, SourceHeader};
use relay_utils::HeaderId;
pub use bp_eth_poa::{ use bp_eth_poa::{
Address, AuraHeader as SubstrateEthereumHeader, Bloom, Bytes, LogEntry as SubstrateEthereumLogEntry, AuraHeader as SubstrateEthereumHeader, LogEntry as SubstrateEthereumLogEntry, Receipt as SubstrateEthereumReceipt,
Receipt as SubstrateEthereumReceipt, TransactionOutcome as SubstrateEthereumTransactionOutcome, H256, U256, TransactionOutcome as SubstrateEthereumTransactionOutcome,
}; };
use relay_ethereum_client::types::{ use relay_ethereum_client::types::{
Header as EthereumHeader, Receipt as EthereumReceipt, HEADER_ID_PROOF as ETHEREUM_HEADER_ID_PROOF, 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<u8>;
/// Substrate header ID.
pub type SubstrateHeaderId = HeaderId<rialto_runtime::Hash, rialto_runtime::BlockNumber>;
/// Queued substrate header ID.
pub type QueuedSubstrateHeader = QueuedHeader<SubstrateHeadersSyncPipeline>;
/// 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<Self>) -> usize {
source.header().encode().len()
}
}
impl From<Header> for SubstrateSyncHeader {
fn from(header: Header) -> Self {
Self(header)
}
}
impl SourceHeader<rialto_runtime::Hash, rialto_runtime::BlockNumber> 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. /// Convert Ethereum header into Ethereum header for Substrate.
pub fn into_substrate_ethereum_header(header: &EthereumHeader) -> SubstrateEthereumHeader { pub fn into_substrate_ethereum_header(header: &EthereumHeader) -> SubstrateEthereumHeader {
SubstrateEthereumHeader { SubstrateEthereumHeader {
+10
View File
@@ -0,0 +1,10 @@
[package]
name = "relay-millau-client"
version = "0.1.0"
authors = ["Parity Technologies <admin@parity.io>"]
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" }
+33
View File
@@ -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 <http://www.gnu.org/licenses/>.
//! 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;
}
+24
View File
@@ -0,0 +1,24 @@
[package]
name = "relay-rialto-client"
version = "0.1.0"
authors = ["Parity Technologies <admin@parity.io>"]
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" }
+132
View File
@@ -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 <http://www.gnu.org/licenses/>.
//! 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_runtime::Hash, rialto_runtime::BlockNumber>;
/// 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<Self>,
signer: &Self::AccountKeyPair,
signer_nonce: <Self::Chain as Chain>::Index,
call: <Self::Chain as Chain>::Call,
) -> Self::SignedTransaction {
let raw_payload = SignedPayload::from_raw(
call,
(
frame_system::CheckSpecVersion::<rialto_runtime::Runtime>::new(),
frame_system::CheckTxVersion::<rialto_runtime::Runtime>::new(),
frame_system::CheckGenesis::<rialto_runtime::Runtime>::new(),
frame_system::CheckEra::<rialto_runtime::Runtime>::from(sp_runtime::generic::Era::Immortal),
frame_system::CheckNonce::<rialto_runtime::Runtime>::from(signer_nonce),
frame_system::CheckWeight::<rialto_runtime::Runtime>::new(),
pallet_transaction_payment::ChargeTransactionPayment::<rialto_runtime::Runtime>::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<rialto_runtime::Header> for SyncHeader {
fn from(header: rialto_runtime::Header) -> Self {
Self(header)
}
}
impl SourceHeader<rialto_runtime::Hash, rialto_runtime::BlockNumber> 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())
}
}
@@ -0,0 +1,22 @@
[package]
name = "relay-substrate-client"
version = "0.1.0"
authors = ["Parity Technologies <admin@parity.io>"]
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" }
@@ -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 <http://www.gnu.org/licenses/>.
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<Number = Self::BlockNumber, Hash = Self::Hash>;
/// 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<Self::Chain>,
signer: &Self::AccountKeyPair,
signer_nonce: <Self::Chain as Chain>::Index,
call: <Self::Chain as Chain>::Call,
) -> Self::SignedTransaction;
}
@@ -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 <http://www.gnu.org/licenses/>.
//! 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<u8>;
/// Substrate client type.
pub struct Client<C: Chain> {
/// Substrate RPC client.
client: RpcClient,
/// Genesis block hash.
genesis_hash: C::Hash,
}
impl<C: Chain> std::fmt::Debug for Client<C> {
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
fmt.debug_struct("Client")
.field("genesis_hash", &self.genesis_hash)
.finish()
}
}
impl<C: Chain> Client<C> {
/// Returns client that is able to call RPCs on Substrate node.
pub async fn new(params: ConnectionParams) -> Result<Self> {
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::<C, _, _>::chain_get_block_hash(&client, number).await?;
Ok(Self { client, genesis_hash })
}
}
impl<C: Chain> Client<C>
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<C::Header> {
Ok(Substrate::<C, _, _>::chain_get_header(&self.client, None).await?)
}
/// Get a Substrate block from its hash.
pub async fn get_block(&self, block_hash: Option<C::Hash>) -> Result<C::SignedBlock> {
Ok(Substrate::<C, _, _>::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<C::Header> {
Ok(Substrate::<C, _, _>::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<C::Hash> {
Ok(Substrate::<C, _, _>::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<C::Header> {
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<C::Index> {
Ok(Substrate::<C, _, _>::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<C::Hash> {
let tx_hash = Substrate::<C, _, _>::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<OpaqueGrandpaAuthoritiesSet> {
let call = SUB_API_GRANDPA_AUTHORITIES.to_string();
let data = Bytes(Vec::new());
let encoded_response = Substrate::<C, _, _>::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<C::Hash>) -> Result<Bytes> {
Substrate::<C, _, _>::state_call(&self.client, method, data, at_block)
.await
.map_err(Into::into)
}
}
@@ -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 <http://www.gnu.org/licenses/>.
//! Substrate node RPC errors.
use jsonrpsee::client::RequestError;
use relay_utils::MaybeConnectionError;
/// Result type used by Substrate client.
pub type Result<T> = std::result::Result<T, Error>;
/// 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<RequestError> 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(),
}
}
}
@@ -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 <http://www.gnu.org/licenses/>.
//! 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,
}
}
}
@@ -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 <http://www.gnu.org/licenses/>.
//! 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<C: Chain> {
#[rpc(method = "chain_getHeader", positional_params)]
fn chain_get_header(block_hash: Option<C::Hash>) -> C::Header;
#[rpc(method = "chain_getBlock", positional_params)]
fn chain_get_block(block_hash: Option<C::Hash>) -> C::SignedBlock;
#[rpc(method = "chain_getBlockHash", positional_params)]
fn chain_get_block_hash(block_number: Option<C::BlockNumber>) -> 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<C::Hash>) -> Bytes;
}
}