iroh_base/
ticket.rs

1//! Tickets is a serializable object combining information required for an operation.
2//! Typically tickets contain all information required for an operation, e.g. an iroh blob
3//! ticket would contain the hash of the data as well as information about how to reach the
4//! provider.
5
6use std::{collections::BTreeSet, net::SocketAddr};
7
8use nested_enum_utils::common_fields;
9use serde::{Deserialize, Serialize};
10use snafu::{Backtrace, Snafu};
11
12use crate::{key::NodeId, relay_url::RelayUrl};
13
14mod node;
15
16pub use self::node::NodeTicket;
17
18/// A ticket is a serializable object combining information required for an operation.
19///
20/// Tickets support serialization to a string using base32 encoding. The kind of
21/// ticket will be prepended to the string to make it somewhat self describing.
22///
23/// Versioning is left to the implementer. Some kinds of tickets might need
24/// versioning, others might not.
25///
26/// The serialization format for converting the ticket from and to bytes is left
27/// to the implementer. We recommend using [postcard] for serialization.
28///
29/// [postcard]: https://docs.rs/postcard/latest/postcard/
30pub trait Ticket: Sized {
31    /// String prefix describing the kind of iroh ticket.
32    ///
33    /// This should be lower case ascii characters.
34    const KIND: &'static str;
35
36    /// Serialize to bytes used in the base32 string representation.
37    fn to_bytes(&self) -> Vec<u8>;
38
39    /// Deserialize from the base32 string representation bytes.
40    fn from_bytes(bytes: &[u8]) -> Result<Self, ParseError>;
41
42    /// Serialize to string.
43    fn serialize(&self) -> String {
44        let mut out = Self::KIND.to_string();
45        data_encoding::BASE32_NOPAD.encode_append(&self.to_bytes(), &mut out);
46        out.to_ascii_lowercase()
47    }
48
49    /// Deserialize from a string.
50    fn deserialize(str: &str) -> Result<Self, ParseError> {
51        let expected = Self::KIND;
52        let Some(rest) = str.strip_prefix(expected) else {
53            return Err(KindSnafu { expected }.build());
54        };
55        let bytes = data_encoding::BASE32_NOPAD.decode(rest.to_ascii_uppercase().as_bytes())?;
56        let ticket = Self::from_bytes(&bytes)?;
57        Ok(ticket)
58    }
59}
60
61/// An error deserializing an iroh ticket.
62#[common_fields({
63    backtrace: Option<Backtrace>,
64    #[snafu(implicit)]
65    span_trace: n0_snafu::SpanTrace,
66})]
67#[derive(Debug, Snafu)]
68#[allow(missing_docs)]
69#[snafu(visibility(pub(crate)))]
70#[non_exhaustive]
71pub enum ParseError {
72    /// Found a ticket with the wrong prefix, indicating the wrong kind.
73    #[snafu(display("wrong prefix, expected {expected}"))]
74    Kind {
75        /// The expected prefix.
76        expected: &'static str,
77    },
78    /// This looks like a ticket, but postcard deserialization failed.
79    #[snafu(transparent)]
80    Postcard { source: postcard::Error },
81    /// This looks like a ticket, but base32 decoding failed.
82    #[snafu(transparent)]
83    Encoding { source: data_encoding::DecodeError },
84    /// Verification of the deserialized bytes failed.
85    #[snafu(display("verification failed: {message}"))]
86    Verify { message: &'static str },
87}
88
89impl ParseError {
90    /// Returns a [`ParseError`] that indicates the given ticket has the wrong
91    /// prefix.
92    ///
93    /// Indicate the expected prefix.
94    pub fn wrong_prefix(expected: &'static str) -> Self {
95        KindSnafu { expected }.build()
96    }
97
98    /// Return a `ParseError` variant that indicates verification of the
99    /// deserialized bytes failed.
100    pub fn verification_failed(message: &'static str) -> Self {
101        VerifySnafu { message }.build()
102    }
103}
104
105#[derive(Serialize, Deserialize)]
106struct Variant0NodeAddr {
107    node_id: NodeId,
108    info: Variant0AddrInfo,
109}
110
111#[derive(Serialize, Deserialize)]
112struct Variant0AddrInfo {
113    relay_url: Option<RelayUrl>,
114    direct_addresses: BTreeSet<SocketAddr>,
115}