1use std::str::FromStr;
4
5use serde::{Deserialize, Serialize};
6
7use super::{Variant0AddrInfo, Variant0NodeAddr};
8use crate::{
9 node_addr::NodeAddr,
10 ticket::{self, ParseError, Ticket},
11};
12
13#[derive(Debug, Clone, PartialEq, Eq, derive_more::Display)]
32#[display("{}", Ticket::serialize(self))]
33pub struct NodeTicket {
34 node: NodeAddr,
35}
36
37#[derive(Serialize, Deserialize)]
39enum TicketWireFormat {
40 Variant0(Variant0NodeTicket),
41}
42
43#[derive(Serialize, Deserialize)]
45struct Variant0NodeTicket {
46 node: Variant0NodeAddr,
47}
48
49impl Ticket for NodeTicket {
50 const KIND: &'static str = "node";
51
52 fn to_bytes(&self) -> Vec<u8> {
53 let data = TicketWireFormat::Variant0(Variant0NodeTicket {
54 node: Variant0NodeAddr {
55 node_id: self.node.node_id,
56 info: Variant0AddrInfo {
57 relay_url: self.node.relay_url.clone(),
58 direct_addresses: self.node.direct_addresses.clone(),
59 },
60 },
61 });
62 postcard::to_stdvec(&data).expect("postcard serialization failed")
63 }
64
65 fn from_bytes(bytes: &[u8]) -> Result<Self, ParseError> {
66 let res: TicketWireFormat = postcard::from_bytes(bytes)?;
67 let TicketWireFormat::Variant0(Variant0NodeTicket { node }) = res;
68 Ok(Self {
69 node: NodeAddr {
70 node_id: node.node_id,
71 relay_url: node.info.relay_url,
72 direct_addresses: node.info.direct_addresses,
73 },
74 })
75 }
76}
77
78impl FromStr for NodeTicket {
79 type Err = ParseError;
80
81 fn from_str(s: &str) -> Result<Self, Self::Err> {
82 ticket::Ticket::deserialize(s)
83 }
84}
85
86impl NodeTicket {
87 pub fn new(node: NodeAddr) -> Self {
89 Self { node }
90 }
91
92 pub fn node_addr(&self) -> &NodeAddr {
94 &self.node
95 }
96}
97
98impl From<NodeAddr> for NodeTicket {
99 fn from(addr: NodeAddr) -> Self {
101 Self { node: addr }
102 }
103}
104
105impl From<NodeTicket> for NodeAddr {
106 fn from(ticket: NodeTicket) -> Self {
108 ticket.node
109 }
110}
111
112impl Serialize for NodeTicket {
113 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
114 if serializer.is_human_readable() {
115 serializer.serialize_str(&self.to_string())
116 } else {
117 let NodeTicket { node } = self;
118 (node).serialize(serializer)
119 }
120 }
121}
122
123impl<'de> Deserialize<'de> for NodeTicket {
124 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
125 if deserializer.is_human_readable() {
126 let s = String::deserialize(deserializer)?;
127 Self::from_str(&s).map_err(serde::de::Error::custom)
128 } else {
129 let peer = Deserialize::deserialize(deserializer)?;
130 Ok(Self::new(peer))
131 }
132 }
133}
134
135#[cfg(test)]
136mod tests {
137 use std::net::{Ipv4Addr, SocketAddr};
138
139 use data_encoding::HEXLOWER;
140 use rand::SeedableRng;
141
142 use super::*;
143 use crate::key::{PublicKey, SecretKey};
144
145 fn make_ticket() -> NodeTicket {
146 let mut rng = rand_chacha::ChaCha8Rng::seed_from_u64(0u64);
147 let peer = SecretKey::generate(&mut rng).public();
148 let addr = SocketAddr::from((Ipv4Addr::LOCALHOST, 1234));
149 let relay_url = None;
150 NodeTicket {
151 node: NodeAddr::from_parts(peer, relay_url, [addr]),
152 }
153 }
154
155 #[test]
156 fn test_ticket_postcard() {
157 let ticket = make_ticket();
158 let bytes = postcard::to_stdvec(&ticket).unwrap();
159 let ticket2: NodeTicket = postcard::from_bytes(&bytes).unwrap();
160 assert_eq!(ticket2, ticket);
161 }
162
163 #[test]
164 fn test_ticket_json() {
165 let ticket = make_ticket();
166 let json = serde_json::to_string(&ticket).unwrap();
167 let ticket2: NodeTicket = serde_json::from_str(&json).unwrap();
168 assert_eq!(ticket2, ticket);
169 }
170
171 #[test]
172 fn test_ticket_base32() {
173 let node_id =
174 PublicKey::from_str("ae58ff8833241ac82d6ff7611046ed67b5072d142c588d0063e942d9a75502b6")
175 .unwrap();
176
177 let ticket = NodeTicket {
178 node: NodeAddr::from_parts(
179 node_id,
180 Some("http://derp.me./".parse().unwrap()),
181 ["127.0.0.1:1024".parse().unwrap()],
182 ),
183 };
184 let base32 = data_encoding::BASE32_NOPAD
185 .decode(
186 ticket
187 .to_string()
188 .strip_prefix("node")
189 .unwrap()
190 .to_ascii_uppercase()
191 .as_bytes(),
192 )
193 .unwrap();
194 let expected = [
195 "00",
197 "ae58ff8833241ac82d6ff7611046ed67b5072d142c588d0063e942d9a75502b6",
199 "01",
201 "10",
203 "687474703a2f2f646572702e6d652e2f",
204 "01",
206 "00",
208 "7f0000018008",
210 ];
211 let expected = HEXLOWER.decode(expected.concat().as_bytes()).unwrap();
212 assert_eq!(base32, expected);
213 }
214}