Extract common part of relay loops (#660)

* extract common parts of relay loops: begin

* merge client impls

* backoff in exchange loop

* reconnect without clone
This commit is contained in:
Svyatoslav Nikolsky
2021-01-26 17:14:33 +03:00
committed by Bastian Köcher
parent 926520292e
commit 44bf84269a
23 changed files with 1016 additions and 776 deletions
+15 -5
View File
@@ -28,22 +28,32 @@ use jsonrpsee::Client as RpcClient;
/// The client used to interact with an Ethereum node through RPC. /// The client used to interact with an Ethereum node through RPC.
#[derive(Clone)] #[derive(Clone)]
pub struct Client { pub struct Client {
params: ConnectionParams,
client: RpcClient, client: RpcClient,
} }
impl Client { impl Client {
/// Create a new Ethereum RPC Client. /// Create a new Ethereum RPC Client.
pub fn new(params: ConnectionParams) -> Self { pub fn new(params: ConnectionParams) -> Self {
Self {
client: Self::build_client(&params),
params,
}
}
/// Build client to use in connection.
fn build_client(params: &ConnectionParams) -> RpcClient {
let uri = format!("http://{}:{}", params.host, params.port); let uri = format!("http://{}:{}", params.host, params.port);
let transport = HttpTransportClient::new(&uri); let transport = HttpTransportClient::new(&uri);
let raw_client = RawClient::new(transport); let raw_client = RawClient::new(transport);
let client: RpcClient = raw_client.into(); raw_client.into()
}
Self { client }
} /// Reopen client connection.
pub fn reconnect(&mut self) {
self.client = Self::build_client(&self.params);
} }
impl Client {
/// Estimate gas usage for the given call. /// Estimate gas usage for the given call.
pub async fn estimate_gas(&self, call_request: CallRequest) -> Result<U256> { pub async fn estimate_gas(&self, call_request: CallRequest) -> Result<U256> {
Ok(Ethereum::estimate_gas(&self.client, call_request).await?) Ok(Ethereum::estimate_gas(&self.client, call_request).await?)
@@ -39,7 +39,7 @@ use relay_rialto_client::{Rialto, SigningParams as RialtoSigningParams};
use relay_substrate_client::{ use relay_substrate_client::{
Chain as SubstrateChain, Client as SubstrateClient, ConnectionParams as SubstrateConnectionParams, Chain as SubstrateChain, Client as SubstrateClient, ConnectionParams as SubstrateConnectionParams,
}; };
use relay_utils::{metrics::MetricsParams, HeaderId}; use relay_utils::{metrics::MetricsParams, relay_loop::Client as RelayClient, HeaderId};
use rialto_runtime::exchange::EthereumTransactionInclusionProof; use rialto_runtime::exchange::EthereumTransactionInclusionProof;
use std::{sync::Arc, time::Duration}; use std::{sync::Arc, time::Duration};
@@ -120,19 +120,28 @@ impl SourceTransaction for EthereumSourceTransaction {
} }
/// Ethereum node as transactions proof source. /// Ethereum node as transactions proof source.
#[derive(Clone)]
struct EthereumTransactionsSource { struct EthereumTransactionsSource {
client: EthereumClient, client: EthereumClient,
} }
#[async_trait] #[async_trait]
impl SourceClient<EthereumToSubstrateExchange> for EthereumTransactionsSource { impl RelayClient for EthereumTransactionsSource {
type Error = RpcError; type Error = RpcError;
async fn reconnect(&mut self) -> Result<(), RpcError> {
self.client.reconnect();
Ok(())
}
}
#[async_trait]
impl SourceClient<EthereumToSubstrateExchange> for EthereumTransactionsSource {
async fn tick(&self) { async fn tick(&self) {
async_std::task::sleep(ETHEREUM_TICK_INTERVAL).await; async_std::task::sleep(ETHEREUM_TICK_INTERVAL).await;
} }
async fn block_by_hash(&self, hash: H256) -> Result<EthereumSourceBlock, Self::Error> { async fn block_by_hash(&self, hash: H256) -> Result<EthereumSourceBlock, RpcError> {
self.client self.client
.header_by_hash_with_transactions(hash) .header_by_hash_with_transactions(hash)
.await .await
@@ -140,7 +149,7 @@ impl SourceClient<EthereumToSubstrateExchange> for EthereumTransactionsSource {
.map_err(Into::into) .map_err(Into::into)
} }
async fn block_by_number(&self, number: u64) -> Result<EthereumSourceBlock, Self::Error> { async fn block_by_number(&self, number: u64) -> Result<EthereumSourceBlock, RpcError> {
self.client self.client
.header_by_number_with_transactions(number) .header_by_number_with_transactions(number)
.await .await
@@ -151,7 +160,7 @@ impl SourceClient<EthereumToSubstrateExchange> for EthereumTransactionsSource {
async fn transaction_block( async fn transaction_block(
&self, &self,
hash: &EthereumTransactionHash, hash: &EthereumTransactionHash,
) -> Result<Option<(EthereumHeaderId, usize)>, Self::Error> { ) -> Result<Option<(EthereumHeaderId, usize)>, RpcError> {
let eth_tx = match self.client.transaction_by_hash(*hash).await? { let eth_tx = match self.client.transaction_by_hash(*hash).await? {
Some(eth_tx) => eth_tx, Some(eth_tx) => eth_tx,
None => return Ok(None), None => return Ok(None),
@@ -173,7 +182,7 @@ impl SourceClient<EthereumToSubstrateExchange> for EthereumTransactionsSource {
&self, &self,
block: &EthereumSourceBlock, block: &EthereumSourceBlock,
tx_index: usize, tx_index: usize,
) -> Result<EthereumTransactionInclusionProof, Self::Error> { ) -> Result<EthereumTransactionInclusionProof, RpcError> {
const TRANSACTION_HAS_RAW_FIELD_PROOF: &str = "RPC level checks that transactions from Ethereum\ const TRANSACTION_HAS_RAW_FIELD_PROOF: &str = "RPC level checks that transactions from Ethereum\
node are having `raw` field; qed"; node are having `raw` field; qed";
const BLOCK_HAS_HASH_FIELD_PROOF: &str = "RPC level checks that block has `hash` field; qed"; const BLOCK_HAS_HASH_FIELD_PROOF: &str = "RPC level checks that block has `hash` field; qed";
@@ -199,6 +208,7 @@ impl SourceClient<EthereumToSubstrateExchange> for EthereumTransactionsSource {
} }
/// Substrate node as transactions proof target. /// Substrate node as transactions proof target.
#[derive(Clone)]
struct SubstrateTransactionsTarget { struct SubstrateTransactionsTarget {
client: SubstrateClient<Rialto>, client: SubstrateClient<Rialto>,
sign_params: RialtoSigningParams, sign_params: RialtoSigningParams,
@@ -206,18 +216,25 @@ struct SubstrateTransactionsTarget {
} }
#[async_trait] #[async_trait]
impl TargetClient<EthereumToSubstrateExchange> for SubstrateTransactionsTarget { impl RelayClient for SubstrateTransactionsTarget {
type Error = RpcError; type Error = RpcError;
async fn reconnect(&mut self) -> Result<(), RpcError> {
Ok(self.client.reconnect().await?)
}
}
#[async_trait]
impl TargetClient<EthereumToSubstrateExchange> for SubstrateTransactionsTarget {
async fn tick(&self) { async fn tick(&self) {
async_std::task::sleep(Rialto::AVERAGE_BLOCK_INTERVAL).await; async_std::task::sleep(Rialto::AVERAGE_BLOCK_INTERVAL).await;
} }
async fn is_header_known(&self, id: &EthereumHeaderId) -> Result<bool, Self::Error> { async fn is_header_known(&self, id: &EthereumHeaderId) -> Result<bool, RpcError> {
self.client.ethereum_header_known(*id).await self.client.ethereum_header_known(*id).await
} }
async fn is_header_finalized(&self, id: &EthereumHeaderId) -> Result<bool, Self::Error> { async fn is_header_finalized(&self, id: &EthereumHeaderId) -> Result<bool, RpcError> {
// we check if header is finalized by simple comparison of the header number and // we check if header is finalized by simple comparison of the header number and
// number of best finalized PoA header known to Substrate node. // number of best finalized PoA header known to Substrate node.
// //
@@ -230,11 +247,11 @@ impl TargetClient<EthereumToSubstrateExchange> for SubstrateTransactionsTarget {
Ok(id.0 <= best_finalized_ethereum_block.0) Ok(id.0 <= best_finalized_ethereum_block.0)
} }
async fn best_finalized_header_id(&self) -> Result<EthereumHeaderId, Self::Error> { async fn best_finalized_header_id(&self) -> Result<EthereumHeaderId, RpcError> {
self.client.best_ethereum_finalized_block().await self.client.best_ethereum_finalized_block().await
} }
async fn filter_transaction_proof(&self, proof: &EthereumTransactionInclusionProof) -> Result<bool, Self::Error> { async fn filter_transaction_proof(&self, proof: &EthereumTransactionInclusionProof) -> Result<bool, RpcError> {
// let's try to parse transaction locally // let's try to parse transaction locally
let (raw_tx, raw_tx_receipt) = &proof.proof[proof.index as usize]; let (raw_tx, raw_tx_receipt) = &proof.proof[proof.index as usize];
let parse_result = rialto_runtime::exchange::EthTransaction::parse(raw_tx); let parse_result = rialto_runtime::exchange::EthTransaction::parse(raw_tx);
@@ -253,7 +270,7 @@ impl TargetClient<EthereumToSubstrateExchange> for SubstrateTransactionsTarget {
self.client.verify_exchange_transaction_proof(proof.clone()).await self.client.verify_exchange_transaction_proof(proof.clone()).await
} }
async fn submit_transaction_proof(&self, proof: EthereumTransactionInclusionProof) -> Result<(), Self::Error> { async fn submit_transaction_proof(&self, proof: EthereumTransactionInclusionProof) -> Result<(), RpcError> {
let (sign_params, bridge_instance) = (self.sign_params.clone(), self.bridge_instance.clone()); let (sign_params, bridge_instance) = (self.sign_params.clone(), self.bridge_instance.clone());
self.client self.client
.submit_exchange_transaction_proof(sign_params, bridge_instance, proof) .submit_exchange_transaction_proof(sign_params, bridge_instance, proof)
@@ -37,7 +37,7 @@ use relay_rialto_client::{Rialto, SigningParams as RialtoSigningParams};
use relay_substrate_client::{ use relay_substrate_client::{
Chain as SubstrateChain, Client as SubstrateClient, ConnectionParams as SubstrateConnectionParams, Chain as SubstrateChain, Client as SubstrateClient, ConnectionParams as SubstrateConnectionParams,
}; };
use relay_utils::metrics::MetricsParams; use relay_utils::{metrics::MetricsParams, relay_loop::Client as RelayClient};
use std::fmt::Debug; use std::fmt::Debug;
use std::{collections::HashSet, sync::Arc, time::Duration}; use std::{collections::HashSet, sync::Arc, time::Duration};
@@ -105,6 +105,7 @@ impl HeadersSyncPipeline for EthereumHeadersSyncPipeline {
pub type QueuedEthereumHeader = QueuedHeader<EthereumHeadersSyncPipeline>; pub type QueuedEthereumHeader = QueuedHeader<EthereumHeadersSyncPipeline>;
/// Ethereum client as headers source. /// Ethereum client as headers source.
#[derive(Clone)]
struct EthereumHeadersSource { struct EthereumHeadersSource {
/// Ethereum node client. /// Ethereum node client.
client: EthereumClient, client: EthereumClient,
@@ -117,14 +118,22 @@ impl EthereumHeadersSource {
} }
#[async_trait] #[async_trait]
impl SourceClient<EthereumHeadersSyncPipeline> for EthereumHeadersSource { impl RelayClient for EthereumHeadersSource {
type Error = RpcError; type Error = RpcError;
async fn best_block_number(&self) -> Result<u64, Self::Error> { async fn reconnect(&mut self) -> Result<(), RpcError> {
self.client.reconnect();
Ok(())
}
}
#[async_trait]
impl SourceClient<EthereumHeadersSyncPipeline> for EthereumHeadersSource {
async fn best_block_number(&self) -> Result<u64, RpcError> {
self.client.best_block_number().await.map_err(Into::into) self.client.best_block_number().await.map_err(Into::into)
} }
async fn header_by_hash(&self, hash: HeaderHash) -> Result<Header, Self::Error> { async fn header_by_hash(&self, hash: HeaderHash) -> Result<Header, RpcError> {
self.client self.client
.header_by_hash(hash) .header_by_hash(hash)
.await .await
@@ -132,7 +141,7 @@ impl SourceClient<EthereumHeadersSyncPipeline> for EthereumHeadersSource {
.map_err(Into::into) .map_err(Into::into)
} }
async fn header_by_number(&self, number: u64) -> Result<Header, Self::Error> { async fn header_by_number(&self, number: u64) -> Result<Header, RpcError> {
self.client self.client
.header_by_number(number) .header_by_number(number)
.await .await
@@ -140,7 +149,7 @@ impl SourceClient<EthereumHeadersSyncPipeline> for EthereumHeadersSource {
.map_err(Into::into) .map_err(Into::into)
} }
async fn header_completion(&self, id: EthereumHeaderId) -> Result<(EthereumHeaderId, Option<()>), Self::Error> { async fn header_completion(&self, id: EthereumHeaderId) -> Result<(EthereumHeaderId, Option<()>), RpcError> {
Ok((id, None)) Ok((id, None))
} }
@@ -148,13 +157,14 @@ impl SourceClient<EthereumHeadersSyncPipeline> for EthereumHeadersSource {
&self, &self,
id: EthereumHeaderId, id: EthereumHeaderId,
header: QueuedEthereumHeader, header: QueuedEthereumHeader,
) -> Result<(EthereumHeaderId, Vec<Receipt>), Self::Error> { ) -> Result<(EthereumHeaderId, Vec<Receipt>), RpcError> {
self.client self.client
.transaction_receipts(id, header.header().transactions.clone()) .transaction_receipts(id, header.header().transactions.clone())
.await .await
} }
} }
#[derive(Clone)]
struct SubstrateHeadersTarget { struct SubstrateHeadersTarget {
/// Substrate node client. /// Substrate node client.
client: SubstrateClient<Rialto>, client: SubstrateClient<Rialto>,
@@ -183,21 +193,25 @@ impl SubstrateHeadersTarget {
} }
#[async_trait] #[async_trait]
impl TargetClient<EthereumHeadersSyncPipeline> for SubstrateHeadersTarget { impl RelayClient for SubstrateHeadersTarget {
type Error = RpcError; type Error = RpcError;
async fn best_header_id(&self) -> Result<EthereumHeaderId, Self::Error> { async fn reconnect(&mut self) -> Result<(), RpcError> {
Ok(self.client.reconnect().await?)
}
}
#[async_trait]
impl TargetClient<EthereumHeadersSyncPipeline> for SubstrateHeadersTarget {
async fn best_header_id(&self) -> Result<EthereumHeaderId, RpcError> {
self.client.best_ethereum_block().await self.client.best_ethereum_block().await
} }
async fn is_known_header(&self, id: EthereumHeaderId) -> Result<(EthereumHeaderId, bool), Self::Error> { async fn is_known_header(&self, id: EthereumHeaderId) -> Result<(EthereumHeaderId, bool), RpcError> {
Ok((id, self.client.ethereum_header_known(id).await?)) Ok((id, self.client.ethereum_header_known(id).await?))
} }
async fn submit_headers( async fn submit_headers(&self, headers: Vec<QueuedEthereumHeader>) -> SubmittedHeaders<EthereumHeaderId, RpcError> {
&self,
headers: Vec<QueuedEthereumHeader>,
) -> SubmittedHeaders<EthereumHeaderId, Self::Error> {
let (sign_params, bridge_instance, sign_transactions) = ( let (sign_params, bridge_instance, sign_transactions) = (
self.sign_params.clone(), self.sign_params.clone(),
self.bridge_instance.clone(), self.bridge_instance.clone(),
@@ -208,16 +222,16 @@ impl TargetClient<EthereumHeadersSyncPipeline> for SubstrateHeadersTarget {
.await .await
} }
async fn incomplete_headers_ids(&self) -> Result<HashSet<EthereumHeaderId>, Self::Error> { async fn incomplete_headers_ids(&self) -> Result<HashSet<EthereumHeaderId>, RpcError> {
Ok(HashSet::new()) Ok(HashSet::new())
} }
#[allow(clippy::unit_arg)] #[allow(clippy::unit_arg)]
async fn complete_header(&self, id: EthereumHeaderId, _completion: ()) -> Result<EthereumHeaderId, Self::Error> { async fn complete_header(&self, id: EthereumHeaderId, _completion: ()) -> Result<EthereumHeaderId, RpcError> {
Ok(id) Ok(id)
} }
async fn requires_extra(&self, header: QueuedEthereumHeader) -> Result<(EthereumHeaderId, bool), Self::Error> { async fn requires_extra(&self, header: QueuedEthereumHeader) -> Result<(EthereumHeaderId, bool), RpcError> {
// we can minimize number of receipts_check calls by checking header // we can minimize number of receipts_check calls by checking header
// logs bloom here, but it may give us false positives (when authorities // logs bloom here, but it may give us false positives (when authorities
// source is contract, we never need any logs) // source is contract, we never need any logs)
@@ -35,7 +35,7 @@ use relay_substrate_client::{
headers_source::HeadersSource, Chain as SubstrateChain, Client as SubstrateClient, headers_source::HeadersSource, Chain as SubstrateChain, Client as SubstrateClient,
ConnectionParams as SubstrateConnectionParams, ConnectionParams as SubstrateConnectionParams,
}; };
use relay_utils::metrics::MetricsParams; use relay_utils::{metrics::MetricsParams, relay_loop::Client as RelayClient};
use sp_runtime::Justification; use sp_runtime::Justification;
use std::fmt::Debug; use std::fmt::Debug;
@@ -98,6 +98,7 @@ pub type QueuedRialtoHeader = QueuedHeader<SubstrateHeadersSyncPipeline>;
type SubstrateHeadersSource = HeadersSource<Rialto, SubstrateHeadersSyncPipeline>; type SubstrateHeadersSource = HeadersSource<Rialto, SubstrateHeadersSyncPipeline>;
/// Ethereum client as Substrate headers target. /// Ethereum client as Substrate headers target.
#[derive(Clone)]
struct EthereumHeadersTarget { struct EthereumHeadersTarget {
/// Ethereum node client. /// Ethereum node client.
client: EthereumClient, client: EthereumClient,
@@ -118,38 +119,42 @@ impl EthereumHeadersTarget {
} }
#[async_trait] #[async_trait]
impl TargetClient<SubstrateHeadersSyncPipeline> for EthereumHeadersTarget { impl RelayClient for EthereumHeadersTarget {
type Error = RpcError; type Error = RpcError;
async fn best_header_id(&self) -> Result<RialtoHeaderId, Self::Error> { async fn reconnect(&mut self) -> Result<(), RpcError> {
self.client.reconnect();
Ok(())
}
}
#[async_trait]
impl TargetClient<SubstrateHeadersSyncPipeline> for EthereumHeadersTarget {
async fn best_header_id(&self) -> Result<RialtoHeaderId, RpcError> {
self.client.best_substrate_block(self.contract).await self.client.best_substrate_block(self.contract).await
} }
async fn is_known_header(&self, id: RialtoHeaderId) -> Result<(RialtoHeaderId, bool), Self::Error> { async fn is_known_header(&self, id: RialtoHeaderId) -> Result<(RialtoHeaderId, bool), RpcError> {
self.client.substrate_header_known(self.contract, id).await self.client.substrate_header_known(self.contract, id).await
} }
async fn submit_headers(&self, headers: Vec<QueuedRialtoHeader>) -> SubmittedHeaders<RialtoHeaderId, Self::Error> { async fn submit_headers(&self, headers: Vec<QueuedRialtoHeader>) -> SubmittedHeaders<RialtoHeaderId, RpcError> {
self.client self.client
.submit_substrate_headers(self.sign_params.clone(), self.contract, headers) .submit_substrate_headers(self.sign_params.clone(), self.contract, headers)
.await .await
} }
async fn incomplete_headers_ids(&self) -> Result<HashSet<RialtoHeaderId>, Self::Error> { async fn incomplete_headers_ids(&self) -> Result<HashSet<RialtoHeaderId>, RpcError> {
self.client.incomplete_substrate_headers(self.contract).await self.client.incomplete_substrate_headers(self.contract).await
} }
async fn complete_header( async fn complete_header(&self, id: RialtoHeaderId, completion: Justification) -> Result<RialtoHeaderId, RpcError> {
&self,
id: RialtoHeaderId,
completion: Justification,
) -> Result<RialtoHeaderId, Self::Error> {
self.client self.client
.complete_substrate_header(self.sign_params.clone(), self.contract, id, completion) .complete_substrate_header(self.sign_params.clone(), self.contract, id, completion)
.await .await
} }
async fn requires_extra(&self, header: QueuedRialtoHeader) -> Result<(RialtoHeaderId, bool), Self::Error> { async fn requires_extra(&self, header: QueuedRialtoHeader) -> Result<(RialtoHeaderId, bool), RpcError> {
Ok((header.header().id(), false)) Ok((header.header().id(), false))
} }
} }
+38 -19
View File
@@ -17,7 +17,9 @@
//! Relaying proofs of exchange transaction. //! Relaying proofs of exchange transaction.
use async_trait::async_trait; use async_trait::async_trait;
use relay_utils::{MaybeConnectionError, StringifiedMaybeConnectionError}; use relay_utils::{
relay_loop::Client as RelayClient, FailedClient, MaybeConnectionError, StringifiedMaybeConnectionError,
};
use std::{ use std::{
fmt::{Debug, Display}, fmt::{Debug, Display},
string::ToString, string::ToString,
@@ -84,10 +86,7 @@ pub type HeaderId<P> = relay_utils::HeaderId<BlockHashOf<P>, BlockNumberOf<P>>;
/// Source client API. /// Source client API.
#[async_trait] #[async_trait]
pub trait SourceClient<P: TransactionProofPipeline> { pub trait SourceClient<P: TransactionProofPipeline>: RelayClient {
/// Error type.
type Error: Debug + MaybeConnectionError;
/// Sleep until exchange-related data is (probably) updated. /// Sleep until exchange-related data is (probably) updated.
async fn tick(&self); async fn tick(&self);
/// Get block by hash. /// Get block by hash.
@@ -104,10 +103,7 @@ pub trait SourceClient<P: TransactionProofPipeline> {
/// Target client API. /// Target client API.
#[async_trait] #[async_trait]
pub trait TargetClient<P: TransactionProofPipeline> { pub trait TargetClient<P: TransactionProofPipeline>: RelayClient {
/// Error type.
type Error: Debug + MaybeConnectionError;
/// 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.
@@ -146,7 +142,7 @@ pub async fn relay_block_transactions<P: TransactionProofPipeline>(
target_client: &impl TargetClient<P>, target_client: &impl TargetClient<P>,
source_block: &P::Block, source_block: &P::Block,
mut relayed_transactions: RelayedBlockTransactions, mut relayed_transactions: RelayedBlockTransactions,
) -> Result<RelayedBlockTransactions, RelayedBlockTransactions> { ) -> Result<RelayedBlockTransactions, (FailedClient, RelayedBlockTransactions)> {
let transactions_to_process = source_block let transactions_to_process = source_block
.transactions() .transactions()
.into_iter() .into_iter()
@@ -156,16 +152,21 @@ pub async fn relay_block_transactions<P: TransactionProofPipeline>(
let result = async { let result = async {
let source_tx_id = format!("{}/{}", source_block.id().1, source_tx_index); let source_tx_id = format!("{}/{}", source_block.id().1, source_tx_index);
let source_tx_proof = let source_tx_proof =
prepare_transaction_proof(source_client, &source_tx_id, source_block, source_tx_index).await?; prepare_transaction_proof(source_client, &source_tx_id, source_block, source_tx_index)
.await
.map_err(|e| (FailedClient::Source, e))?;
let needs_to_be_relayed = let needs_to_be_relayed =
target_client target_client
.filter_transaction_proof(&source_tx_proof) .filter_transaction_proof(&source_tx_proof)
.await .await
.map_err(|err| { .map_err(|err| {
(
FailedClient::Target,
StringifiedMaybeConnectionError::new( StringifiedMaybeConnectionError::new(
err.is_connection_error(), err.is_connection_error(),
format!("Transaction filtering has failed with {:?}", err), format!("Transaction filtering has failed with {:?}", err),
),
) )
})?; })?;
@@ -176,6 +177,7 @@ pub async fn relay_block_transactions<P: TransactionProofPipeline>(
relay_ready_transaction_proof(target_client, &source_tx_id, source_tx_proof) relay_ready_transaction_proof(target_client, &source_tx_id, source_tx_proof)
.await .await
.map(|_| true) .map(|_| true)
.map_err(|e| (FailedClient::Target, e))
} }
.await; .await;
@@ -205,7 +207,7 @@ pub async fn relay_block_transactions<P: TransactionProofPipeline>(
relayed_transactions.processed += 1; relayed_transactions.processed += 1;
relayed_transactions.relayed += 1; relayed_transactions.relayed += 1;
} }
Err(err) => { Err((failed_client, err)) => {
log::error!( log::error!(
target: "bridge", target: "bridge",
"Error relaying {} transaction {} proof to {} node: {}. {}", "Error relaying {} transaction {} proof to {} node: {}. {}",
@@ -221,7 +223,7 @@ pub async fn relay_block_transactions<P: TransactionProofPipeline>(
); );
if err.is_connection_error() { if err.is_connection_error() {
return Err(relayed_transactions); return Err((failed_client, relayed_transactions));
} }
relayed_transactions.processed += 1; relayed_transactions.processed += 1;
@@ -529,8 +531,9 @@ pub(crate) mod tests {
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct TestTransactionProof(pub TestTransactionHash); pub struct TestTransactionProof(pub TestTransactionHash);
#[derive(Clone)]
pub struct TestTransactionsSource { pub struct TestTransactionsSource {
pub on_tick: Box<dyn Fn(&mut TestTransactionsSourceData) + Send + Sync>, pub on_tick: Arc<dyn Fn(&mut TestTransactionsSourceData) + Send + Sync>,
pub data: Arc<Mutex<TestTransactionsSourceData>>, pub data: Arc<Mutex<TestTransactionsSourceData>>,
} }
@@ -543,7 +546,7 @@ pub(crate) mod tests {
impl TestTransactionsSource { impl TestTransactionsSource {
pub fn new(on_tick: Box<dyn Fn(&mut TestTransactionsSourceData) + Send + Sync>) -> Self { pub fn new(on_tick: Box<dyn Fn(&mut TestTransactionsSourceData) + Send + Sync>) -> Self {
Self { Self {
on_tick, on_tick: Arc::new(on_tick),
data: Arc::new(Mutex::new(TestTransactionsSourceData { data: Arc::new(Mutex::new(TestTransactionsSourceData {
block: Ok(test_block()), block: Ok(test_block()),
transaction_block: Ok(Some((test_block_id(), 0))), transaction_block: Ok(Some((test_block_id(), 0))),
@@ -554,9 +557,16 @@ pub(crate) mod tests {
} }
#[async_trait] #[async_trait]
impl SourceClient<TestTransactionProofPipeline> for TestTransactionsSource { impl RelayClient for TestTransactionsSource {
type Error = TestError; type Error = TestError;
async fn reconnect(&mut self) -> Result<(), TestError> {
Ok(())
}
}
#[async_trait]
impl SourceClient<TestTransactionProofPipeline> for TestTransactionsSource {
async fn tick(&self) { async fn tick(&self) {
(self.on_tick)(&mut *self.data.lock()) (self.on_tick)(&mut *self.data.lock())
} }
@@ -584,8 +594,9 @@ pub(crate) mod tests {
} }
} }
#[derive(Clone)]
pub struct TestTransactionsTarget { pub struct TestTransactionsTarget {
pub on_tick: Box<dyn Fn(&mut TestTransactionsTargetData) + Send + Sync>, pub on_tick: Arc<dyn Fn(&mut TestTransactionsTargetData) + Send + Sync>,
pub data: Arc<Mutex<TestTransactionsTargetData>>, pub data: Arc<Mutex<TestTransactionsTargetData>>,
} }
@@ -600,7 +611,7 @@ pub(crate) mod tests {
impl TestTransactionsTarget { impl TestTransactionsTarget {
pub fn new(on_tick: Box<dyn Fn(&mut TestTransactionsTargetData) + Send + Sync>) -> Self { pub fn new(on_tick: Box<dyn Fn(&mut TestTransactionsTargetData) + Send + Sync>) -> Self {
Self { Self {
on_tick, on_tick: Arc::new(on_tick),
data: Arc::new(Mutex::new(TestTransactionsTargetData { data: Arc::new(Mutex::new(TestTransactionsTargetData {
is_header_known: Ok(true), is_header_known: Ok(true),
is_header_finalized: Ok(true), is_header_finalized: Ok(true),
@@ -613,9 +624,16 @@ pub(crate) mod tests {
} }
#[async_trait] #[async_trait]
impl TargetClient<TestTransactionProofPipeline> for TestTransactionsTarget { impl RelayClient for TestTransactionsTarget {
type Error = TestError; type Error = TestError;
async fn reconnect(&mut self) -> Result<(), TestError> {
Ok(())
}
}
#[async_trait]
impl TargetClient<TestTransactionProofPipeline> for TestTransactionsTarget {
async fn tick(&self) { async fn tick(&self) {
(self.on_tick)(&mut *self.data.lock()) (self.on_tick)(&mut *self.data.lock())
} }
@@ -784,6 +802,7 @@ pub(crate) mod tests {
), ),
pre_relayed, pre_relayed,
)) ))
.map_err(|(_, transactions)| transactions)
} }
#[test] #[test]
@@ -27,13 +27,9 @@ use futures::{future::FutureExt, select};
use num_traits::One; use num_traits::One;
use relay_utils::{ use relay_utils::{
metrics::{start as metrics_start, GlobalMetrics, MetricsParams}, metrics::{start as metrics_start, GlobalMetrics, MetricsParams},
retry_backoff, retry_backoff, FailedClient, MaybeConnectionError,
}; };
use std::{future::Future, time::Duration}; use std::future::Future;
/// Delay after connection-related error happened before we'll try
/// reconnection again.
const CONNECTION_ERROR_DELAY: Duration = Duration::from_secs(10);
/// Transactions proofs relay state. /// Transactions proofs relay state.
#[derive(Debug)] #[derive(Debug)]
@@ -43,7 +39,7 @@ pub struct TransactionProofsRelayState<BlockNumber> {
} }
/// Transactions proofs relay storage. /// Transactions proofs relay storage.
pub trait TransactionProofsRelayStorage { pub trait TransactionProofsRelayStorage: Clone {
/// Associated block number. /// Associated block number.
type BlockNumber; type BlockNumber;
@@ -54,7 +50,7 @@ pub trait TransactionProofsRelayStorage {
} }
/// In-memory storage for auto-relay loop. /// In-memory storage for auto-relay loop.
#[derive(Debug)] #[derive(Debug, Clone)]
pub struct InMemoryStorage<BlockNumber> { pub struct InMemoryStorage<BlockNumber> {
best_processed_header_number: BlockNumber, best_processed_header_number: BlockNumber,
} }
@@ -84,21 +80,15 @@ impl<BlockNumber: Clone + Copy> TransactionProofsRelayStorage for InMemoryStorag
/// Run proofs synchronization. /// Run proofs synchronization.
pub fn run<P: TransactionProofPipeline>( pub fn run<P: TransactionProofPipeline>(
mut storage: impl TransactionProofsRelayStorage<BlockNumber = BlockNumberOf<P>>, storage: impl TransactionProofsRelayStorage<BlockNumber = BlockNumberOf<P>>,
source_client: impl SourceClient<P>, source_client: impl SourceClient<P>,
target_client: impl TargetClient<P>, target_client: impl TargetClient<P>,
metrics_params: Option<MetricsParams>, metrics_params: Option<MetricsParams>,
exit_signal: impl Future<Output = ()>, exit_signal: impl Future<Output = ()>,
) { ) {
let mut local_pool = futures::executor::LocalPool::new(); let exit_signal = exit_signal.shared();
let metrics_global = GlobalMetrics::default();
local_pool.run_until(async move { let metrics_exch = ExchangeLoopMetrics::default();
let mut retry_backoff = retry_backoff();
let mut state = storage.state();
let mut current_finalized_block = None;
let mut metrics_global = GlobalMetrics::default();
let mut metrics_exch = ExchangeLoopMetrics::default();
let metrics_enabled = metrics_params.is_some(); let metrics_enabled = metrics_params.is_some();
metrics_start( metrics_start(
format!("{}_to_{}_Exchange", P::SOURCE_NAME, P::TARGET_NAME), format!("{}_to_{}_Exchange", P::SOURCE_NAME, P::TARGET_NAME),
@@ -107,6 +97,44 @@ pub fn run<P: TransactionProofPipeline>(
&metrics_exch, &metrics_exch,
); );
relay_utils::relay_loop::run(
relay_utils::relay_loop::RECONNECT_DELAY,
source_client,
target_client,
|source_client, target_client| {
run_until_connection_lost(
storage.clone(),
source_client,
target_client,
if metrics_enabled {
Some(metrics_global.clone())
} else {
None
},
if metrics_enabled {
Some(metrics_exch.clone())
} else {
None
},
exit_signal.clone(),
)
},
);
}
/// Run proofs synchronization.
async fn run_until_connection_lost<P: TransactionProofPipeline>(
mut storage: impl TransactionProofsRelayStorage<BlockNumber = BlockNumberOf<P>>,
source_client: impl SourceClient<P>,
target_client: impl TargetClient<P>,
metrics_global: Option<GlobalMetrics>,
metrics_exch: Option<ExchangeLoopMetrics>,
exit_signal: impl Future<Output = ()>,
) -> Result<(), FailedClient> {
let mut retry_backoff = retry_backoff();
let mut state = storage.state();
let mut current_finalized_block = None;
let exit_signal = exit_signal.fuse(); let exit_signal = exit_signal.fuse();
futures::pin_mut!(exit_signal); futures::pin_mut!(exit_signal);
@@ -118,35 +146,36 @@ pub fn run<P: TransactionProofPipeline>(
&target_client, &target_client,
&mut state, &mut state,
&mut current_finalized_block, &mut current_finalized_block,
if metrics_enabled { Some(&mut metrics_exch) } else { None }, metrics_exch.as_ref(),
) )
.await; .await;
if metrics_enabled { if let Some(ref metrics_global) = metrics_global {
metrics_global.update(); metrics_global.update().await;
} }
match iteration_result { if let Err((is_connection_error, failed_client)) = iteration_result {
Ok(_) => { if is_connection_error {
return Err(failed_client);
}
let retry_timeout = retry_backoff
.next_backoff()
.unwrap_or(relay_utils::relay_loop::RECONNECT_DELAY);
select! {
_ = async_std::task::sleep(retry_timeout).fuse() => {},
_ = exit_signal => return Ok(()),
}
} else {
retry_backoff.reset(); retry_backoff.reset();
select! { select! {
_ = source_client.tick().fuse() => {}, _ = source_client.tick().fuse() => {},
_ = exit_signal => return, _ = exit_signal => return Ok(()),
}
}
Err(_) => {
let retry_timeout = retry_backoff.next_backoff().unwrap_or(CONNECTION_ERROR_DELAY);
select! {
_ = async_std::task::sleep(retry_timeout).fuse() => {},
_ = exit_signal => return,
} }
} }
} }
} }
});
}
/// Run exchange loop until we need to break. /// Run exchange loop until we need to break.
async fn run_loop_iteration<P: TransactionProofPipeline>( async fn run_loop_iteration<P: TransactionProofPipeline>(
@@ -155,8 +184,8 @@ async fn run_loop_iteration<P: TransactionProofPipeline>(
target_client: &impl TargetClient<P>, target_client: &impl TargetClient<P>,
state: &mut TransactionProofsRelayState<BlockNumberOf<P>>, state: &mut TransactionProofsRelayState<BlockNumberOf<P>>,
current_finalized_block: &mut Option<(P::Block, RelayedBlockTransactions)>, current_finalized_block: &mut Option<(P::Block, RelayedBlockTransactions)>,
mut exchange_loop_metrics: Option<&mut ExchangeLoopMetrics>, exchange_loop_metrics: Option<&ExchangeLoopMetrics>,
) -> Result<(), ()> { ) -> Result<(), (bool, FailedClient)> {
let best_finalized_header_id = match target_client.best_finalized_header_id().await { let best_finalized_header_id = match target_client.best_finalized_header_id().await {
Ok(best_finalized_header_id) => { Ok(best_finalized_header_id) => {
log::debug!( log::debug!(
@@ -178,7 +207,7 @@ async fn run_loop_iteration<P: TransactionProofPipeline>(
err, err,
); );
return Err(()); return Err((err.is_connection_error(), FailedClient::Target));
} }
}; };
@@ -202,7 +231,7 @@ async fn run_loop_iteration<P: TransactionProofPipeline>(
state.best_processed_header_number = state.best_processed_header_number + One::one(); state.best_processed_header_number = state.best_processed_header_number + One::one();
storage.set_state(state); storage.set_state(state);
if let Some(exchange_loop_metrics) = exchange_loop_metrics.as_mut() { if let Some(ref exchange_loop_metrics) = exchange_loop_metrics {
exchange_loop_metrics.update::<P>( exchange_loop_metrics.update::<P>(
state.best_processed_header_number, state.best_processed_header_number,
best_finalized_header_id.0, best_finalized_header_id.0,
@@ -212,9 +241,9 @@ async fn run_loop_iteration<P: TransactionProofPipeline>(
// we have just updated state => proceed to next block retrieval // we have just updated state => proceed to next block retrieval
} }
Err(relayed_transactions) => { Err((failed_client, relayed_transactions)) => {
*current_finalized_block = Some((block, relayed_transactions)); *current_finalized_block = Some((block, relayed_transactions));
return Err(()); return Err((true, failed_client));
} }
} }
} }
@@ -240,7 +269,7 @@ async fn run_loop_iteration<P: TransactionProofPipeline>(
err, err,
); );
return Err(()); return Err((err.is_connection_error(), FailedClient::Source));
} }
} }
} }
@@ -20,6 +20,7 @@ use crate::exchange::{BlockNumberOf, RelayedBlockTransactions, TransactionProofP
use relay_utils::metrics::{register, Counter, CounterVec, GaugeVec, Metrics, Opts, Registry, U64}; use relay_utils::metrics::{register, Counter, CounterVec, GaugeVec, Metrics, Opts, Registry, U64};
/// Exchange transactions relay metrics. /// Exchange transactions relay metrics.
#[derive(Clone)]
pub struct ExchangeLoopMetrics { pub struct ExchangeLoopMetrics {
/// Best finalized block numbers - "processed" and "known". /// Best finalized block numbers - "processed" and "known".
best_block_numbers: GaugeVec<U64>, best_block_numbers: GaugeVec<U64>,
@@ -60,7 +61,7 @@ impl Default for ExchangeLoopMetrics {
impl ExchangeLoopMetrics { impl ExchangeLoopMetrics {
/// Update metrics when single block is relayed. /// Update metrics when single block is relayed.
pub fn update<P: TransactionProofPipeline>( pub fn update<P: TransactionProofPipeline>(
&mut self, &self,
best_processed_block_number: BlockNumberOf<P>, best_processed_block_number: BlockNumberOf<P>,
best_known_block_number: BlockNumberOf<P>, best_known_block_number: BlockNumberOf<P>,
relayed_transactions: RelayedBlockTransactions, relayed_transactions: RelayedBlockTransactions,
+75 -38
View File
@@ -26,7 +26,9 @@ use num_traits::{Saturating, Zero};
use relay_utils::{ use relay_utils::{
format_ids, interval, format_ids, interval,
metrics::{start as metrics_start, GlobalMetrics, MetricsParams}, metrics::{start as metrics_start, GlobalMetrics, MetricsParams},
process_future_result, retry_backoff, MaybeConnectionError, StringifiedMaybeConnectionError, process_future_result,
relay_loop::Client as RelayClient,
retry_backoff, FailedClient, MaybeConnectionError, StringifiedMaybeConnectionError,
}; };
use std::{ use std::{
collections::HashSet, collections::HashSet,
@@ -53,10 +55,7 @@ const MAINTAIN_INTERVAL: Duration = Duration::from_secs(30);
/// Source client trait. /// Source client trait.
#[async_trait] #[async_trait]
pub trait SourceClient<P: HeadersSyncPipeline> { pub trait SourceClient<P: HeadersSyncPipeline>: RelayClient {
/// Type of error this clients returns.
type Error: std::fmt::Debug + MaybeConnectionError;
/// Get best block number. /// Get best block number.
async fn best_block_number(&self) -> Result<P::Number, Self::Error>; async fn best_block_number(&self) -> Result<P::Number, Self::Error>;
@@ -80,10 +79,7 @@ pub trait SourceClient<P: HeadersSyncPipeline> {
/// Target client trait. /// Target client trait.
#[async_trait] #[async_trait]
pub trait TargetClient<P: HeadersSyncPipeline> { pub trait TargetClient<P: HeadersSyncPipeline>: RelayClient {
/// Type of error this clients returns.
type Error: std::fmt::Debug + MaybeConnectionError;
/// Returns ID of best header known to the target node. /// Returns ID of best header known to the target node.
async fn best_header_id(&self) -> Result<HeaderIdOf<P>, Self::Error>; async fn best_header_id(&self) -> Result<HeaderIdOf<P>, Self::Error>;
@@ -106,7 +102,7 @@ pub trait TargetClient<P: HeadersSyncPipeline> {
/// Synchronization maintain procedure. /// Synchronization maintain procedure.
#[async_trait] #[async_trait]
pub trait SyncMaintain<P: HeadersSyncPipeline>: Send + Sync { pub trait SyncMaintain<P: HeadersSyncPipeline>: Clone + Send + Sync {
/// Run custom maintain procedures. This is guaranteed to be called when both source and target /// Run custom maintain procedures. This is guaranteed to be called when both source and target
/// clients are unoccupied. /// clients are unoccupied.
async fn maintain(&self, _sync: &mut HeadersSync<P>) {} async fn maintain(&self, _sync: &mut HeadersSync<P>) {}
@@ -126,17 +122,10 @@ pub fn run<P: HeadersSyncPipeline, TC: TargetClient<P>>(
metrics_params: Option<MetricsParams>, metrics_params: Option<MetricsParams>,
exit_signal: impl Future<Output = ()>, exit_signal: impl Future<Output = ()>,
) { ) {
#![allow(unused_variables)] // this is to suppress weird errors from clippy let exit_signal = exit_signal.shared();
let mut local_pool = futures::executor::LocalPool::new();
let mut progress_context = (Instant::now(), None, None);
local_pool.run_until(async move { let metrics_global = GlobalMetrics::default();
let mut sync = HeadersSync::<P>::new(sync_params); let metrics_sync = SyncLoopMetrics::default();
let mut stall_countdown = None;
let mut last_update_time = Instant::now();
let mut metrics_global = GlobalMetrics::default();
let mut metrics_sync = SyncLoopMetrics::default();
let metrics_enabled = metrics_params.is_some(); let metrics_enabled = metrics_params.is_some();
metrics_start( metrics_start(
format!("{}_to_{}_Sync", P::SOURCE_NAME, P::TARGET_NAME), format!("{}_to_{}_Sync", P::SOURCE_NAME, P::TARGET_NAME),
@@ -145,6 +134,53 @@ pub fn run<P: HeadersSyncPipeline, TC: TargetClient<P>>(
&metrics_sync, &metrics_sync,
); );
relay_utils::relay_loop::run(
relay_utils::relay_loop::RECONNECT_DELAY,
source_client,
target_client,
|source_client, target_client| {
run_until_connection_lost(
source_client,
source_tick,
target_client,
target_tick,
sync_maintain.clone(),
sync_params.clone(),
if metrics_enabled {
Some(metrics_global.clone())
} else {
None
},
if metrics_enabled {
Some(metrics_sync.clone())
} else {
None
},
exit_signal.clone(),
)
},
);
}
/// Run headers synchronization.
#[allow(clippy::too_many_arguments)]
async fn run_until_connection_lost<P: HeadersSyncPipeline, TC: TargetClient<P>>(
source_client: impl SourceClient<P>,
source_tick: Duration,
target_client: TC,
target_tick: Duration,
sync_maintain: impl SyncMaintain<P>,
sync_params: HeadersSyncParams,
metrics_global: Option<GlobalMetrics>,
metrics_sync: Option<SyncLoopMetrics>,
exit_signal: impl Future<Output = ()>,
) -> Result<(), FailedClient> {
let mut progress_context = (Instant::now(), None, None);
let mut sync = HeadersSync::<P>::new(sync_params);
let mut stall_countdown = None;
let mut last_update_time = Instant::now();
let mut source_retry_backoff = retry_backoff(); let mut source_retry_backoff = retry_backoff();
let mut source_client_is_online = false; let mut source_client_is_online = false;
let mut source_best_block_number_required = false; let mut source_best_block_number_required = false;
@@ -206,7 +242,7 @@ pub fn run<P: HeadersSyncPipeline, TC: TargetClient<P>>(
&mut source_go_offline_future, &mut source_go_offline_future,
async_std::task::sleep, async_std::task::sleep,
|| format!("Error retrieving best header number from {}", P::SOURCE_NAME), || format!("Error retrieving best header number from {}", P::SOURCE_NAME),
).is_ok(); ).fail_if_connection_error(FailedClient::Source)?;
}, },
source_new_header = source_new_header_future => { source_new_header = source_new_header_future => {
source_client_is_online = process_future_result( source_client_is_online = process_future_result(
@@ -216,7 +252,7 @@ pub fn run<P: HeadersSyncPipeline, TC: TargetClient<P>>(
&mut source_go_offline_future, &mut source_go_offline_future,
async_std::task::sleep, async_std::task::sleep,
|| format!("Error retrieving header from {} node", P::SOURCE_NAME), || format!("Error retrieving header from {} node", P::SOURCE_NAME),
).is_ok(); ).fail_if_connection_error(FailedClient::Source)?;
}, },
source_orphan_header = source_orphan_header_future => { source_orphan_header = source_orphan_header_future => {
source_client_is_online = process_future_result( source_client_is_online = process_future_result(
@@ -226,7 +262,7 @@ pub fn run<P: HeadersSyncPipeline, TC: TargetClient<P>>(
&mut source_go_offline_future, &mut source_go_offline_future,
async_std::task::sleep, async_std::task::sleep,
|| format!("Error retrieving orphan header from {} node", P::SOURCE_NAME), || format!("Error retrieving orphan header from {} node", P::SOURCE_NAME),
).is_ok(); ).fail_if_connection_error(FailedClient::Source)?;
}, },
source_extra = source_extra_future => { source_extra = source_extra_future => {
source_client_is_online = process_future_result( source_client_is_online = process_future_result(
@@ -236,7 +272,7 @@ pub fn run<P: HeadersSyncPipeline, TC: TargetClient<P>>(
&mut source_go_offline_future, &mut source_go_offline_future,
async_std::task::sleep, async_std::task::sleep,
|| format!("Error retrieving extra data from {} node", P::SOURCE_NAME), || format!("Error retrieving extra data from {} node", P::SOURCE_NAME),
).is_ok(); ).fail_if_connection_error(FailedClient::Source)?;
}, },
source_completion = source_completion_future => { source_completion = source_completion_future => {
source_client_is_online = process_future_result( source_client_is_online = process_future_result(
@@ -246,9 +282,9 @@ pub fn run<P: HeadersSyncPipeline, TC: TargetClient<P>>(
&mut source_go_offline_future, &mut source_go_offline_future,
async_std::task::sleep, async_std::task::sleep,
|| format!("Error retrieving completion data from {} node", P::SOURCE_NAME), || format!("Error retrieving completion data from {} node", P::SOURCE_NAME),
).is_ok(); ).fail_if_connection_error(FailedClient::Source)?;
}, },
source_client = source_go_offline_future => { _ = source_go_offline_future => {
source_client_is_online = true; source_client_is_online = true;
}, },
_ = source_tick_stream.next() => { _ = source_tick_stream.next() => {
@@ -297,7 +333,7 @@ pub fn run<P: HeadersSyncPipeline, TC: TargetClient<P>>(
&mut target_go_offline_future, &mut target_go_offline_future,
async_std::task::sleep, async_std::task::sleep,
|| format!("Error retrieving best known {} header from {} node", P::SOURCE_NAME, P::TARGET_NAME), || format!("Error retrieving best known {} header from {} node", P::SOURCE_NAME, P::TARGET_NAME),
).is_ok(); ).fail_if_connection_error(FailedClient::Target)?;
}, },
incomplete_headers_ids = target_incomplete_headers_future => { incomplete_headers_ids = target_incomplete_headers_future => {
target_incomplete_headers_required = false; target_incomplete_headers_required = false;
@@ -309,7 +345,7 @@ pub fn run<P: HeadersSyncPipeline, TC: TargetClient<P>>(
&mut target_go_offline_future, &mut target_go_offline_future,
async_std::task::sleep, async_std::task::sleep,
|| format!("Error retrieving incomplete headers from {} node", P::TARGET_NAME), || format!("Error retrieving incomplete headers from {} node", P::TARGET_NAME),
).is_ok(); ).fail_if_connection_error(FailedClient::Target)?;
}, },
target_existence_status = target_existence_status_future => { target_existence_status = target_existence_status_future => {
target_client_is_online = process_future_result( target_client_is_online = process_future_result(
@@ -321,7 +357,7 @@ pub fn run<P: HeadersSyncPipeline, TC: TargetClient<P>>(
&mut target_go_offline_future, &mut target_go_offline_future,
async_std::task::sleep, async_std::task::sleep,
|| format!("Error retrieving existence status from {} node", P::TARGET_NAME), || format!("Error retrieving existence status from {} node", P::TARGET_NAME),
).is_ok(); ).fail_if_connection_error(FailedClient::Target)?;
}, },
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` :/
@@ -349,7 +385,7 @@ pub fn run<P: HeadersSyncPipeline, TC: TargetClient<P>>(
&mut target_go_offline_future, &mut target_go_offline_future,
async_std::task::sleep, async_std::task::sleep,
|| format!("Error submitting headers to {} node", P::TARGET_NAME), || format!("Error submitting headers to {} node", P::TARGET_NAME),
).is_ok(); ).fail_if_connection_error(FailedClient::Target)?;
log::debug!(target: "bridge", "Header submit result: {}", submitted_headers_str); log::debug!(target: "bridge", "Header submit result: {}", submitted_headers_str);
@@ -370,7 +406,7 @@ pub fn run<P: HeadersSyncPipeline, TC: TargetClient<P>>(
&mut target_go_offline_future, &mut target_go_offline_future,
async_std::task::sleep, async_std::task::sleep,
|| format!("Error completing headers at {}", P::TARGET_NAME), || format!("Error completing headers at {}", P::TARGET_NAME),
).is_ok(); ).fail_if_connection_error(FailedClient::Target)?;
}, },
target_extra_check_result = target_extra_check_future => { target_extra_check_result = target_extra_check_future => {
target_client_is_online = process_future_result( target_client_is_online = process_future_result(
@@ -382,9 +418,9 @@ pub fn run<P: HeadersSyncPipeline, TC: TargetClient<P>>(
&mut target_go_offline_future, &mut target_go_offline_future,
async_std::task::sleep, async_std::task::sleep,
|| format!("Error retrieving receipts requirement from {} node", P::TARGET_NAME), || format!("Error retrieving receipts requirement from {} node", P::TARGET_NAME),
).is_ok(); ).fail_if_connection_error(FailedClient::Target)?;
}, },
target_client = target_go_offline_future => { _ = target_go_offline_future => {
target_client_is_online = true; target_client_is_online = true;
}, },
_ = target_tick_stream.next() => { _ = target_tick_stream.next() => {
@@ -396,13 +432,15 @@ pub fn run<P: HeadersSyncPipeline, TC: TargetClient<P>>(
maintain_required = true; maintain_required = true;
}, },
_ = exit_signal => { _ = exit_signal => {
return; return Ok(());
} }
} }
// update metrics // update metrics
if metrics_enabled { if let Some(ref metrics_global) = metrics_global {
metrics_global.update(); metrics_global.update().await;
}
if let Some(ref metrics_sync) = metrics_sync {
metrics_sync.update(&sync); metrics_sync.update(&sync);
} }
@@ -559,7 +597,7 @@ pub fn run<P: HeadersSyncPipeline, TC: TargetClient<P>>(
P::SOURCE_NAME, P::SOURCE_NAME,
P::TARGET_NAME, P::TARGET_NAME,
); );
return; return Ok(());
} }
log::debug!( log::debug!(
@@ -584,7 +622,6 @@ pub fn run<P: HeadersSyncPipeline, TC: TargetClient<P>>(
} }
} }
} }
});
} }
/// Print synchronization progress. /// Print synchronization progress.
@@ -23,6 +23,7 @@ use num_traits::Zero;
use relay_utils::metrics::{register, GaugeVec, Metrics, Opts, Registry, U64}; use relay_utils::metrics::{register, GaugeVec, Metrics, Opts, Registry, U64};
/// Headers sync metrics. /// Headers sync metrics.
#[derive(Clone)]
pub struct SyncLoopMetrics { pub struct SyncLoopMetrics {
/// Best syncing headers at "source" and "target" nodes. /// Best syncing headers at "source" and "target" nodes.
best_block_numbers: GaugeVec<U64>, best_block_numbers: GaugeVec<U64>,
@@ -57,7 +58,7 @@ impl Default for SyncLoopMetrics {
impl SyncLoopMetrics { impl SyncLoopMetrics {
/// Update metrics. /// Update metrics.
pub fn update<P: HeadersSyncPipeline>(&mut self, sync: &HeadersSync<P>) { pub fn update<P: HeadersSyncPipeline>(&self, sync: &HeadersSync<P>) {
let headers = sync.headers(); let headers = sync.headers();
let source_best_number = sync.source_best_number().unwrap_or_else(Zero::zero); let source_best_number = sync.source_best_number().unwrap_or_else(Zero::zero);
let target_best_number = sync.target_best_header().map(|id| id.0).unwrap_or_else(Zero::zero); let target_best_number = sync.target_best_header().map(|id| id.0).unwrap_or_else(Zero::zero);
@@ -23,7 +23,9 @@ use async_trait::async_trait;
use backoff::backoff::Backoff; use backoff::backoff::Backoff;
use futures::{future::FutureExt, stream::StreamExt}; use futures::{future::FutureExt, stream::StreamExt};
use parking_lot::Mutex; use parking_lot::Mutex;
use relay_utils::{process_future_result, retry_backoff, HeaderId, MaybeConnectionError}; use relay_utils::{
process_future_result, relay_loop::Client as RelayClient, retry_backoff, HeaderId, MaybeConnectionError,
};
use std::{ use std::{
collections::{HashMap, HashSet}, collections::{HashMap, HashSet},
sync::Arc, sync::Arc,
@@ -89,8 +91,9 @@ enum SourceMethod {
HeaderExtra(TestHeaderId, TestQueuedHeader), HeaderExtra(TestHeaderId, TestQueuedHeader),
} }
#[derive(Clone)]
struct Source { struct Source {
data: Mutex<SourceData>, data: Arc<Mutex<SourceData>>,
on_method_call: Arc<dyn Fn(SourceMethod, &mut SourceData) + Send + Sync>, on_method_call: Arc<dyn Fn(SourceMethod, &mut SourceData) + Send + Sync>,
} }
@@ -109,7 +112,7 @@ impl Source {
on_method_call: impl Fn(SourceMethod, &mut SourceData) + Send + Sync + 'static, on_method_call: impl Fn(SourceMethod, &mut SourceData) + Send + Sync + 'static,
) -> Self { ) -> Self {
Source { Source {
data: Mutex::new(SourceData { data: Arc::new(Mutex::new(SourceData {
best_block_number: Ok(best_block_id.0), best_block_number: Ok(best_block_id.0),
header_by_hash: headers header_by_hash: headers
.iter() .iter()
@@ -127,35 +130,42 @@ impl Source {
.collect(), .collect(),
provides_completion: true, provides_completion: true,
provides_extra: true, provides_extra: true,
}), })),
on_method_call: Arc::new(on_method_call), on_method_call: Arc::new(on_method_call),
} }
} }
} }
#[async_trait] #[async_trait]
impl SourceClient<TestHeadersSyncPipeline> for Source { impl RelayClient for Source {
type Error = TestError; type Error = TestError;
async fn best_block_number(&self) -> Result<TestNumber, Self::Error> { async fn reconnect(&mut self) -> Result<(), TestError> {
unimplemented!()
}
}
#[async_trait]
impl SourceClient<TestHeadersSyncPipeline> for Source {
async fn best_block_number(&self) -> Result<TestNumber, TestError> {
let mut data = self.data.lock(); let mut data = self.data.lock();
(self.on_method_call)(SourceMethod::BestBlockNumber, &mut *data); (self.on_method_call)(SourceMethod::BestBlockNumber, &mut *data);
data.best_block_number.clone() data.best_block_number.clone()
} }
async fn header_by_hash(&self, hash: TestHash) -> Result<TestHeader, Self::Error> { async fn header_by_hash(&self, hash: TestHash) -> Result<TestHeader, TestError> {
let mut data = self.data.lock(); let mut data = self.data.lock();
(self.on_method_call)(SourceMethod::HeaderByHash(hash), &mut *data); (self.on_method_call)(SourceMethod::HeaderByHash(hash), &mut *data);
data.header_by_hash.get(&hash).cloned().ok_or(TestError(false)) data.header_by_hash.get(&hash).cloned().ok_or(TestError(false))
} }
async fn header_by_number(&self, number: TestNumber) -> Result<TestHeader, Self::Error> { async fn header_by_number(&self, number: TestNumber) -> Result<TestHeader, TestError> {
let mut data = self.data.lock(); let mut data = self.data.lock();
(self.on_method_call)(SourceMethod::HeaderByNumber(number), &mut *data); (self.on_method_call)(SourceMethod::HeaderByNumber(number), &mut *data);
data.header_by_number.get(&number).cloned().ok_or(TestError(false)) data.header_by_number.get(&number).cloned().ok_or(TestError(false))
} }
async fn header_completion(&self, id: TestHeaderId) -> Result<(TestHeaderId, Option<TestCompletion>), Self::Error> { async fn header_completion(&self, id: TestHeaderId) -> Result<(TestHeaderId, Option<TestCompletion>), TestError> {
let mut data = self.data.lock(); let mut data = self.data.lock();
(self.on_method_call)(SourceMethod::HeaderCompletion(id), &mut *data); (self.on_method_call)(SourceMethod::HeaderCompletion(id), &mut *data);
if data.provides_completion { if data.provides_completion {
@@ -169,7 +179,7 @@ impl SourceClient<TestHeadersSyncPipeline> for Source {
&self, &self,
id: TestHeaderId, id: TestHeaderId,
header: TestQueuedHeader, header: TestQueuedHeader,
) -> Result<(TestHeaderId, TestExtra), Self::Error> { ) -> Result<(TestHeaderId, TestExtra), TestError> {
let mut data = self.data.lock(); let mut data = self.data.lock();
(self.on_method_call)(SourceMethod::HeaderExtra(id, header), &mut *data); (self.on_method_call)(SourceMethod::HeaderExtra(id, header), &mut *data);
if data.provides_extra { if data.provides_extra {
@@ -189,8 +199,9 @@ enum TargetMethod {
RequiresExtra(TestQueuedHeader), RequiresExtra(TestQueuedHeader),
} }
#[derive(Clone)]
struct Target { struct Target {
data: Mutex<TargetData>, data: Arc<Mutex<TargetData>>,
on_method_call: Arc<dyn Fn(TargetMethod, &mut TargetData) + Send + Sync>, on_method_call: Arc<dyn Fn(TargetMethod, &mut TargetData) + Send + Sync>,
} }
@@ -211,7 +222,7 @@ impl Target {
on_method_call: impl Fn(TargetMethod, &mut TargetData) + Send + Sync + 'static, on_method_call: impl Fn(TargetMethod, &mut TargetData) + Send + Sync + 'static,
) -> Self { ) -> Self {
Target { Target {
data: Mutex::new(TargetData { data: Arc::new(Mutex::new(TargetData {
best_header_id: Ok(best_header_id), best_header_id: Ok(best_header_id),
is_known_header_by_hash: headers.iter().map(|header| (header.1, true)).collect(), is_known_header_by_hash: headers.iter().map(|header| (header.1, true)).collect(),
submitted_headers: HashMap::new(), submitted_headers: HashMap::new(),
@@ -219,16 +230,23 @@ impl Target {
completed_headers: HashMap::new(), completed_headers: HashMap::new(),
requires_completion: false, requires_completion: false,
requires_extra: false, requires_extra: false,
}), })),
on_method_call: Arc::new(on_method_call), on_method_call: Arc::new(on_method_call),
} }
} }
} }
#[async_trait] #[async_trait]
impl TargetClient<TestHeadersSyncPipeline> for Target { impl RelayClient for Target {
type Error = TestError; type Error = TestError;
async fn reconnect(&mut self) -> Result<(), TestError> {
unimplemented!()
}
}
#[async_trait]
impl TargetClient<TestHeadersSyncPipeline> for Target {
async fn best_header_id(&self) -> Result<TestHeaderId, TestError> { async fn best_header_id(&self) -> Result<TestHeaderId, TestError> {
let mut data = self.data.lock(); let mut data = self.data.lock();
(self.on_method_call)(TargetMethod::BestHeaderId, &mut *data); (self.on_method_call)(TargetMethod::BestHeaderId, &mut *data);
@@ -35,7 +35,9 @@ use futures::{channel::mpsc::unbounded, future::FutureExt, stream::StreamExt};
use relay_utils::{ use relay_utils::{
interval, interval,
metrics::{start as metrics_start, GlobalMetrics, MetricsParams}, metrics::{start as metrics_start, GlobalMetrics, MetricsParams},
process_future_result, retry_backoff, FailedClient, MaybeConnectionError, process_future_result,
relay_loop::Client as RelayClient,
retry_backoff, FailedClient,
}; };
use std::{collections::BTreeMap, fmt::Debug, future::Future, ops::RangeInclusive, time::Duration}; use std::{collections::BTreeMap, fmt::Debug, future::Future, ops::RangeInclusive, time::Duration};
@@ -98,13 +100,7 @@ pub struct MessageProofParameters {
/// Source client trait. /// Source client trait.
#[async_trait] #[async_trait]
pub trait SourceClient<P: MessageLane>: Clone + Send + Sync { pub trait SourceClient<P: MessageLane>: RelayClient {
/// Type of error this clients returns.
type Error: std::fmt::Debug + MaybeConnectionError;
/// Try to reconnect to source node.
async fn reconnect(self) -> Result<Self, Self::Error>;
/// Returns state of the client. /// Returns state of the client.
async fn state(&self) -> Result<SourceClientState<P>, Self::Error>; async fn state(&self) -> Result<SourceClientState<P>, Self::Error>;
@@ -147,13 +143,7 @@ pub trait SourceClient<P: MessageLane>: Clone + Send + Sync {
/// Target client trait. /// Target client trait.
#[async_trait] #[async_trait]
pub trait TargetClient<P: MessageLane>: Clone + Send + Sync { pub trait TargetClient<P: MessageLane>: RelayClient {
/// Type of error this clients returns.
type Error: std::fmt::Debug + MaybeConnectionError;
/// Try to reconnect to source node.
async fn reconnect(self) -> Result<Self, Self::Error>;
/// Returns state of the client. /// Returns state of the client.
async fn state(&self) -> Result<TargetClientState<P>, Self::Error>; async fn state(&self) -> Result<TargetClientState<P>, Self::Error>;
@@ -218,16 +208,13 @@ pub struct ClientsState<P: MessageLane> {
/// Run message lane service loop. /// Run message lane service loop.
pub fn run<P: MessageLane>( pub fn run<P: MessageLane>(
params: Params, params: Params,
mut source_client: impl SourceClient<P>, source_client: impl SourceClient<P>,
mut target_client: impl TargetClient<P>, target_client: impl TargetClient<P>,
metrics_params: Option<MetricsParams>, metrics_params: Option<MetricsParams>,
exit_signal: impl Future<Output = ()>, exit_signal: impl Future<Output = ()>,
) { ) {
let mut local_pool = futures::executor::LocalPool::new();
let exit_signal = exit_signal.shared(); let exit_signal = exit_signal.shared();
let metrics_global = GlobalMetrics::default();
local_pool.run_until(async move {
let mut metrics_global = GlobalMetrics::default();
let metrics_msg = MessageLaneLoopMetrics::default(); let metrics_msg = MessageLaneLoopMetrics::default();
let metrics_enabled = metrics_params.is_some(); let metrics_enabled = metrics_params.is_some();
metrics_start( metrics_start(
@@ -242,13 +229,17 @@ pub fn run<P: MessageLane>(
&metrics_msg, &metrics_msg,
); );
loop { relay_utils::relay_loop::run(
let result = run_until_connection_lost( params.reconnect_delay,
source_client,
target_client,
|source_client, target_client| {
run_until_connection_lost(
params.clone(), params.clone(),
source_client.clone(), source_client,
target_client.clone(), target_client,
if metrics_enabled { if metrics_enabled {
Some(&mut metrics_global) Some(metrics_global.clone())
} else { } else {
None None
}, },
@@ -259,63 +250,16 @@ pub fn run<P: MessageLane>(
}, },
exit_signal.clone(), exit_signal.clone(),
) )
.await;
match result {
Ok(()) => break,
Err(failed_client) => loop {
async_std::task::sleep(params.reconnect_delay).await;
if failed_client == FailedClient::Both || failed_client == FailedClient::Source {
source_client = match source_client.clone().reconnect().await {
Ok(source_client) => source_client,
Err(error) => {
log::warn!(
target: "bridge",
"Failed to reconnect {}. Going to retry in {}s: {:?}",
P::SOURCE_NAME,
params.reconnect_delay.as_secs(),
error,
);
continue;
}
}
}
if failed_client == FailedClient::Both || failed_client == FailedClient::Target {
target_client = match target_client.clone().reconnect().await {
Ok(target_client) => target_client,
Err(error) => {
log::warn!(
target: "bridge",
"Failed to reconnect {}. Going to retry in {}s: {:?}",
P::TARGET_NAME,
params.reconnect_delay.as_secs(),
error,
);
continue;
}
}
}
break;
}, },
}
log::debug!(
target: "bridge",
"Restarting lane {} -> {}",
P::SOURCE_NAME,
P::TARGET_NAME,
); );
} }
});
}
/// Run one-way message delivery loop until connection with target or source node is lost, or exit signal is received. /// Run one-way message delivery loop until connection with target or source node is lost, or exit signal is received.
async fn run_until_connection_lost<P: MessageLane, SC: SourceClient<P>, TC: TargetClient<P>>( async fn run_until_connection_lost<P: MessageLane, SC: SourceClient<P>, TC: TargetClient<P>>(
params: Params, params: Params,
source_client: SC, source_client: SC,
target_client: TC, target_client: TC,
mut metrics_global: Option<&mut GlobalMetrics>, metrics_global: Option<GlobalMetrics>,
metrics_msg: Option<MessageLaneLoopMetrics>, metrics_msg: Option<MessageLaneLoopMetrics>,
exit_signal: impl Future<Output = ()>, exit_signal: impl Future<Output = ()>,
) -> Result<(), FailedClient> { ) -> Result<(), FailedClient> {
@@ -459,8 +403,8 @@ async fn run_until_connection_lost<P: MessageLane, SC: SourceClient<P>, TC: Targ
} }
} }
if let Some(metrics_global) = metrics_global.as_mut() { if let Some(ref metrics_global) = metrics_global {
metrics_global.update(); metrics_global.update().await;
} }
if source_client_is_online && source_state_required { if source_client_is_online && source_state_required {
@@ -482,7 +426,7 @@ pub(crate) mod tests {
use super::*; use super::*;
use futures::stream::StreamExt; use futures::stream::StreamExt;
use parking_lot::Mutex; use parking_lot::Mutex;
use relay_utils::HeaderId; use relay_utils::{HeaderId, MaybeConnectionError};
use std::sync::Arc; use std::sync::Arc;
pub fn header_id(number: TestSourceHeaderNumber) -> TestSourceHeaderId { pub fn header_id(number: TestSourceHeaderNumber) -> TestSourceHeaderId {
@@ -550,19 +494,22 @@ pub(crate) mod tests {
} }
#[async_trait] #[async_trait]
impl SourceClient<TestMessageLane> for TestSourceClient { impl RelayClient for TestSourceClient {
type Error = TestError; type Error = TestError;
async fn reconnect(self) -> Result<Self, Self::Error> { async fn reconnect(&mut self) -> Result<(), TestError> {
{ {
let mut data = self.data.lock(); let mut data = self.data.lock();
(self.tick)(&mut *data); (self.tick)(&mut *data);
data.is_source_reconnected = true; data.is_source_reconnected = true;
} }
Ok(self) Ok(())
}
} }
async fn state(&self) -> Result<SourceClientState<TestMessageLane>, Self::Error> { #[async_trait]
impl SourceClient<TestMessageLane> for TestSourceClient {
async fn state(&self) -> Result<SourceClientState<TestMessageLane>, TestError> {
let mut data = self.data.lock(); let mut data = self.data.lock();
(self.tick)(&mut *data); (self.tick)(&mut *data);
if data.is_source_fails { if data.is_source_fails {
@@ -574,7 +521,7 @@ pub(crate) mod tests {
async fn latest_generated_nonce( async fn latest_generated_nonce(
&self, &self,
id: SourceHeaderIdOf<TestMessageLane>, id: SourceHeaderIdOf<TestMessageLane>,
) -> Result<(SourceHeaderIdOf<TestMessageLane>, MessageNonce), Self::Error> { ) -> Result<(SourceHeaderIdOf<TestMessageLane>, MessageNonce), TestError> {
let mut data = self.data.lock(); let mut data = self.data.lock();
(self.tick)(&mut *data); (self.tick)(&mut *data);
if data.is_source_fails { if data.is_source_fails {
@@ -586,7 +533,7 @@ pub(crate) mod tests {
async fn latest_confirmed_received_nonce( async fn latest_confirmed_received_nonce(
&self, &self,
id: SourceHeaderIdOf<TestMessageLane>, id: SourceHeaderIdOf<TestMessageLane>,
) -> Result<(SourceHeaderIdOf<TestMessageLane>, MessageNonce), Self::Error> { ) -> Result<(SourceHeaderIdOf<TestMessageLane>, MessageNonce), TestError> {
let mut data = self.data.lock(); let mut data = self.data.lock();
(self.tick)(&mut *data); (self.tick)(&mut *data);
Ok((id, data.source_latest_confirmed_received_nonce)) Ok((id, data.source_latest_confirmed_received_nonce))
@@ -596,7 +543,7 @@ pub(crate) mod tests {
&self, &self,
_id: SourceHeaderIdOf<TestMessageLane>, _id: SourceHeaderIdOf<TestMessageLane>,
nonces: RangeInclusive<MessageNonce>, nonces: RangeInclusive<MessageNonce>,
) -> Result<MessageWeightsMap, Self::Error> { ) -> Result<MessageWeightsMap, TestError> {
Ok(nonces Ok(nonces
.map(|nonce| (nonce, MessageWeights { weight: 1, size: 1 })) .map(|nonce| (nonce, MessageWeights { weight: 1, size: 1 }))
.collect()) .collect())
@@ -613,7 +560,7 @@ pub(crate) mod tests {
RangeInclusive<MessageNonce>, RangeInclusive<MessageNonce>,
TestMessagesProof, TestMessagesProof,
), ),
Self::Error, TestError,
> { > {
let mut data = self.data.lock(); let mut data = self.data.lock();
(self.tick)(&mut *data); (self.tick)(&mut *data);
@@ -635,7 +582,7 @@ pub(crate) mod tests {
&self, &self,
_generated_at_block: TargetHeaderIdOf<TestMessageLane>, _generated_at_block: TargetHeaderIdOf<TestMessageLane>,
proof: TestMessagesReceivingProof, proof: TestMessagesReceivingProof,
) -> Result<(), Self::Error> { ) -> Result<(), TestError> {
let mut data = self.data.lock(); let mut data = self.data.lock();
(self.tick)(&mut *data); (self.tick)(&mut *data);
data.submitted_messages_receiving_proofs.push(proof); data.submitted_messages_receiving_proofs.push(proof);
@@ -651,19 +598,22 @@ pub(crate) mod tests {
} }
#[async_trait] #[async_trait]
impl TargetClient<TestMessageLane> for TestTargetClient { impl RelayClient for TestTargetClient {
type Error = TestError; type Error = TestError;
async fn reconnect(self) -> Result<Self, Self::Error> { async fn reconnect(&mut self) -> Result<(), TestError> {
{ {
let mut data = self.data.lock(); let mut data = self.data.lock();
(self.tick)(&mut *data); (self.tick)(&mut *data);
data.is_target_reconnected = true; data.is_target_reconnected = true;
} }
Ok(self) Ok(())
}
} }
async fn state(&self) -> Result<TargetClientState<TestMessageLane>, Self::Error> { #[async_trait]
impl TargetClient<TestMessageLane> for TestTargetClient {
async fn state(&self) -> Result<TargetClientState<TestMessageLane>, TestError> {
let mut data = self.data.lock(); let mut data = self.data.lock();
(self.tick)(&mut *data); (self.tick)(&mut *data);
if data.is_target_fails { if data.is_target_fails {
@@ -675,7 +625,7 @@ pub(crate) mod tests {
async fn latest_received_nonce( async fn latest_received_nonce(
&self, &self,
id: TargetHeaderIdOf<TestMessageLane>, id: TargetHeaderIdOf<TestMessageLane>,
) -> Result<(TargetHeaderIdOf<TestMessageLane>, MessageNonce), Self::Error> { ) -> Result<(TargetHeaderIdOf<TestMessageLane>, MessageNonce), TestError> {
let mut data = self.data.lock(); let mut data = self.data.lock();
(self.tick)(&mut *data); (self.tick)(&mut *data);
if data.is_target_fails { if data.is_target_fails {
@@ -687,7 +637,7 @@ pub(crate) mod tests {
async fn unrewarded_relayers_state( async fn unrewarded_relayers_state(
&self, &self,
id: TargetHeaderIdOf<TestMessageLane>, id: TargetHeaderIdOf<TestMessageLane>,
) -> Result<(TargetHeaderIdOf<TestMessageLane>, UnrewardedRelayersState), Self::Error> { ) -> Result<(TargetHeaderIdOf<TestMessageLane>, UnrewardedRelayersState), TestError> {
Ok(( Ok((
id, id,
UnrewardedRelayersState { UnrewardedRelayersState {
@@ -701,7 +651,7 @@ pub(crate) mod tests {
async fn latest_confirmed_received_nonce( async fn latest_confirmed_received_nonce(
&self, &self,
id: TargetHeaderIdOf<TestMessageLane>, id: TargetHeaderIdOf<TestMessageLane>,
) -> Result<(TargetHeaderIdOf<TestMessageLane>, MessageNonce), Self::Error> { ) -> Result<(TargetHeaderIdOf<TestMessageLane>, MessageNonce), TestError> {
let mut data = self.data.lock(); let mut data = self.data.lock();
(self.tick)(&mut *data); (self.tick)(&mut *data);
if data.is_target_fails { if data.is_target_fails {
@@ -713,7 +663,7 @@ pub(crate) mod tests {
async fn prove_messages_receiving( async fn prove_messages_receiving(
&self, &self,
id: TargetHeaderIdOf<TestMessageLane>, id: TargetHeaderIdOf<TestMessageLane>,
) -> Result<(TargetHeaderIdOf<TestMessageLane>, TestMessagesReceivingProof), Self::Error> { ) -> Result<(TargetHeaderIdOf<TestMessageLane>, TestMessagesReceivingProof), TestError> {
Ok((id, self.data.lock().target_latest_received_nonce)) Ok((id, self.data.lock().target_latest_received_nonce))
} }
@@ -722,7 +672,7 @@ pub(crate) mod tests {
_generated_at_header: SourceHeaderIdOf<TestMessageLane>, _generated_at_header: SourceHeaderIdOf<TestMessageLane>,
nonces: RangeInclusive<MessageNonce>, nonces: RangeInclusive<MessageNonce>,
proof: TestMessagesProof, proof: TestMessagesProof,
) -> Result<RangeInclusive<MessageNonce>, Self::Error> { ) -> Result<RangeInclusive<MessageNonce>, TestError> {
let mut data = self.data.lock(); let mut data = self.data.lock();
(self.tick)(&mut *data); (self.tick)(&mut *data);
if data.is_target_fails { if data.is_target_fails {
@@ -89,12 +89,9 @@ impl<C: Chain> Client<C> {
} }
/// Reopen client connection. /// Reopen client connection.
pub async fn reconnect(self) -> Result<Self> { pub async fn reconnect(&mut self) -> Result<()> {
Ok(Self { self.client = Self::build_client(self.params.clone()).await?;
params: self.params.clone(), Ok(())
client: Self::build_client(self.params).await?,
genesis_hash: self.genesis_hash,
})
} }
/// Build client to use in connection. /// Build client to use in connection.
@@ -25,6 +25,7 @@ use headers_relay::{
sync_loop::SourceClient, sync_loop::SourceClient,
sync_types::{HeaderIdOf, HeadersSyncPipeline, QueuedHeader, SourceHeader}, sync_types::{HeaderIdOf, HeadersSyncPipeline, QueuedHeader, SourceHeader},
}; };
use relay_utils::relay_loop::Client as RelayClient;
use sp_runtime::{traits::Header as HeaderT, Justification}; use sp_runtime::{traits::Header as HeaderT, Justification};
use std::marker::PhantomData; use std::marker::PhantomData;
@@ -44,6 +45,24 @@ impl<C: Chain, P> HeadersSource<C, P> {
} }
} }
impl<C: Chain, P> Clone for HeadersSource<C, P> {
fn clone(&self) -> Self {
HeadersSource {
client: self.client.clone(),
_phantom: Default::default(),
}
}
}
#[async_trait]
impl<C: Chain, P: HeadersSyncPipeline> RelayClient for HeadersSource<C, P> {
type Error = Error;
async fn reconnect(&mut self) -> Result<(), Error> {
self.client.reconnect().await
}
}
#[async_trait] #[async_trait]
impl<C, P> SourceClient<P> for HeadersSource<C, P> impl<C, P> SourceClient<P> for HeadersSource<C, P>
where where
@@ -53,13 +72,11 @@ where
P: HeadersSyncPipeline<Extra = (), Completion = Justification, Hash = C::Hash, Number = C::BlockNumber>, P: HeadersSyncPipeline<Extra = (), Completion = Justification, Hash = C::Hash, Number = C::BlockNumber>,
P::Header: SourceHeader<C::Hash, C::BlockNumber>, P::Header: SourceHeader<C::Hash, C::BlockNumber>,
{ {
type Error = Error; async fn best_block_number(&self) -> Result<P::Number, Error> {
async fn best_block_number(&self) -> Result<P::Number, Self::Error> {
Ok(*self.client.best_header().await?.number()) Ok(*self.client.best_header().await?.number())
} }
async fn header_by_hash(&self, hash: P::Hash) -> Result<P::Header, Self::Error> { async fn header_by_hash(&self, hash: P::Hash) -> Result<P::Header, Error> {
self.client self.client
.header_by_hash(hash) .header_by_hash(hash)
.await .await
@@ -67,7 +84,7 @@ where
.map_err(Into::into) .map_err(Into::into)
} }
async fn header_by_number(&self, number: P::Number) -> Result<P::Header, Self::Error> { async fn header_by_number(&self, number: P::Number) -> Result<P::Header, Error> {
self.client self.client
.header_by_number(number) .header_by_number(number)
.await .await
@@ -75,10 +92,7 @@ where
.map_err(Into::into) .map_err(Into::into)
} }
async fn header_completion( async fn header_completion(&self, id: HeaderIdOf<P>) -> Result<(HeaderIdOf<P>, Option<P::Completion>), Error> {
&self,
id: HeaderIdOf<P>,
) -> Result<(HeaderIdOf<P>, Option<P::Completion>), Self::Error> {
let hash = id.1; let hash = id.1;
let signed_block = self.client.get_block(Some(hash)).await?; let signed_block = self.client.get_block(Some(hash)).await?;
let grandpa_justification = signed_block.justification().cloned(); let grandpa_justification = signed_block.justification().cloned();
@@ -86,11 +100,7 @@ where
Ok((id, grandpa_justification)) Ok((id, grandpa_justification))
} }
async fn header_extra( async fn header_extra(&self, id: HeaderIdOf<P>, _header: QueuedHeader<P>) -> Result<(HeaderIdOf<P>, ()), Error> {
&self,
id: HeaderIdOf<P>,
_header: QueuedHeader<P>,
) -> Result<(HeaderIdOf<P>, ()), Self::Error> {
Ok((id, ())) Ok((id, ()))
} }
} }
@@ -80,6 +80,20 @@ impl<P: SubstrateHeadersSyncPipeline, SourceChain, TargetChain: Chain>
} }
} }
#[async_trait]
impl<P: SubstrateHeadersSyncPipeline, SourceChain, TargetChain: Chain> Clone
for SubstrateHeadersToSubstrateMaintain<P, SourceChain, TargetChain>
{
fn clone(&self) -> Self {
SubstrateHeadersToSubstrateMaintain {
pipeline: self.pipeline.clone(),
target_client: self.target_client.clone(),
justifications: self.justifications.clone(),
_marker: Default::default(),
}
}
}
#[async_trait] #[async_trait]
impl<P, SourceChain, TargetChain> SyncMaintain<P> for SubstrateHeadersToSubstrateMaintain<P, SourceChain, TargetChain> impl<P, SourceChain, TargetChain> SyncMaintain<P> for SubstrateHeadersToSubstrateMaintain<P, SourceChain, TargetChain>
where where
+28 -9
View File
@@ -28,7 +28,7 @@ use headers_relay::{
sync_types::{HeaderIdOf, QueuedHeader, SubmittedHeaders}, sync_types::{HeaderIdOf, QueuedHeader, SubmittedHeaders},
}; };
use relay_substrate_client::{Chain, Client, Error as SubstrateError}; use relay_substrate_client::{Chain, Client, Error as SubstrateError};
use relay_utils::HeaderId; use relay_utils::{relay_loop::Client as RelayClient, HeaderId};
use sp_core::Bytes; use sp_core::Bytes;
use sp_runtime::Justification; use sp_runtime::Justification;
use std::collections::HashSet; use std::collections::HashSet;
@@ -46,6 +46,24 @@ impl<C: Chain, P> SubstrateHeadersTarget<C, P> {
} }
} }
impl<C: Chain, P: SubstrateHeadersSyncPipeline> Clone for SubstrateHeadersTarget<C, P> {
fn clone(&self) -> Self {
SubstrateHeadersTarget {
client: self.client.clone(),
pipeline: self.pipeline.clone(),
}
}
}
#[async_trait]
impl<C: Chain, P: SubstrateHeadersSyncPipeline> RelayClient for SubstrateHeadersTarget<C, P> {
type Error = SubstrateError;
async fn reconnect(&mut self) -> Result<(), SubstrateError> {
self.client.reconnect().await
}
}
#[async_trait] #[async_trait]
impl<C, P> TargetClient<P> for SubstrateHeadersTarget<C, P> impl<C, P> TargetClient<P> for SubstrateHeadersTarget<C, P>
where where
@@ -54,9 +72,7 @@ where
P::Hash: Decode + Encode, P::Hash: Decode + Encode,
P: SubstrateHeadersSyncPipeline<Completion = Justification, Extra = ()>, P: SubstrateHeadersSyncPipeline<Completion = Justification, Extra = ()>,
{ {
type Error = SubstrateError; async fn best_header_id(&self) -> Result<HeaderIdOf<P>, SubstrateError> {
async fn best_header_id(&self) -> Result<HeaderIdOf<P>, Self::Error> {
let call = P::BEST_BLOCK_METHOD.into(); let call = P::BEST_BLOCK_METHOD.into();
let data = Bytes(Vec::new()); let data = Bytes(Vec::new());
@@ -72,7 +88,7 @@ where
.map(|(num, hash)| HeaderId(*num, *hash)) .map(|(num, hash)| HeaderId(*num, *hash))
} }
async fn is_known_header(&self, id: HeaderIdOf<P>) -> Result<(HeaderIdOf<P>, bool), Self::Error> { async fn is_known_header(&self, id: HeaderIdOf<P>) -> Result<(HeaderIdOf<P>, bool), SubstrateError> {
let call = P::IS_KNOWN_BLOCK_METHOD.into(); let call = P::IS_KNOWN_BLOCK_METHOD.into();
let data = Bytes(id.1.encode()); let data = Bytes(id.1.encode());
@@ -83,7 +99,10 @@ where
Ok((id, is_known_block)) Ok((id, is_known_block))
} }
async fn submit_headers(&self, mut headers: Vec<QueuedHeader<P>>) -> SubmittedHeaders<HeaderIdOf<P>, Self::Error> { async fn submit_headers(
&self,
mut headers: Vec<QueuedHeader<P>>,
) -> SubmittedHeaders<HeaderIdOf<P>, SubstrateError> {
debug_assert_eq!( debug_assert_eq!(
headers.len(), headers.len(),
1, 1,
@@ -114,7 +133,7 @@ where
} }
} }
async fn incomplete_headers_ids(&self) -> Result<HashSet<HeaderIdOf<P>>, Self::Error> { async fn incomplete_headers_ids(&self) -> Result<HashSet<HeaderIdOf<P>>, SubstrateError> {
let call = P::INCOMPLETE_HEADERS_METHOD.into(); let call = P::INCOMPLETE_HEADERS_METHOD.into();
let data = Bytes(Vec::new()); let data = Bytes(Vec::new());
@@ -133,13 +152,13 @@ where
&self, &self,
id: HeaderIdOf<P>, id: HeaderIdOf<P>,
completion: Justification, completion: Justification,
) -> Result<HeaderIdOf<P>, Self::Error> { ) -> Result<HeaderIdOf<P>, SubstrateError> {
let tx = self.pipeline.make_complete_header_transaction(id, completion).await?; let tx = self.pipeline.make_complete_header_transaction(id, completion).await?;
self.client.submit_extrinsic(Bytes(tx.encode())).await?; self.client.submit_extrinsic(Bytes(tx.encode())).await?;
Ok(id) Ok(id)
} }
async fn requires_extra(&self, header: QueuedHeader<P>) -> Result<(HeaderIdOf<P>, bool), Self::Error> { async fn requires_extra(&self, header: QueuedHeader<P>) -> Result<(HeaderIdOf<P>, bool), SubstrateError> {
Ok((header.id(), false)) Ok((header.id(), false))
} }
} }
+16 -15
View File
@@ -32,7 +32,7 @@ use messages_relay::{
}, },
}; };
use relay_substrate_client::{Chain, Client, Error as SubstrateError, HashOf, HeaderIdOf}; use relay_substrate_client::{Chain, Client, Error as SubstrateError, HashOf, HeaderIdOf};
use relay_utils::{BlockNumberBase, HeaderId}; use relay_utils::{relay_loop::Client as RelayClient, BlockNumberBase, HeaderId};
use sp_core::Bytes; use sp_core::Bytes;
use sp_runtime::{traits::Header as HeaderT, DeserializeOwned}; use sp_runtime::{traits::Header as HeaderT, DeserializeOwned};
use sp_trie::StorageProof; use sp_trie::StorageProof;
@@ -74,6 +74,15 @@ impl<C: Chain, P: SubstrateMessageLane> Clone for SubstrateMessagesSource<C, P>
} }
} }
#[async_trait]
impl<C: Chain, P: SubstrateMessageLane> RelayClient for SubstrateMessagesSource<C, P> {
type Error = SubstrateError;
async fn reconnect(&mut self) -> Result<(), SubstrateError> {
self.client.reconnect().await
}
}
#[async_trait] #[async_trait]
impl<C, P> SourceClient<P> for SubstrateMessagesSource<C, P> impl<C, P> SourceClient<P> for SubstrateMessagesSource<C, P>
where where
@@ -89,15 +98,7 @@ where
P::TargetHeaderNumber: Decode, P::TargetHeaderNumber: Decode,
P::TargetHeaderHash: Decode, P::TargetHeaderHash: Decode,
{ {
type Error = SubstrateError; async fn state(&self) -> Result<SourceClientState<P>, SubstrateError> {
async fn reconnect(mut self) -> Result<Self, Self::Error> {
let new_client = self.client.clone().reconnect().await?;
self.client = new_client;
Ok(self)
}
async fn state(&self) -> Result<SourceClientState<P>, Self::Error> {
read_client_state::<_, P::TargetHeaderHash, P::TargetHeaderNumber>( read_client_state::<_, P::TargetHeaderHash, P::TargetHeaderNumber>(
&self.client, &self.client,
P::BEST_FINALIZED_TARGET_HEADER_ID_AT_SOURCE, P::BEST_FINALIZED_TARGET_HEADER_ID_AT_SOURCE,
@@ -108,7 +109,7 @@ where
async fn latest_generated_nonce( async fn latest_generated_nonce(
&self, &self,
id: SourceHeaderIdOf<P>, id: SourceHeaderIdOf<P>,
) -> Result<(SourceHeaderIdOf<P>, MessageNonce), Self::Error> { ) -> Result<(SourceHeaderIdOf<P>, MessageNonce), SubstrateError> {
let encoded_response = self let encoded_response = self
.client .client
.state_call( .state_call(
@@ -125,7 +126,7 @@ where
async fn latest_confirmed_received_nonce( async fn latest_confirmed_received_nonce(
&self, &self,
id: SourceHeaderIdOf<P>, id: SourceHeaderIdOf<P>,
) -> Result<(SourceHeaderIdOf<P>, MessageNonce), Self::Error> { ) -> Result<(SourceHeaderIdOf<P>, MessageNonce), SubstrateError> {
let encoded_response = self let encoded_response = self
.client .client
.state_call( .state_call(
@@ -143,7 +144,7 @@ where
&self, &self,
id: SourceHeaderIdOf<P>, id: SourceHeaderIdOf<P>,
nonces: RangeInclusive<MessageNonce>, nonces: RangeInclusive<MessageNonce>,
) -> Result<MessageWeightsMap, Self::Error> { ) -> Result<MessageWeightsMap, SubstrateError> {
let encoded_response = self let encoded_response = self
.client .client
.state_call( .state_call(
@@ -164,7 +165,7 @@ where
id: SourceHeaderIdOf<P>, id: SourceHeaderIdOf<P>,
nonces: RangeInclusive<MessageNonce>, nonces: RangeInclusive<MessageNonce>,
proof_parameters: MessageProofParameters, proof_parameters: MessageProofParameters,
) -> Result<(SourceHeaderIdOf<P>, RangeInclusive<MessageNonce>, P::MessagesProof), Self::Error> { ) -> Result<(SourceHeaderIdOf<P>, RangeInclusive<MessageNonce>, P::MessagesProof), SubstrateError> {
let proof = self let proof = self
.client .client
.prove_messages( .prove_messages(
@@ -183,7 +184,7 @@ where
&self, &self,
generated_at_block: TargetHeaderIdOf<P>, generated_at_block: TargetHeaderIdOf<P>,
proof: P::MessagesReceivingProof, proof: P::MessagesReceivingProof,
) -> Result<(), Self::Error> { ) -> Result<(), SubstrateError> {
let tx = self let tx = self
.lane .lane
.make_messages_receiving_proof_transaction(generated_at_block, proof) .make_messages_receiving_proof_transaction(generated_at_block, proof)
+16 -15
View File
@@ -30,7 +30,7 @@ use messages_relay::{
message_lane_loop::{TargetClient, TargetClientState}, message_lane_loop::{TargetClient, TargetClientState},
}; };
use relay_substrate_client::{Chain, Client, Error as SubstrateError, HashOf}; use relay_substrate_client::{Chain, Client, Error as SubstrateError, HashOf};
use relay_utils::BlockNumberBase; use relay_utils::{relay_loop::Client as RelayClient, BlockNumberBase};
use sp_core::Bytes; use sp_core::Bytes;
use sp_runtime::{traits::Header as HeaderT, DeserializeOwned}; use sp_runtime::{traits::Header as HeaderT, DeserializeOwned};
use sp_trie::StorageProof; use sp_trie::StorageProof;
@@ -70,6 +70,15 @@ impl<C: Chain, P: SubstrateMessageLane> Clone for SubstrateMessagesTarget<C, P>
} }
} }
#[async_trait]
impl<C: Chain, P: SubstrateMessageLane> RelayClient for SubstrateMessagesTarget<C, P> {
type Error = SubstrateError;
async fn reconnect(&mut self) -> Result<(), SubstrateError> {
self.client.reconnect().await
}
}
#[async_trait] #[async_trait]
impl<C, P> TargetClient<P> for SubstrateMessagesTarget<C, P> impl<C, P> TargetClient<P> for SubstrateMessagesTarget<C, P>
where where
@@ -85,15 +94,7 @@ where
P::SourceHeaderNumber: Decode, P::SourceHeaderNumber: Decode,
P::SourceHeaderHash: Decode, P::SourceHeaderHash: Decode,
{ {
type Error = SubstrateError; async fn state(&self) -> Result<TargetClientState<P>, SubstrateError> {
async fn reconnect(mut self) -> Result<Self, Self::Error> {
let new_client = self.client.clone().reconnect().await?;
self.client = new_client;
Ok(self)
}
async fn state(&self) -> Result<TargetClientState<P>, Self::Error> {
read_client_state::<_, P::SourceHeaderHash, P::SourceHeaderNumber>( read_client_state::<_, P::SourceHeaderHash, P::SourceHeaderNumber>(
&self.client, &self.client,
P::BEST_FINALIZED_SOURCE_HEADER_ID_AT_TARGET, P::BEST_FINALIZED_SOURCE_HEADER_ID_AT_TARGET,
@@ -104,7 +105,7 @@ where
async fn latest_received_nonce( async fn latest_received_nonce(
&self, &self,
id: TargetHeaderIdOf<P>, id: TargetHeaderIdOf<P>,
) -> Result<(TargetHeaderIdOf<P>, MessageNonce), Self::Error> { ) -> Result<(TargetHeaderIdOf<P>, MessageNonce), SubstrateError> {
let encoded_response = self let encoded_response = self
.client .client
.state_call( .state_call(
@@ -121,7 +122,7 @@ where
async fn latest_confirmed_received_nonce( async fn latest_confirmed_received_nonce(
&self, &self,
id: TargetHeaderIdOf<P>, id: TargetHeaderIdOf<P>,
) -> Result<(TargetHeaderIdOf<P>, MessageNonce), Self::Error> { ) -> Result<(TargetHeaderIdOf<P>, MessageNonce), SubstrateError> {
let encoded_response = self let encoded_response = self
.client .client
.state_call( .state_call(
@@ -138,7 +139,7 @@ where
async fn unrewarded_relayers_state( async fn unrewarded_relayers_state(
&self, &self,
id: TargetHeaderIdOf<P>, id: TargetHeaderIdOf<P>,
) -> Result<(TargetHeaderIdOf<P>, UnrewardedRelayersState), Self::Error> { ) -> Result<(TargetHeaderIdOf<P>, UnrewardedRelayersState), SubstrateError> {
let encoded_response = self let encoded_response = self
.client .client
.state_call( .state_call(
@@ -155,7 +156,7 @@ where
async fn prove_messages_receiving( async fn prove_messages_receiving(
&self, &self,
id: TargetHeaderIdOf<P>, id: TargetHeaderIdOf<P>,
) -> Result<(TargetHeaderIdOf<P>, P::MessagesReceivingProof), Self::Error> { ) -> Result<(TargetHeaderIdOf<P>, P::MessagesReceivingProof), SubstrateError> {
let (id, relayers_state) = self.unrewarded_relayers_state(id).await?; let (id, relayers_state) = self.unrewarded_relayers_state(id).await?;
let proof = self let proof = self
.client .client
@@ -170,7 +171,7 @@ where
generated_at_header: SourceHeaderIdOf<P>, generated_at_header: SourceHeaderIdOf<P>,
nonces: RangeInclusive<MessageNonce>, nonces: RangeInclusive<MessageNonce>,
proof: P::MessagesProof, proof: P::MessagesProof,
) -> Result<RangeInclusive<MessageNonce>, Self::Error> { ) -> Result<RangeInclusive<MessageNonce>, SubstrateError> {
let tx = self let tx = self
.lane .lane
.make_messages_delivery_transaction(generated_at_header, nonces.clone(), proof) .make_messages_delivery_transaction(generated_at_header, nonces.clone(), proof)
@@ -105,7 +105,6 @@ pub fn run(
lane_id: LaneId, lane_id: LaneId,
metrics_params: Option<MetricsParams>, metrics_params: Option<MetricsParams>,
) { ) {
let reconnect_delay = Duration::from_secs(10);
let stall_timeout = Duration::from_secs(5 * 60); let stall_timeout = Duration::from_secs(5 * 60);
let relayer_id_at_millau = millau_sign.signer.public().as_array_ref().clone().into(); let relayer_id_at_millau = millau_sign.signer.public().as_array_ref().clone().into();
@@ -135,7 +134,7 @@ pub fn run(
lane: lane_id, lane: lane_id,
source_tick: Millau::AVERAGE_BLOCK_INTERVAL, source_tick: Millau::AVERAGE_BLOCK_INTERVAL,
target_tick: Rialto::AVERAGE_BLOCK_INTERVAL, target_tick: Rialto::AVERAGE_BLOCK_INTERVAL,
reconnect_delay, reconnect_delay: relay_utils::relay_loop::RECONNECT_DELAY,
stall_timeout, stall_timeout,
delivery_params: messages_relay::message_lane_loop::MessageDeliveryParams { delivery_params: messages_relay::message_lane_loop::MessageDeliveryParams {
max_unrewarded_relayer_entries_at_target: bp_rialto::MAX_UNREWARDED_RELAYER_ENTRIES_AT_INBOUND_LANE, max_unrewarded_relayer_entries_at_target: bp_rialto::MAX_UNREWARDED_RELAYER_ENTRIES_AT_INBOUND_LANE,
@@ -105,7 +105,6 @@ pub fn run(
lane_id: LaneId, lane_id: LaneId,
metrics_params: Option<MetricsParams>, metrics_params: Option<MetricsParams>,
) { ) {
let reconnect_delay = Duration::from_secs(10);
let stall_timeout = Duration::from_secs(5 * 60); let stall_timeout = Duration::from_secs(5 * 60);
let relayer_id_at_rialto = rialto_sign.signer.public().as_array_ref().clone().into(); let relayer_id_at_rialto = rialto_sign.signer.public().as_array_ref().clone().into();
@@ -134,7 +133,7 @@ pub fn run(
lane: lane_id, lane: lane_id,
source_tick: Rialto::AVERAGE_BLOCK_INTERVAL, source_tick: Rialto::AVERAGE_BLOCK_INTERVAL,
target_tick: Millau::AVERAGE_BLOCK_INTERVAL, target_tick: Millau::AVERAGE_BLOCK_INTERVAL,
reconnect_delay, reconnect_delay: relay_utils::relay_loop::RECONNECT_DELAY,
stall_timeout, stall_timeout,
delivery_params: messages_relay::message_lane_loop::MessageDeliveryParams { delivery_params: messages_relay::message_lane_loop::MessageDeliveryParams {
max_unrewarded_relayer_entries_at_target: bp_millau::MAX_UNREWARDED_RELAYER_ENTRIES_AT_INBOUND_LANE, max_unrewarded_relayer_entries_at_target: bp_millau::MAX_UNREWARDED_RELAYER_ENTRIES_AT_INBOUND_LANE,
+1
View File
@@ -8,6 +8,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0"
[dependencies] [dependencies]
ansi_term = "0.12" ansi_term = "0.12"
async-std = "1.6.5" async-std = "1.6.5"
async-trait = "0.1.40"
backoff = "0.2" backoff = "0.2"
env_logger = "0.8.2" env_logger = "0.8.2"
futures = "0.3.5" futures = "0.3.5"
+1
View File
@@ -29,6 +29,7 @@ pub const CONNECTION_ERROR_DELAY: Duration = Duration::from_secs(10);
pub mod initialize; pub mod initialize;
pub mod metrics; pub mod metrics;
pub mod relay_loop;
/// Block number traits shared by all chains that relay is able to serve. /// Block number traits shared by all chains that relay is able to serve.
pub trait BlockNumberBase: pub trait BlockNumberBase:
+9 -7
View File
@@ -16,6 +16,7 @@
pub use substrate_prometheus_endpoint::{register, Counter, CounterVec, Gauge, GaugeVec, Opts, Registry, F64, U64}; pub use substrate_prometheus_endpoint::{register, Counter, CounterVec, Gauge, GaugeVec, Opts, Registry, F64, U64};
use async_std::sync::{Arc, Mutex};
use std::net::SocketAddr; use std::net::SocketAddr;
use substrate_prometheus_endpoint::init_prometheus; use substrate_prometheus_endpoint::init_prometheus;
use sysinfo::{ProcessExt, RefreshKind, System, SystemExt}; use sysinfo::{ProcessExt, RefreshKind, System, SystemExt};
@@ -36,9 +37,9 @@ pub trait Metrics {
} }
/// Global Prometheus metrics. /// Global Prometheus metrics.
#[derive(Debug)] #[derive(Debug, Clone)]
pub struct GlobalMetrics { pub struct GlobalMetrics {
system: System, system: Arc<Mutex<System>>,
system_average_load: GaugeVec<F64>, system_average_load: GaugeVec<F64>,
process_cpu_usage_percentage: Gauge<F64>, process_cpu_usage_percentage: Gauge<F64>,
process_memory_usage_bytes: Gauge<U64>, process_memory_usage_bytes: Gauge<U64>,
@@ -110,7 +111,7 @@ impl Metrics for GlobalMetrics {
impl Default for GlobalMetrics { impl Default for GlobalMetrics {
fn default() -> Self { fn default() -> Self {
GlobalMetrics { GlobalMetrics {
system: System::new_with_specifics(RefreshKind::everything()), system: Arc::new(Mutex::new(System::new_with_specifics(RefreshKind::everything()))),
system_average_load: GaugeVec::new(Opts::new("system_average_load", "System load average"), &["over"]) system_average_load: GaugeVec::new(Opts::new("system_average_load", "System load average"), &["over"])
.expect("metric is static and thus valid; qed"), .expect("metric is static and thus valid; qed"),
process_cpu_usage_percentage: Gauge::new("process_cpu_usage_percentage", "Process CPU usage") process_cpu_usage_percentage: Gauge::new("process_cpu_usage_percentage", "Process CPU usage")
@@ -126,9 +127,10 @@ impl Default for GlobalMetrics {
impl GlobalMetrics { impl GlobalMetrics {
/// Update metrics. /// Update metrics.
pub fn update(&mut self) { pub async fn update(&self) {
// update system-wide metrics // update system-wide metrics
let load = self.system.get_load_average(); let mut system = self.system.lock().await;
let load = system.get_load_average();
self.system_average_load.with_label_values(&["1min"]).set(load.one); self.system_average_load.with_label_values(&["1min"]).set(load.one);
self.system_average_load.with_label_values(&["5min"]).set(load.five); self.system_average_load.with_label_values(&["5min"]).set(load.five);
self.system_average_load.with_label_values(&["15min"]).set(load.fifteen); self.system_average_load.with_label_values(&["15min"]).set(load.fifteen);
@@ -139,8 +141,8 @@ impl GlobalMetrics {
relay is not supposed to run in such MetricsParamss;\ relay is not supposed to run in such MetricsParamss;\
qed", qed",
); );
let is_process_refreshed = self.system.refresh_process(pid); let is_process_refreshed = system.refresh_process(pid);
match (is_process_refreshed, self.system.get_process(pid)) { match (is_process_refreshed, system.get_process(pid)) {
(true, Some(process_info)) => { (true, Some(process_info)) => {
let cpu_usage = process_info.cpu_usage() as f64; let cpu_usage = process_info.cpu_usage() as f64;
let memory_usage = process_info.memory() * 1024; let memory_usage = process_info.memory() * 1024;
+95
View File
@@ -0,0 +1,95 @@
// Copyright 2019-2020 Parity Technologies (UK) Ltd.
// This file is part of Parity Bridges Common.
// Parity Bridges Common is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity Bridges Common is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
use crate::{FailedClient, MaybeConnectionError};
use async_trait::async_trait;
use std::{fmt::Debug, future::Future, time::Duration};
/// Default pause between reconnect attempts.
pub const RECONNECT_DELAY: Duration = Duration::from_secs(10);
/// Basic blockchain client from relay perspective.
#[async_trait]
pub trait Client: Clone + Send + Sync {
/// Type of error this clients returns.
type Error: Debug + MaybeConnectionError;
/// Try to reconnect to source node.
async fn reconnect(&mut self) -> Result<(), Self::Error>;
}
/// Run relay loop.
///
/// This function represents an outer loop, which in turn calls provided `loop_run` function to do
/// actual job. When `loop_run` returns, this outer loop reconnects to failed client (source,
/// target or both) and calls `loop_run` again.
pub fn run<SC: Client, TC: Client, R, F>(
reconnect_delay: Duration,
mut source_client: SC,
mut target_client: TC,
loop_run: R,
) where
R: Fn(SC, TC) -> F,
F: Future<Output = Result<(), FailedClient>>,
{
let mut local_pool = futures::executor::LocalPool::new();
local_pool.run_until(async move {
loop {
let result = loop_run(source_client.clone(), target_client.clone()).await;
match result {
Ok(()) => break,
Err(failed_client) => loop {
async_std::task::sleep(reconnect_delay).await;
if failed_client == FailedClient::Both || failed_client == FailedClient::Source {
match source_client.reconnect().await {
Ok(()) => (),
Err(error) => {
log::warn!(
target: "bridge",
"Failed to reconnect to source client. Going to retry in {}s: {:?}",
reconnect_delay.as_secs(),
error,
);
continue;
}
}
}
if failed_client == FailedClient::Both || failed_client == FailedClient::Target {
match target_client.reconnect().await {
Ok(()) => (),
Err(error) => {
log::warn!(
target: "bridge",
"Failed to reconnect to target client. Going to retry in {}s: {:?}",
reconnect_delay.as_secs(),
error,
);
continue;
}
}
}
break;
},
}
log::debug!(target: "bridge", "Restarting relay loop");
}
});
}