mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-30 19:51:02 +00:00
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:
committed by
Bastian Köcher
parent
d614cdaba8
commit
ec34870eab
@@ -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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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(¶ms.signer.secret().as_fixed_bytes().into(), ¶ms.chain_id);
|
||||
|
||||
let _ = client.submit_transaction(raw_transaction).await?;
|
||||
Ok(())
|
||||
}
|
||||
+15
-39
@@ -14,11 +14,9 @@
|
||||
// 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::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::{HeadersSyncPipeline, QueuedHeader, SourceHeader};
|
||||
use relay_utils::HeaderId;
|
||||
use headers_relay::sync_types::SourceHeader;
|
||||
|
||||
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.
|
||||
pub const HEADER_ID_PROOF: &str = "checked on retrieval; qed";
|
||||
|
||||
/// Ethereum transaction hash type.
|
||||
pub type HeaderHash = H256;
|
||||
|
||||
/// Ethereum transaction hash type.
|
||||
pub type TransactionHash = H256;
|
||||
|
||||
@@ -37,10 +38,11 @@ pub type Header = web3::types::Block<H256>;
|
||||
|
||||
/// Ethereum header type used in headers sync.
|
||||
#[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;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
@@ -53,52 +55,26 @@ pub type HeaderWithTransactions = web3::types::Block<Transaction>;
|
||||
pub type Receipt = web3::types::TransactionReceipt;
|
||||
|
||||
/// Ethereum header ID.
|
||||
pub type EthereumHeaderId = HeaderId<H256, u64>;
|
||||
|
||||
/// Queued ethereum header ID.
|
||||
pub type QueuedEthereumHeader = QueuedHeader<EthereumHeadersSyncPipeline>;
|
||||
pub type HeaderId = relay_utils::HeaderId<H256, u64>;
|
||||
|
||||
/// A raw Ethereum transaction that's been signed.
|
||||
pub type SignedRawTx = Vec<u8>;
|
||||
|
||||
/// 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 = 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 {
|
||||
impl From<Header> for SyncHeader {
|
||||
fn from(header: Header) -> Self {
|
||||
Self(header)
|
||||
}
|
||||
}
|
||||
|
||||
impl SourceHeader<H256, u64> for EthereumSyncHeader {
|
||||
fn id(&self) -> EthereumHeaderId {
|
||||
HeaderId(
|
||||
impl SourceHeader<H256, u64> for SyncHeader {
|
||||
fn id(&self) -> HeaderId {
|
||||
relay_utils::HeaderId(
|
||||
self.number.expect(HEADER_ID_PROOF).as_u64(),
|
||||
self.hash.expect(HEADER_ID_PROOF),
|
||||
)
|
||||
}
|
||||
|
||||
fn parent_id(&self) -> EthereumHeaderId {
|
||||
HeaderId(self.number.expect(HEADER_ID_PROOF).as_u64() - 1, self.parent_hash)
|
||||
fn parent_id(&self) -> HeaderId {
|
||||
relay_utils::HeaderId(self.number.expect(HEADER_ID_PROOF).as_u64() - 1, self.parent_hash)
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,6 @@ env_logger = "0.7.0"
|
||||
ethabi = "12.0"
|
||||
ethabi-contract = "11.0"
|
||||
ethabi-derive = "12.0"
|
||||
ethereum-tx-sign = "3.0"
|
||||
exchange-relay = { path = "../exchange-relay" }
|
||||
futures = "0.3.5"
|
||||
headers-relay = { path = "../headers-relay" }
|
||||
@@ -27,11 +26,11 @@ log = "0.4.11"
|
||||
messages-relay = { path = "../messages-relay" }
|
||||
num-traits = "0.2"
|
||||
parity-crypto = { version = "0.6", features = ["publickey"] }
|
||||
relay-ethereum-client = { path = "../ethereum-client" }
|
||||
relay-utils = { path = "../utils" }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0.57"
|
||||
time = "0.2"
|
||||
web3 = "0.13"
|
||||
|
||||
[dependencies.jsonrpsee]
|
||||
git = "https://github.com/svyatonik/jsonrpsee.git"
|
||||
|
||||
@@ -14,22 +14,18 @@
|
||||
// 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_types::{
|
||||
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::rpc_errors::RpcError;
|
||||
use crate::substrate_types::{GrandpaJustification, Hash as SubstrateHash, QueuedSubstrateHeader, SubstrateHeaderId};
|
||||
|
||||
use async_trait::async_trait;
|
||||
use codec::{Decode, Encode};
|
||||
use ethabi::FunctionOutputDecoder;
|
||||
use headers_relay::sync_types::SubmittedHeaders;
|
||||
use jsonrpsee::raw::RawClient;
|
||||
use jsonrpsee::transport::http::HttpTransportClient;
|
||||
use jsonrpsee::Client;
|
||||
use parity_crypto::publickey::KeyPair;
|
||||
use relay_ethereum_client::{
|
||||
sign_and_submit_transaction,
|
||||
types::{Address, CallRequest, HeaderId as EthereumHeaderId, Receipt, H256, U256},
|
||||
Client as EthereumClient, Error as EthereumNodeError, SigningParams as EthereumSigningParams,
|
||||
};
|
||||
use relay_utils::{HeaderId, MaybeConnectionError};
|
||||
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>;
|
||||
|
||||
/// 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
|
||||
/// interactions involving, for example, an Ethereum contract.
|
||||
#[async_trait]
|
||||
pub trait EthereumHighLevelRpc: EthereumRpc {
|
||||
pub trait EthereumHighLevelRpc {
|
||||
/// Returns best Substrate block that PoA chain knows of.
|
||||
async fn best_substrate_block(&self, contract_address: Address) -> RpcResult<SubstrateHeaderId>;
|
||||
|
||||
@@ -240,7 +87,7 @@ pub trait EthereumHighLevelRpc: EthereumRpc {
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl EthereumHighLevelRpc for EthereumRpcClient {
|
||||
impl EthereumHighLevelRpc for EthereumClient {
|
||||
async fn best_substrate_block(&self, contract_address: Address) -> RpcResult<SubstrateHeaderId> {
|
||||
let (encoded_call, call_decoder) = bridge_contract::functions::best_known_header::call();
|
||||
let call_request = CallRequest {
|
||||
@@ -293,7 +140,7 @@ impl EthereumHighLevelRpc for EthereumRpcClient {
|
||||
submitted: Vec::new(),
|
||||
incomplete: Vec::new(),
|
||||
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
|
||||
submit_substrate_headers(
|
||||
EthereumHeadersSubmitter {
|
||||
client: EthereumRpcClient {
|
||||
client: self.client.clone(),
|
||||
},
|
||||
client: self.clone(),
|
||||
params,
|
||||
contract_address,
|
||||
nonce,
|
||||
@@ -369,32 +214,9 @@ impl EthereumHighLevelRpc for EthereumRpcClient {
|
||||
double_gas: bool,
|
||||
encoded_call: Vec<u8>,
|
||||
) -> RpcResult<()> {
|
||||
let nonce = if let Some(n) = nonce {
|
||||
n
|
||||
} else {
|
||||
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(¶ms.signer.secret().as_fixed_bytes().into(), ¶ms.chain_id);
|
||||
|
||||
let _ = self.submit_transaction(raw_transaction).await?;
|
||||
Ok(())
|
||||
sign_and_submit_transaction(self, params, contract_address, nonce, double_gas, encoded_call)
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
async fn transaction_receipts(
|
||||
@@ -524,7 +346,7 @@ trait HeadersSubmitter {
|
||||
|
||||
/// Implementation of Substrate headers submitter that sends headers to running Ethereum node.
|
||||
struct EthereumHeadersSubmitter {
|
||||
client: EthereumRpcClient,
|
||||
client: EthereumClient,
|
||||
params: EthereumSigningParams,
|
||||
contract_address: Address,
|
||||
nonce: U256,
|
||||
|
||||
@@ -14,9 +14,7 @@
|
||||
// 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_client::{
|
||||
bridge_contract, EthereumConnectionParams, EthereumHighLevelRpc, EthereumRpcClient, EthereumSigningParams,
|
||||
};
|
||||
use crate::ethereum_client::{bridge_contract, EthereumHighLevelRpc};
|
||||
use crate::instances::BridgeInstance;
|
||||
use crate::rpc::SubstrateRpc;
|
||||
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 num_traits::Zero;
|
||||
use relay_ethereum_client::{
|
||||
Client as EthereumClient, ConnectionParams as EthereumConnectionParams, SigningParams as EthereumSigningParams,
|
||||
};
|
||||
use relay_utils::HeaderId;
|
||||
|
||||
/// Ethereum synchronization parameters.
|
||||
@@ -63,7 +64,7 @@ pub fn run(params: EthereumDeployContractParams) {
|
||||
} = params;
|
||||
|
||||
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 (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.
|
||||
async fn deploy_bridge_contract(
|
||||
eth_client: &EthereumRpcClient,
|
||||
eth_client: &EthereumClient,
|
||||
params: &EthereumSigningParams,
|
||||
contract_code: Vec<u8>,
|
||||
initial_header: Vec<u8>,
|
||||
|
||||
@@ -16,13 +16,8 @@
|
||||
|
||||
//! 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::rpc::{EthereumRpc, SubstrateRpc};
|
||||
use crate::rpc::SubstrateRpc;
|
||||
use crate::rpc_errors::RpcError;
|
||||
use crate::substrate_client::{
|
||||
SubmitEthereumExchangeTransactionProof, SubstrateConnectionParams, SubstrateRpcClient, SubstrateSigningParams,
|
||||
@@ -36,6 +31,13 @@ use exchange_relay::exchange::{
|
||||
TransactionProofPipeline,
|
||||
};
|
||||
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 rialto_runtime::exchange::EthereumTransactionInclusionProof;
|
||||
use std::time::Duration;
|
||||
@@ -92,8 +94,8 @@ impl SourceBlock for EthereumSourceBlock {
|
||||
|
||||
fn id(&self) -> EthereumHeaderId {
|
||||
HeaderId(
|
||||
self.0.number.expect(crate::ethereum_types::HEADER_ID_PROOF).as_u64(),
|
||||
self.0.hash.expect(crate::ethereum_types::HEADER_ID_PROOF),
|
||||
self.0.number.expect(HEADER_ID_PROOF).as_u64(),
|
||||
self.0.hash.expect(HEADER_ID_PROOF),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -120,7 +122,7 @@ impl SourceTransaction for EthereumSourceTransaction {
|
||||
|
||||
/// Ethereum node as transactions proof source.
|
||||
struct EthereumTransactionsSource {
|
||||
client: EthereumRpcClient,
|
||||
client: EthereumClient,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
@@ -136,6 +138,7 @@ impl SourceClient<EthereumToSubstrateExchange> for EthereumTransactionsSource {
|
||||
.header_by_hash_with_transactions(hash)
|
||||
.await
|
||||
.map(EthereumSourceBlock)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
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)
|
||||
.await
|
||||
.map(EthereumSourceBlock)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
async fn transaction_block(
|
||||
@@ -278,7 +282,7 @@ fn run_single_transaction_relay(params: EthereumExchangeParams, eth_tx_hash: H25
|
||||
} = params;
|
||||
|
||||
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 source = EthereumTransactionsSource { client: eth_client };
|
||||
@@ -321,7 +325,7 @@ fn run_auto_transactions_relay_loop(params: EthereumExchangeParams, eth_start_wi
|
||||
} = params;
|
||||
|
||||
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))
|
||||
.map_err(|err| format!("Error starting Substrate client: {:?}", err))?;
|
||||
|
||||
|
||||
@@ -16,14 +16,14 @@
|
||||
|
||||
//! 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::{
|
||||
signatures::{SecretKey, SignTransaction},
|
||||
UnsignedTransaction,
|
||||
};
|
||||
use relay_ethereum_client::{
|
||||
types::{CallRequest, U256},
|
||||
Client as EthereumClient, ConnectionParams as EthereumConnectionParams, SigningParams as EthereumSigningParams,
|
||||
};
|
||||
use rialto_runtime::exchange::LOCK_FUNDS_ADDRESS;
|
||||
|
||||
/// Ethereum exchange transaction params.
|
||||
@@ -54,7 +54,7 @@ pub fn run(params: EthereumExchangeSubmitParams) {
|
||||
} = params;
|
||||
|
||||
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 sub_recipient_encoded = sub_recipient;
|
||||
|
||||
@@ -16,26 +16,27 @@
|
||||
|
||||
//! Ethereum PoA -> Substrate synchronization.
|
||||
|
||||
use crate::ethereum_client::{EthereumConnectionParams, EthereumHighLevelRpc, EthereumRpcClient};
|
||||
use crate::ethereum_types::{
|
||||
EthereumHeaderId, EthereumHeadersSyncPipeline, EthereumSyncHeader as Header, QueuedEthereumHeader, Receipt,
|
||||
};
|
||||
use crate::ethereum_client::EthereumHighLevelRpc;
|
||||
use crate::instances::BridgeInstance;
|
||||
use crate::rpc::{EthereumRpc, SubstrateRpc};
|
||||
use crate::rpc::SubstrateRpc;
|
||||
use crate::rpc_errors::RpcError;
|
||||
use crate::substrate_client::{
|
||||
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 codec::Encode;
|
||||
use headers_relay::{
|
||||
sync::{HeadersSyncParams, TargetTransactionMode},
|
||||
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 web3::types::H256;
|
||||
|
||||
use std::fmt::Debug;
|
||||
use std::{collections::HashSet, time::Duration};
|
||||
@@ -78,14 +79,40 @@ pub struct EthereumSyncParams {
|
||||
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.
|
||||
struct EthereumHeadersSource {
|
||||
/// Ethereum node client.
|
||||
client: EthereumRpcClient,
|
||||
client: EthereumClient,
|
||||
}
|
||||
|
||||
impl EthereumHeadersSource {
|
||||
fn new(client: EthereumRpcClient) -> Self {
|
||||
fn new(client: EthereumClient) -> Self {
|
||||
Self { client }
|
||||
}
|
||||
}
|
||||
@@ -95,15 +122,23 @@ impl SourceClient<EthereumHeadersSyncPipeline> for EthereumHeadersSource {
|
||||
type Error = RpcError;
|
||||
|
||||
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> {
|
||||
self.client.header_by_hash(hash).await.map(Into::into)
|
||||
async fn header_by_hash(&self, hash: HeaderHash) -> Result<Header, Self::Error> {
|
||||
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> {
|
||||
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> {
|
||||
@@ -192,7 +227,7 @@ pub fn run(params: EthereumSyncParams) -> Result<(), RpcError> {
|
||||
instance,
|
||||
} = 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 sign_sub_transactions = match sync_params.target_tx_mode {
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
//!
|
||||
//! 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 rialto_runtime::exchange::EthereumTransactionInclusionProof as Proof;
|
||||
|
||||
@@ -21,7 +21,6 @@ mod ethereum_deploy_contract;
|
||||
mod ethereum_exchange;
|
||||
mod ethereum_exchange_submit;
|
||||
mod ethereum_sync_loop;
|
||||
mod ethereum_types;
|
||||
mod instances;
|
||||
mod rpc;
|
||||
mod rpc_errors;
|
||||
@@ -29,7 +28,6 @@ mod substrate_client;
|
||||
mod substrate_sync_loop;
|
||||
mod substrate_types;
|
||||
|
||||
use ethereum_client::{EthereumConnectionParams, EthereumSigningParams};
|
||||
use ethereum_deploy_contract::EthereumDeployContractParams;
|
||||
use ethereum_exchange::EthereumExchangeParams;
|
||||
use ethereum_exchange_submit::EthereumExchangeSubmitParams;
|
||||
@@ -44,6 +42,7 @@ use substrate_client::{SubstrateConnectionParams, SubstrateSigningParams};
|
||||
use substrate_sync_loop::SubstrateSyncParams;
|
||||
|
||||
use headers_relay::sync::HeadersSyncParams;
|
||||
use relay_ethereum_client::{ConnectionParams as EthereumConnectionParams, SigningParams as EthereumSigningParams};
|
||||
use std::io::Write;
|
||||
|
||||
fn main() {
|
||||
@@ -250,13 +249,14 @@ fn ethereum_sync_params(matches: &clap::ArgMatches) -> Result<EthereumSyncParams
|
||||
fn substrate_sync_params(matches: &clap::ArgMatches) -> Result<SubstrateSyncParams, String> {
|
||||
use crate::substrate_sync_loop::consts::*;
|
||||
|
||||
let eth_contract_address: ethereum_types::Address = if let Some(eth_contract) = matches.value_of("eth-contract") {
|
||||
eth_contract.parse().map_err(|e| format!("{}", e))?
|
||||
} else {
|
||||
"731a10897d267e19b34503ad902d0a29173ba4b1"
|
||||
.parse()
|
||||
.expect("address is hardcoded, thus valid; qed")
|
||||
};
|
||||
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))?
|
||||
} else {
|
||||
"731a10897d267e19b34503ad902d0a29173ba4b1"
|
||||
.parse()
|
||||
.expect("address is hardcoded, thus valid; qed")
|
||||
};
|
||||
|
||||
let params = SubstrateSyncParams {
|
||||
sub_params: substrate_connection_params(matches)?,
|
||||
@@ -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> {
|
||||
let eth_nonce = if let Some(eth_nonce) = matches.value_of("eth-nonce") {
|
||||
Some(ethereum_types::U256::from_dec_str(ð_nonce).map_err(|e| format!("Failed to parse eth-nonce: {}", e))?)
|
||||
Some(
|
||||
relay_ethereum_client::types::U256::from_dec_str(ð_nonce)
|
||||
.map_err(|e| format!("Failed to parse eth-nonce: {}", e))?,
|
||||
)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
@@ -23,11 +23,6 @@
|
||||
#![allow(unused_variables)]
|
||||
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::substrate_types::{
|
||||
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 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) 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 {
|
||||
#[rpc(method = "chain_getHeader", positional_params)]
|
||||
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.
|
||||
#[async_trait]
|
||||
pub trait SubstrateRpc {
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
// 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_utils::MaybeConnectionError;
|
||||
|
||||
/// Contains common errors that can occur when
|
||||
@@ -76,7 +77,11 @@ impl From<ethabi::Error> for RpcError {
|
||||
|
||||
impl MaybeConnectionError for RpcError {
|
||||
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
|
||||
/// a Substrate node through RPC.
|
||||
#[derive(Debug)]
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
// 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_types::{Bytes, EthereumHeaderId, QueuedEthereumHeader, H256};
|
||||
use crate::ethereum_sync_loop::QueuedEthereumHeader;
|
||||
use crate::instances::BridgeInstance;
|
||||
use crate::rpc::{Substrate, SubstrateRpc};
|
||||
use crate::rpc_errors::RpcError;
|
||||
@@ -28,6 +28,7 @@ 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;
|
||||
|
||||
@@ -16,10 +16,7 @@
|
||||
|
||||
//! Substrate -> Ethereum synchronization.
|
||||
|
||||
use crate::ethereum_client::{
|
||||
EthereumConnectionParams, EthereumHighLevelRpc, EthereumRpcClient, EthereumSigningParams,
|
||||
};
|
||||
use crate::ethereum_types::Address;
|
||||
use crate::ethereum_client::EthereumHighLevelRpc;
|
||||
use crate::instances::BridgeInstance;
|
||||
use crate::rpc::SubstrateRpc;
|
||||
use crate::rpc_errors::RpcError;
|
||||
@@ -35,6 +32,10 @@ use headers_relay::{
|
||||
sync_loop::{SourceClient, TargetClient},
|
||||
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 std::fmt::Debug;
|
||||
@@ -125,7 +126,7 @@ impl SourceClient<SubstrateHeadersSyncPipeline> for SubstrateHeadersSource {
|
||||
/// Ethereum client as Substrate headers target.
|
||||
struct EthereumHeadersTarget {
|
||||
/// Ethereum node client.
|
||||
client: EthereumRpcClient,
|
||||
client: EthereumClient,
|
||||
/// Bridge contract address.
|
||||
contract: Address,
|
||||
/// Ethereum signing params.
|
||||
@@ -133,7 +134,7 @@ struct EthereumHeadersTarget {
|
||||
}
|
||||
|
||||
impl EthereumHeadersTarget {
|
||||
fn new(client: EthereumRpcClient, contract: Address, sign_params: EthereumSigningParams) -> Self {
|
||||
fn new(client: EthereumClient, contract: Address, sign_params: EthereumSigningParams) -> Self {
|
||||
Self {
|
||||
client,
|
||||
contract,
|
||||
@@ -194,7 +195,7 @@ pub fn run(params: SubstrateSyncParams) -> Result<(), RpcError> {
|
||||
instance,
|
||||
} = 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 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
|
||||
// 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 headers_relay::sync_types::{HeadersSyncPipeline, QueuedHeader, SourceHeader};
|
||||
use relay_utils::HeaderId;
|
||||
@@ -26,6 +22,9 @@ pub use bp_eth_poa::{
|
||||
Address, AuraHeader as SubstrateEthereumHeader, Bloom, Bytes, LogEntry as SubstrateEthereumLogEntry,
|
||||
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.
|
||||
pub type Hash = rialto_runtime::Hash;
|
||||
|
||||
Reference in New Issue
Block a user