Skip to content

Commit adce3a5

Browse files
feat(l2): monitor handle panics (#3607)
**Motivation** Monitor had panics in its code. <!-- Why does this pull request exist? What are its goals? --> **Description** Added new variants for `MonitorError` and used them to remove the panics in the monitor <!-- A clear and concise general description of the changes this PR introduces --> **How to test** - Add `--monitor` to the `init-l2-no-metrics` target in `crates/l2/Makefile`. - Run a Sequencer (I suggest `make restart` in `crates/l2`). - Run the prover with `make init-prover` in `crates/l2`. - Run `make test` in `crates/l2`. <!-- Link to issues: Resolves #111, Resolves #222 --> Closes https://github.com/orgs/lambdaclass/projects/37/views/10?pane=issue&itemId=118823684&issue=lambdaclass%7Cethrex%7C3536 --------- Co-authored-by: Ivan Litteri <[email protected]>
1 parent 09fc7fd commit adce3a5

File tree

8 files changed

+133
-82
lines changed

8 files changed

+133
-82
lines changed

crates/l2/monitor/app.rs

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -58,14 +58,14 @@ impl EthrexMonitor {
5858
store: Store,
5959
rollup_store: StoreRollup,
6060
cfg: &SequencerConfig,
61-
) -> Self {
61+
) -> Result<Self, MonitorError> {
6262
let eth_client = EthClient::new(cfg.eth.rpc_url.first().expect("No RPC URLs provided"))
6363
.expect("Failed to create EthClient");
6464
// TODO: De-hardcode the rollup client URL
6565
let rollup_client =
6666
EthClient::new("http://localhost:1729").expect("Failed to create RollupClient");
6767

68-
EthrexMonitor {
68+
Ok(EthrexMonitor {
6969
title: if cfg.based.based {
7070
"Based Ethrex Monitor".to_string()
7171
} else {
@@ -89,25 +89,25 @@ impl EthrexMonitor {
8989
&eth_client,
9090
&rollup_store,
9191
)
92-
.await,
93-
blocks_table: BlocksTable::new(&store).await,
92+
.await?,
93+
blocks_table: BlocksTable::new(&store).await?,
9494
l1_to_l2_messages: L1ToL2MessagesTable::new(
9595
cfg.l1_watcher.bridge_address,
9696
&eth_client,
9797
&store,
9898
)
99-
.await,
99+
.await?,
100100
l2_to_l1_messages: L2ToL1MessagesTable::new(
101101
cfg.l1_watcher.bridge_address,
102102
&eth_client,
103103
&rollup_client,
104104
)
105-
.await,
105+
.await?,
106106
eth_client,
107107
rollup_client,
108108
store,
109109
rollup_store,
110-
}
110+
})
111111
}
112112

113113
pub async fn start(mut self) -> Result<(), MonitorError> {
@@ -147,7 +147,7 @@ impl EthrexMonitor {
147147

148148
let timeout = Duration::from_millis(self.tick_rate).saturating_sub(last_tick.elapsed());
149149
if !event::poll(timeout)? {
150-
self.on_tick().await;
150+
self.on_tick().await?;
151151
last_tick = Instant::now();
152152
continue;
153153
}
@@ -207,22 +207,24 @@ impl EthrexMonitor {
207207
}
208208
}
209209

210-
pub async fn on_tick(&mut self) {
210+
pub async fn on_tick(&mut self) -> Result<(), MonitorError> {
211211
self.node_status.on_tick(&self.store).await;
212212
self.global_chain_status
213213
.on_tick(&self.eth_client, &self.store, &self.rollup_store)
214214
.await;
215215
self.mempool.on_tick(&self.rollup_client).await;
216216
self.batches_table
217217
.on_tick(&self.eth_client, &self.rollup_store)
218-
.await;
219-
self.blocks_table.on_tick(&self.store).await;
218+
.await?;
219+
self.blocks_table.on_tick(&self.store).await?;
220220
self.l1_to_l2_messages
221221
.on_tick(&self.eth_client, &self.store)
222-
.await;
222+
.await?;
223223
self.l2_to_l1_messages
224224
.on_tick(&self.eth_client, &self.rollup_client)
225-
.await;
225+
.await?;
226+
227+
Ok(())
226228
}
227229
}
228230

crates/l2/monitor/mod.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
// TODO: Handle this expects
22
#![expect(clippy::expect_used)]
3-
#![expect(clippy::panic)]
43
#![expect(clippy::indexing_slicing)]
5-
4+
#[expect(clippy::result_large_err)]
65
pub(crate) mod app;
76
pub(crate) mod utils;
87
pub(crate) mod widget;
@@ -21,7 +20,7 @@ pub async fn start_monitor(
2120
rollup_store: StoreRollup,
2221
cfg: SequencerConfig,
2322
) -> Result<(), SequencerError> {
24-
let app = EthrexMonitor::new(sequencer_state, store, rollup_store, &cfg).await;
23+
let app = EthrexMonitor::new(sequencer_state, store, rollup_store, &cfg).await?;
2524
app.start().await?;
2625
Ok(())
2726
}

crates/l2/monitor/utils.rs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,14 @@ use ethrex_common::{Address, U256};
44
use ethrex_rpc::{EthClient, types::receipt::RpcLog};
55
use keccak_hash::keccak;
66

7+
use crate::sequencer::errors::MonitorError;
8+
79
pub async fn get_logs(
810
last_block_fetched: &mut U256,
911
emitter: Address,
1012
logs_signatures: Vec<&str>,
1113
client: &EthClient,
12-
) -> Vec<RpcLog> {
14+
) -> Result<Vec<RpcLog>, MonitorError> {
1315
let last_block_number = client
1416
.get_block_number()
1517
.await
@@ -31,13 +33,19 @@ pub async fn get_logs(
3133
.collect(),
3234
)
3335
.await
34-
.unwrap_or_else(|_| panic!("Failed to fetch {logs_signatures:?} logs from {emitter}"));
36+
.map_err(|e| {
37+
MonitorError::LogsSignatures(
38+
logs_signatures.iter().map(|s| s.to_string()).collect(),
39+
emitter,
40+
e,
41+
)
42+
})?;
3543

3644
// Update the last L1 block fetched.
3745
*last_block_fetched = new_last_l1_fetched_block;
3846

3947
batch_committed_logs.extend_from_slice(&logs);
4048
}
4149

42-
batch_committed_logs
50+
Ok(batch_committed_logs)
4351
}

crates/l2/monitor/widget/batches.rs

Lines changed: 33 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@ use ratatui::{
99
widgets::{Block, Row, StatefulWidget, Table, TableState},
1010
};
1111

12-
use crate::monitor::widget::{HASH_LENGTH_IN_DIGITS, NUMBER_LENGTH_IN_DIGITS};
12+
use crate::{
13+
monitor::widget::{HASH_LENGTH_IN_DIGITS, NUMBER_LENGTH_IN_DIGITS},
14+
sequencer::errors::MonitorError,
15+
};
1316

1417
pub struct BatchesTable {
1518
pub state: TableState,
@@ -25,43 +28,49 @@ impl BatchesTable {
2528
on_chain_proposer_address: Address,
2629
eth_client: &EthClient,
2730
rollup_store: &StoreRollup,
28-
) -> Self {
31+
) -> Result<Self, MonitorError> {
2932
let mut last_l1_block_fetched = 0;
3033
let items = Self::fetch_new_items(
3134
&mut last_l1_block_fetched,
3235
on_chain_proposer_address,
3336
eth_client,
3437
rollup_store,
3538
)
36-
.await;
37-
Self {
39+
.await?;
40+
Ok(Self {
3841
state: TableState::default(),
3942
items,
4043
last_l1_block_fetched,
4144
on_chain_proposer_address,
42-
}
45+
})
4346
}
4447

45-
pub async fn on_tick(&mut self, eth_client: &EthClient, rollup_store: &StoreRollup) {
48+
pub async fn on_tick(
49+
&mut self,
50+
eth_client: &EthClient,
51+
rollup_store: &StoreRollup,
52+
) -> Result<(), MonitorError> {
4653
let mut new_latest_batches = Self::fetch_new_items(
4754
&mut self.last_l1_block_fetched,
4855
self.on_chain_proposer_address,
4956
eth_client,
5057
rollup_store,
5158
)
52-
.await;
59+
.await?;
5360
new_latest_batches.truncate(50);
5461

5562
let n_new_latest_batches = new_latest_batches.len();
5663
self.items.truncate(50 - n_new_latest_batches);
57-
self.refresh_items(rollup_store).await;
64+
self.refresh_items(rollup_store).await?;
5865
self.items.extend_from_slice(&new_latest_batches);
5966
self.items.rotate_right(n_new_latest_batches);
67+
68+
Ok(())
6069
}
6170

62-
async fn refresh_items(&mut self, rollup_store: &StoreRollup) {
71+
async fn refresh_items(&mut self, rollup_store: &StoreRollup) -> Result<(), MonitorError> {
6372
if self.items.is_empty() {
64-
return;
73+
return Ok(());
6574
}
6675

6776
let mut from = self.items.last().expect("Expected items in the table").0 - 1;
@@ -71,48 +80,52 @@ impl BatchesTable {
7180
self.items.first().expect("Expected items in the table").0,
7281
rollup_store,
7382
)
74-
.await;
83+
.await?;
7584

7685
let refreshed_items = Self::process_batches(refreshed_batches).await;
7786

7887
self.items = refreshed_items;
88+
89+
Ok(())
7990
}
8091

8192
async fn fetch_new_items(
8293
last_l2_batch_fetched: &mut u64,
8394
on_chain_proposer_address: Address,
8495
eth_client: &EthClient,
8596
rollup_store: &StoreRollup,
86-
) -> Vec<(u64, u64, usize, Option<H256>, Option<H256>)> {
97+
) -> Result<Vec<(u64, u64, usize, Option<H256>, Option<H256>)>, MonitorError> {
8798
let last_l2_batch_number = eth_client
8899
.get_last_committed_batch(on_chain_proposer_address)
89100
.await
90101
.expect("Failed to get latest L2 batch");
91102

92103
let new_batches =
93-
Self::get_batches(last_l2_batch_fetched, last_l2_batch_number, rollup_store).await;
104+
Self::get_batches(last_l2_batch_fetched, last_l2_batch_number, rollup_store).await?;
94105

95-
Self::process_batches(new_batches).await
106+
Ok(Self::process_batches(new_batches).await)
96107
}
97108

98-
async fn get_batches(from: &mut u64, to: u64, rollup_store: &StoreRollup) -> Vec<Batch> {
109+
async fn get_batches(
110+
from: &mut u64,
111+
to: u64,
112+
rollup_store: &StoreRollup,
113+
) -> Result<Vec<Batch>, MonitorError> {
99114
let mut new_batches = Vec::new();
100115

101116
for batch_number in *from + 1..=to {
102117
let batch = rollup_store
103118
.get_batch(batch_number)
104119
.await
105-
.unwrap_or_else(|err| {
106-
panic!("Failed to get batch by number ({batch_number}): {err}")
107-
})
108-
.unwrap_or_else(|| panic!("Batch {batch_number} not found in the rollup store"));
120+
.map_err(|e| MonitorError::RollupStore(batch_number, e))?
121+
.ok_or(MonitorError::BatchNotFound(batch_number))?;
109122

110123
*from = batch_number;
111124

112125
new_batches.push(batch);
113126
}
114127

115-
new_batches
128+
Ok(new_batches)
116129
}
117130

118131
async fn process_batches(

crates/l2/monitor/widget/blocks.rs

Lines changed: 25 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,12 @@ use ratatui::{
1111
widgets::{Row, StatefulWidget, Table, TableState},
1212
};
1313

14-
use crate::monitor::widget::{
15-
ADDRESS_LENGTH_IN_DIGITS, BLOCK_SIZE_LENGTH_IN_DIGITS, GAS_USED_LENGTH_IN_DIGITS,
16-
HASH_LENGTH_IN_DIGITS, NUMBER_LENGTH_IN_DIGITS, TX_NUMBER_LENGTH_IN_DIGITS,
14+
use crate::{
15+
monitor::widget::{
16+
ADDRESS_LENGTH_IN_DIGITS, BLOCK_SIZE_LENGTH_IN_DIGITS, GAS_USED_LENGTH_IN_DIGITS,
17+
HASH_LENGTH_IN_DIGITS, NUMBER_LENGTH_IN_DIGITS, TX_NUMBER_LENGTH_IN_DIGITS,
18+
},
19+
sequencer::errors::MonitorError,
1720
};
1821

1922
pub struct BlocksTable {
@@ -24,35 +27,37 @@ pub struct BlocksTable {
2427
}
2528

2629
impl BlocksTable {
27-
pub async fn new(store: &Store) -> Self {
30+
pub async fn new(store: &Store) -> Result<Self, MonitorError> {
2831
let mut last_l2_block_known = 0;
29-
let items = Self::refresh_items(&mut last_l2_block_known, store).await;
30-
Self {
32+
let items = Self::refresh_items(&mut last_l2_block_known, store).await?;
33+
Ok(Self {
3134
state: TableState::default(),
3235
items,
3336
last_l2_block_known,
34-
}
37+
})
3538
}
3639

37-
pub async fn on_tick(&mut self, store: &Store) {
38-
let mut new_blocks = Self::refresh_items(&mut self.last_l2_block_known, store).await;
40+
pub async fn on_tick(&mut self, store: &Store) -> Result<(), MonitorError> {
41+
let mut new_blocks = Self::refresh_items(&mut self.last_l2_block_known, store).await?;
3942
new_blocks.truncate(50);
4043

4144
let n_new_blocks = new_blocks.len();
4245
self.items.truncate(50 - n_new_blocks);
4346
self.items.extend_from_slice(&new_blocks);
4447
self.items.rotate_right(n_new_blocks);
48+
49+
Ok(())
4550
}
4651

4752
async fn refresh_items(
4853
last_l2_block_known: &mut u64,
4954
store: &Store,
50-
) -> Vec<(String, String, String, String, String, String, String)> {
51-
let new_blocks = Self::get_blocks(last_l2_block_known, store).await;
55+
) -> Result<Vec<(String, String, String, String, String, String, String)>, MonitorError> {
56+
let new_blocks = Self::get_blocks(last_l2_block_known, store).await?;
5257

5358
let new_blocks_processed = Self::process_blocks(new_blocks).await;
5459

55-
new_blocks_processed
60+
Ok(new_blocks_processed
5661
.iter()
5762
.map(|(number, n_txs, hash, coinbase, gas, blob_gas, size)| {
5863
(
@@ -65,10 +70,13 @@ impl BlocksTable {
6570
size.to_string(),
6671
)
6772
})
68-
.collect()
73+
.collect())
6974
}
7075

71-
async fn get_blocks(last_l2_block_known: &mut u64, store: &Store) -> Vec<Block> {
76+
async fn get_blocks(
77+
last_l2_block_known: &mut u64,
78+
store: &Store,
79+
) -> Result<Vec<Block>, MonitorError> {
7280
let last_l2_block_number = store
7381
.get_latest_block_number()
7482
.await
@@ -81,20 +89,16 @@ impl BlocksTable {
8189
let new_block = store
8290
.get_block_by_number(new_last_l1_fetched_block)
8391
.await
84-
.unwrap_or_else(|_| {
85-
panic!("Failed to get block by number ({new_last_l1_fetched_block})")
86-
})
87-
.unwrap_or_else(|| {
88-
panic!("Block {new_last_l1_fetched_block} not found in the store")
89-
});
92+
.map_err(|e| MonitorError::GetBlockByNumber(new_last_l1_fetched_block, e))?
93+
.ok_or(MonitorError::BlockNotFound(new_last_l1_fetched_block))?;
9094

9195
// Update the last L1 block fetched.
9296
*last_l2_block_known = new_last_l1_fetched_block;
9397

9498
new_blocks.push(new_block);
9599
}
96100

97-
new_blocks
101+
Ok(new_blocks)
98102
}
99103

100104
async fn process_blocks(

0 commit comments

Comments
 (0)