mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-30 21:01:02 +00:00
Unify error enums in substrate and ethereum clients with thiserror (#1094)
* Unify error enums in substrate and ethereum clients with `thiserror` Related to https://github.com/paritytech/parity-bridges-common/issues/857 * Add license pre-amble * rustfmt * Fix spelling
This commit is contained in:
committed by
Bastian Köcher
parent
7b4f1c2236
commit
5842968273
@@ -21,6 +21,7 @@ libsecp256k1 = { version = "0.7", default-features = false, features = ["hmac"]
|
|||||||
log = "0.4.14"
|
log = "0.4.14"
|
||||||
num-traits = "0.2"
|
num-traits = "0.2"
|
||||||
serde_json = "1.0.64"
|
serde_json = "1.0.64"
|
||||||
|
thiserror = "1.0.26"
|
||||||
|
|
||||||
# Bridge dependencies
|
# Bridge dependencies
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
// Copyright 2021 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Parity Bridges Common.
|
||||||
|
// Parity Bridges Common is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
// Parity Bridges Common is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
use crate::rpc_errors::RpcError;
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
/// Result type used by PoA relay.
|
||||||
|
pub type Result<T> = std::result::Result<T, Error>;
|
||||||
|
|
||||||
|
/// Ethereum PoA relay errors.
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum Error {
|
||||||
|
/// Failed to decode initial header.
|
||||||
|
#[error("Error decoding initial header: {0}")]
|
||||||
|
DecodeInitialHeader(codec::Error),
|
||||||
|
/// RPC error.
|
||||||
|
#[error("{0}")]
|
||||||
|
Rpc(#[from] RpcError),
|
||||||
|
/// Failed to read genesis header.
|
||||||
|
#[error("Error reading Substrate genesis header: {0:?}")]
|
||||||
|
ReadGenesisHeader(relay_substrate_client::Error),
|
||||||
|
/// Failed to read initial GRANDPA authorities.
|
||||||
|
#[error("Error reading GRANDPA authorities set: {0:?}")]
|
||||||
|
ReadAuthorities(relay_substrate_client::Error),
|
||||||
|
/// Failed to deploy bridge contract to Ethereum chain.
|
||||||
|
#[error("Error deploying contract: {0:?}")]
|
||||||
|
DeployContract(RpcError),
|
||||||
|
}
|
||||||
@@ -15,6 +15,7 @@
|
|||||||
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
error::{Error, Result},
|
||||||
ethereum_client::{bridge_contract, EthereumHighLevelRpc},
|
ethereum_client::{bridge_contract, EthereumHighLevelRpc},
|
||||||
rpc_errors::RpcError,
|
rpc_errors::RpcError,
|
||||||
};
|
};
|
||||||
@@ -104,20 +105,20 @@ pub async fn run(params: EthereumDeployContractParams) {
|
|||||||
async fn prepare_initial_header(
|
async fn prepare_initial_header(
|
||||||
sub_client: &SubstrateClient<Rialto>,
|
sub_client: &SubstrateClient<Rialto>,
|
||||||
sub_initial_header: Option<Vec<u8>>,
|
sub_initial_header: Option<Vec<u8>>,
|
||||||
) -> Result<(RialtoHeaderId, Vec<u8>), String> {
|
) -> Result<(RialtoHeaderId, Vec<u8>)> {
|
||||||
match sub_initial_header {
|
match sub_initial_header {
|
||||||
Some(raw_initial_header) => {
|
Some(raw_initial_header) => {
|
||||||
match rialto_runtime::Header::decode(&mut &raw_initial_header[..]) {
|
match rialto_runtime::Header::decode(&mut &raw_initial_header[..]) {
|
||||||
Ok(initial_header) =>
|
Ok(initial_header) =>
|
||||||
Ok((HeaderId(initial_header.number, initial_header.hash()), raw_initial_header)),
|
Ok((HeaderId(initial_header.number, initial_header.hash()), raw_initial_header)),
|
||||||
Err(error) => Err(format!("Error decoding initial header: {}", error)),
|
Err(error) => Err(Error::DecodeInitialHeader(error)),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
None => {
|
None => {
|
||||||
let initial_header = sub_client.header_by_number(Zero::zero()).await;
|
let initial_header = sub_client.header_by_number(Zero::zero()).await;
|
||||||
initial_header
|
initial_header
|
||||||
.map(|header| (HeaderId(Zero::zero(), header.hash()), header.encode()))
|
.map(|header| (HeaderId(Zero::zero(), header.hash()), header.encode()))
|
||||||
.map_err(|error| format!("Error reading Substrate genesis header: {:?}", error))
|
.map_err(|error| Error::ReadGenesisHeader(error))
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -127,14 +128,13 @@ async fn prepare_initial_authorities_set(
|
|||||||
sub_client: &SubstrateClient<Rialto>,
|
sub_client: &SubstrateClient<Rialto>,
|
||||||
sub_initial_header_hash: rialto_runtime::Hash,
|
sub_initial_header_hash: rialto_runtime::Hash,
|
||||||
sub_initial_authorities_set: Option<Vec<u8>>,
|
sub_initial_authorities_set: Option<Vec<u8>>,
|
||||||
) -> Result<OpaqueGrandpaAuthoritiesSet, String> {
|
) -> Result<OpaqueGrandpaAuthoritiesSet> {
|
||||||
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,
|
||||||
};
|
};
|
||||||
|
|
||||||
initial_authorities_set
|
initial_authorities_set.map_err(|error| Error::ReadAuthorities(error))
|
||||||
.map_err(|error| format!("Error reading GRANDPA authorities set: {:?}", error))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Deploy bridge contract to Ethereum chain.
|
/// Deploy bridge contract to Ethereum chain.
|
||||||
@@ -145,7 +145,7 @@ async fn deploy_bridge_contract(
|
|||||||
initial_header: Vec<u8>,
|
initial_header: Vec<u8>,
|
||||||
initial_set_id: u64,
|
initial_set_id: u64,
|
||||||
initial_authorities: Vec<u8>,
|
initial_authorities: Vec<u8>,
|
||||||
) -> Result<(), String> {
|
) -> Result<()> {
|
||||||
eth_client
|
eth_client
|
||||||
.submit_ethereum_transaction(
|
.submit_ethereum_transaction(
|
||||||
params,
|
params,
|
||||||
@@ -160,5 +160,5 @@ async fn deploy_bridge_contract(
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(|error| format!("Error deploying contract: {:?}", error))
|
.map_err(|error| Error::DeployContract(error))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -339,7 +339,7 @@ pub async fn run(params: EthereumExchangeParams) {
|
|||||||
async fn run_single_transaction_relay(
|
async fn run_single_transaction_relay(
|
||||||
params: EthereumExchangeParams,
|
params: EthereumExchangeParams,
|
||||||
eth_tx_hash: H256,
|
eth_tx_hash: H256,
|
||||||
) -> Result<(), String> {
|
) -> anyhow::Result<()> {
|
||||||
let EthereumExchangeParams { eth_params, sub_params, sub_sign, instance, .. } = params;
|
let EthereumExchangeParams { eth_params, sub_params, sub_sign, instance, .. } = params;
|
||||||
|
|
||||||
let eth_client = EthereumClient::try_connect(eth_params).await.map_err(RpcError::Ethereum)?;
|
let eth_client = EthereumClient::try_connect(eth_params).await.map_err(RpcError::Ethereum)?;
|
||||||
@@ -354,7 +354,9 @@ async fn run_single_transaction_relay(
|
|||||||
bridge_instance: instance,
|
bridge_instance: instance,
|
||||||
};
|
};
|
||||||
|
|
||||||
relay_single_transaction_proof(&source, &target, eth_tx_hash).await
|
relay_single_transaction_proof(&source, &target, eth_tx_hash)
|
||||||
|
.await
|
||||||
|
.map_err(Into::into)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn run_auto_transactions_relay_loop(
|
async fn run_auto_transactions_relay_loop(
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
//! Submitting Ethereum -> Substrate exchange transactions.
|
//! Submitting Ethereum -> Substrate exchange transactions.
|
||||||
|
|
||||||
|
use anyhow::anyhow;
|
||||||
use bp_eth_poa::{
|
use bp_eth_poa::{
|
||||||
signatures::{secret_to_address, SignTransaction},
|
signatures::{secret_to_address, SignTransaction},
|
||||||
UnsignedTransaction,
|
UnsignedTransaction,
|
||||||
@@ -47,10 +48,10 @@ pub async fn run(params: EthereumExchangeSubmitParams) {
|
|||||||
let EthereumExchangeSubmitParams { eth_params, eth_sign, eth_nonce, eth_amount, sub_recipient } =
|
let EthereumExchangeSubmitParams { eth_params, eth_sign, eth_nonce, eth_amount, sub_recipient } =
|
||||||
params;
|
params;
|
||||||
|
|
||||||
let result: Result<_, String> = async move {
|
let result: anyhow::Result<_> = async move {
|
||||||
let eth_client = EthereumClient::try_connect(eth_params)
|
let eth_client = EthereumClient::try_connect(eth_params)
|
||||||
.await
|
.await
|
||||||
.map_err(|err| format!("error connecting to Ethereum node: {:?}", err))?;
|
.map_err(|err| anyhow!("error connecting to Ethereum node: {:?}", err))?;
|
||||||
|
|
||||||
let eth_signer_address = secret_to_address(ð_sign.signer);
|
let eth_signer_address = secret_to_address(ð_sign.signer);
|
||||||
let sub_recipient_encoded = sub_recipient;
|
let sub_recipient_encoded = sub_recipient;
|
||||||
@@ -59,7 +60,7 @@ pub async fn run(params: EthereumExchangeSubmitParams) {
|
|||||||
None => eth_client
|
None => eth_client
|
||||||
.account_nonce(eth_signer_address)
|
.account_nonce(eth_signer_address)
|
||||||
.await
|
.await
|
||||||
.map_err(|err| format!("error fetching acount nonce: {:?}", err))?,
|
.map_err(|err| anyhow!("error fetching acount nonce: {:?}", err))?,
|
||||||
};
|
};
|
||||||
let gas = eth_client
|
let gas = eth_client
|
||||||
.estimate_gas(CallRequest {
|
.estimate_gas(CallRequest {
|
||||||
@@ -70,7 +71,7 @@ pub async fn run(params: EthereumExchangeSubmitParams) {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.map_err(|err| format!("error estimating gas requirements: {:?}", err))?;
|
.map_err(|err| anyhow!("error estimating gas requirements: {:?}", err))?;
|
||||||
let eth_tx_unsigned = UnsignedTransaction {
|
let eth_tx_unsigned = UnsignedTransaction {
|
||||||
nonce,
|
nonce,
|
||||||
gas_price: eth_sign.gas_price,
|
gas_price: eth_sign.gas_price,
|
||||||
@@ -84,7 +85,7 @@ pub async fn run(params: EthereumExchangeSubmitParams) {
|
|||||||
eth_client
|
eth_client
|
||||||
.submit_transaction(eth_tx_signed)
|
.submit_transaction(eth_tx_signed)
|
||||||
.await
|
.await
|
||||||
.map_err(|err| format!("error submitting transaction: {:?}", err))?;
|
.map_err(|err| anyhow!("error submitting transaction: {:?}", err))?;
|
||||||
|
|
||||||
Ok(eth_tx_unsigned)
|
Ok(eth_tx_unsigned)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
#![recursion_limit = "1024"]
|
#![recursion_limit = "1024"]
|
||||||
|
|
||||||
|
mod error;
|
||||||
mod ethereum_client;
|
mod ethereum_client;
|
||||||
mod ethereum_deploy_contract;
|
mod ethereum_deploy_contract;
|
||||||
mod ethereum_exchange;
|
mod ethereum_exchange;
|
||||||
@@ -27,6 +28,7 @@ mod rpc_errors;
|
|||||||
mod substrate_sync_loop;
|
mod substrate_sync_loop;
|
||||||
mod substrate_types;
|
mod substrate_types;
|
||||||
|
|
||||||
|
use anyhow::anyhow;
|
||||||
use ethereum_deploy_contract::EthereumDeployContractParams;
|
use ethereum_deploy_contract::EthereumDeployContractParams;
|
||||||
use ethereum_exchange::EthereumExchangeParams;
|
use ethereum_exchange::EthereumExchangeParams;
|
||||||
use ethereum_exchange_submit::EthereumExchangeSubmitParams;
|
use ethereum_exchange_submit::EthereumExchangeSubmitParams;
|
||||||
@@ -136,58 +138,58 @@ async fn run_command(matches: &clap::ArgMatches<'_>) {
|
|||||||
|
|
||||||
fn ethereum_connection_params(
|
fn ethereum_connection_params(
|
||||||
matches: &clap::ArgMatches,
|
matches: &clap::ArgMatches,
|
||||||
) -> Result<EthereumConnectionParams, String> {
|
) -> anyhow::Result<EthereumConnectionParams> {
|
||||||
let mut params = EthereumConnectionParams::default();
|
let mut params = EthereumConnectionParams::default();
|
||||||
if let Some(eth_host) = matches.value_of("eth-host") {
|
if let Some(eth_host) = matches.value_of("eth-host") {
|
||||||
params.host = eth_host.into();
|
params.host = eth_host.into();
|
||||||
}
|
}
|
||||||
if let Some(eth_port) = matches.value_of("eth-port") {
|
if let Some(eth_port) = matches.value_of("eth-port") {
|
||||||
params.port = eth_port.parse().map_err(|e| format!("Failed to parse eth-port: {}", e))?;
|
params.port = eth_port.parse().map_err(|e| anyhow!("Failed to parse eth-port: {}", e))?;
|
||||||
}
|
}
|
||||||
Ok(params)
|
Ok(params)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ethereum_signing_params(matches: &clap::ArgMatches) -> Result<EthereumSigningParams, String> {
|
fn ethereum_signing_params(matches: &clap::ArgMatches) -> anyhow::Result<EthereumSigningParams> {
|
||||||
let mut params = EthereumSigningParams::default();
|
let mut params = EthereumSigningParams::default();
|
||||||
if let Some(eth_signer) = matches.value_of("eth-signer") {
|
if let Some(eth_signer) = matches.value_of("eth-signer") {
|
||||||
params.signer = SecretKey::parse_slice(
|
params.signer = SecretKey::parse_slice(
|
||||||
&hex::decode(eth_signer).map_err(|e| format!("Failed to parse eth-signer: {}", e))?,
|
&hex::decode(eth_signer).map_err(|e| anyhow!("Failed to parse eth-signer: {}", e))?,
|
||||||
)
|
)
|
||||||
.map_err(|e| format!("Invalid eth-signer: {}", e))?;
|
.map_err(|e| anyhow!("Invalid eth-signer: {}", e))?;
|
||||||
}
|
}
|
||||||
if let Some(eth_chain_id) = matches.value_of("eth-chain-id") {
|
if let Some(eth_chain_id) = matches.value_of("eth-chain-id") {
|
||||||
params.chain_id = eth_chain_id
|
params.chain_id = eth_chain_id
|
||||||
.parse::<u64>()
|
.parse::<u64>()
|
||||||
.map_err(|e| format!("Failed to parse eth-chain-id: {}", e))?;
|
.map_err(|e| anyhow!("Failed to parse eth-chain-id: {}", e))?;
|
||||||
}
|
}
|
||||||
Ok(params)
|
Ok(params)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn substrate_connection_params(
|
fn substrate_connection_params(
|
||||||
matches: &clap::ArgMatches,
|
matches: &clap::ArgMatches,
|
||||||
) -> Result<SubstrateConnectionParams, String> {
|
) -> anyhow::Result<SubstrateConnectionParams> {
|
||||||
let mut params = SubstrateConnectionParams::default();
|
let mut params = SubstrateConnectionParams::default();
|
||||||
if let Some(sub_host) = matches.value_of("sub-host") {
|
if let Some(sub_host) = matches.value_of("sub-host") {
|
||||||
params.host = sub_host.into();
|
params.host = sub_host.into();
|
||||||
}
|
}
|
||||||
if let Some(sub_port) = matches.value_of("sub-port") {
|
if let Some(sub_port) = matches.value_of("sub-port") {
|
||||||
params.port = sub_port.parse().map_err(|e| format!("Failed to parse sub-port: {}", e))?;
|
params.port = sub_port.parse().map_err(|e| anyhow!("Failed to parse sub-port: {}", e))?;
|
||||||
}
|
}
|
||||||
Ok(params)
|
Ok(params)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rialto_signing_params(matches: &clap::ArgMatches) -> Result<RialtoSigningParams, String> {
|
fn rialto_signing_params(matches: &clap::ArgMatches) -> anyhow::Result<RialtoSigningParams> {
|
||||||
let mut params = sp_keyring::AccountKeyring::Alice.pair();
|
let mut params = sp_keyring::AccountKeyring::Alice.pair();
|
||||||
|
|
||||||
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 = sp_core::sr25519::Pair::from_string(sub_signer, sub_signer_password)
|
params = sp_core::sr25519::Pair::from_string(sub_signer, sub_signer_password)
|
||||||
.map_err(|e| format!("Failed to parse sub-signer: {:?}", e))?;
|
.map_err(|e| anyhow!("Failed to parse sub-signer: {:?}", e))?;
|
||||||
}
|
}
|
||||||
Ok(params)
|
Ok(params)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ethereum_sync_params(matches: &clap::ArgMatches) -> Result<EthereumSyncParams, String> {
|
fn ethereum_sync_params(matches: &clap::ArgMatches) -> anyhow::Result<EthereumSyncParams> {
|
||||||
use crate::ethereum_sync_loop::consts::*;
|
use crate::ethereum_sync_loop::consts::*;
|
||||||
|
|
||||||
let mut sync_params = HeadersSyncParams {
|
let mut sync_params = HeadersSyncParams {
|
||||||
@@ -208,7 +210,7 @@ fn ethereum_sync_params(matches: &clap::ArgMatches) -> Result<EthereumSyncParams
|
|||||||
sync_params.max_headers_in_submitted_status = 10;
|
sync_params.max_headers_in_submitted_status = 10;
|
||||||
},
|
},
|
||||||
Some("backup") => sync_params.target_tx_mode = TargetTransactionMode::Backup,
|
Some("backup") => sync_params.target_tx_mode = TargetTransactionMode::Backup,
|
||||||
Some(mode) => return Err(format!("Invalid sub-tx-mode: {}", mode)),
|
Some(mode) => return Err(anyhow!("Invalid sub-tx-mode: {}", mode)),
|
||||||
None => sync_params.target_tx_mode = TargetTransactionMode::Signed,
|
None => sync_params.target_tx_mode = TargetTransactionMode::Signed,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -226,12 +228,12 @@ fn ethereum_sync_params(matches: &clap::ArgMatches) -> Result<EthereumSyncParams
|
|||||||
Ok(params)
|
Ok(params)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn substrate_sync_params(matches: &clap::ArgMatches) -> Result<SubstrateSyncParams, String> {
|
fn substrate_sync_params(matches: &clap::ArgMatches) -> anyhow::Result<SubstrateSyncParams> {
|
||||||
use crate::substrate_sync_loop::consts::*;
|
use crate::substrate_sync_loop::consts::*;
|
||||||
|
|
||||||
let eth_contract_address: relay_ethereum_client::types::Address =
|
let eth_contract_address: relay_ethereum_client::types::Address =
|
||||||
if let Some(eth_contract) = matches.value_of("eth-contract") {
|
if let Some(eth_contract) = matches.value_of("eth-contract") {
|
||||||
eth_contract.parse().map_err(|e| format!("{}", e))?
|
eth_contract.parse()?
|
||||||
} else {
|
} else {
|
||||||
"731a10897d267e19b34503ad902d0a29173ba4b1"
|
"731a10897d267e19b34503ad902d0a29173ba4b1"
|
||||||
.parse()
|
.parse()
|
||||||
@@ -261,7 +263,7 @@ fn substrate_sync_params(matches: &clap::ArgMatches) -> Result<SubstrateSyncPara
|
|||||||
|
|
||||||
fn ethereum_deploy_contract_params(
|
fn ethereum_deploy_contract_params(
|
||||||
matches: &clap::ArgMatches,
|
matches: &clap::ArgMatches,
|
||||||
) -> Result<EthereumDeployContractParams, String> {
|
) -> anyhow::Result<EthereumDeployContractParams> {
|
||||||
let eth_contract_code =
|
let eth_contract_code =
|
||||||
parse_hex_argument(matches, "eth-contract-code")?.unwrap_or_else(|| {
|
parse_hex_argument(matches, "eth-contract-code")?.unwrap_or_else(|| {
|
||||||
hex::decode(include_str!("../res/substrate-bridge-bytecode.hex"))
|
hex::decode(include_str!("../res/substrate-bridge-bytecode.hex"))
|
||||||
@@ -271,7 +273,7 @@ fn ethereum_deploy_contract_params(
|
|||||||
.value_of("sub-authorities-set-id")
|
.value_of("sub-authorities-set-id")
|
||||||
.map(|set| {
|
.map(|set| {
|
||||||
set.parse()
|
set.parse()
|
||||||
.map_err(|e| format!("Failed to parse sub-authorities-set-id: {}", e))
|
.map_err(|e| anyhow!("Failed to parse sub-authorities-set-id: {}", e))
|
||||||
})
|
})
|
||||||
.transpose()?;
|
.transpose()?;
|
||||||
let sub_initial_authorities_set = parse_hex_argument(matches, "sub-authorities-set")?;
|
let sub_initial_authorities_set = parse_hex_argument(matches, "sub-authorities-set")?;
|
||||||
@@ -294,19 +296,19 @@ fn ethereum_deploy_contract_params(
|
|||||||
|
|
||||||
fn ethereum_exchange_submit_params(
|
fn ethereum_exchange_submit_params(
|
||||||
matches: &clap::ArgMatches,
|
matches: &clap::ArgMatches,
|
||||||
) -> Result<EthereumExchangeSubmitParams, String> {
|
) -> anyhow::Result<EthereumExchangeSubmitParams> {
|
||||||
let eth_nonce = matches
|
let eth_nonce = matches
|
||||||
.value_of("eth-nonce")
|
.value_of("eth-nonce")
|
||||||
.map(|eth_nonce| {
|
.map(|eth_nonce| {
|
||||||
relay_ethereum_client::types::U256::from_dec_str(eth_nonce)
|
relay_ethereum_client::types::U256::from_dec_str(eth_nonce)
|
||||||
.map_err(|e| format!("Failed to parse eth-nonce: {}", e))
|
.map_err(|e| anyhow!("Failed to parse eth-nonce: {}", e))
|
||||||
})
|
})
|
||||||
.transpose()?;
|
.transpose()?;
|
||||||
|
|
||||||
let eth_amount = matches
|
let eth_amount = matches
|
||||||
.value_of("eth-amount")
|
.value_of("eth-amount")
|
||||||
.map(|eth_amount| {
|
.map(|eth_amount| {
|
||||||
eth_amount.parse().map_err(|e| format!("Failed to parse eth-amount: {}", e))
|
eth_amount.parse().map_err(|e| anyhow!("Failed to parse eth-amount: {}", e))
|
||||||
})
|
})
|
||||||
.transpose()?
|
.transpose()?
|
||||||
.unwrap_or_else(|| {
|
.unwrap_or_else(|| {
|
||||||
@@ -331,7 +333,7 @@ fn ethereum_exchange_submit_params(
|
|||||||
Ok(sub_recipient)
|
Ok(sub_recipient)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.map_err(|e| format!("Failed to parse sub-recipient: {}", e))?
|
.map_err(|e| anyhow!("Failed to parse sub-recipient: {}", e))?
|
||||||
} else {
|
} else {
|
||||||
default_recepient
|
default_recepient
|
||||||
};
|
};
|
||||||
@@ -349,10 +351,10 @@ fn ethereum_exchange_submit_params(
|
|||||||
Ok(params)
|
Ok(params)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ethereum_exchange_params(matches: &clap::ArgMatches) -> Result<EthereumExchangeParams, String> {
|
fn ethereum_exchange_params(matches: &clap::ArgMatches) -> anyhow::Result<EthereumExchangeParams> {
|
||||||
let mode = match matches.value_of("eth-tx-hash") {
|
let mode = match matches.value_of("eth-tx-hash") {
|
||||||
Some(eth_tx_hash) => ethereum_exchange::ExchangeRelayMode::Single(
|
Some(eth_tx_hash) => ethereum_exchange::ExchangeRelayMode::Single(
|
||||||
eth_tx_hash.parse().map_err(|e| format!("Failed to parse eth-tx-hash: {}", e))?,
|
eth_tx_hash.parse().map_err(|e| anyhow!("Failed to parse eth-tx-hash: {}", e))?,
|
||||||
),
|
),
|
||||||
None => ethereum_exchange::ExchangeRelayMode::Auto(
|
None => ethereum_exchange::ExchangeRelayMode::Auto(
|
||||||
matches
|
matches
|
||||||
@@ -360,7 +362,7 @@ fn ethereum_exchange_params(matches: &clap::ArgMatches) -> Result<EthereumExchan
|
|||||||
.map(|eth_start_with_block| {
|
.map(|eth_start_with_block| {
|
||||||
eth_start_with_block
|
eth_start_with_block
|
||||||
.parse()
|
.parse()
|
||||||
.map_err(|e| format!("Failed to parse eth-start-with-block: {}", e))
|
.map_err(|e| anyhow!("Failed to parse eth-start-with-block: {}", e))
|
||||||
})
|
})
|
||||||
.transpose()?,
|
.transpose()?,
|
||||||
),
|
),
|
||||||
@@ -380,7 +382,7 @@ fn ethereum_exchange_params(matches: &clap::ArgMatches) -> Result<EthereumExchan
|
|||||||
Ok(params)
|
Ok(params)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn metrics_params(matches: &clap::ArgMatches) -> Result<MetricsParams, String> {
|
fn metrics_params(matches: &clap::ArgMatches) -> anyhow::Result<MetricsParams> {
|
||||||
if matches.is_present("no-prometheus") {
|
if matches.is_present("no-prometheus") {
|
||||||
return Ok(None.into())
|
return Ok(None.into())
|
||||||
}
|
}
|
||||||
@@ -393,18 +395,18 @@ fn metrics_params(matches: &clap::ArgMatches) -> Result<MetricsParams, String> {
|
|||||||
if let Some(prometheus_port) = matches.value_of("prometheus-port") {
|
if let Some(prometheus_port) = matches.value_of("prometheus-port") {
|
||||||
metrics_params.port = prometheus_port
|
metrics_params.port = prometheus_port
|
||||||
.parse()
|
.parse()
|
||||||
.map_err(|e| format!("Failed to parse prometheus-port: {}", e))?;
|
.map_err(|e| anyhow!("Failed to parse prometheus-port: {}", e))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Some(metrics_params).into())
|
Ok(Some(metrics_params).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn instance_params(matches: &clap::ArgMatches) -> Result<Arc<dyn BridgeInstance>, String> {
|
fn instance_params(matches: &clap::ArgMatches) -> anyhow::Result<Arc<dyn BridgeInstance>> {
|
||||||
let instance = 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" => Arc::new(RialtoPoA) as Arc<dyn BridgeInstance>,
|
"rialto" => Arc::new(RialtoPoA) as Arc<dyn BridgeInstance>,
|
||||||
"kovan" => Arc::new(Kovan),
|
"kovan" => Arc::new(Kovan),
|
||||||
_ => return Err("Unsupported bridge pallet instance".to_string()),
|
_ => return Err(anyhow!("Unsupported bridge pallet instance")),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
unreachable!("CLI config enforces a default instance, can never be None")
|
unreachable!("CLI config enforces a default instance, can never be None")
|
||||||
@@ -413,10 +415,10 @@ fn instance_params(matches: &clap::ArgMatches) -> Result<Arc<dyn BridgeInstance>
|
|||||||
Ok(instance)
|
Ok(instance)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_hex_argument(matches: &clap::ArgMatches, arg: &str) -> Result<Option<Vec<u8>>, String> {
|
fn parse_hex_argument(matches: &clap::ArgMatches, arg: &str) -> anyhow::Result<Option<Vec<u8>>> {
|
||||||
match matches.value_of(arg) {
|
match matches.value_of(arg) {
|
||||||
Some(value) =>
|
Some(value) =>
|
||||||
Ok(Some(hex::decode(value).map_err(|e| format!("Failed to parse {}: {}", arg, e))?)),
|
Ok(Some(hex::decode(value).map_err(|e| anyhow!("Failed to parse {}: {}", arg, e))?)),
|
||||||
None => Ok(None),
|
None => Ok(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,48 +17,30 @@
|
|||||||
use relay_ethereum_client::Error as EthereumNodeError;
|
use relay_ethereum_client::Error as EthereumNodeError;
|
||||||
use relay_substrate_client::Error as SubstrateNodeError;
|
use relay_substrate_client::Error as SubstrateNodeError;
|
||||||
use relay_utils::MaybeConnectionError;
|
use relay_utils::MaybeConnectionError;
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
/// Contains common errors that can occur when
|
/// Contains common errors that can occur when
|
||||||
/// interacting with a Substrate or Ethereum node
|
/// interacting with a Substrate or Ethereum node
|
||||||
/// through RPC.
|
/// through RPC.
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Error)]
|
||||||
pub enum RpcError {
|
pub enum RpcError {
|
||||||
/// The arguments to the RPC method failed to serialize.
|
/// The arguments to the RPC method failed to serialize.
|
||||||
Serialization(serde_json::Error),
|
#[error("RPC arguments serialization failed: {0}")]
|
||||||
|
Serialization(#[from] serde_json::Error),
|
||||||
/// An error occurred when interacting with an Ethereum node.
|
/// An error occurred when interacting with an Ethereum node.
|
||||||
Ethereum(EthereumNodeError),
|
#[error("Ethereum node error: {0}")]
|
||||||
|
Ethereum(#[from] EthereumNodeError),
|
||||||
/// An error occurred when interacting with a Substrate node.
|
/// An error occurred when interacting with a Substrate node.
|
||||||
Substrate(SubstrateNodeError),
|
#[error("Substrate node error: {0}")]
|
||||||
|
Substrate(#[from] SubstrateNodeError),
|
||||||
/// Error running relay loop.
|
/// Error running relay loop.
|
||||||
|
#[error("{0}")]
|
||||||
SyncLoop(String),
|
SyncLoop(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<RpcError> for String {
|
impl From<RpcError> for String {
|
||||||
fn from(err: RpcError) -> Self {
|
fn from(err: RpcError) -> Self {
|
||||||
match err {
|
format!("{}", err)
|
||||||
RpcError::Serialization(e) => e.to_string(),
|
|
||||||
RpcError::Ethereum(e) => e.to_string(),
|
|
||||||
RpcError::Substrate(e) => e.to_string(),
|
|
||||||
RpcError::SyncLoop(e) => e,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<serde_json::Error> for RpcError {
|
|
||||||
fn from(err: serde_json::Error) -> Self {
|
|
||||||
Self::Serialization(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<EthereumNodeError> for RpcError {
|
|
||||||
fn from(err: EthereumNodeError) -> Self {
|
|
||||||
Self::Ethereum(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<SubstrateNodeError> for RpcError {
|
|
||||||
fn from(err: SubstrateNodeError) -> Self {
|
|
||||||
Self::Substrate(err)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ impl CliChain for Kusama {
|
|||||||
|
|
||||||
fn encode_message(
|
fn encode_message(
|
||||||
_message: encode_message::MessagePayload,
|
_message: encode_message::MessagePayload,
|
||||||
) -> Result<Self::MessagePayload, String> {
|
) -> anyhow::Result<Self::MessagePayload> {
|
||||||
Err("Sending messages from Kusama is not yet supported.".into())
|
anyhow::bail!("Sending messages from Kusama is not yet supported.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -256,6 +256,7 @@ pub async fn run(
|
|||||||
futures::future::pending(),
|
futures::future::pending(),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
.map_err(Into::into)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add standalone metrics for the Kusama -> Polkadot messages loop.
|
/// Add standalone metrics for the Kusama -> Polkadot messages loop.
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ use crate::cli::{
|
|||||||
send_message::{self, DispatchFeePayment},
|
send_message::{self, DispatchFeePayment},
|
||||||
CliChain,
|
CliChain,
|
||||||
};
|
};
|
||||||
|
use anyhow::anyhow;
|
||||||
use bp_message_dispatch::{CallOrigin, MessagePayload};
|
use bp_message_dispatch::{CallOrigin, MessagePayload};
|
||||||
use codec::Decode;
|
use codec::Decode;
|
||||||
use frame_support::weights::{DispatchInfo, GetDispatchInfo, Weight};
|
use frame_support::weights::{DispatchInfo, GetDispatchInfo, Weight};
|
||||||
@@ -87,10 +88,10 @@ impl CliChain for Millau {
|
|||||||
// TODO [#854|#843] support multiple bridges?
|
// TODO [#854|#843] support multiple bridges?
|
||||||
fn encode_message(
|
fn encode_message(
|
||||||
message: encode_message::MessagePayload,
|
message: encode_message::MessagePayload,
|
||||||
) -> Result<Self::MessagePayload, String> {
|
) -> anyhow::Result<Self::MessagePayload> {
|
||||||
match message {
|
match message {
|
||||||
encode_message::MessagePayload::Raw { data } => MessagePayload::decode(&mut &*data.0)
|
encode_message::MessagePayload::Raw { data } => MessagePayload::decode(&mut &*data.0)
|
||||||
.map_err(|e| format!("Failed to decode Millau's MessagePayload: {:?}", e)),
|
.map_err(|e| anyhow!("Failed to decode Millau's MessagePayload: {:?}", e)),
|
||||||
encode_message::MessagePayload::Call { mut call, mut sender } => {
|
encode_message::MessagePayload::Call { mut call, mut sender } => {
|
||||||
type Source = Millau;
|
type Source = Millau;
|
||||||
type Target = relay_rialto_client::Rialto;
|
type Target = relay_rialto_client::Rialto;
|
||||||
@@ -102,7 +103,7 @@ impl CliChain for Millau {
|
|||||||
&mut call,
|
&mut call,
|
||||||
bridge::MILLAU_TO_RIALTO_INDEX,
|
bridge::MILLAU_TO_RIALTO_INDEX,
|
||||||
);
|
);
|
||||||
let call = Target::encode_call(&call).map_err(|e| e.to_string())?;
|
let call = Target::encode_call(&call)?;
|
||||||
let weight = call.get_dispatch_info().weight;
|
let weight = call.get_dispatch_info().weight;
|
||||||
|
|
||||||
Ok(send_message::message_payload(
|
Ok(send_message::message_payload(
|
||||||
|
|||||||
@@ -250,6 +250,7 @@ pub async fn run(
|
|||||||
futures::future::pending(),
|
futures::future::pending(),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
.map_err(Into::into)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add standalone metrics for the Millau -> Rialto messages loop.
|
/// Add standalone metrics for the Millau -> Rialto messages loop.
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ impl CliChain for Polkadot {
|
|||||||
|
|
||||||
fn encode_message(
|
fn encode_message(
|
||||||
_message: encode_message::MessagePayload,
|
_message: encode_message::MessagePayload,
|
||||||
) -> Result<Self::MessagePayload, String> {
|
) -> anyhow::Result<Self::MessagePayload> {
|
||||||
Err("Sending messages from Polkadot is not yet supported.".into())
|
anyhow::bail!("Sending messages from Polkadot is not yet supported.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -255,6 +255,7 @@ pub async fn run(
|
|||||||
futures::future::pending(),
|
futures::future::pending(),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
.map_err(Into::into)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add standalone metrics for the Polkadot -> Kusama messages loop.
|
/// Add standalone metrics for the Polkadot -> Kusama messages loop.
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ use crate::cli::{
|
|||||||
send_message::{self, DispatchFeePayment},
|
send_message::{self, DispatchFeePayment},
|
||||||
CliChain,
|
CliChain,
|
||||||
};
|
};
|
||||||
|
use anyhow::anyhow;
|
||||||
use bp_message_dispatch::{CallOrigin, MessagePayload};
|
use bp_message_dispatch::{CallOrigin, MessagePayload};
|
||||||
use codec::Decode;
|
use codec::Decode;
|
||||||
use frame_support::weights::{DispatchInfo, GetDispatchInfo, Weight};
|
use frame_support::weights::{DispatchInfo, GetDispatchInfo, Weight};
|
||||||
@@ -86,10 +87,10 @@ impl CliChain for Rialto {
|
|||||||
|
|
||||||
fn encode_message(
|
fn encode_message(
|
||||||
message: encode_message::MessagePayload,
|
message: encode_message::MessagePayload,
|
||||||
) -> Result<Self::MessagePayload, String> {
|
) -> anyhow::Result<Self::MessagePayload> {
|
||||||
match message {
|
match message {
|
||||||
encode_message::MessagePayload::Raw { data } => MessagePayload::decode(&mut &*data.0)
|
encode_message::MessagePayload::Raw { data } => MessagePayload::decode(&mut &*data.0)
|
||||||
.map_err(|e| format!("Failed to decode Rialto's MessagePayload: {:?}", e)),
|
.map_err(|e| anyhow!("Failed to decode Rialto's MessagePayload: {:?}", e)),
|
||||||
encode_message::MessagePayload::Call { mut call, mut sender } => {
|
encode_message::MessagePayload::Call { mut call, mut sender } => {
|
||||||
type Source = Rialto;
|
type Source = Rialto;
|
||||||
type Target = relay_millau_client::Millau;
|
type Target = relay_millau_client::Millau;
|
||||||
@@ -101,7 +102,7 @@ impl CliChain for Rialto {
|
|||||||
&mut call,
|
&mut call,
|
||||||
bridge::RIALTO_TO_MILLAU_INDEX,
|
bridge::RIALTO_TO_MILLAU_INDEX,
|
||||||
);
|
);
|
||||||
let call = Target::encode_call(&call).map_err(|e| e.to_string())?;
|
let call = Target::encode_call(&call)?;
|
||||||
let weight = call.get_dispatch_info().weight;
|
let weight = call.get_dispatch_info().weight;
|
||||||
|
|
||||||
Ok(send_message::message_payload(
|
Ok(send_message::message_payload(
|
||||||
|
|||||||
@@ -249,6 +249,7 @@ pub async fn run(
|
|||||||
futures::future::pending(),
|
futures::future::pending(),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
.map_err(Into::into)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add standalone metrics for the Rialto -> Millau messages loop.
|
/// Add standalone metrics for the Rialto -> Millau messages loop.
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ impl CliChain for RialtoParachain {
|
|||||||
|
|
||||||
fn encode_message(
|
fn encode_message(
|
||||||
_message: encode_message::MessagePayload,
|
_message: encode_message::MessagePayload,
|
||||||
) -> Result<Self::MessagePayload, String> {
|
) -> anyhow::Result<Self::MessagePayload> {
|
||||||
Err("Not supported".into())
|
anyhow::bail!("Not supported")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
use anyhow::anyhow;
|
||||||
use codec::Decode;
|
use codec::Decode;
|
||||||
use frame_support::weights::{DispatchClass, DispatchInfo, Pays, Weight};
|
use frame_support::weights::{DispatchClass, DispatchInfo, Pays, Weight};
|
||||||
use relay_rococo_client::Rococo;
|
use relay_rococo_client::Rococo;
|
||||||
@@ -94,7 +95,7 @@ impl CliChain for Rococo {
|
|||||||
|
|
||||||
fn encode_message(
|
fn encode_message(
|
||||||
_message: encode_message::MessagePayload,
|
_message: encode_message::MessagePayload,
|
||||||
) -> Result<Self::MessagePayload, String> {
|
) -> anyhow::Result<Self::MessagePayload> {
|
||||||
Err("Sending messages from Rococo is not yet supported.".into())
|
Err(anyhow!("Sending messages from Rococo is not yet supported."))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -253,6 +253,7 @@ pub async fn run(
|
|||||||
futures::future::pending(),
|
futures::future::pending(),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
.map_err(Into::into)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add standalone metrics for the Rococo -> Wococo messages loop.
|
/// Add standalone metrics for the Rococo -> Wococo messages loop.
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
//! Westend chain specification for CLI.
|
//! Westend chain specification for CLI.
|
||||||
|
|
||||||
use crate::cli::{encode_message, CliChain};
|
use crate::cli::{encode_message, CliChain};
|
||||||
|
use anyhow::anyhow;
|
||||||
use frame_support::weights::Weight;
|
use frame_support::weights::Weight;
|
||||||
use relay_westend_client::Westend;
|
use relay_westend_client::Westend;
|
||||||
use sp_version::RuntimeVersion;
|
use sp_version::RuntimeVersion;
|
||||||
@@ -37,7 +38,7 @@ impl CliChain for Westend {
|
|||||||
|
|
||||||
fn encode_message(
|
fn encode_message(
|
||||||
_message: encode_message::MessagePayload,
|
_message: encode_message::MessagePayload,
|
||||||
) -> Result<Self::MessagePayload, String> {
|
) -> anyhow::Result<Self::MessagePayload> {
|
||||||
Err("Sending messages from Westend is not yet supported.".into())
|
Err(anyhow!("Sending messages from Westend is not yet supported."))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
use anyhow::anyhow;
|
||||||
use codec::Decode;
|
use codec::Decode;
|
||||||
use frame_support::weights::{DispatchClass, DispatchInfo, Pays, Weight};
|
use frame_support::weights::{DispatchClass, DispatchInfo, Pays, Weight};
|
||||||
use relay_wococo_client::Wococo;
|
use relay_wococo_client::Wococo;
|
||||||
@@ -88,7 +89,7 @@ impl CliChain for Wococo {
|
|||||||
|
|
||||||
fn encode_message(
|
fn encode_message(
|
||||||
_message: encode_message::MessagePayload,
|
_message: encode_message::MessagePayload,
|
||||||
) -> Result<Self::MessagePayload, String> {
|
) -> anyhow::Result<Self::MessagePayload> {
|
||||||
Err("Sending messages from Wococo is not yet supported.".into())
|
Err(anyhow!("Sending messages from Wococo is not yet supported."))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -252,6 +252,7 @@ pub async fn run(
|
|||||||
futures::future::pending(),
|
futures::future::pending(),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
.map_err(Into::into)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add standalone metrics for the Wococo -> Rococo messages loop.
|
/// Add standalone metrics for the Wococo -> Rococo messages loop.
|
||||||
|
|||||||
@@ -257,7 +257,7 @@ pub trait CliChain: relay_substrate_client::Chain {
|
|||||||
/// Construct message payload to be sent over the bridge.
|
/// Construct message payload to be sent over the bridge.
|
||||||
fn encode_message(
|
fn encode_message(
|
||||||
message: crate::cli::encode_message::MessagePayload,
|
message: crate::cli::encode_message::MessagePayload,
|
||||||
) -> Result<Self::MessagePayload, String>;
|
) -> anyhow::Result<Self::MessagePayload>;
|
||||||
|
|
||||||
/// Maximal extrinsic weight (from the runtime).
|
/// Maximal extrinsic weight (from the runtime).
|
||||||
fn max_extrinsic_weight() -> Weight;
|
fn max_extrinsic_weight() -> Weight;
|
||||||
|
|||||||
@@ -141,6 +141,7 @@ impl ResubmitTransactions {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
|
.map_err(Into::into)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,3 +17,4 @@ log = "0.4.11"
|
|||||||
relay-utils = { path = "../utils" }
|
relay-utils = { path = "../utils" }
|
||||||
tokio = "1.8"
|
tokio = "1.8"
|
||||||
web3 = { git = "https://github.com/svyatonik/rust-web3.git", branch = "bump-deps" }
|
web3 = { git = "https://github.com/svyatonik/rust-web3.git", branch = "bump-deps" }
|
||||||
|
thiserror = "1.0.26"
|
||||||
|
|||||||
@@ -20,49 +20,47 @@ use crate::types::U256;
|
|||||||
|
|
||||||
use jsonrpsee_ws_client::types::Error as RpcError;
|
use jsonrpsee_ws_client::types::Error as RpcError;
|
||||||
use relay_utils::MaybeConnectionError;
|
use relay_utils::MaybeConnectionError;
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
/// Result type used by Ethereum client.
|
/// Result type used by Ethereum client.
|
||||||
pub type Result<T> = std::result::Result<T, Error>;
|
pub type Result<T> = std::result::Result<T, Error>;
|
||||||
|
|
||||||
/// Errors that can occur only when interacting with
|
/// Errors that can occur only when interacting with
|
||||||
/// an Ethereum node through RPC.
|
/// an Ethereum node through RPC.
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Error)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
/// IO error.
|
/// IO error.
|
||||||
Io(std::io::Error),
|
#[error("IO error: {0}")]
|
||||||
|
Io(#[from] std::io::Error),
|
||||||
/// An error that can occur when making an HTTP request to
|
/// An error that can occur when making an HTTP request to
|
||||||
/// an JSON-RPC client.
|
/// an JSON-RPC client.
|
||||||
RpcError(RpcError),
|
#[error("RPC error: {0}")]
|
||||||
|
RpcError(#[from] RpcError),
|
||||||
/// Failed to parse response.
|
/// Failed to parse response.
|
||||||
|
#[error("Response parse failed: {0}")]
|
||||||
ResponseParseFailed(String),
|
ResponseParseFailed(String),
|
||||||
/// We have received a header with missing fields.
|
/// We have received a header with missing fields.
|
||||||
|
#[error("Incomplete Ethereum Header Received (missing some of required fields - hash, number, logs_bloom).")]
|
||||||
IncompleteHeader,
|
IncompleteHeader,
|
||||||
/// We have received a transaction missing a `raw` field.
|
/// We have received a transaction missing a `raw` field.
|
||||||
|
#[error("Incomplete Ethereum Transaction (missing required field - raw).")]
|
||||||
IncompleteTransaction,
|
IncompleteTransaction,
|
||||||
/// An invalid Substrate block number was received from
|
/// An invalid Substrate block number was received from
|
||||||
/// an Ethereum node.
|
/// an Ethereum node.
|
||||||
|
#[error("Received an invalid Substrate block from Ethereum Node.")]
|
||||||
InvalidSubstrateBlockNumber,
|
InvalidSubstrateBlockNumber,
|
||||||
/// An invalid index has been received from an Ethereum node.
|
/// An invalid index has been received from an Ethereum node.
|
||||||
|
#[error("Received an invalid incomplete index from Ethereum Node.")]
|
||||||
InvalidIncompleteIndex,
|
InvalidIncompleteIndex,
|
||||||
/// The client we're connected to is not synced, so we can't rely on its state. Contains
|
/// The client we're connected to is not synced, so we can't rely on its state. Contains
|
||||||
/// number of unsynced headers.
|
/// number of unsynced headers.
|
||||||
|
#[error("Ethereum client is not synced: syncing {0} headers.")]
|
||||||
ClientNotSynced(U256),
|
ClientNotSynced(U256),
|
||||||
/// Custom logic error.
|
/// Custom logic error.
|
||||||
|
#[error("{0}")]
|
||||||
Custom(String),
|
Custom(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<RpcError> for Error {
|
|
||||||
fn from(error: RpcError) -> Self {
|
|
||||||
Error::RpcError(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<std::io::Error> for Error {
|
|
||||||
fn from(error: std::io::Error) -> Self {
|
|
||||||
Error::Io(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<tokio::task::JoinError> for Error {
|
impl From<tokio::task::JoinError> for Error {
|
||||||
fn from(error: tokio::task::JoinError) -> Self {
|
fn from(error: tokio::task::JoinError) -> Self {
|
||||||
Error::Custom(format!("Failed to wait tokio task: {}", error))
|
Error::Custom(format!("Failed to wait tokio task: {}", error))
|
||||||
@@ -82,24 +80,3 @@ impl MaybeConnectionError for Error {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToString for Error {
|
|
||||||
fn to_string(&self) -> String {
|
|
||||||
match self {
|
|
||||||
Self::Io(e) => e.to_string(),
|
|
||||||
Self::RpcError(e) => e.to_string(),
|
|
||||||
Self::ResponseParseFailed(e) => e.to_string(),
|
|
||||||
Self::IncompleteHeader => {
|
|
||||||
"Incomplete Ethereum Header Received (missing some of required fields - hash, number, logs_bloom)"
|
|
||||||
.to_string()
|
|
||||||
}
|
|
||||||
Self::IncompleteTransaction => "Incomplete Ethereum Transaction (missing required field - raw)".to_string(),
|
|
||||||
Self::InvalidSubstrateBlockNumber => "Received an invalid Substrate block from Ethereum Node".to_string(),
|
|
||||||
Self::InvalidIncompleteIndex => "Received an invalid incomplete index from Ethereum Node".to_string(),
|
|
||||||
Self::ClientNotSynced(missing_headers) => {
|
|
||||||
format!("Ethereum client is not synced: syncing {} headers", missing_headers)
|
|
||||||
}
|
|
||||||
Self::Custom(ref e) => e.clone(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ log = "0.4.11"
|
|||||||
num-traits = "0.2"
|
num-traits = "0.2"
|
||||||
rand = "0.7"
|
rand = "0.7"
|
||||||
tokio = "1.8"
|
tokio = "1.8"
|
||||||
|
thiserror = "1.0.26"
|
||||||
|
|
||||||
# Bridge dependencies
|
# Bridge dependencies
|
||||||
|
|
||||||
|
|||||||
@@ -20,78 +20,54 @@ use jsonrpsee_ws_client::types::Error as RpcError;
|
|||||||
use relay_utils::MaybeConnectionError;
|
use relay_utils::MaybeConnectionError;
|
||||||
use sc_rpc_api::system::Health;
|
use sc_rpc_api::system::Health;
|
||||||
use sp_runtime::transaction_validity::TransactionValidityError;
|
use sp_runtime::transaction_validity::TransactionValidityError;
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
/// Result type used by Substrate client.
|
/// Result type used by Substrate client.
|
||||||
pub type Result<T> = std::result::Result<T, Error>;
|
pub type Result<T> = std::result::Result<T, Error>;
|
||||||
|
|
||||||
/// Errors that can occur only when interacting with
|
/// Errors that can occur only when interacting with
|
||||||
/// a Substrate node through RPC.
|
/// a Substrate node through RPC.
|
||||||
#[derive(Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
/// IO error.
|
/// IO error.
|
||||||
Io(std::io::Error),
|
#[error("IO error: {0}")]
|
||||||
|
Io(#[from] std::io::Error),
|
||||||
/// An error that can occur when making a request to
|
/// An error that can occur when making a request to
|
||||||
/// an JSON-RPC server.
|
/// an JSON-RPC server.
|
||||||
RpcError(RpcError),
|
#[error("RPC error: {0}")]
|
||||||
|
RpcError(#[from] RpcError),
|
||||||
/// The response from the server could not be SCALE decoded.
|
/// The response from the server could not be SCALE decoded.
|
||||||
ResponseParseFailed(codec::Error),
|
#[error("Response parse failed: {0}")]
|
||||||
|
ResponseParseFailed(#[from] codec::Error),
|
||||||
/// The Substrate bridge pallet has not yet been initialized.
|
/// The Substrate bridge pallet has not yet been initialized.
|
||||||
|
#[error("The Substrate bridge pallet has not been initialized yet.")]
|
||||||
UninitializedBridgePallet,
|
UninitializedBridgePallet,
|
||||||
/// Account does not exist on the chain.
|
/// Account does not exist on the chain.
|
||||||
|
#[error("Account does not exist on the chain.")]
|
||||||
AccountDoesNotExist,
|
AccountDoesNotExist,
|
||||||
/// Runtime storage is missing mandatory ":code:" entry.
|
/// Runtime storage is missing mandatory ":code:" entry.
|
||||||
|
#[error("Mandatory :code: entry is missing from runtime storage.")]
|
||||||
MissingMandatoryCodeEntry,
|
MissingMandatoryCodeEntry,
|
||||||
/// The client we're connected to is not synced, so we can't rely on its state.
|
/// The client we're connected to is not synced, so we can't rely on its state.
|
||||||
|
#[error("Substrate client is not synced {0}.")]
|
||||||
ClientNotSynced(Health),
|
ClientNotSynced(Health),
|
||||||
/// An error has happened when we have tried to parse storage proof.
|
/// An error has happened when we have tried to parse storage proof.
|
||||||
|
#[error("Error when parsing storage proof: {0:?}.")]
|
||||||
StorageProofError(bp_runtime::StorageProofError),
|
StorageProofError(bp_runtime::StorageProofError),
|
||||||
/// The Substrate transaction is invalid.
|
/// The Substrate transaction is invalid.
|
||||||
TransactionInvalid(TransactionValidityError),
|
#[error("Substrate transaction is invalid: {0:?}")]
|
||||||
|
TransactionInvalid(#[from] TransactionValidityError),
|
||||||
/// Custom logic error.
|
/// Custom logic error.
|
||||||
|
#[error("{0}")]
|
||||||
Custom(String),
|
Custom(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::error::Error for Error {
|
|
||||||
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
|
||||||
match self {
|
|
||||||
Self::Io(ref e) => Some(e),
|
|
||||||
Self::RpcError(ref e) => Some(e),
|
|
||||||
Self::ResponseParseFailed(ref e) => Some(e),
|
|
||||||
Self::UninitializedBridgePallet => None,
|
|
||||||
Self::AccountDoesNotExist => None,
|
|
||||||
Self::MissingMandatoryCodeEntry => None,
|
|
||||||
Self::ClientNotSynced(_) => None,
|
|
||||||
Self::StorageProofError(_) => None,
|
|
||||||
Self::TransactionInvalid(_) => None,
|
|
||||||
Self::Custom(_) => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<RpcError> for Error {
|
|
||||||
fn from(error: RpcError) -> Self {
|
|
||||||
Error::RpcError(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<std::io::Error> for Error {
|
|
||||||
fn from(error: std::io::Error) -> Self {
|
|
||||||
Error::Io(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<tokio::task::JoinError> for Error {
|
impl From<tokio::task::JoinError> for Error {
|
||||||
fn from(error: tokio::task::JoinError) -> Self {
|
fn from(error: tokio::task::JoinError) -> Self {
|
||||||
Error::Custom(format!("Failed to wait tokio task: {}", error))
|
Error::Custom(format!("Failed to wait tokio task: {}", error))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<TransactionValidityError> for Error {
|
|
||||||
fn from(error: TransactionValidityError) -> Self {
|
|
||||||
Error::TransactionInvalid(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MaybeConnectionError for Error {
|
impl MaybeConnectionError for Error {
|
||||||
fn is_connection_error(&self) -> bool {
|
fn is_connection_error(&self) -> bool {
|
||||||
matches!(
|
matches!(
|
||||||
@@ -105,30 +81,3 @@ impl MaybeConnectionError for Error {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for Error {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
||||||
let s = match self {
|
|
||||||
Self::Io(e) => e.to_string(),
|
|
||||||
Self::RpcError(e) => e.to_string(),
|
|
||||||
Self::ResponseParseFailed(e) => e.to_string(),
|
|
||||||
Self::UninitializedBridgePallet =>
|
|
||||||
"The Substrate bridge pallet has not been initialized yet.".into(),
|
|
||||||
Self::AccountDoesNotExist => "Account does not exist on the chain".into(),
|
|
||||||
Self::MissingMandatoryCodeEntry =>
|
|
||||||
"Mandatory :code: entry is missing from runtime storage".into(),
|
|
||||||
Self::StorageProofError(e) => format!("Error when parsing storage proof: {:?}", e),
|
|
||||||
Self::ClientNotSynced(health) => format!("Substrate client is not synced: {}", health),
|
|
||||||
Self::TransactionInvalid(e) => format!("Substrate transaction is invalid: {:?}", e),
|
|
||||||
Self::Custom(e) => e.clone(),
|
|
||||||
};
|
|
||||||
|
|
||||||
write!(f, "{}", s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Error> for String {
|
|
||||||
fn from(error: Error) -> String {
|
|
||||||
error.to_string()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -17,23 +17,30 @@
|
|||||||
//! Pallet provides a set of guard functions that are running in background threads
|
//! Pallet provides a set of guard functions that are running in background threads
|
||||||
//! and are aborting process if some condition fails.
|
//! and are aborting process if some condition fails.
|
||||||
|
|
||||||
use crate::{Chain, ChainWithBalances, Client};
|
use crate::{error::Error, Chain, ChainWithBalances, Client};
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use num_traits::CheckedSub;
|
use num_traits::CheckedSub;
|
||||||
use sp_version::RuntimeVersion;
|
use sp_version::RuntimeVersion;
|
||||||
use std::{
|
use std::{
|
||||||
collections::VecDeque,
|
collections::VecDeque,
|
||||||
|
fmt::Display,
|
||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Guards environment.
|
/// Guards environment.
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub trait Environment<C: ChainWithBalances>: Send + Sync + 'static {
|
pub trait Environment<C: ChainWithBalances>: Send + Sync + 'static {
|
||||||
|
/// Error type.
|
||||||
|
type Error: Display + Send + Sync + 'static;
|
||||||
|
|
||||||
/// Return current runtime version.
|
/// Return current runtime version.
|
||||||
async fn runtime_version(&mut self) -> Result<RuntimeVersion, String>;
|
async fn runtime_version(&mut self) -> Result<RuntimeVersion, Self::Error>;
|
||||||
/// Return free native balance of the account on the chain.
|
/// Return free native balance of the account on the chain.
|
||||||
async fn free_native_balance(&mut self, account: C::AccountId) -> Result<C::Balance, String>;
|
async fn free_native_balance(
|
||||||
|
&mut self,
|
||||||
|
account: C::AccountId,
|
||||||
|
) -> Result<C::Balance, Self::Error>;
|
||||||
|
|
||||||
/// Return current time.
|
/// Return current time.
|
||||||
fn now(&self) -> Instant {
|
fn now(&self) -> Instant {
|
||||||
@@ -74,7 +81,7 @@ pub fn abort_on_spec_version_change<C: ChainWithBalances>(
|
|||||||
},
|
},
|
||||||
Err(error) => log::warn!(
|
Err(error) => log::warn!(
|
||||||
target: "bridge-guard",
|
target: "bridge-guard",
|
||||||
"Failed to read {} runtime version: {:?}. Relay may need to be stopped manually",
|
"Failed to read {} runtime version: {}. Relay may need to be stopped manually",
|
||||||
C::NAME,
|
C::NAME,
|
||||||
error,
|
error,
|
||||||
),
|
),
|
||||||
@@ -137,7 +144,7 @@ pub fn abort_when_account_balance_decreased<C: ChainWithBalances>(
|
|||||||
Err(error) => {
|
Err(error) => {
|
||||||
log::warn!(
|
log::warn!(
|
||||||
target: "bridge-guard",
|
target: "bridge-guard",
|
||||||
"Failed to read {} account {:?} balance: {:?}. Relay may need to be stopped manually",
|
"Failed to read {} account {:?} balance: {}. Relay may need to be stopped manually",
|
||||||
C::NAME,
|
C::NAME,
|
||||||
account_id,
|
account_id,
|
||||||
error,
|
error,
|
||||||
@@ -157,12 +164,17 @@ fn conditions_check_delay<C: Chain>() -> Duration {
|
|||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl<C: ChainWithBalances> Environment<C> for Client<C> {
|
impl<C: ChainWithBalances> Environment<C> for Client<C> {
|
||||||
async fn runtime_version(&mut self) -> Result<RuntimeVersion, String> {
|
type Error = Error;
|
||||||
Client::<C>::runtime_version(self).await.map_err(|e| e.to_string())
|
|
||||||
|
async fn runtime_version(&mut self) -> Result<RuntimeVersion, Self::Error> {
|
||||||
|
Client::<C>::runtime_version(self).await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn free_native_balance(&mut self, account: C::AccountId) -> Result<C::Balance, String> {
|
async fn free_native_balance(
|
||||||
Client::<C>::free_native_balance(self, account).await.map_err(|e| e.to_string())
|
&mut self,
|
||||||
|
account: C::AccountId,
|
||||||
|
) -> Result<C::Balance, Self::Error> {
|
||||||
|
Client::<C>::free_native_balance(self, account).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -220,11 +232,13 @@ mod tests {
|
|||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Environment<TestChain> for TestEnvironment {
|
impl Environment<TestChain> for TestEnvironment {
|
||||||
async fn runtime_version(&mut self) -> Result<RuntimeVersion, String> {
|
type Error = Error;
|
||||||
|
|
||||||
|
async fn runtime_version(&mut self) -> Result<RuntimeVersion, Self::Error> {
|
||||||
Ok(self.runtime_version_rx.next().await.unwrap_or_default())
|
Ok(self.runtime_version_rx.next().await.unwrap_or_default())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn free_native_balance(&mut self, _account: u32) -> Result<u32, String> {
|
async fn free_native_balance(&mut self, _account: u32) -> Result<u32, Self::Error> {
|
||||||
Ok(self.free_native_balance_rx.next().await.unwrap_or_default())
|
Ok(self.free_native_balance_rx.next().await.unwrap_or_default())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,3 +15,4 @@ log = "0.4.11"
|
|||||||
num-traits = "0.2"
|
num-traits = "0.2"
|
||||||
parking_lot = "0.11.0"
|
parking_lot = "0.11.0"
|
||||||
relay-utils = { path = "../utils" }
|
relay-utils = { path = "../utils" }
|
||||||
|
thiserror = "1.0.26"
|
||||||
|
|||||||
@@ -0,0 +1,66 @@
|
|||||||
|
// Copyright 2019-2021 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/>.
|
||||||
|
|
||||||
|
//! Exchange-relay errors.
|
||||||
|
|
||||||
|
use crate::exchange::{BlockHashOf, BlockNumberOf, TransactionHashOf};
|
||||||
|
|
||||||
|
use relay_utils::MaybeConnectionError;
|
||||||
|
use std::fmt::{Debug, Display};
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
/// Error type given pipeline.
|
||||||
|
pub type ErrorOf<P> = Error<BlockHashOf<P>, BlockNumberOf<P>, TransactionHashOf<P>>;
|
||||||
|
|
||||||
|
/// Exchange-relay error type.
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum Error<Hash: Display, HeaderNumber: Display, SourceTxHash: Display> {
|
||||||
|
/// Failed to check finality of the requested header on the target node.
|
||||||
|
#[error("Failed to check finality of header {0}/{1} on {2} node: {3:?}")]
|
||||||
|
Finality(HeaderNumber, Hash, &'static str, anyhow::Error),
|
||||||
|
/// Error retrieving block from the source node.
|
||||||
|
#[error("Error retrieving block {0} from {1} node: {2:?}")]
|
||||||
|
RetrievingBlock(Hash, &'static str, anyhow::Error),
|
||||||
|
/// Error retrieving transaction from the source node.
|
||||||
|
#[error("Error retrieving transaction {0} from {1} node: {2:?}")]
|
||||||
|
RetrievingTransaction(SourceTxHash, &'static str, anyhow::Error),
|
||||||
|
/// Failed to check existence of header from the target node.
|
||||||
|
#[error("Failed to check existence of header {0}/{1} on {2} node: {3:?}")]
|
||||||
|
CheckHeaderExistence(HeaderNumber, Hash, &'static str, anyhow::Error),
|
||||||
|
/// Failed to prepare proof for the transaction from the source node.
|
||||||
|
#[error("Error building transaction {0} proof on {1} node: {2:?}")]
|
||||||
|
BuildTransactionProof(String, &'static str, anyhow::Error, bool),
|
||||||
|
/// Failed to submit the transaction proof to the target node.
|
||||||
|
#[error("Error submitting transaction {0} proof to {1} node: {2:?}")]
|
||||||
|
SubmitTransactionProof(String, &'static str, anyhow::Error, bool),
|
||||||
|
/// Transaction filtering failed.
|
||||||
|
#[error("Transaction filtering has failed with {0:?}")]
|
||||||
|
TransactionFiltering(anyhow::Error, bool),
|
||||||
|
/// Utilities/metrics error.
|
||||||
|
#[error("{0}")]
|
||||||
|
Utils(#[from] relay_utils::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Display, U: Display, V: Display> MaybeConnectionError for Error<T, U, V> {
|
||||||
|
fn is_connection_error(&self) -> bool {
|
||||||
|
match *self {
|
||||||
|
Self::BuildTransactionProof(_, _, _, b) => b,
|
||||||
|
Self::SubmitTransactionProof(_, _, _, b) => b,
|
||||||
|
Self::TransactionFiltering(_, b) => b,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,11 +16,11 @@
|
|||||||
|
|
||||||
//! Relaying proofs of exchange transaction.
|
//! Relaying proofs of exchange transaction.
|
||||||
|
|
||||||
|
use crate::error::{Error, ErrorOf};
|
||||||
|
|
||||||
|
use anyhow::anyhow;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use relay_utils::{
|
use relay_utils::{relay_loop::Client as RelayClient, FailedClient, MaybeConnectionError};
|
||||||
relay_loop::Client as RelayClient, FailedClient, MaybeConnectionError,
|
|
||||||
StringifiedMaybeConnectionError,
|
|
||||||
};
|
|
||||||
use std::{
|
use std::{
|
||||||
fmt::{Debug, Display},
|
fmt::{Debug, Display},
|
||||||
string::ToString,
|
string::ToString,
|
||||||
@@ -67,7 +67,7 @@ pub trait SourceBlock: 'static + Send + Sync {
|
|||||||
/// Transaction that is participating in exchange.
|
/// Transaction that is participating in exchange.
|
||||||
pub trait SourceTransaction: 'static + Send {
|
pub trait SourceTransaction: 'static + Send {
|
||||||
/// Transaction hash type.
|
/// Transaction hash type.
|
||||||
type Hash: Debug + Display;
|
type Hash: Debug + Display + Clone;
|
||||||
|
|
||||||
/// Return transaction hash.
|
/// Return transaction hash.
|
||||||
fn hash(&self) -> Self::Hash;
|
fn hash(&self) -> Self::Hash;
|
||||||
@@ -117,7 +117,7 @@ pub trait TargetClient<P: TransactionProofPipeline>: RelayClient {
|
|||||||
/// Sleep until exchange-related data is (probably) updated.
|
/// Sleep until exchange-related data is (probably) updated.
|
||||||
async fn tick(&self);
|
async fn tick(&self);
|
||||||
/// Returns `Ok(true)` if header is known to the target node.
|
/// Returns `Ok(true)` if header is known to the target node.
|
||||||
async fn is_header_known(&self, id: &HeaderId<P>) -> Result<bool, Self::Error>;
|
async fn is_header_known(&self, id: &HeaderId<P>) -> std::result::Result<bool, Self::Error>;
|
||||||
/// Returns `Ok(true)` if header is finalized by the target node.
|
/// Returns `Ok(true)` if header is finalized by the target node.
|
||||||
async fn is_header_finalized(&self, id: &HeaderId<P>) -> Result<bool, Self::Error>;
|
async fn is_header_finalized(&self, id: &HeaderId<P>) -> Result<bool, Self::Error>;
|
||||||
/// Returns best finalized header id.
|
/// Returns best finalized header id.
|
||||||
@@ -178,9 +178,9 @@ pub async fn relay_block_transactions<P: TransactionProofPipeline>(
|
|||||||
target_client.filter_transaction_proof(&source_tx_proof).await.map_err(|err| {
|
target_client.filter_transaction_proof(&source_tx_proof).await.map_err(|err| {
|
||||||
(
|
(
|
||||||
FailedClient::Target,
|
FailedClient::Target,
|
||||||
StringifiedMaybeConnectionError::new(
|
Error::TransactionFiltering(
|
||||||
|
anyhow!("{:?}", err),
|
||||||
err.is_connection_error(),
|
err.is_connection_error(),
|
||||||
format!("Transaction filtering has failed with {:?}", err),
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
@@ -256,20 +256,14 @@ pub async fn relay_single_transaction_proof<P: TransactionProofPipeline>(
|
|||||||
source_client: &impl SourceClient<P>,
|
source_client: &impl SourceClient<P>,
|
||||||
target_client: &impl TargetClient<P>,
|
target_client: &impl TargetClient<P>,
|
||||||
source_tx_hash: TransactionHashOf<P>,
|
source_tx_hash: TransactionHashOf<P>,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), ErrorOf<P>> {
|
||||||
// wait for transaction and header on source node
|
// wait for transaction and header on source node
|
||||||
let (source_header_id, source_tx_index) =
|
let (source_header_id, source_tx_index) =
|
||||||
wait_transaction_mined(source_client, &source_tx_hash).await?;
|
wait_transaction_mined(source_client, &source_tx_hash).await?;
|
||||||
let source_block = source_client.block_by_hash(source_header_id.1.clone()).await;
|
let source_block = source_client.block_by_hash(source_header_id.1.clone()).await;
|
||||||
let source_block = source_block.map_err(|err| {
|
let source_block = source_block.map_err(|err| {
|
||||||
format!(
|
Error::RetrievingBlock(source_header_id.1.clone(), P::SOURCE_NAME, anyhow!("{:?}", err))
|
||||||
"Error retrieving block {} from {} node: {:?}",
|
|
||||||
source_header_id.1,
|
|
||||||
P::SOURCE_NAME,
|
|
||||||
err,
|
|
||||||
)
|
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
// wait for transaction and header on target node
|
// wait for transaction and header on target node
|
||||||
wait_header_imported(target_client, &source_header_id).await?;
|
wait_header_imported(target_client, &source_header_id).await?;
|
||||||
wait_header_finalized(target_client, &source_header_id).await?;
|
wait_header_finalized(target_client, &source_header_id).await?;
|
||||||
@@ -280,11 +274,10 @@ pub async fn relay_single_transaction_proof<P: TransactionProofPipeline>(
|
|||||||
target_client,
|
target_client,
|
||||||
&source_tx_id,
|
&source_tx_id,
|
||||||
prepare_transaction_proof(source_client, &source_tx_id, &source_block, source_tx_index)
|
prepare_transaction_proof(source_client, &source_tx_id, &source_block, source_tx_index)
|
||||||
.await
|
.await?,
|
||||||
.map_err(|err| err.to_string())?,
|
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(|err| err.to_string())
|
.map_err(Into::into)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Prepare transaction proof.
|
/// Prepare transaction proof.
|
||||||
@@ -293,19 +286,16 @@ async fn prepare_transaction_proof<P: TransactionProofPipeline>(
|
|||||||
source_tx_id: &str,
|
source_tx_id: &str,
|
||||||
source_block: &P::Block,
|
source_block: &P::Block,
|
||||||
source_tx_index: usize,
|
source_tx_index: usize,
|
||||||
) -> Result<P::TransactionProof, StringifiedMaybeConnectionError> {
|
) -> Result<P::TransactionProof, ErrorOf<P>> {
|
||||||
source_client
|
source_client
|
||||||
.transaction_proof(source_block, source_tx_index)
|
.transaction_proof(source_block, source_tx_index)
|
||||||
.await
|
.await
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
StringifiedMaybeConnectionError::new(
|
Error::BuildTransactionProof(
|
||||||
err.is_connection_error(),
|
source_tx_id.to_owned(),
|
||||||
format!(
|
|
||||||
"Error building transaction {} proof on {} node: {:?}",
|
|
||||||
source_tx_id,
|
|
||||||
P::SOURCE_NAME,
|
P::SOURCE_NAME,
|
||||||
err,
|
anyhow!("{:?}", err),
|
||||||
),
|
err.is_connection_error(),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -315,16 +305,13 @@ async fn relay_ready_transaction_proof<P: TransactionProofPipeline>(
|
|||||||
target_client: &impl TargetClient<P>,
|
target_client: &impl TargetClient<P>,
|
||||||
source_tx_id: &str,
|
source_tx_id: &str,
|
||||||
source_tx_proof: P::TransactionProof,
|
source_tx_proof: P::TransactionProof,
|
||||||
) -> Result<(), StringifiedMaybeConnectionError> {
|
) -> Result<(), ErrorOf<P>> {
|
||||||
target_client.submit_transaction_proof(source_tx_proof).await.map_err(|err| {
|
target_client.submit_transaction_proof(source_tx_proof).await.map_err(|err| {
|
||||||
StringifiedMaybeConnectionError::new(
|
Error::SubmitTransactionProof(
|
||||||
err.is_connection_error(),
|
source_tx_id.to_owned(),
|
||||||
format!(
|
|
||||||
"Error submitting transaction {} proof to {} node: {:?}",
|
|
||||||
source_tx_id,
|
|
||||||
P::TARGET_NAME,
|
P::TARGET_NAME,
|
||||||
err,
|
anyhow!("{:?}", err),
|
||||||
),
|
err.is_connection_error(),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -333,15 +320,14 @@ async fn relay_ready_transaction_proof<P: TransactionProofPipeline>(
|
|||||||
async fn wait_transaction_mined<P: TransactionProofPipeline>(
|
async fn wait_transaction_mined<P: TransactionProofPipeline>(
|
||||||
source_client: &impl SourceClient<P>,
|
source_client: &impl SourceClient<P>,
|
||||||
source_tx_hash: &TransactionHashOf<P>,
|
source_tx_hash: &TransactionHashOf<P>,
|
||||||
) -> Result<(HeaderId<P>, usize), String> {
|
) -> Result<(HeaderId<P>, usize), ErrorOf<P>> {
|
||||||
loop {
|
loop {
|
||||||
let source_header_and_tx =
|
let source_header_and_tx =
|
||||||
source_client.transaction_block(source_tx_hash).await.map_err(|err| {
|
source_client.transaction_block(source_tx_hash).await.map_err(|err| {
|
||||||
format!(
|
Error::RetrievingTransaction(
|
||||||
"Error retrieving transaction {} from {} node: {:?}",
|
source_tx_hash.clone(),
|
||||||
source_tx_hash,
|
|
||||||
P::SOURCE_NAME,
|
P::SOURCE_NAME,
|
||||||
err,
|
anyhow!("{:?}", err),
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
match source_header_and_tx {
|
match source_header_and_tx {
|
||||||
@@ -373,16 +359,15 @@ async fn wait_transaction_mined<P: TransactionProofPipeline>(
|
|||||||
async fn wait_header_imported<P: TransactionProofPipeline>(
|
async fn wait_header_imported<P: TransactionProofPipeline>(
|
||||||
target_client: &impl TargetClient<P>,
|
target_client: &impl TargetClient<P>,
|
||||||
source_header_id: &HeaderId<P>,
|
source_header_id: &HeaderId<P>,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), ErrorOf<P>> {
|
||||||
loop {
|
loop {
|
||||||
let is_header_known =
|
let is_header_known =
|
||||||
target_client.is_header_known(source_header_id).await.map_err(|err| {
|
target_client.is_header_known(source_header_id).await.map_err(|err| {
|
||||||
format!(
|
Error::CheckHeaderExistence(
|
||||||
"Failed to check existence of header {}/{} on {} node: {:?}",
|
|
||||||
source_header_id.0,
|
source_header_id.0,
|
||||||
source_header_id.1,
|
source_header_id.1.clone(),
|
||||||
P::TARGET_NAME,
|
P::TARGET_NAME,
|
||||||
err,
|
anyhow!("{:?}", err),
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
match is_header_known {
|
match is_header_known {
|
||||||
@@ -416,16 +401,15 @@ async fn wait_header_imported<P: TransactionProofPipeline>(
|
|||||||
async fn wait_header_finalized<P: TransactionProofPipeline>(
|
async fn wait_header_finalized<P: TransactionProofPipeline>(
|
||||||
target_client: &impl TargetClient<P>,
|
target_client: &impl TargetClient<P>,
|
||||||
source_header_id: &HeaderId<P>,
|
source_header_id: &HeaderId<P>,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), ErrorOf<P>> {
|
||||||
loop {
|
loop {
|
||||||
let is_header_finalized =
|
let is_header_finalized =
|
||||||
target_client.is_header_finalized(source_header_id).await.map_err(|err| {
|
target_client.is_header_finalized(source_header_id).await.map_err(|err| {
|
||||||
format!(
|
Error::Finality(
|
||||||
"Failed to check finality of header {}/{} on {} node: {:?}",
|
|
||||||
source_header_id.0,
|
source_header_id.0,
|
||||||
source_header_id.1,
|
source_header_id.1.clone(),
|
||||||
P::TARGET_NAME,
|
P::TARGET_NAME,
|
||||||
err,
|
anyhow!("{:?}", err),
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
match is_header_finalized {
|
match is_header_finalized {
|
||||||
@@ -691,14 +675,12 @@ pub(crate) mod tests {
|
|||||||
source: &TestTransactionsSource,
|
source: &TestTransactionsSource,
|
||||||
target: &TestTransactionsTarget,
|
target: &TestTransactionsTarget,
|
||||||
) {
|
) {
|
||||||
assert_eq!(
|
assert!(async_std::task::block_on(relay_single_transaction_proof(
|
||||||
async_std::task::block_on(relay_single_transaction_proof(
|
|
||||||
source,
|
source,
|
||||||
target,
|
target,
|
||||||
test_transaction_hash(0),
|
test_transaction_hash(0)
|
||||||
)),
|
))
|
||||||
Ok(()),
|
.is_ok());
|
||||||
);
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
target.data.lock().submitted_proofs,
|
target.data.lock().submitted_proofs,
|
||||||
vec![TestTransactionProof(test_transaction_hash(0))],
|
vec![TestTransactionProof(test_transaction_hash(0))],
|
||||||
@@ -711,7 +693,7 @@ pub(crate) mod tests {
|
|||||||
&target,
|
&target,
|
||||||
test_transaction_hash(0),
|
test_transaction_hash(0),
|
||||||
))
|
))
|
||||||
.is_err(),);
|
.is_err());
|
||||||
assert!(target.data.lock().submitted_proofs.is_empty());
|
assert!(target.data.lock().submitted_proofs.is_empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
//! Relaying proofs of exchange transactions.
|
//! Relaying proofs of exchange transactions.
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
error::Error,
|
||||||
exchange::{
|
exchange::{
|
||||||
relay_block_transactions, BlockNumberOf, RelayedBlockTransactions, SourceClient,
|
relay_block_transactions, BlockNumberOf, RelayedBlockTransactions, SourceClient,
|
||||||
TargetClient, TransactionProofPipeline,
|
TargetClient, TransactionProofPipeline,
|
||||||
@@ -24,6 +25,7 @@ use crate::{
|
|||||||
exchange_loop_metrics::ExchangeLoopMetrics,
|
exchange_loop_metrics::ExchangeLoopMetrics,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use crate::error::ErrorOf;
|
||||||
use backoff::backoff::Backoff;
|
use backoff::backoff::Backoff;
|
||||||
use futures::{future::FutureExt, select};
|
use futures::{future::FutureExt, select};
|
||||||
use num_traits::One;
|
use num_traits::One;
|
||||||
@@ -92,7 +94,7 @@ pub async fn run<P: TransactionProofPipeline>(
|
|||||||
target_client: impl TargetClient<P>,
|
target_client: impl TargetClient<P>,
|
||||||
metrics_params: MetricsParams,
|
metrics_params: MetricsParams,
|
||||||
exit_signal: impl Future<Output = ()> + 'static + Send,
|
exit_signal: impl Future<Output = ()> + 'static + Send,
|
||||||
) -> anyhow::Result<()> {
|
) -> Result<(), ErrorOf<P>> {
|
||||||
let exit_signal = exit_signal.shared();
|
let exit_signal = exit_signal.shared();
|
||||||
|
|
||||||
relay_utils::relay_loop(source_client, target_client)
|
relay_utils::relay_loop(source_client, target_client)
|
||||||
@@ -111,6 +113,7 @@ pub async fn run<P: TransactionProofPipeline>(
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
|
.map_err(Error::Utils)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Run proofs synchronization.
|
/// Run proofs synchronization.
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
|
|
||||||
#![warn(missing_docs)]
|
#![warn(missing_docs)]
|
||||||
|
|
||||||
|
pub mod error;
|
||||||
pub mod exchange;
|
pub mod exchange;
|
||||||
pub mod exchange_loop;
|
pub mod exchange_loop;
|
||||||
pub mod exchange_loop_metrics;
|
pub mod exchange_loop_metrics;
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0"
|
|||||||
description = "Finality proofs relay"
|
description = "Finality proofs relay"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0"
|
|
||||||
async-std = "1.6.5"
|
async-std = "1.6.5"
|
||||||
async-trait = "0.1.40"
|
async-trait = "0.1.40"
|
||||||
backoff = "0.2"
|
backoff = "0.2"
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ pub async fn run<P: FinalitySyncPipeline>(
|
|||||||
sync_params: FinalitySyncParams,
|
sync_params: FinalitySyncParams,
|
||||||
metrics_params: MetricsParams,
|
metrics_params: MetricsParams,
|
||||||
exit_signal: impl Future<Output = ()> + 'static + Send,
|
exit_signal: impl Future<Output = ()> + 'static + Send,
|
||||||
) -> anyhow::Result<()> {
|
) -> Result<(), relay_utils::Error> {
|
||||||
let exit_signal = exit_signal.shared();
|
let exit_signal = exit_signal.shared();
|
||||||
relay_utils::relay_loop(source_client, target_client)
|
relay_utils::relay_loop(source_client, target_client)
|
||||||
.with_metrics(Some(metrics_prefix::<P>()), metrics_params)
|
.with_metrics(Some(metrics_prefix::<P>()), metrics_params)
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ edition = "2018"
|
|||||||
license = "GPL-3.0-or-later WITH Classpath-exception-2.0"
|
license = "GPL-3.0-or-later WITH Classpath-exception-2.0"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0"
|
|
||||||
async-std = "1.6.5"
|
async-std = "1.6.5"
|
||||||
async-trait = "0.1.40"
|
async-trait = "0.1.40"
|
||||||
backoff = "0.2"
|
backoff = "0.2"
|
||||||
|
|||||||
@@ -143,7 +143,7 @@ pub async fn run<P: HeadersSyncPipeline, TC: TargetClient<P>>(
|
|||||||
sync_params: HeadersSyncParams,
|
sync_params: HeadersSyncParams,
|
||||||
metrics_params: MetricsParams,
|
metrics_params: MetricsParams,
|
||||||
exit_signal: impl Future<Output = ()> + 'static + Send,
|
exit_signal: impl Future<Output = ()> + 'static + Send,
|
||||||
) -> anyhow::Result<()> {
|
) -> Result<(), relay_utils::Error> {
|
||||||
let exit_signal = exit_signal.shared();
|
let exit_signal = exit_signal.shared();
|
||||||
relay_utils::relay_loop(source_client, target_client)
|
relay_utils::relay_loop(source_client, target_client)
|
||||||
.with_metrics(Some(metrics_prefix::<P>()), metrics_params)
|
.with_metrics(Some(metrics_prefix::<P>()), metrics_params)
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
|
thiserror = "1.0.26"
|
||||||
async-std = "1.9.0"
|
async-std = "1.9.0"
|
||||||
async-trait = "0.1.42"
|
async-trait = "0.1.42"
|
||||||
codec = { package = "parity-scale-codec", version = "2.2.0" }
|
codec = { package = "parity-scale-codec", version = "2.2.0" }
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
// Copyright 2019-2021 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/>.
|
||||||
|
|
||||||
|
//! Relay errors.
|
||||||
|
|
||||||
|
use relay_substrate_client as client;
|
||||||
|
use sp_finality_grandpa::AuthorityList;
|
||||||
|
use sp_runtime::traits::MaybeDisplay;
|
||||||
|
use std::fmt::Debug;
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
/// Relay errors.
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum Error<Hash: Debug + MaybeDisplay, HeaderNumber: Debug + MaybeDisplay> {
|
||||||
|
/// Failed to submit signed extrinsic from to the target chain.
|
||||||
|
#[error("Failed to submit {0} transaction: {1:?}")]
|
||||||
|
SubmitTransaction(&'static str, client::Error),
|
||||||
|
/// Failed subscribe to justification stream of the source chain.
|
||||||
|
#[error("Failed to subscribe to {0} justifications: {1:?}")]
|
||||||
|
Subscribe(&'static str, client::Error),
|
||||||
|
/// Failed subscribe to read justification from the source chain (client error).
|
||||||
|
#[error("Failed to read {0} justification from the stream: {1}")]
|
||||||
|
ReadJustification(&'static str, client::Error),
|
||||||
|
/// Failed subscribe to read justification from the source chain (stream ended).
|
||||||
|
#[error("Failed to read {0} justification from the stream: stream has ended unexpectedly")]
|
||||||
|
ReadJustificationStreamEnded(&'static str),
|
||||||
|
/// Failed subscribe to decode justification from the source chain.
|
||||||
|
#[error("Failed to decode {0} justification: {1:?}")]
|
||||||
|
DecodeJustification(&'static str, codec::Error),
|
||||||
|
/// GRANDPA authorities read from the source chain are invalid.
|
||||||
|
#[error("Read invalid {0} authorities set: {1:?}")]
|
||||||
|
ReadInvalidAuthorities(&'static str, AuthorityList),
|
||||||
|
/// Failed to guess initial GRANDPA authorities at the given header of the source chain.
|
||||||
|
#[error("Failed to guess initial {0} GRANDPA authorities set id: checked all possible ids in range [0; {1}]")]
|
||||||
|
GuessInitialAuthorities(&'static str, HeaderNumber),
|
||||||
|
/// Failed to retrieve GRANDPA authorities at the given header from the source chain.
|
||||||
|
#[error("Failed to retrive {0} GRANDPA authorities set at header {1}: {2:?}")]
|
||||||
|
RetrieveAuthorities(&'static str, Hash, client::Error),
|
||||||
|
/// Failed to decode GRANDPA authorities at the given header of the source chain.
|
||||||
|
#[error("Failed to decode {0} GRANDPA authorities set at header {1}: {2:?}")]
|
||||||
|
DecodeAuthorities(&'static str, Hash, codec::Error),
|
||||||
|
/// Failed to retrieve header by the hash from the source chain.
|
||||||
|
#[error("Failed to retrieve {0} header with hash {1}: {:?}")]
|
||||||
|
RetrieveHeader(&'static str, Hash, client::Error),
|
||||||
|
}
|
||||||
@@ -21,6 +21,8 @@
|
|||||||
//! and authorities set from source to target chain. The headers sync starts
|
//! and authorities set from source to target chain. The headers sync starts
|
||||||
//! with this header.
|
//! with this header.
|
||||||
|
|
||||||
|
use crate::error::Error;
|
||||||
|
|
||||||
use bp_header_chain::{
|
use bp_header_chain::{
|
||||||
find_grandpa_authorities_scheduled_change,
|
find_grandpa_authorities_scheduled_change,
|
||||||
justification::{verify_justification, GrandpaJustification},
|
justification::{verify_justification, GrandpaJustification},
|
||||||
@@ -32,7 +34,7 @@ use num_traits::{One, Zero};
|
|||||||
use relay_substrate_client::{Chain, Client};
|
use relay_substrate_client::{Chain, Client};
|
||||||
use sp_core::Bytes;
|
use sp_core::Bytes;
|
||||||
use sp_finality_grandpa::AuthorityList as GrandpaAuthoritiesSet;
|
use sp_finality_grandpa::AuthorityList as GrandpaAuthoritiesSet;
|
||||||
use sp_runtime::traits::Header as HeaderT;
|
use sp_runtime::traits::{Header as HeaderT, Header};
|
||||||
|
|
||||||
/// Submit headers-bridge initialization transaction.
|
/// Submit headers-bridge initialization transaction.
|
||||||
pub async fn initialize<SourceChain: Chain, TargetChain: Chain>(
|
pub async fn initialize<SourceChain: Chain, TargetChain: Chain>(
|
||||||
@@ -77,7 +79,7 @@ async fn do_initialize<SourceChain: Chain, TargetChain: Chain>(
|
|||||||
prepare_initialize_transaction: impl FnOnce(TargetChain::Index, InitializationData<SourceChain::Header>) -> Bytes
|
prepare_initialize_transaction: impl FnOnce(TargetChain::Index, InitializationData<SourceChain::Header>) -> Bytes
|
||||||
+ Send
|
+ Send
|
||||||
+ 'static,
|
+ 'static,
|
||||||
) -> Result<TargetChain::Hash, String> {
|
) -> Result<TargetChain::Hash, Error<SourceChain::Hash, <SourceChain::Header as Header>::Number>> {
|
||||||
let initialization_data = prepare_initialization_data(source_client).await?;
|
let initialization_data = prepare_initialization_data(source_client).await?;
|
||||||
log::info!(
|
log::info!(
|
||||||
target: "bridge",
|
target: "bridge",
|
||||||
@@ -92,41 +94,40 @@ async fn do_initialize<SourceChain: Chain, TargetChain: Chain>(
|
|||||||
prepare_initialize_transaction(transaction_nonce, initialization_data)
|
prepare_initialize_transaction(transaction_nonce, initialization_data)
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.map_err(|err| format!("Failed to submit {} transaction: {:?}", TargetChain::NAME, err))?;
|
.map_err(|err| Error::SubmitTransaction(TargetChain::NAME, err))?;
|
||||||
Ok(initialization_tx_hash)
|
Ok(initialization_tx_hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Prepare initialization data for the GRANDPA verifier pallet.
|
/// Prepare initialization data for the GRANDPA verifier pallet.
|
||||||
async fn prepare_initialization_data<SourceChain: Chain>(
|
async fn prepare_initialization_data<SourceChain: Chain>(
|
||||||
source_client: Client<SourceChain>,
|
source_client: Client<SourceChain>,
|
||||||
) -> Result<InitializationData<SourceChain::Header>, String> {
|
) -> Result<
|
||||||
|
InitializationData<SourceChain::Header>,
|
||||||
|
Error<SourceChain::Hash, <SourceChain::Header as Header>::Number>,
|
||||||
|
> {
|
||||||
// In ideal world we just need to get best finalized header and then to read GRANDPA authorities
|
// In ideal world we just need to get best finalized header and then to read GRANDPA authorities
|
||||||
// set (`pallet_grandpa::CurrentSetId` + `GrandpaApi::grandpa_authorities()`) at this header.
|
// set (`pallet_grandpa::CurrentSetId` + `GrandpaApi::grandpa_authorities()`) at this header.
|
||||||
//
|
//
|
||||||
// But now there are problems with this approach - `CurrentSetId` may return invalid value. So
|
// But now there are problems with this approach - `CurrentSetId` may return invalid value. So
|
||||||
// here we're waiting for the next justification, read the authorities set and then try to
|
// here we're waiting for the next justification, read the authorities set and then try to
|
||||||
// figure out the set id with bruteforce.
|
// figure out the set id with bruteforce.
|
||||||
let justifications = source_client.subscribe_justifications().await.map_err(|err| {
|
let justifications = source_client
|
||||||
format!("Failed to subscribe to {} justifications: {:?}", SourceChain::NAME, err)
|
.subscribe_justifications()
|
||||||
})?;
|
.await
|
||||||
|
.map_err(|err| Error::Subscribe(SourceChain::NAME, err))?;
|
||||||
// Read next justification - the header that it finalizes will be used as initial header.
|
// Read next justification - the header that it finalizes will be used as initial header.
|
||||||
let justification = justifications
|
let justification = justifications
|
||||||
.next()
|
.next()
|
||||||
.await
|
.await
|
||||||
.map_err(|err| err.to_string())
|
.map_err(|e| Error::ReadJustification(SourceChain::NAME, e))
|
||||||
.and_then(|justification| {
|
.and_then(|justification| {
|
||||||
justification.ok_or_else(|| "stream has ended unexpectedly".into())
|
justification.ok_or_else(|| Error::ReadJustificationStreamEnded(SourceChain::NAME))
|
||||||
})
|
|
||||||
.map_err(|err| {
|
|
||||||
format!("Failed to read {} justification from the stream: {}", SourceChain::NAME, err,)
|
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
// Read initial header.
|
// Read initial header.
|
||||||
let justification: GrandpaJustification<SourceChain::Header> =
|
let justification: GrandpaJustification<SourceChain::Header> =
|
||||||
Decode::decode(&mut &justification.0[..]).map_err(|err| {
|
Decode::decode(&mut &justification.0[..])
|
||||||
format!("Failed to decode {} justification: {:?}", SourceChain::NAME, err)
|
.map_err(|err| Error::DecodeJustification(SourceChain::NAME, err))?;
|
||||||
})?;
|
|
||||||
|
|
||||||
let (initial_header_hash, initial_header_number) =
|
let (initial_header_hash, initial_header_number) =
|
||||||
(justification.commit.target_hash, justification.commit.target_number);
|
(justification.commit.target_hash, justification.commit.target_number);
|
||||||
@@ -174,11 +175,7 @@ async fn prepare_initialization_data<SourceChain: Chain>(
|
|||||||
let mut min_possible_block_number = SourceChain::BlockNumber::zero();
|
let mut min_possible_block_number = SourceChain::BlockNumber::zero();
|
||||||
let authorities_for_verification = VoterSet::new(authorities_for_verification.clone())
|
let authorities_for_verification = VoterSet::new(authorities_for_verification.clone())
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| {
|
||||||
format!(
|
Error::ReadInvalidAuthorities(SourceChain::NAME, authorities_for_verification)
|
||||||
"Read invalid {} authorities set: {:?}",
|
|
||||||
SourceChain::NAME,
|
|
||||||
authorities_for_verification,
|
|
||||||
)
|
|
||||||
})?;
|
})?;
|
||||||
loop {
|
loop {
|
||||||
log::trace!(
|
log::trace!(
|
||||||
@@ -205,12 +202,7 @@ async fn prepare_initialization_data<SourceChain: Chain>(
|
|||||||
// there can't be more authorities set changes than headers => if we have reached
|
// there can't be more authorities set changes than headers => if we have reached
|
||||||
// `initial_block_number` and still have not found correct value of
|
// `initial_block_number` and still have not found correct value of
|
||||||
// `initial_authorities_set_id`, then something else is broken => fail
|
// `initial_authorities_set_id`, then something else is broken => fail
|
||||||
return Err(format!(
|
return Err(Error::GuessInitialAuthorities(SourceChain::NAME, initial_header_number))
|
||||||
"Failed to guess initial {} GRANDPA authorities set id: checked all\
|
|
||||||
possible ids in range [0; {}]",
|
|
||||||
SourceChain::NAME,
|
|
||||||
initial_header_number
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -230,37 +222,24 @@ async fn prepare_initialization_data<SourceChain: Chain>(
|
|||||||
async fn source_header<SourceChain: Chain>(
|
async fn source_header<SourceChain: Chain>(
|
||||||
source_client: &Client<SourceChain>,
|
source_client: &Client<SourceChain>,
|
||||||
header_hash: SourceChain::Hash,
|
header_hash: SourceChain::Hash,
|
||||||
) -> Result<SourceChain::Header, String> {
|
) -> Result<SourceChain::Header, Error<SourceChain::Hash, <SourceChain::Header as Header>::Number>>
|
||||||
source_client.header_by_hash(header_hash).await.map_err(|err| {
|
{
|
||||||
format!(
|
source_client
|
||||||
"Failed to retrive {} header with hash {}: {:?}",
|
.header_by_hash(header_hash)
|
||||||
SourceChain::NAME,
|
.await
|
||||||
header_hash,
|
.map_err(|err| Error::RetrieveHeader(SourceChain::NAME, header_hash, err))
|
||||||
err,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read GRANDPA authorities set at given header.
|
/// Read GRANDPA authorities set at given header.
|
||||||
async fn source_authorities_set<SourceChain: Chain>(
|
async fn source_authorities_set<SourceChain: Chain>(
|
||||||
source_client: &Client<SourceChain>,
|
source_client: &Client<SourceChain>,
|
||||||
header_hash: SourceChain::Hash,
|
header_hash: SourceChain::Hash,
|
||||||
) -> Result<GrandpaAuthoritiesSet, String> {
|
) -> Result<GrandpaAuthoritiesSet, Error<SourceChain::Hash, <SourceChain::Header as Header>::Number>>
|
||||||
let raw_authorities_set =
|
{
|
||||||
source_client.grandpa_authorities_set(header_hash).await.map_err(|err| {
|
let raw_authorities_set = source_client
|
||||||
format!(
|
.grandpa_authorities_set(header_hash)
|
||||||
"Failed to retrive {} GRANDPA authorities set at header {}: {:?}",
|
.await
|
||||||
SourceChain::NAME,
|
.map_err(|err| Error::RetrieveAuthorities(SourceChain::NAME, header_hash, err))?;
|
||||||
header_hash,
|
GrandpaAuthoritiesSet::decode(&mut &raw_authorities_set[..])
|
||||||
err,
|
.map_err(|err| Error::DecodeAuthorities(SourceChain::NAME, header_hash, err))
|
||||||
)
|
|
||||||
})?;
|
|
||||||
GrandpaAuthoritiesSet::decode(&mut &raw_authorities_set[..]).map_err(|err| {
|
|
||||||
format!(
|
|
||||||
"Failed to decode {} GRANDPA authorities set at header {}: {:?}",
|
|
||||||
SourceChain::NAME,
|
|
||||||
header_hash,
|
|
||||||
err,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
#![warn(missing_docs)]
|
#![warn(missing_docs)]
|
||||||
|
|
||||||
pub mod conversion_rate_update;
|
pub mod conversion_rate_update;
|
||||||
|
pub mod error;
|
||||||
pub mod finality_pipeline;
|
pub mod finality_pipeline;
|
||||||
pub mod finality_target;
|
pub mod finality_target;
|
||||||
pub mod headers_initialize;
|
pub mod headers_initialize;
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ edition = "2018"
|
|||||||
license = "GPL-3.0-or-later WITH Classpath-exception-2.0"
|
license = "GPL-3.0-or-later WITH Classpath-exception-2.0"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0"
|
|
||||||
async-std = { version = "1.6.5", features = ["attributes"] }
|
async-std = { version = "1.6.5", features = ["attributes"] }
|
||||||
async-trait = "0.1.40"
|
async-trait = "0.1.40"
|
||||||
futures = "0.3.5"
|
futures = "0.3.5"
|
||||||
|
|||||||
@@ -263,7 +263,7 @@ pub async fn run<P: MessageLane>(
|
|||||||
target_client: impl TargetClient<P>,
|
target_client: impl TargetClient<P>,
|
||||||
metrics_params: MetricsParams,
|
metrics_params: MetricsParams,
|
||||||
exit_signal: impl Future<Output = ()> + Send + 'static,
|
exit_signal: impl Future<Output = ()> + Send + 'static,
|
||||||
) -> anyhow::Result<()> {
|
) -> Result<(), relay_utils::Error> {
|
||||||
let exit_signal = exit_signal.shared();
|
let exit_signal = exit_signal.shared();
|
||||||
relay_utils::relay_loop(source_client, target_client)
|
relay_utils::relay_loop(source_client, target_client)
|
||||||
.reconnect_delay(params.reconnect_delay)
|
.reconnect_delay(params.reconnect_delay)
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ num-traits = "0.2"
|
|||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
sysinfo = "0.15"
|
sysinfo = "0.15"
|
||||||
time = "0.2"
|
time = "0.2"
|
||||||
|
thiserror = "1.0.26"
|
||||||
|
|
||||||
# Substrate dependencies
|
# Substrate dependencies
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
// Copyright 2019-2021 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 std::net::AddrParseError;
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
/// Result type used by relay utilities.
|
||||||
|
pub type Result<T> = std::result::Result<T, Error>;
|
||||||
|
|
||||||
|
/// Relay utilities errors.
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum Error {
|
||||||
|
/// Failed to request a float value from HTTP service.
|
||||||
|
#[error("Failed to fetch token price from remote server: {0}")]
|
||||||
|
FetchTokenPrice(#[source] anyhow::Error),
|
||||||
|
/// Failed to parse the response from HTTP service.
|
||||||
|
#[error("Failed to parse HTTP service response: {0:?}. Response: {1:?}")]
|
||||||
|
ParseHttp(serde_json::Error, String),
|
||||||
|
/// Failed to select response value from the JSON response.
|
||||||
|
#[error("Failed to select value from response: {0:?}. Response: {1:?}")]
|
||||||
|
SelectResponseValue(jsonpath_lib::JsonPathError, String),
|
||||||
|
/// Failed to parse float value from the selected value.
|
||||||
|
#[error(
|
||||||
|
"Failed to parse float value {0:?} from response. It is assumed to be positive and normal"
|
||||||
|
)]
|
||||||
|
ParseFloat(f64),
|
||||||
|
/// Couldn't found value in the JSON response.
|
||||||
|
#[error("Missing required value from response: {0:?}")]
|
||||||
|
MissingResponseValue(String),
|
||||||
|
/// Invalid host address was used for exposing Prometheus metrics.
|
||||||
|
#[error("Invalid host {0} is used to expose Prometheus metrics: {1}")]
|
||||||
|
ExposingMetricsInvalidHost(String, AddrParseError),
|
||||||
|
/// Prometheus error.
|
||||||
|
#[error("{0}")]
|
||||||
|
Prometheus(#[from] substrate_prometheus_endpoint::prometheus::Error),
|
||||||
|
}
|
||||||
@@ -16,11 +16,13 @@
|
|||||||
|
|
||||||
//! Utilities used by different relays.
|
//! Utilities used by different relays.
|
||||||
|
|
||||||
|
pub use error::Error;
|
||||||
pub use relay_loop::{relay_loop, relay_metrics};
|
pub use relay_loop::{relay_loop, relay_metrics};
|
||||||
|
|
||||||
use backoff::{backoff::Backoff, ExponentialBackoff};
|
use backoff::{backoff::Backoff, ExponentialBackoff};
|
||||||
use futures::future::FutureExt;
|
use futures::future::FutureExt;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
/// Max delay after connection-unrelated error happened before we'll try the
|
/// Max delay after connection-unrelated error happened before we'll try the
|
||||||
/// same request again.
|
/// same request again.
|
||||||
@@ -29,6 +31,7 @@ pub const MAX_BACKOFF_INTERVAL: Duration = Duration::from_secs(60);
|
|||||||
/// reconnection again.
|
/// reconnection again.
|
||||||
pub const CONNECTION_ERROR_DELAY: Duration = Duration::from_secs(10);
|
pub const CONNECTION_ERROR_DELAY: Duration = Duration::from_secs(10);
|
||||||
|
|
||||||
|
pub mod error;
|
||||||
pub mod initialize;
|
pub mod initialize;
|
||||||
pub mod metrics;
|
pub mod metrics;
|
||||||
pub mod relay_loop;
|
pub mod relay_loop;
|
||||||
@@ -111,11 +114,13 @@ pub trait MaybeConnectionError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Stringified error that may be either connection-related or not.
|
/// Stringified error that may be either connection-related or not.
|
||||||
#[derive(Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum StringifiedMaybeConnectionError {
|
pub enum StringifiedMaybeConnectionError {
|
||||||
/// The error is connection-related error.
|
/// The error is connection-related error.
|
||||||
|
#[error("{0}")]
|
||||||
Connection(String),
|
Connection(String),
|
||||||
/// The error is connection-unrelated error.
|
/// The error is connection-unrelated error.
|
||||||
|
#[error("{0}")]
|
||||||
NonConnection(String),
|
NonConnection(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,15 +144,6 @@ impl MaybeConnectionError for StringifiedMaybeConnectionError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToString for StringifiedMaybeConnectionError {
|
|
||||||
fn to_string(&self) -> String {
|
|
||||||
match *self {
|
|
||||||
StringifiedMaybeConnectionError::Connection(ref err) => err.clone(),
|
|
||||||
StringifiedMaybeConnectionError::NonConnection(ref err) => err.clone(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Exponential backoff for connection-unrelated errors retries.
|
/// Exponential backoff for connection-unrelated errors retries.
|
||||||
pub fn retry_backoff() -> ExponentialBackoff {
|
pub fn retry_backoff() -> ExponentialBackoff {
|
||||||
ExponentialBackoff {
|
ExponentialBackoff {
|
||||||
|
|||||||
@@ -14,8 +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/>.
|
||||||
|
|
||||||
use crate::metrics::{
|
use crate::{
|
||||||
metric_name, register, F64SharedRef, Gauge, PrometheusError, Registry, StandaloneMetrics, F64,
|
error::{self, Error},
|
||||||
|
metrics::{
|
||||||
|
metric_name, register, F64SharedRef, Gauge, PrometheusError, Registry, StandaloneMetrics,
|
||||||
|
F64,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use async_std::sync::{Arc, RwLock};
|
use async_std::sync::{Arc, RwLock};
|
||||||
@@ -61,27 +65,18 @@ impl FloatJsonValueMetric {
|
|||||||
self.shared_value_ref.clone()
|
self.shared_value_ref.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read value from HTTP service.
|
/// Request value from HTTP service.
|
||||||
async fn read_value(&self) -> Result<f64, String> {
|
async fn request_value(&self) -> anyhow::Result<String> {
|
||||||
use isahc::{AsyncReadResponseExt, HttpClient, Request};
|
use isahc::{AsyncReadResponseExt, HttpClient, Request};
|
||||||
|
|
||||||
fn map_isahc_err(err: impl std::fmt::Display) -> String {
|
let request = Request::get(&self.url).header("Accept", "application/json").body(())?;
|
||||||
format!("Failed to fetch token price from remote server: {}", err)
|
let raw_response = HttpClient::new()?.send_async(request).await?.text().await?;
|
||||||
|
Ok(raw_response)
|
||||||
}
|
}
|
||||||
|
|
||||||
let request = Request::get(&self.url)
|
/// Read value from HTTP service.
|
||||||
.header("Accept", "application/json")
|
async fn read_value(&self) -> error::Result<f64> {
|
||||||
.body(())
|
let raw_response = self.request_value().await.map_err(Error::FetchTokenPrice)?;
|
||||||
.map_err(map_isahc_err)?;
|
|
||||||
let raw_response = HttpClient::new()
|
|
||||||
.map_err(map_isahc_err)?
|
|
||||||
.send_async(request)
|
|
||||||
.await
|
|
||||||
.map_err(map_isahc_err)?
|
|
||||||
.text()
|
|
||||||
.await
|
|
||||||
.map_err(map_isahc_err)?;
|
|
||||||
|
|
||||||
parse_service_response(&self.json_path, &raw_response)
|
parse_service_response(&self.json_path, &raw_response)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -94,30 +89,26 @@ impl StandaloneMetrics for FloatJsonValueMetric {
|
|||||||
|
|
||||||
async fn update(&self) {
|
async fn update(&self) {
|
||||||
let value = self.read_value().await;
|
let value = self.read_value().await;
|
||||||
crate::metrics::set_gauge_value(&self.metric, value.clone().map(Some));
|
let maybe_ok = value.as_ref().ok().copied();
|
||||||
*self.shared_value_ref.write().await = value.ok();
|
crate::metrics::set_gauge_value(&self.metric, value.map(Some));
|
||||||
|
*self.shared_value_ref.write().await = maybe_ok;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse HTTP service response.
|
/// Parse HTTP service response.
|
||||||
fn parse_service_response(json_path: &str, response: &str) -> Result<f64, String> {
|
fn parse_service_response(json_path: &str, response: &str) -> error::Result<f64> {
|
||||||
let json = serde_json::from_str(response).map_err(|err| {
|
let json =
|
||||||
format!("Failed to parse HTTP service response: {:?}. Response: {:?}", err, response,)
|
serde_json::from_str(response).map_err(|err| Error::ParseHttp(err, response.to_owned()))?;
|
||||||
})?;
|
|
||||||
|
|
||||||
let mut selector = jsonpath_lib::selector(&json);
|
let mut selector = jsonpath_lib::selector(&json);
|
||||||
let maybe_selected_value = selector(json_path).map_err(|err| {
|
let maybe_selected_value =
|
||||||
format!("Failed to select value from response: {:?}. Response: {:?}", err, response,)
|
selector(json_path).map_err(|err| Error::SelectResponseValue(err, response.to_owned()))?;
|
||||||
})?;
|
|
||||||
let selected_value = maybe_selected_value
|
let selected_value = maybe_selected_value
|
||||||
.first()
|
.first()
|
||||||
.and_then(|v| v.as_f64())
|
.and_then(|v| v.as_f64())
|
||||||
.ok_or_else(|| format!("Missing required value from response: {:?}", response,))?;
|
.ok_or_else(|| Error::MissingResponseValue(response.to_owned()))?;
|
||||||
if !selected_value.is_normal() || selected_value < 0.0 {
|
if !selected_value.is_normal() || selected_value < 0.0 {
|
||||||
return Err(format!(
|
return Err(Error::ParseFloat(selected_value))
|
||||||
"Failed to parse float value {:?} from response. It is assumed to be positive and normal",
|
|
||||||
selected_value,
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(selected_value)
|
Ok(selected_value)
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
error::Error,
|
||||||
metrics::{Metrics, MetricsAddress, MetricsParams, PrometheusError, StandaloneMetrics},
|
metrics::{Metrics, MetricsAddress, MetricsParams, PrometheusError, StandaloneMetrics},
|
||||||
FailedClient, MaybeConnectionError,
|
FailedClient, MaybeConnectionError,
|
||||||
};
|
};
|
||||||
@@ -116,7 +117,7 @@ impl<SC, TC, LM> Loop<SC, TC, LM> {
|
|||||||
/// This function represents an outer loop, which in turn calls provided `run_loop` function to
|
/// This function represents an outer loop, which in turn calls provided `run_loop` function to
|
||||||
/// do actual job. When `run_loop` returns, this outer loop reconnects to failed client (source,
|
/// do actual job. When `run_loop` returns, this outer loop reconnects to failed client (source,
|
||||||
/// target or both) and calls `run_loop` again.
|
/// target or both) and calls `run_loop` again.
|
||||||
pub async fn run<R, F>(mut self, loop_name: String, run_loop: R) -> anyhow::Result<()>
|
pub async fn run<R, F>(mut self, loop_name: String, run_loop: R) -> Result<(), Error>
|
||||||
where
|
where
|
||||||
R: 'static + Send + Fn(SC, TC, Option<LM>) -> F,
|
R: 'static + Send + Fn(SC, TC, Option<LM>) -> F,
|
||||||
F: 'static + Send + Future<Output = Result<(), FailedClient>>,
|
F: 'static + Send + Future<Output = Result<(), FailedClient>>,
|
||||||
@@ -162,7 +163,7 @@ impl<SC, TC, LM> LoopMetrics<SC, TC, LM> {
|
|||||||
pub fn loop_metric<NewLM: Metrics>(
|
pub fn loop_metric<NewLM: Metrics>(
|
||||||
self,
|
self,
|
||||||
create_metric: impl FnOnce(&Registry, Option<&str>) -> Result<NewLM, PrometheusError>,
|
create_metric: impl FnOnce(&Registry, Option<&str>) -> Result<NewLM, PrometheusError>,
|
||||||
) -> anyhow::Result<LoopMetrics<SC, TC, NewLM>> {
|
) -> Result<LoopMetrics<SC, TC, NewLM>, Error> {
|
||||||
let loop_metric = create_metric(&self.registry, self.metrics_prefix.as_deref())?;
|
let loop_metric = create_metric(&self.registry, self.metrics_prefix.as_deref())?;
|
||||||
|
|
||||||
Ok(LoopMetrics {
|
Ok(LoopMetrics {
|
||||||
@@ -178,13 +179,13 @@ impl<SC, TC, LM> LoopMetrics<SC, TC, LM> {
|
|||||||
pub fn standalone_metric<M: StandaloneMetrics>(
|
pub fn standalone_metric<M: StandaloneMetrics>(
|
||||||
self,
|
self,
|
||||||
create_metric: impl FnOnce(&Registry, Option<&str>) -> Result<M, PrometheusError>,
|
create_metric: impl FnOnce(&Registry, Option<&str>) -> Result<M, PrometheusError>,
|
||||||
) -> anyhow::Result<Self> {
|
) -> Result<Self, Error> {
|
||||||
// since standalone metrics are updating themselves, we may just ignore the fact that the
|
// since standalone metrics are updating themselves, we may just ignore the fact that the
|
||||||
// same standalone metric is exposed by several loops && only spawn single metric
|
// same standalone metric is exposed by several loops && only spawn single metric
|
||||||
match create_metric(&self.registry, self.metrics_prefix.as_deref()) {
|
match create_metric(&self.registry, self.metrics_prefix.as_deref()) {
|
||||||
Ok(standalone_metrics) => standalone_metrics.spawn(),
|
Ok(standalone_metrics) => standalone_metrics.spawn(),
|
||||||
Err(PrometheusError::AlreadyReg) => (),
|
Err(PrometheusError::AlreadyReg) => (),
|
||||||
Err(e) => anyhow::bail!(e),
|
Err(e) => return Err(e.into()),
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(self)
|
Ok(self)
|
||||||
@@ -202,16 +203,13 @@ impl<SC, TC, LM> LoopMetrics<SC, TC, LM> {
|
|||||||
/// Expose metrics using address passed at creation.
|
/// Expose metrics using address passed at creation.
|
||||||
///
|
///
|
||||||
/// If passed `address` is `None`, metrics are not exposed.
|
/// If passed `address` is `None`, metrics are not exposed.
|
||||||
pub async fn expose(self) -> anyhow::Result<Loop<SC, TC, LM>> {
|
pub async fn expose(self) -> Result<Loop<SC, TC, LM>, Error> {
|
||||||
if let Some(address) = self.address {
|
if let Some(address) = self.address {
|
||||||
let socket_addr = SocketAddr::new(
|
let socket_addr = SocketAddr::new(
|
||||||
address.host.parse().map_err(|err| {
|
address
|
||||||
anyhow::format_err!(
|
.host
|
||||||
"Invalid host {} is used to expose Prometheus metrics: {}",
|
.parse()
|
||||||
address.host,
|
.map_err(|err| Error::ExposingMetricsInvalidHost(address.host.clone(), err))?,
|
||||||
err,
|
|
||||||
)
|
|
||||||
})?,
|
|
||||||
address.port,
|
address.port,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user