tor_netdoc/types/
family.rs1use crate::types::misc::LongIdent;
7use crate::{Error, NetdocErrorKind, NormalItemArgument, Pos, Result};
8use base64ct::Encoding;
9use derive_deftly::Deftly;
10use tor_basic_utils::derive_deftly_template_GloballyInternable;
11use tor_basic_utils::intern::{GloballyInternable, Intern};
12use tor_llcrypto::pk::ed25519::{ED25519_ID_LEN, Ed25519Identity};
13use tor_llcrypto::pk::rsa::RsaIdentity;
14
15#[derive(Clone, Debug, Default, Hash, Eq, PartialEq, Deftly)]
35#[derive_deftly(ItemValueEncodable, ItemValueParseable, GloballyInternable)]
36pub struct RelayFamily(Vec<LongIdent>);
37
38impl RelayFamily {
39 pub fn new() -> Self {
41 RelayFamily::default()
42 }
43
44 pub fn push(&mut self, rsa_id: RsaIdentity) {
46 self.0.push(rsa_id.into());
47 }
48
49 fn normalize(&mut self) {
51 self.0.sort_by_key(|v| v.0);
52 self.0.dedup();
53 }
54
55 pub fn intern(mut self) -> Intern<Self> {
58 self.normalize();
59 Self::into_intern(self)
60 }
61
62 pub fn contains(&self, rsa_id: &RsaIdentity) -> bool {
64 self.0.contains(&LongIdent(*rsa_id))
65 }
66
67 pub fn members(&self) -> impl Iterator<Item = &RsaIdentity> {
70 self.0.iter().map(|id| &id.0)
71 }
72
73 pub fn is_empty(&self) -> bool {
75 self.0.is_empty()
76 }
77}
78
79impl std::str::FromStr for RelayFamily {
80 type Err = Error;
81 fn from_str(s: &str) -> Result<Self> {
82 let v: Result<Vec<LongIdent>> = s
83 .split(crate::parse::tokenize::is_sp)
84 .map(|e| e.parse::<LongIdent>())
85 .filter(Result::is_ok)
86 .collect();
87 Ok(RelayFamily(v?))
88 }
89}
90
91#[derive(Clone, Debug, Eq, PartialEq)]
99#[non_exhaustive]
100pub enum RelayFamilyId {
101 Ed25519(Ed25519Identity),
103 Unrecognized(String),
105}
106
107const ED25519_ID_PREFIX: &str = "ed25519:";
109
110impl std::str::FromStr for RelayFamilyId {
111 type Err = Error;
112
113 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
114 let mut buf = [0_u8; ED25519_ID_LEN];
115 if let Some(s) = s.strip_prefix(ED25519_ID_PREFIX) {
116 if let Ok(decoded) = base64ct::Base64Unpadded::decode(s, &mut buf) {
117 if let Some(ed_id) = Ed25519Identity::from_bytes(decoded) {
118 return Ok(RelayFamilyId::Ed25519(ed_id));
119 }
120 }
121 return Err(NetdocErrorKind::BadArgument
122 .with_msg("Invalid ed25519 family ID")
123 .at_pos(Pos::at(s)));
124 }
125 Ok(RelayFamilyId::Unrecognized(s.to_string()))
126 }
127}
128
129impl std::fmt::Display for RelayFamilyId {
130 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
131 match self {
132 RelayFamilyId::Ed25519(id) => write!(f, "{}{}", ED25519_ID_PREFIX, id),
133 RelayFamilyId::Unrecognized(s) => write!(f, "{}", s),
134 }
135 }
136}
137
138impl PartialOrd for RelayFamilyId {
139 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
140 Some(Ord::cmp(self, other))
141 }
142}
143impl Ord for RelayFamilyId {
144 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
145 Ord::cmp(&self.to_string(), &other.to_string())
148 }
149}
150
151impl From<Ed25519Identity> for RelayFamilyId {
152 fn from(value: Ed25519Identity) -> Self {
153 Self::Ed25519(value)
154 }
155}
156
157impl NormalItemArgument for RelayFamilyId {}
158
159#[derive(Clone, Debug, Default, Eq, PartialEq, Deftly, derive_more::AsRef)]
166#[derive_deftly(ItemValueEncodable, ItemValueParseable)]
167pub struct RelayFamilyIds(
168 Vec<RelayFamilyId>,
171);
172
173impl RelayFamilyIds {
174 pub fn new() -> Self {
176 Self::default()
177 }
178
179 pub fn push(&mut self, family_id: RelayFamilyId) {
181 self.0.push(family_id);
182 }
183
184 pub fn sort(&mut self) {
186 self.0.sort();
187 }
188
189 pub fn dedup(&mut self) {
191 self.0.dedup();
192 }
193}
194
195impl FromIterator<RelayFamilyId> for RelayFamilyIds {
196 fn from_iter<T: IntoIterator<Item = RelayFamilyId>>(iter: T) -> Self {
197 let mut res = Self(iter.into_iter().collect());
198 res.sort();
202 res.dedup();
203 res
204 }
205}
206
207#[cfg(test)]
208mod test {
209 #![allow(clippy::bool_assert_comparison)]
211 #![allow(clippy::clone_on_copy)]
212 #![allow(clippy::dbg_macro)]
213 #![allow(clippy::mixed_attributes_style)]
214 #![allow(clippy::print_stderr)]
215 #![allow(clippy::print_stdout)]
216 #![allow(clippy::single_char_pattern)]
217 #![allow(clippy::unwrap_used)]
218 #![allow(clippy::unchecked_time_subtraction)]
219 #![allow(clippy::useless_vec)]
220 #![allow(clippy::needless_pass_by_value)]
221 #![allow(clippy::string_slice)] use std::str::FromStr;
224
225 use super::*;
226 use crate::Result;
227 #[test]
228 fn family() -> Result<()> {
229 let f = "nickname1 nickname2 $ffffffffffffffffffffffffffffffffffffffff=foo eeeeeeeeeeeeeeeeeeeEEEeeeeeeeeeeeeeeeeee ddddddddddddddddddddddddddddddddd $cccccccccccccccccccccccccccccccccccccccc~blarg ".parse::<RelayFamily>()?;
230 let v = vec![
231 RsaIdentity::from_bytes(
232 &hex::decode("ffffffffffffffffffffffffffffffffffffffff").unwrap()[..],
233 )
234 .unwrap(),
235 RsaIdentity::from_bytes(
236 &hex::decode("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee").unwrap()[..],
237 )
238 .unwrap(),
239 RsaIdentity::from_bytes(
240 &hex::decode("cccccccccccccccccccccccccccccccccccccccc").unwrap()[..],
241 )
242 .unwrap(),
243 ];
244 assert_eq!(f.members().cloned().collect::<Vec<_>>(), v);
245 Ok(())
246 }
247
248 #[test]
249 fn test_contains() -> Result<()> {
250 let family =
251 "ffffffffffffffffffffffffffffffffffffffff eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"
252 .parse::<RelayFamily>()?;
253 let in_family = RsaIdentity::from_bytes(
254 &hex::decode("ffffffffffffffffffffffffffffffffffffffff").unwrap()[..],
255 )
256 .unwrap();
257 let not_in_family = RsaIdentity::from_bytes(
258 &hex::decode("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").unwrap()[..],
259 )
260 .unwrap();
261 assert!(family.contains(&in_family), "Relay not found in family");
262 assert!(
263 !family.contains(¬_in_family),
264 "Extra relay found in family"
265 );
266 Ok(())
267 }
268
269 #[test]
270 fn mutable() {
271 let mut family = RelayFamily::default();
272 let key = RsaIdentity::from_hex("ffffffffffffffffffffffffffffffffffffffff").unwrap();
273 assert!(!family.contains(&key));
274 family.push(key);
275 assert!(family.contains(&key));
276 }
277
278 #[test]
279 fn family_ids() {
280 let ed_str_rep = "ed25519:7sToQRuge1bU2hS0CG0ViMndc4m82JhO4B4kdrQey80";
281 let ed_id = RelayFamilyId::from_str(ed_str_rep).unwrap();
282 assert!(matches!(ed_id, RelayFamilyId::Ed25519(_)));
283 assert_eq!(ed_id.to_string().as_str(), ed_str_rep);
284
285 let other_str_rep = "hello-world";
286 let other_id = RelayFamilyId::from_str(other_str_rep).unwrap();
287 assert!(matches!(other_id, RelayFamilyId::Unrecognized(_)));
288 assert_eq!(other_id.to_string().as_str(), other_str_rep);
289
290 assert_eq!(ed_id, ed_id);
291 assert_ne!(ed_id, other_id);
292 }
293
294 #[test]
295 fn parse2() {
296 #[derive(Debug, PartialEq, Eq, derive_deftly::Deftly)]
297 #[derive_deftly(NetdocParseable)]
298 struct Wrapper {
299 family: RelayFamily,
300 }
301
302 const LINE: &str = "family $0000000000000000000000000000000000000000 $FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF";
303 let parsed =
304 crate::parse2::parse_netdoc::<Wrapper>(&crate::parse2::ParseInput::new(LINE, ""))
305 .unwrap();
306 assert_eq!(
307 parsed,
308 Wrapper {
309 family: RelayFamily(vec![
310 RsaIdentity::from_hex("0000000000000000000000000000000000000000")
311 .unwrap()
312 .into(),
313 RsaIdentity::from_hex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF")
314 .unwrap()
315 .into()
316 ])
317 }
318 );
319 }
320}