mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-30 22:11:02 +00:00
Support Pallet Instances in Relay (#238)
* Sketch out how to support different bridge pallet instances * Create a common interface for using pallet instances * Start introducing generic instance parameter Attemps to make the compiler happy, but I'm having second thoughts about this approach. Commiting now as a way to have a checkpoint, but I think I'm going to need to re-consider my approach here. Ideally I want a change which introduces minimal changes, but this seems to be propagating around the codebase in ways I don't want. * Use trait objects instead of generics * Implement traits for Boxed trait objects This is done in order to statisfy trait bounds by types which use these new trait objects * Remove Clone usage for sync parameters * Remove implementation of Default for sync params * Require that BridgeInstance implements Debug * Ensure that BridgeInstance trait implements Send/Sync * Add documentation related to instances * Rust Fmt * Remove needless format * Make instance CLI option case insensitive * Replace `with_*` constructors with `new` * Clean up usage of instance structs * Enforce a default instance in the CLI params * Build sync params as we process input from CLI * Remove case insensitivity from sub-tx-mode I think this should happen, but maybe as part of a different PR * Process default Eth contract deployment config in CLI * Build EthereumExchangeParams in CLI * Process EthereumExchangeSubmitParams params in CLI
This commit is contained in:
committed by
Bastian Köcher
parent
6fd1651b21
commit
7f8360d8ab
@@ -43,6 +43,17 @@ subcommands:
|
||||
long: sub-signer-password
|
||||
value_name: SUB_SIGNER_PASSWORD
|
||||
help: The password for the SURI of secret key to use when transactions are submitted to the Substrate node.
|
||||
- sub-pallet-instance: &sub-pallet-instance
|
||||
long: instance
|
||||
short: i
|
||||
value_name: PALLET_INSTANCE
|
||||
help: The instance of the bridge pallet the relay should follow.
|
||||
takes_value: true
|
||||
case_insensitive: true
|
||||
possible_values:
|
||||
- Rialto
|
||||
- Kovan
|
||||
default_value: Rialto
|
||||
- no-prometheus: &no-prometheus
|
||||
long: no-prometheus
|
||||
help: Do not expose a Prometheus metric endpoint.
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
use crate::ethereum_client::{
|
||||
bridge_contract, EthereumConnectionParams, EthereumHighLevelRpc, EthereumRpcClient, EthereumSigningParams,
|
||||
};
|
||||
use crate::instances::BridgeInstance;
|
||||
use crate::rpc::SubstrateRpc;
|
||||
use crate::substrate_client::{SubstrateConnectionParams, SubstrateRpcClient};
|
||||
use crate::substrate_types::{Hash as SubstrateHash, Header as SubstrateHeader, SubstrateHeaderId};
|
||||
@@ -29,50 +30,48 @@ use num_traits::Zero;
|
||||
#[derive(Debug)]
|
||||
pub struct EthereumDeployContractParams {
|
||||
/// Ethereum connection params.
|
||||
pub eth: EthereumConnectionParams,
|
||||
pub eth_params: EthereumConnectionParams,
|
||||
/// Ethereum signing params.
|
||||
pub eth_sign: EthereumSigningParams,
|
||||
/// Ethereum contract bytecode.
|
||||
pub eth_contract_code: Vec<u8>,
|
||||
/// Substrate connection params.
|
||||
pub sub: SubstrateConnectionParams,
|
||||
pub sub_params: SubstrateConnectionParams,
|
||||
/// Initial authorities set id.
|
||||
pub sub_initial_authorities_set_id: Option<u64>,
|
||||
/// Initial authorities set.
|
||||
pub sub_initial_authorities_set: Option<Vec<u8>>,
|
||||
/// Initial header.
|
||||
pub sub_initial_header: Option<Vec<u8>>,
|
||||
}
|
||||
|
||||
impl Default for EthereumDeployContractParams {
|
||||
fn default() -> Self {
|
||||
EthereumDeployContractParams {
|
||||
eth: Default::default(),
|
||||
eth_sign: Default::default(),
|
||||
eth_contract_code: hex::decode(include_str!("../res/substrate-bridge-bytecode.hex"))
|
||||
.expect("code is hardcoded, thus valid; qed"),
|
||||
sub: Default::default(),
|
||||
sub_initial_authorities_set_id: None,
|
||||
sub_initial_authorities_set: None,
|
||||
sub_initial_header: None,
|
||||
}
|
||||
}
|
||||
/// Instance of the bridge pallet being synchronized.
|
||||
pub instance: Box<dyn BridgeInstance>,
|
||||
}
|
||||
|
||||
/// Deploy Bridge contract on Ethereum chain.
|
||||
pub fn run(params: EthereumDeployContractParams) {
|
||||
let mut local_pool = futures::executor::LocalPool::new();
|
||||
|
||||
let result = local_pool.run_until(async move {
|
||||
let eth_client = EthereumRpcClient::new(params.eth);
|
||||
let sub_client = SubstrateRpcClient::new(params.sub).await?;
|
||||
let EthereumDeployContractParams {
|
||||
eth_params,
|
||||
eth_sign,
|
||||
sub_params,
|
||||
instance,
|
||||
sub_initial_authorities_set_id,
|
||||
sub_initial_authorities_set,
|
||||
sub_initial_header,
|
||||
eth_contract_code,
|
||||
} = params;
|
||||
|
||||
let (initial_header_id, initial_header) = prepare_initial_header(&sub_client, params.sub_initial_header).await?;
|
||||
let initial_set_id = params.sub_initial_authorities_set_id.unwrap_or(0);
|
||||
let result = local_pool.run_until(async move {
|
||||
let eth_client = EthereumRpcClient::new(eth_params);
|
||||
let sub_client = SubstrateRpcClient::new(sub_params, instance).await?;
|
||||
|
||||
let (initial_header_id, initial_header) = prepare_initial_header(&sub_client, sub_initial_header).await?;
|
||||
let initial_set_id = sub_initial_authorities_set_id.unwrap_or(0);
|
||||
let initial_set = prepare_initial_authorities_set(
|
||||
&sub_client,
|
||||
initial_header_id.1,
|
||||
params.sub_initial_authorities_set,
|
||||
sub_initial_authorities_set,
|
||||
).await?;
|
||||
|
||||
log::info!(
|
||||
@@ -87,8 +86,8 @@ pub fn run(params: EthereumDeployContractParams) {
|
||||
|
||||
deploy_bridge_contract(
|
||||
ð_client,
|
||||
¶ms.eth_sign,
|
||||
params.eth_contract_code,
|
||||
ð_sign,
|
||||
eth_contract_code,
|
||||
initial_header,
|
||||
initial_set_id,
|
||||
initial_set,
|
||||
|
||||
@@ -26,6 +26,7 @@ use crate::exchange::{
|
||||
TransactionProofPipeline,
|
||||
};
|
||||
use crate::exchange_loop::{run as run_loop, InMemoryStorage};
|
||||
use crate::instances::BridgeInstance;
|
||||
use crate::metrics::MetricsParams;
|
||||
use crate::rpc::{EthereumRpc, SubstrateRpc};
|
||||
use crate::rpc_errors::RpcError;
|
||||
@@ -58,15 +59,17 @@ pub enum ExchangeRelayMode {
|
||||
#[derive(Debug)]
|
||||
pub struct EthereumExchangeParams {
|
||||
/// Ethereum connection params.
|
||||
pub eth: EthereumConnectionParams,
|
||||
pub eth_params: EthereumConnectionParams,
|
||||
/// Substrate connection params.
|
||||
pub sub: SubstrateConnectionParams,
|
||||
pub sub_params: SubstrateConnectionParams,
|
||||
/// Substrate signing params.
|
||||
pub sub_sign: SubstrateSigningParams,
|
||||
/// Relay working mode.
|
||||
pub mode: ExchangeRelayMode,
|
||||
/// Metrics parameters.
|
||||
pub metrics_params: Option<MetricsParams>,
|
||||
/// Instance of the bridge pallet being synchronized.
|
||||
pub instance: Box<dyn BridgeInstance>,
|
||||
}
|
||||
|
||||
/// Ethereum to Substrate exchange pipeline.
|
||||
@@ -253,18 +256,6 @@ impl TargetClient<EthereumToSubstrateExchange> for SubstrateTransactionsTarget {
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for EthereumExchangeParams {
|
||||
fn default() -> Self {
|
||||
EthereumExchangeParams {
|
||||
eth: Default::default(),
|
||||
sub: Default::default(),
|
||||
sub_sign: Default::default(),
|
||||
mode: ExchangeRelayMode::Auto(None),
|
||||
metrics_params: Some(Default::default()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Relay exchange transaction proof(s) to Substrate node.
|
||||
pub fn run(params: EthereumExchangeParams) {
|
||||
match params.mode {
|
||||
@@ -279,14 +270,22 @@ pub fn run(params: EthereumExchangeParams) {
|
||||
fn run_single_transaction_relay(params: EthereumExchangeParams, eth_tx_hash: H256) {
|
||||
let mut local_pool = futures::executor::LocalPool::new();
|
||||
|
||||
let EthereumExchangeParams {
|
||||
eth_params,
|
||||
sub_params,
|
||||
sub_sign,
|
||||
instance,
|
||||
..
|
||||
} = params;
|
||||
|
||||
let result = local_pool.run_until(async move {
|
||||
let eth_client = EthereumRpcClient::new(params.eth);
|
||||
let sub_client = SubstrateRpcClient::new(params.sub).await?;
|
||||
let eth_client = EthereumRpcClient::new(eth_params);
|
||||
let sub_client = SubstrateRpcClient::new(sub_params, instance).await?;
|
||||
|
||||
let source = EthereumTransactionsSource { client: eth_client };
|
||||
let target = SubstrateTransactionsTarget {
|
||||
client: sub_client,
|
||||
sign_params: params.sub_sign,
|
||||
sign_params: sub_sign,
|
||||
};
|
||||
|
||||
relay_single_transaction_proof(&source, &target, eth_tx_hash).await
|
||||
@@ -313,9 +312,18 @@ fn run_single_transaction_relay(params: EthereumExchangeParams, eth_tx_hash: H25
|
||||
|
||||
/// Run auto-relay loop.
|
||||
fn run_auto_transactions_relay_loop(params: EthereumExchangeParams, eth_start_with_block_number: Option<u64>) {
|
||||
let EthereumExchangeParams {
|
||||
eth_params,
|
||||
sub_params,
|
||||
sub_sign,
|
||||
metrics_params,
|
||||
instance,
|
||||
..
|
||||
} = params;
|
||||
|
||||
let do_run_loop = move || -> Result<(), String> {
|
||||
let eth_client = EthereumRpcClient::new(params.eth);
|
||||
let sub_client = async_std::task::block_on(SubstrateRpcClient::new(params.sub))
|
||||
let eth_client = EthereumRpcClient::new(eth_params);
|
||||
let sub_client = async_std::task::block_on(SubstrateRpcClient::new(sub_params, instance))
|
||||
.map_err(|err| format!("Error starting Substrate client: {:?}", err))?;
|
||||
|
||||
let eth_start_with_block_number = match eth_start_with_block_number {
|
||||
@@ -337,9 +345,9 @@ fn run_auto_transactions_relay_loop(params: EthereumExchangeParams, eth_start_wi
|
||||
EthereumTransactionsSource { client: eth_client },
|
||||
SubstrateTransactionsTarget {
|
||||
client: sub_client,
|
||||
sign_params: params.sub_sign,
|
||||
sign_params: sub_sign,
|
||||
},
|
||||
params.metrics_params,
|
||||
metrics_params,
|
||||
futures::future::pending(),
|
||||
);
|
||||
|
||||
|
||||
@@ -21,7 +21,6 @@ use crate::ethereum_types::{CallRequest, U256};
|
||||
use crate::rpc::EthereumRpc;
|
||||
|
||||
use bridge_node_runtime::exchange::LOCK_FUNDS_ADDRESS;
|
||||
use hex_literal::hex;
|
||||
use sp_bridge_eth_poa::{
|
||||
signatures::{SecretKey, SignTransaction},
|
||||
UnsignedTransaction,
|
||||
@@ -31,7 +30,7 @@ use sp_bridge_eth_poa::{
|
||||
#[derive(Debug)]
|
||||
pub struct EthereumExchangeSubmitParams {
|
||||
/// Ethereum connection params.
|
||||
pub eth: EthereumConnectionParams,
|
||||
pub eth_params: EthereumConnectionParams,
|
||||
/// Ethereum signing params.
|
||||
pub eth_sign: EthereumSigningParams,
|
||||
/// Ethereum signer nonce.
|
||||
@@ -42,28 +41,24 @@ pub struct EthereumExchangeSubmitParams {
|
||||
pub sub_recipient: [u8; 32],
|
||||
}
|
||||
|
||||
impl Default for EthereumExchangeSubmitParams {
|
||||
fn default() -> Self {
|
||||
EthereumExchangeSubmitParams {
|
||||
eth: Default::default(),
|
||||
eth_sign: Default::default(),
|
||||
eth_nonce: None,
|
||||
eth_amount: 1_000_000_000_000_000_000_u64.into(), // 1 ETH
|
||||
sub_recipient: hex!("1cbd2d43530a44705ad088af313e18f80b53ef16b36177cd4b77b846f2a5f07c"), // ferdie
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Submit single Ethereum -> Substrate exchange transaction.
|
||||
pub fn run(params: EthereumExchangeSubmitParams) {
|
||||
let mut local_pool = futures::executor::LocalPool::new();
|
||||
|
||||
let result: Result<_, String> = local_pool.run_until(async move {
|
||||
let eth_client = EthereumRpcClient::new(params.eth);
|
||||
let EthereumExchangeSubmitParams {
|
||||
eth_params,
|
||||
eth_sign,
|
||||
eth_nonce,
|
||||
eth_amount,
|
||||
sub_recipient,
|
||||
} = params;
|
||||
|
||||
let eth_signer_address = params.eth_sign.signer.address();
|
||||
let sub_recipient_encoded = params.sub_recipient;
|
||||
let nonce = match params.eth_nonce {
|
||||
let result: Result<_, String> = local_pool.run_until(async move {
|
||||
let eth_client = EthereumRpcClient::new(eth_params);
|
||||
|
||||
let eth_signer_address = eth_sign.signer.address();
|
||||
let sub_recipient_encoded = sub_recipient;
|
||||
let nonce = match eth_nonce {
|
||||
Some(eth_nonce) => eth_nonce,
|
||||
None => eth_client
|
||||
.account_nonce(eth_signer_address)
|
||||
@@ -74,7 +69,7 @@ pub fn run(params: EthereumExchangeSubmitParams) {
|
||||
.estimate_gas(CallRequest {
|
||||
from: Some(eth_signer_address),
|
||||
to: Some(LOCK_FUNDS_ADDRESS.into()),
|
||||
value: Some(params.eth_amount),
|
||||
value: Some(eth_amount),
|
||||
data: Some(sub_recipient_encoded.to_vec().into()),
|
||||
..Default::default()
|
||||
})
|
||||
@@ -82,16 +77,16 @@ pub fn run(params: EthereumExchangeSubmitParams) {
|
||||
.map_err(|err| format!("error estimating gas requirements: {:?}", err))?;
|
||||
let eth_tx_unsigned = UnsignedTransaction {
|
||||
nonce,
|
||||
gas_price: params.eth_sign.gas_price,
|
||||
gas_price: eth_sign.gas_price,
|
||||
gas,
|
||||
to: Some(LOCK_FUNDS_ADDRESS.into()),
|
||||
value: params.eth_amount,
|
||||
value: eth_amount,
|
||||
payload: sub_recipient_encoded.to_vec(),
|
||||
};
|
||||
let eth_tx_signed = eth_tx_unsigned.clone().sign_by(
|
||||
&SecretKey::parse(params.eth_sign.signer.secret().as_fixed_bytes())
|
||||
&SecretKey::parse(eth_sign.signer.secret().as_fixed_bytes())
|
||||
.expect("key is accepted by secp256k1::KeyPair and thus is valid; qed"),
|
||||
Some(params.eth_sign.chain_id),
|
||||
Some(eth_sign.chain_id),
|
||||
);
|
||||
eth_client
|
||||
.submit_transaction(eth_tx_signed)
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
use crate::ethereum_client::{EthereumConnectionParams, EthereumHighLevelRpc, EthereumRpcClient};
|
||||
use crate::ethereum_types::{EthereumHeaderId, EthereumHeadersSyncPipeline, Header, QueuedEthereumHeader, Receipt};
|
||||
use crate::instances::BridgeInstance;
|
||||
use crate::metrics::MetricsParams;
|
||||
use crate::rpc::{EthereumRpc, SubstrateRpc};
|
||||
use crate::rpc_errors::RpcError;
|
||||
@@ -32,57 +33,45 @@ use crate::sync_types::{SourceHeader, SubmittedHeaders};
|
||||
use async_trait::async_trait;
|
||||
use web3::types::H256;
|
||||
|
||||
use std::fmt::Debug;
|
||||
use std::{collections::HashSet, time::Duration};
|
||||
|
||||
/// Interval at which we check new Ethereum headers when we are synced/almost synced.
|
||||
const ETHEREUM_TICK_INTERVAL: Duration = Duration::from_secs(10);
|
||||
/// Interval at which we check new Substrate blocks.
|
||||
const SUBSTRATE_TICK_INTERVAL: Duration = Duration::from_secs(5);
|
||||
/// Max number of headers in single submit transaction.
|
||||
const MAX_HEADERS_IN_SINGLE_SUBMIT: usize = 32;
|
||||
/// Max total size of headers in single submit transaction. This only affects signed
|
||||
/// submissions, when several headers are submitted at once. 4096 is the maximal **expected**
|
||||
/// size of the Ethereum header + transactions receipts (if they're required).
|
||||
const MAX_HEADERS_SIZE_IN_SINGLE_SUBMIT: usize = MAX_HEADERS_IN_SINGLE_SUBMIT * 4096;
|
||||
/// Max Ethereum headers we want to have in all 'before-submitted' states.
|
||||
const MAX_FUTURE_HEADERS_TO_DOWNLOAD: usize = 128;
|
||||
/// Max Ethereum headers count we want to have in 'submitted' state.
|
||||
const MAX_SUBMITTED_HEADERS: usize = 128;
|
||||
/// Max depth of in-memory headers in all states. Past this depth they will be forgotten (pruned).
|
||||
const PRUNE_DEPTH: u32 = 4096;
|
||||
pub mod consts {
|
||||
use super::*;
|
||||
|
||||
/// Interval at which we check new Ethereum headers when we are synced/almost synced.
|
||||
pub const ETHEREUM_TICK_INTERVAL: Duration = Duration::from_secs(10);
|
||||
/// Interval at which we check new Substrate blocks.
|
||||
pub const SUBSTRATE_TICK_INTERVAL: Duration = Duration::from_secs(5);
|
||||
/// Max number of headers in single submit transaction.
|
||||
pub const MAX_HEADERS_IN_SINGLE_SUBMIT: usize = 32;
|
||||
/// Max total size of headers in single submit transaction. This only affects signed
|
||||
/// submissions, when several headers are submitted at once. 4096 is the maximal **expected**
|
||||
/// size of the Ethereum header + transactions receipts (if they're required).
|
||||
pub const MAX_HEADERS_SIZE_IN_SINGLE_SUBMIT: usize = MAX_HEADERS_IN_SINGLE_SUBMIT * 4096;
|
||||
/// Max Ethereum headers we want to have in all 'before-submitted' states.
|
||||
pub const MAX_FUTURE_HEADERS_TO_DOWNLOAD: usize = 128;
|
||||
/// Max Ethereum headers count we want to have in 'submitted' state.
|
||||
pub const MAX_SUBMITTED_HEADERS: usize = 128;
|
||||
/// Max depth of in-memory headers in all states. Past this depth they will be forgotten (pruned).
|
||||
pub const PRUNE_DEPTH: u32 = 4096;
|
||||
}
|
||||
|
||||
/// Ethereum synchronization parameters.
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Debug)]
|
||||
pub struct EthereumSyncParams {
|
||||
/// Ethereum connection params.
|
||||
pub eth: EthereumConnectionParams,
|
||||
pub eth_params: EthereumConnectionParams,
|
||||
/// Substrate connection params.
|
||||
pub sub: SubstrateConnectionParams,
|
||||
pub sub_params: SubstrateConnectionParams,
|
||||
/// Substrate signing params.
|
||||
pub sub_sign: SubstrateSigningParams,
|
||||
/// Synchronization parameters.
|
||||
pub sync_params: HeadersSyncParams,
|
||||
/// Metrics parameters.
|
||||
pub metrics_params: Option<MetricsParams>,
|
||||
}
|
||||
|
||||
impl Default for EthereumSyncParams {
|
||||
fn default() -> Self {
|
||||
EthereumSyncParams {
|
||||
eth: Default::default(),
|
||||
sub: Default::default(),
|
||||
sub_sign: Default::default(),
|
||||
sync_params: HeadersSyncParams {
|
||||
max_future_headers_to_download: MAX_FUTURE_HEADERS_TO_DOWNLOAD,
|
||||
max_headers_in_submitted_status: MAX_SUBMITTED_HEADERS,
|
||||
max_headers_in_single_submit: MAX_HEADERS_IN_SINGLE_SUBMIT,
|
||||
max_headers_size_in_single_submit: MAX_HEADERS_SIZE_IN_SINGLE_SUBMIT,
|
||||
prune_depth: PRUNE_DEPTH,
|
||||
target_tx_mode: TargetTransactionMode::Signed,
|
||||
},
|
||||
metrics_params: Some(Default::default()),
|
||||
}
|
||||
}
|
||||
/// Instance of the bridge pallet being synchronized.
|
||||
pub instance: Box<dyn BridgeInstance>,
|
||||
}
|
||||
|
||||
/// Ethereum client as headers source.
|
||||
@@ -190,26 +179,33 @@ impl TargetClient<EthereumHeadersSyncPipeline> for SubstrateHeadersTarget {
|
||||
|
||||
/// Run Ethereum headers synchronization.
|
||||
pub fn run(params: EthereumSyncParams) -> Result<(), RpcError> {
|
||||
let sub_params = params.clone();
|
||||
let EthereumSyncParams {
|
||||
eth_params,
|
||||
sub_params,
|
||||
sub_sign,
|
||||
sync_params,
|
||||
metrics_params,
|
||||
instance,
|
||||
} = params;
|
||||
|
||||
let eth_client = EthereumRpcClient::new(params.eth);
|
||||
let sub_client = async_std::task::block_on(async { SubstrateRpcClient::new(sub_params.sub).await })?;
|
||||
let eth_client = EthereumRpcClient::new(eth_params);
|
||||
let sub_client = async_std::task::block_on(async { SubstrateRpcClient::new(sub_params, instance).await })?;
|
||||
|
||||
let sign_sub_transactions = match params.sync_params.target_tx_mode {
|
||||
let sign_sub_transactions = match sync_params.target_tx_mode {
|
||||
TargetTransactionMode::Signed | TargetTransactionMode::Backup => true,
|
||||
TargetTransactionMode::Unsigned => false,
|
||||
};
|
||||
|
||||
let source = EthereumHeadersSource::new(eth_client);
|
||||
let target = SubstrateHeadersTarget::new(sub_client, sign_sub_transactions, params.sub_sign);
|
||||
let target = SubstrateHeadersTarget::new(sub_client, sign_sub_transactions, sub_sign);
|
||||
|
||||
crate::sync_loop::run(
|
||||
source,
|
||||
ETHEREUM_TICK_INTERVAL,
|
||||
consts::ETHEREUM_TICK_INTERVAL,
|
||||
target,
|
||||
SUBSTRATE_TICK_INTERVAL,
|
||||
params.sync_params,
|
||||
params.metrics_params,
|
||||
consts::SUBSTRATE_TICK_INTERVAL,
|
||||
sync_params,
|
||||
metrics_params,
|
||||
futures::future::pending(),
|
||||
);
|
||||
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
// Copyright 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 PoA Bridge Pallet provides a way to include multiple instances of itself in a runtime. When
|
||||
//! synchronizing a Substrate chain which can include multiple instances of the bridge pallet we
|
||||
//! must somehow decide which of the instances to sync.
|
||||
//!
|
||||
//! Note that each instance of the bridge pallet is coupled with an instance of the currency exchange
|
||||
//! pallet. We must also have a way to create `Call`s for the correct currency exchange instance.
|
||||
//!
|
||||
//! This module helps by preparing the correct `Call`s for each of the different pallet instances.
|
||||
|
||||
use crate::ethereum_types::QueuedEthereumHeader;
|
||||
use crate::substrate_types::{into_substrate_ethereum_header, into_substrate_ethereum_receipts};
|
||||
|
||||
use bridge_node_runtime::exchange::EthereumTransactionInclusionProof as Proof;
|
||||
use bridge_node_runtime::Call;
|
||||
|
||||
/// Interface for `Calls` which are needed to correctly sync the bridge.
|
||||
///
|
||||
/// Each instance of the bridge and currency exchange pallets in the bridge runtime requires similar
|
||||
/// but slightly different `Call` in order to be synchronized.
|
||||
pub trait BridgeInstance: Send + Sync + std::fmt::Debug {
|
||||
/// Used to build a `Call` for importing signed headers to a Substrate runtime.
|
||||
fn build_signed_header_call(&self, headers: Vec<QueuedEthereumHeader>) -> Call;
|
||||
/// Used to build a `Call` for importing an unsigned header to a Substrate runtime.
|
||||
fn build_unsigned_header_call(&self, header: QueuedEthereumHeader) -> Call;
|
||||
/// Used to build a `Call` for importing peer transactions to a Substrate runtime.
|
||||
fn build_currency_exchange_call(&self, proof: Proof) -> Call;
|
||||
}
|
||||
|
||||
/// Corresponds to the Rialto instance used in the bridge runtime.
|
||||
#[derive(Default, Clone, Debug)]
|
||||
pub struct Rialto;
|
||||
|
||||
impl BridgeInstance for Rialto {
|
||||
fn build_signed_header_call(&self, headers: Vec<QueuedEthereumHeader>) -> Call {
|
||||
let pallet_call = bridge_node_runtime::BridgeEthPoACall::import_signed_headers(
|
||||
headers
|
||||
.into_iter()
|
||||
.map(|header| {
|
||||
(
|
||||
into_substrate_ethereum_header(header.header()),
|
||||
into_substrate_ethereum_receipts(header.extra()),
|
||||
)
|
||||
})
|
||||
.collect(),
|
||||
);
|
||||
|
||||
bridge_node_runtime::Call::BridgeRialto(pallet_call)
|
||||
}
|
||||
|
||||
fn build_unsigned_header_call(&self, header: QueuedEthereumHeader) -> Call {
|
||||
let pallet_call = bridge_node_runtime::BridgeEthPoACall::import_unsigned_header(
|
||||
into_substrate_ethereum_header(header.header()),
|
||||
into_substrate_ethereum_receipts(header.extra()),
|
||||
);
|
||||
|
||||
bridge_node_runtime::Call::BridgeRialto(pallet_call)
|
||||
}
|
||||
|
||||
fn build_currency_exchange_call(&self, proof: Proof) -> Call {
|
||||
let pallet_call = bridge_node_runtime::BridgeCurrencyExchangeCall::import_peer_transaction(proof);
|
||||
bridge_node_runtime::Call::BridgeRialtoCurrencyExchange(pallet_call)
|
||||
}
|
||||
}
|
||||
|
||||
/// Corresponds to the Kovan instance used in the bridge runtime.
|
||||
#[derive(Default, Clone, Debug)]
|
||||
pub struct Kovan;
|
||||
|
||||
impl BridgeInstance for Kovan {
|
||||
fn build_signed_header_call(&self, headers: Vec<QueuedEthereumHeader>) -> Call {
|
||||
let pallet_call = bridge_node_runtime::BridgeEthPoACall::import_signed_headers(
|
||||
headers
|
||||
.into_iter()
|
||||
.map(|header| {
|
||||
(
|
||||
into_substrate_ethereum_header(header.header()),
|
||||
into_substrate_ethereum_receipts(header.extra()),
|
||||
)
|
||||
})
|
||||
.collect(),
|
||||
);
|
||||
|
||||
bridge_node_runtime::Call::BridgeKovan(pallet_call)
|
||||
}
|
||||
|
||||
fn build_unsigned_header_call(&self, header: QueuedEthereumHeader) -> Call {
|
||||
let pallet_call = bridge_node_runtime::BridgeEthPoACall::import_unsigned_header(
|
||||
into_substrate_ethereum_header(header.header()),
|
||||
into_substrate_ethereum_receipts(header.extra()),
|
||||
);
|
||||
|
||||
bridge_node_runtime::Call::BridgeKovan(pallet_call)
|
||||
}
|
||||
|
||||
fn build_currency_exchange_call(&self, proof: Proof) -> Call {
|
||||
let pallet_call = bridge_node_runtime::BridgeCurrencyExchangeCall::import_peer_transaction(proof);
|
||||
bridge_node_runtime::Call::BridgeKovanCurrencyExchange(pallet_call)
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,7 @@ mod exchange;
|
||||
mod exchange_loop;
|
||||
mod exchange_loop_metrics;
|
||||
mod headers;
|
||||
mod instances;
|
||||
mod metrics;
|
||||
mod rpc;
|
||||
mod rpc_errors;
|
||||
@@ -40,12 +41,19 @@ mod sync_types;
|
||||
mod utils;
|
||||
|
||||
use ethereum_client::{EthereumConnectionParams, EthereumSigningParams};
|
||||
use ethereum_deploy_contract::EthereumDeployContractParams;
|
||||
use ethereum_exchange::EthereumExchangeParams;
|
||||
use ethereum_exchange_submit::EthereumExchangeSubmitParams;
|
||||
use ethereum_sync_loop::EthereumSyncParams;
|
||||
use hex_literal::hex;
|
||||
use instances::{BridgeInstance, Kovan, Rialto};
|
||||
use parity_crypto::publickey::{KeyPair, Secret};
|
||||
use sp_core::crypto::Pair;
|
||||
use std::io::Write;
|
||||
use substrate_client::{SubstrateConnectionParams, SubstrateSigningParams};
|
||||
use substrate_sync_loop::SubstrateSyncParams;
|
||||
use sync::HeadersSyncParams;
|
||||
|
||||
use std::io::Write;
|
||||
|
||||
fn main() {
|
||||
initialize();
|
||||
@@ -210,110 +218,134 @@ fn substrate_signing_params(matches: &clap::ArgMatches) -> Result<SubstrateSigni
|
||||
}
|
||||
|
||||
fn ethereum_sync_params(matches: &clap::ArgMatches) -> Result<EthereumSyncParams, String> {
|
||||
let mut eth_sync_params = EthereumSyncParams::default();
|
||||
eth_sync_params.eth = ethereum_connection_params(matches)?;
|
||||
eth_sync_params.sub = substrate_connection_params(matches)?;
|
||||
eth_sync_params.sub_sign = substrate_signing_params(matches)?;
|
||||
eth_sync_params.metrics_params = metrics_params(matches)?;
|
||||
let mut sync_params = HeadersSyncParams::ethereum_sync_default();
|
||||
|
||||
match matches.value_of("sub-tx-mode") {
|
||||
Some("signed") => eth_sync_params.sync_params.target_tx_mode = sync::TargetTransactionMode::Signed,
|
||||
Some("signed") => sync_params.target_tx_mode = sync::TargetTransactionMode::Signed,
|
||||
Some("unsigned") => {
|
||||
eth_sync_params.sync_params.target_tx_mode = sync::TargetTransactionMode::Unsigned;
|
||||
sync_params.target_tx_mode = sync::TargetTransactionMode::Unsigned;
|
||||
|
||||
// tx pool won't accept too much unsigned transactions
|
||||
eth_sync_params.sync_params.max_headers_in_submitted_status = 10;
|
||||
sync_params.max_headers_in_submitted_status = 10;
|
||||
}
|
||||
Some("backup") => eth_sync_params.sync_params.target_tx_mode = sync::TargetTransactionMode::Backup,
|
||||
Some("backup") => sync_params.target_tx_mode = sync::TargetTransactionMode::Backup,
|
||||
Some(mode) => return Err(format!("Invalid sub-tx-mode: {}", mode)),
|
||||
None => eth_sync_params.sync_params.target_tx_mode = sync::TargetTransactionMode::Signed,
|
||||
None => sync_params.target_tx_mode = sync::TargetTransactionMode::Signed,
|
||||
}
|
||||
|
||||
log::debug!(target: "bridge", "Ethereum sync params: {:?}", eth_sync_params);
|
||||
let params = EthereumSyncParams {
|
||||
eth_params: ethereum_connection_params(matches)?,
|
||||
sub_params: substrate_connection_params(matches)?,
|
||||
sub_sign: substrate_signing_params(matches)?,
|
||||
metrics_params: metrics_params(matches)?,
|
||||
instance: instance_params(matches)?,
|
||||
sync_params,
|
||||
};
|
||||
|
||||
Ok(eth_sync_params)
|
||||
log::debug!(target: "bridge", "Ethereum sync params: {:?}", params);
|
||||
|
||||
Ok(params)
|
||||
}
|
||||
|
||||
fn substrate_sync_params(matches: &clap::ArgMatches) -> Result<SubstrateSyncParams, String> {
|
||||
let mut sub_sync_params = SubstrateSyncParams::default();
|
||||
sub_sync_params.eth = ethereum_connection_params(matches)?;
|
||||
sub_sync_params.eth_sign = ethereum_signing_params(matches)?;
|
||||
sub_sync_params.sub = substrate_connection_params(matches)?;
|
||||
sub_sync_params.metrics_params = metrics_params(matches)?;
|
||||
|
||||
if let Some(eth_contract) = matches.value_of("eth-contract") {
|
||||
sub_sync_params.eth_contract_address = eth_contract.parse().map_err(|e| format!("{}", e))?;
|
||||
}
|
||||
|
||||
log::debug!(target: "bridge", "Substrate sync params: {:?}", sub_sync_params);
|
||||
|
||||
Ok(sub_sync_params)
|
||||
}
|
||||
|
||||
fn ethereum_deploy_contract_params(
|
||||
matches: &clap::ArgMatches,
|
||||
) -> Result<ethereum_deploy_contract::EthereumDeployContractParams, String> {
|
||||
let mut eth_deploy_params = ethereum_deploy_contract::EthereumDeployContractParams::default();
|
||||
eth_deploy_params.eth = ethereum_connection_params(matches)?;
|
||||
eth_deploy_params.eth_sign = ethereum_signing_params(matches)?;
|
||||
eth_deploy_params.sub = substrate_connection_params(matches)?;
|
||||
|
||||
if let Some(eth_contract_code) = matches.value_of("eth-contract-code") {
|
||||
eth_deploy_params.eth_contract_code =
|
||||
hex::decode(ð_contract_code).map_err(|e| format!("Failed to parse eth-contract-code: {}", e))?;
|
||||
}
|
||||
|
||||
log::debug!(target: "bridge", "Deploy params: {:?}", eth_deploy_params);
|
||||
|
||||
Ok(eth_deploy_params)
|
||||
}
|
||||
|
||||
fn ethereum_exchange_submit_params(
|
||||
matches: &clap::ArgMatches,
|
||||
) -> Result<ethereum_exchange_submit::EthereumExchangeSubmitParams, String> {
|
||||
let mut params = ethereum_exchange_submit::EthereumExchangeSubmitParams::default();
|
||||
params.eth = ethereum_connection_params(matches)?;
|
||||
params.eth_sign = ethereum_signing_params(matches)?;
|
||||
|
||||
if let Some(eth_nonce) = matches.value_of("eth-nonce") {
|
||||
params.eth_nonce = Some(
|
||||
ethereum_types::U256::from_dec_str(ð_nonce).map_err(|e| format!("Failed to parse eth-nonce: {}", e))?,
|
||||
);
|
||||
}
|
||||
if let Some(eth_amount) = matches.value_of("eth-amount") {
|
||||
params.eth_amount = eth_amount
|
||||
let eth_contract_address: ethereum_types::Address = if let Some(eth_contract) = matches.value_of("eth-contract") {
|
||||
eth_contract.parse().map_err(|e| format!("{}", e))?
|
||||
} else {
|
||||
"731a10897d267e19b34503ad902d0a29173ba4b1"
|
||||
.parse()
|
||||
.map_err(|e| format!("Failed to parse eth-amount: {}", e))?;
|
||||
}
|
||||
if let Some(sub_recipient) = matches.value_of("sub-recipient") {
|
||||
params.sub_recipient = hex::decode(&sub_recipient)
|
||||
.expect("address is hardcoded, thus valid; qed")
|
||||
};
|
||||
|
||||
let params = SubstrateSyncParams {
|
||||
sub_params: substrate_connection_params(matches)?,
|
||||
eth_params: ethereum_connection_params(matches)?,
|
||||
eth_sign: ethereum_signing_params(matches)?,
|
||||
metrics_params: metrics_params(matches)?,
|
||||
instance: instance_params(matches)?,
|
||||
sync_params: HeadersSyncParams::substrate_sync_default(),
|
||||
eth_contract_address,
|
||||
};
|
||||
|
||||
log::debug!(target: "bridge", "Substrate sync params: {:?}", params);
|
||||
|
||||
Ok(params)
|
||||
}
|
||||
|
||||
fn ethereum_deploy_contract_params(matches: &clap::ArgMatches) -> Result<EthereumDeployContractParams, String> {
|
||||
let eth_contract_code = if let Some(eth_contract_code) = matches.value_of("eth-contract-code") {
|
||||
hex::decode(ð_contract_code).map_err(|e| format!("Failed to parse eth-contract-code: {}", e))?
|
||||
} else {
|
||||
hex::decode(include_str!("../res/substrate-bridge-bytecode.hex")).expect("code is hardcoded, thus valid; qed")
|
||||
};
|
||||
|
||||
let params = EthereumDeployContractParams {
|
||||
eth_params: ethereum_connection_params(matches)?,
|
||||
eth_sign: ethereum_signing_params(matches)?,
|
||||
sub_params: substrate_connection_params(matches)?,
|
||||
instance: instance_params(matches)?,
|
||||
sub_initial_authorities_set_id: None,
|
||||
sub_initial_authorities_set: None,
|
||||
sub_initial_header: None,
|
||||
eth_contract_code,
|
||||
};
|
||||
|
||||
log::debug!(target: "bridge", "Deploy params: {:?}", params);
|
||||
|
||||
Ok(params)
|
||||
}
|
||||
|
||||
fn ethereum_exchange_submit_params(matches: &clap::ArgMatches) -> Result<EthereumExchangeSubmitParams, String> {
|
||||
let eth_nonce = if let Some(eth_nonce) = matches.value_of("eth-nonce") {
|
||||
Some(ethereum_types::U256::from_dec_str(ð_nonce).map_err(|e| format!("Failed to parse eth-nonce: {}", e))?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let eth_amount = if let Some(eth_amount) = matches.value_of("eth-amount") {
|
||||
eth_amount
|
||||
.parse()
|
||||
.map_err(|e| format!("Failed to parse eth-amount: {}", e))?
|
||||
} else {
|
||||
// This is in Wei, represents 1 ETH
|
||||
1_000_000_000_000_000_000_u64.into()
|
||||
};
|
||||
|
||||
// This is the well-known Substrate account of Ferdie
|
||||
let default_recepient = hex!("1cbd2d43530a44705ad088af313e18f80b53ef16b36177cd4b77b846f2a5f07c");
|
||||
|
||||
let sub_recipient = if let Some(sub_recipient) = matches.value_of("sub-recipient") {
|
||||
hex::decode(&sub_recipient)
|
||||
.map_err(|err| err.to_string())
|
||||
.and_then(|vsub_recipient| {
|
||||
let expected_len = params.sub_recipient.len();
|
||||
let expected_len = default_recepient.len();
|
||||
if expected_len != vsub_recipient.len() {
|
||||
Err(format!("invalid length. Expected {} bytes", expected_len))
|
||||
} else {
|
||||
let mut sub_recipient = params.sub_recipient;
|
||||
let mut sub_recipient = default_recepient;
|
||||
sub_recipient.copy_from_slice(&vsub_recipient[..expected_len]);
|
||||
Ok(sub_recipient)
|
||||
}
|
||||
})
|
||||
.map_err(|e| format!("Failed to parse sub-recipient: {}", e))?;
|
||||
}
|
||||
.map_err(|e| format!("Failed to parse sub-recipient: {}", e))?
|
||||
} else {
|
||||
default_recepient
|
||||
};
|
||||
|
||||
let params = EthereumExchangeSubmitParams {
|
||||
eth_params: ethereum_connection_params(matches)?,
|
||||
eth_sign: ethereum_signing_params(matches)?,
|
||||
eth_nonce,
|
||||
eth_amount,
|
||||
sub_recipient,
|
||||
};
|
||||
|
||||
log::debug!(target: "bridge", "Submit Ethereum exchange tx params: {:?}", params);
|
||||
|
||||
Ok(params)
|
||||
}
|
||||
|
||||
fn ethereum_exchange_params(matches: &clap::ArgMatches) -> Result<ethereum_exchange::EthereumExchangeParams, String> {
|
||||
let mut params = ethereum_exchange::EthereumExchangeParams::default();
|
||||
params.eth = ethereum_connection_params(matches)?;
|
||||
params.sub = substrate_connection_params(matches)?;
|
||||
params.sub_sign = substrate_signing_params(matches)?;
|
||||
params.metrics_params = metrics_params(matches)?;
|
||||
|
||||
params.mode = match matches.value_of("eth-tx-hash") {
|
||||
fn ethereum_exchange_params(matches: &clap::ArgMatches) -> Result<EthereumExchangeParams, String> {
|
||||
let mode = match matches.value_of("eth-tx-hash") {
|
||||
Some(eth_tx_hash) => ethereum_exchange::ExchangeRelayMode::Single(
|
||||
eth_tx_hash
|
||||
.parse()
|
||||
@@ -329,6 +361,15 @@ fn ethereum_exchange_params(matches: &clap::ArgMatches) -> Result<ethereum_excha
|
||||
}),
|
||||
};
|
||||
|
||||
let params = EthereumExchangeParams {
|
||||
eth_params: ethereum_connection_params(matches)?,
|
||||
sub_params: substrate_connection_params(matches)?,
|
||||
sub_sign: substrate_signing_params(matches)?,
|
||||
metrics_params: metrics_params(matches)?,
|
||||
instance: instance_params(matches)?,
|
||||
mode,
|
||||
};
|
||||
|
||||
log::debug!(target: "bridge", "Ethereum exchange params: {:?}", params);
|
||||
|
||||
Ok(params)
|
||||
@@ -352,3 +393,17 @@ fn metrics_params(matches: &clap::ArgMatches) -> Result<Option<metrics::MetricsP
|
||||
|
||||
Ok(Some(metrics_params))
|
||||
}
|
||||
|
||||
fn instance_params(matches: &clap::ArgMatches) -> Result<Box<dyn BridgeInstance>, String> {
|
||||
let instance: Box<dyn BridgeInstance> = if let Some(instance) = matches.value_of("sub-pallet-instance") {
|
||||
match instance.to_lowercase().as_str() {
|
||||
"rialto" => Box::new(Rialto::default()),
|
||||
"kovan" => Box::new(Kovan::default()),
|
||||
_ => return Err("Unsupported bridge pallet instance".to_string()),
|
||||
}
|
||||
} else {
|
||||
unreachable!("CLI config enforces a default instance, can never be None")
|
||||
};
|
||||
|
||||
Ok(instance)
|
||||
}
|
||||
|
||||
@@ -15,12 +15,10 @@
|
||||
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::ethereum_types::{Bytes, EthereumHeaderId, QueuedEthereumHeader, H256};
|
||||
use crate::instances::BridgeInstance;
|
||||
use crate::rpc::{Substrate, SubstrateRpc};
|
||||
use crate::rpc_errors::RpcError;
|
||||
use crate::substrate_types::{
|
||||
into_substrate_ethereum_header, into_substrate_ethereum_receipts, Hash, Header as SubstrateHeader, Number,
|
||||
SignedBlock as SignedSubstrateBlock,
|
||||
};
|
||||
use crate::substrate_types::{Hash, Header as SubstrateHeader, Number, SignedBlock as SignedSubstrateBlock};
|
||||
use crate::sync_types::{HeaderId, SubmittedHeaders};
|
||||
|
||||
use async_trait::async_trait;
|
||||
@@ -89,11 +87,13 @@ pub struct SubstrateRpcClient {
|
||||
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) -> Result<Self> {
|
||||
pub async fn new(params: SubstrateConnectionParams, instance: Box<dyn BridgeInstance>) -> Result<Self> {
|
||||
let uri = format!("http://{}:{}", params.host, params.port);
|
||||
let transport = HttpTransportClient::new(&uri);
|
||||
let raw_client = RawClient::new(transport);
|
||||
@@ -102,7 +102,11 @@ impl SubstrateRpcClient {
|
||||
let number: Number = Zero::zero();
|
||||
let genesis_hash = Substrate::chain_get_block_hash(&client, number).await?;
|
||||
|
||||
Ok(Self { client, genesis_hash })
|
||||
Ok(Self {
|
||||
client,
|
||||
genesis_hash,
|
||||
instance,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -248,7 +252,8 @@ impl SubmitEthereumHeaders for SubstrateRpcClient {
|
||||
let account_id = params.signer.public().as_array_ref().clone().into();
|
||||
let nonce = self.next_account_index(account_id).await?;
|
||||
|
||||
let transaction = create_signed_submit_transaction(headers, ¶ms.signer, nonce, self.genesis_hash);
|
||||
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(())
|
||||
}
|
||||
@@ -276,9 +281,13 @@ impl SubmitEthereumHeaders for SubstrateRpcClient {
|
||||
) -> 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 transaction = create_unsigned_submit_transaction(header);
|
||||
|
||||
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) => {
|
||||
@@ -335,15 +344,9 @@ impl SubmitEthereumExchangeTransactionProof for SubstrateRpcClient {
|
||||
let account_id = params.signer.public().as_array_ref().clone().into();
|
||||
let nonce = self.next_account_index(account_id).await?;
|
||||
|
||||
let transaction = create_signed_transaction(
|
||||
// TODO [#209]: Change so that that it's dynamic
|
||||
bridge_node_runtime::Call::BridgeRialtoCurrencyExchange(
|
||||
bridge_node_runtime::BridgeCurrencyExchangeCall::import_peer_transaction(proof),
|
||||
),
|
||||
¶ms.signer,
|
||||
nonce,
|
||||
self.genesis_hash,
|
||||
);
|
||||
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(())
|
||||
}
|
||||
@@ -351,40 +354,17 @@ impl SubmitEthereumExchangeTransactionProof for SubstrateRpcClient {
|
||||
|
||||
/// Create signed Substrate transaction for submitting Ethereum headers.
|
||||
fn create_signed_submit_transaction(
|
||||
headers: Vec<QueuedEthereumHeader>,
|
||||
signed_call: bridge_node_runtime::Call,
|
||||
signer: &sp_core::sr25519::Pair,
|
||||
index: node_primitives::Index,
|
||||
genesis_hash: H256,
|
||||
) -> bridge_node_runtime::UncheckedExtrinsic {
|
||||
create_signed_transaction(
|
||||
// TODO [#209]: Change so that that it's dynamic
|
||||
bridge_node_runtime::Call::BridgeRialto(bridge_node_runtime::BridgeEthPoACall::import_signed_headers(
|
||||
headers
|
||||
.into_iter()
|
||||
.map(|header| {
|
||||
(
|
||||
into_substrate_ethereum_header(header.header()),
|
||||
into_substrate_ethereum_receipts(header.extra()),
|
||||
)
|
||||
})
|
||||
.collect(),
|
||||
)),
|
||||
signer,
|
||||
index,
|
||||
genesis_hash,
|
||||
)
|
||||
create_signed_transaction(signed_call, signer, index, genesis_hash)
|
||||
}
|
||||
|
||||
/// Create unsigned Substrate transaction for submitting Ethereum header.
|
||||
fn create_unsigned_submit_transaction(header: QueuedEthereumHeader) -> bridge_node_runtime::UncheckedExtrinsic {
|
||||
let function =
|
||||
// TODO [#209]: Change so that that it's dynamic
|
||||
bridge_node_runtime::Call::BridgeRialto(bridge_node_runtime::BridgeEthPoACall::import_unsigned_header(
|
||||
into_substrate_ethereum_header(header.header()),
|
||||
into_substrate_ethereum_receipts(header.extra()),
|
||||
));
|
||||
|
||||
bridge_node_runtime::UncheckedExtrinsic::new_unsigned(function)
|
||||
fn create_unsigned_submit_transaction(call: bridge_node_runtime::Call) -> bridge_node_runtime::UncheckedExtrinsic {
|
||||
bridge_node_runtime::UncheckedExtrinsic::new_unsigned(call)
|
||||
}
|
||||
|
||||
/// Create signed Substrate transaction.
|
||||
|
||||
@@ -20,6 +20,7 @@ use crate::ethereum_client::{
|
||||
EthereumConnectionParams, EthereumHighLevelRpc, EthereumRpcClient, EthereumSigningParams,
|
||||
};
|
||||
use crate::ethereum_types::Address;
|
||||
use crate::instances::BridgeInstance;
|
||||
use crate::metrics::MetricsParams;
|
||||
use crate::rpc::SubstrateRpc;
|
||||
use crate::rpc_errors::RpcError;
|
||||
@@ -27,66 +28,47 @@ use crate::substrate_client::{SubstrateConnectionParams, SubstrateRpcClient};
|
||||
use crate::substrate_types::{
|
||||
GrandpaJustification, Hash, Header, Number, QueuedSubstrateHeader, SubstrateHeaderId, SubstrateHeadersSyncPipeline,
|
||||
};
|
||||
use crate::sync::{HeadersSyncParams, TargetTransactionMode};
|
||||
use crate::sync::HeadersSyncParams;
|
||||
use crate::sync_loop::{SourceClient, TargetClient};
|
||||
use crate::sync_types::{SourceHeader, SubmittedHeaders};
|
||||
|
||||
use async_trait::async_trait;
|
||||
|
||||
use std::fmt::Debug;
|
||||
use std::{collections::HashSet, time::Duration};
|
||||
|
||||
/// Interval at which we check new Substrate headers when we are synced/almost synced.
|
||||
const SUBSTRATE_TICK_INTERVAL: Duration = Duration::from_secs(10);
|
||||
/// Interval at which we check new Ethereum blocks.
|
||||
const ETHEREUM_TICK_INTERVAL: Duration = Duration::from_secs(5);
|
||||
/// Max Ethereum headers we want to have in all 'before-submitted' states.
|
||||
const MAX_FUTURE_HEADERS_TO_DOWNLOAD: usize = 8;
|
||||
/// Max Ethereum headers count we want to have in 'submitted' state.
|
||||
const MAX_SUBMITTED_HEADERS: usize = 1;
|
||||
/// Max depth of in-memory headers in all states. Past this depth they will be forgotten (pruned).
|
||||
const PRUNE_DEPTH: u32 = 256;
|
||||
pub mod consts {
|
||||
use super::*;
|
||||
|
||||
/// Interval at which we check new Substrate headers when we are synced/almost synced.
|
||||
pub const SUBSTRATE_TICK_INTERVAL: Duration = Duration::from_secs(10);
|
||||
/// Interval at which we check new Ethereum blocks.
|
||||
pub const ETHEREUM_TICK_INTERVAL: Duration = Duration::from_secs(5);
|
||||
/// Max Ethereum headers we want to have in all 'before-submitted' states.
|
||||
pub const MAX_FUTURE_HEADERS_TO_DOWNLOAD: usize = 8;
|
||||
/// Max Ethereum headers count we want to have in 'submitted' state.
|
||||
pub const MAX_SUBMITTED_HEADERS: usize = 1;
|
||||
/// Max depth of in-memory headers in all states. Past this depth they will be forgotten (pruned).
|
||||
pub const PRUNE_DEPTH: u32 = 256;
|
||||
}
|
||||
|
||||
/// Substrate synchronization parameters.
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug)]
|
||||
pub struct SubstrateSyncParams {
|
||||
/// Substrate connection params.
|
||||
pub sub_params: SubstrateConnectionParams,
|
||||
/// Ethereum connection params.
|
||||
pub eth: EthereumConnectionParams,
|
||||
pub eth_params: EthereumConnectionParams,
|
||||
/// Ethereum signing params.
|
||||
pub eth_sign: EthereumSigningParams,
|
||||
/// Ethereum bridge contract address.
|
||||
pub eth_contract_address: Address,
|
||||
/// Substrate connection params.
|
||||
pub sub: SubstrateConnectionParams,
|
||||
/// Synchronization parameters.
|
||||
pub sync_params: HeadersSyncParams,
|
||||
/// Metrics parameters.
|
||||
pub metrics_params: Option<MetricsParams>,
|
||||
}
|
||||
|
||||
impl Default for SubstrateSyncParams {
|
||||
fn default() -> Self {
|
||||
SubstrateSyncParams {
|
||||
eth: Default::default(),
|
||||
eth_sign: Default::default(),
|
||||
// the address 0x731a10897d267e19b34503ad902d0a29173ba4b1 is the address
|
||||
// of the contract that is deployed by default signer and 0 nonce
|
||||
eth_contract_address: "731a10897d267e19b34503ad902d0a29173ba4b1"
|
||||
.parse()
|
||||
.expect("address is hardcoded, thus valid; qed"),
|
||||
sub: Default::default(),
|
||||
sync_params: HeadersSyncParams {
|
||||
max_future_headers_to_download: MAX_FUTURE_HEADERS_TO_DOWNLOAD,
|
||||
max_headers_in_submitted_status: MAX_SUBMITTED_HEADERS,
|
||||
// since we always have single Substrate header in separate Ethereum transaction,
|
||||
// all max_**_in_single_submit aren't important here
|
||||
max_headers_in_single_submit: 4,
|
||||
max_headers_size_in_single_submit: std::usize::MAX,
|
||||
prune_depth: PRUNE_DEPTH,
|
||||
target_tx_mode: TargetTransactionMode::Signed,
|
||||
},
|
||||
metrics_params: Some(Default::default()),
|
||||
}
|
||||
}
|
||||
/// Instance of the bridge pallet being synchronized.
|
||||
pub instance: Box<dyn BridgeInstance>,
|
||||
}
|
||||
|
||||
/// Substrate client as headers source.
|
||||
@@ -199,21 +181,29 @@ impl TargetClient<SubstrateHeadersSyncPipeline> for EthereumHeadersTarget {
|
||||
|
||||
/// Run Substrate headers synchronization.
|
||||
pub fn run(params: SubstrateSyncParams) -> Result<(), RpcError> {
|
||||
let sub_params = params.clone();
|
||||
let SubstrateSyncParams {
|
||||
sub_params,
|
||||
eth_params,
|
||||
eth_sign,
|
||||
eth_contract_address,
|
||||
sync_params,
|
||||
metrics_params,
|
||||
instance,
|
||||
} = params;
|
||||
|
||||
let eth_client = EthereumRpcClient::new(params.eth);
|
||||
let sub_client = async_std::task::block_on(async { SubstrateRpcClient::new(sub_params.sub).await })?;
|
||||
let eth_client = EthereumRpcClient::new(eth_params);
|
||||
let sub_client = async_std::task::block_on(async { SubstrateRpcClient::new(sub_params, instance).await })?;
|
||||
|
||||
let target = EthereumHeadersTarget::new(eth_client, params.eth_contract_address, params.eth_sign);
|
||||
let target = EthereumHeadersTarget::new(eth_client, eth_contract_address, eth_sign);
|
||||
let source = SubstrateHeadersSource::new(sub_client);
|
||||
|
||||
crate::sync_loop::run(
|
||||
source,
|
||||
SUBSTRATE_TICK_INTERVAL,
|
||||
consts::SUBSTRATE_TICK_INTERVAL,
|
||||
target,
|
||||
ETHEREUM_TICK_INTERVAL,
|
||||
params.sync_params,
|
||||
params.metrics_params,
|
||||
consts::ETHEREUM_TICK_INTERVAL,
|
||||
sync_params,
|
||||
metrics_params,
|
||||
futures::future::pending(),
|
||||
);
|
||||
|
||||
|
||||
@@ -36,6 +36,38 @@ pub struct HeadersSyncParams {
|
||||
pub target_tx_mode: TargetTransactionMode,
|
||||
}
|
||||
|
||||
impl HeadersSyncParams {
|
||||
/// Default parameters for syncing Ethereum headers.
|
||||
pub fn ethereum_sync_default() -> Self {
|
||||
use crate::ethereum_sync_loop::consts::*;
|
||||
|
||||
Self {
|
||||
max_future_headers_to_download: MAX_FUTURE_HEADERS_TO_DOWNLOAD,
|
||||
max_headers_in_submitted_status: MAX_SUBMITTED_HEADERS,
|
||||
max_headers_in_single_submit: MAX_HEADERS_IN_SINGLE_SUBMIT,
|
||||
max_headers_size_in_single_submit: MAX_HEADERS_SIZE_IN_SINGLE_SUBMIT,
|
||||
prune_depth: PRUNE_DEPTH,
|
||||
target_tx_mode: TargetTransactionMode::Signed,
|
||||
}
|
||||
}
|
||||
|
||||
/// Default parameters for syncing Substrate headers.
|
||||
pub fn substrate_sync_default() -> Self {
|
||||
use crate::substrate_sync_loop::consts::*;
|
||||
|
||||
Self {
|
||||
max_future_headers_to_download: MAX_FUTURE_HEADERS_TO_DOWNLOAD,
|
||||
max_headers_in_submitted_status: MAX_SUBMITTED_HEADERS,
|
||||
// since we always have single Substrate header in separate Ethereum transaction,
|
||||
// all max_**_in_single_submit aren't important here
|
||||
max_headers_in_single_submit: 4,
|
||||
max_headers_size_in_single_submit: std::usize::MAX,
|
||||
prune_depth: PRUNE_DEPTH,
|
||||
target_tx_mode: TargetTransactionMode::Signed,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Target transaction mode.
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum TargetTransactionMode {
|
||||
|
||||
Reference in New Issue
Block a user