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:
Svyatoslav Nikolsky
2020-07-30 18:05:50 +03:00
committed by Bastian Köcher
parent fddfbb5b1c
commit b98b7d2e43
11 changed files with 117 additions and 72 deletions
@@ -21,7 +21,8 @@ use crate::ethereum_types::{
use crate::rpc::{Ethereum, EthereumRpc};
use crate::rpc_errors::{EthereumNodeError, RpcError};
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 codec::{Decode, Encode};
@@ -19,7 +19,8 @@ use crate::ethereum_client::{
};
use crate::rpc::SubstrateRpc;
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 num_traits::Zero;
@@ -66,18 +67,19 @@ pub fn run(params: EthereumDeployContractParams) {
let eth_client = EthereumRpcClient::new(params.eth);
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 = prepare_initial_authorities_set(
&sub_client,
initial_header_hash,
initial_header_id.1,
params.sub_initial_authorities_set,
).await?;
log::info!(
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_id,
hex::encode(&initial_header),
initial_set_id,
hex::encode(&initial_set),
@@ -102,16 +104,19 @@ pub fn run(params: EthereumDeployContractParams) {
async fn prepare_initial_header(
sub_client: &SubstrateRpcClient,
sub_initial_header: Option<Vec<u8>>,
) -> Result<(SubstrateHash, Vec<u8>), String> {
) -> Result<(SubstrateHeaderId, Vec<u8>), String> {
match sub_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)),
},
None => {
let initial_header = sub_client.header_by_number(Zero::zero()).await;
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))
}
}
+1 -37
View File
@@ -16,7 +16,7 @@
//! Relaying proofs of exchange transaction.
use crate::sync_types::MaybeConnectionError;
use crate::utils::{MaybeConnectionError, StringifiedMaybeConnectionError};
use async_trait::async_trait;
use std::{
@@ -135,14 +135,6 @@ pub struct RelayedBlockTransactions {
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.
///
/// 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)]
pub(crate) mod tests {
use super::*;
+1 -1
View File
@@ -14,7 +14,7 @@
// 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::sync_types::MaybeConnectionError;
use crate::utils::MaybeConnectionError;
use jsonrpsee::client::RequestError;
@@ -187,9 +187,9 @@ impl SubstrateRpc for SubstrateRpcClient {
async fn grandpa_authorities_set(&self, block: Hash) -> Result<GrandpaAuthorityList> {
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;
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.
const MAX_FUTURE_HEADERS_TO_DOWNLOAD: usize = 8;
/// 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).
const PRUNE_DEPTH: u32 = 256;
+15 -11
View File
@@ -17,10 +17,8 @@
use crate::metrics::{start as metrics_start, GlobalMetrics, MetricsParams};
use crate::sync::HeadersSyncParams;
use crate::sync_loop_metrics::SyncLoopMetrics;
use crate::sync_types::{
HeaderIdOf, HeaderStatus, HeadersSyncPipeline, MaybeConnectionError, QueuedHeader, SubmittedHeaders,
};
use crate::utils::retry_backoff;
use crate::sync_types::{HeaderIdOf, HeaderStatus, HeadersSyncPipeline, QueuedHeader, SubmittedHeaders};
use crate::utils::{format_ids, retry_backoff, MaybeConnectionError, StringifiedMaybeConnectionError};
use async_trait::async_trait;
use backoff::{backoff::Backoff, ExponentialBackoff};
@@ -309,7 +307,16 @@ pub fn run<P: HeadersSyncPipeline, TC: TargetClient<P>>(
submitted_headers = target_submit_header_future => {
// following line helps Rust understand the type of `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(
maybe_fatal_error,
@@ -320,6 +327,8 @@ pub fn run<P: HeadersSyncPipeline, TC: TargetClient<P>>(
|| 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().add_incomplete_headers(submitted_headers.incomplete);
},
@@ -434,17 +443,12 @@ pub fn run<P: HeadersSyncPipeline, TC: TargetClient<P>>(
} else if let Some(headers) =
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!(
target: "bridge",
"Submitting {} header(s) to {} node: {:?}",
headers.len(),
P::TARGET_NAME,
ids,
format_ids(headers.iter().map(|header| header.id())),
);
let headers = headers.into_iter().cloned().collect();
@@ -17,10 +17,8 @@
#![cfg(test)]
use crate::sync_loop::{process_future_result, run, SourceClient, TargetClient};
use crate::sync_types::{
HeaderId, HeadersSyncPipeline, MaybeConnectionError, QueuedHeader, SourceHeader, SubmittedHeaders,
};
use crate::utils::retry_backoff;
use crate::sync_types::{HeaderId, HeadersSyncPipeline, QueuedHeader, SourceHeader, SubmittedHeaders};
use crate::utils::{retry_backoff, MaybeConnectionError};
use async_trait::async_trait;
use backoff::backoff::Backoff;
+16 -6
View File
@@ -14,6 +14,8 @@
// 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::utils::format_ids;
use std::{ops::Deref, sync::Arc};
/// Ethereum header Id.
@@ -43,12 +45,6 @@ pub enum HeaderStatus {
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.
pub trait HeadersSyncPipeline: Clone + Copy {
/// 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
)
}
}
+63
View File
@@ -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.
pub fn retry_backoff() -> ExponentialBackoff {
let mut backoff = ExponentialBackoff::default();
@@ -51,3 +95,22 @@ pub fn retry_backoff() -> ExponentialBackoff {
backoff.max_interval = MAX_BACKOFF_INTERVAL;
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)
}
}
}