forked from paritytech/polkadot-sdk
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Extract minimal ethereum client (paritytech#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
- Loading branch information
Showing
20 changed files
with
556 additions
and
384 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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?) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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(), | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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, | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} | ||
} |
Oops, something went wrong.