1use crate::node_client::{NodeClient, NodeClientExt};
5use crate::utils::AsyncJoinOnDrop;
6use ab_core_primitives::pieces::{Piece, PieceIndex};
7use ab_core_primitives::segments::{
8 SegmentIndex, SuperSegmentHeader, SuperSegmentIndex, SuperSegmentRoot,
9};
10use ab_farmer_rpc_primitives::{
11 BlockSealInfo, BlockSealResponse, FarmerAppInfo, FarmerShardMembershipInfo,
12 MAX_SUPER_SEGMENT_HEADERS_PER_REQUEST, SlotInfo, SolutionResponse,
13};
14use async_lock::{
15 Mutex as AsyncMutex, RwLock as AsyncRwLock,
16 RwLockUpgradableReadGuardArc as AsyncRwLockUpgradableReadGuard,
17 RwLockWriteGuardArc as AsyncRwLockWriteGuard,
18};
19use async_trait::async_trait;
20use futures::{FutureExt, Stream, StreamExt, select};
21use std::pin::Pin;
22use std::sync::Arc as StdArc;
23use std::time::{Duration, Instant};
24use tokio::sync::watch;
25use tokio_stream::wrappers::WatchStream;
26use tracing::{debug, info, trace, warn};
27use triomphe::Arc;
28
29const SEGMENT_HEADERS_SYNC_INTERVAL: Duration = Duration::from_secs(1);
30const FARMER_APP_INFO_DEDUPLICATION_WINDOW: Duration = Duration::from_secs(1);
31
32#[derive(Debug, Default)]
33struct SuperSegmentHeaders {
34 super_segment_headers: Vec<SuperSegmentHeader>,
35 last_synced: Option<Instant>,
36}
37
38impl SuperSegmentHeaders {
39 fn push(&mut self, new_segment_header: SuperSegmentHeader) {
42 if self.super_segment_headers.len() as u64 == u64::from(new_segment_header.index.as_inner())
43 {
44 self.super_segment_headers.push(new_segment_header);
45 }
46 }
47
48 fn get_super_segment_headers(
52 &self,
53 super_segment_indices: &[SuperSegmentIndex],
54 ) -> Vec<Option<SuperSegmentHeader>> {
55 super_segment_indices
56 .iter()
57 .map(|super_segment_index| {
58 self.super_segment_headers
59 .get(u64::from(*super_segment_index) as usize)
60 .copied()
61 })
62 .collect::<Vec<_>>()
63 }
64
65 fn last_super_segment_headers(&self, limit: u32) -> Vec<Option<SuperSegmentHeader>> {
67 self.super_segment_headers
68 .iter()
69 .rev()
70 .take(limit as usize)
71 .rev()
72 .copied()
73 .map(Some)
74 .collect()
75 }
76
77 fn super_segment_root_for_segment_index(
80 &self,
81 segment_index: SegmentIndex,
82 ) -> Option<SuperSegmentRoot> {
83 let index = self
84 .super_segment_headers
85 .binary_search_by_key(&segment_index, |super_segment_header| {
86 super_segment_header.max_segment_index.as_inner()
87 })
88 .unwrap_or_else(|insert_index| insert_index);
89
90 let super_segment_header = self.super_segment_headers.get(index).copied()?;
91
92 let max_segment_index = super_segment_header.max_segment_index.as_inner();
93 let first_segment_index = max_segment_index
94 - SegmentIndex::from(u64::from(super_segment_header.num_segments))
95 + SegmentIndex::ONE;
96
97 (first_segment_index..=max_segment_index)
98 .contains(&segment_index)
99 .then_some(super_segment_header.root)
100 }
101
102 fn is_sync_rate_limited(&self) -> bool {
104 self.last_synced
105 .is_some_and(|last_synced| last_synced.elapsed() < SEGMENT_HEADERS_SYNC_INTERVAL)
106 }
107
108 async fn request_uncached_headers<NC>(
115 &self,
116 client: &NC,
117 ) -> anyhow::Result<Vec<SuperSegmentHeader>>
118 where
119 NC: NodeClient,
120 {
121 let mut extra_super_segment_headers = Vec::new();
122 let mut super_segment_index_offset =
123 SuperSegmentIndex::from(self.super_segment_headers.len() as u64);
124 let segment_index_step =
125 SuperSegmentIndex::from(MAX_SUPER_SEGMENT_HEADERS_PER_REQUEST as u64);
126
127 'outer: loop {
128 let from = super_segment_index_offset;
129 let to = super_segment_index_offset + segment_index_step;
130 trace!(%from, %to, "Requesting super segment headers");
131
132 for maybe_super_segment_header in client
133 .super_segment_headers((from..to).collect::<Vec<_>>())
134 .await
135 .map_err(|error| {
136 anyhow::anyhow!(
137 "Failed to download super segment headers {from}..{to} from node: {error}"
138 )
139 })?
140 {
141 let Some(super_segment_header) = maybe_super_segment_header else {
142 break 'outer;
144 };
145
146 extra_super_segment_headers.push(super_segment_header);
147 }
148
149 super_segment_index_offset += segment_index_step;
150 }
151
152 Ok(extra_super_segment_headers)
153 }
154
155 fn write_cache(&mut self, extra_super_segment_headers: Vec<SuperSegmentHeader>) {
157 for super_segment_header in extra_super_segment_headers {
158 self.push(super_segment_header);
159 }
160 self.last_synced.replace(Instant::now());
161 }
162}
163
164#[derive(Debug, Clone)]
172pub struct CachingProxyNodeClient<NC> {
173 inner: NC,
174 slot_info_receiver: watch::Receiver<Option<SlotInfo>>,
175 new_super_segment_headers_receiver: watch::Receiver<Option<SuperSegmentHeader>>,
176 block_sealing_receiver: watch::Receiver<Option<BlockSealInfo>>,
177 super_segment_headers: StdArc<AsyncRwLock<SuperSegmentHeaders>>,
178 last_farmer_app_info: Arc<AsyncMutex<(FarmerAppInfo, Instant)>>,
179 _background_task: Arc<AsyncJoinOnDrop<()>>,
180}
181
182impl<NC> CachingProxyNodeClient<NC>
183where
184 NC: NodeClient + Clone,
185{
186 pub async fn new(client: NC) -> anyhow::Result<Self> {
188 let mut super_segment_headers = SuperSegmentHeaders::default();
189 let mut new_super_segments_notifications =
190 client.subscribe_new_super_segment_headers().await?;
191
192 info!("Downloading all super segment headers from node...");
193 let headers = super_segment_headers
195 .request_uncached_headers(&client)
196 .await?;
197 super_segment_headers.write_cache(headers);
198 info!("Downloaded all super segment headers from node successfully");
199
200 let super_segment_headers = StdArc::new(AsyncRwLock::new(super_segment_headers));
201
202 let (slot_info_sender, slot_info_receiver) = watch::channel(None::<SlotInfo>);
203 let slot_info_proxy_fut = {
204 let mut slot_info_subscription = client.subscribe_slot_info().await?;
205
206 async move {
207 let mut last_slot_number = None;
208 while let Some(slot_info) = slot_info_subscription.next().await {
209 if let Some(last_slot_number) = last_slot_number
210 && last_slot_number >= slot_info.slot
211 {
212 continue;
213 }
214 last_slot_number.replace(slot_info.slot);
215
216 if let Err(error) = slot_info_sender.send(Some(slot_info)) {
217 warn!(%error, "Failed to proxy slot info notification");
218 return;
219 }
220 }
221 }
222 };
223
224 let (new_super_segment_headers_sender, new_super_segment_headers_receiver) =
225 watch::channel(None::<SuperSegmentHeader>);
226 let super_segment_headers_maintenance_fut = {
227 let client = client.clone();
228 let super_segment_headers = StdArc::clone(&super_segment_headers);
229
230 async move {
231 let mut last_super_segment_index = None;
232 while let Some(new_segment_header) = new_super_segments_notifications.next().await {
233 let super_segment_index = new_segment_header.index;
234 trace!(?new_segment_header, "New super segment header notification");
235
236 if let Some(last_super_segment_index) = last_super_segment_index
237 && last_super_segment_index >= super_segment_index
238 {
239 continue;
240 }
241 last_super_segment_index.replace(super_segment_index);
242
243 loop {
247 let cached_count = super_segment_headers
248 .read()
249 .await
250 .super_segment_headers
251 .len() as u64;
252 if cached_count >= u64::from(super_segment_index.as_inner()) {
253 break;
254 }
255
256 let result = super_segment_headers
257 .read()
258 .await
259 .request_uncached_headers(&client)
260 .await;
261 match result {
262 Ok(extra_super_segment_headers) => {
263 if !extra_super_segment_headers.is_empty() {
264 super_segment_headers
265 .write()
266 .await
267 .write_cache(extra_super_segment_headers);
268 continue;
269 }
270
271 debug!(
272 %super_segment_index,
273 "Missing super segment headers are not available yet, \
274 retrying"
275 );
276 }
277 Err(error) => {
278 warn!(
279 %error,
280 %super_segment_index,
281 "Failed to download missing super segment headers, retrying"
282 );
283 }
284 }
285
286 tokio::time::sleep(SEGMENT_HEADERS_SYNC_INTERVAL).await;
287 }
288
289 super_segment_headers.write().await.push(new_segment_header);
291
292 if let Err(error) =
293 new_super_segment_headers_sender.send(Some(new_segment_header))
294 {
295 warn!(%error, "Failed to proxy new super segment header notification");
296 return;
297 }
298 }
299 }
300 };
301
302 let (block_sealing_sender, block_sealing_receiver) = watch::channel(None::<BlockSealInfo>);
303 let block_sealing_proxy_fut = {
304 let mut block_sealing_subscription = client.subscribe_block_sealing().await?;
305
306 async move {
307 while let Some(block_sealing_info) = block_sealing_subscription.next().await {
308 if let Err(error) = block_sealing_sender.send(Some(block_sealing_info)) {
309 warn!(%error, "Failed to proxy block sealing notification");
310 return;
311 }
312 }
313 }
314 };
315
316 let farmer_app_info = client
317 .farmer_app_info()
318 .await
319 .map_err(|error| anyhow::anyhow!("Failed to get farmer app info: {error}"))?;
320 let last_farmer_app_info = Arc::new(AsyncMutex::new((farmer_app_info, Instant::now())));
321
322 let background_task = tokio::spawn(async move {
323 select! {
324 () = slot_info_proxy_fut.fuse() => {},
325 () = super_segment_headers_maintenance_fut.fuse() => {},
326 () = block_sealing_proxy_fut.fuse() => {},
327 }
328 });
329
330 let node_client = Self {
331 inner: client,
332 slot_info_receiver,
333 new_super_segment_headers_receiver,
334 block_sealing_receiver,
335 super_segment_headers,
336 last_farmer_app_info,
337 _background_task: Arc::new(AsyncJoinOnDrop::new(background_task, true)),
338 };
339
340 Ok(node_client)
341 }
342}
343
344#[async_trait]
345impl<NC> NodeClient for CachingProxyNodeClient<NC>
346where
347 NC: NodeClient,
348{
349 async fn farmer_app_info(&self) -> anyhow::Result<FarmerAppInfo> {
350 let (last_farmer_app_info, last_farmer_app_info_request) =
351 &mut *self.last_farmer_app_info.lock().await;
352
353 if last_farmer_app_info_request.elapsed() > FARMER_APP_INFO_DEDUPLICATION_WINDOW {
354 let new_last_farmer_app_info = self.inner.farmer_app_info().await?;
355
356 *last_farmer_app_info = new_last_farmer_app_info;
357 *last_farmer_app_info_request = Instant::now();
358 }
359
360 Ok(last_farmer_app_info.clone())
361 }
362
363 async fn subscribe_slot_info(
364 &self,
365 ) -> anyhow::Result<Pin<Box<dyn Stream<Item = SlotInfo> + Send + 'static>>> {
366 Ok(Box::pin(
367 WatchStream::new(self.slot_info_receiver.clone())
368 .filter_map(|maybe_slot_info| async move { maybe_slot_info }),
369 ))
370 }
371
372 async fn submit_solution_response(
373 &self,
374 solution_response: SolutionResponse,
375 ) -> anyhow::Result<()> {
376 self.inner.submit_solution_response(solution_response).await
377 }
378
379 async fn subscribe_block_sealing(
380 &self,
381 ) -> anyhow::Result<Pin<Box<dyn Stream<Item = BlockSealInfo> + Send + 'static>>> {
382 Ok(Box::pin(
383 WatchStream::new(self.block_sealing_receiver.clone())
384 .filter_map(|maybe_block_sealing_info| async move { maybe_block_sealing_info }),
385 ))
386 }
387
388 async fn submit_block_seal(&self, block_seal: BlockSealResponse) -> anyhow::Result<()> {
389 self.inner.submit_block_seal(block_seal).await
390 }
391
392 async fn subscribe_new_super_segment_headers(
393 &self,
394 ) -> anyhow::Result<Pin<Box<dyn Stream<Item = SuperSegmentHeader> + Send + 'static>>> {
395 Ok(Box::pin(
396 WatchStream::new(self.new_super_segment_headers_receiver.clone())
397 .filter_map(|maybe_super_segment_header| async move { maybe_super_segment_header }),
398 ))
399 }
400
401 async fn super_segment_headers(
402 &self,
403 super_segment_indices: Vec<SuperSegmentIndex>,
404 ) -> anyhow::Result<Vec<Option<SuperSegmentHeader>>> {
405 let retrieved_super_segment_headers = self
406 .super_segment_headers
407 .read()
408 .await
409 .get_super_segment_headers(&super_segment_indices);
410
411 if retrieved_super_segment_headers.iter().all(Option::is_some) {
412 return Ok(retrieved_super_segment_headers);
413 }
414
415 let super_segment_headers = self.super_segment_headers.upgradable_read_arc().await;
423
424 let retrieved_super_segment_headers =
427 super_segment_headers.get_super_segment_headers(&super_segment_indices);
428 if retrieved_super_segment_headers.iter().all(Option::is_some) {
429 return Ok(retrieved_super_segment_headers);
430 }
431
432 if super_segment_headers.is_sync_rate_limited() {
434 return Ok(retrieved_super_segment_headers);
435 }
436
437 let extra_super_segment_headers = super_segment_headers
439 .request_uncached_headers(&self.inner)
440 .await?;
441
442 if extra_super_segment_headers.is_empty() {
443 return Ok(retrieved_super_segment_headers);
445 }
446
447 let mut super_segment_headers =
449 AsyncRwLockUpgradableReadGuard::upgrade(super_segment_headers).await;
450 super_segment_headers.write_cache(extra_super_segment_headers);
451
452 Ok(AsyncRwLockWriteGuard::downgrade(super_segment_headers)
455 .get_super_segment_headers(&super_segment_indices))
456 }
457
458 async fn super_segment_root_for_segment_index(
459 &self,
460 segment_index: SegmentIndex,
461 ) -> anyhow::Result<Option<SuperSegmentRoot>> {
462 Ok(self
463 .super_segment_headers
464 .read()
465 .await
466 .super_segment_root_for_segment_index(segment_index))
467 }
468
469 async fn piece(&self, piece_index: PieceIndex) -> anyhow::Result<Option<Piece>> {
470 self.inner.piece(piece_index).await
471 }
472
473 async fn update_shard_membership_info(
474 &self,
475 info: FarmerShardMembershipInfo,
476 ) -> anyhow::Result<()> {
477 self.inner.update_shard_membership_info(info).await
478 }
479}
480
481#[async_trait]
482impl<NC> NodeClientExt for CachingProxyNodeClient<NC>
483where
484 NC: NodeClientExt,
485{
486 async fn cached_super_segment_headers(
487 &self,
488 super_segment_indices: Vec<SuperSegmentIndex>,
489 ) -> anyhow::Result<Vec<Option<SuperSegmentHeader>>> {
490 Ok(self
493 .super_segment_headers
494 .read()
495 .await
496 .get_super_segment_headers(&super_segment_indices))
497 }
498
499 async fn last_super_segment_headers(
500 &self,
501 limit: u32,
502 ) -> anyhow::Result<Vec<Option<SuperSegmentHeader>>> {
503 Ok(self
504 .super_segment_headers
505 .read()
506 .await
507 .last_super_segment_headers(limit))
508 }
509}