Submit exchange transactions to PoA node (#229)

* submit Eth exchange transactions

* submit ethereum in docker-compose

* submit Eth exchange transactions

* fix duplicate message

* fix relay script

* lost file

* cargo fmt --all

* cargo +nightly clippy

* Show sccache

* remove test-helpers remains

* what's going on with jsonrpsee + Cargo.lock?

* relay-eth-submit-exchange-tx -> poa-exchange-tx-generator

* Update relays/ethereum/src/main.rs

Co-authored-by: Tomasz Drwięga <tomusdrw@users.noreply.github.com>

* configuring exchange-gen loop using env variables

* fixed signer account from dev chain to Arthur

* improve debug prints

* parse nonce from relay output

* --eth-nonce=

* fix compilation

* cargo fmt --all

* fix typo

* duplicate relay output to tty

* allow using from bash scripts tests

* fix: U256::parse() expects hex string :/

* cargo fmt --all

* BRIDGE_HASH: ${BRIDGE_HASH:-master}

* script comment

* generate exchange PoA transactions by Bertha

* Bertha address

Co-authored-by: Denis S. Soldatov aka General-Beck <general.beck@gmail.com>
Co-authored-by: Tomasz Drwięga <tomusdrw@users.noreply.github.com>
This commit is contained in:
Svyatoslav Nikolsky
2020-07-29 22:04:33 +03:00
committed by Bastian Köcher
parent d3f6948050
commit fddfbb5b1c
9 changed files with 200 additions and 13 deletions
+1
View File
@@ -20,6 +20,7 @@ ethabi-derive = "12.0"
ethereum-tx-sign = "3.0"
futures = "0.3.5"
hex = "0.4"
hex-literal = "0.3"
linked-hash-map = "0.5.3"
log = "0.4.11"
num-traits = "0.2"
+22
View File
@@ -106,6 +106,28 @@ subcommands:
value_name: SUB_INITIAL_HEADER
help: Encoded initial Substrate header.
takes_value: true
- eth-submit-exchange-tx:
about: Submit lock funds transaction to Ethereum node.
args:
- eth-host: *eth-host
- eth-port: *eth-port
- eth-nonce:
long: eth-nonce
value_name: ETH_NONCE
help: Nonce that have to be used when building transaction. If not specified, read from PoA node.
takes_value: true
- eth-signer: *eth-signer
- eth-chain-id: *eth-chain-id
- eth-amount:
long: eth-amount
value_name: ETH_AMOUNT
help: Amount of ETH to lock (in wei).
takes_value: true
- sub-recipient:
long: sub-recipient
value_name: SUB_RECIPIENT
help: Hex-encoded Public key of funds recipient in Substrate chain.
takes_value: true
- eth-exchange-sub:
about: Submit proof of PoA lock funds transaction to Substrate node.
args:
@@ -0,0 +1,120 @@
// 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/>.
//! Submitting Ethereum -> Substrate exchange transactions.
use crate::ethereum_client::{EthereumConnectionParams, EthereumRpcClient, EthereumSigningParams};
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,
};
/// Ethereum exchange transaction params.
#[derive(Debug)]
pub struct EthereumExchangeSubmitParams {
/// Ethereum connection params.
pub eth: EthereumConnectionParams,
/// Ethereum signing params.
pub eth_sign: EthereumSigningParams,
/// Ethereum signer nonce.
pub eth_nonce: Option<U256>,
/// Amount of Ethereum tokens to lock.
pub eth_amount: U256,
/// Funds recipient on Substrate side.
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 eth_signer_address = params.eth_sign.signer.address();
let sub_recipient_encoded = params.sub_recipient;
let nonce = match params.eth_nonce {
Some(eth_nonce) => eth_nonce,
None => eth_client
.account_nonce(eth_signer_address)
.await
.map_err(|err| format!("error fetching acount nonce: {:?}", err))?,
};
let gas = eth_client
.estimate_gas(CallRequest {
from: Some(eth_signer_address),
to: Some(LOCK_FUNDS_ADDRESS.into()),
value: Some(params.eth_amount),
data: Some(sub_recipient_encoded.to_vec().into()),
..Default::default()
})
.await
.map_err(|err| format!("error estimating gas requirements: {:?}", err))?;
let eth_tx_unsigned = UnsignedTransaction {
nonce,
gas_price: params.eth_sign.gas_price,
gas,
to: Some(LOCK_FUNDS_ADDRESS.into()),
value: params.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())
.expect("key is accepted by secp256k1::KeyPair and thus is valid; qed"),
Some(params.eth_sign.chain_id),
);
eth_client
.submit_transaction(eth_tx_signed)
.await
.map_err(|err| format!("error submitting transaction: {:?}", err))?;
Ok(eth_tx_unsigned)
});
match result {
Ok(eth_tx_unsigned) => {
log::info!(
target: "bridge",
"Exchange transaction has been submitted to Ethereum node: {:?}",
eth_tx_unsigned,
);
}
Err(err) => {
log::error!(
target: "bridge",
"Error submitting exchange transaction to Ethereum node: {}",
err,
);
}
}
}
+53 -1
View File
@@ -19,6 +19,7 @@
mod ethereum_client;
mod ethereum_deploy_contract;
mod ethereum_exchange;
mod ethereum_exchange_submit;
mod ethereum_sync_loop;
mod ethereum_types;
mod exchange;
@@ -85,14 +86,25 @@ fn main() {
("eth-deploy-contract", Some(eth_deploy_matches)) => {
log::info!(target: "bridge", "Deploying ETH contracts.");
ethereum_deploy_contract::run(match ethereum_deploy_contract_params(&eth_deploy_matches) {
Ok(ethereum_deploy_matches) => ethereum_deploy_matches,
Ok(ethereum_deploy_params) => ethereum_deploy_params,
Err(err) => {
log::error!(target: "bridge", "Error during contract deployment: {}", err);
return;
}
});
}
("eth-submit-exchange-tx", Some(eth_exchange_submit_matches)) => {
log::info!(target: "bridge", "Submitting ETH ➡ SUB exchange transaction.");
ethereum_exchange_submit::run(match ethereum_exchange_submit_params(&eth_exchange_submit_matches) {
Ok(eth_exchange_submit_params) => eth_exchange_submit_params,
Err(err) => {
log::error!(target: "bridge", "Error submitting Eethereum exchange transaction: {}", err);
return;
}
});
}
("eth-exchange-sub", Some(eth_exchange_matches)) => {
log::info!(target: "bridge", "Starting ETH ➡ SUB exchange transactions relay.");
ethereum_exchange::run(match ethereum_exchange_params(&eth_exchange_matches) {
Ok(eth_exchange_params) => eth_exchange_params,
Err(err) => {
@@ -256,6 +268,44 @@ fn ethereum_deploy_contract_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(&eth_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
.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)
.map_err(|err| err.to_string())
.and_then(|vsub_recipient| {
let expected_len = params.sub_recipient.len();
if expected_len != vsub_recipient.len() {
Err(format!("invalid length. Expected {} bytes", expected_len))
} else {
let mut sub_recipient = params.sub_recipient;
sub_recipient.copy_from_slice(&vsub_recipient[..expected_len]);
Ok(sub_recipient)
}
})
.map_err(|e| format!("Failed to parse sub-recipient: {}", e))?;
}
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)?;
@@ -279,6 +329,8 @@ fn ethereum_exchange_params(matches: &clap::ArgMatches) -> Result<ethereum_excha
}),
};
log::debug!(target: "bridge", "Ethereum exchange params: {:?}", params);
Ok(params)
}