Extract minimal ethereum client (#359)

* relay-ethereum-client

* use relay-ethereum-client from ethereum-poa-relay

* cargo fmt --all

* #![warn(missing_docs)]

* EthereumRpcClient -> EthereumClient

* make EthereumHeadersSyncPipeline private

* return concrete type from crate::new

* cleanup dependencies

* *self -> self

* remove trait Client

* sort deps
This commit is contained in:
Svyatoslav Nikolsky
2020-09-22 11:43:04 +03:00
committed by Bastian Köcher
parent d614cdaba8
commit ec34870eab
20 changed files with 556 additions and 384 deletions
+17
View File
@@ -0,0 +1,17 @@
[package]
name = "relay-ethereum-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" }
ethereum-tx-sign = "3.0"
headers-relay = { path = "../headers-relay" }
hex = "0.4"
jsonrpsee = { git = "https://github.com/svyatonik/jsonrpsee.git", branch = "shared-client-in-rpc-api", default-features = false, features = ["http"] }
log = "0.4.11"
parity-crypto = { version = "0.6", features = ["publickey"] }
relay-utils = { path = "../utils" }
web3 = "0.13"
@@ -0,0 +1,142 @@
// 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::rpc::Ethereum;
use crate::types::{
Address, Bytes, CallRequest, Header, HeaderWithTransactions, Receipt, SignedRawTx, Transaction, TransactionHash,
H256, U256,
};
use crate::{ConnectionParams, Error, Result};
use jsonrpsee::raw::RawClient;
use jsonrpsee::transport::http::HttpTransportClient;
use jsonrpsee::Client as RpcClient;
/// The client used to interact with an Ethereum node through RPC.
#[derive(Clone)]
pub struct Client {
client: RpcClient,
}
impl Client {
/// Create a new Ethereum RPC Client.
pub fn new(params: ConnectionParams) -> 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();
Self { client }
}
}
impl Client {
/// Estimate gas usage for the given call.
pub async fn estimate_gas(&self, call_request: CallRequest) -> Result<U256> {
Ok(Ethereum::estimate_gas(&self.client, call_request).await?)
}
/// Retrieve number of the best known block from the Ethereum node.
pub async fn best_block_number(&self) -> Result<u64> {
Ok(Ethereum::block_number(&self.client).await?.as_u64())
}
/// Retrieve number of the best known block from the Ethereum node.
pub async fn header_by_number(&self, block_number: u64) -> Result<Header> {
let get_full_tx_objects = false;
let header = Ethereum::get_block_by_number(&self.client, block_number, get_full_tx_objects).await?;
match header.number.is_some() && header.hash.is_some() && header.logs_bloom.is_some() {
true => Ok(header),
false => Err(Error::IncompleteHeader),
}
}
/// Retrieve block header by its hash from Ethereum node.
pub async fn header_by_hash(&self, hash: H256) -> Result<Header> {
let get_full_tx_objects = false;
let header = Ethereum::get_block_by_hash(&self.client, hash, get_full_tx_objects).await?;
match header.number.is_some() && header.hash.is_some() && header.logs_bloom.is_some() {
true => Ok(header),
false => Err(Error::IncompleteHeader),
}
}
/// Retrieve block header and its transactions by its number from Ethereum node.
pub async fn header_by_number_with_transactions(&self, number: u64) -> Result<HeaderWithTransactions> {
let get_full_tx_objects = true;
let header = Ethereum::get_block_by_number_with_transactions(&self.client, number, get_full_tx_objects).await?;
let is_complete_header = header.number.is_some() && header.hash.is_some() && header.logs_bloom.is_some();
if !is_complete_header {
return Err(Error::IncompleteHeader);
}
let is_complete_transactions = header.transactions.iter().all(|tx| tx.raw.is_some());
if !is_complete_transactions {
return Err(Error::IncompleteTransaction);
}
Ok(header)
}
/// Retrieve block header and its transactions by its hash from Ethereum node.
pub async fn header_by_hash_with_transactions(&self, hash: H256) -> Result<HeaderWithTransactions> {
let get_full_tx_objects = true;
let header = Ethereum::get_block_by_hash_with_transactions(&self.client, hash, get_full_tx_objects).await?;
let is_complete_header = header.number.is_some() && header.hash.is_some() && header.logs_bloom.is_some();
if !is_complete_header {
return Err(Error::IncompleteHeader);
}
let is_complete_transactions = header.transactions.iter().all(|tx| tx.raw.is_some());
if !is_complete_transactions {
return Err(Error::IncompleteTransaction);
}
Ok(header)
}
/// Retrieve transaction by its hash from Ethereum node.
pub async fn transaction_by_hash(&self, hash: H256) -> Result<Option<Transaction>> {
Ok(Ethereum::transaction_by_hash(&self.client, hash).await?)
}
/// Retrieve transaction receipt by transaction hash.
pub async fn transaction_receipt(&self, transaction_hash: H256) -> Result<Receipt> {
Ok(Ethereum::get_transaction_receipt(&self.client, transaction_hash).await?)
}
/// Get the nonce of the given account.
pub async fn account_nonce(&self, address: Address) -> Result<U256> {
Ok(Ethereum::get_transaction_count(&self.client, address).await?)
}
/// Submit an Ethereum transaction.
///
/// The transaction must already be signed before sending it through this method.
pub async fn submit_transaction(&self, signed_raw_tx: SignedRawTx) -> Result<TransactionHash> {
let transaction = Bytes(signed_raw_tx);
let tx_hash = Ethereum::submit_transaction(&self.client, transaction).await?;
log::trace!(target: "bridge", "Sent transaction to Ethereum node: {:?}", tx_hash);
Ok(tx_hash)
}
/// Call Ethereum smart contract.
pub async fn eth_call(&self, call_transaction: CallRequest) -> Result<Bytes> {
Ok(Ethereum::call(&self.client, call_transaction).await?)
}
}
@@ -0,0 +1,71 @@
// 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/>.
//! Ethereum node RPC errors.
use jsonrpsee::client::RequestError;
use relay_utils::MaybeConnectionError;
/// Result type used by Ethereum client.
pub type Result<T> = std::result::Result<T, Error>;
/// Errors that can occur only when interacting with
/// an Ethereum 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),
/// Failed to parse response.
ResponseParseFailed(String),
/// We have received a header with missing fields.
IncompleteHeader,
/// We have received a transaction missing a `raw` field.
IncompleteTransaction,
/// An invalid Substrate block number was received from
/// an Ethereum node.
InvalidSubstrateBlockNumber,
/// An invalid index has been received from an Ethereum node.
InvalidIncompleteIndex,
}
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.to_string(),
Self::IncompleteHeader => {
"Incomplete Ethereum Header Received (missing some of required fields - hash, number, logs_bloom)"
.to_string()
}
Self::IncompleteTransaction => "Incomplete Ethereum Transaction (missing required field - raw)".to_string(),
Self::InvalidSubstrateBlockNumber => "Received an invalid Substrate block from Ethereum Node".to_string(),
Self::InvalidIncompleteIndex => "Received an invalid incomplete index from Ethereum Node".to_string(),
}
}
}
+48
View File
@@ -0,0 +1,48 @@
// 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 client;
mod error;
mod rpc;
mod sign;
pub use crate::client::Client;
pub use crate::error::{Error, Result};
pub use crate::sign::{sign_and_submit_transaction, SigningParams};
pub mod types;
/// Ethereum connection params.
#[derive(Debug, Clone)]
pub struct ConnectionParams {
/// Ethereum RPC host.
pub host: String,
/// Ethereum RPC port.
pub port: u16,
}
impl Default for ConnectionParams {
fn default() -> Self {
ConnectionParams {
host: "localhost".into(),
port: 8545,
}
}
}
+53
View File
@@ -0,0 +1,53 @@
// 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/>.
//! Ethereum node RPC interface.
// The compiler doesn't think we're using the
// code from rpc_api!
#![allow(dead_code)]
#![allow(unused_variables)]
use crate::types::{
Address, Bytes, CallRequest, Header, HeaderWithTransactions, Receipt, Transaction, TransactionHash, H256, U256, U64,
};
jsonrpsee::rpc_api! {
pub(crate) Ethereum {
#[rpc(method = "eth_estimateGas", positional_params)]
fn estimate_gas(call_request: CallRequest) -> U256;
#[rpc(method = "eth_blockNumber", positional_params)]
fn block_number() -> U64;
#[rpc(method = "eth_getBlockByNumber", positional_params)]
fn get_block_by_number(block_number: U64, full_tx_objs: bool) -> Header;
#[rpc(method = "eth_getBlockByHash", positional_params)]
fn get_block_by_hash(hash: H256, full_tx_objs: bool) -> Header;
#[rpc(method = "eth_getBlockByNumber", positional_params)]
fn get_block_by_number_with_transactions(number: U64, full_tx_objs: bool) -> HeaderWithTransactions;
#[rpc(method = "eth_getBlockByHash", positional_params)]
fn get_block_by_hash_with_transactions(hash: H256, full_tx_objs: bool) -> HeaderWithTransactions;
#[rpc(method = "eth_getTransactionByHash", positional_params)]
fn transaction_by_hash(hash: H256) -> Option<Transaction>;
#[rpc(method = "eth_getTransactionReceipt", positional_params)]
fn get_transaction_receipt(transaction_hash: H256) -> Receipt;
#[rpc(method = "eth_getTransactionCount", positional_params)]
fn get_transaction_count(address: Address) -> U256;
#[rpc(method = "eth_submitTransaction", positional_params)]
fn submit_transaction(transaction: Bytes) -> TransactionHash;
#[rpc(method = "eth_call", positional_params)]
fn call(transaction_call: CallRequest) -> Bytes;
}
}
@@ -0,0 +1,85 @@
// 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::types::{Address, CallRequest, U256};
use crate::{Client, Result};
use parity_crypto::publickey::KeyPair;
/// Ethereum signing params.
#[derive(Clone, Debug)]
pub struct SigningParams {
/// Ethereum chain id.
pub chain_id: u64,
/// Ethereum transactions signer.
pub signer: KeyPair,
/// Gas price we agree to pay.
pub gas_price: U256,
}
impl Default for SigningParams {
fn default() -> Self {
SigningParams {
chain_id: 0x11, // Parity dev chain
// account that has a lot of ether when we run instant seal engine
// address: 0x00a329c0648769a73afac7f9381e08fb43dbea72
// secret: 0x4d5db4107d237df6a3d58ee5f70ae63d73d7658d4026f2eefd2f204c81682cb7
signer: KeyPair::from_secret_slice(
&hex::decode("4d5db4107d237df6a3d58ee5f70ae63d73d7658d4026f2eefd2f204c81682cb7")
.expect("secret is hardcoded, thus valid; qed"),
)
.expect("secret is hardcoded, thus valid; qed"),
gas_price: 8_000_000_000u64.into(), // 8 Gwei
}
}
}
/// Sign and submit tranaction using given Ethereum client.
pub async fn sign_and_submit_transaction(
client: &Client,
params: &SigningParams,
contract_address: Option<Address>,
nonce: Option<U256>,
double_gas: bool,
encoded_call: Vec<u8>,
) -> Result<()> {
let nonce = if let Some(n) = nonce {
n
} else {
let address: Address = params.signer.address().as_fixed_bytes().into();
client.account_nonce(address).await?
};
let call_request = CallRequest {
to: contract_address,
data: Some(encoded_call.clone().into()),
..Default::default()
};
let gas = client.estimate_gas(call_request).await?;
let raw_transaction = ethereum_tx_sign::RawTransaction {
nonce,
to: contract_address,
value: U256::zero(),
gas: if double_gas { gas.saturating_mul(2.into()) } else { gas },
gas_price: params.gas_price,
data: encoded_call,
}
.sign(&params.signer.secret().as_fixed_bytes().into(), &params.chain_id);
let _ = client.submit_transaction(raw_transaction).await?;
Ok(())
}
@@ -14,11 +14,9 @@
// 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 crate::substrate_types::{into_substrate_ethereum_header, into_substrate_ethereum_receipts}; //! Common types that are used in relay <-> Ethereum node communications.
use codec::Encode; use headers_relay::sync_types::SourceHeader;
use headers_relay::sync_types::{HeadersSyncPipeline, QueuedHeader, SourceHeader};
use relay_utils::HeaderId;
pub use web3::types::{Address, Bytes, CallRequest, H256, U128, U256, U64}; pub use web3::types::{Address, Bytes, CallRequest, H256, U128, U256, U64};
@@ -26,6 +24,9 @@ pub use web3::types::{Address, Bytes, CallRequest, H256, U128, U256, U64};
/// both number and hash fields filled. /// both number and hash fields filled.
pub const HEADER_ID_PROOF: &str = "checked on retrieval; qed"; pub const HEADER_ID_PROOF: &str = "checked on retrieval; qed";
/// Ethereum transaction hash type.
pub type HeaderHash = H256;
/// Ethereum transaction hash type. /// Ethereum transaction hash type.
pub type TransactionHash = H256; pub type TransactionHash = H256;
@@ -37,10 +38,11 @@ pub type Header = web3::types::Block<H256>;
/// Ethereum header type used in headers sync. /// Ethereum header type used in headers sync.
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub struct EthereumSyncHeader(Header); pub struct SyncHeader(Header);
impl std::ops::Deref for EthereumSyncHeader { impl std::ops::Deref for SyncHeader {
type Target = Header; type Target = Header;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
&self.0 &self.0
} }
@@ -53,52 +55,26 @@ pub type HeaderWithTransactions = web3::types::Block<Transaction>;
pub type Receipt = web3::types::TransactionReceipt; pub type Receipt = web3::types::TransactionReceipt;
/// Ethereum header ID. /// Ethereum header ID.
pub type EthereumHeaderId = HeaderId<H256, u64>; pub type HeaderId = relay_utils::HeaderId<H256, u64>;
/// Queued ethereum header ID.
pub type QueuedEthereumHeader = QueuedHeader<EthereumHeadersSyncPipeline>;
/// A raw Ethereum transaction that's been signed. /// A raw Ethereum transaction that's been signed.
pub type SignedRawTx = Vec<u8>; pub type SignedRawTx = Vec<u8>;
/// Ethereum synchronization pipeline. impl From<Header> for SyncHeader {
#[derive(Clone, Copy, Debug)]
#[cfg_attr(test, derive(PartialEq))]
pub struct EthereumHeadersSyncPipeline;
impl HeadersSyncPipeline for EthereumHeadersSyncPipeline {
const SOURCE_NAME: &'static str = "Ethereum";
const TARGET_NAME: &'static str = "Substrate";
type Hash = H256;
type Number = u64;
type Header = EthereumSyncHeader;
type Extra = Vec<Receipt>;
type Completion = ();
fn estimate_size(source: &QueuedHeader<Self>) -> usize {
into_substrate_ethereum_header(source.header()).encode().len()
+ into_substrate_ethereum_receipts(source.extra())
.map(|extra| extra.encode().len())
.unwrap_or(0)
}
}
impl From<Header> for EthereumSyncHeader {
fn from(header: Header) -> Self { fn from(header: Header) -> Self {
Self(header) Self(header)
} }
} }
impl SourceHeader<H256, u64> for EthereumSyncHeader { impl SourceHeader<H256, u64> for SyncHeader {
fn id(&self) -> EthereumHeaderId { fn id(&self) -> HeaderId {
HeaderId( relay_utils::HeaderId(
self.number.expect(HEADER_ID_PROOF).as_u64(), self.number.expect(HEADER_ID_PROOF).as_u64(),
self.hash.expect(HEADER_ID_PROOF), self.hash.expect(HEADER_ID_PROOF),
) )
} }
fn parent_id(&self) -> EthereumHeaderId { fn parent_id(&self) -> HeaderId {
HeaderId(self.number.expect(HEADER_ID_PROOF).as_u64() - 1, self.parent_hash) relay_utils::HeaderId(self.number.expect(HEADER_ID_PROOF).as_u64() - 1, self.parent_hash)
} }
} }
+1 -2
View File
@@ -17,7 +17,6 @@ env_logger = "0.7.0"
ethabi = "12.0" ethabi = "12.0"
ethabi-contract = "11.0" ethabi-contract = "11.0"
ethabi-derive = "12.0" ethabi-derive = "12.0"
ethereum-tx-sign = "3.0"
exchange-relay = { path = "../exchange-relay" } exchange-relay = { path = "../exchange-relay" }
futures = "0.3.5" futures = "0.3.5"
headers-relay = { path = "../headers-relay" } headers-relay = { path = "../headers-relay" }
@@ -27,11 +26,11 @@ log = "0.4.11"
messages-relay = { path = "../messages-relay" } 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-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"
web3 = "0.13"
[dependencies.jsonrpsee] [dependencies.jsonrpsee]
git = "https://github.com/svyatonik/jsonrpsee.git" git = "https://github.com/svyatonik/jsonrpsee.git"
+14 -192
View File
@@ -14,22 +14,18 @@
// 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 crate::ethereum_types::{ use crate::rpc_errors::RpcError;
Address, Bytes, CallRequest, EthereumHeaderId, Header, HeaderWithTransactions, Receipt, SignedRawTx, Transaction,
TransactionHash, H256, U256,
};
use crate::rpc::{Ethereum, EthereumRpc};
use crate::rpc_errors::{EthereumNodeError, RpcError};
use crate::substrate_types::{GrandpaJustification, Hash as SubstrateHash, QueuedSubstrateHeader, SubstrateHeaderId}; use crate::substrate_types::{GrandpaJustification, Hash as SubstrateHash, QueuedSubstrateHeader, SubstrateHeaderId};
use async_trait::async_trait; use async_trait::async_trait;
use codec::{Decode, Encode}; use codec::{Decode, Encode};
use ethabi::FunctionOutputDecoder; use ethabi::FunctionOutputDecoder;
use headers_relay::sync_types::SubmittedHeaders; use headers_relay::sync_types::SubmittedHeaders;
use jsonrpsee::raw::RawClient; use relay_ethereum_client::{
use jsonrpsee::transport::http::HttpTransportClient; sign_and_submit_transaction,
use jsonrpsee::Client; types::{Address, CallRequest, HeaderId as EthereumHeaderId, Receipt, H256, U256},
use parity_crypto::publickey::KeyPair; Client as EthereumClient, Error as EthereumNodeError, SigningParams as EthereumSigningParams,
};
use relay_utils::{HeaderId, MaybeConnectionError}; use relay_utils::{HeaderId, MaybeConnectionError};
use std::collections::HashSet; use std::collections::HashSet;
@@ -38,159 +34,10 @@ ethabi_contract::use_contract!(bridge_contract, "res/substrate-bridge-abi.json")
type RpcResult<T> = std::result::Result<T, RpcError>; type RpcResult<T> = std::result::Result<T, RpcError>;
/// Ethereum connection params.
#[derive(Debug, Clone)]
pub struct EthereumConnectionParams {
/// Ethereum RPC host.
pub host: String,
/// Ethereum RPC port.
pub port: u16,
}
impl Default for EthereumConnectionParams {
fn default() -> Self {
EthereumConnectionParams {
host: "localhost".into(),
port: 8545,
}
}
}
/// Ethereum signing params.
#[derive(Clone, Debug)]
pub struct EthereumSigningParams {
/// Ethereum chain id.
pub chain_id: u64,
/// Ethereum transactions signer.
pub signer: KeyPair,
/// Gas price we agree to pay.
pub gas_price: U256,
}
impl Default for EthereumSigningParams {
fn default() -> Self {
EthereumSigningParams {
chain_id: 0x11, // Parity dev chain
// account that has a lot of ether when we run instant seal engine
// address: 0x00a329c0648769a73afac7f9381e08fb43dbea72
// secret: 0x4d5db4107d237df6a3d58ee5f70ae63d73d7658d4026f2eefd2f204c81682cb7
signer: KeyPair::from_secret_slice(
&hex::decode("4d5db4107d237df6a3d58ee5f70ae63d73d7658d4026f2eefd2f204c81682cb7")
.expect("secret is hardcoded, thus valid; qed"),
)
.expect("secret is hardcoded, thus valid; qed"),
gas_price: 8_000_000_000u64.into(), // 8 Gwei
}
}
}
/// The client used to interact with an Ethereum node through RPC.
pub struct EthereumRpcClient {
client: Client,
}
impl EthereumRpcClient {
/// Create a new Ethereum RPC Client.
pub fn new(params: EthereumConnectionParams) -> 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();
Self { client }
}
}
#[async_trait]
impl EthereumRpc for EthereumRpcClient {
async fn estimate_gas(&self, call_request: CallRequest) -> RpcResult<U256> {
Ok(Ethereum::estimate_gas(&self.client, call_request).await?)
}
async fn best_block_number(&self) -> RpcResult<u64> {
Ok(Ethereum::block_number(&self.client).await?.as_u64())
}
async fn header_by_number(&self, block_number: u64) -> RpcResult<Header> {
let get_full_tx_objects = false;
let header = Ethereum::get_block_by_number(&self.client, block_number, get_full_tx_objects).await?;
match header.number.is_some() && header.hash.is_some() && header.logs_bloom.is_some() {
true => Ok(header),
false => Err(RpcError::Ethereum(EthereumNodeError::IncompleteHeader)),
}
}
async fn header_by_hash(&self, hash: H256) -> RpcResult<Header> {
let get_full_tx_objects = false;
let header = Ethereum::get_block_by_hash(&self.client, hash, get_full_tx_objects).await?;
match header.number.is_some() && header.hash.is_some() && header.logs_bloom.is_some() {
true => Ok(header),
false => Err(RpcError::Ethereum(EthereumNodeError::IncompleteHeader)),
}
}
async fn header_by_number_with_transactions(&self, number: u64) -> RpcResult<HeaderWithTransactions> {
let get_full_tx_objects = true;
let header = Ethereum::get_block_by_number_with_transactions(&self.client, number, get_full_tx_objects).await?;
let is_complete_header = header.number.is_some() && header.hash.is_some() && header.logs_bloom.is_some();
if !is_complete_header {
return Err(RpcError::Ethereum(EthereumNodeError::IncompleteHeader));
}
let is_complete_transactions = header.transactions.iter().all(|tx| tx.raw.is_some());
if !is_complete_transactions {
return Err(RpcError::Ethereum(EthereumNodeError::IncompleteTransaction));
}
Ok(header)
}
async fn header_by_hash_with_transactions(&self, hash: H256) -> RpcResult<HeaderWithTransactions> {
let get_full_tx_objects = true;
let header = Ethereum::get_block_by_hash_with_transactions(&self.client, hash, get_full_tx_objects).await?;
let is_complete_header = header.number.is_some() && header.hash.is_some() && header.logs_bloom.is_some();
if !is_complete_header {
return Err(RpcError::Ethereum(EthereumNodeError::IncompleteHeader));
}
let is_complete_transactions = header.transactions.iter().all(|tx| tx.raw.is_some());
if !is_complete_transactions {
return Err(RpcError::Ethereum(EthereumNodeError::IncompleteTransaction));
}
Ok(header)
}
async fn transaction_by_hash(&self, hash: H256) -> RpcResult<Option<Transaction>> {
Ok(Ethereum::transaction_by_hash(&self.client, hash).await?)
}
async fn transaction_receipt(&self, transaction_hash: H256) -> RpcResult<Receipt> {
Ok(Ethereum::get_transaction_receipt(&self.client, transaction_hash).await?)
}
async fn account_nonce(&self, address: Address) -> RpcResult<U256> {
Ok(Ethereum::get_transaction_count(&self.client, address).await?)
}
async fn submit_transaction(&self, signed_raw_tx: SignedRawTx) -> RpcResult<TransactionHash> {
let transaction = Bytes(signed_raw_tx);
let tx_hash = Ethereum::submit_transaction(&self.client, transaction).await?;
log::trace!(target: "bridge", "Sent transaction to Ethereum node: {:?}", tx_hash);
Ok(tx_hash)
}
async fn eth_call(&self, call_transaction: CallRequest) -> RpcResult<Bytes> {
Ok(Ethereum::call(&self.client, call_transaction).await?)
}
}
/// A trait which contains methods that work by using multiple low-level RPCs, or more complicated /// A trait which contains methods that work by using multiple low-level RPCs, or more complicated
/// interactions involving, for example, an Ethereum contract. /// interactions involving, for example, an Ethereum contract.
#[async_trait] #[async_trait]
pub trait EthereumHighLevelRpc: EthereumRpc { 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<SubstrateHeaderId>;
@@ -240,7 +87,7 @@ pub trait EthereumHighLevelRpc: EthereumRpc {
} }
#[async_trait] #[async_trait]
impl EthereumHighLevelRpc for EthereumRpcClient { 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<SubstrateHeaderId> {
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 {
@@ -293,7 +140,7 @@ impl EthereumHighLevelRpc for EthereumRpcClient {
submitted: Vec::new(), submitted: Vec::new(),
incomplete: Vec::new(), incomplete: Vec::new(),
rejected: headers.iter().rev().map(|header| header.id()).collect(), rejected: headers.iter().rev().map(|header| header.id()).collect(),
fatal_error: Some(error), fatal_error: Some(error.into()),
} }
} }
}; };
@@ -302,9 +149,7 @@ impl EthereumHighLevelRpc for EthereumRpcClient {
// cloning `jsonrpsee::Client` only clones reference to background threads // cloning `jsonrpsee::Client` only clones reference to background threads
submit_substrate_headers( submit_substrate_headers(
EthereumHeadersSubmitter { EthereumHeadersSubmitter {
client: EthereumRpcClient { client: self.clone(),
client: self.client.clone(),
},
params, params,
contract_address, contract_address,
nonce, nonce,
@@ -369,32 +214,9 @@ impl EthereumHighLevelRpc for EthereumRpcClient {
double_gas: bool, double_gas: bool,
encoded_call: Vec<u8>, encoded_call: Vec<u8>,
) -> RpcResult<()> { ) -> RpcResult<()> {
let nonce = if let Some(n) = nonce { sign_and_submit_transaction(self, params, contract_address, nonce, double_gas, encoded_call)
n .await
} else { .map_err(Into::into)
let address: Address = params.signer.address().as_fixed_bytes().into();
self.account_nonce(address).await?
};
let call_request = CallRequest {
to: contract_address,
data: Some(encoded_call.clone().into()),
..Default::default()
};
let gas = self.estimate_gas(call_request).await?;
let raw_transaction = ethereum_tx_sign::RawTransaction {
nonce,
to: contract_address,
value: U256::zero(),
gas: if double_gas { gas.saturating_mul(2.into()) } else { gas },
gas_price: params.gas_price,
data: encoded_call,
}
.sign(&params.signer.secret().as_fixed_bytes().into(), &params.chain_id);
let _ = self.submit_transaction(raw_transaction).await?;
Ok(())
} }
async fn transaction_receipts( async fn transaction_receipts(
@@ -524,7 +346,7 @@ trait HeadersSubmitter {
/// Implementation of Substrate headers submitter that sends headers to running Ethereum node. /// Implementation of Substrate headers submitter that sends headers to running Ethereum node.
struct EthereumHeadersSubmitter { struct EthereumHeadersSubmitter {
client: EthereumRpcClient, client: EthereumClient,
params: EthereumSigningParams, params: EthereumSigningParams,
contract_address: Address, contract_address: Address,
nonce: U256, nonce: U256,
@@ -14,9 +14,7 @@
// 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 crate::ethereum_client::{ use crate::ethereum_client::{bridge_contract, EthereumHighLevelRpc};
bridge_contract, EthereumConnectionParams, EthereumHighLevelRpc, EthereumRpcClient, EthereumSigningParams,
};
use crate::instances::BridgeInstance; use crate::instances::BridgeInstance;
use crate::rpc::SubstrateRpc; use crate::rpc::SubstrateRpc;
use crate::substrate_client::{SubstrateConnectionParams, SubstrateRpcClient}; use crate::substrate_client::{SubstrateConnectionParams, SubstrateRpcClient};
@@ -24,6 +22,9 @@ use crate::substrate_types::{Hash as SubstrateHash, Header as SubstrateHeader, S
use codec::{Decode, Encode}; use codec::{Decode, Encode};
use num_traits::Zero; use num_traits::Zero;
use relay_ethereum_client::{
Client as EthereumClient, ConnectionParams as EthereumConnectionParams, SigningParams as EthereumSigningParams,
};
use relay_utils::HeaderId; use relay_utils::HeaderId;
/// Ethereum synchronization parameters. /// Ethereum synchronization parameters.
@@ -63,7 +64,7 @@ pub fn run(params: EthereumDeployContractParams) {
} = params; } = params;
let result = local_pool.run_until(async move { let result = local_pool.run_until(async move {
let eth_client = EthereumRpcClient::new(eth_params); let eth_client = EthereumClient::new(eth_params);
let sub_client = SubstrateRpcClient::new(sub_params, instance).await?; let sub_client = SubstrateRpcClient::new(sub_params, instance).await?;
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?;
@@ -137,7 +138,7 @@ async fn prepare_initial_authorities_set(
/// Deploy bridge contract to Ethereum chain. /// Deploy bridge contract to Ethereum chain.
async fn deploy_bridge_contract( async fn deploy_bridge_contract(
eth_client: &EthereumRpcClient, eth_client: &EthereumClient,
params: &EthereumSigningParams, params: &EthereumSigningParams,
contract_code: Vec<u8>, contract_code: Vec<u8>,
initial_header: Vec<u8>, initial_header: Vec<u8>,
@@ -16,13 +16,8 @@
//! Relaying proofs of PoA -> Substrate exchange transactions. //! Relaying proofs of PoA -> Substrate exchange transactions.
use crate::ethereum_client::{EthereumConnectionParams, EthereumRpcClient};
use crate::ethereum_types::{
EthereumHeaderId, HeaderWithTransactions as EthereumHeaderWithTransactions, Transaction as EthereumTransaction,
TransactionHash as EthereumTransactionHash, H256,
};
use crate::instances::BridgeInstance; use crate::instances::BridgeInstance;
use crate::rpc::{EthereumRpc, SubstrateRpc}; use crate::rpc::SubstrateRpc;
use crate::rpc_errors::RpcError; use crate::rpc_errors::RpcError;
use crate::substrate_client::{ use crate::substrate_client::{
SubmitEthereumExchangeTransactionProof, SubstrateConnectionParams, SubstrateRpcClient, SubstrateSigningParams, SubmitEthereumExchangeTransactionProof, SubstrateConnectionParams, SubstrateRpcClient, SubstrateSigningParams,
@@ -36,6 +31,13 @@ use exchange_relay::exchange::{
TransactionProofPipeline, TransactionProofPipeline,
}; };
use exchange_relay::exchange_loop::{run as run_loop, InMemoryStorage}; use exchange_relay::exchange_loop::{run as run_loop, InMemoryStorage};
use relay_ethereum_client::{
types::{
HeaderId as EthereumHeaderId, HeaderWithTransactions as EthereumHeaderWithTransactions,
Transaction as EthereumTransaction, TransactionHash as EthereumTransactionHash, H256, HEADER_ID_PROOF,
},
Client as EthereumClient, ConnectionParams as EthereumConnectionParams,
};
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::time::Duration;
@@ -92,8 +94,8 @@ impl SourceBlock for EthereumSourceBlock {
fn id(&self) -> EthereumHeaderId { fn id(&self) -> EthereumHeaderId {
HeaderId( HeaderId(
self.0.number.expect(crate::ethereum_types::HEADER_ID_PROOF).as_u64(), self.0.number.expect(HEADER_ID_PROOF).as_u64(),
self.0.hash.expect(crate::ethereum_types::HEADER_ID_PROOF), self.0.hash.expect(HEADER_ID_PROOF),
) )
} }
@@ -120,7 +122,7 @@ impl SourceTransaction for EthereumSourceTransaction {
/// Ethereum node as transactions proof source. /// Ethereum node as transactions proof source.
struct EthereumTransactionsSource { struct EthereumTransactionsSource {
client: EthereumRpcClient, client: EthereumClient,
} }
#[async_trait] #[async_trait]
@@ -136,6 +138,7 @@ impl SourceClient<EthereumToSubstrateExchange> for EthereumTransactionsSource {
.header_by_hash_with_transactions(hash) .header_by_hash_with_transactions(hash)
.await .await
.map(EthereumSourceBlock) .map(EthereumSourceBlock)
.map_err(Into::into)
} }
async fn block_by_number(&self, number: u64) -> Result<EthereumSourceBlock, Self::Error> { async fn block_by_number(&self, number: u64) -> Result<EthereumSourceBlock, Self::Error> {
@@ -143,6 +146,7 @@ impl SourceClient<EthereumToSubstrateExchange> for EthereumTransactionsSource {
.header_by_number_with_transactions(number) .header_by_number_with_transactions(number)
.await .await
.map(EthereumSourceBlock) .map(EthereumSourceBlock)
.map_err(Into::into)
} }
async fn transaction_block( async fn transaction_block(
@@ -278,7 +282,7 @@ fn run_single_transaction_relay(params: EthereumExchangeParams, eth_tx_hash: H25
} = params; } = params;
let result = local_pool.run_until(async move { let result = local_pool.run_until(async move {
let eth_client = EthereumRpcClient::new(eth_params); let eth_client = EthereumClient::new(eth_params);
let sub_client = SubstrateRpcClient::new(sub_params, instance).await?; let sub_client = SubstrateRpcClient::new(sub_params, instance).await?;
let source = EthereumTransactionsSource { client: eth_client }; let source = EthereumTransactionsSource { client: eth_client };
@@ -321,7 +325,7 @@ fn run_auto_transactions_relay_loop(params: EthereumExchangeParams, eth_start_wi
} = params; } = params;
let do_run_loop = move || -> Result<(), String> { let do_run_loop = move || -> Result<(), String> {
let eth_client = EthereumRpcClient::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(SubstrateRpcClient::new(sub_params, instance))
.map_err(|err| format!("Error starting Substrate client: {:?}", err))?; .map_err(|err| format!("Error starting Substrate client: {:?}", err))?;
@@ -16,14 +16,14 @@
//! Submitting Ethereum -> Substrate exchange transactions. //! Submitting Ethereum -> Substrate exchange transactions.
use crate::ethereum_client::{EthereumConnectionParams, EthereumRpcClient, EthereumSigningParams};
use crate::ethereum_types::{CallRequest, U256};
use crate::rpc::EthereumRpc;
use bp_eth_poa::{ use bp_eth_poa::{
signatures::{SecretKey, SignTransaction}, signatures::{SecretKey, SignTransaction},
UnsignedTransaction, UnsignedTransaction,
}; };
use relay_ethereum_client::{
types::{CallRequest, U256},
Client as EthereumClient, ConnectionParams as EthereumConnectionParams, SigningParams as EthereumSigningParams,
};
use rialto_runtime::exchange::LOCK_FUNDS_ADDRESS; use rialto_runtime::exchange::LOCK_FUNDS_ADDRESS;
/// Ethereum exchange transaction params. /// Ethereum exchange transaction params.
@@ -54,7 +54,7 @@ pub fn run(params: EthereumExchangeSubmitParams) {
} = params; } = params;
let result: Result<_, String> = local_pool.run_until(async move { let result: Result<_, String> = local_pool.run_until(async move {
let eth_client = EthereumRpcClient::new(eth_params); let eth_client = EthereumClient::new(eth_params);
let eth_signer_address = eth_sign.signer.address(); let eth_signer_address = eth_sign.signer.address();
let sub_recipient_encoded = sub_recipient; let sub_recipient_encoded = sub_recipient;
@@ -16,26 +16,27 @@
//! Ethereum PoA -> Substrate synchronization. //! Ethereum PoA -> Substrate synchronization.
use crate::ethereum_client::{EthereumConnectionParams, EthereumHighLevelRpc, EthereumRpcClient}; use crate::ethereum_client::EthereumHighLevelRpc;
use crate::ethereum_types::{
EthereumHeaderId, EthereumHeadersSyncPipeline, EthereumSyncHeader as Header, QueuedEthereumHeader, Receipt,
};
use crate::instances::BridgeInstance; use crate::instances::BridgeInstance;
use crate::rpc::{EthereumRpc, SubstrateRpc}; use crate::rpc::SubstrateRpc;
use crate::rpc_errors::RpcError; use crate::rpc_errors::RpcError;
use crate::substrate_client::{ use crate::substrate_client::{
SubmitEthereumHeaders, SubstrateConnectionParams, SubstrateRpcClient, SubstrateSigningParams, SubmitEthereumHeaders, SubstrateConnectionParams, SubstrateRpcClient, SubstrateSigningParams,
}; };
use crate::substrate_types::into_substrate_ethereum_header; use crate::substrate_types::{into_substrate_ethereum_header, into_substrate_ethereum_receipts};
use async_trait::async_trait; use async_trait::async_trait;
use codec::Encode;
use headers_relay::{ use headers_relay::{
sync::{HeadersSyncParams, TargetTransactionMode}, sync::{HeadersSyncParams, TargetTransactionMode},
sync_loop::{SourceClient, TargetClient}, sync_loop::{SourceClient, TargetClient},
sync_types::{SourceHeader, SubmittedHeaders}, sync_types::{HeadersSyncPipeline, QueuedHeader, SourceHeader, SubmittedHeaders},
};
use relay_ethereum_client::{
types::{HeaderHash, HeaderId as EthereumHeaderId, Receipt, SyncHeader as Header},
Client as EthereumClient, ConnectionParams as EthereumConnectionParams,
}; };
use relay_utils::metrics::MetricsParams; use relay_utils::metrics::MetricsParams;
use web3::types::H256;
use std::fmt::Debug; use std::fmt::Debug;
use std::{collections::HashSet, time::Duration}; use std::{collections::HashSet, time::Duration};
@@ -78,14 +79,40 @@ pub struct EthereumSyncParams {
pub instance: Box<dyn BridgeInstance>, pub instance: Box<dyn BridgeInstance>,
} }
/// Ethereum synchronization pipeline.
#[derive(Clone, Copy, Debug)]
#[cfg_attr(test, derive(PartialEq))]
pub struct EthereumHeadersSyncPipeline;
impl HeadersSyncPipeline for EthereumHeadersSyncPipeline {
const SOURCE_NAME: &'static str = "Ethereum";
const TARGET_NAME: &'static str = "Substrate";
type Hash = HeaderHash;
type Number = u64;
type Header = Header;
type Extra = Vec<Receipt>;
type Completion = ();
fn estimate_size(source: &QueuedHeader<Self>) -> usize {
into_substrate_ethereum_header(source.header()).encode().len()
+ into_substrate_ethereum_receipts(source.extra())
.map(|extra| extra.encode().len())
.unwrap_or(0)
}
}
/// Queued ethereum header ID.
pub type QueuedEthereumHeader = QueuedHeader<EthereumHeadersSyncPipeline>;
/// Ethereum client as headers source. /// Ethereum client as headers source.
struct EthereumHeadersSource { struct EthereumHeadersSource {
/// Ethereum node client. /// Ethereum node client.
client: EthereumRpcClient, client: EthereumClient,
} }
impl EthereumHeadersSource { impl EthereumHeadersSource {
fn new(client: EthereumRpcClient) -> Self { fn new(client: EthereumClient) -> Self {
Self { client } Self { client }
} }
} }
@@ -95,15 +122,23 @@ impl SourceClient<EthereumHeadersSyncPipeline> for EthereumHeadersSource {
type Error = RpcError; type Error = RpcError;
async fn best_block_number(&self) -> Result<u64, Self::Error> { async fn best_block_number(&self) -> Result<u64, Self::Error> {
self.client.best_block_number().await self.client.best_block_number().await.map_err(Into::into)
} }
async fn header_by_hash(&self, hash: H256) -> Result<Header, Self::Error> { async fn header_by_hash(&self, hash: HeaderHash) -> Result<Header, 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: u64) -> Result<Header, Self::Error> { async fn header_by_number(&self, number: u64) -> Result<Header, 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(&self, id: EthereumHeaderId) -> Result<(EthereumHeaderId, Option<()>), Self::Error> { async fn header_completion(&self, id: EthereumHeaderId) -> Result<(EthereumHeaderId, Option<()>), Self::Error> {
@@ -192,7 +227,7 @@ pub fn run(params: EthereumSyncParams) -> Result<(), RpcError> {
instance, instance,
} = params; } = params;
let eth_client = EthereumRpcClient::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 { SubstrateRpcClient::new(sub_params, instance).await })?;
let sign_sub_transactions = match sync_params.target_tx_mode { let sign_sub_transactions = match sync_params.target_tx_mode {
+1 -1
View File
@@ -23,7 +23,7 @@
//! //!
//! This module helps by preparing the correct `Call`s for each of the different pallet instances. //! This module helps by preparing the correct `Call`s for each of the different pallet instances.
use crate::ethereum_types::QueuedEthereumHeader; use crate::ethereum_sync_loop::QueuedEthereumHeader;
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 rialto_runtime::exchange::EthereumTransactionInclusionProof as Proof; use rialto_runtime::exchange::EthereumTransactionInclusionProof as Proof;
+7 -4
View File
@@ -21,7 +21,6 @@ mod ethereum_deploy_contract;
mod ethereum_exchange; mod ethereum_exchange;
mod ethereum_exchange_submit; mod ethereum_exchange_submit;
mod ethereum_sync_loop; mod ethereum_sync_loop;
mod ethereum_types;
mod instances; mod instances;
mod rpc; mod rpc;
mod rpc_errors; mod rpc_errors;
@@ -29,7 +28,6 @@ mod substrate_client;
mod substrate_sync_loop; mod substrate_sync_loop;
mod substrate_types; mod substrate_types;
use ethereum_client::{EthereumConnectionParams, EthereumSigningParams};
use ethereum_deploy_contract::EthereumDeployContractParams; use ethereum_deploy_contract::EthereumDeployContractParams;
use ethereum_exchange::EthereumExchangeParams; use ethereum_exchange::EthereumExchangeParams;
use ethereum_exchange_submit::EthereumExchangeSubmitParams; use ethereum_exchange_submit::EthereumExchangeSubmitParams;
@@ -44,6 +42,7 @@ 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 std::io::Write; use std::io::Write;
fn main() { fn main() {
@@ -250,7 +249,8 @@ fn ethereum_sync_params(matches: &clap::ArgMatches) -> Result<EthereumSyncParams
fn substrate_sync_params(matches: &clap::ArgMatches) -> Result<SubstrateSyncParams, String> { fn substrate_sync_params(matches: &clap::ArgMatches) -> Result<SubstrateSyncParams, String> {
use crate::substrate_sync_loop::consts::*; use crate::substrate_sync_loop::consts::*;
let eth_contract_address: ethereum_types::Address = if let Some(eth_contract) = matches.value_of("eth-contract") { let eth_contract_address: relay_ethereum_client::types::Address =
if let Some(eth_contract) = matches.value_of("eth-contract") {
eth_contract.parse().map_err(|e| format!("{}", e))? eth_contract.parse().map_err(|e| format!("{}", e))?
} else { } else {
"731a10897d267e19b34503ad902d0a29173ba4b1" "731a10897d267e19b34503ad902d0a29173ba4b1"
@@ -313,7 +313,10 @@ fn ethereum_deploy_contract_params(matches: &clap::ArgMatches) -> Result<Ethereu
fn ethereum_exchange_submit_params(matches: &clap::ArgMatches) -> Result<EthereumExchangeSubmitParams, String> { fn ethereum_exchange_submit_params(matches: &clap::ArgMatches) -> Result<EthereumExchangeSubmitParams, String> {
let eth_nonce = if let Some(eth_nonce) = matches.value_of("eth-nonce") { let eth_nonce = if let Some(eth_nonce) = matches.value_of("eth-nonce") {
Some(ethereum_types::U256::from_dec_str(&eth_nonce).map_err(|e| format!("Failed to parse eth-nonce: {}", e))?) Some(
relay_ethereum_client::types::U256::from_dec_str(&eth_nonce)
.map_err(|e| format!("Failed to parse eth-nonce: {}", e))?,
)
} else { } else {
None None
}; };
+1 -59
View File
@@ -23,11 +23,6 @@
#![allow(unused_variables)] #![allow(unused_variables)]
use std::result; use std::result;
use crate::ethereum_types::{
Address as EthAddress, Bytes, CallRequest, EthereumHeaderId, Header as EthereumHeader,
HeaderWithTransactions as EthereumHeaderWithTransactions, Receipt, SignedRawTx, Transaction as EthereumTransaction,
TransactionHash as EthereumTxHash, H256, U256, U64,
};
use crate::rpc_errors::RpcError; use crate::rpc_errors::RpcError;
use crate::substrate_types::{ use crate::substrate_types::{
Hash as SubstrateHash, Header as SubstrateHeader, Number as SubBlockNumber, SignedBlock as SubstrateBlock, Hash as SubstrateHash, Header as SubstrateHeader, Number as SubBlockNumber, SignedBlock as SubstrateBlock,
@@ -35,36 +30,12 @@ use crate::substrate_types::{
use async_trait::async_trait; use async_trait::async_trait;
use bp_eth_poa::AuraHeader as SubstrateEthereumHeader; use bp_eth_poa::AuraHeader as SubstrateEthereumHeader;
use relay_ethereum_client::types::{Bytes, HeaderId as EthereumHeaderId};
type Result<T> = result::Result<T, RpcError>; type Result<T> = result::Result<T, RpcError>;
type GrandpaAuthorityList = Vec<u8>; type GrandpaAuthorityList = Vec<u8>;
jsonrpsee::rpc_api! { jsonrpsee::rpc_api! {
pub(crate) Ethereum {
#[rpc(method = "eth_estimateGas", positional_params)]
fn estimate_gas(call_request: CallRequest) -> U256;
#[rpc(method = "eth_blockNumber", positional_params)]
fn block_number() -> U64;
#[rpc(method = "eth_getBlockByNumber", positional_params)]
fn get_block_by_number(block_number: U64, full_tx_objs: bool) -> EthereumHeader;
#[rpc(method = "eth_getBlockByHash", positional_params)]
fn get_block_by_hash(hash: H256, full_tx_objs: bool) -> EthereumHeader;
#[rpc(method = "eth_getBlockByNumber", positional_params)]
fn get_block_by_number_with_transactions(number: U64, full_tx_objs: bool) -> EthereumHeaderWithTransactions;
#[rpc(method = "eth_getBlockByHash", positional_params)]
fn get_block_by_hash_with_transactions(hash: H256, full_tx_objs: bool) -> EthereumHeaderWithTransactions;
#[rpc(method = "eth_getTransactionByHash", positional_params)]
fn transaction_by_hash(hash: H256) -> Option<EthereumTransaction>;
#[rpc(method = "eth_getTransactionReceipt", positional_params)]
fn get_transaction_receipt(transaction_hash: H256) -> Receipt;
#[rpc(method = "eth_getTransactionCount", positional_params)]
fn get_transaction_count(address: EthAddress) -> U256;
#[rpc(method = "eth_submitTransaction", positional_params)]
fn submit_transaction(transaction: Bytes) -> EthereumTxHash;
#[rpc(method = "eth_call", positional_params)]
fn call(transaction_call: CallRequest) -> Bytes;
}
pub(crate) Substrate { pub(crate) Substrate {
#[rpc(method = "chain_getHeader", positional_params)] #[rpc(method = "chain_getHeader", positional_params)]
fn chain_get_header(block_hash: Option<SubstrateHash>) -> SubstrateHeader; fn chain_get_header(block_hash: Option<SubstrateHash>) -> SubstrateHeader;
@@ -81,35 +52,6 @@ jsonrpsee::rpc_api! {
} }
} }
/// The API for the supported Ethereum RPC methods.
#[async_trait]
pub trait EthereumRpc {
/// Estimate gas usage for the given call.
async fn estimate_gas(&self, call_request: CallRequest) -> Result<U256>;
/// Retrieve number of the best known block from the Ethereum node.
async fn best_block_number(&self) -> Result<u64>;
/// Retrieve block header by its number from Ethereum node.
async fn header_by_number(&self, block_number: u64) -> Result<EthereumHeader>;
/// Retrieve block header by its hash from Ethereum node.
async fn header_by_hash(&self, hash: H256) -> Result<EthereumHeader>;
/// Retrieve block header and its transactions by its number from Ethereum node.
async fn header_by_number_with_transactions(&self, block_number: u64) -> Result<EthereumHeaderWithTransactions>;
/// Retrieve block header and its transactions by its hash from Ethereum node.
async fn header_by_hash_with_transactions(&self, hash: H256) -> Result<EthereumHeaderWithTransactions>;
/// Retrieve transaction by its hash from Ethereum node.
async fn transaction_by_hash(&self, hash: H256) -> Result<Option<EthereumTransaction>>;
/// Retrieve transaction receipt by transaction hash.
async fn transaction_receipt(&self, transaction_hash: H256) -> Result<Receipt>;
/// Get the nonce of the given account.
async fn account_nonce(&self, address: EthAddress) -> Result<U256>;
/// Submit an Ethereum transaction.
///
/// The transaction must already be signed before sending it through this method.
async fn submit_transaction(&self, signed_raw_tx: SignedRawTx) -> Result<EthereumTxHash>;
/// Submit a call to an Ethereum smart contract.
async fn eth_call(&self, call_transaction: CallRequest) -> Result<Bytes>;
}
/// The API for the supported Substrate RPC methods. /// The API for the supported Substrate RPC methods.
#[async_trait] #[async_trait]
pub trait SubstrateRpc { pub trait SubstrateRpc {
+6 -33
View File
@@ -15,6 +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 jsonrpsee::client::RequestError; use jsonrpsee::client::RequestError;
use relay_ethereum_client::Error as EthereumNodeError;
use relay_utils::MaybeConnectionError; use relay_utils::MaybeConnectionError;
/// Contains common errors that can occur when /// Contains common errors that can occur when
@@ -76,7 +77,11 @@ 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 {
matches!(*self, RpcError::Request(RequestError::TransportError(_))) match self {
RpcError::Request(RequestError::TransportError(_)) => true,
RpcError::Ethereum(ref error) => error.is_connection_error(),
_ => false,
}
} }
} }
@@ -86,38 +91,6 @@ impl From<codec::Error> for RpcError {
} }
} }
/// Errors that can occur only when interacting with
/// an Ethereum node through RPC.
#[derive(Debug)]
pub enum EthereumNodeError {
/// Failed to parse response.
ResponseParseFailed(String),
/// We have received a header with missing fields.
IncompleteHeader,
/// We have received a transaction missing a `raw` field.
IncompleteTransaction,
/// An invalid Substrate block number was received from
/// an Ethereum node.
InvalidSubstrateBlockNumber,
/// An invalid index has been received from an Ethereum node.
InvalidIncompleteIndex,
}
impl ToString for EthereumNodeError {
fn to_string(&self) -> String {
match self {
Self::ResponseParseFailed(e) => e.to_string(),
Self::IncompleteHeader => {
"Incomplete Ethereum Header Received (missing some of required fields - hash, number, logs_bloom)"
.to_string()
}
Self::IncompleteTransaction => "Incomplete Ethereum Transaction (missing required field - raw)".to_string(),
Self::InvalidSubstrateBlockNumber => "Received an invalid Substrate block from Ethereum Node".to_string(),
Self::InvalidIncompleteIndex => "Received an invalid incomplete index from Ethereum Node".to_string(),
}
}
}
/// Errors that can occur only when interacting with /// Errors that can occur only when interacting with
/// a Substrate node through RPC. /// a Substrate node through RPC.
#[derive(Debug)] #[derive(Debug)]
@@ -14,7 +14,7 @@
// 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 crate::ethereum_types::{Bytes, EthereumHeaderId, QueuedEthereumHeader, H256}; use crate::ethereum_sync_loop::QueuedEthereumHeader;
use crate::instances::BridgeInstance; use crate::instances::BridgeInstance;
use crate::rpc::{Substrate, SubstrateRpc}; use crate::rpc::{Substrate, SubstrateRpc};
use crate::rpc_errors::RpcError; use crate::rpc_errors::RpcError;
@@ -28,6 +28,7 @@ use jsonrpsee::raw::RawClient;
use jsonrpsee::transport::http::HttpTransportClient; use jsonrpsee::transport::http::HttpTransportClient;
use jsonrpsee::Client; use jsonrpsee::Client;
use num_traits::Zero; use num_traits::Zero;
use relay_ethereum_client::types::{Bytes, HeaderId as EthereumHeaderId, H256};
use relay_utils::HeaderId; use relay_utils::HeaderId;
use sp_core::crypto::Pair; use sp_core::crypto::Pair;
use sp_runtime::traits::IdentifyAccount; use sp_runtime::traits::IdentifyAccount;
@@ -16,10 +16,7 @@
//! Substrate -> Ethereum synchronization. //! Substrate -> Ethereum synchronization.
use crate::ethereum_client::{ use crate::ethereum_client::EthereumHighLevelRpc;
EthereumConnectionParams, EthereumHighLevelRpc, EthereumRpcClient, EthereumSigningParams,
};
use crate::ethereum_types::Address;
use crate::instances::BridgeInstance; use crate::instances::BridgeInstance;
use crate::rpc::SubstrateRpc; use crate::rpc::SubstrateRpc;
use crate::rpc_errors::RpcError; use crate::rpc_errors::RpcError;
@@ -35,6 +32,10 @@ use headers_relay::{
sync_loop::{SourceClient, TargetClient}, sync_loop::{SourceClient, TargetClient},
sync_types::{SourceHeader, SubmittedHeaders}, sync_types::{SourceHeader, SubmittedHeaders},
}; };
use relay_ethereum_client::{
types::Address, Client as EthereumClient, ConnectionParams as EthereumConnectionParams,
SigningParams as EthereumSigningParams,
};
use relay_utils::metrics::MetricsParams; use relay_utils::metrics::MetricsParams;
use std::fmt::Debug; use std::fmt::Debug;
@@ -125,7 +126,7 @@ impl SourceClient<SubstrateHeadersSyncPipeline> for SubstrateHeadersSource {
/// Ethereum client as Substrate headers target. /// Ethereum client as Substrate headers target.
struct EthereumHeadersTarget { struct EthereumHeadersTarget {
/// Ethereum node client. /// Ethereum node client.
client: EthereumRpcClient, client: EthereumClient,
/// Bridge contract address. /// Bridge contract address.
contract: Address, contract: Address,
/// Ethereum signing params. /// Ethereum signing params.
@@ -133,7 +134,7 @@ struct EthereumHeadersTarget {
} }
impl EthereumHeadersTarget { impl EthereumHeadersTarget {
fn new(client: EthereumRpcClient, contract: Address, sign_params: EthereumSigningParams) -> Self { fn new(client: EthereumClient, contract: Address, sign_params: EthereumSigningParams) -> Self {
Self { Self {
client, client,
contract, contract,
@@ -194,7 +195,7 @@ pub fn run(params: SubstrateSyncParams) -> Result<(), RpcError> {
instance, instance,
} = params; } = params;
let eth_client = EthereumRpcClient::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 { SubstrateRpcClient::new(sub_params, instance).await })?;
let target = EthereumHeadersTarget::new(eth_client, eth_contract_address, eth_sign); let target = EthereumHeadersTarget::new(eth_client, eth_contract_address, eth_sign);
@@ -14,10 +14,6 @@
// 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 crate::ethereum_types::{
Header as EthereumHeader, Receipt as EthereumReceipt, HEADER_ID_PROOF as ETHEREUM_HEADER_ID_PROOF,
};
use codec::Encode; use codec::Encode;
use headers_relay::sync_types::{HeadersSyncPipeline, QueuedHeader, SourceHeader}; use headers_relay::sync_types::{HeadersSyncPipeline, QueuedHeader, SourceHeader};
use relay_utils::HeaderId; use relay_utils::HeaderId;
@@ -26,6 +22,9 @@ pub use bp_eth_poa::{
Address, AuraHeader as SubstrateEthereumHeader, Bloom, Bytes, LogEntry as SubstrateEthereumLogEntry, Address, AuraHeader as SubstrateEthereumHeader, Bloom, Bytes, LogEntry as SubstrateEthereumLogEntry,
Receipt as SubstrateEthereumReceipt, TransactionOutcome as SubstrateEthereumTransactionOutcome, H256, U256, Receipt as SubstrateEthereumReceipt, TransactionOutcome as SubstrateEthereumTransactionOutcome, H256, U256,
}; };
use relay_ethereum_client::types::{
Header as EthereumHeader, Receipt as EthereumReceipt, HEADER_ID_PROOF as ETHEREUM_HEADER_ID_PROOF,
};
/// Substrate header hash. /// Substrate header hash.
pub type Hash = rialto_runtime::Hash; pub type Hash = rialto_runtime::Hash;