1use async_trait::async_trait;
4use espresso_types::{Certificate2, Payload, SeqTypes};
5use hotshot_query_service::{
6 availability::{BlockQueryData, LeafId, LeafQueryData, VidCommonQueryData},
7 fetching::{
8 NonEmptyRange, Provider,
9 request::{
10 BlockRangeRequest, BlockRangesRequest, BlockRangesResponse, Certificate2Request,
11 LeafRangeRequest, LeafRangesRequest, LeafRequest, PayloadRequest,
12 VidCommonRangeRequest, VidCommonRangesRequest, VidCommonRequest,
13 },
14 },
15 node::BlockId,
16};
17use hotshot_types::data::VidCommon;
18
19use crate::{LightClient, client::Client, storage::Storage};
20
21#[async_trait]
22impl<P, S> Provider<SeqTypes, LeafRequest> for LightClient<P, S>
23where
24 P: Storage,
25 S: Client,
26{
27 async fn fetch(&self, req: LeafRequest) -> Option<LeafQueryData<SeqTypes>> {
28 match self.fetch_leaf(LeafId::Number(req.height as usize)).await {
29 Ok(leaf) => Some(leaf),
30 Err(err) => {
31 tracing::warn!(?req, "failed to fetch leaf: {err:#}");
32 None
33 },
34 }
35 }
36}
37
38#[async_trait]
39impl<P, S> Provider<SeqTypes, PayloadRequest> for LightClient<P, S>
40where
41 P: Storage,
42 S: Client,
43{
44 async fn fetch(&self, req: PayloadRequest) -> Option<Payload> {
45 match self.fetch_payload(BlockId::PayloadHash(req.0)).await {
46 Ok(payload) => Some(payload.data),
47 Err(err) => {
48 tracing::warn!(?req, "failed to fetch payload: {err:#}");
49 None
50 },
51 }
52 }
53}
54
55#[async_trait]
56impl<P, S> Provider<SeqTypes, VidCommonRequest> for LightClient<P, S>
57where
58 P: Storage,
59 S: Client,
60{
61 async fn fetch(&self, req: VidCommonRequest) -> Option<VidCommon> {
62 match self.fetch_vid_common(BlockId::PayloadHash(req.0)).await {
63 Ok(vid) => Some(vid.common),
64 Err(err) => {
65 tracing::warn!(?req, "failed to fetch VID common: {err:#}");
66 None
67 },
68 }
69 }
70}
71
72#[async_trait]
73impl<P, S> Provider<SeqTypes, LeafRangeRequest> for LightClient<P, S>
74where
75 P: Storage,
76 S: Client,
77{
78 async fn fetch(&self, req: LeafRangeRequest) -> Option<NonEmptyRange<LeafQueryData<SeqTypes>>> {
79 let leaves = match self
80 .fetch_leaves_in_range(req.start as usize, req.end as usize)
81 .await
82 {
83 Ok(leaves) => leaves,
84 Err(err) => {
85 tracing::warn!(?req, "failed to fetch leaf: {err:#}");
86 return None;
87 },
88 };
89 match leaves.try_into() {
90 Ok(leaves) => Some(leaves),
91 Err(err) => {
92 tracing::warn!(?req, "received invalid leaf range: {err:#}");
93 None
94 },
95 }
96 }
97}
98
99#[async_trait]
100impl<P, S> Provider<SeqTypes, BlockRangeRequest> for LightClient<P, S>
101where
102 P: Storage,
103 S: Client,
104{
105 async fn fetch(
106 &self,
107 req: BlockRangeRequest,
108 ) -> Option<NonEmptyRange<BlockQueryData<SeqTypes>>> {
109 let blocks = match self
110 .fetch_blocks_in_range(req.start as usize, req.end as usize)
111 .await
112 {
113 Ok(blocks) => blocks,
114 Err(err) => {
115 tracing::warn!(?req, "failed to fetch block range: {err:#}");
116 return None;
117 },
118 };
119 match blocks.try_into() {
120 Ok(blocks) => Some(blocks),
121 Err(err) => {
122 tracing::warn!(?req, "received invalid block range: {err:#}");
123 None
124 },
125 }
126 }
127}
128
129#[async_trait]
130impl<P, S> Provider<SeqTypes, VidCommonRangeRequest> for LightClient<P, S>
131where
132 P: Storage,
133 S: Client,
134{
135 async fn fetch(
136 &self,
137 req: VidCommonRangeRequest,
138 ) -> Option<NonEmptyRange<VidCommonQueryData<SeqTypes>>> {
139 let vid_common = match self
140 .fetch_vid_common_in_range(req.start as usize, req.end as usize)
141 .await
142 {
143 Ok(vid_common) => vid_common,
144 Err(err) => {
145 tracing::warn!(?req, "failed to fetch VID common range: {err:#}");
146 return None;
147 },
148 };
149 match vid_common.try_into() {
150 Ok(vid_common) => Some(vid_common),
151 Err(err) => {
152 tracing::warn!(?req, "received invalid VID common range: {err:#}");
153 None
154 },
155 }
156 }
157}
158
159#[async_trait]
160impl<P, S> Provider<SeqTypes, Certificate2Request> for LightClient<P, S>
161where
162 P: Storage,
163 S: Client,
164{
165 async fn fetch(&self, req: Certificate2Request) -> Option<Option<Certificate2<SeqTypes>>> {
166 match self.fetch_certificate2(req.height).await {
167 Ok(cert2) => Some(cert2),
168 Err(err) => {
169 tracing::info!(?req, %err, "failed to fetch cert2, will retry");
170 None
171 },
172 }
173 }
174}
175
176#[async_trait]
183impl<P, S> Provider<SeqTypes, LeafRangesRequest> for LightClient<P, S>
184where
185 P: Storage,
186 S: Client,
187{
188 async fn fetch(&self, req: LeafRangesRequest) -> Option<Vec<LeafQueryData<SeqTypes>>> {
189 match self.fetch_leaves_for_ranges(&req.0).await {
190 Ok(leaves) => Some(leaves),
191 Err(err) => {
192 tracing::warn!(?req, "failed to fetch leaf ranges: {err:#}");
193 None
194 },
195 }
196 }
197}
198
199#[async_trait]
200impl<P, S> Provider<SeqTypes, BlockRangesRequest> for LightClient<P, S>
201where
202 P: Storage,
203 S: Client,
204{
205 async fn fetch(&self, req: BlockRangesRequest) -> Option<BlockRangesResponse<SeqTypes>> {
206 let fetched = match req.0.as_slice() {
208 [range] => {
209 self.fetch_blocks_and_vid_common_in_range(range.start as usize, range.end as usize)
210 .await
211 },
212 _ => self.fetch_blocks_and_vid_common_for_ranges(&req.0).await,
213 };
214 match fetched {
215 Ok(fetched) => {
216 let (blocks, vid_common) = fetched.into_iter().unzip();
217 Some(BlockRangesResponse { blocks, vid_common })
218 },
219 Err(err) => {
220 tracing::warn!(?req, "failed to fetch block ranges: {err:#}");
221 None
222 },
223 }
224 }
225}
226
227#[async_trait]
228impl<P, S> Provider<SeqTypes, VidCommonRangesRequest> for LightClient<P, S>
229where
230 P: Storage,
231 S: Client,
232{
233 async fn fetch(
234 &self,
235 req: VidCommonRangesRequest,
236 ) -> Option<Vec<VidCommonQueryData<SeqTypes>>> {
237 let fetched = match req.0.as_slice() {
238 [range] => {
239 self.fetch_blocks_and_vid_common_in_range(range.start as usize, range.end as usize)
240 .await
241 },
242 _ => self.fetch_blocks_and_vid_common_for_ranges(&req.0).await,
243 };
244 match fetched {
245 Ok(fetched) => Some(fetched.into_iter().map(|(_, common)| common).collect()),
246 Err(err) => {
247 tracing::warn!(?req, "failed to fetch VID common ranges: {err:#}");
248 None
249 },
250 }
251 }
252}
253
254#[cfg(test)]
255mod test {
256 use hotshot_query_service::types::HeightIndexed;
257
258 use super::*;
259 use crate::{storage::SqlStorage, testing::TestClient};
260
261 #[tokio::test]
262 #[test_log::test]
263 async fn test_block_ranges_carry_vid_common() {
264 let client = TestClient::default();
265 let lc = LightClient::from_genesis(
266 SqlStorage::default().await.unwrap(),
267 client.clone(),
268 client.genesis().await,
269 );
270 for height in 1..8 {
271 client.payload(height).await;
272 }
273
274 let batch = Provider::<SeqTypes, BlockRangesRequest>::fetch(
275 &lc,
276 BlockRangesRequest(vec![1..3, 5..8]),
277 )
278 .await
279 .unwrap();
280
281 assert_eq!(
282 batch
283 .blocks
284 .iter()
285 .map(|block| block.height())
286 .collect::<Vec<_>>(),
287 [1, 2, 5, 6, 7]
288 );
289 assert_eq!(batch.vid_common.len(), batch.blocks.len());
290 for (block, common) in batch.blocks.iter().zip(&batch.vid_common) {
291 assert_eq!(common.height(), block.height());
292 assert_eq!(common.payload_hash(), block.payload_hash());
293 }
294 }
295}