Skip to content

Commit d0c9fce

Browse files
authored
refactor(relayer): Handle errors another way in gear-eth (#802)
1 parent c13b6a3 commit d0c9fce

File tree

3 files changed

+116
-85
lines changed

3 files changed

+116
-85
lines changed

ethereum/README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,8 +102,9 @@ $ forge script script/upgrades/WrappedVara.s.sol:WrappedVaraScript --rpc-url $HO
102102
4. generate an update message with the help of `governance-tool` (see `tools/governance/README.md`):
103103

104104
```bash
105-
NEW_IMPLEMENTATION="0x0000000000000000000000000000000000000000" # must exist on https://etherscan.io
106-
cargo run --package governance-tool --release -- --rpc-url $MAINNET_RPC_URL GovernanceAdmin UpgradeProxy MessageQueue $NEW_IMPLEMENTATION $(cast calldata "function reinitialize()")
105+
# must exist on https://etherscan.io
106+
NEW_IMPLEMENTATION="0x0000000000000000000000000000000000000000"
107+
cargo run --package governance-tool --release -- --ethereum-endpoint $MAINNET_RPC_URL GovernanceAdmin UpgradeProxy MessageQueue $NEW_IMPLEMENTATION $(cast calldata "function reinitialize()")
107108
```
108109

109110
5. send the extrinsic `gearEthBridge::sendEthMessage` in behalf of `governance admin`

relayer/src/message_relayer/common/ethereum/merkle_root_extractor.rs

Lines changed: 68 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
1-
use crate::{
2-
common::{self, BASE_RETRY_DELAY, MAX_RETRIES},
3-
message_relayer::{
4-
common::{AuthoritySetId, GearBlockNumber, RelayedMerkleRoot},
5-
eth_to_gear::api_provider::ApiProviderConnection,
6-
},
1+
use crate::message_relayer::{
2+
common::{AuthoritySetId, GearBlockNumber, RelayedMerkleRoot},
3+
eth_to_gear::api_provider::ApiProviderConnection,
74
};
85
use alloy::{
96
providers::{PendingTransactionBuilder, Provider},
107
sol_types::SolEvent,
118
};
129
use ethereum_client::{abi::IMessageQueue::MerkleRoot, EthApi};
1310
use futures::StreamExt;
11+
use gear_rpc_client::GearApi;
12+
use primitive_types::H256;
1413
use prometheus::IntGauge;
1514
use tokio::sync::mpsc::UnboundedSender;
1615
use utils_prometheus::{impl_metered_service, MeteredService};
@@ -63,61 +62,73 @@ impl MerkleRootExtractor {
6362
pub fn spawn(self) {
6463
tokio::task::spawn(task(self));
6564
}
66-
}
6765

68-
async fn task(mut this: MerkleRootExtractor) {
69-
let mut attempts = 0;
66+
async fn fetch_hash_auth_id(
67+
&mut self,
68+
block_number_gear: u32,
69+
) -> Option<(H256, AuthoritySetId)> {
70+
let gear_api = self.api_provider.client();
71+
loop {
72+
match self::fetch_hash_auth_id(&gear_api, block_number_gear).await {
73+
Ok(result) => return Some(result),
74+
75+
Err(e) => {
76+
log::error!(r#"Merkle root extractor failed to fetch block_hash: "{e:?}""#);
77+
log::trace!(
78+
r#"e.downcast_ref::<gsdk::Error>(): "{:?}""#,
79+
e.downcast_ref::<gsdk::Error>()
80+
);
81+
log::trace!(
82+
r#"e.downcast_ref::<subxt::Error>(): "{:?}""#,
83+
e.downcast_ref::<subxt::Error>()
84+
);
85+
for cause in e.chain() {
86+
log::trace!(r#"cause: "{cause:?}""#);
87+
}
88+
}
89+
}
7090

71-
loop {
72-
let res = task_inner(&this).await;
73-
if let Err(err) = res {
74-
attempts += 1;
75-
log::error!(
76-
"Merkle root extractor failed (attempt {}/{}): {}. Retrying in {:?}...",
77-
attempts,
78-
MAX_RETRIES,
79-
err,
80-
BASE_RETRY_DELAY * 2u32.pow(attempts - 1),
81-
);
82-
if attempts >= MAX_RETRIES {
83-
log::error!("Merkle root extractor failed {attempts} times: {err}");
84-
break;
91+
if let Err(e) = self.api_provider.reconnect().await {
92+
log::error!(r#"Merkle root extractor unable to reconnect: "{e}""#);
93+
return None;
8594
}
8695

87-
tokio::time::sleep(BASE_RETRY_DELAY * 2u32.pow(attempts - 1)).await;
96+
log::debug!("API provider reconnected");
97+
}
98+
}
99+
}
88100

89-
match this.api_provider.reconnect().await {
90-
Ok(()) => {
91-
log::info!("API provider reconnected");
92-
}
101+
async fn fetch_hash_auth_id(
102+
gear_api: &GearApi,
103+
block_number_gear: u32,
104+
) -> anyhow::Result<(H256, AuthoritySetId)> {
105+
let block_hash = gear_api.block_number_to_hash(block_number_gear).await?;
93106

94-
Err(err) => {
95-
log::error!("Merkle root extractor unable to reconnect: {err}");
96-
return;
97-
}
98-
}
107+
let authority_set_id = AuthoritySetId(gear_api.signed_by_authority_set_id(block_hash).await?);
99108

100-
if common::is_transport_error_recoverable(&err) {
101-
this.eth_api = match this.eth_api.reconnect().await {
102-
Ok(eth_api) => eth_api,
103-
Err(err) => {
104-
log::error!("Failed to reconnect to Ethereum: {err}");
105-
break;
106-
}
107-
};
108-
} else {
109-
log::error!("Merkle root extractor failed: {err}");
110-
break;
111-
}
112-
} else {
109+
Ok((block_hash, authority_set_id))
110+
}
111+
112+
async fn task(mut this: MerkleRootExtractor) {
113+
loop {
114+
let Err(err) = task_inner(&mut this).await else {
113115
log::info!("Exiting");
114116
break;
115-
}
117+
};
118+
119+
log::error!(r#"Merkle root extractor failed: "{err:?}""#);
120+
121+
this.eth_api = match this.eth_api.reconnect().await {
122+
Ok(eth_api) => eth_api,
123+
Err(err) => {
124+
log::error!(r#"Failed to reconnect to Ethereum: "{err}""#);
125+
break;
126+
}
127+
};
116128
}
117129
}
118130

119-
async fn task_inner(this: &MerkleRootExtractor) -> anyhow::Result<()> {
120-
let gear_api = this.api_provider.client();
131+
async fn task_inner(this: &mut MerkleRootExtractor) -> anyhow::Result<()> {
121132
let subscription = this.eth_api.subscribe_logs().await?;
122133

123134
let mut stream = subscription.into_result_stream();
@@ -185,24 +196,25 @@ async fn task_inner(this: &MerkleRootExtractor) -> anyhow::Result<()> {
185196
.latest_merkle_root_for_block
186197
.set(block_number_gear as i64);
187198

188-
let block_hash = gear_api.block_number_to_hash(block_number_gear).await?;
189-
190-
let authority_set_id =
191-
AuthoritySetId(gear_api.signed_by_authority_set_id(block_hash).await?);
199+
let Some((block_hash, authority_set_id)) = this.fetch_hash_auth_id(block_number_gear).await else {
200+
return Ok(());
201+
};
192202

193203
log::info!(
194204
"Merkle root {:?} is for era #{authority_set_id}",
195205
(root.blockNumber, root.merkleRoot),
196206
);
197207

198-
this.sender.send(RelayedMerkleRoot {
208+
if let Err(e) = this.sender.send(RelayedMerkleRoot {
199209
block: GearBlockNumber(block_number_gear),
200210
block_hash,
201211
authority_set_id,
202212
merkle_root: root.merkleRoot.0.into(),
203213
timestamp: block_timestamp,
204-
})?;
205-
214+
}) {
215+
log::error!(r#"Sender channel closed: "{e:?}"."#);
216+
return Ok(());
217+
}
206218
}
207219
}
208220
}

relayer/src/message_relayer/gear_to_eth/paid_token_transfers.rs

Lines changed: 45 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::{
2-
common::MAX_RETRIES,
2+
common::{BlockRange, MAX_RETRIES},
33
message_relayer::{
44
common::{
55
ethereum::{
@@ -203,44 +203,62 @@ async fn fetch_merkle_roots_inner(
203203
sender: UnboundedSender<RelayedMerkleRoot>,
204204
) -> AnyResult<()> {
205205
const COUNT: u64 = 2_000;
206+
const COUNT_STEP: u64 = 50;
206207

207208
let block_finalized = eth_api.finalized_block_number().await?;
209+
let block_latest = eth_api.latest_block_number().await?;
208210
let gear_api = api_provider.client();
209211

210-
for i in 0..50 {
212+
for i in 0..COUNT_STEP {
211213
let block_range = crate::common::create_range(
212214
(block_finalized - (i + 1) * COUNT).into(),
213215
block_finalized - i * COUNT,
214216
);
215-
let merkle_roots = eth_api
216-
.fetch_merkle_roots_in_range(block_range.from, block_range.to)
217-
.await?;
218-
219-
let len = merkle_roots.len();
220-
log::trace!("Found {len} entry(ies) with merkle roots (i = {i})");
221-
for (root, _block_number_eth) in merkle_roots
222-
.into_iter()
223-
.filter_map(|(root, block)| block.map(|block| (root, block)))
224-
{
225-
let timestamp = eth_api.get_block_timestamp(_block_number_eth).await?;
226-
let block_hash = gear_api
227-
.block_number_to_hash(root.block_number as u32)
228-
.await?;
229-
let authority_set_id = gear_api.signed_by_authority_set_id(block_hash).await?;
230-
231-
sender.send(RelayedMerkleRoot {
232-
block: GearBlockNumber(root.block_number as u32),
233-
block_hash,
234-
authority_set_id: AuthoritySetId(authority_set_id),
235-
merkle_root: root.merkle_root,
236-
timestamp,
237-
})?;
238-
}
239217

240-
log::trace!("Successfuly sent {len} merkle root entry(ies) (i = {i})");
218+
fetch_merkle_roots_in_range(&eth_api, &gear_api, &sender, i, block_range).await?;
241219

242220
time::sleep(time::Duration::from_secs(5)).await;
243221
}
244222

223+
// to that moment block_latest should have the required number of confirmations
224+
let block_range = crate::common::create_range((block_finalized + 1).into(), block_latest);
225+
226+
fetch_merkle_roots_in_range(&eth_api, &gear_api, &sender, COUNT_STEP, block_range).await
227+
}
228+
229+
async fn fetch_merkle_roots_in_range(
230+
eth_api: &EthApi,
231+
gear_api: &gear_rpc_client::GearApi,
232+
sender: &UnboundedSender<RelayedMerkleRoot>,
233+
i: u64,
234+
block_range: BlockRange,
235+
) -> AnyResult<()> {
236+
let merkle_roots = eth_api
237+
.fetch_merkle_roots_in_range(block_range.from, block_range.to)
238+
.await?;
239+
240+
let len = merkle_roots.len();
241+
log::trace!("Found {len} entry(ies) with merkle roots (i = {i})");
242+
for (root, _block_number_eth) in merkle_roots
243+
.into_iter()
244+
.filter_map(|(root, block)| block.map(|block| (root, block)))
245+
{
246+
let timestamp = eth_api.get_block_timestamp(_block_number_eth).await?;
247+
let block_hash = gear_api
248+
.block_number_to_hash(root.block_number as u32)
249+
.await?;
250+
let authority_set_id = gear_api.signed_by_authority_set_id(block_hash).await?;
251+
252+
sender.send(RelayedMerkleRoot {
253+
block: GearBlockNumber(root.block_number as u32),
254+
block_hash,
255+
authority_set_id: AuthoritySetId(authority_set_id),
256+
merkle_root: root.merkle_root,
257+
timestamp,
258+
})?;
259+
}
260+
261+
log::trace!("Successfuly sent {len} merkle root entry(ies) (i = {i})");
262+
245263
Ok(())
246264
}

0 commit comments

Comments
 (0)