Skip to content

Commit b06b649

Browse files
authored
feat add aws s3 blob client (#242)
* feat: add aws s3 blob client * feat: add s3 blob client * address comment * rm extra files
1 parent f19a7f3 commit b06b649

File tree

4 files changed

+92
-4
lines changed

4 files changed

+92
-4
lines changed

crates/providers/src/l1/blob/mod.rs

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,15 @@ pub use client::BeaconClientProvider;
99
mod mock;
1010
pub use mock::MockBeaconProvider;
1111

12+
mod s3;
13+
pub use s3::S3BlobProvider;
14+
1215
use crate::L1ProviderError;
1316
use std::sync::Arc;
1417

1518
use alloy_eips::eip4844::Blob;
1619
use alloy_primitives::B256;
1720
use eyre::OptionExt;
18-
1921
/// An instance of the trait can be used to fetch L1 blob data.
2022
#[async_trait::async_trait]
2123
#[auto_impl::auto_impl(Arc, &)]
@@ -52,11 +54,39 @@ impl BlobSource {
5254
)
5355
.await,
5456
),
55-
// TODO: implement AWS S3 blob provider.
56-
Self::Mock | Self::S3 => Arc::new(MockBeaconProvider::default()),
57+
Self::Mock => Arc::new(MockBeaconProvider::default()),
58+
Self::S3 => Arc::new(S3BlobProvider::new_http(
59+
url.ok_or_eyre("missing url for s3 blob provider")?,
60+
)),
5761
Self::Anvil => Arc::new(AnvilBlobProvider::new_http(
5862
url.ok_or_eyre("missing url for anvil blob provider")?,
5963
)),
6064
})
6165
}
6266
}
67+
68+
#[cfg(test)]
69+
mod tests {
70+
use super::*;
71+
use std::str::FromStr;
72+
73+
#[tokio::test]
74+
async fn test_s3_blob_provider() {
75+
let provider = S3BlobProvider::new_http(
76+
reqwest::Url::parse("https://scroll-mainnet-blob-data.s3.us-west-2.amazonaws.com")
77+
.unwrap(),
78+
);
79+
let blob = provider
80+
.blob(
81+
0,
82+
B256::from_str(
83+
"0x0155ba17dcd008d7ba499d0e2f1fdc26dcc9fb4d83ee37d5c4bb3d1040c3f48a",
84+
)
85+
.unwrap(),
86+
)
87+
.await
88+
.unwrap();
89+
90+
assert!(blob.is_some());
91+
}
92+
}

crates/providers/src/l1/blob/s3.rs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
use crate::{BlobProvider, L1ProviderError};
2+
use reqwest::Client;
3+
use std::sync::Arc;
4+
5+
use alloy_eips::eip4844::Blob;
6+
use alloy_primitives::B256;
7+
8+
/// An implementation of a blob provider client using S3.
9+
#[derive(Debug, Clone)]
10+
pub struct S3BlobProvider {
11+
/// The base URL for the S3 service.
12+
pub base_url: String,
13+
/// HTTP client for making requests.
14+
pub client: Client,
15+
}
16+
17+
impl S3BlobProvider {
18+
/// Creates a new [`S3BlobProvider`] from the provided url.
19+
pub fn new_http(base: reqwest::Url) -> Self {
20+
// If base ends with a slash, remove it
21+
let mut base = base.to_string();
22+
if base.ends_with('/') {
23+
base.remove(base.len() - 1);
24+
}
25+
Self { base_url: base, client: reqwest::Client::new() }
26+
}
27+
}
28+
29+
#[async_trait::async_trait]
30+
impl BlobProvider for S3BlobProvider {
31+
#[allow(clippy::large_stack_frames)]
32+
async fn blob(
33+
&self,
34+
_block_timestamp: u64,
35+
hash: B256,
36+
) -> Result<Option<Arc<Blob>>, L1ProviderError> {
37+
let url = format!("{}/{}", self.base_url, hash);
38+
let response = self.client.get(&url).send().await.map_err(L1ProviderError::S3Provider)?;
39+
40+
if response.status().is_success() {
41+
let blob_data = response.bytes().await.map_err(L1ProviderError::S3Provider)?;
42+
43+
let blob = Blob::try_from(blob_data.as_ref())
44+
.map_err(|_| L1ProviderError::Other("Invalid blob data"))?;
45+
Ok(Some(Arc::new(blob)))
46+
} else if response.status() == 404 {
47+
Ok(None)
48+
} else {
49+
Err(L1ProviderError::Other("S3 client HTTP error"))
50+
}
51+
}
52+
}

crates/providers/src/l1/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ pub enum L1ProviderError {
2121
/// Error at the beacon provider.
2222
#[error("Beacon provider error: {0}")]
2323
BeaconProvider(#[from] reqwest::Error),
24+
/// Error at the s3 provider.
25+
#[error("S3 provider error: {0}")]
26+
S3Provider(reqwest::Error),
2427
/// Invalid timestamp for slot.
2528
#[error("invalid block timestamp: genesis {0}, provided {1}")]
2629
InvalidBlockTimestamp(u64, u64),

crates/providers/src/lib.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@ pub use block::BlockDataProvider;
99

1010
mod l1;
1111
pub use l1::{
12-
blob::{AnvilBlobProvider, BeaconClientProvider, BlobProvider, BlobSource, MockBeaconProvider},
12+
blob::{
13+
AnvilBlobProvider, BeaconClientProvider, BlobProvider, BlobSource, MockBeaconProvider,
14+
S3BlobProvider,
15+
},
1316
message::{DatabaseL1MessageProvider, L1MessageProvider},
1417
system_contract::{SystemContractProvider, AUTHORIZED_SIGNER_STORAGE_SLOT},
1518
FullL1Provider, L1Provider, L1ProviderError,

0 commit comments

Comments
 (0)