mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-31 00:31:02 +00:00
Prepare for sub to eth relay (#248)
* fixed PoA contract deploy (granda_authorities call) * pause if all submitted headers were rejected * give funds to Bertha and Carlos * max 1 active PoA transaction in headers sync :( * display initial header id when deploying PoA contract * cargo fmt + clipy * fix compilation * Update relays/ethereum/src/sync_types.rs Co-authored-by: Tomasz Drwięga <tomusdrw@users.noreply.github.com> * Update relays/ethereum/src/utils.rs Co-authored-by: Tomasz Drwięga <tomusdrw@users.noreply.github.com> Co-authored-by: Tomasz Drwięga <tomusdrw@users.noreply.github.com>
This commit is contained in:
committed by
Bastian Köcher
parent
fddfbb5b1c
commit
b98b7d2e43
@@ -79,7 +79,7 @@ pub fn genesis_header() -> Header {
|
|||||||
transactions_root: hex!("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421").into(),
|
transactions_root: hex!("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421").into(),
|
||||||
uncles_hash: hex!("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347").into(),
|
uncles_hash: hex!("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347").into(),
|
||||||
extra_data: vec![],
|
extra_data: vec![],
|
||||||
state_root: hex!("d6368925ffd9acad81f411ce45891d3722e14355af2790391839488e23d74b0d").into(),
|
state_root: hex!("eccf6b74c2bcbe115c71116a23fe963c54406010c244d9650526028ad3e32cce").into(),
|
||||||
receipts_root: hex!("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421").into(),
|
receipts_root: hex!("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421").into(),
|
||||||
log_bloom: Default::default(),
|
log_bloom: Default::default(),
|
||||||
gas_used: Default::default(),
|
gas_used: Default::default(),
|
||||||
@@ -134,7 +134,7 @@ mod tests {
|
|||||||
fn genesis_hash_matches() {
|
fn genesis_hash_matches() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
genesis_header().compute_hash(),
|
genesis_header().compute_hash(),
|
||||||
hex!("bc936e808b668546250ad43de5c0a95fe2a9644a850a2ff69b57f874e3e35644").into(),
|
hex!("9ff57c7fa155853586382022f0982b71c51fa313a0942f8c456300896643e890").into(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,8 @@ use crate::ethereum_types::{
|
|||||||
use crate::rpc::{Ethereum, EthereumRpc};
|
use crate::rpc::{Ethereum, EthereumRpc};
|
||||||
use crate::rpc_errors::{EthereumNodeError, RpcError};
|
use crate::rpc_errors::{EthereumNodeError, RpcError};
|
||||||
use crate::substrate_types::{GrandpaJustification, Hash as SubstrateHash, QueuedSubstrateHeader, SubstrateHeaderId};
|
use crate::substrate_types::{GrandpaJustification, Hash as SubstrateHash, QueuedSubstrateHeader, SubstrateHeaderId};
|
||||||
use crate::sync_types::{HeaderId, MaybeConnectionError, SubmittedHeaders};
|
use crate::sync_types::{HeaderId, SubmittedHeaders};
|
||||||
|
use crate::utils::MaybeConnectionError;
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use codec::{Decode, Encode};
|
use codec::{Decode, Encode};
|
||||||
|
|||||||
@@ -19,7 +19,8 @@ use crate::ethereum_client::{
|
|||||||
};
|
};
|
||||||
use crate::rpc::SubstrateRpc;
|
use crate::rpc::SubstrateRpc;
|
||||||
use crate::substrate_client::{SubstrateConnectionParams, SubstrateRpcClient};
|
use crate::substrate_client::{SubstrateConnectionParams, SubstrateRpcClient};
|
||||||
use crate::substrate_types::{Hash as SubstrateHash, Header as SubstrateHeader};
|
use crate::substrate_types::{Hash as SubstrateHash, Header as SubstrateHeader, SubstrateHeaderId};
|
||||||
|
use crate::sync_types::HeaderId;
|
||||||
|
|
||||||
use codec::{Decode, Encode};
|
use codec::{Decode, Encode};
|
||||||
use num_traits::Zero;
|
use num_traits::Zero;
|
||||||
@@ -66,18 +67,19 @@ pub fn run(params: EthereumDeployContractParams) {
|
|||||||
let eth_client = EthereumRpcClient::new(params.eth);
|
let eth_client = EthereumRpcClient::new(params.eth);
|
||||||
let sub_client = SubstrateRpcClient::new(params.sub).await?;
|
let sub_client = SubstrateRpcClient::new(params.sub).await?;
|
||||||
|
|
||||||
let (initial_header_hash, initial_header) = prepare_initial_header(&sub_client, params.sub_initial_header).await?;
|
let (initial_header_id, initial_header) = prepare_initial_header(&sub_client, params.sub_initial_header).await?;
|
||||||
let initial_set_id = params.sub_initial_authorities_set_id.unwrap_or(0);
|
let initial_set_id = params.sub_initial_authorities_set_id.unwrap_or(0);
|
||||||
let initial_set = prepare_initial_authorities_set(
|
let initial_set = prepare_initial_authorities_set(
|
||||||
&sub_client,
|
&sub_client,
|
||||||
initial_header_hash,
|
initial_header_id.1,
|
||||||
params.sub_initial_authorities_set,
|
params.sub_initial_authorities_set,
|
||||||
).await?;
|
).await?;
|
||||||
|
|
||||||
log::info!(
|
log::info!(
|
||||||
target: "bridge",
|
target: "bridge",
|
||||||
"Deploying Ethereum contract.\r\n\tInitial header: {:?}\r\n\tInitial header encoded: {}\r\n\tInitial authorities set ID: {}\r\n\tInitial authorities set: {}",
|
"Deploying Ethereum contract.\r\n\tInitial header: {:?}\r\n\tInitial header id: {:?}\r\n\tInitial header encoded: {}\r\n\tInitial authorities set ID: {}\r\n\tInitial authorities set: {}",
|
||||||
initial_header,
|
initial_header,
|
||||||
|
initial_header_id,
|
||||||
hex::encode(&initial_header),
|
hex::encode(&initial_header),
|
||||||
initial_set_id,
|
initial_set_id,
|
||||||
hex::encode(&initial_set),
|
hex::encode(&initial_set),
|
||||||
@@ -102,16 +104,19 @@ pub fn run(params: EthereumDeployContractParams) {
|
|||||||
async fn prepare_initial_header(
|
async fn prepare_initial_header(
|
||||||
sub_client: &SubstrateRpcClient,
|
sub_client: &SubstrateRpcClient,
|
||||||
sub_initial_header: Option<Vec<u8>>,
|
sub_initial_header: Option<Vec<u8>>,
|
||||||
) -> Result<(SubstrateHash, Vec<u8>), String> {
|
) -> Result<(SubstrateHeaderId, Vec<u8>), String> {
|
||||||
match sub_initial_header {
|
match sub_initial_header {
|
||||||
Some(raw_initial_header) => match SubstrateHeader::decode(&mut &raw_initial_header[..]) {
|
Some(raw_initial_header) => match SubstrateHeader::decode(&mut &raw_initial_header[..]) {
|
||||||
Ok(initial_header) => Ok((initial_header.hash(), raw_initial_header)),
|
Ok(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(format!("Error decoding initial header: {}", 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| (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| format!("Error reading Substrate genesis header: {:?}", error))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
//! Relaying proofs of exchange transaction.
|
//! Relaying proofs of exchange transaction.
|
||||||
|
|
||||||
use crate::sync_types::MaybeConnectionError;
|
use crate::utils::{MaybeConnectionError, StringifiedMaybeConnectionError};
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use std::{
|
use std::{
|
||||||
@@ -135,14 +135,6 @@ pub struct RelayedBlockTransactions {
|
|||||||
pub failed: usize,
|
pub failed: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Stringified error that may be either connection-related or not.
|
|
||||||
enum StringifiedMaybeConnectionError {
|
|
||||||
/// The error is connection-related error.
|
|
||||||
Connection(String),
|
|
||||||
/// The error is connection-unrelated error.
|
|
||||||
NonConnection(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Relay all suitable transactions from single block.
|
/// Relay all suitable transactions from single block.
|
||||||
///
|
///
|
||||||
/// If connection error occurs, returns Err with number of successfully processed transactions.
|
/// If connection error occurs, returns Err with number of successfully processed transactions.
|
||||||
@@ -448,34 +440,6 @@ async fn wait_header_finalized<P: TransactionProofPipeline>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StringifiedMaybeConnectionError {
|
|
||||||
fn new(is_connection_error: bool, error: String) -> Self {
|
|
||||||
if is_connection_error {
|
|
||||||
StringifiedMaybeConnectionError::Connection(error)
|
|
||||||
} else {
|
|
||||||
StringifiedMaybeConnectionError::NonConnection(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MaybeConnectionError for StringifiedMaybeConnectionError {
|
|
||||||
fn is_connection_error(&self) -> bool {
|
|
||||||
match *self {
|
|
||||||
StringifiedMaybeConnectionError::Connection(_) => true,
|
|
||||||
StringifiedMaybeConnectionError::NonConnection(_) => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToString for StringifiedMaybeConnectionError {
|
|
||||||
fn to_string(&self) -> String {
|
|
||||||
match *self {
|
|
||||||
StringifiedMaybeConnectionError::Connection(ref err) => err.clone(),
|
|
||||||
StringifiedMaybeConnectionError::NonConnection(ref err) => err.clone(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub(crate) mod tests {
|
pub(crate) mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use crate::sync_types::MaybeConnectionError;
|
use crate::utils::MaybeConnectionError;
|
||||||
|
|
||||||
use jsonrpsee::client::RequestError;
|
use jsonrpsee::client::RequestError;
|
||||||
|
|
||||||
|
|||||||
@@ -187,9 +187,9 @@ impl SubstrateRpc for SubstrateRpcClient {
|
|||||||
|
|
||||||
async fn grandpa_authorities_set(&self, block: Hash) -> Result<GrandpaAuthorityList> {
|
async fn grandpa_authorities_set(&self, block: Hash) -> Result<GrandpaAuthorityList> {
|
||||||
let call = SUB_API_GRANDPA_AUTHORITIES.to_string();
|
let call = SUB_API_GRANDPA_AUTHORITIES.to_string();
|
||||||
let data = Bytes(block.as_bytes().to_vec());
|
let data = Bytes(Vec::new());
|
||||||
|
|
||||||
let encoded_response = Substrate::state_call(&self.client, call, data, None).await?;
|
let encoded_response = Substrate::state_call(&self.client, call, data, Some(block)).await?;
|
||||||
let authority_list = encoded_response.0;
|
let authority_list = encoded_response.0;
|
||||||
|
|
||||||
Ok(authority_list)
|
Ok(authority_list)
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ const ETHEREUM_TICK_INTERVAL: Duration = Duration::from_secs(5);
|
|||||||
/// Max Ethereum headers we want to have in all 'before-submitted' states.
|
/// Max Ethereum headers we want to have in all 'before-submitted' states.
|
||||||
const MAX_FUTURE_HEADERS_TO_DOWNLOAD: usize = 8;
|
const MAX_FUTURE_HEADERS_TO_DOWNLOAD: usize = 8;
|
||||||
/// Max Ethereum headers count we want to have in 'submitted' state.
|
/// Max Ethereum headers count we want to have in 'submitted' state.
|
||||||
const MAX_SUBMITTED_HEADERS: usize = 4;
|
const MAX_SUBMITTED_HEADERS: usize = 1;
|
||||||
/// Max depth of in-memory headers in all states. Past this depth they will be forgotten (pruned).
|
/// Max depth of in-memory headers in all states. Past this depth they will be forgotten (pruned).
|
||||||
const PRUNE_DEPTH: u32 = 256;
|
const PRUNE_DEPTH: u32 = 256;
|
||||||
|
|
||||||
|
|||||||
@@ -17,10 +17,8 @@
|
|||||||
use crate::metrics::{start as metrics_start, GlobalMetrics, MetricsParams};
|
use crate::metrics::{start as metrics_start, GlobalMetrics, MetricsParams};
|
||||||
use crate::sync::HeadersSyncParams;
|
use crate::sync::HeadersSyncParams;
|
||||||
use crate::sync_loop_metrics::SyncLoopMetrics;
|
use crate::sync_loop_metrics::SyncLoopMetrics;
|
||||||
use crate::sync_types::{
|
use crate::sync_types::{HeaderIdOf, HeaderStatus, HeadersSyncPipeline, QueuedHeader, SubmittedHeaders};
|
||||||
HeaderIdOf, HeaderStatus, HeadersSyncPipeline, MaybeConnectionError, QueuedHeader, SubmittedHeaders,
|
use crate::utils::{format_ids, retry_backoff, MaybeConnectionError, StringifiedMaybeConnectionError};
|
||||||
};
|
|
||||||
use crate::utils::retry_backoff;
|
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use backoff::{backoff::Backoff, ExponentialBackoff};
|
use backoff::{backoff::Backoff, ExponentialBackoff};
|
||||||
@@ -309,7 +307,16 @@ pub fn run<P: HeadersSyncPipeline, TC: TargetClient<P>>(
|
|||||||
submitted_headers = target_submit_header_future => {
|
submitted_headers = target_submit_header_future => {
|
||||||
// following line helps Rust understand the type of `submitted_headers` :/
|
// following line helps Rust understand the type of `submitted_headers` :/
|
||||||
let submitted_headers: SubmittedHeaders<HeaderIdOf<P>, TC::Error> = submitted_headers;
|
let submitted_headers: SubmittedHeaders<HeaderIdOf<P>, TC::Error> = submitted_headers;
|
||||||
let maybe_fatal_error = submitted_headers.fatal_error.map(Err).unwrap_or(Ok(()));
|
let submitted_headers_str = format!("{}", submitted_headers);
|
||||||
|
let maybe_fatal_error = match submitted_headers.fatal_error {
|
||||||
|
Some(fatal_error) => Err(StringifiedMaybeConnectionError::new(
|
||||||
|
fatal_error.is_connection_error(),
|
||||||
|
format!("{:?}", fatal_error),
|
||||||
|
)),
|
||||||
|
None if submitted_headers.submitted.is_empty() && submitted_headers.incomplete.is_empty() =>
|
||||||
|
Err(StringifiedMaybeConnectionError::new(false, "All headers were rejected".into())),
|
||||||
|
None => Ok(()),
|
||||||
|
};
|
||||||
|
|
||||||
target_client_is_online = process_future_result(
|
target_client_is_online = process_future_result(
|
||||||
maybe_fatal_error,
|
maybe_fatal_error,
|
||||||
@@ -320,6 +327,8 @@ pub fn run<P: HeadersSyncPipeline, TC: TargetClient<P>>(
|
|||||||
|| format!("Error submitting headers to {} node", P::TARGET_NAME),
|
|| format!("Error submitting headers to {} node", P::TARGET_NAME),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
log::debug!(target: "bridge", "Header submit result: {}", submitted_headers_str);
|
||||||
|
|
||||||
sync.headers_mut().headers_submitted(submitted_headers.submitted);
|
sync.headers_mut().headers_submitted(submitted_headers.submitted);
|
||||||
sync.headers_mut().add_incomplete_headers(submitted_headers.incomplete);
|
sync.headers_mut().add_incomplete_headers(submitted_headers.incomplete);
|
||||||
},
|
},
|
||||||
@@ -434,17 +443,12 @@ pub fn run<P: HeadersSyncPipeline, TC: TargetClient<P>>(
|
|||||||
} else if let Some(headers) =
|
} else if let Some(headers) =
|
||||||
sync.select_headers_to_submit(last_update_time.elapsed() > BACKUP_STALL_SYNC_TIMEOUT)
|
sync.select_headers_to_submit(last_update_time.elapsed() > BACKUP_STALL_SYNC_TIMEOUT)
|
||||||
{
|
{
|
||||||
let ids = match headers.len() {
|
|
||||||
1 => format!("{:?}", headers[0].id()),
|
|
||||||
2 => format!("[{:?}, {:?}]", headers[0].id(), headers[1].id()),
|
|
||||||
len => format!("[{:?} ... {:?}]", headers[0].id(), headers[len - 1].id()),
|
|
||||||
};
|
|
||||||
log::debug!(
|
log::debug!(
|
||||||
target: "bridge",
|
target: "bridge",
|
||||||
"Submitting {} header(s) to {} node: {:?}",
|
"Submitting {} header(s) to {} node: {:?}",
|
||||||
headers.len(),
|
headers.len(),
|
||||||
P::TARGET_NAME,
|
P::TARGET_NAME,
|
||||||
ids,
|
format_ids(headers.iter().map(|header| header.id())),
|
||||||
);
|
);
|
||||||
|
|
||||||
let headers = headers.into_iter().cloned().collect();
|
let headers = headers.into_iter().cloned().collect();
|
||||||
|
|||||||
@@ -17,10 +17,8 @@
|
|||||||
#![cfg(test)]
|
#![cfg(test)]
|
||||||
|
|
||||||
use crate::sync_loop::{process_future_result, run, SourceClient, TargetClient};
|
use crate::sync_loop::{process_future_result, run, SourceClient, TargetClient};
|
||||||
use crate::sync_types::{
|
use crate::sync_types::{HeaderId, HeadersSyncPipeline, QueuedHeader, SourceHeader, SubmittedHeaders};
|
||||||
HeaderId, HeadersSyncPipeline, MaybeConnectionError, QueuedHeader, SourceHeader, SubmittedHeaders,
|
use crate::utils::{retry_backoff, MaybeConnectionError};
|
||||||
};
|
|
||||||
use crate::utils::retry_backoff;
|
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use backoff::backoff::Backoff;
|
use backoff::backoff::Backoff;
|
||||||
|
|||||||
@@ -14,6 +14,8 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
use crate::utils::format_ids;
|
||||||
|
|
||||||
use std::{ops::Deref, sync::Arc};
|
use std::{ops::Deref, sync::Arc};
|
||||||
|
|
||||||
/// Ethereum header Id.
|
/// Ethereum header Id.
|
||||||
@@ -43,12 +45,6 @@ pub enum HeaderStatus {
|
|||||||
Synced,
|
Synced,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Error type that can signal connection errors.
|
|
||||||
pub trait MaybeConnectionError {
|
|
||||||
/// Returns true if error (maybe) represents connection error.
|
|
||||||
fn is_connection_error(&self) -> bool;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Headers synchronization pipeline.
|
/// Headers synchronization pipeline.
|
||||||
pub trait HeadersSyncPipeline: Clone + Copy {
|
pub trait HeadersSyncPipeline: Clone + Copy {
|
||||||
/// Name of the headers source.
|
/// Name of the headers source.
|
||||||
@@ -192,3 +188,17 @@ impl<Id, Error> Default for SubmittedHeaders<Id, Error> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<Id: std::fmt::Debug, Error> std::fmt::Display for SubmittedHeaders<Id, Error> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
let submitted = format_ids(self.submitted.iter());
|
||||||
|
let incomplete = format_ids(self.incomplete.iter());
|
||||||
|
let rejected = format_ids(self.rejected.iter());
|
||||||
|
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"Submitted: {}, Incomplete: {}, Rejected: {}",
|
||||||
|
submitted, incomplete, rejected
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -43,6 +43,50 @@ macro_rules! bail_on_arg_error {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Error type that can signal connection errors.
|
||||||
|
pub trait MaybeConnectionError {
|
||||||
|
/// Returns true if error (maybe) represents connection error.
|
||||||
|
fn is_connection_error(&self) -> bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Stringified error that may be either connection-related or not.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum StringifiedMaybeConnectionError {
|
||||||
|
/// The error is connection-related error.
|
||||||
|
Connection(String),
|
||||||
|
/// The error is connection-unrelated error.
|
||||||
|
NonConnection(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StringifiedMaybeConnectionError {
|
||||||
|
/// Create new stringified connection error.
|
||||||
|
pub fn new(is_connection_error: bool, error: String) -> Self {
|
||||||
|
if is_connection_error {
|
||||||
|
StringifiedMaybeConnectionError::Connection(error)
|
||||||
|
} else {
|
||||||
|
StringifiedMaybeConnectionError::NonConnection(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MaybeConnectionError for StringifiedMaybeConnectionError {
|
||||||
|
fn is_connection_error(&self) -> bool {
|
||||||
|
match *self {
|
||||||
|
StringifiedMaybeConnectionError::Connection(_) => true,
|
||||||
|
StringifiedMaybeConnectionError::NonConnection(_) => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
let mut backoff = ExponentialBackoff::default();
|
let mut backoff = ExponentialBackoff::default();
|
||||||
@@ -51,3 +95,22 @@ pub fn retry_backoff() -> ExponentialBackoff {
|
|||||||
backoff.max_interval = MAX_BACKOFF_INTERVAL;
|
backoff.max_interval = MAX_BACKOFF_INTERVAL;
|
||||||
backoff
|
backoff
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Compact format of IDs vector.
|
||||||
|
pub fn format_ids<Id: std::fmt::Debug>(mut ids: impl ExactSizeIterator<Item = Id>) -> String {
|
||||||
|
const NTH_PROOF: &str = "we have checked len; qed";
|
||||||
|
match ids.len() {
|
||||||
|
0 => "<nothing>".into(),
|
||||||
|
1 => format!("{:?}", ids.next().expect(NTH_PROOF)),
|
||||||
|
2 => {
|
||||||
|
let id0 = ids.next().expect(NTH_PROOF);
|
||||||
|
let id1 = ids.next().expect(NTH_PROOF);
|
||||||
|
format!("[{:?}, {:?}]", id0, id1)
|
||||||
|
}
|
||||||
|
len => {
|
||||||
|
let id0 = ids.next().expect(NTH_PROOF);
|
||||||
|
let id_last = ids.last().expect(NTH_PROOF);
|
||||||
|
format!("{}:[{:?} ... {:?}]", len, id0, id_last)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user