Skip to main content

urma_node_client/client/
overlay.rs

1//! Overlay reads. Book entries and omissions are operator data: routing hints
2//! and the operator's own scan findings, not evidence about any peer.
3use super::{Client, OPERATION_TIMEOUT, invalid, remaining};
4use crate::{
5    Error,
6    model::{BookEntry, Omission},
7};
8use serde::de::DeserializeOwned;
9use std::time::Instant;
10
11const MAX_OMISSIONS: usize = 1024;
12const MAX_BOOK: usize = 1024;
13const MAX_ADDRESSES: usize = 4;
14
15impl Client {
16    /// The newest (highest) omissions recorded by the operator's scans, at
17    /// most 1024, ascending by height. HTTP 404 (a daemon without the overlay
18    /// routes) is `Error::Invalid("overlay disabled")`.
19    pub fn overlay_omissions(&self) -> Result<Vec<Omission>, Error> {
20        let omissions: Vec<Omission> = self.overlay_read("/v1/overlay/omissions", "omissions")?;
21        if omissions.len() > MAX_OMISSIONS {
22            return Err(invalid("local overlay omissions exceed 1024 entries"));
23        }
24        if omissions
25            .windows(2)
26            .any(|pair| pair[0].block.height > pair[1].block.height)
27        {
28            return Err(invalid("local overlay omissions are not ordered by height"));
29        }
30        Ok(omissions)
31    }
32
33    /// The operator's peer book: untrusted routing hints, at most 1024
34    /// entries of one to four addresses. HTTP 404 (discovery not configured)
35    /// is `Error::Invalid("overlay disabled")`.
36    pub fn overlay_book(&self) -> Result<Vec<BookEntry>, Error> {
37        let book: Vec<BookEntry> = self.overlay_read("/v1/overlay/book", "book")?;
38        if book.len() > MAX_BOOK {
39            return Err(invalid("local overlay book exceeds 1024 entries"));
40        }
41        if book
42            .iter()
43            .any(|entry| entry.addrs.is_empty() || entry.addrs.len() > MAX_ADDRESSES)
44        {
45            return Err(invalid(
46                "local overlay book entry needs one to four addresses",
47            ));
48        }
49        Ok(book)
50    }
51
52    fn overlay_read<T: DeserializeOwned>(&self, path: &str, what: &str) -> Result<T, Error> {
53        let deadline = Instant::now() + OPERATION_TIMEOUT;
54        let (code, body) = self.request("GET", path, deadline)?;
55        match code {
56            200 => {
57                let value = serde_json::from_slice(&body)?;
58                remaining(deadline)?;
59                Ok(value)
60            }
61            404 => {
62                let _: serde_json::Value = serde_json::from_slice(&body)?;
63                remaining(deadline)?;
64                Err(invalid("overlay disabled"))
65            }
66            _ => Err(invalid(format!("local overlay {what} HTTP {code}"))),
67        }
68    }
69}