Skip to content

Commit 8496196

Browse files
committed
feat: introduce BeaconProvider trait along with Mock implementation.
1 parent 5666c83 commit 8496196

File tree

10 files changed

+126
-38
lines changed

10 files changed

+126
-38
lines changed

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@ derive_more = { version = "2.0", default-features = false }
173173
eyre = "0.6"
174174
futures = { version = "0.3", default-features = false }
175175
rand = { version = "0.9" }
176+
reqwest = "0.12"
176177
secp256k1 = { version = "0.29", default-features = false }
177178
thiserror = "2.0"
178179
tokio = { version = "1.39", default-features = false }

bin/rollup/Cargo.toml

+2
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ reth-tracing = { git = "https://github.com/scroll-tech/reth.git" }
7575

7676
# misc
7777
futures.workspace = true
78+
reqwest.workspace = true
7879
serde_json = { version = "1.0.94", default-features = false, features = ["alloc"] }
7980
tokio = { workspace = true, features = ["full"] }
8081

@@ -92,6 +93,7 @@ test-utils = [
9293
"rollup-node-watcher/test-utils",
9394
"scroll-db/test-utils",
9495
"scroll-db/test-utils",
96+
"rollup-node-providers/test-utils",
9597
]
9698
serde = [
9799
"alloy-primitives/serde",

bin/rollup/src/constants.rs

+3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
/// The block number at which to start the L1 watcher.
22
pub const WATCHER_START_BLOCK_NUMBER: u64 = 8000374;
33

4+
/// The size of the blob cache for the provider.
5+
pub const PROVIDER_BLOB_CACHE_SIZE: usize = 100;
6+
47
/// The max retries for the L1 provider.
58
pub const PROVIDER_MAX_RETRIES: u32 = 10;
69

bin/rollup/src/network.rs

+10-5
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use reth_scroll_primitives::ScrollPrimitives;
1212
use reth_transaction_pool::{PoolTransaction, TransactionPool};
1313
use rollup_node_indexer::Indexer;
1414
use rollup_node_manager::{PoAConsensus, RollupNodeManager};
15-
use rollup_node_providers::{DatabaseL1MessageProvider, OnlineBeaconClient, OnlineL1Provider};
15+
use rollup_node_providers::{beacon_provider, DatabaseL1MessageProvider, OnlineL1Provider};
1616
use rollup_node_watcher::L1Watcher;
1717
use scroll_alloy_provider::ScrollAuthEngineApiProvider;
1818
use scroll_db::{Database, DatabaseConnectionProvider};
@@ -22,7 +22,10 @@ use scroll_wire::{ProtocolHandler, ScrollWireConfig};
2222
use std::sync::Arc;
2323
use tracing::info;
2424

25-
use crate::{L1ProviderArgs, ScrollRollupNodeArgs, WATCHER_START_BLOCK_NUMBER};
25+
use crate::{
26+
constants::PROVIDER_BLOB_CACHE_SIZE, L1ProviderArgs, ScrollRollupNodeArgs,
27+
WATCHER_START_BLOCK_NUMBER,
28+
};
2629

2730
/// The network builder for the eth-wire to scroll-wire bridge.
2831
#[derive(Debug)]
@@ -138,10 +141,12 @@ where
138141
None
139142
};
140143

141-
let beacon_client =
142-
OnlineBeaconClient::new_http(l1_provider_args.beacon_rpc_url.to_string());
144+
// Construct the l1 provider.
145+
let beacon_provider = beacon_provider(l1_provider_args.beacon_rpc_url.to_string());
143146
let l1_messages_provider = DatabaseL1MessageProvider::new(db.clone());
144-
let l1_provider = OnlineL1Provider::new(beacon_client, 100, l1_messages_provider).await;
147+
let l1_provider =
148+
OnlineL1Provider::new(beacon_provider, PROVIDER_BLOB_CACHE_SIZE, l1_messages_provider)
149+
.await;
145150

