avail_rust_client/subscription/
sub.rs1use super::should_retry;
2use crate::{BlockInfo, Client, H256, RpcError, platform::sleep};
3use std::time::Duration;
4
5#[doc = include_str!("../../examples/sub_doc.rs")]
36#[derive(Clone)]
38pub enum Sub {
39 UnInit(UnInitSub),
40 BestBlock(BestBlockSub),
41 FinalizedBlock(FinalizedBlockSub),
42}
43
44impl Sub {
45 pub fn new(client: Client) -> Self {
47 Self::UnInit(UnInitSub::new(client))
48 }
49
50 pub async fn next(&mut self) -> Result<BlockInfo, RpcError> {
61 if let Self::UnInit(u) = self {
62 let concrete = u.build().await?;
63 *self = concrete;
64 };
65
66 match self {
67 Self::BestBlock(s) => s.next().await,
68 Self::FinalizedBlock(s) => s.next().await,
69 _ => unreachable!("We cannot be here."),
70 }
71 }
72
73 pub async fn prev(&mut self) -> Result<BlockInfo, RpcError> {
82 if let Self::UnInit(u) = self {
83 let concrete = u.build().await?;
84 *self = concrete;
85 };
86
87 match self {
88 Self::BestBlock(s) => s.prev().await,
89 Self::FinalizedBlock(s) => s.prev().await,
90 _ => unreachable!("We cannot be here."),
91 }
92 }
93
94 pub fn should_retry_on_error(&self) -> bool {
99 let value = match self {
100 Self::UnInit(u) => u.retry_on_error,
101 Self::BestBlock(s) => s.retry_on_error,
102 Self::FinalizedBlock(s) => s.retry_on_error,
103 };
104
105 should_retry(self.client_ref(), value)
106 }
107
108 pub fn use_best_block(&mut self, value: bool) {
116 if let Self::UnInit(u) = self {
117 u.use_best_block = value;
118 }
119 }
120
121 pub fn set_block_height(&mut self, value: u32) {
126 match self {
127 Self::UnInit(u) => u.block_height = Some(value),
128 Self::BestBlock(x) => {
129 x.current_block_height = value;
130 x.block_processed.clear();
131 },
132 Self::FinalizedBlock(x) => {
133 x.next_block_height = value;
134 x.processed_previous_block = false;
135 },
136 }
137 }
138
139 pub fn set_pool_rate(&mut self, value: Duration) {
144 match self {
145 Self::UnInit(u) => u.poll_rate = value,
146 Self::BestBlock(x) => x.poll_rate = value,
147 Self::FinalizedBlock(x) => x.poll_rate = value,
148 }
149 }
150
151 pub fn set_retry_on_error(&mut self, value: Option<bool>) {
157 match self {
158 Self::UnInit(u) => u.retry_on_error = value,
159 Self::BestBlock(x) => x.retry_on_error = value,
160 Self::FinalizedBlock(x) => x.retry_on_error = value,
161 }
162 }
163
164 pub(crate) fn client_ref(&self) -> &Client {
165 match self {
166 Sub::UnInit(x) => &x.client,
167 Sub::BestBlock(x) => &x.client,
168 Sub::FinalizedBlock(x) => &x.client,
169 }
170 }
171
172 #[cfg(test)]
173 pub(crate) fn as_finalized(&self) -> &FinalizedBlockSub {
174 if let Self::FinalizedBlock(f) = self {
175 return f;
176 }
177 panic!("Not Finalized Sub");
178 }
179}
180
181#[derive(Clone)]
185pub struct UnInitSub {
186 client: Client,
187 use_best_block: bool,
188 block_height: Option<u32>,
189 poll_rate: Duration,
190 retry_on_error: Option<bool>,
191}
192
193impl UnInitSub {
194 pub fn new(client: Client) -> Self {
196 Self {
197 client,
198 use_best_block: false,
199 block_height: Default::default(),
200 poll_rate: Duration::from_secs(3),
201 retry_on_error: None,
202 }
203 }
204
205 pub async fn build(&self) -> Result<Sub, RpcError> {
212 let block_height = match self.block_height {
213 Some(x) => x,
214 None => match self.use_best_block {
215 true => self.client.best().block_height().await?,
216 false => self.client.finalized().block_height().await?,
217 },
218 };
219
220 let sub = match self.use_best_block {
221 true => Sub::BestBlock(BestBlockSub {
222 client: self.client.clone(),
223 poll_rate: self.poll_rate,
224 current_block_height: block_height,
225 block_processed: Vec::new(),
226 retry_on_error: self.retry_on_error,
227 latest_finalized_height: None,
228 }),
229 false => Sub::FinalizedBlock(FinalizedBlockSub {
230 client: self.client.clone(),
231 poll_rate: self.poll_rate,
232 next_block_height: block_height,
233 retry_on_error: self.retry_on_error,
234 latest_finalized_height: None,
235 processed_previous_block: false,
236 }),
237 };
238
239 Ok(sub)
240 }
241}
242
243#[derive(Clone)]
247pub struct FinalizedBlockSub {
248 client: Client,
249 poll_rate: Duration,
250 pub(crate) next_block_height: u32,
251 retry_on_error: Option<bool>,
252 latest_finalized_height: Option<u32>,
253 processed_previous_block: bool,
254}
255
256impl FinalizedBlockSub {
257 pub async fn next(&mut self) -> Result<BlockInfo, RpcError> {
263 let latest_finalized_height = self.fetch_latest_finalized_height().await?;
264
265 let result = if latest_finalized_height > self.next_block_height {
266 self.run_historical().await?
267 } else {
268 self.run_head().await?
269 };
270
271 self.next_block_height = result.height + 1;
272 self.processed_previous_block = true;
273 Ok(result)
274 }
275
276 pub async fn prev(&mut self) -> Result<BlockInfo, RpcError> {
281 self.next_block_height = self.next_block_height.saturating_sub(1);
282 if self.processed_previous_block {
283 self.next_block_height = self.next_block_height.saturating_sub(1);
284 }
285 self.processed_previous_block = false;
286
287 self.next().await
288 }
289
290 async fn fetch_latest_finalized_height(&mut self) -> Result<u32, RpcError> {
292 if let Some(height) = self.latest_finalized_height.as_ref() {
293 return Ok(*height);
294 }
295
296 let retry_on_error = Some(should_retry(&self.client, self.retry_on_error));
297 let latest_finalized_height = self.client.finalized().retry_on(retry_on_error).block_height().await?;
298 self.latest_finalized_height = Some(latest_finalized_height);
299 Ok(latest_finalized_height)
300 }
301
302 async fn run_historical(&mut self) -> Result<BlockInfo, RpcError> {
304 let height = self.next_block_height;
305 let hash = self
306 .client
307 .chain()
308 .retry_on(self.retry_on_error, None)
309 .block_hash(Some(height))
310 .await?;
311 let hash = hash.ok_or(RpcError::ExpectedData("Expected to fetch block hash".into()))?;
312
313 Ok(BlockInfo { hash, height })
314 }
315
316 async fn run_head(&mut self) -> Result<BlockInfo, RpcError> {
319 loop {
320 let head = self.client.finalized().block_info().await?;
321
322 let is_past_block = self.next_block_height > head.height;
323 if is_past_block {
324 sleep(self.poll_rate).await;
325 continue;
326 }
327
328 if self.next_block_height == head.height {
329 return Ok(head);
330 }
331
332 let height = self.next_block_height;
333 let hash = self
334 .client
335 .chain()
336 .retry_on(self.retry_on_error, Some(true))
337 .block_hash(Some(height))
338 .await?;
339 let hash = hash.ok_or(RpcError::ExpectedData("Expected to fetch block hash".into()))?;
340
341 return Ok(BlockInfo { hash, height });
342 }
343 }
344}
345
346#[derive(Clone)]
350pub struct BestBlockSub {
351 client: Client,
352 poll_rate: Duration,
353 pub(crate) current_block_height: u32,
354 block_processed: Vec<H256>,
355 retry_on_error: Option<bool>,
356 latest_finalized_height: Option<u32>,
357}
358
359impl BestBlockSub {
360 pub async fn next(&mut self) -> Result<BlockInfo, RpcError> {
366 let latest_finalized_height = self.fetch_latest_finalized_height().await?;
367
368 if latest_finalized_height > self.current_block_height {
370 let info = self.run_historical().await?;
371 self.block_processed.clear();
372 self.block_processed.push(info.hash);
373 self.current_block_height = info.height;
374 return Ok(info);
375 }
376
377 let info = self.run_head().await?;
378 if info.height == self.current_block_height {
379 self.block_processed.push(info.hash);
380 } else {
381 self.block_processed.clear();
382 self.block_processed.push(info.hash);
383 self.current_block_height = info.height;
384 }
385
386 Ok(info)
387 }
388
389 pub async fn prev(&mut self) -> Result<BlockInfo, RpcError> {
394 self.current_block_height = self.current_block_height.saturating_sub(1);
395 self.block_processed.clear();
396 self.next().await
397 }
398
399 async fn fetch_latest_finalized_height(&mut self) -> Result<u32, RpcError> {
401 if let Some(height) = self.latest_finalized_height.as_ref() {
402 return Ok(*height);
403 }
404
405 let latest_finalized_height = self
406 .client
407 .finalized()
408 .retry_on(self.retry_on_error)
409 .block_height()
410 .await?;
411 self.latest_finalized_height = Some(latest_finalized_height);
412 Ok(latest_finalized_height)
413 }
414
415 async fn run_historical(&mut self) -> Result<BlockInfo, RpcError> {
417 let mut height = self.current_block_height;
418 if !self.block_processed.is_empty() {
419 height += 1;
420 }
421
422 let hash = self
423 .client
424 .chain()
425 .retry_on(self.retry_on_error, None)
426 .block_hash(Some(height))
427 .await?;
428 let hash = hash.ok_or(RpcError::ExpectedData("Expected to fetch block hash".into()))?;
429
430 Ok(BlockInfo { hash, height })
431 }
432
433 async fn run_head(&mut self) -> Result<BlockInfo, RpcError> {
434 loop {
435 let head = self.client.best().retry_on(self.retry_on_error).block_info().await?;
436
437 let is_past_block = self.current_block_height > head.height;
438 let block_already_processed = self.block_processed.contains(&head.hash);
439 if is_past_block || block_already_processed {
440 sleep(self.poll_rate).await;
441 continue;
442 }
443
444 let no_block_processed_yet = self.block_processed.is_empty();
445 if no_block_processed_yet {
446 let hash = self
447 .client
448 .chain()
449 .retry_on(self.retry_on_error, Some(true))
450 .block_hash(Some(self.current_block_height))
451 .await?;
452 let hash = hash.ok_or(RpcError::ExpectedData("Expected to fetch block hash".into()))?;
453
454 return Ok(BlockInfo { hash, height: self.current_block_height });
455 }
456
457 let is_current_block = self.current_block_height == head.height;
458 let is_next_block = self.current_block_height + 1 == head.height;
459 if is_current_block || is_next_block {
460 return Ok(head);
461 }
462
463 let height = self.current_block_height + 1;
464 let hash = self
465 .client
466 .chain()
467 .retry_on(Some(true), Some(true))
468 .block_hash(Some(height))
469 .await?;
470 let hash = hash.ok_or(RpcError::ExpectedData("Expected to fetch block hash".into()))?;
471
472 return Ok(BlockInfo { hash, height });
473 }
474 }
475}
476
477#[cfg(test)]
478mod tests {
479 use super::*;
480 use crate::{error::Error, prelude::*};
481
482 #[tokio::test]
483 async fn sub_test() -> Result<(), Error> {
484 let client = Client::new(TURING_ENDPOINT).await?;
485 let mut sub = Sub::new(client.clone());
486
487 client.set_global_retries_enabled(true);
491 assert_eq!(sub.should_retry_on_error(), true);
492
493 client.set_global_retries_enabled(false);
494 assert_eq!(sub.should_retry_on_error(), false);
495
496 sub.set_retry_on_error(Some(false));
500
501 client.set_global_retries_enabled(true);
502 assert_eq!(sub.should_retry_on_error(), false);
503
504 client.set_global_retries_enabled(false);
505 assert_eq!(sub.should_retry_on_error(), false);
506
507 sub.set_retry_on_error(Some(true));
511
512 client.set_global_retries_enabled(true);
513 assert_eq!(sub.should_retry_on_error(), true);
514
515 client.set_global_retries_enabled(false);
516 assert_eq!(sub.should_retry_on_error(), true);
517
518 Ok(())
519 }
520
521 #[tokio::test]
523 async fn best_sub_test() -> Result<(), Error> {
524 let client = Client::new(TURING_ENDPOINT).await?;
525
526 let mut sub = Sub::new(client.clone());
530 sub.use_best_block(true);
531
532 let block_height = client.best().block_height().await?;
533 let value = sub.next().await?;
534 assert_eq!(value.height, block_height);
535
536 let mut sub = Sub::new(client.clone());
540 sub.use_best_block(true);
541
542 let block_height = client.best().block_height().await?;
543 let value = sub.prev().await?;
544 assert_eq!(value.height, block_height - 1);
545
546 let block_height = 1900000u32;
550 let mut sub = Sub::new(client.clone());
551 sub.use_best_block(true);
552 sub.set_block_height(block_height);
553 for i in 0..3 {
554 let value = sub.next().await?;
555 assert_eq!(value.height, block_height + i);
556 }
557
558 let block_height = 1900000u32;
562 let mut sub = Sub::new(client.clone());
563 sub.use_best_block(true);
564 sub.set_block_height(block_height);
565 for i in 0..3 {
566 let value = sub.prev().await?;
567 assert_eq!(value.height, block_height - i - 1);
568 }
569
570 let block_height = 1900000u32;
574 let mut sub = Sub::new(client.clone());
575 sub.use_best_block(true);
576 sub.set_block_height(block_height);
577
578 let value = sub.next().await?;
579 assert_eq!(value.height, block_height);
580
581 let value = sub.prev().await?;
582 assert_eq!(value.height, block_height - 1);
583
584 let block_height = 1900000u32;
588 let mut sub = Sub::new(client.clone());
589 sub.use_best_block(true);
590 sub.set_block_height(block_height);
591
592 let value = sub.prev().await?;
593 assert_eq!(value.height, block_height - 1);
594
595 let value = sub.next().await?;
596 assert_eq!(value.height, block_height);
597
598 Ok(())
599 }
600
601 #[tokio::test]
603 async fn finalized_sub_test() -> Result<(), Error> {
604 let client = Client::new(TURING_ENDPOINT).await?;
605
606 let mut sub = Sub::new(client.clone());
610
611 let block_height = client.finalized().block_height().await?;
612 let value = sub.next().await?;
613 assert_eq!(value.height, block_height);
614
615 let mut sub = Sub::new(client.clone());
619
620 let block_height = client.finalized().block_height().await?;
621 let value = sub.prev().await?;
622 assert_eq!(value.height, block_height - 1);
623
624 let block_height = 1900000u32;
628 let mut sub = Sub::new(client.clone());
629 sub.set_block_height(block_height);
630 for i in 0..3 {
631 let value = sub.next().await?;
632 assert_eq!(value.height, block_height + i);
633 }
634
635 let block_height = 1900000u32;
639 let mut sub = Sub::new(client.clone());
640 sub.set_block_height(block_height);
641 for i in 0..3 {
642 let value = sub.prev().await?;
643 assert_eq!(value.height, block_height - i - 1);
644 }
645
646 let block_height = 1900000u32;
650 let mut sub = Sub::new(client.clone());
651 sub.set_block_height(block_height);
652
653 let value = sub.next().await?;
654 assert_eq!(value.height, block_height);
655
656 let value = sub.prev().await?;
657 assert_eq!(value.height, block_height - 1);
658
659 let block_height = 1900000u32;
663 let mut sub = Sub::new(client.clone());
664 sub.set_block_height(block_height);
665
666 let value = sub.prev().await?;
667 assert_eq!(value.height, block_height - 1);
668
669 let value = sub.next().await?;
670 assert_eq!(value.height, block_height);
671
672 Ok(())
673 }
674}