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 serde::{Deserialize, Serialize};
9
10use crate::{key::NodeId, relay_url::RelayUrl};
11
12mod node;
13
14pub use self::node::NodeTicket;
15
16/// A ticket is a serializable object combining information required for an operation.
17///
18/// Tickets support serialization to a string using base32 encoding. The kind of
19/// ticket will be prepended to the string to make it somewhat self describing.
20///
21/// Versioning is left to the implementer. Some kinds of tickets might need
22/// versioning, others might not.
23///
24/// The serialization format for converting the ticket from and to bytes is left
25/// to the implementer. We recommend using [postcard] for serialization.
26///
27/// [postcard]: https://docs.rs/postcard/latest/postcard/
28pub trait Ticket: Sized {
29 /// String prefix describing the kind of iroh ticket.
30 ///
31 /// This should be lower case ascii characters.
32 const KIND: &'static str;
33
34 /// Serialize to bytes used in the base32 string representation.
35 fn to_bytes(&self) -> Vec<u8>;
36
37 /// Deserialize from the base32 string representation bytes.
38 fn from_bytes(bytes: &[u8]) -> Result<Self, Error>;
39
40 /// Serialize to string.
41 fn serialize(&self) -> String {
42 let mut out = Self::KIND.to_string();
43 data_encoding::BASE32_NOPAD.encode_append(&self.to_bytes(), &mut out);
44 out.to_ascii_lowercase()
45 }
46
47 /// Deserialize from a string.
48 fn deserialize(str: &str) -> Result<Self, Error> {
49 let expected = Self::KIND;
50 let Some(rest) = str.strip_prefix(expected) else {
51 return Err(Error::Kind { expected });
52 };
53 let bytes = data_encoding::BASE32_NOPAD.decode(rest.to_ascii_uppercase().as_bytes())?;
54 let ticket = Self::from_bytes(&bytes)?;
55 Ok(ticket)
56 }
57}
58
59/// An error deserializing an iroh ticket.
60#[derive(Debug, thiserror::Error)]
61pub enum Error {
62 /// Found a ticket of with the wrong prefix, indicating the wrong kind.
63 #[error("wrong prefix, expected {expected}")]
64 Kind {
65 /// The expected prefix.
66 expected: &'static str,
67 },
68 /// This looks like a ticket, but postcard deserialization failed.
69 #[error("deserialization failed: {_0}")]
70 Postcard(#[from] postcard::Error),
71 /// This looks like a ticket, but base32 decoding failed.
72 #[error("decoding failed: {_0}")]
73 Encoding(#[from] data_encoding::DecodeError),
74 /// Verification of the deserialized bytes failed.
75 #[error("verification failed: {_0}")]
76 Verify(&'static str),
77}
78
79#[derive(Serialize, Deserialize)]
80struct Variant0NodeAddr {
81 node_id: NodeId,
82 info: Variant0AddrInfo,
83}
84
85#[derive(Serialize, Deserialize)]
86struct Variant0AddrInfo {
87 relay_url: Option<RelayUrl>,
88 direct_addresses: BTreeSet<SocketAddr>,
89}