146151
// Spawn the rollup node manager
147152
let rollup_node_manager = RollupNodeManager::new(

crates/engine/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ test-utils = [
6262
"arbitrary",
6363
"dep:arbitrary",
6464
"reth-primitives/test-utils",
65+
"rollup-node-providers/test-utils",
6566
]
6667
serde = [
6768
"alloy-eips/serde",

crates/providers/Cargo.toml

+4-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ scroll-db.workspace = true
2525
async-trait.workspace = true
2626
auto_impl.workspace = true
2727
lru = "0.13.0"
28-
reqwest = { version = "0.12", features = ["json"] }
28+
reqwest = { workspace = true, features = ["json"] }
2929
serde = { version = "1.0", features = ["derive"] }
3030
thiserror.workspace = true
3131
tokio = { workspace = true, default-features = false }
@@ -34,3 +34,6 @@ tokio = { workspace = true, default-features = false }
3434
eyre.workspace = true
3535
scroll-db = { workspace = true, features = ["test-utils"] }
3636
tokio = { workspace = true, features = ["macros"] }
37+
38+
[features]
39+
test-utils = ["scroll-db/test-utils"]

crates/providers/src/beacon_client.rs renamed to crates/providers/src/beacon.rs

+43-6
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,41 @@
1-
//! Contains an implementation of a Beacon client.
1+
//! Exposes the [`BeaconProvider`] trait allowing to retrieve information from the Beacon chain.
22
//! Credit to <https://github.com/op-rs/kona/tree/main/crates/providers/providers-alloy>
33
4+
use crate::L1ProviderError;
5+
use std::{format, sync::Arc, vec::Vec};
6+
47
use alloy_rpc_types_beacon::sidecar::{BeaconBlobBundle, BlobData};
58
use reqwest::Client;
6-
use std::{format, vec::Vec};
9+
10+
/// Returns a Beacon provider implementation, yielding a [`crate::test_utils::MockBeaconProvider`]
11+
/// when the test-utils feature is activated.
12+
pub fn beacon_provider(
13+
_base: String,
14+
) -> Arc<dyn BeaconProvider<Error = reqwest::Error> + Send + Sync> {
15+
#[cfg(feature = "test-utils")]
16+
{
17+
Arc::new(crate::test_utils::MockBeaconProvider::default())
18+
}
19+
#[cfg(not(feature = "test-utils"))]
20+
{
21+
Arc::new(OnlineBeaconClient::new_http(_base))
22+
}
23+
}
24+
25+
/// An implementation of the trait can provide information related to the Beacon chain.
26+
#[async_trait::async_trait]
27+
#[auto_impl::auto_impl(&, Arc)]
28+
pub trait BeaconProvider {
29+
/// The error type for the provider.
30+
type Error: Into<L1ProviderError> + std::fmt::Debug;
31+
32+
/// Returns the reduced configuration data for the Beacon client.
33+
async fn config_spec(&self) -> Result<APIResponse<ReducedConfigData>, Self::Error>;
34+
/// Returns the Beacon genesis information.
35+
async fn beacon_genesis(&self) -> Result<APIResponse<ReducedGenesisData>, Self::Error>;
36+
/// Returns the blobs for the provided slot.
37+
async fn blobs(&self, slot: u64) -> Result<Vec<BlobData>, Self::Error>;
38+
}
739

840
/// The config spec engine api method.
941
const SPEC_METHOD: &str = "eth/v1/config/spec";
@@ -59,21 +91,24 @@ impl OnlineBeaconClient {
5991
}
6092
}
6193

62-
impl OnlineBeaconClient {
94+
#[async_trait::async_trait]
95+
impl BeaconProvider for OnlineBeaconClient {
96+
type Error = reqwest::Error;
97+
6398
/// Returns the reduced configuration data for the Beacon client.
64-
pub async fn config_spec(&self) -> Result<APIResponse<ReducedConfigData>, reqwest::Error> {
99+
async fn config_spec(&self) -> Result<APIResponse<ReducedConfigData>, Self::Error> {
65100
let first = self.inner.get(format!("{}/{}", self.base, SPEC_METHOD)).send().await?;
66101
first.json::<APIResponse<ReducedConfigData>>().await
67102
}
68103

69104
/// Returns the Beacon genesis information.
70-
pub async fn beacon_genesis(&self) -> Result<APIResponse<ReducedGenesisData>, reqwest::Error> {
105+
async fn beacon_genesis(&self) -> Result<APIResponse<ReducedGenesisData>, Self::Error> {
71106
let first = self.inner.get(format!("{}/{}", self.base, GENESIS_METHOD)).send().await?;
72107
first.json::<APIResponse<ReducedGenesisData>>().await
73108
}
74109

75110
/// Returns the blobs for the provided slot.
76-
pub async fn blobs(&self, slot: u64) -> Result<Vec<BlobData>, reqwest::Error> {
111+
async fn blobs(&self, slot: u64) -> Result<Vec<BlobData>, Self::Error> {
77112
let raw_response = self
78113
.inner
79114
.get(format!("{}/{}/{}", self.base, SIDECARS_METHOD_PREFIX, slot))
@@ -93,6 +128,7 @@ mod tests {
93128
const BEACON_CLIENT_URL: &str = "https://eth-beacon-chain.drpc.org/rest/";
94129

95130
#[tokio::test]
131+
#[ignore]
96132
async fn test_should_return_genesis() -> eyre::Result<()> {
97133
let client = OnlineBeaconClient::new_http(BEACON_CLIENT_URL.to_string());
98134
let genesis = client.beacon_genesis().await?;
@@ -103,6 +139,7 @@ mod tests {
103139
}
104140

105141
#[tokio::test]
142+
#[ignore]
106143
async fn test_should_return_config() -> eyre::Result<()> {
107144
let client = OnlineBeaconClient::new_http(BEACON_CLIENT_URL.to_string());
108145
let config = client.config_spec().await?;

crates/providers/src/l1/mod.rs

+28-24
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
pub(crate) mod blob;
22
pub(crate) mod message;
33

4-
use crate::{beacon_client::OnlineBeaconClient, l1::message::L1MessageProvider, L1BlobProvider};
4+
use crate::{beacon::BeaconProvider, l1::message::L1MessageProvider, L1BlobProvider};
55
use std::{num::NonZeroUsize, sync::Arc};
66

77
use alloy_eips::eip4844::{Blob, BlobTransactionSidecarItem};
@@ -18,9 +18,9 @@ impl<T> L1Provider for T where T: L1BlobProvider + L1MessageProvider {}
1818
/// An error occurring at the [`L1Provider`].
1919
#[derive(Debug, thiserror::Error)]
2020
pub enum L1ProviderError {
21-
/// Invalid timestamp for slot.
22-
#[error("Beacon client error: {0}")]
23-
BeaconClient(#[from] reqwest::Error),
21+
/// Error at the beacon provider.
22+
#[error("Beacon provider error: {0}")]
23+
BeaconProvider(#[from] reqwest::Error),
2424
/// Invalid timestamp for slot.
2525
#[error("invalid block timestamp: genesis {0}, provided {1}")]
2626
InvalidBlockTimestamp(u64, u64),
@@ -34,37 +34,40 @@ pub enum L1ProviderError {
3434

3535
/// An online implementation of the [`L1Provider`] trait.
3636
#[derive(Debug, Clone)]
37-
pub struct OnlineL1Provider<P> {
38-
/// The Beacon client.
39-
beacon_client: OnlineBeaconClient,
37+
pub struct OnlineL1Provider<L1P, BP> {
38+
/// The beacon provider.
39+
beacon_provider: BP,
4040
/// The cache for blobs from similar blocks.
4141
cache: Arc<Mutex<LruCache<B256, Arc<Blob>>>>,
4242
/// The L1 message provider
43-
l1_message_provider: P,
43+
l1_message_provider: L1P,
4444
/// The genesis timestamp for the Beacon chain.
4545
genesis_timestamp: u64,
4646
/// The slot interval for the Beacon chain.
4747
slot_interval: u64,
4848
}
4949

50-
impl<P> OnlineL1Provider<P> {
50+
impl<L1P, BP> OnlineL1Provider<L1P, BP>
51+
where
52+
BP: BeaconProvider,
53+
{
5154
/// Returns a new [`OnlineBeaconClient`] from the provided [`OnlineBeaconClient`], blob capacity
5255
/// and [`L1MessageProvider`].
53-
pub async fn new(
54-
client: OnlineBeaconClient,
55-
blob_capacity: usize,
56-
l1_message_provider: P,
57-
) -> Self {
56+
pub async fn new(beacon_provider: BP, blob_capacity: usize, l1_message_provider: L1P) -> Self {
5857
let cache = Arc::new(Mutex::new(LruCache::new(
5958
NonZeroUsize::new(blob_capacity).expect("cache requires non-zero capacity"),
6059
)));
61-
let config =
62-
client.config_spec().await.expect("failed to fetch Beacon chain configuration");
63-
let genesis =
64-
client.beacon_genesis().await.expect("failed to fetch Beacon chain genesis info");
60+
let config = beacon_provider
61+
.config_spec()
62+
.await
63+
.expect("failed to fetch Beacon chain configuration");
64+
let genesis = beacon_provider
65+
.beacon_genesis()
66+
.await
67+
.expect("failed to fetch Beacon chain genesis info");
6568

6669
Self {
67-
beacon_client: client,
70+
beacon_provider,
6871
cache,
6972
l1_message_provider,
7073
genesis_timestamp: genesis.data.genesis_time,
@@ -85,7 +88,7 @@ impl<P> OnlineL1Provider<P> {
8588
}
8689

8790
#[async_trait::async_trait]
88-
impl<P: Sync> L1BlobProvider for OnlineL1Provider<P> {
91+
impl<L1P: Sync, BP: BeaconProvider + Sync> L1BlobProvider for OnlineL1Provider<L1P, BP> {
8992
/// Returns the requested blob corresponding to the passed hash.
9093
async fn blob(
9194
&self,
@@ -103,9 +106,10 @@ impl<P: Sync> L1BlobProvider for OnlineL1Provider<P> {
103106
// query the blobs with the client, return target blob and store all others in cache.
104107
let slot = self.slot(block_timestamp)?;
105108
let mut blobs = self
106-
.beacon_client
109+
.beacon_provider
107110
.blobs(slot)
108-
.await?
111+
.await
112+
.map_err(Into::into)?
109113
.into_iter()
110114
.map(|blob| BlobTransactionSidecarItem {
111115
index: blob.index,
@@ -134,8 +138,8 @@ impl<P: Sync> L1BlobProvider for OnlineL1Provider<P> {
134138
}
135139

136140
#[async_trait::async_trait]
137-
impl<P: L1MessageProvider + Sync> L1MessageProvider for OnlineL1Provider<P> {
138-
type Error = <P>::Error;
141+
impl<L1P: L1MessageProvider + Sync, BP: Sync> L1MessageProvider for OnlineL1Provider<L1P, BP> {
142+
type Error = <L1P>::Error;
139143

140144
async fn next_l1_message(&self) -> Result<Option<TxL1Message>, Self::Error> {
141145
self.l1_message_provider.next_l1_message().await

crates/providers/src/lib.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
//! The crate exposes various Providers along with their implementations for usage across the rollup
22
//! node.
33
4-
pub use beacon_client::OnlineBeaconClient;
5-
mod beacon_client;
4+
pub use beacon::{beacon_provider, BeaconProvider, OnlineBeaconClient};
5+
mod beacon;
66

77
pub use execution_payload::ExecutionPayloadProvider;
88
mod execution_payload;
@@ -13,3 +13,7 @@ pub use l1::{
1313
L1Provider, L1ProviderError, OnlineL1Provider,
1414
};
1515
mod l1;
16+
17+
/// Test utils related to providers.
18+
#[cfg(feature = "test-utils")]
19+
pub mod test_utils;

crates/providers/src/test_utils.rs

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
use crate::{
2+
beacon::{APIResponse, ReducedConfigData, ReducedGenesisData},
3+
BeaconProvider,
4+
};
5+
6+
use alloy_rpc_types_beacon::sidecar::BlobData;
7+
8+
/// Mocks all calls to the beacon chain.
9+
#[derive(Debug, Default)]
10+
#[non_exhaustive]
11+
pub struct MockBeaconProvider;
12+
13+
#[async_trait::async_trait]
14+
impl BeaconProvider for MockBeaconProvider {
15+
type Error = reqwest::Error;
16+
17+
async fn config_spec(&self) -> Result<APIResponse<ReducedConfigData>, Self::Error> {
18+
Ok(APIResponse { data: ReducedConfigData::default() })
19+
}
20+
21+
async fn beacon_genesis(&self) -> Result<APIResponse<ReducedGenesisData>, Self::Error> {
22+
Ok(APIResponse { data: ReducedGenesisData::default() })
23+
}
24+
25+
async fn blobs(&self, _slot: u64) -> Result<Vec<BlobData>, Self::Error> {
26+
Ok(vec![])
27+
}
28+
}

0 commit comments

Comments
 (0)