Skip to content

Commit ffbcfd2

Browse files
committed
apollo_l1_gas_price: start scraper with a loop to find start block, instead of panic if it fails
1 parent d3ea928 commit ffbcfd2

File tree

2 files changed

+70
-21
lines changed

2 files changed

+70
-21
lines changed

crates/apollo_l1_gas_price/src/l1_gas_price_scraper.rs

Lines changed: 33 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use async_trait::async_trait;
1212
use papyrus_base_layer::{BaseLayerContract, L1BlockHeader, L1BlockNumber};
1313
use starknet_api::block::GasPrice;
1414
use thiserror::Error;
15-
use tracing::{error, info, trace};
15+
use tracing::{error, info, trace, warn};
1616

1717
use crate::metrics::{
1818
register_scraper_metrics,
@@ -63,6 +63,31 @@ impl<B: BaseLayerContract + Send + Sync + Debug> L1GasPriceScraper<B> {
6363
Self { config, l1_gas_price_provider, base_layer, last_l1_header: None }
6464
}
6565

66+
pub async fn get_first_block_number(&mut self) -> L1BlockNumber {
67+
if let Some(block) = self.config.starting_block {
68+
return block;
69+
};
70+
loop {
71+
let latest = self.latest_l1_block_number().await;
72+
let Ok(latest) = latest else {
73+
warn!(
74+
"Scraper startup failure: could not get the latest L1 block number: {latest:?}"
75+
);
76+
L1_GAS_PRICE_SCRAPER_BASELAYER_ERROR_COUNT.increment(1);
77+
tokio::time::sleep(self.config.polling_interval).await;
78+
continue;
79+
};
80+
// If no starting block is provided, the default is to start from
81+
// startup_num_blocks_multiplier * number_of_blocks_for_mean before the tip of
82+
// L1. Note that for new chains this subtraction may be
83+
// negative, hence the use of saturating_sub.
84+
let latest = latest.saturating_sub(
85+
self.config.number_of_blocks_for_mean * self.config.startup_num_blocks_multiplier,
86+
);
87+
return latest;
88+
}
89+
}
90+
6691
/// Run the scraper, starting from the given L1 `block_number`, indefinitely.
6792
pub async fn run(&mut self, mut block_number: L1BlockNumber) -> L1GasPriceScraperResult<(), B> {
6893
self.l1_gas_price_provider
@@ -174,24 +199,12 @@ where
174199
async fn start(&mut self) {
175200
info!("Starting component {}.", type_name::<Self>());
176201
register_scraper_metrics();
177-
let start_from = match self.config.starting_block {
178-
Some(block) => block,
179-
None => {
180-
let latest = self
181-
.latest_l1_block_number()
182-
.await
183-
.expect("Failed to get the latest L1 block number at startup");
184-
185-
// If no starting block is provided, the default is to start from
186-
// startup_num_blocks_multiplier * number_of_blocks_for_mean before the tip of L1.
187-
// Note that for new chains this subtraction may be negative,
188-
// hence the use of saturating_sub.
189-
latest.saturating_sub(
190-
self.config.number_of_blocks_for_mean
191-
* self.config.startup_num_blocks_multiplier,
192-
)
193-
}
194-
};
195-
self.run(start_from).await.unwrap_or_else(|e| panic!("L1 gas price scraper failed: {e}"))
202+
// Loop and retry base layer until we successfully get the first block number.
203+
let first_block_number = self.get_first_block_number().await;
204+
205+
// Run the scraper on an endless loop.
206+
self.run(first_block_number)
207+
.await
208+
.unwrap_or_else(|e| panic!("L1 gas price scraper failed: {e}"))
196209
}
197210
}

crates/apollo_l1_gas_price/src/l1_gas_price_scraper_test.rs

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
22
use std::sync::Arc;
3+
use std::time::Duration;
34

45
use apollo_l1_gas_price_types::{GasPriceData, MockL1GasPriceProviderClient};
56
use assert_matches::assert_matches;
6-
use papyrus_base_layer::{L1BlockHash, L1BlockHeader, MockBaseLayerContract};
7+
use papyrus_base_layer::{L1BlockHash, L1BlockHeader, MockBaseLayerContract, MockError};
78
use rstest::rstest;
89
use starknet_api::block::GasPrice;
910

@@ -323,4 +324,39 @@ async fn base_layer_returns_block_number_below_finality_causes_error() {
323324
);
324325
}
325326

327+
#[tokio::test]
328+
async fn get_first_block_fails_then_succeeds() {
329+
const NUM_BLOCKS_FOR_MEAN: u64 = 2;
330+
const STARTUP_NUM_BLOCKS_MULTIPLIER: u64 = 3;
331+
const LATEST_BLOCK_NUMBER: u64 = 10;
332+
let expected_start_block_number =
333+
LATEST_BLOCK_NUMBER - NUM_BLOCKS_FOR_MEAN * STARTUP_NUM_BLOCKS_MULTIPLIER;
334+
335+
let mut mock_contract = MockBaseLayerContract::new();
336+
// First fail.
337+
mock_contract
338+
.expect_latest_l1_block_number()
339+
.times(1)
340+
.returning(move || Err(MockError::MockError));
341+
342+
// Then succeed.
343+
mock_contract
344+
.expect_latest_l1_block_number()
345+
.times(1)
346+
.returning(move || Ok(LATEST_BLOCK_NUMBER));
347+
348+
let mut scraper = L1GasPriceScraper::new(
349+
L1GasPriceScraperConfig {
350+
polling_interval: Duration::from_secs(0),
351+
number_of_blocks_for_mean: NUM_BLOCKS_FOR_MEAN,
352+
startup_num_blocks_multiplier: STARTUP_NUM_BLOCKS_MULTIPLIER,
353+
..Default::default()
354+
},
355+
Arc::new(MockL1GasPriceProviderClient::new()),
356+
mock_contract,
357+
);
358+
let first_block_number = scraper.get_first_block_number().await;
359+
assert_eq!(first_block_number, expected_start_block_number);
360+
}
361+
326362
// TODO(guyn): test scraper with a provider timeout

0 commit comments

Comments
 (0)