mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-30 22:11:02 +00:00
Extract minimal Rialto-Sub and Millau-Sub clients (#365)
* extracting sub clients * fmt + lost docs * revert enum BridgeInstance * apply suggestions from review * explicite debug impl * remove unused imports from Millau * fix typo * fix instance + API name * Update relays/ethereum/src/ethereum_sync_loop.rs Co-authored-by: Hernando Castano <HCastano@users.noreply.github.com> * separate crates for millau and rialto client * cargo fmt * fix * fmt * remove no_std support * fix compilation again * Update relays/substrate-client/Cargo.toml * Update relay clients to Substrate 2.0 Co-authored-by: Hernando Castano <HCastano@users.noreply.github.com> Co-authored-by: Hernando Castano <castano.ha@gmail.com>
This commit is contained in:
committed by
Bastian Köcher
parent
7f7d62d813
commit
52c1913fff
@@ -522,7 +522,7 @@ impl_runtime_apis! {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl bp_eth_poa::RialtoHeaderApi<Block> for Runtime {
|
impl bp_eth_poa::RialtoPoAHeaderApi<Block> for Runtime {
|
||||||
fn best_block() -> (u64, bp_eth_poa::H256) {
|
fn best_block() -> (u64, bp_eth_poa::H256) {
|
||||||
let best_block = BridgeRialto::best_block();
|
let best_block = BridgeRialto::best_block();
|
||||||
(best_block.number, best_block.hash)
|
(best_block.number, best_block.hash)
|
||||||
|
|||||||
@@ -549,7 +549,7 @@ pub fn step_validator<T>(header_validators: &[T], header_step: u64) -> &T {
|
|||||||
|
|
||||||
sp_api::decl_runtime_apis! {
|
sp_api::decl_runtime_apis! {
|
||||||
/// API for querying information about headers from the Rialto Bridge Pallet
|
/// API for querying information about headers from the Rialto Bridge Pallet
|
||||||
pub trait RialtoHeaderApi {
|
pub trait RialtoPoAHeaderApi {
|
||||||
/// Returns number and hash of the best block known to the bridge module.
|
/// Returns number and hash of the best block known to the bridge module.
|
||||||
///
|
///
|
||||||
/// The caller should only submit an `import_header` transaction that makes
|
/// The caller should only submit an `import_header` transaction that makes
|
||||||
|
|||||||
@@ -27,17 +27,14 @@ messages-relay = { path = "../messages-relay" }
|
|||||||
num-traits = "0.2"
|
num-traits = "0.2"
|
||||||
parity-crypto = { version = "0.6", features = ["publickey"] }
|
parity-crypto = { version = "0.6", features = ["publickey"] }
|
||||||
relay-ethereum-client = { path = "../ethereum-client" }
|
relay-ethereum-client = { path = "../ethereum-client" }
|
||||||
|
relay-rialto-client = { path = "../rialto-client" }
|
||||||
|
relay-substrate-client = { path = "../substrate-client" }
|
||||||
|
rialto-runtime = { path = "../../bin/rialto-runtime" }
|
||||||
relay-utils = { path = "../utils" }
|
relay-utils = { path = "../utils" }
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = "1.0.57"
|
serde_json = "1.0.57"
|
||||||
time = "0.2"
|
time = "0.2"
|
||||||
|
|
||||||
[dependencies.jsonrpsee]
|
|
||||||
git = "https://github.com/svyatonik/jsonrpsee.git"
|
|
||||||
branch = "shared-client-in-rpc-api"
|
|
||||||
default-features = false
|
|
||||||
features = ["http"]
|
|
||||||
|
|
||||||
# Substrate Based Dependencies
|
# Substrate Based Dependencies
|
||||||
[dependencies.frame-system]
|
[dependencies.frame-system]
|
||||||
version = "2.0.0"
|
version = "2.0.0"
|
||||||
@@ -73,7 +70,3 @@ git = "https://github.com/paritytech/substrate.git"
|
|||||||
version = "0.8.0"
|
version = "0.8.0"
|
||||||
tag = 'v2.0.0'
|
tag = 'v2.0.0'
|
||||||
git = "https://github.com/paritytech/substrate.git"
|
git = "https://github.com/paritytech/substrate.git"
|
||||||
|
|
||||||
[dependencies.rialto-runtime]
|
|
||||||
version = "0.1.0"
|
|
||||||
path = "../../bin/rialto-runtime"
|
|
||||||
|
|||||||
@@ -85,7 +85,6 @@ subcommands:
|
|||||||
help: Hex-encoded secret to use when transactions are submitted to the Ethereum node.
|
help: Hex-encoded secret to use when transactions are submitted to the Ethereum node.
|
||||||
- sub-host: *sub-host
|
- sub-host: *sub-host
|
||||||
- sub-port: *sub-port
|
- sub-port: *sub-port
|
||||||
- sub-pallet-instance: *sub-pallet-instance
|
|
||||||
- no-prometheus: *no-prometheus
|
- no-prometheus: *no-prometheus
|
||||||
- prometheus-host: *prometheus-host
|
- prometheus-host: *prometheus-host
|
||||||
- prometheus-port: *prometheus-port
|
- prometheus-port: *prometheus-port
|
||||||
@@ -103,7 +102,6 @@ subcommands:
|
|||||||
takes_value: true
|
takes_value: true
|
||||||
- sub-host: *sub-host
|
- sub-host: *sub-host
|
||||||
- sub-port: *sub-port
|
- sub-port: *sub-port
|
||||||
- sub-pallet-instance: *sub-pallet-instance
|
|
||||||
- sub-authorities-set-id:
|
- sub-authorities-set-id:
|
||||||
long: sub-authorities-set-id
|
long: sub-authorities-set-id
|
||||||
value_name: SUB_AUTHORITIES_SET_ID
|
value_name: SUB_AUTHORITIES_SET_ID
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use crate::rpc_errors::RpcError;
|
use crate::rpc_errors::RpcError;
|
||||||
use crate::substrate_types::{GrandpaJustification, Hash as SubstrateHash, QueuedSubstrateHeader, SubstrateHeaderId};
|
use crate::substrate_sync_loop::QueuedRialtoHeader;
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use codec::{Decode, Encode};
|
use codec::{Decode, Encode};
|
||||||
@@ -26,7 +26,9 @@ use relay_ethereum_client::{
|
|||||||
types::{Address, CallRequest, HeaderId as EthereumHeaderId, Receipt, H256, U256},
|
types::{Address, CallRequest, HeaderId as EthereumHeaderId, Receipt, H256, U256},
|
||||||
Client as EthereumClient, Error as EthereumNodeError, SigningParams as EthereumSigningParams,
|
Client as EthereumClient, Error as EthereumNodeError, SigningParams as EthereumSigningParams,
|
||||||
};
|
};
|
||||||
|
use relay_rialto_client::HeaderId as RialtoHeaderId;
|
||||||
use relay_utils::{HeaderId, MaybeConnectionError};
|
use relay_utils::{HeaderId, MaybeConnectionError};
|
||||||
|
use sp_runtime::Justification;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
// to encode/decode contract calls
|
// to encode/decode contract calls
|
||||||
@@ -39,34 +41,34 @@ type RpcResult<T> = std::result::Result<T, RpcError>;
|
|||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub trait EthereumHighLevelRpc {
|
pub trait EthereumHighLevelRpc {
|
||||||
/// Returns best Substrate block that PoA chain knows of.
|
/// Returns best Substrate block that PoA chain knows of.
|
||||||
async fn best_substrate_block(&self, contract_address: Address) -> RpcResult<SubstrateHeaderId>;
|
async fn best_substrate_block(&self, contract_address: Address) -> RpcResult<RialtoHeaderId>;
|
||||||
|
|
||||||
/// Returns true if Substrate header is known to Ethereum node.
|
/// Returns true if Substrate header is known to Ethereum node.
|
||||||
async fn substrate_header_known(
|
async fn substrate_header_known(
|
||||||
&self,
|
&self,
|
||||||
contract_address: Address,
|
contract_address: Address,
|
||||||
id: SubstrateHeaderId,
|
id: RialtoHeaderId,
|
||||||
) -> RpcResult<(SubstrateHeaderId, bool)>;
|
) -> RpcResult<(RialtoHeaderId, bool)>;
|
||||||
|
|
||||||
/// Submits Substrate headers to Ethereum contract.
|
/// Submits Substrate headers to Ethereum contract.
|
||||||
async fn submit_substrate_headers(
|
async fn submit_substrate_headers(
|
||||||
&self,
|
&self,
|
||||||
params: EthereumSigningParams,
|
params: EthereumSigningParams,
|
||||||
contract_address: Address,
|
contract_address: Address,
|
||||||
headers: Vec<QueuedSubstrateHeader>,
|
headers: Vec<QueuedRialtoHeader>,
|
||||||
) -> SubmittedHeaders<SubstrateHeaderId, RpcError>;
|
) -> SubmittedHeaders<RialtoHeaderId, RpcError>;
|
||||||
|
|
||||||
/// Returns ids of incomplete Substrate headers.
|
/// Returns ids of incomplete Substrate headers.
|
||||||
async fn incomplete_substrate_headers(&self, contract_address: Address) -> RpcResult<HashSet<SubstrateHeaderId>>;
|
async fn incomplete_substrate_headers(&self, contract_address: Address) -> RpcResult<HashSet<RialtoHeaderId>>;
|
||||||
|
|
||||||
/// Complete Substrate header.
|
/// Complete Substrate header.
|
||||||
async fn complete_substrate_header(
|
async fn complete_substrate_header(
|
||||||
&self,
|
&self,
|
||||||
params: EthereumSigningParams,
|
params: EthereumSigningParams,
|
||||||
contract_address: Address,
|
contract_address: Address,
|
||||||
id: SubstrateHeaderId,
|
id: RialtoHeaderId,
|
||||||
justification: GrandpaJustification,
|
justification: Justification,
|
||||||
) -> RpcResult<SubstrateHeaderId>;
|
) -> RpcResult<RialtoHeaderId>;
|
||||||
|
|
||||||
/// Submit ethereum transaction.
|
/// Submit ethereum transaction.
|
||||||
async fn submit_ethereum_transaction(
|
async fn submit_ethereum_transaction(
|
||||||
@@ -88,7 +90,7 @@ pub trait EthereumHighLevelRpc {
|
|||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl EthereumHighLevelRpc for EthereumClient {
|
impl EthereumHighLevelRpc for EthereumClient {
|
||||||
async fn best_substrate_block(&self, contract_address: Address) -> RpcResult<SubstrateHeaderId> {
|
async fn best_substrate_block(&self, contract_address: Address) -> RpcResult<RialtoHeaderId> {
|
||||||
let (encoded_call, call_decoder) = bridge_contract::functions::best_known_header::call();
|
let (encoded_call, call_decoder) = bridge_contract::functions::best_known_header::call();
|
||||||
let call_request = CallRequest {
|
let call_request = CallRequest {
|
||||||
to: Some(contract_address),
|
to: Some(contract_address),
|
||||||
@@ -98,7 +100,7 @@ impl EthereumHighLevelRpc for EthereumClient {
|
|||||||
|
|
||||||
let call_result = self.eth_call(call_request).await?;
|
let call_result = self.eth_call(call_request).await?;
|
||||||
let (number, raw_hash) = call_decoder.decode(&call_result.0)?;
|
let (number, raw_hash) = call_decoder.decode(&call_result.0)?;
|
||||||
let hash = SubstrateHash::decode(&mut &raw_hash[..])?;
|
let hash = rialto_runtime::Hash::decode(&mut &raw_hash[..])?;
|
||||||
|
|
||||||
if number != number.low_u32().into() {
|
if number != number.low_u32().into() {
|
||||||
return Err(RpcError::Ethereum(EthereumNodeError::InvalidSubstrateBlockNumber));
|
return Err(RpcError::Ethereum(EthereumNodeError::InvalidSubstrateBlockNumber));
|
||||||
@@ -110,8 +112,8 @@ impl EthereumHighLevelRpc for EthereumClient {
|
|||||||
async fn substrate_header_known(
|
async fn substrate_header_known(
|
||||||
&self,
|
&self,
|
||||||
contract_address: Address,
|
contract_address: Address,
|
||||||
id: SubstrateHeaderId,
|
id: RialtoHeaderId,
|
||||||
) -> RpcResult<(SubstrateHeaderId, bool)> {
|
) -> RpcResult<(RialtoHeaderId, bool)> {
|
||||||
let (encoded_call, call_decoder) = bridge_contract::functions::is_known_header::call(id.1);
|
let (encoded_call, call_decoder) = bridge_contract::functions::is_known_header::call(id.1);
|
||||||
let call_request = CallRequest {
|
let call_request = CallRequest {
|
||||||
to: Some(contract_address),
|
to: Some(contract_address),
|
||||||
@@ -129,8 +131,8 @@ impl EthereumHighLevelRpc for EthereumClient {
|
|||||||
&self,
|
&self,
|
||||||
params: EthereumSigningParams,
|
params: EthereumSigningParams,
|
||||||
contract_address: Address,
|
contract_address: Address,
|
||||||
headers: Vec<QueuedSubstrateHeader>,
|
headers: Vec<QueuedRialtoHeader>,
|
||||||
) -> SubmittedHeaders<SubstrateHeaderId, RpcError> {
|
) -> SubmittedHeaders<RialtoHeaderId, RpcError> {
|
||||||
// read nonce of signer
|
// read nonce of signer
|
||||||
let address: Address = params.signer.address().as_fixed_bytes().into();
|
let address: Address = params.signer.address().as_fixed_bytes().into();
|
||||||
let nonce = match self.account_nonce(address).await {
|
let nonce = match self.account_nonce(address).await {
|
||||||
@@ -159,7 +161,7 @@ impl EthereumHighLevelRpc for EthereumClient {
|
|||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn incomplete_substrate_headers(&self, contract_address: Address) -> RpcResult<HashSet<SubstrateHeaderId>> {
|
async fn incomplete_substrate_headers(&self, contract_address: Address) -> RpcResult<HashSet<RialtoHeaderId>> {
|
||||||
let (encoded_call, call_decoder) = bridge_contract::functions::incomplete_headers::call();
|
let (encoded_call, call_decoder) = bridge_contract::functions::incomplete_headers::call();
|
||||||
let call_request = CallRequest {
|
let call_request = CallRequest {
|
||||||
to: Some(contract_address),
|
to: Some(contract_address),
|
||||||
@@ -190,9 +192,9 @@ impl EthereumHighLevelRpc for EthereumClient {
|
|||||||
&self,
|
&self,
|
||||||
params: EthereumSigningParams,
|
params: EthereumSigningParams,
|
||||||
contract_address: Address,
|
contract_address: Address,
|
||||||
id: SubstrateHeaderId,
|
id: RialtoHeaderId,
|
||||||
justification: GrandpaJustification,
|
justification: Justification,
|
||||||
) -> RpcResult<SubstrateHeaderId> {
|
) -> RpcResult<RialtoHeaderId> {
|
||||||
let _ = self
|
let _ = self
|
||||||
.submit_ethereum_transaction(
|
.submit_ethereum_transaction(
|
||||||
¶ms,
|
¶ms,
|
||||||
@@ -243,21 +245,21 @@ pub const HEADERS_BATCH: usize = 4;
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
#[cfg_attr(test, derive(Clone))]
|
#[cfg_attr(test, derive(Clone))]
|
||||||
pub struct HeadersBatch {
|
pub struct HeadersBatch {
|
||||||
pub header1: QueuedSubstrateHeader,
|
pub header1: QueuedRialtoHeader,
|
||||||
pub header2: Option<QueuedSubstrateHeader>,
|
pub header2: Option<QueuedRialtoHeader>,
|
||||||
pub header3: Option<QueuedSubstrateHeader>,
|
pub header3: Option<QueuedRialtoHeader>,
|
||||||
pub header4: Option<QueuedSubstrateHeader>,
|
pub header4: Option<QueuedRialtoHeader>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HeadersBatch {
|
impl HeadersBatch {
|
||||||
/// Create new headers from given header & ids collections.
|
/// Create new headers from given header & ids collections.
|
||||||
///
|
///
|
||||||
/// This method will pop `HEADERS_BATCH` items from both collections
|
/// This method will pop `HEADERS_BATCH` items from both collections
|
||||||
/// and construct `Headers` object and a vector of `SubstrateheaderId`s.
|
/// and construct `Headers` object and a vector of `RialtoHeaderId`s.
|
||||||
pub fn pop_from(
|
pub fn pop_from(
|
||||||
headers: &mut Vec<QueuedSubstrateHeader>,
|
headers: &mut Vec<QueuedRialtoHeader>,
|
||||||
ids: &mut Vec<SubstrateHeaderId>,
|
ids: &mut Vec<RialtoHeaderId>,
|
||||||
) -> Result<(Self, Vec<SubstrateHeaderId>), ()> {
|
) -> Result<(Self, Vec<RialtoHeaderId>), ()> {
|
||||||
if headers.len() != ids.len() {
|
if headers.len() != ids.len() {
|
||||||
log::error!(target: "bridge", "Collection size mismatch ({} vs {})", headers.len(), ids.len());
|
log::error!(target: "bridge", "Collection size mismatch ({} vs {})", headers.len(), ids.len());
|
||||||
return Err(());
|
return Err(());
|
||||||
@@ -287,7 +289,7 @@ impl HeadersBatch {
|
|||||||
/// Returns unified array of headers.
|
/// Returns unified array of headers.
|
||||||
///
|
///
|
||||||
/// The first element is always `Some`.
|
/// The first element is always `Some`.
|
||||||
fn headers(&self) -> [Option<&QueuedSubstrateHeader>; HEADERS_BATCH] {
|
fn headers(&self) -> [Option<&QueuedRialtoHeader>; HEADERS_BATCH] {
|
||||||
[
|
[
|
||||||
Some(&self.header1),
|
Some(&self.header1),
|
||||||
self.header2.as_ref(),
|
self.header2.as_ref(),
|
||||||
@@ -298,7 +300,7 @@ impl HeadersBatch {
|
|||||||
|
|
||||||
/// Encodes all headers. If header is not present an empty vector will be returned.
|
/// Encodes all headers. If header is not present an empty vector will be returned.
|
||||||
pub fn encode(&self) -> [Vec<u8>; HEADERS_BATCH] {
|
pub fn encode(&self) -> [Vec<u8>; HEADERS_BATCH] {
|
||||||
let encode = |h: &QueuedSubstrateHeader| h.header().encode();
|
let encode = |h: &QueuedRialtoHeader| h.header().encode();
|
||||||
let headers = self.headers();
|
let headers = self.headers();
|
||||||
[
|
[
|
||||||
headers[0].map(encode).unwrap_or_default(),
|
headers[0].map(encode).unwrap_or_default(),
|
||||||
@@ -309,7 +311,7 @@ impl HeadersBatch {
|
|||||||
}
|
}
|
||||||
/// Returns number of contained headers.
|
/// Returns number of contained headers.
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
let is_set = |h: &Option<&QueuedSubstrateHeader>| if h.is_some() { 1 } else { 0 };
|
let is_set = |h: &Option<&QueuedRialtoHeader>| if h.is_some() { 1 } else { 0 };
|
||||||
self.headers().iter().map(is_set).sum()
|
self.headers().iter().map(is_set).sum()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -396,8 +398,8 @@ impl HeadersSubmitter for EthereumHeadersSubmitter {
|
|||||||
/// Submit multiple Substrate headers.
|
/// Submit multiple Substrate headers.
|
||||||
async fn submit_substrate_headers(
|
async fn submit_substrate_headers(
|
||||||
mut header_submitter: impl HeadersSubmitter,
|
mut header_submitter: impl HeadersSubmitter,
|
||||||
mut headers: Vec<QueuedSubstrateHeader>,
|
mut headers: Vec<QueuedRialtoHeader>,
|
||||||
) -> SubmittedHeaders<SubstrateHeaderId, RpcError> {
|
) -> SubmittedHeaders<RialtoHeaderId, RpcError> {
|
||||||
let mut submitted_headers = SubmittedHeaders::default();
|
let mut submitted_headers = SubmittedHeaders::default();
|
||||||
|
|
||||||
let mut ids = headers.iter().map(|header| header.id()).rev().collect::<Vec<_>>();
|
let mut ids = headers.iter().map(|header| header.id()).rev().collect::<Vec<_>>();
|
||||||
@@ -424,8 +426,8 @@ async fn submit_substrate_headers(
|
|||||||
/// Submit 4 Substrate headers in single PoA transaction.
|
/// Submit 4 Substrate headers in single PoA transaction.
|
||||||
async fn submit_substrate_headers_batch(
|
async fn submit_substrate_headers_batch(
|
||||||
header_submitter: &mut impl HeadersSubmitter,
|
header_submitter: &mut impl HeadersSubmitter,
|
||||||
submitted_headers: &mut SubmittedHeaders<SubstrateHeaderId, RpcError>,
|
submitted_headers: &mut SubmittedHeaders<RialtoHeaderId, RpcError>,
|
||||||
mut ids: Vec<SubstrateHeaderId>,
|
mut ids: Vec<RialtoHeaderId>,
|
||||||
mut headers: HeadersBatch,
|
mut headers: HeadersBatch,
|
||||||
) -> Option<RpcError> {
|
) -> Option<RpcError> {
|
||||||
debug_assert_eq!(ids.len(), headers.len(),);
|
debug_assert_eq!(ids.len(), headers.len(),);
|
||||||
@@ -486,12 +488,11 @@ async fn submit_substrate_headers_batch(
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::substrate_types::{Header as SubstrateHeader, Number as SubstrateBlockNumber};
|
|
||||||
use sp_runtime::traits::Header;
|
use sp_runtime::traits::Header;
|
||||||
|
|
||||||
struct TestHeadersSubmitter {
|
struct TestHeadersSubmitter {
|
||||||
incomplete: Vec<SubstrateHeaderId>,
|
incomplete: Vec<RialtoHeaderId>,
|
||||||
failed: Vec<SubstrateHeaderId>,
|
failed: Vec<RialtoHeaderId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
@@ -513,9 +514,9 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn header(number: SubstrateBlockNumber) -> QueuedSubstrateHeader {
|
fn header(number: rialto_runtime::BlockNumber) -> QueuedRialtoHeader {
|
||||||
QueuedSubstrateHeader::new(
|
QueuedRialtoHeader::new(
|
||||||
SubstrateHeader::new(
|
rialto_runtime::Header::new(
|
||||||
number,
|
number,
|
||||||
Default::default(),
|
Default::default(),
|
||||||
Default::default(),
|
Default::default(),
|
||||||
|
|||||||
@@ -15,16 +15,17 @@
|
|||||||
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use crate::ethereum_client::{bridge_contract, EthereumHighLevelRpc};
|
use crate::ethereum_client::{bridge_contract, EthereumHighLevelRpc};
|
||||||
use crate::instances::BridgeInstance;
|
use crate::rpc_errors::RpcError;
|
||||||
use crate::rpc::SubstrateRpc;
|
|
||||||
use crate::substrate_client::{SubstrateConnectionParams, SubstrateRpcClient};
|
|
||||||
use crate::substrate_types::{Hash as SubstrateHash, Header as SubstrateHeader, SubstrateHeaderId};
|
|
||||||
|
|
||||||
use codec::{Decode, Encode};
|
use codec::{Decode, Encode};
|
||||||
use num_traits::Zero;
|
use num_traits::Zero;
|
||||||
use relay_ethereum_client::{
|
use relay_ethereum_client::{
|
||||||
Client as EthereumClient, ConnectionParams as EthereumConnectionParams, SigningParams as EthereumSigningParams,
|
Client as EthereumClient, ConnectionParams as EthereumConnectionParams, SigningParams as EthereumSigningParams,
|
||||||
};
|
};
|
||||||
|
use relay_rialto_client::{HeaderId as RialtoHeaderId, Rialto};
|
||||||
|
use relay_substrate_client::{
|
||||||
|
Client as SubstrateClient, ConnectionParams as SubstrateConnectionParams, OpaqueGrandpaAuthoritiesSet,
|
||||||
|
};
|
||||||
use relay_utils::HeaderId;
|
use relay_utils::HeaderId;
|
||||||
|
|
||||||
/// Ethereum synchronization parameters.
|
/// Ethereum synchronization parameters.
|
||||||
@@ -44,8 +45,6 @@ pub struct EthereumDeployContractParams {
|
|||||||
pub sub_initial_authorities_set: Option<Vec<u8>>,
|
pub sub_initial_authorities_set: Option<Vec<u8>>,
|
||||||
/// Initial header.
|
/// Initial header.
|
||||||
pub sub_initial_header: Option<Vec<u8>>,
|
pub sub_initial_header: Option<Vec<u8>>,
|
||||||
/// Instance of the bridge pallet being synchronized.
|
|
||||||
pub instance: Box<dyn BridgeInstance>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Deploy Bridge contract on Ethereum chain.
|
/// Deploy Bridge contract on Ethereum chain.
|
||||||
@@ -56,7 +55,6 @@ pub fn run(params: EthereumDeployContractParams) {
|
|||||||
eth_params,
|
eth_params,
|
||||||
eth_sign,
|
eth_sign,
|
||||||
sub_params,
|
sub_params,
|
||||||
instance,
|
|
||||||
sub_initial_authorities_set_id,
|
sub_initial_authorities_set_id,
|
||||||
sub_initial_authorities_set,
|
sub_initial_authorities_set,
|
||||||
sub_initial_header,
|
sub_initial_header,
|
||||||
@@ -65,7 +63,7 @@ pub fn run(params: EthereumDeployContractParams) {
|
|||||||
|
|
||||||
let result = local_pool.run_until(async move {
|
let result = local_pool.run_until(async move {
|
||||||
let eth_client = EthereumClient::new(eth_params);
|
let eth_client = EthereumClient::new(eth_params);
|
||||||
let sub_client = SubstrateRpcClient::new(sub_params, instance).await?;
|
let sub_client = SubstrateClient::<Rialto>::new(sub_params).await.map_err(RpcError::Substrate)?;
|
||||||
|
|
||||||
let (initial_header_id, initial_header) = prepare_initial_header(&sub_client, sub_initial_header).await?;
|
let (initial_header_id, initial_header) = prepare_initial_header(&sub_client, sub_initial_header).await?;
|
||||||
let initial_set_id = sub_initial_authorities_set_id.unwrap_or(0);
|
let initial_set_id = sub_initial_authorities_set_id.unwrap_or(0);
|
||||||
@@ -102,11 +100,11 @@ pub fn run(params: EthereumDeployContractParams) {
|
|||||||
|
|
||||||
/// Prepare initial header.
|
/// Prepare initial header.
|
||||||
async fn prepare_initial_header(
|
async fn prepare_initial_header(
|
||||||
sub_client: &SubstrateRpcClient,
|
sub_client: &SubstrateClient<Rialto>,
|
||||||
sub_initial_header: Option<Vec<u8>>,
|
sub_initial_header: Option<Vec<u8>>,
|
||||||
) -> Result<(SubstrateHeaderId, Vec<u8>), String> {
|
) -> Result<(RialtoHeaderId, Vec<u8>), String> {
|
||||||
match sub_initial_header {
|
match sub_initial_header {
|
||||||
Some(raw_initial_header) => match SubstrateHeader::decode(&mut &raw_initial_header[..]) {
|
Some(raw_initial_header) => match rialto_runtime::Header::decode(&mut &raw_initial_header[..]) {
|
||||||
Ok(initial_header) => Ok((
|
Ok(initial_header) => Ok((
|
||||||
HeaderId(initial_header.number, initial_header.hash()),
|
HeaderId(initial_header.number, initial_header.hash()),
|
||||||
raw_initial_header,
|
raw_initial_header,
|
||||||
@@ -124,10 +122,10 @@ async fn prepare_initial_header(
|
|||||||
|
|
||||||
/// Prepare initial GRANDPA authorities set.
|
/// Prepare initial GRANDPA authorities set.
|
||||||
async fn prepare_initial_authorities_set(
|
async fn prepare_initial_authorities_set(
|
||||||
sub_client: &SubstrateRpcClient,
|
sub_client: &SubstrateClient<Rialto>,
|
||||||
sub_initial_header_hash: SubstrateHash,
|
sub_initial_header_hash: rialto_runtime::Hash,
|
||||||
sub_initial_authorities_set: Option<Vec<u8>>,
|
sub_initial_authorities_set: Option<Vec<u8>>,
|
||||||
) -> Result<Vec<u8>, String> {
|
) -> Result<OpaqueGrandpaAuthoritiesSet, String> {
|
||||||
let initial_authorities_set = match sub_initial_authorities_set {
|
let initial_authorities_set = match sub_initial_authorities_set {
|
||||||
Some(initial_authorities_set) => Ok(initial_authorities_set),
|
Some(initial_authorities_set) => Ok(initial_authorities_set),
|
||||||
None => sub_client.grandpa_authorities_set(sub_initial_header_hash).await,
|
None => sub_client.grandpa_authorities_set(sub_initial_header_hash).await,
|
||||||
|
|||||||
@@ -17,11 +17,8 @@
|
|||||||
//! Relaying proofs of PoA -> Substrate exchange transactions.
|
//! Relaying proofs of PoA -> Substrate exchange transactions.
|
||||||
|
|
||||||
use crate::instances::BridgeInstance;
|
use crate::instances::BridgeInstance;
|
||||||
use crate::rpc::SubstrateRpc;
|
use crate::rialto_client::{SubmitEthereumExchangeTransactionProof, SubstrateHighLevelRpc};
|
||||||
use crate::rpc_errors::RpcError;
|
use crate::rpc_errors::RpcError;
|
||||||
use crate::substrate_client::{
|
|
||||||
SubmitEthereumExchangeTransactionProof, SubstrateConnectionParams, SubstrateRpcClient, SubstrateSigningParams,
|
|
||||||
};
|
|
||||||
use crate::substrate_types::into_substrate_ethereum_receipt;
|
use crate::substrate_types::into_substrate_ethereum_receipt;
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
@@ -38,9 +35,11 @@ use relay_ethereum_client::{
|
|||||||
},
|
},
|
||||||
Client as EthereumClient, ConnectionParams as EthereumConnectionParams,
|
Client as EthereumClient, ConnectionParams as EthereumConnectionParams,
|
||||||
};
|
};
|
||||||
|
use relay_rialto_client::{Rialto, SigningParams as RialtoSigningParams};
|
||||||
|
use relay_substrate_client::{Client as SubstrateClient, ConnectionParams as SubstrateConnectionParams};
|
||||||
use relay_utils::{metrics::MetricsParams, HeaderId};
|
use relay_utils::{metrics::MetricsParams, HeaderId};
|
||||||
use rialto_runtime::exchange::EthereumTransactionInclusionProof;
|
use rialto_runtime::exchange::EthereumTransactionInclusionProof;
|
||||||
use std::time::Duration;
|
use std::{sync::Arc, time::Duration};
|
||||||
|
|
||||||
/// Interval at which we ask Ethereum node for updates.
|
/// Interval at which we ask Ethereum node for updates.
|
||||||
const ETHEREUM_TICK_INTERVAL: Duration = Duration::from_secs(10);
|
const ETHEREUM_TICK_INTERVAL: Duration = Duration::from_secs(10);
|
||||||
@@ -64,13 +63,13 @@ pub struct EthereumExchangeParams {
|
|||||||
/// Substrate connection params.
|
/// Substrate connection params.
|
||||||
pub sub_params: SubstrateConnectionParams,
|
pub sub_params: SubstrateConnectionParams,
|
||||||
/// Substrate signing params.
|
/// Substrate signing params.
|
||||||
pub sub_sign: SubstrateSigningParams,
|
pub sub_sign: RialtoSigningParams,
|
||||||
/// Relay working mode.
|
/// Relay working mode.
|
||||||
pub mode: ExchangeRelayMode,
|
pub mode: ExchangeRelayMode,
|
||||||
/// Metrics parameters.
|
/// Metrics parameters.
|
||||||
pub metrics_params: Option<MetricsParams>,
|
pub metrics_params: Option<MetricsParams>,
|
||||||
/// Instance of the bridge pallet being synchronized.
|
/// Instance of the bridge pallet being synchronized.
|
||||||
pub instance: Box<dyn BridgeInstance>,
|
pub instance: Arc<dyn BridgeInstance>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ethereum to Substrate exchange pipeline.
|
/// Ethereum to Substrate exchange pipeline.
|
||||||
@@ -201,8 +200,9 @@ impl SourceClient<EthereumToSubstrateExchange> for EthereumTransactionsSource {
|
|||||||
|
|
||||||
/// Substrate node as transactions proof target.
|
/// Substrate node as transactions proof target.
|
||||||
struct SubstrateTransactionsTarget {
|
struct SubstrateTransactionsTarget {
|
||||||
client: SubstrateRpcClient,
|
client: SubstrateClient<Rialto>,
|
||||||
sign_params: SubstrateSigningParams,
|
sign_params: RialtoSigningParams,
|
||||||
|
bridge_instance: Arc<dyn BridgeInstance>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
@@ -254,8 +254,10 @@ impl TargetClient<EthereumToSubstrateExchange> for SubstrateTransactionsTarget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn submit_transaction_proof(&self, proof: EthereumTransactionInclusionProof) -> Result<(), Self::Error> {
|
async fn submit_transaction_proof(&self, proof: EthereumTransactionInclusionProof) -> Result<(), Self::Error> {
|
||||||
let sign_params = self.sign_params.clone();
|
let (sign_params, bridge_instance) = (self.sign_params.clone(), self.bridge_instance.clone());
|
||||||
self.client.submit_exchange_transaction_proof(sign_params, proof).await
|
self.client
|
||||||
|
.submit_exchange_transaction_proof(sign_params, bridge_instance, proof)
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -283,12 +285,15 @@ fn run_single_transaction_relay(params: EthereumExchangeParams, eth_tx_hash: H25
|
|||||||
|
|
||||||
let result = local_pool.run_until(async move {
|
let result = local_pool.run_until(async move {
|
||||||
let eth_client = EthereumClient::new(eth_params);
|
let eth_client = EthereumClient::new(eth_params);
|
||||||
let sub_client = SubstrateRpcClient::new(sub_params, instance).await?;
|
let sub_client = SubstrateClient::<Rialto>::new(sub_params)
|
||||||
|
.await
|
||||||
|
.map_err(RpcError::Substrate)?;
|
||||||
|
|
||||||
let source = EthereumTransactionsSource { client: eth_client };
|
let source = EthereumTransactionsSource { client: eth_client };
|
||||||
let target = SubstrateTransactionsTarget {
|
let target = SubstrateTransactionsTarget {
|
||||||
client: sub_client,
|
client: sub_client,
|
||||||
sign_params: sub_sign,
|
sign_params: sub_sign,
|
||||||
|
bridge_instance: instance,
|
||||||
};
|
};
|
||||||
|
|
||||||
relay_single_transaction_proof(&source, &target, eth_tx_hash).await
|
relay_single_transaction_proof(&source, &target, eth_tx_hash).await
|
||||||
@@ -326,7 +331,7 @@ fn run_auto_transactions_relay_loop(params: EthereumExchangeParams, eth_start_wi
|
|||||||
|
|
||||||
let do_run_loop = move || -> Result<(), String> {
|
let do_run_loop = move || -> Result<(), String> {
|
||||||
let eth_client = EthereumClient::new(eth_params);
|
let eth_client = EthereumClient::new(eth_params);
|
||||||
let sub_client = async_std::task::block_on(SubstrateRpcClient::new(sub_params, instance))
|
let sub_client = async_std::task::block_on(SubstrateClient::<Rialto>::new(sub_params))
|
||||||
.map_err(|err| format!("Error starting Substrate client: {:?}", err))?;
|
.map_err(|err| format!("Error starting Substrate client: {:?}", err))?;
|
||||||
|
|
||||||
let eth_start_with_block_number = match eth_start_with_block_number {
|
let eth_start_with_block_number = match eth_start_with_block_number {
|
||||||
@@ -349,6 +354,7 @@ fn run_auto_transactions_relay_loop(params: EthereumExchangeParams, eth_start_wi
|
|||||||
SubstrateTransactionsTarget {
|
SubstrateTransactionsTarget {
|
||||||
client: sub_client,
|
client: sub_client,
|
||||||
sign_params: sub_sign,
|
sign_params: sub_sign,
|
||||||
|
bridge_instance: instance,
|
||||||
},
|
},
|
||||||
metrics_params,
|
metrics_params,
|
||||||
futures::future::pending(),
|
futures::future::pending(),
|
||||||
|
|||||||
@@ -14,15 +14,12 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
//! Ethereum PoA -> Substrate synchronization.
|
//! Ethereum PoA -> Rialto-Substrate synchronization.
|
||||||
|
|
||||||
use crate::ethereum_client::EthereumHighLevelRpc;
|
use crate::ethereum_client::EthereumHighLevelRpc;
|
||||||
use crate::instances::BridgeInstance;
|
use crate::instances::BridgeInstance;
|
||||||
use crate::rpc::SubstrateRpc;
|
use crate::rialto_client::{SubmitEthereumHeaders, SubstrateHighLevelRpc};
|
||||||
use crate::rpc_errors::RpcError;
|
use crate::rpc_errors::RpcError;
|
||||||
use crate::substrate_client::{
|
|
||||||
SubmitEthereumHeaders, SubstrateConnectionParams, SubstrateRpcClient, SubstrateSigningParams,
|
|
||||||
};
|
|
||||||
use crate::substrate_types::{into_substrate_ethereum_header, into_substrate_ethereum_receipts};
|
use crate::substrate_types::{into_substrate_ethereum_header, into_substrate_ethereum_receipts};
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
@@ -36,10 +33,12 @@ use relay_ethereum_client::{
|
|||||||
types::{HeaderHash, HeaderId as EthereumHeaderId, Receipt, SyncHeader as Header},
|
types::{HeaderHash, HeaderId as EthereumHeaderId, Receipt, SyncHeader as Header},
|
||||||
Client as EthereumClient, ConnectionParams as EthereumConnectionParams,
|
Client as EthereumClient, ConnectionParams as EthereumConnectionParams,
|
||||||
};
|
};
|
||||||
|
use relay_rialto_client::{Rialto, SigningParams as RialtoSigningParams};
|
||||||
|
use relay_substrate_client::{Client as SubstrateClient, ConnectionParams as SubstrateConnectionParams};
|
||||||
use relay_utils::metrics::MetricsParams;
|
use relay_utils::metrics::MetricsParams;
|
||||||
|
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::{collections::HashSet, time::Duration};
|
use std::{collections::HashSet, sync::Arc, time::Duration};
|
||||||
|
|
||||||
pub mod consts {
|
pub mod consts {
|
||||||
use super::*;
|
use super::*;
|
||||||
@@ -70,13 +69,13 @@ pub struct EthereumSyncParams {
|
|||||||
/// Substrate connection params.
|
/// Substrate connection params.
|
||||||
pub sub_params: SubstrateConnectionParams,
|
pub sub_params: SubstrateConnectionParams,
|
||||||
/// Substrate signing params.
|
/// Substrate signing params.
|
||||||
pub sub_sign: SubstrateSigningParams,
|
pub sub_sign: RialtoSigningParams,
|
||||||
/// Synchronization parameters.
|
/// Synchronization parameters.
|
||||||
pub sync_params: HeadersSyncParams,
|
pub sync_params: HeadersSyncParams,
|
||||||
/// Metrics parameters.
|
/// Metrics parameters.
|
||||||
pub metrics_params: Option<MetricsParams>,
|
pub metrics_params: Option<MetricsParams>,
|
||||||
/// Instance of the bridge pallet being synchronized.
|
/// Instance of the bridge pallet being synchronized.
|
||||||
pub instance: Box<dyn BridgeInstance>,
|
pub instance: Arc<dyn BridgeInstance>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ethereum synchronization pipeline.
|
/// Ethereum synchronization pipeline.
|
||||||
@@ -158,19 +157,27 @@ impl SourceClient<EthereumHeadersSyncPipeline> for EthereumHeadersSource {
|
|||||||
|
|
||||||
struct SubstrateHeadersTarget {
|
struct SubstrateHeadersTarget {
|
||||||
/// Substrate node client.
|
/// Substrate node client.
|
||||||
client: SubstrateRpcClient,
|
client: SubstrateClient<Rialto>,
|
||||||
/// Whether we want to submit signed (true), or unsigned (false) transactions.
|
/// Whether we want to submit signed (true), or unsigned (false) transactions.
|
||||||
sign_transactions: bool,
|
sign_transactions: bool,
|
||||||
/// Substrate signing params.
|
/// Substrate signing params.
|
||||||
sign_params: SubstrateSigningParams,
|
sign_params: RialtoSigningParams,
|
||||||
|
/// Bridge instance used in Ethereum to Substrate sync.
|
||||||
|
bridge_instance: Arc<dyn BridgeInstance>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SubstrateHeadersTarget {
|
impl SubstrateHeadersTarget {
|
||||||
fn new(client: SubstrateRpcClient, sign_transactions: bool, sign_params: SubstrateSigningParams) -> Self {
|
fn new(
|
||||||
|
client: SubstrateClient<Rialto>,
|
||||||
|
sign_transactions: bool,
|
||||||
|
sign_params: RialtoSigningParams,
|
||||||
|
bridge_instance: Arc<dyn BridgeInstance>,
|
||||||
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
client,
|
client,
|
||||||
sign_transactions,
|
sign_transactions,
|
||||||
sign_params,
|
sign_params,
|
||||||
|
bridge_instance,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -191,9 +198,13 @@ impl TargetClient<EthereumHeadersSyncPipeline> for SubstrateHeadersTarget {
|
|||||||
&self,
|
&self,
|
||||||
headers: Vec<QueuedEthereumHeader>,
|
headers: Vec<QueuedEthereumHeader>,
|
||||||
) -> SubmittedHeaders<EthereumHeaderId, Self::Error> {
|
) -> SubmittedHeaders<EthereumHeaderId, Self::Error> {
|
||||||
let (sign_params, sign_transactions) = (self.sign_params.clone(), self.sign_transactions);
|
let (sign_params, bridge_instance, sign_transactions) = (
|
||||||
|
self.sign_params.clone(),
|
||||||
|
self.bridge_instance.clone(),
|
||||||
|
self.sign_transactions,
|
||||||
|
);
|
||||||
self.client
|
self.client
|
||||||
.submit_ethereum_headers(sign_params, headers, sign_transactions)
|
.submit_ethereum_headers(sign_params, bridge_instance, headers, sign_transactions)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -228,7 +239,7 @@ pub fn run(params: EthereumSyncParams) -> Result<(), RpcError> {
|
|||||||
} = params;
|
} = params;
|
||||||
|
|
||||||
let eth_client = EthereumClient::new(eth_params);
|
let eth_client = EthereumClient::new(eth_params);
|
||||||
let sub_client = async_std::task::block_on(async { SubstrateRpcClient::new(sub_params, instance).await })?;
|
let sub_client = async_std::task::block_on(async { SubstrateClient::<Rialto>::new(sub_params).await })?;
|
||||||
|
|
||||||
let sign_sub_transactions = match sync_params.target_tx_mode {
|
let sign_sub_transactions = match sync_params.target_tx_mode {
|
||||||
TargetTransactionMode::Signed | TargetTransactionMode::Backup => true,
|
TargetTransactionMode::Signed | TargetTransactionMode::Backup => true,
|
||||||
@@ -236,7 +247,7 @@ pub fn run(params: EthereumSyncParams) -> Result<(), RpcError> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let source = EthereumHeadersSource::new(eth_client);
|
let source = EthereumHeadersSource::new(eth_client);
|
||||||
let target = SubstrateHeadersTarget::new(sub_client, sign_sub_transactions, sub_sign);
|
let target = SubstrateHeadersTarget::new(sub_client, sign_sub_transactions, sub_sign, instance);
|
||||||
|
|
||||||
headers_relay::sync_loop::run(
|
headers_relay::sync_loop::run(
|
||||||
source,
|
source,
|
||||||
|
|||||||
@@ -44,9 +44,9 @@ pub trait BridgeInstance: Send + Sync + std::fmt::Debug {
|
|||||||
|
|
||||||
/// Corresponds to the Rialto instance used in the bridge runtime.
|
/// Corresponds to the Rialto instance used in the bridge runtime.
|
||||||
#[derive(Default, Clone, Debug)]
|
#[derive(Default, Clone, Debug)]
|
||||||
pub struct Rialto;
|
pub struct RialtoPoA;
|
||||||
|
|
||||||
impl BridgeInstance for Rialto {
|
impl BridgeInstance for RialtoPoA {
|
||||||
fn build_signed_header_call(&self, headers: Vec<QueuedEthereumHeader>) -> Call {
|
fn build_signed_header_call(&self, headers: Vec<QueuedEthereumHeader>) -> Call {
|
||||||
let pallet_call = rialto_runtime::BridgeEthPoACall::import_signed_headers(
|
let pallet_call = rialto_runtime::BridgeEthPoACall::import_signed_headers(
|
||||||
headers
|
headers
|
||||||
|
|||||||
@@ -22,9 +22,8 @@ mod ethereum_exchange;
|
|||||||
mod ethereum_exchange_submit;
|
mod ethereum_exchange_submit;
|
||||||
mod ethereum_sync_loop;
|
mod ethereum_sync_loop;
|
||||||
mod instances;
|
mod instances;
|
||||||
mod rpc;
|
mod rialto_client;
|
||||||
mod rpc_errors;
|
mod rpc_errors;
|
||||||
mod substrate_client;
|
|
||||||
mod substrate_sync_loop;
|
mod substrate_sync_loop;
|
||||||
mod substrate_types;
|
mod substrate_types;
|
||||||
|
|
||||||
@@ -34,16 +33,17 @@ use ethereum_exchange_submit::EthereumExchangeSubmitParams;
|
|||||||
use ethereum_sync_loop::EthereumSyncParams;
|
use ethereum_sync_loop::EthereumSyncParams;
|
||||||
use headers_relay::sync::TargetTransactionMode;
|
use headers_relay::sync::TargetTransactionMode;
|
||||||
use hex_literal::hex;
|
use hex_literal::hex;
|
||||||
use instances::{BridgeInstance, Kovan, Rialto};
|
use instances::{BridgeInstance, Kovan, RialtoPoA};
|
||||||
use parity_crypto::publickey::{KeyPair, Secret};
|
use parity_crypto::publickey::{KeyPair, Secret};
|
||||||
use relay_utils::metrics::MetricsParams;
|
use relay_utils::metrics::MetricsParams;
|
||||||
use sp_core::crypto::Pair;
|
use sp_core::crypto::Pair;
|
||||||
use substrate_client::{SubstrateConnectionParams, SubstrateSigningParams};
|
|
||||||
use substrate_sync_loop::SubstrateSyncParams;
|
use substrate_sync_loop::SubstrateSyncParams;
|
||||||
|
|
||||||
use headers_relay::sync::HeadersSyncParams;
|
use headers_relay::sync::HeadersSyncParams;
|
||||||
use relay_ethereum_client::{ConnectionParams as EthereumConnectionParams, SigningParams as EthereumSigningParams};
|
use relay_ethereum_client::{ConnectionParams as EthereumConnectionParams, SigningParams as EthereumSigningParams};
|
||||||
use std::io::Write;
|
use relay_rialto_client::SigningParams as RialtoSigningParams;
|
||||||
|
use relay_substrate_client::ConnectionParams as SubstrateConnectionParams;
|
||||||
|
use std::{io::Write, sync::Arc};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
initialize();
|
initialize();
|
||||||
@@ -197,8 +197,8 @@ fn substrate_connection_params(matches: &clap::ArgMatches) -> Result<SubstrateCo
|
|||||||
Ok(params)
|
Ok(params)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn substrate_signing_params(matches: &clap::ArgMatches) -> Result<SubstrateSigningParams, String> {
|
fn rialto_signing_params(matches: &clap::ArgMatches) -> Result<RialtoSigningParams, String> {
|
||||||
let mut params = SubstrateSigningParams::default();
|
let mut params = RialtoSigningParams::default();
|
||||||
if let Some(sub_signer) = matches.value_of("sub-signer") {
|
if let Some(sub_signer) = matches.value_of("sub-signer") {
|
||||||
let sub_signer_password = matches.value_of("sub-signer-password");
|
let sub_signer_password = matches.value_of("sub-signer-password");
|
||||||
params.signer = sp_core::sr25519::Pair::from_string(sub_signer, sub_signer_password)
|
params.signer = sp_core::sr25519::Pair::from_string(sub_signer, sub_signer_password)
|
||||||
@@ -235,7 +235,7 @@ fn ethereum_sync_params(matches: &clap::ArgMatches) -> Result<EthereumSyncParams
|
|||||||
let params = EthereumSyncParams {
|
let params = EthereumSyncParams {
|
||||||
eth_params: ethereum_connection_params(matches)?,
|
eth_params: ethereum_connection_params(matches)?,
|
||||||
sub_params: substrate_connection_params(matches)?,
|
sub_params: substrate_connection_params(matches)?,
|
||||||
sub_sign: substrate_signing_params(matches)?,
|
sub_sign: rialto_signing_params(matches)?,
|
||||||
metrics_params: metrics_params(matches)?,
|
metrics_params: metrics_params(matches)?,
|
||||||
instance: instance_params(matches)?,
|
instance: instance_params(matches)?,
|
||||||
sync_params,
|
sync_params,
|
||||||
@@ -263,11 +263,10 @@ fn substrate_sync_params(matches: &clap::ArgMatches) -> Result<SubstrateSyncPara
|
|||||||
eth_params: ethereum_connection_params(matches)?,
|
eth_params: ethereum_connection_params(matches)?,
|
||||||
eth_sign: ethereum_signing_params(matches)?,
|
eth_sign: ethereum_signing_params(matches)?,
|
||||||
metrics_params: metrics_params(matches)?,
|
metrics_params: metrics_params(matches)?,
|
||||||
instance: instance_params(matches)?,
|
|
||||||
sync_params: HeadersSyncParams {
|
sync_params: HeadersSyncParams {
|
||||||
max_future_headers_to_download: MAX_FUTURE_HEADERS_TO_DOWNLOAD,
|
max_future_headers_to_download: MAX_FUTURE_HEADERS_TO_DOWNLOAD,
|
||||||
max_headers_in_submitted_status: MAX_SUBMITTED_HEADERS,
|
max_headers_in_submitted_status: MAX_SUBMITTED_HEADERS,
|
||||||
max_headers_in_single_submit: 4,
|
max_headers_in_single_submit: MAX_SUBMITTED_HEADERS,
|
||||||
max_headers_size_in_single_submit: std::usize::MAX,
|
max_headers_size_in_single_submit: std::usize::MAX,
|
||||||
prune_depth: PRUNE_DEPTH,
|
prune_depth: PRUNE_DEPTH,
|
||||||
target_tx_mode: TargetTransactionMode::Signed,
|
target_tx_mode: TargetTransactionMode::Signed,
|
||||||
@@ -299,7 +298,6 @@ fn ethereum_deploy_contract_params(matches: &clap::ArgMatches) -> Result<Ethereu
|
|||||||
eth_params: ethereum_connection_params(matches)?,
|
eth_params: ethereum_connection_params(matches)?,
|
||||||
eth_sign: ethereum_signing_params(matches)?,
|
eth_sign: ethereum_signing_params(matches)?,
|
||||||
sub_params: substrate_connection_params(matches)?,
|
sub_params: substrate_connection_params(matches)?,
|
||||||
instance: instance_params(matches)?,
|
|
||||||
sub_initial_authorities_set_id,
|
sub_initial_authorities_set_id,
|
||||||
sub_initial_authorities_set,
|
sub_initial_authorities_set,
|
||||||
sub_initial_header,
|
sub_initial_header,
|
||||||
@@ -384,7 +382,7 @@ fn ethereum_exchange_params(matches: &clap::ArgMatches) -> Result<EthereumExchan
|
|||||||
let params = EthereumExchangeParams {
|
let params = EthereumExchangeParams {
|
||||||
eth_params: ethereum_connection_params(matches)?,
|
eth_params: ethereum_connection_params(matches)?,
|
||||||
sub_params: substrate_connection_params(matches)?,
|
sub_params: substrate_connection_params(matches)?,
|
||||||
sub_sign: substrate_signing_params(matches)?,
|
sub_sign: rialto_signing_params(matches)?,
|
||||||
metrics_params: metrics_params(matches)?,
|
metrics_params: metrics_params(matches)?,
|
||||||
instance: instance_params(matches)?,
|
instance: instance_params(matches)?,
|
||||||
mode,
|
mode,
|
||||||
@@ -414,11 +412,11 @@ fn metrics_params(matches: &clap::ArgMatches) -> Result<Option<MetricsParams>, S
|
|||||||
Ok(Some(metrics_params))
|
Ok(Some(metrics_params))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn instance_params(matches: &clap::ArgMatches) -> Result<Box<dyn BridgeInstance>, String> {
|
fn instance_params(matches: &clap::ArgMatches) -> Result<Arc<dyn BridgeInstance>, String> {
|
||||||
let instance: Box<dyn BridgeInstance> = if let Some(instance) = matches.value_of("sub-pallet-instance") {
|
let instance = if let Some(instance) = matches.value_of("sub-pallet-instance") {
|
||||||
match instance.to_lowercase().as_str() {
|
match instance.to_lowercase().as_str() {
|
||||||
"rialto" => Box::new(Rialto::default()),
|
"rialto" => Arc::new(RialtoPoA) as Arc<dyn BridgeInstance>,
|
||||||
"kovan" => Box::new(Kovan::default()),
|
"kovan" => Arc::new(Kovan),
|
||||||
_ => return Err("Unsupported bridge pallet instance".to_string()),
|
_ => return Err("Unsupported bridge pallet instance".to_string()),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -0,0 +1,269 @@
|
|||||||
|
// Copyright 2019-2020 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Parity Bridges Common.
|
||||||
|
|
||||||
|
// Parity Bridges Common is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity Bridges Common is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
use crate::ethereum_sync_loop::QueuedEthereumHeader;
|
||||||
|
use crate::instances::BridgeInstance;
|
||||||
|
use crate::rpc_errors::RpcError;
|
||||||
|
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use bp_eth_poa::AuraHeader as SubstrateEthereumHeader;
|
||||||
|
use codec::{Decode, Encode};
|
||||||
|
use headers_relay::sync_types::SubmittedHeaders;
|
||||||
|
use relay_ethereum_client::types::HeaderId as EthereumHeaderId;
|
||||||
|
use relay_rialto_client::{Rialto, SigningParams as RialtoSigningParams};
|
||||||
|
use relay_substrate_client::{Client as SubstrateClient, TransactionSignScheme};
|
||||||
|
use relay_utils::HeaderId;
|
||||||
|
use sp_core::{crypto::Pair, Bytes};
|
||||||
|
use std::{collections::VecDeque, sync::Arc};
|
||||||
|
|
||||||
|
const ETH_API_IMPORT_REQUIRES_RECEIPTS: &str = "RialtoPoAHeaderApi_is_import_requires_receipts";
|
||||||
|
const ETH_API_IS_KNOWN_BLOCK: &str = "RialtoPoAHeaderApi_is_known_block";
|
||||||
|
const ETH_API_BEST_BLOCK: &str = "RialtoPoAHeaderApi_best_block";
|
||||||
|
const ETH_API_BEST_FINALIZED_BLOCK: &str = "RialtoPoAHeaderApi_finalized_block";
|
||||||
|
const EXCH_API_FILTER_TRANSACTION_PROOF: &str = "RialtoCurrencyExchangeApi_filter_transaction_proof";
|
||||||
|
|
||||||
|
type RpcResult<T> = std::result::Result<T, RpcError>;
|
||||||
|
|
||||||
|
/// A trait which contains methods that work by using multiple low-level RPCs, or more complicated
|
||||||
|
/// interactions involving, for example, an Ethereum bridge module.
|
||||||
|
#[async_trait]
|
||||||
|
pub trait SubstrateHighLevelRpc {
|
||||||
|
/// Returns best Ethereum block that Substrate runtime knows of.
|
||||||
|
async fn best_ethereum_block(&self) -> RpcResult<EthereumHeaderId>;
|
||||||
|
/// Returns best finalized Ethereum block that Substrate runtime knows of.
|
||||||
|
async fn best_ethereum_finalized_block(&self) -> RpcResult<EthereumHeaderId>;
|
||||||
|
/// Returns whether or not transactions receipts are required for Ethereum header submission.
|
||||||
|
async fn ethereum_receipts_required(&self, header: SubstrateEthereumHeader) -> RpcResult<bool>;
|
||||||
|
/// Returns whether or not the given Ethereum header is known to the Substrate runtime.
|
||||||
|
async fn ethereum_header_known(&self, header_id: EthereumHeaderId) -> RpcResult<bool>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl SubstrateHighLevelRpc for SubstrateClient<Rialto> {
|
||||||
|
async fn best_ethereum_block(&self) -> RpcResult<EthereumHeaderId> {
|
||||||
|
let call = ETH_API_BEST_BLOCK.to_string();
|
||||||
|
let data = Bytes(Vec::new());
|
||||||
|
|
||||||
|
let encoded_response = self.state_call(call, data, None).await?;
|
||||||
|
let decoded_response: (u64, bp_eth_poa::H256) = Decode::decode(&mut &encoded_response.0[..])?;
|
||||||
|
|
||||||
|
let best_header_id = HeaderId(decoded_response.0, decoded_response.1);
|
||||||
|
Ok(best_header_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn best_ethereum_finalized_block(&self) -> RpcResult<EthereumHeaderId> {
|
||||||
|
let call = ETH_API_BEST_FINALIZED_BLOCK.to_string();
|
||||||
|
let data = Bytes(Vec::new());
|
||||||
|
|
||||||
|
let encoded_response = self.state_call(call, data, None).await?;
|
||||||
|
let decoded_response: (u64, bp_eth_poa::H256) = Decode::decode(&mut &encoded_response.0[..])?;
|
||||||
|
|
||||||
|
let best_header_id = HeaderId(decoded_response.0, decoded_response.1);
|
||||||
|
Ok(best_header_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn ethereum_receipts_required(&self, header: SubstrateEthereumHeader) -> RpcResult<bool> {
|
||||||
|
let call = ETH_API_IMPORT_REQUIRES_RECEIPTS.to_string();
|
||||||
|
let data = Bytes(header.encode());
|
||||||
|
|
||||||
|
let encoded_response = self.state_call(call, data, None).await?;
|
||||||
|
let receipts_required: bool = Decode::decode(&mut &encoded_response.0[..])?;
|
||||||
|
|
||||||
|
Ok(receipts_required)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The Substrate module could prune old headers. So this function could return false even
|
||||||
|
// if header is synced. And we'll mark corresponding Ethereum header as Orphan.
|
||||||
|
//
|
||||||
|
// But when we read the best header from Substrate next time, we will know that
|
||||||
|
// there's a better header. This Orphan will either be marked as synced, or
|
||||||
|
// eventually pruned.
|
||||||
|
async fn ethereum_header_known(&self, header_id: EthereumHeaderId) -> RpcResult<bool> {
|
||||||
|
let call = ETH_API_IS_KNOWN_BLOCK.to_string();
|
||||||
|
let data = Bytes(header_id.1.encode());
|
||||||
|
|
||||||
|
let encoded_response = self.state_call(call, data, None).await?;
|
||||||
|
let is_known_block: bool = Decode::decode(&mut &encoded_response.0[..])?;
|
||||||
|
|
||||||
|
Ok(is_known_block)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A trait for RPC calls which are used to submit Ethereum headers to a Substrate
|
||||||
|
/// runtime. These are typically calls which use a combination of other low-level RPC
|
||||||
|
/// calls.
|
||||||
|
#[async_trait]
|
||||||
|
pub trait SubmitEthereumHeaders {
|
||||||
|
/// Submits Ethereum header to Substrate runtime.
|
||||||
|
async fn submit_ethereum_headers(
|
||||||
|
&self,
|
||||||
|
params: RialtoSigningParams,
|
||||||
|
instance: Arc<dyn BridgeInstance>,
|
||||||
|
headers: Vec<QueuedEthereumHeader>,
|
||||||
|
sign_transactions: bool,
|
||||||
|
) -> SubmittedHeaders<EthereumHeaderId, RpcError>;
|
||||||
|
|
||||||
|
/// Submits signed Ethereum header to Substrate runtime.
|
||||||
|
async fn submit_signed_ethereum_headers(
|
||||||
|
&self,
|
||||||
|
params: RialtoSigningParams,
|
||||||
|
instance: Arc<dyn BridgeInstance>,
|
||||||
|
headers: Vec<QueuedEthereumHeader>,
|
||||||
|
) -> SubmittedHeaders<EthereumHeaderId, RpcError>;
|
||||||
|
|
||||||
|
/// Submits unsigned Ethereum header to Substrate runtime.
|
||||||
|
async fn submit_unsigned_ethereum_headers(
|
||||||
|
&self,
|
||||||
|
instance: Arc<dyn BridgeInstance>,
|
||||||
|
headers: Vec<QueuedEthereumHeader>,
|
||||||
|
) -> SubmittedHeaders<EthereumHeaderId, RpcError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl SubmitEthereumHeaders for SubstrateClient<Rialto> {
|
||||||
|
async fn submit_ethereum_headers(
|
||||||
|
&self,
|
||||||
|
params: RialtoSigningParams,
|
||||||
|
instance: Arc<dyn BridgeInstance>,
|
||||||
|
headers: Vec<QueuedEthereumHeader>,
|
||||||
|
sign_transactions: bool,
|
||||||
|
) -> SubmittedHeaders<EthereumHeaderId, RpcError> {
|
||||||
|
if sign_transactions {
|
||||||
|
self.submit_signed_ethereum_headers(params, instance, headers).await
|
||||||
|
} else {
|
||||||
|
self.submit_unsigned_ethereum_headers(instance, headers).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn submit_signed_ethereum_headers(
|
||||||
|
&self,
|
||||||
|
params: RialtoSigningParams,
|
||||||
|
instance: Arc<dyn BridgeInstance>,
|
||||||
|
headers: Vec<QueuedEthereumHeader>,
|
||||||
|
) -> SubmittedHeaders<EthereumHeaderId, RpcError> {
|
||||||
|
let ids = headers.iter().map(|header| header.id()).collect();
|
||||||
|
let submission_result = async {
|
||||||
|
let account_id = params.signer.public().as_array_ref().clone().into();
|
||||||
|
let nonce = self.next_account_index(account_id).await?;
|
||||||
|
|
||||||
|
let call = instance.build_signed_header_call(headers);
|
||||||
|
let transaction = Rialto::sign_transaction(self, ¶ms.signer, nonce, call);
|
||||||
|
|
||||||
|
let _ = self.submit_extrinsic(Bytes(transaction.encode())).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
.await;
|
||||||
|
|
||||||
|
match submission_result {
|
||||||
|
Ok(_) => SubmittedHeaders {
|
||||||
|
submitted: ids,
|
||||||
|
incomplete: Vec::new(),
|
||||||
|
rejected: Vec::new(),
|
||||||
|
fatal_error: None,
|
||||||
|
},
|
||||||
|
Err(error) => SubmittedHeaders {
|
||||||
|
submitted: Vec::new(),
|
||||||
|
incomplete: Vec::new(),
|
||||||
|
rejected: ids,
|
||||||
|
fatal_error: Some(error),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn submit_unsigned_ethereum_headers(
|
||||||
|
&self,
|
||||||
|
instance: Arc<dyn BridgeInstance>,
|
||||||
|
headers: Vec<QueuedEthereumHeader>,
|
||||||
|
) -> SubmittedHeaders<EthereumHeaderId, RpcError> {
|
||||||
|
let mut ids = headers.iter().map(|header| header.id()).collect::<VecDeque<_>>();
|
||||||
|
let mut submitted_headers = SubmittedHeaders::default();
|
||||||
|
|
||||||
|
for header in headers {
|
||||||
|
let id = ids.pop_front().expect("both collections have same size; qed");
|
||||||
|
|
||||||
|
let call = instance.build_unsigned_header_call(header);
|
||||||
|
let transaction = create_unsigned_submit_transaction(call);
|
||||||
|
|
||||||
|
match self.submit_extrinsic(Bytes(transaction.encode())).await {
|
||||||
|
Ok(_) => submitted_headers.submitted.push(id),
|
||||||
|
Err(error) => {
|
||||||
|
submitted_headers.rejected.push(id);
|
||||||
|
submitted_headers.rejected.extend(ids);
|
||||||
|
submitted_headers.fatal_error = Some(error.into());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
submitted_headers
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A trait for RPC calls which are used to submit proof of Ethereum exchange transaction to a
|
||||||
|
/// Substrate runtime. These are typically calls which use a combination of other low-level RPC
|
||||||
|
/// calls.
|
||||||
|
#[async_trait]
|
||||||
|
pub trait SubmitEthereumExchangeTransactionProof {
|
||||||
|
/// Pre-verify Ethereum exchange transaction proof.
|
||||||
|
async fn verify_exchange_transaction_proof(
|
||||||
|
&self,
|
||||||
|
proof: rialto_runtime::exchange::EthereumTransactionInclusionProof,
|
||||||
|
) -> RpcResult<bool>;
|
||||||
|
/// Submits Ethereum exchange transaction proof to Substrate runtime.
|
||||||
|
async fn submit_exchange_transaction_proof(
|
||||||
|
&self,
|
||||||
|
params: RialtoSigningParams,
|
||||||
|
instance: Arc<dyn BridgeInstance>,
|
||||||
|
proof: rialto_runtime::exchange::EthereumTransactionInclusionProof,
|
||||||
|
) -> RpcResult<()>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl SubmitEthereumExchangeTransactionProof for SubstrateClient<Rialto> {
|
||||||
|
async fn verify_exchange_transaction_proof(
|
||||||
|
&self,
|
||||||
|
proof: rialto_runtime::exchange::EthereumTransactionInclusionProof,
|
||||||
|
) -> RpcResult<bool> {
|
||||||
|
let call = EXCH_API_FILTER_TRANSACTION_PROOF.to_string();
|
||||||
|
let data = Bytes(proof.encode());
|
||||||
|
|
||||||
|
let encoded_response = self.state_call(call, data, None).await?;
|
||||||
|
let is_allowed: bool = Decode::decode(&mut &encoded_response.0[..])?;
|
||||||
|
|
||||||
|
Ok(is_allowed)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn submit_exchange_transaction_proof(
|
||||||
|
&self,
|
||||||
|
params: RialtoSigningParams,
|
||||||
|
instance: Arc<dyn BridgeInstance>,
|
||||||
|
proof: rialto_runtime::exchange::EthereumTransactionInclusionProof,
|
||||||
|
) -> RpcResult<()> {
|
||||||
|
let account_id = params.signer.public().as_array_ref().clone().into();
|
||||||
|
let nonce = self.next_account_index(account_id).await?;
|
||||||
|
|
||||||
|
let call = instance.build_currency_exchange_call(proof);
|
||||||
|
let transaction = Rialto::sign_transaction(self, ¶ms.signer, nonce, call);
|
||||||
|
|
||||||
|
let _ = self.submit_extrinsic(Bytes(transaction.encode())).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create unsigned Substrate transaction for submitting Ethereum header.
|
||||||
|
fn create_unsigned_submit_transaction(call: rialto_runtime::Call) -> rialto_runtime::UncheckedExtrinsic {
|
||||||
|
rialto_runtime::UncheckedExtrinsic::new_unsigned(call)
|
||||||
|
}
|
||||||
@@ -1,86 +0,0 @@
|
|||||||
// Copyright 2019-2020 Parity Technologies (UK) Ltd.
|
|
||||||
// This file is part of Parity Bridges Common.
|
|
||||||
|
|
||||||
// Parity Bridges Common is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
|
|
||||||
// Parity Bridges Common is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
//! RPC Module
|
|
||||||
|
|
||||||
#![warn(missing_docs)]
|
|
||||||
// The compiler doesn't think we're using the
|
|
||||||
// code from rpc_api!
|
|
||||||
#![allow(dead_code)]
|
|
||||||
#![allow(unused_variables)]
|
|
||||||
use std::result;
|
|
||||||
|
|
||||||
use crate::rpc_errors::RpcError;
|
|
||||||
use crate::substrate_types::{
|
|
||||||
Hash as SubstrateHash, Header as SubstrateHeader, Number as SubBlockNumber, SignedBlock as SubstrateBlock,
|
|
||||||
};
|
|
||||||
|
|
||||||
use async_trait::async_trait;
|
|
||||||
use bp_eth_poa::AuraHeader as SubstrateEthereumHeader;
|
|
||||||
use relay_ethereum_client::types::{Bytes, HeaderId as EthereumHeaderId};
|
|
||||||
|
|
||||||
type Result<T> = result::Result<T, RpcError>;
|
|
||||||
type GrandpaAuthorityList = Vec<u8>;
|
|
||||||
|
|
||||||
jsonrpsee::rpc_api! {
|
|
||||||
pub(crate) Substrate {
|
|
||||||
#[rpc(method = "chain_getHeader", positional_params)]
|
|
||||||
fn chain_get_header(block_hash: Option<SubstrateHash>) -> SubstrateHeader;
|
|
||||||
#[rpc(method = "chain_getBlock", positional_params)]
|
|
||||||
fn chain_get_block(block_hash: Option<SubstrateHash>) -> SubstrateBlock;
|
|
||||||
#[rpc(method = "chain_getBlockHash", positional_params)]
|
|
||||||
fn chain_get_block_hash(block_number: Option<SubBlockNumber>) -> SubstrateHash;
|
|
||||||
#[rpc(method = "system_accountNextIndex", positional_params)]
|
|
||||||
fn system_account_next_index(account_id: node_primitives::AccountId) -> node_primitives::Index;
|
|
||||||
#[rpc(method = "author_submitExtrinsic", positional_params)]
|
|
||||||
fn author_submit_extrinsic(extrinsic: Bytes) -> SubstrateHash;
|
|
||||||
#[rpc(method = "state_call", positional_params)]
|
|
||||||
fn state_call(method: String, data: Bytes, at_block: Option<SubstrateHash>) -> Bytes;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The API for the supported Substrate RPC methods.
|
|
||||||
#[async_trait]
|
|
||||||
pub trait SubstrateRpc {
|
|
||||||
/// Returns the best Substrate header.
|
|
||||||
async fn best_header(&self) -> Result<SubstrateHeader>;
|
|
||||||
/// Get a Substrate block from its hash.
|
|
||||||
async fn get_block(&self, block_hash: Option<SubstrateHash>) -> Result<SubstrateBlock>;
|
|
||||||
/// Get a Substrate header by its hash.
|
|
||||||
async fn header_by_hash(&self, hash: SubstrateHash) -> Result<SubstrateHeader>;
|
|
||||||
/// Get a Substrate block hash by its number.
|
|
||||||
async fn block_hash_by_number(&self, number: SubBlockNumber) -> Result<SubstrateHash>;
|
|
||||||
/// Get a Substrate header by its number.
|
|
||||||
async fn header_by_number(&self, block_number: SubBlockNumber) -> Result<SubstrateHeader>;
|
|
||||||
/// Get the nonce of the given Substrate account.
|
|
||||||
///
|
|
||||||
/// Note: It's the caller's responsibility to make sure `account` is a valid ss58 address.
|
|
||||||
async fn next_account_index(&self, account: node_primitives::AccountId) -> Result<node_primitives::Index>;
|
|
||||||
/// Returns best Ethereum block that Substrate runtime knows of.
|
|
||||||
async fn best_ethereum_block(&self) -> Result<EthereumHeaderId>;
|
|
||||||
/// Returns best finalized Ethereum block that Substrate runtime knows of.
|
|
||||||
async fn best_ethereum_finalized_block(&self) -> Result<EthereumHeaderId>;
|
|
||||||
/// Returns whether or not transactions receipts are required for Ethereum header submission.
|
|
||||||
async fn ethereum_receipts_required(&self, header: SubstrateEthereumHeader) -> Result<bool>;
|
|
||||||
/// Returns whether or not the given Ethereum header is known to the Substrate runtime.
|
|
||||||
async fn ethereum_header_known(&self, header_id: EthereumHeaderId) -> Result<bool>;
|
|
||||||
/// Submit an extrinsic for inclusion in a block.
|
|
||||||
///
|
|
||||||
/// Note: The given transaction does not need be SCALE encoded beforehand.
|
|
||||||
async fn submit_extrinsic(&self, transaction: Bytes) -> Result<SubstrateHash>;
|
|
||||||
/// Get the GRANDPA authority set at given block.
|
|
||||||
async fn grandpa_authorities_set(&self, block: SubstrateHash) -> Result<GrandpaAuthorityList>;
|
|
||||||
}
|
|
||||||
@@ -14,8 +14,8 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use jsonrpsee::client::RequestError;
|
|
||||||
use relay_ethereum_client::Error as EthereumNodeError;
|
use relay_ethereum_client::Error as EthereumNodeError;
|
||||||
|
use relay_substrate_client::Error as SubstrateNodeError;
|
||||||
use relay_utils::MaybeConnectionError;
|
use relay_utils::MaybeConnectionError;
|
||||||
|
|
||||||
/// Contains common errors that can occur when
|
/// Contains common errors that can occur when
|
||||||
@@ -29,9 +29,6 @@ pub enum RpcError {
|
|||||||
Ethereum(EthereumNodeError),
|
Ethereum(EthereumNodeError),
|
||||||
/// An error occured when interacting with a Substrate node.
|
/// An error occured when interacting with a Substrate node.
|
||||||
Substrate(SubstrateNodeError),
|
Substrate(SubstrateNodeError),
|
||||||
/// An error that can occur when making an HTTP request to
|
|
||||||
/// an JSON-RPC client.
|
|
||||||
Request(RequestError),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<RpcError> for String {
|
impl From<RpcError> for String {
|
||||||
@@ -40,7 +37,6 @@ impl From<RpcError> for String {
|
|||||||
RpcError::Serialization(e) => e.to_string(),
|
RpcError::Serialization(e) => e.to_string(),
|
||||||
RpcError::Ethereum(e) => e.to_string(),
|
RpcError::Ethereum(e) => e.to_string(),
|
||||||
RpcError::Substrate(e) => e.to_string(),
|
RpcError::Substrate(e) => e.to_string(),
|
||||||
RpcError::Request(e) => e.to_string(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -63,12 +59,6 @@ impl From<SubstrateNodeError> for RpcError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<RequestError> for RpcError {
|
|
||||||
fn from(err: RequestError) -> Self {
|
|
||||||
Self::Request(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<ethabi::Error> for RpcError {
|
impl From<ethabi::Error> for RpcError {
|
||||||
fn from(err: ethabi::Error) -> Self {
|
fn from(err: ethabi::Error) -> Self {
|
||||||
Self::Ethereum(EthereumNodeError::ResponseParseFailed(format!("{}", err)))
|
Self::Ethereum(EthereumNodeError::ResponseParseFailed(format!("{}", err)))
|
||||||
@@ -78,8 +68,8 @@ impl From<ethabi::Error> for RpcError {
|
|||||||
impl MaybeConnectionError for RpcError {
|
impl MaybeConnectionError for RpcError {
|
||||||
fn is_connection_error(&self) -> bool {
|
fn is_connection_error(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
RpcError::Request(RequestError::TransportError(_)) => true,
|
|
||||||
RpcError::Ethereum(ref error) => error.is_connection_error(),
|
RpcError::Ethereum(ref error) => error.is_connection_error(),
|
||||||
|
RpcError::Substrate(ref error) => error.is_connection_error(),
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -87,22 +77,6 @@ impl MaybeConnectionError for RpcError {
|
|||||||
|
|
||||||
impl From<codec::Error> for RpcError {
|
impl From<codec::Error> for RpcError {
|
||||||
fn from(err: codec::Error) -> Self {
|
fn from(err: codec::Error) -> Self {
|
||||||
Self::Substrate(SubstrateNodeError::Decoding(err))
|
Self::Substrate(SubstrateNodeError::ResponseParseFailed(err))
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Errors that can occur only when interacting with
|
|
||||||
/// a Substrate node through RPC.
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum SubstrateNodeError {
|
|
||||||
/// The response from the client could not be SCALE decoded.
|
|
||||||
Decoding(codec::Error),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToString for SubstrateNodeError {
|
|
||||||
fn to_string(&self) -> String {
|
|
||||||
match self {
|
|
||||||
Self::Decoding(e) => e.what().to_string(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,410 +0,0 @@
|
|||||||
// Copyright 2019-2020 Parity Technologies (UK) Ltd.
|
|
||||||
// This file is part of Parity Bridges Common.
|
|
||||||
|
|
||||||
// Parity Bridges Common is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
|
|
||||||
// Parity Bridges Common is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
use crate::ethereum_sync_loop::QueuedEthereumHeader;
|
|
||||||
use crate::instances::BridgeInstance;
|
|
||||||
use crate::rpc::{Substrate, SubstrateRpc};
|
|
||||||
use crate::rpc_errors::RpcError;
|
|
||||||
use crate::substrate_types::{Hash, Header as SubstrateHeader, Number, SignedBlock as SignedSubstrateBlock};
|
|
||||||
|
|
||||||
use async_trait::async_trait;
|
|
||||||
use bp_eth_poa::AuraHeader as SubstrateEthereumHeader;
|
|
||||||
use codec::{Decode, Encode};
|
|
||||||
use headers_relay::sync_types::SubmittedHeaders;
|
|
||||||
use jsonrpsee::raw::RawClient;
|
|
||||||
use jsonrpsee::transport::http::HttpTransportClient;
|
|
||||||
use jsonrpsee::Client;
|
|
||||||
use num_traits::Zero;
|
|
||||||
use relay_ethereum_client::types::{Bytes, HeaderId as EthereumHeaderId, H256};
|
|
||||||
use relay_utils::HeaderId;
|
|
||||||
use sp_core::crypto::Pair;
|
|
||||||
use sp_runtime::traits::IdentifyAccount;
|
|
||||||
use std::collections::VecDeque;
|
|
||||||
|
|
||||||
const ETH_API_IMPORT_REQUIRES_RECEIPTS: &str = "RialtoHeaderApi_is_import_requires_receipts";
|
|
||||||
const ETH_API_IS_KNOWN_BLOCK: &str = "RialtoHeaderApi_is_known_block";
|
|
||||||
const ETH_API_BEST_BLOCK: &str = "RialtoHeaderApi_best_block";
|
|
||||||
const ETH_API_BEST_FINALIZED_BLOCK: &str = "RialtoHeaderApi_finalized_block";
|
|
||||||
const EXCH_API_FILTER_TRANSACTION_PROOF: &str = "RialtoCurrencyExchangeApi_filter_transaction_proof";
|
|
||||||
const SUB_API_GRANDPA_AUTHORITIES: &str = "GrandpaApi_grandpa_authorities";
|
|
||||||
|
|
||||||
type RpcResult<T> = std::result::Result<T, RpcError>;
|
|
||||||
type GrandpaAuthorityList = Vec<u8>;
|
|
||||||
|
|
||||||
/// Substrate connection params.
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct SubstrateConnectionParams {
|
|
||||||
/// Substrate RPC host.
|
|
||||||
pub host: String,
|
|
||||||
/// Substrate RPC port.
|
|
||||||
pub port: u16,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for SubstrateConnectionParams {
|
|
||||||
fn default() -> Self {
|
|
||||||
SubstrateConnectionParams {
|
|
||||||
host: "localhost".into(),
|
|
||||||
port: 9933,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Substrate signing params.
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct SubstrateSigningParams {
|
|
||||||
/// Substrate transactions signer.
|
|
||||||
pub signer: sp_core::sr25519::Pair,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::fmt::Debug for SubstrateSigningParams {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
||||||
write!(f, "{}", self.signer.public())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for SubstrateSigningParams {
|
|
||||||
fn default() -> Self {
|
|
||||||
SubstrateSigningParams {
|
|
||||||
signer: sp_keyring::AccountKeyring::Alice.pair(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Substrate client type.
|
|
||||||
pub struct SubstrateRpcClient {
|
|
||||||
/// Substrate RPC client.
|
|
||||||
client: Client,
|
|
||||||
/// Genesis block hash.
|
|
||||||
genesis_hash: H256,
|
|
||||||
/// Instance of the bridge pallet being synchronized.
|
|
||||||
instance: Box<dyn BridgeInstance>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SubstrateRpcClient {
|
|
||||||
/// Returns client that is able to call RPCs on Substrate node.
|
|
||||||
pub async fn new(params: SubstrateConnectionParams, instance: Box<dyn BridgeInstance>) -> RpcResult<Self> {
|
|
||||||
let uri = format!("http://{}:{}", params.host, params.port);
|
|
||||||
let transport = HttpTransportClient::new(&uri);
|
|
||||||
let raw_client = RawClient::new(transport);
|
|
||||||
let client: Client = raw_client.into();
|
|
||||||
|
|
||||||
let number: Number = Zero::zero();
|
|
||||||
let genesis_hash = Substrate::chain_get_block_hash(&client, number).await?;
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
client,
|
|
||||||
genesis_hash,
|
|
||||||
instance,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl SubstrateRpc for SubstrateRpcClient {
|
|
||||||
async fn best_header(&self) -> RpcResult<SubstrateHeader> {
|
|
||||||
Ok(Substrate::chain_get_header(&self.client, None).await?)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn get_block(&self, block_hash: Option<Hash>) -> RpcResult<SignedSubstrateBlock> {
|
|
||||||
Ok(Substrate::chain_get_block(&self.client, block_hash).await?)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn header_by_hash(&self, block_hash: Hash) -> RpcResult<SubstrateHeader> {
|
|
||||||
Ok(Substrate::chain_get_header(&self.client, block_hash).await?)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn block_hash_by_number(&self, number: Number) -> RpcResult<Hash> {
|
|
||||||
Ok(Substrate::chain_get_block_hash(&self.client, number).await?)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn header_by_number(&self, block_number: Number) -> RpcResult<SubstrateHeader> {
|
|
||||||
let block_hash = Self::block_hash_by_number(self, block_number).await?;
|
|
||||||
Ok(Self::header_by_hash(self, block_hash).await?)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn next_account_index(&self, account: node_primitives::AccountId) -> RpcResult<node_primitives::Index> {
|
|
||||||
Ok(Substrate::system_account_next_index(&self.client, account).await?)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn best_ethereum_block(&self) -> RpcResult<EthereumHeaderId> {
|
|
||||||
let call = ETH_API_BEST_BLOCK.to_string();
|
|
||||||
let data = Bytes(Vec::new());
|
|
||||||
|
|
||||||
let encoded_response = Substrate::state_call(&self.client, call, data, None).await?;
|
|
||||||
let decoded_response: (u64, bp_eth_poa::H256) = Decode::decode(&mut &encoded_response.0[..])?;
|
|
||||||
|
|
||||||
let best_header_id = HeaderId(decoded_response.0, decoded_response.1);
|
|
||||||
Ok(best_header_id)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn best_ethereum_finalized_block(&self) -> RpcResult<EthereumHeaderId> {
|
|
||||||
let call = ETH_API_BEST_FINALIZED_BLOCK.to_string();
|
|
||||||
let data = Bytes(Vec::new());
|
|
||||||
|
|
||||||
let encoded_response = Substrate::state_call(&self.client, call, data, None).await?;
|
|
||||||
let decoded_response: (u64, bp_eth_poa::H256) = Decode::decode(&mut &encoded_response.0[..])?;
|
|
||||||
|
|
||||||
let best_header_id = HeaderId(decoded_response.0, decoded_response.1);
|
|
||||||
Ok(best_header_id)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn ethereum_receipts_required(&self, header: SubstrateEthereumHeader) -> RpcResult<bool> {
|
|
||||||
let call = ETH_API_IMPORT_REQUIRES_RECEIPTS.to_string();
|
|
||||||
let data = Bytes(header.encode());
|
|
||||||
|
|
||||||
let encoded_response = Substrate::state_call(&self.client, call, data, None).await?;
|
|
||||||
let receipts_required: bool = Decode::decode(&mut &encoded_response.0[..])?;
|
|
||||||
|
|
||||||
Ok(receipts_required)
|
|
||||||
}
|
|
||||||
|
|
||||||
// The Substrate module could prune old headers. So this function could return false even
|
|
||||||
// if header is synced. And we'll mark corresponding Ethereum header as Orphan.
|
|
||||||
//
|
|
||||||
// But when we read the best header from Substrate next time, we will know that
|
|
||||||
// there's a better header. This Orphan will either be marked as synced, or
|
|
||||||
// eventually pruned.
|
|
||||||
async fn ethereum_header_known(&self, header_id: EthereumHeaderId) -> RpcResult<bool> {
|
|
||||||
let call = ETH_API_IS_KNOWN_BLOCK.to_string();
|
|
||||||
let data = Bytes(header_id.1.encode());
|
|
||||||
|
|
||||||
let encoded_response = Substrate::state_call(&self.client, call, data, None).await?;
|
|
||||||
let is_known_block: bool = Decode::decode(&mut &encoded_response.0[..])?;
|
|
||||||
|
|
||||||
Ok(is_known_block)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn submit_extrinsic(&self, transaction: Bytes) -> RpcResult<Hash> {
|
|
||||||
let tx_hash = Substrate::author_submit_extrinsic(&self.client, transaction).await?;
|
|
||||||
log::trace!(target: "bridge", "Sent transaction to Substrate node: {:?}", tx_hash);
|
|
||||||
Ok(tx_hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn grandpa_authorities_set(&self, block: Hash) -> RpcResult<GrandpaAuthorityList> {
|
|
||||||
let call = SUB_API_GRANDPA_AUTHORITIES.to_string();
|
|
||||||
let data = Bytes(Vec::new());
|
|
||||||
|
|
||||||
let encoded_response = Substrate::state_call(&self.client, call, data, Some(block)).await?;
|
|
||||||
let authority_list = encoded_response.0;
|
|
||||||
|
|
||||||
Ok(authority_list)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A trait for RPC calls which are used to submit Ethereum headers to a Substrate
|
|
||||||
/// runtime. These are typically calls which use a combination of other low-level RPC
|
|
||||||
/// calls.
|
|
||||||
#[async_trait]
|
|
||||||
pub trait SubmitEthereumHeaders: SubstrateRpc {
|
|
||||||
/// Submits Ethereum header to Substrate runtime.
|
|
||||||
async fn submit_ethereum_headers(
|
|
||||||
&self,
|
|
||||||
params: SubstrateSigningParams,
|
|
||||||
headers: Vec<QueuedEthereumHeader>,
|
|
||||||
sign_transactions: bool,
|
|
||||||
) -> SubmittedHeaders<EthereumHeaderId, RpcError>;
|
|
||||||
|
|
||||||
/// Submits signed Ethereum header to Substrate runtime.
|
|
||||||
async fn submit_signed_ethereum_headers(
|
|
||||||
&self,
|
|
||||||
params: SubstrateSigningParams,
|
|
||||||
headers: Vec<QueuedEthereumHeader>,
|
|
||||||
) -> SubmittedHeaders<EthereumHeaderId, RpcError>;
|
|
||||||
|
|
||||||
/// Submits unsigned Ethereum header to Substrate runtime.
|
|
||||||
async fn submit_unsigned_ethereum_headers(
|
|
||||||
&self,
|
|
||||||
headers: Vec<QueuedEthereumHeader>,
|
|
||||||
) -> SubmittedHeaders<EthereumHeaderId, RpcError>;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl SubmitEthereumHeaders for SubstrateRpcClient {
|
|
||||||
async fn submit_ethereum_headers(
|
|
||||||
&self,
|
|
||||||
params: SubstrateSigningParams,
|
|
||||||
headers: Vec<QueuedEthereumHeader>,
|
|
||||||
sign_transactions: bool,
|
|
||||||
) -> SubmittedHeaders<EthereumHeaderId, RpcError> {
|
|
||||||
if sign_transactions {
|
|
||||||
self.submit_signed_ethereum_headers(params, headers).await
|
|
||||||
} else {
|
|
||||||
self.submit_unsigned_ethereum_headers(headers).await
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn submit_signed_ethereum_headers(
|
|
||||||
&self,
|
|
||||||
params: SubstrateSigningParams,
|
|
||||||
headers: Vec<QueuedEthereumHeader>,
|
|
||||||
) -> SubmittedHeaders<EthereumHeaderId, RpcError> {
|
|
||||||
let ids = headers.iter().map(|header| header.id()).collect();
|
|
||||||
let submission_result = async {
|
|
||||||
let account_id = params.signer.public().as_array_ref().clone().into();
|
|
||||||
let nonce = self.next_account_index(account_id).await?;
|
|
||||||
|
|
||||||
let call = self.instance.build_signed_header_call(headers);
|
|
||||||
let transaction = create_signed_submit_transaction(call, ¶ms.signer, nonce, self.genesis_hash);
|
|
||||||
let _ = self.submit_extrinsic(Bytes(transaction.encode())).await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
.await;
|
|
||||||
|
|
||||||
match submission_result {
|
|
||||||
Ok(_) => SubmittedHeaders {
|
|
||||||
submitted: ids,
|
|
||||||
incomplete: Vec::new(),
|
|
||||||
rejected: Vec::new(),
|
|
||||||
fatal_error: None,
|
|
||||||
},
|
|
||||||
Err(error) => SubmittedHeaders {
|
|
||||||
submitted: Vec::new(),
|
|
||||||
incomplete: Vec::new(),
|
|
||||||
rejected: ids,
|
|
||||||
fatal_error: Some(error),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn submit_unsigned_ethereum_headers(
|
|
||||||
&self,
|
|
||||||
headers: Vec<QueuedEthereumHeader>,
|
|
||||||
) -> SubmittedHeaders<EthereumHeaderId, RpcError> {
|
|
||||||
let mut ids = headers.iter().map(|header| header.id()).collect::<VecDeque<_>>();
|
|
||||||
let mut submitted_headers = SubmittedHeaders::default();
|
|
||||||
|
|
||||||
for header in headers {
|
|
||||||
let id = ids.pop_front().expect("both collections have same size; qed");
|
|
||||||
|
|
||||||
let call = self.instance.build_unsigned_header_call(header);
|
|
||||||
let transaction = create_unsigned_submit_transaction(call);
|
|
||||||
|
|
||||||
match self.submit_extrinsic(Bytes(transaction.encode())).await {
|
|
||||||
Ok(_) => submitted_headers.submitted.push(id),
|
|
||||||
Err(error) => {
|
|
||||||
submitted_headers.rejected.push(id);
|
|
||||||
submitted_headers.rejected.extend(ids);
|
|
||||||
submitted_headers.fatal_error = Some(error);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
submitted_headers
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A trait for RPC calls which are used to submit proof of Ethereum exchange transaction to a
|
|
||||||
/// Substrate runtime. These are typically calls which use a combination of other low-level RPC
|
|
||||||
/// calls.
|
|
||||||
#[async_trait]
|
|
||||||
pub trait SubmitEthereumExchangeTransactionProof: SubstrateRpc {
|
|
||||||
/// Pre-verify Ethereum exchange transaction proof.
|
|
||||||
async fn verify_exchange_transaction_proof(
|
|
||||||
&self,
|
|
||||||
proof: rialto_runtime::exchange::EthereumTransactionInclusionProof,
|
|
||||||
) -> RpcResult<bool>;
|
|
||||||
/// Submits Ethereum exchange transaction proof to Substrate runtime.
|
|
||||||
async fn submit_exchange_transaction_proof(
|
|
||||||
&self,
|
|
||||||
params: SubstrateSigningParams,
|
|
||||||
proof: rialto_runtime::exchange::EthereumTransactionInclusionProof,
|
|
||||||
) -> RpcResult<()>;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl SubmitEthereumExchangeTransactionProof for SubstrateRpcClient {
|
|
||||||
async fn verify_exchange_transaction_proof(
|
|
||||||
&self,
|
|
||||||
proof: rialto_runtime::exchange::EthereumTransactionInclusionProof,
|
|
||||||
) -> RpcResult<bool> {
|
|
||||||
let call = EXCH_API_FILTER_TRANSACTION_PROOF.to_string();
|
|
||||||
let data = Bytes(proof.encode());
|
|
||||||
|
|
||||||
let encoded_response = Substrate::state_call(&self.client, call, data, None).await?;
|
|
||||||
let is_allowed: bool = Decode::decode(&mut &encoded_response.0[..])?;
|
|
||||||
|
|
||||||
Ok(is_allowed)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn submit_exchange_transaction_proof(
|
|
||||||
&self,
|
|
||||||
params: SubstrateSigningParams,
|
|
||||||
proof: rialto_runtime::exchange::EthereumTransactionInclusionProof,
|
|
||||||
) -> RpcResult<()> {
|
|
||||||
let account_id = params.signer.public().as_array_ref().clone().into();
|
|
||||||
let nonce = self.next_account_index(account_id).await?;
|
|
||||||
|
|
||||||
let call = self.instance.build_currency_exchange_call(proof);
|
|
||||||
let transaction = create_signed_transaction(call, ¶ms.signer, nonce, self.genesis_hash);
|
|
||||||
|
|
||||||
let _ = self.submit_extrinsic(Bytes(transaction.encode())).await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create signed Substrate transaction for submitting Ethereum headers.
|
|
||||||
fn create_signed_submit_transaction(
|
|
||||||
signed_call: rialto_runtime::Call,
|
|
||||||
signer: &sp_core::sr25519::Pair,
|
|
||||||
index: node_primitives::Index,
|
|
||||||
genesis_hash: H256,
|
|
||||||
) -> rialto_runtime::UncheckedExtrinsic {
|
|
||||||
create_signed_transaction(signed_call, signer, index, genesis_hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create unsigned Substrate transaction for submitting Ethereum header.
|
|
||||||
fn create_unsigned_submit_transaction(call: rialto_runtime::Call) -> rialto_runtime::UncheckedExtrinsic {
|
|
||||||
rialto_runtime::UncheckedExtrinsic::new_unsigned(call)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create signed Substrate transaction.
|
|
||||||
fn create_signed_transaction(
|
|
||||||
function: rialto_runtime::Call,
|
|
||||||
signer: &sp_core::sr25519::Pair,
|
|
||||||
index: node_primitives::Index,
|
|
||||||
genesis_hash: H256,
|
|
||||||
) -> rialto_runtime::UncheckedExtrinsic {
|
|
||||||
let extra = |i: node_primitives::Index, f: node_primitives::Balance| {
|
|
||||||
(
|
|
||||||
frame_system::CheckSpecVersion::<rialto_runtime::Runtime>::new(),
|
|
||||||
frame_system::CheckTxVersion::<rialto_runtime::Runtime>::new(),
|
|
||||||
frame_system::CheckGenesis::<rialto_runtime::Runtime>::new(),
|
|
||||||
frame_system::CheckEra::<rialto_runtime::Runtime>::from(sp_runtime::generic::Era::Immortal),
|
|
||||||
frame_system::CheckNonce::<rialto_runtime::Runtime>::from(i),
|
|
||||||
frame_system::CheckWeight::<rialto_runtime::Runtime>::new(),
|
|
||||||
pallet_transaction_payment::ChargeTransactionPayment::<rialto_runtime::Runtime>::from(f),
|
|
||||||
)
|
|
||||||
};
|
|
||||||
let raw_payload = rialto_runtime::SignedPayload::from_raw(
|
|
||||||
function,
|
|
||||||
extra(index, 0),
|
|
||||||
(
|
|
||||||
rialto_runtime::VERSION.spec_version,
|
|
||||||
rialto_runtime::VERSION.transaction_version,
|
|
||||||
genesis_hash,
|
|
||||||
genesis_hash,
|
|
||||||
(),
|
|
||||||
(),
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
let signature = raw_payload.using_encoded(|payload| signer.sign(payload));
|
|
||||||
let signer: sp_runtime::MultiSigner = signer.public().into();
|
|
||||||
let (function, extra, _) = raw_payload.deconstruct();
|
|
||||||
|
|
||||||
rialto_runtime::UncheckedExtrinsic::new_signed(function, signer.into_account(), signature.into(), extra)
|
|
||||||
}
|
|
||||||
@@ -14,29 +14,26 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
//! Substrate -> Ethereum synchronization.
|
//! Rialto-Substrate -> Ethereum PoA synchronization.
|
||||||
|
|
||||||
use crate::ethereum_client::EthereumHighLevelRpc;
|
use crate::ethereum_client::EthereumHighLevelRpc;
|
||||||
use crate::instances::BridgeInstance;
|
|
||||||
use crate::rpc::SubstrateRpc;
|
|
||||||
use crate::rpc_errors::RpcError;
|
use crate::rpc_errors::RpcError;
|
||||||
use crate::substrate_client::{SubstrateConnectionParams, SubstrateRpcClient};
|
|
||||||
use crate::substrate_types::{
|
|
||||||
GrandpaJustification, Hash, Number, QueuedSubstrateHeader, SubstrateHeaderId, SubstrateHeadersSyncPipeline,
|
|
||||||
SubstrateSyncHeader as Header,
|
|
||||||
};
|
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
use codec::Encode;
|
||||||
use headers_relay::{
|
use headers_relay::{
|
||||||
sync::HeadersSyncParams,
|
sync::HeadersSyncParams,
|
||||||
sync_loop::{SourceClient, TargetClient},
|
sync_loop::{SourceClient, TargetClient},
|
||||||
sync_types::{SourceHeader, SubmittedHeaders},
|
sync_types::{HeadersSyncPipeline, QueuedHeader, SourceHeader, SubmittedHeaders},
|
||||||
};
|
};
|
||||||
use relay_ethereum_client::{
|
use relay_ethereum_client::{
|
||||||
types::Address, Client as EthereumClient, ConnectionParams as EthereumConnectionParams,
|
types::Address, Client as EthereumClient, ConnectionParams as EthereumConnectionParams,
|
||||||
SigningParams as EthereumSigningParams,
|
SigningParams as EthereumSigningParams,
|
||||||
};
|
};
|
||||||
|
use relay_rialto_client::{HeaderId as RialtoHeaderId, Rialto, SyncHeader as RialtoSyncHeader};
|
||||||
|
use relay_substrate_client::{Client as SubstrateClient, ConnectionParams as SubstrateConnectionParams};
|
||||||
use relay_utils::metrics::MetricsParams;
|
use relay_utils::metrics::MetricsParams;
|
||||||
|
use sp_runtime::Justification;
|
||||||
|
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::{collections::HashSet, time::Duration};
|
use std::{collections::HashSet, time::Duration};
|
||||||
@@ -71,18 +68,39 @@ pub struct SubstrateSyncParams {
|
|||||||
pub sync_params: HeadersSyncParams,
|
pub sync_params: HeadersSyncParams,
|
||||||
/// Metrics parameters.
|
/// Metrics parameters.
|
||||||
pub metrics_params: Option<MetricsParams>,
|
pub metrics_params: Option<MetricsParams>,
|
||||||
/// Instance of the bridge pallet being synchronized.
|
|
||||||
pub instance: Box<dyn BridgeInstance>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Substrate synchronization pipeline.
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
#[cfg_attr(test, derive(PartialEq))]
|
||||||
|
pub struct SubstrateHeadersSyncPipeline;
|
||||||
|
|
||||||
|
impl HeadersSyncPipeline for SubstrateHeadersSyncPipeline {
|
||||||
|
const SOURCE_NAME: &'static str = "Substrate";
|
||||||
|
const TARGET_NAME: &'static str = "Ethereum";
|
||||||
|
|
||||||
|
type Hash = rialto_runtime::Hash;
|
||||||
|
type Number = rialto_runtime::BlockNumber;
|
||||||
|
type Header = RialtoSyncHeader;
|
||||||
|
type Extra = ();
|
||||||
|
type Completion = Justification;
|
||||||
|
|
||||||
|
fn estimate_size(source: &QueuedHeader<Self>) -> usize {
|
||||||
|
source.header().encode().len()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Queued substrate header ID.
|
||||||
|
pub type QueuedRialtoHeader = QueuedHeader<SubstrateHeadersSyncPipeline>;
|
||||||
|
|
||||||
/// Substrate client as headers source.
|
/// Substrate client as headers source.
|
||||||
struct SubstrateHeadersSource {
|
struct SubstrateHeadersSource {
|
||||||
/// Substrate node client.
|
/// Substrate node client.
|
||||||
client: SubstrateRpcClient,
|
client: SubstrateClient<Rialto>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SubstrateHeadersSource {
|
impl SubstrateHeadersSource {
|
||||||
fn new(client: SubstrateRpcClient) -> Self {
|
fn new(client: SubstrateClient<Rialto>) -> Self {
|
||||||
Self { client }
|
Self { client }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -91,22 +109,30 @@ impl SubstrateHeadersSource {
|
|||||||
impl SourceClient<SubstrateHeadersSyncPipeline> for SubstrateHeadersSource {
|
impl SourceClient<SubstrateHeadersSyncPipeline> for SubstrateHeadersSource {
|
||||||
type Error = RpcError;
|
type Error = RpcError;
|
||||||
|
|
||||||
async fn best_block_number(&self) -> Result<Number, Self::Error> {
|
async fn best_block_number(&self) -> Result<rialto_runtime::BlockNumber, Self::Error> {
|
||||||
Ok(self.client.best_header().await?.number)
|
Ok(self.client.best_header().await?.number)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn header_by_hash(&self, hash: Hash) -> Result<Header, Self::Error> {
|
async fn header_by_hash(&self, hash: rialto_runtime::Hash) -> Result<RialtoSyncHeader, Self::Error> {
|
||||||
self.client.header_by_hash(hash).await.map(Into::into)
|
self.client
|
||||||
|
.header_by_hash(hash)
|
||||||
|
.await
|
||||||
|
.map(Into::into)
|
||||||
|
.map_err(Into::into)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn header_by_number(&self, number: Number) -> Result<Header, Self::Error> {
|
async fn header_by_number(&self, number: rialto_runtime::BlockNumber) -> Result<RialtoSyncHeader, Self::Error> {
|
||||||
self.client.header_by_number(number).await.map(Into::into)
|
self.client
|
||||||
|
.header_by_number(number)
|
||||||
|
.await
|
||||||
|
.map(Into::into)
|
||||||
|
.map_err(Into::into)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn header_completion(
|
async fn header_completion(
|
||||||
&self,
|
&self,
|
||||||
id: SubstrateHeaderId,
|
id: RialtoHeaderId,
|
||||||
) -> Result<(SubstrateHeaderId, Option<GrandpaJustification>), Self::Error> {
|
) -> Result<(RialtoHeaderId, Option<Justification>), Self::Error> {
|
||||||
let hash = id.1;
|
let hash = id.1;
|
||||||
let signed_block = self.client.get_block(Some(hash)).await?;
|
let signed_block = self.client.get_block(Some(hash)).await?;
|
||||||
let grandpa_justification = signed_block.justification;
|
let grandpa_justification = signed_block.justification;
|
||||||
@@ -116,9 +142,9 @@ impl SourceClient<SubstrateHeadersSyncPipeline> for SubstrateHeadersSource {
|
|||||||
|
|
||||||
async fn header_extra(
|
async fn header_extra(
|
||||||
&self,
|
&self,
|
||||||
id: SubstrateHeaderId,
|
id: RialtoHeaderId,
|
||||||
_header: QueuedSubstrateHeader,
|
_header: QueuedRialtoHeader,
|
||||||
) -> Result<(SubstrateHeaderId, ()), Self::Error> {
|
) -> Result<(RialtoHeaderId, ()), Self::Error> {
|
||||||
Ok((id, ()))
|
Ok((id, ()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -147,38 +173,35 @@ impl EthereumHeadersTarget {
|
|||||||
impl TargetClient<SubstrateHeadersSyncPipeline> for EthereumHeadersTarget {
|
impl TargetClient<SubstrateHeadersSyncPipeline> for EthereumHeadersTarget {
|
||||||
type Error = RpcError;
|
type Error = RpcError;
|
||||||
|
|
||||||
async fn best_header_id(&self) -> Result<SubstrateHeaderId, Self::Error> {
|
async fn best_header_id(&self) -> Result<RialtoHeaderId, Self::Error> {
|
||||||
self.client.best_substrate_block(self.contract).await
|
self.client.best_substrate_block(self.contract).await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn is_known_header(&self, id: SubstrateHeaderId) -> Result<(SubstrateHeaderId, bool), Self::Error> {
|
async fn is_known_header(&self, id: RialtoHeaderId) -> Result<(RialtoHeaderId, bool), Self::Error> {
|
||||||
self.client.substrate_header_known(self.contract, id).await
|
self.client.substrate_header_known(self.contract, id).await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn submit_headers(
|
async fn submit_headers(&self, headers: Vec<QueuedRialtoHeader>) -> SubmittedHeaders<RialtoHeaderId, Self::Error> {
|
||||||
&self,
|
|
||||||
headers: Vec<QueuedSubstrateHeader>,
|
|
||||||
) -> SubmittedHeaders<SubstrateHeaderId, Self::Error> {
|
|
||||||
self.client
|
self.client
|
||||||
.submit_substrate_headers(self.sign_params.clone(), self.contract, headers)
|
.submit_substrate_headers(self.sign_params.clone(), self.contract, headers)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn incomplete_headers_ids(&self) -> Result<HashSet<SubstrateHeaderId>, Self::Error> {
|
async fn incomplete_headers_ids(&self) -> Result<HashSet<RialtoHeaderId>, Self::Error> {
|
||||||
self.client.incomplete_substrate_headers(self.contract).await
|
self.client.incomplete_substrate_headers(self.contract).await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn complete_header(
|
async fn complete_header(
|
||||||
&self,
|
&self,
|
||||||
id: SubstrateHeaderId,
|
id: RialtoHeaderId,
|
||||||
completion: GrandpaJustification,
|
completion: Justification,
|
||||||
) -> Result<SubstrateHeaderId, Self::Error> {
|
) -> Result<RialtoHeaderId, Self::Error> {
|
||||||
self.client
|
self.client
|
||||||
.complete_substrate_header(self.sign_params.clone(), self.contract, id, completion)
|
.complete_substrate_header(self.sign_params.clone(), self.contract, id, completion)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn requires_extra(&self, header: QueuedSubstrateHeader) -> Result<(SubstrateHeaderId, bool), Self::Error> {
|
async fn requires_extra(&self, header: QueuedRialtoHeader) -> Result<(RialtoHeaderId, bool), Self::Error> {
|
||||||
Ok((header.header().id(), false))
|
Ok((header.header().id(), false))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -192,11 +215,10 @@ pub fn run(params: SubstrateSyncParams) -> Result<(), RpcError> {
|
|||||||
eth_contract_address,
|
eth_contract_address,
|
||||||
sync_params,
|
sync_params,
|
||||||
metrics_params,
|
metrics_params,
|
||||||
instance,
|
|
||||||
} = params;
|
} = params;
|
||||||
|
|
||||||
let eth_client = EthereumClient::new(eth_params);
|
let eth_client = EthereumClient::new(eth_params);
|
||||||
let sub_client = async_std::task::block_on(async { SubstrateRpcClient::new(sub_params, instance).await })?;
|
let sub_client = async_std::task::block_on(async { SubstrateClient::<Rialto>::new(sub_params).await })?;
|
||||||
|
|
||||||
let target = EthereumHeadersTarget::new(eth_client, eth_contract_address, eth_sign);
|
let target = EthereumHeadersTarget::new(eth_client, eth_contract_address, eth_sign);
|
||||||
let source = SubstrateHeadersSource::new(sub_client);
|
let source = SubstrateHeadersSource::new(sub_client);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2019-2020 Parity Technologies (UK) Ltd.
|
// Copyright 2020 Parity Technologies (UK) Ltd.
|
||||||
// This file is part of Parity Bridges Common.
|
// This file is part of Parity Bridges Common.
|
||||||
|
|
||||||
// Parity Bridges Common is free software: you can redistribute it and/or modify
|
// Parity Bridges Common is free software: you can redistribute it and/or modify
|
||||||
@@ -14,86 +14,16 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use codec::Encode;
|
//! Converting between Ethereum headers and bridge module types.
|
||||||
use headers_relay::sync_types::{HeadersSyncPipeline, QueuedHeader, SourceHeader};
|
|
||||||
use relay_utils::HeaderId;
|
|
||||||
|
|
||||||
pub use bp_eth_poa::{
|
use bp_eth_poa::{
|
||||||
Address, AuraHeader as SubstrateEthereumHeader, Bloom, Bytes, LogEntry as SubstrateEthereumLogEntry,
|
AuraHeader as SubstrateEthereumHeader, LogEntry as SubstrateEthereumLogEntry, Receipt as SubstrateEthereumReceipt,
|
||||||
Receipt as SubstrateEthereumReceipt, TransactionOutcome as SubstrateEthereumTransactionOutcome, H256, U256,
|
TransactionOutcome as SubstrateEthereumTransactionOutcome,
|
||||||
};
|
};
|
||||||
use relay_ethereum_client::types::{
|
use relay_ethereum_client::types::{
|
||||||
Header as EthereumHeader, Receipt as EthereumReceipt, HEADER_ID_PROOF as ETHEREUM_HEADER_ID_PROOF,
|
Header as EthereumHeader, Receipt as EthereumReceipt, HEADER_ID_PROOF as ETHEREUM_HEADER_ID_PROOF,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Substrate header hash.
|
|
||||||
pub type Hash = rialto_runtime::Hash;
|
|
||||||
|
|
||||||
/// Substrate header number.
|
|
||||||
pub type Number = rialto_runtime::BlockNumber;
|
|
||||||
|
|
||||||
/// Substrate header type.
|
|
||||||
pub type Header = rialto_runtime::Header;
|
|
||||||
|
|
||||||
/// Substrate header type used in headers sync.
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
|
||||||
pub struct SubstrateSyncHeader(Header);
|
|
||||||
|
|
||||||
impl std::ops::Deref for SubstrateSyncHeader {
|
|
||||||
type Target = Header;
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Substrate signed block type.
|
|
||||||
pub type SignedBlock = rialto_runtime::SignedBlock;
|
|
||||||
|
|
||||||
/// GRANDPA justification.
|
|
||||||
pub type GrandpaJustification = Vec<u8>;
|
|
||||||
|
|
||||||
/// Substrate header ID.
|
|
||||||
pub type SubstrateHeaderId = HeaderId<rialto_runtime::Hash, rialto_runtime::BlockNumber>;
|
|
||||||
|
|
||||||
/// Queued substrate header ID.
|
|
||||||
pub type QueuedSubstrateHeader = QueuedHeader<SubstrateHeadersSyncPipeline>;
|
|
||||||
|
|
||||||
/// Substrate synchronization pipeline.
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
|
||||||
#[cfg_attr(test, derive(PartialEq))]
|
|
||||||
pub struct SubstrateHeadersSyncPipeline;
|
|
||||||
|
|
||||||
impl HeadersSyncPipeline for SubstrateHeadersSyncPipeline {
|
|
||||||
const SOURCE_NAME: &'static str = "Substrate";
|
|
||||||
const TARGET_NAME: &'static str = "Ethereum";
|
|
||||||
|
|
||||||
type Hash = rialto_runtime::Hash;
|
|
||||||
type Number = rialto_runtime::BlockNumber;
|
|
||||||
type Header = SubstrateSyncHeader;
|
|
||||||
type Extra = ();
|
|
||||||
type Completion = GrandpaJustification;
|
|
||||||
|
|
||||||
fn estimate_size(source: &QueuedHeader<Self>) -> usize {
|
|
||||||
source.header().encode().len()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Header> for SubstrateSyncHeader {
|
|
||||||
fn from(header: Header) -> Self {
|
|
||||||
Self(header)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SourceHeader<rialto_runtime::Hash, rialto_runtime::BlockNumber> for SubstrateSyncHeader {
|
|
||||||
fn id(&self) -> SubstrateHeaderId {
|
|
||||||
HeaderId(self.number, self.hash())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parent_id(&self) -> SubstrateHeaderId {
|
|
||||||
HeaderId(self.number - 1, self.parent_hash)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Convert Ethereum header into Ethereum header for Substrate.
|
/// Convert Ethereum header into Ethereum header for Substrate.
|
||||||
pub fn into_substrate_ethereum_header(header: &EthereumHeader) -> SubstrateEthereumHeader {
|
pub fn into_substrate_ethereum_header(header: &EthereumHeader) -> SubstrateEthereumHeader {
|
||||||
SubstrateEthereumHeader {
|
SubstrateEthereumHeader {
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
[package]
|
||||||
|
name = "relay-millau-client"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Parity Technologies <admin@parity.io>"]
|
||||||
|
edition = "2018"
|
||||||
|
license = "GPL-3.0-or-later WITH Classpath-exception-2.0"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
millau-runtime = { path = "../../bin/millau-runtime" }
|
||||||
|
substrate-client = { path = "../substrate-client" }
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
// Copyright 2019-2020 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Parity Bridges Common.
|
||||||
|
|
||||||
|
// Parity Bridges Common is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity Bridges Common is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! Types used to connect to the Millau-Substrate chain.
|
||||||
|
|
||||||
|
use relay_substrate_client::Chain;
|
||||||
|
|
||||||
|
/// Millau chain definition.
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct Millau;
|
||||||
|
|
||||||
|
impl Chain for Millau {
|
||||||
|
type BlockNumber = millau_runtime::BlockNumber;
|
||||||
|
type Hash = millau_runtime::Hash;
|
||||||
|
type Header = millau_runtime::Header;
|
||||||
|
type AccountId = millau_runtime::AccountId;
|
||||||
|
type Index = millau_runtime::Index;
|
||||||
|
type SignedBlock = millau_runtime::SignedBlock;
|
||||||
|
type Call = rialto_runtime::Call;
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
[package]
|
||||||
|
name = "relay-rialto-client"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Parity Technologies <admin@parity.io>"]
|
||||||
|
edition = "2018"
|
||||||
|
license = "GPL-3.0-or-later WITH Classpath-exception-2.0"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
codec = { package = "parity-scale-codec", version = "1.3.4" }
|
||||||
|
headers-relay = { path = "../headers-relay" }
|
||||||
|
relay-substrate-client = { path = "../substrate-client" }
|
||||||
|
relay-utils = { path = "../utils" }
|
||||||
|
|
||||||
|
# Supported Chains
|
||||||
|
|
||||||
|
rialto-runtime = { path = "../../bin/rialto-runtime" }
|
||||||
|
|
||||||
|
# Substrate Dependencies
|
||||||
|
|
||||||
|
frame-system = { version = "2.0.0", tag = 'v2.0.0', git = "https://github.com/paritytech/substrate.git" }
|
||||||
|
pallet-transaction-payment = { version = "2.0.0", tag = 'v2.0.0', git = "https://github.com/paritytech/substrate.git" }
|
||||||
|
sp-core = { version = "2.0.0", tag = 'v2.0.0', git = "https://github.com/paritytech/substrate.git" }
|
||||||
|
sp-keyring = { version = "2.0.0", tag = 'v2.0.0', git = "https://github.com/paritytech/substrate.git" }
|
||||||
|
sp-runtime = { version = "2.0.0", tag = 'v2.0.0', git = "https://github.com/paritytech/substrate.git" }
|
||||||
@@ -0,0 +1,132 @@
|
|||||||
|
// Copyright 2019-2020 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Parity Bridges Common.
|
||||||
|
|
||||||
|
// Parity Bridges Common is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity Bridges Common is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! Types used to connect to the Rialto-Substrate chain.
|
||||||
|
|
||||||
|
use codec::Encode;
|
||||||
|
use headers_relay::sync_types::SourceHeader;
|
||||||
|
use relay_substrate_client::{Chain, Client, TransactionSignScheme};
|
||||||
|
use sp_core::Pair;
|
||||||
|
use sp_runtime::{
|
||||||
|
generic::SignedPayload,
|
||||||
|
traits::{Header as HeaderT, IdentifyAccount},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Rialto header id.
|
||||||
|
pub type HeaderId = relay_utils::HeaderId<rialto_runtime::Hash, rialto_runtime::BlockNumber>;
|
||||||
|
|
||||||
|
/// Rialto chain definition
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct Rialto;
|
||||||
|
|
||||||
|
impl Chain for Rialto {
|
||||||
|
type BlockNumber = rialto_runtime::BlockNumber;
|
||||||
|
type Hash = rialto_runtime::Hash;
|
||||||
|
type Header = rialto_runtime::Header;
|
||||||
|
type AccountId = rialto_runtime::AccountId;
|
||||||
|
type Index = rialto_runtime::Index;
|
||||||
|
type SignedBlock = rialto_runtime::SignedBlock;
|
||||||
|
type Call = rialto_runtime::Call;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TransactionSignScheme for Rialto {
|
||||||
|
type Chain = Rialto;
|
||||||
|
type AccountKeyPair = sp_core::sr25519::Pair;
|
||||||
|
type SignedTransaction = rialto_runtime::UncheckedExtrinsic;
|
||||||
|
|
||||||
|
fn sign_transaction(
|
||||||
|
client: &Client<Self>,
|
||||||
|
signer: &Self::AccountKeyPair,
|
||||||
|
signer_nonce: <Self::Chain as Chain>::Index,
|
||||||
|
call: <Self::Chain as Chain>::Call,
|
||||||
|
) -> Self::SignedTransaction {
|
||||||
|
let raw_payload = SignedPayload::from_raw(
|
||||||
|
call,
|
||||||
|
(
|
||||||
|
frame_system::CheckSpecVersion::<rialto_runtime::Runtime>::new(),
|
||||||
|
frame_system::CheckTxVersion::<rialto_runtime::Runtime>::new(),
|
||||||
|
frame_system::CheckGenesis::<rialto_runtime::Runtime>::new(),
|
||||||
|
frame_system::CheckEra::<rialto_runtime::Runtime>::from(sp_runtime::generic::Era::Immortal),
|
||||||
|
frame_system::CheckNonce::<rialto_runtime::Runtime>::from(signer_nonce),
|
||||||
|
frame_system::CheckWeight::<rialto_runtime::Runtime>::new(),
|
||||||
|
pallet_transaction_payment::ChargeTransactionPayment::<rialto_runtime::Runtime>::from(0),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
rialto_runtime::VERSION.spec_version,
|
||||||
|
rialto_runtime::VERSION.transaction_version,
|
||||||
|
*client.genesis_hash(),
|
||||||
|
*client.genesis_hash(),
|
||||||
|
(),
|
||||||
|
(),
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
let signature = raw_payload.using_encoded(|payload| signer.sign(payload));
|
||||||
|
let signer: sp_runtime::MultiSigner = signer.public().into();
|
||||||
|
let (call, extra, _) = raw_payload.deconstruct();
|
||||||
|
|
||||||
|
rialto_runtime::UncheckedExtrinsic::new_signed(call, signer.into_account(), signature.into(), extra)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Rialto signing params.
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct SigningParams {
|
||||||
|
/// Substrate transactions signer.
|
||||||
|
pub signer: sp_core::sr25519::Pair,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Debug for SigningParams {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
write!(f, "{}", self.signer.public())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for SigningParams {
|
||||||
|
fn default() -> Self {
|
||||||
|
SigningParams {
|
||||||
|
signer: sp_keyring::AccountKeyring::Alice.pair(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Rialto header type used in headers sync.
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub struct SyncHeader(rialto_runtime::Header);
|
||||||
|
|
||||||
|
impl std::ops::Deref for SyncHeader {
|
||||||
|
type Target = rialto_runtime::Header;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<rialto_runtime::Header> for SyncHeader {
|
||||||
|
fn from(header: rialto_runtime::Header) -> Self {
|
||||||
|
Self(header)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SourceHeader<rialto_runtime::Hash, rialto_runtime::BlockNumber> for SyncHeader {
|
||||||
|
fn id(&self) -> HeaderId {
|
||||||
|
relay_utils::HeaderId(*self.number(), self.hash())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parent_id(&self) -> HeaderId {
|
||||||
|
relay_utils::HeaderId(*self.number() - 1, *self.parent_hash())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
[package]
|
||||||
|
name = "relay-substrate-client"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Parity Technologies <admin@parity.io>"]
|
||||||
|
edition = "2018"
|
||||||
|
license = "GPL-3.0-or-later WITH Classpath-exception-2.0"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
codec = { package = "parity-scale-codec", version = "1.3.4" }
|
||||||
|
headers-relay = { path = "../headers-relay" }
|
||||||
|
jsonrpsee = { git = "https://github.com/svyatonik/jsonrpsee.git", branch = "shared-client-in-rpc-api", default-features = false, features = ["http"] }
|
||||||
|
log = "0.4.11"
|
||||||
|
num-traits = "0.2"
|
||||||
|
relay-utils = { path = "../utils" }
|
||||||
|
|
||||||
|
# Substrate Dependencies
|
||||||
|
|
||||||
|
frame-support = { version = "2.0.0", tag = 'v2.0.0', git = "https://github.com/paritytech/substrate.git" }
|
||||||
|
frame-system = { version = "2.0.0", tag = 'v2.0.0', git = "https://github.com/paritytech/substrate.git" }
|
||||||
|
sp-core = { version = "2.0.0", tag = 'v2.0.0', git = "https://github.com/paritytech/substrate.git" }
|
||||||
|
sp-runtime = { version = "2.0.0", tag = 'v2.0.0', git = "https://github.com/paritytech/substrate.git" }
|
||||||
|
sp-std = { version = "2.0.0", tag = 'v2.0.0', git = "https://github.com/paritytech/substrate.git" }
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
// Copyright 2019-2020 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Parity Bridges Common.
|
||||||
|
|
||||||
|
// Parity Bridges Common is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity Bridges Common is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
use crate::client::Client;
|
||||||
|
|
||||||
|
use frame_support::Parameter;
|
||||||
|
use jsonrpsee::common::{DeserializeOwned, Serialize};
|
||||||
|
use sp_core::Pair;
|
||||||
|
use sp_runtime::traits::{
|
||||||
|
AtLeast32Bit, AtLeast32BitUnsigned, Bounded, CheckEqual, Dispatchable, Header as HeaderT, MaybeDisplay,
|
||||||
|
MaybeMallocSizeOf, MaybeSerialize, MaybeSerializeDeserialize, Member, SimpleBitOps,
|
||||||
|
};
|
||||||
|
use sp_std::fmt::Debug;
|
||||||
|
|
||||||
|
/// Substrate-based chain from minimal relay-client point of view.
|
||||||
|
pub trait Chain {
|
||||||
|
/// The block number type used by the runtime.
|
||||||
|
type BlockNumber: Parameter
|
||||||
|
+ Member
|
||||||
|
+ MaybeSerializeDeserialize
|
||||||
|
+ Debug
|
||||||
|
+ MaybeDisplay
|
||||||
|
+ AtLeast32BitUnsigned
|
||||||
|
+ Default
|
||||||
|
+ Bounded
|
||||||
|
+ Copy
|
||||||
|
+ sp_std::hash::Hash
|
||||||
|
+ sp_std::str::FromStr
|
||||||
|
+ MaybeMallocSizeOf;
|
||||||
|
/// The output of the `Hashing` function.
|
||||||
|
type Hash: Parameter
|
||||||
|
+ Member
|
||||||
|
+ MaybeSerializeDeserialize
|
||||||
|
+ Debug
|
||||||
|
+ MaybeDisplay
|
||||||
|
+ SimpleBitOps
|
||||||
|
+ Ord
|
||||||
|
+ Default
|
||||||
|
+ Copy
|
||||||
|
+ CheckEqual
|
||||||
|
+ sp_std::hash::Hash
|
||||||
|
+ AsRef<[u8]>
|
||||||
|
+ AsMut<[u8]>
|
||||||
|
+ MaybeMallocSizeOf;
|
||||||
|
/// The block header.
|
||||||
|
type Header: Parameter + HeaderT<Number = Self::BlockNumber, Hash = Self::Hash>;
|
||||||
|
/// The user account identifier type for the runtime.
|
||||||
|
type AccountId: Parameter + Member + MaybeSerializeDeserialize + Debug + MaybeDisplay + Ord + Default;
|
||||||
|
/// Account index (aka nonce) type. This stores the number of previous transactions associated
|
||||||
|
/// with a sender account.
|
||||||
|
type Index: Parameter + Member + MaybeSerialize + Debug + Default + MaybeDisplay + AtLeast32Bit + Copy;
|
||||||
|
/// Block type.
|
||||||
|
type SignedBlock: Member + Serialize + DeserializeOwned;
|
||||||
|
/// The aggregated `Call` type.
|
||||||
|
type Call: Dispatchable + Debug;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Substrate-based chain transactions signing scheme.
|
||||||
|
pub trait TransactionSignScheme {
|
||||||
|
/// Chain that this scheme is to be used.
|
||||||
|
type Chain: Chain;
|
||||||
|
/// Type of key pairs used to sign transactions.
|
||||||
|
type AccountKeyPair: Pair;
|
||||||
|
/// Signed transaction.
|
||||||
|
type SignedTransaction;
|
||||||
|
|
||||||
|
/// Create transaction for given runtime call, signed by given account.
|
||||||
|
fn sign_transaction(
|
||||||
|
client: &Client<Self::Chain>,
|
||||||
|
signer: &Self::AccountKeyPair,
|
||||||
|
signer_nonce: <Self::Chain as Chain>::Index,
|
||||||
|
call: <Self::Chain as Chain>::Call,
|
||||||
|
) -> Self::SignedTransaction;
|
||||||
|
}
|
||||||
@@ -0,0 +1,135 @@
|
|||||||
|
// Copyright 2019-2020 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Parity Bridges Common.
|
||||||
|
|
||||||
|
// Parity Bridges Common is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity Bridges Common is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! Substrate node client.
|
||||||
|
|
||||||
|
use crate::chain::Chain;
|
||||||
|
use crate::rpc::Substrate;
|
||||||
|
use crate::{ConnectionParams, Result};
|
||||||
|
|
||||||
|
use jsonrpsee::common::DeserializeOwned;
|
||||||
|
use jsonrpsee::raw::RawClient;
|
||||||
|
use jsonrpsee::transport::http::HttpTransportClient;
|
||||||
|
use jsonrpsee::Client as RpcClient;
|
||||||
|
use num_traits::Zero;
|
||||||
|
use sp_core::Bytes;
|
||||||
|
|
||||||
|
const SUB_API_GRANDPA_AUTHORITIES: &str = "GrandpaApi_grandpa_authorities";
|
||||||
|
|
||||||
|
/// Opaque GRANDPA authorities set.
|
||||||
|
pub type OpaqueGrandpaAuthoritiesSet = Vec<u8>;
|
||||||
|
|
||||||
|
/// Substrate client type.
|
||||||
|
pub struct Client<C: Chain> {
|
||||||
|
/// Substrate RPC client.
|
||||||
|
client: RpcClient,
|
||||||
|
/// Genesis block hash.
|
||||||
|
genesis_hash: C::Hash,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C: Chain> std::fmt::Debug for Client<C> {
|
||||||
|
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
fmt.debug_struct("Client")
|
||||||
|
.field("genesis_hash", &self.genesis_hash)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C: Chain> Client<C> {
|
||||||
|
/// Returns client that is able to call RPCs on Substrate node.
|
||||||
|
pub async fn new(params: ConnectionParams) -> Result<Self> {
|
||||||
|
let uri = format!("http://{}:{}", params.host, params.port);
|
||||||
|
let transport = HttpTransportClient::new(&uri);
|
||||||
|
let raw_client = RawClient::new(transport);
|
||||||
|
let client: RpcClient = raw_client.into();
|
||||||
|
|
||||||
|
let number: C::BlockNumber = Zero::zero();
|
||||||
|
let genesis_hash = Substrate::<C, _, _>::chain_get_block_hash(&client, number).await?;
|
||||||
|
|
||||||
|
Ok(Self { client, genesis_hash })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C: Chain> Client<C>
|
||||||
|
where
|
||||||
|
C::Header: DeserializeOwned,
|
||||||
|
C::Index: DeserializeOwned,
|
||||||
|
{
|
||||||
|
/// Return hash of the genesis block.
|
||||||
|
pub fn genesis_hash(&self) -> &C::Hash {
|
||||||
|
&self.genesis_hash
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the best Substrate header.
|
||||||
|
pub async fn best_header(&self) -> Result<C::Header> {
|
||||||
|
Ok(Substrate::<C, _, _>::chain_get_header(&self.client, None).await?)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a Substrate block from its hash.
|
||||||
|
pub async fn get_block(&self, block_hash: Option<C::Hash>) -> Result<C::SignedBlock> {
|
||||||
|
Ok(Substrate::<C, _, _>::chain_get_block(&self.client, block_hash).await?)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a Substrate header by its hash.
|
||||||
|
pub async fn header_by_hash(&self, block_hash: C::Hash) -> Result<C::Header> {
|
||||||
|
Ok(Substrate::<C, _, _>::chain_get_header(&self.client, block_hash).await?)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a Substrate block hash by its number.
|
||||||
|
pub async fn block_hash_by_number(&self, number: C::BlockNumber) -> Result<C::Hash> {
|
||||||
|
Ok(Substrate::<C, _, _>::chain_get_block_hash(&self.client, number).await?)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a Substrate header by its number.
|
||||||
|
pub async fn header_by_number(&self, block_number: C::BlockNumber) -> Result<C::Header> {
|
||||||
|
let block_hash = Self::block_hash_by_number(self, block_number).await?;
|
||||||
|
Ok(Self::header_by_hash(self, block_hash).await?)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the nonce of the given Substrate account.
|
||||||
|
///
|
||||||
|
/// Note: It's the caller's responsibility to make sure `account` is a valid ss58 address.
|
||||||
|
pub async fn next_account_index(&self, account: C::AccountId) -> Result<C::Index> {
|
||||||
|
Ok(Substrate::<C, _, _>::system_account_next_index(&self.client, account).await?)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Submit an extrinsic for inclusion in a block.
|
||||||
|
///
|
||||||
|
/// Note: The given transaction does not need be SCALE encoded beforehand.
|
||||||
|
pub async fn submit_extrinsic(&self, transaction: Bytes) -> Result<C::Hash> {
|
||||||
|
let tx_hash = Substrate::<C, _, _>::author_submit_extrinsic(&self.client, transaction).await?;
|
||||||
|
log::trace!(target: "bridge", "Sent transaction to Substrate node: {:?}", tx_hash);
|
||||||
|
Ok(tx_hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the GRANDPA authority set at given block.
|
||||||
|
pub async fn grandpa_authorities_set(&self, block: C::Hash) -> Result<OpaqueGrandpaAuthoritiesSet> {
|
||||||
|
let call = SUB_API_GRANDPA_AUTHORITIES.to_string();
|
||||||
|
let data = Bytes(Vec::new());
|
||||||
|
|
||||||
|
let encoded_response = Substrate::<C, _, _>::state_call(&self.client, call, data, Some(block)).await?;
|
||||||
|
let authority_list = encoded_response.0;
|
||||||
|
|
||||||
|
Ok(authority_list)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Execute runtime call at given block.
|
||||||
|
pub async fn state_call(&self, method: String, data: Bytes, at_block: Option<C::Hash>) -> Result<Bytes> {
|
||||||
|
Substrate::<C, _, _>::state_call(&self.client, method, data, at_block)
|
||||||
|
.await
|
||||||
|
.map_err(Into::into)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
// Copyright 2019-2020 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Parity Bridges Common.
|
||||||
|
|
||||||
|
// Parity Bridges Common is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity Bridges Common is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! Substrate node RPC errors.
|
||||||
|
|
||||||
|
use jsonrpsee::client::RequestError;
|
||||||
|
use relay_utils::MaybeConnectionError;
|
||||||
|
|
||||||
|
/// Result type used by Substrate client.
|
||||||
|
pub type Result<T> = std::result::Result<T, Error>;
|
||||||
|
|
||||||
|
/// Errors that can occur only when interacting with
|
||||||
|
/// a Substrate node through RPC.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Error {
|
||||||
|
/// An error that can occur when making an HTTP request to
|
||||||
|
/// an JSON-RPC client.
|
||||||
|
Request(RequestError),
|
||||||
|
/// The response from the client could not be SCALE decoded.
|
||||||
|
ResponseParseFailed(codec::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<RequestError> for Error {
|
||||||
|
fn from(error: RequestError) -> Self {
|
||||||
|
Error::Request(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MaybeConnectionError for Error {
|
||||||
|
fn is_connection_error(&self) -> bool {
|
||||||
|
matches!(*self, Error::Request(RequestError::TransportError(_)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToString for Error {
|
||||||
|
fn to_string(&self) -> String {
|
||||||
|
match self {
|
||||||
|
Self::Request(e) => e.to_string(),
|
||||||
|
Self::ResponseParseFailed(e) => e.what().to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
// Copyright 2019-2020 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Parity Bridges Common.
|
||||||
|
|
||||||
|
// Parity Bridges Common is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity Bridges Common is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! Tools to interact with (Open) Ethereum node using RPC methods.
|
||||||
|
|
||||||
|
#![warn(missing_docs)]
|
||||||
|
|
||||||
|
mod chain;
|
||||||
|
mod client;
|
||||||
|
mod error;
|
||||||
|
mod rpc;
|
||||||
|
|
||||||
|
pub use crate::chain::{Chain, TransactionSignScheme};
|
||||||
|
pub use crate::client::{Client, OpaqueGrandpaAuthoritiesSet};
|
||||||
|
pub use crate::error::{Error, Result};
|
||||||
|
|
||||||
|
/// Substrate connection params.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct ConnectionParams {
|
||||||
|
/// Substrate RPC host.
|
||||||
|
pub host: String,
|
||||||
|
/// Substrate RPC port.
|
||||||
|
pub port: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ConnectionParams {
|
||||||
|
fn default() -> Self {
|
||||||
|
ConnectionParams {
|
||||||
|
host: "localhost".into(),
|
||||||
|
port: 9933,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
// Copyright 2019-2020 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Parity Bridges Common.
|
||||||
|
|
||||||
|
// Parity Bridges Common is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity Bridges Common is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! The most generic Substrate node RPC interface.
|
||||||
|
|
||||||
|
// The compiler doesn't think we're using the
|
||||||
|
// code from rpc_api!
|
||||||
|
#![allow(dead_code)]
|
||||||
|
#![allow(unused_variables)]
|
||||||
|
|
||||||
|
use crate::chain::Chain;
|
||||||
|
|
||||||
|
use sp_core::Bytes;
|
||||||
|
|
||||||
|
jsonrpsee::rpc_api! {
|
||||||
|
pub(crate) Substrate<C: Chain> {
|
||||||
|
#[rpc(method = "chain_getHeader", positional_params)]
|
||||||
|
fn chain_get_header(block_hash: Option<C::Hash>) -> C::Header;
|
||||||
|
#[rpc(method = "chain_getBlock", positional_params)]
|
||||||
|
fn chain_get_block(block_hash: Option<C::Hash>) -> C::SignedBlock;
|
||||||
|
#[rpc(method = "chain_getBlockHash", positional_params)]
|
||||||
|
fn chain_get_block_hash(block_number: Option<C::BlockNumber>) -> C::Hash;
|
||||||
|
#[rpc(method = "system_accountNextIndex", positional_params)]
|
||||||
|
fn system_account_next_index(account_id: C::AccountId) -> C::Index;
|
||||||
|
#[rpc(method = "author_submitExtrinsic", positional_params)]
|
||||||
|
fn author_submit_extrinsic(extrinsic: Bytes) -> C::Hash;
|
||||||
|
#[rpc(method = "state_call", positional_params)]
|
||||||
|
fn state_call(method: String, data: Bytes, at_block: Option<C::Hash>) -> Bytes;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user