mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-30 22:11: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
|
// 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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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,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(¶ms.signer.secret().as_fixed_bytes().into(), ¶ms.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 {
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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(ð_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 {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user