interledger_packet/
address.rs

1//! ILP address types.
2//!
3//! Reference: [ILP Addresses - v2.0.0](https://github.com/interledger/rfcs/blob/master/0015-ilp-addresses/0015-ilp-addresses.md).
4
5// Addresses are never empty.
6#![allow(clippy::len_without_is_empty)]
7
8use regex::Regex;
9use std::fmt;
10use std::str;
11
12use crate::errors::ParseError;
13use bytes::{BufMut, Bytes, BytesMut};
14use std::convert::TryFrom;
15use std::str::FromStr;
16
17use lazy_static::lazy_static;
18
19const MAX_ADDRESS_LENGTH: usize = 1023;
20
21#[derive(Debug)]
22pub enum AddressError {
23    InvalidLength(usize),
24    InvalidFormat,
25}
26
27lazy_static! {
28    static ref ADDRESS_PATTERN: Regex =
29        Regex::new(r"^(g|private|example|peer|self|test[1-3]?|local)([.][a-zA-Z0-9_~-]+)+$")
30            .unwrap();
31}
32
33use std::error::Error;
34impl Error for AddressError {
35    fn description(&self) -> &str {
36        match *self {
37            AddressError::InvalidLength(_length) => "Invalid address length",
38            AddressError::InvalidFormat => "Invalid address format",
39        }
40    }
41}
42
43impl fmt::Display for AddressError {
44    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
45        self.description().fmt(f)
46    }
47}
48
49/// An ILP address backed by `Bytes`.
50#[derive(Clone, Eq, Hash, PartialEq)]
51pub struct Address(Bytes);
52
53impl FromStr for Address {
54    type Err = ParseError;
55
56    fn from_str(src: &str) -> Result<Self, Self::Err> {
57        Address::try_from(Bytes::from(src))
58    }
59}
60
61impl TryFrom<Bytes> for Address {
62    type Error = ParseError;
63
64    fn try_from(bytes: Bytes) -> Result<Self, Self::Error> {
65        // https://interledger.org/rfcs/0015-ilp-addresses/#address-requirements
66        if bytes.len() > MAX_ADDRESS_LENGTH {
67            return Err(ParseError::InvalidAddress(AddressError::InvalidLength(
68                bytes.len(),
69            )));
70        }
71
72        if ADDRESS_PATTERN.is_match(str::from_utf8(&bytes)?) {
73            Ok(Address(bytes))
74        } else {
75            Err(ParseError::InvalidAddress(AddressError::InvalidFormat))
76        }
77    }
78}
79
80impl TryFrom<&[u8]> for Address {
81    type Error = ParseError;
82
83    fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
84        Self::try_from(Bytes::from(bytes))
85    }
86}
87
88impl std::ops::Deref for Address {
89    type Target = str;
90
91    fn deref(&self) -> &str {
92        // This call is safe to execute because the parsing is done
93        // during creation of the Address in try_from.
94        unsafe { str::from_utf8_unchecked(self.0.as_ref()) }
95    }
96}
97
98impl AsRef<[u8]> for Address {
99    #[inline]
100    fn as_ref(&self) -> &[u8] {
101        self.0.as_ref()
102    }
103}
104
105impl AsRef<Bytes> for Address {
106    #[inline]
107    fn as_ref(&self) -> &Bytes {
108        &self.0
109    }
110}
111
112impl fmt::Debug for Address {
113    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
114        formatter
115            .debug_tuple("Address")
116            .field(&self.to_string())
117            .finish()
118    }
119}
120
121impl fmt::Display for Address {
122    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
123        formatter.write_str(self)
124    }
125}
126
127impl Address {
128    /// Returns the length of the ILP Address.
129    #[inline]
130    pub fn len(&self) -> usize {
131        self.0.len()
132    }
133
134    /// Returns the `Bytes` conversion of the ILP Address
135    pub fn to_bytes(&self) -> Bytes {
136        self.0.clone()
137    }
138
139    /// Creates an ILP address without validating the bytes.
140    ///
141    /// # Safety
142    ///
143    /// The given bytes must be a valid ILP address.
144    #[inline]
145    pub unsafe fn new_unchecked(bytes: Bytes) -> Self {
146        debug_assert!(Address::try_from(bytes.as_ref()).is_ok());
147        Address(bytes)
148    }
149
150    /// Returns an iterator over all the segments of the ILP Address
151    pub fn segments(&self) -> impl DoubleEndedIterator<Item = &str> {
152        unsafe {
153            self.0
154                .split(|&b| b == b'.')
155                .map(|s| str::from_utf8_unchecked(&s))
156        }
157    }
158
159    /// Returns the first segment of the address, which is the scheme.
160    /// See [`IL-RFC 15: ILP Addresses](https://github.com/interledger/rfcs/blob/master/0015-ilp-addresses/0015-ilp-addresses.md#allocation-schemes)
161    pub fn scheme(&self) -> &str {
162        // This should neve panic because we validate the Address when it's created
163        self.segments()
164            .next()
165            .expect("Addresses must have a scheme as the first segment")
166    }
167
168    /// Suffixes the ILP Address with the provided suffix. Includes a '.' separator
169    pub fn with_suffix(&self, suffix: &[u8]) -> Result<Address, ParseError> {
170        let new_address_len = self.len() + 1 + suffix.len();
171        let mut new_address = BytesMut::with_capacity(new_address_len);
172
173        new_address.put_slice(self.0.as_ref());
174        new_address.put(b'.');
175        new_address.put_slice(suffix);
176
177        Address::try_from(new_address.freeze())
178    }
179}
180
181impl<'a> PartialEq<[u8]> for Address {
182    fn eq(&self, other: &[u8]) -> bool {
183        self.0 == other
184    }
185}
186
187#[cfg(any(feature = "serde", test))]
188impl<'de> serde::Deserialize<'de> for Address {
189    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
190    where
191        D: serde::Deserializer<'de>,
192    {
193        let string = <&str>::deserialize(deserializer)?;
194        Address::from_str(string).map_err(serde::de::Error::custom)
195    }
196}
197
198#[cfg(any(feature = "serde", test))]
199impl serde::Serialize for Address {
200    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
201    where
202        S: serde::Serializer,
203    {
204        serializer.serialize_str(&*self)
205    }
206}
207
208#[cfg(test)]
209mod test_address {
210    use serde_test::{assert_de_tokens, assert_de_tokens_error, assert_ser_tokens, Token};
211
212    use super::*;
213
214    static VALID_ADDRESSES: &[&[u8]] = &[
215        b"test.alice.XYZ.1234.-_~",
216        b"g.us-fed.ach.0.acmebank.swx0a0.acmecorp.sales.199.~ipr.cdfa5e16-e759-4ba3-88f6-8b9dc83c1868.2",
217
218        b"g.A", b"private.A", b"example.A", b"peer.A", b"self.A",
219        b"test.A", b"test1.A", b"test2.A", b"test3.A", b"local.A",
220    ];
221
222    static INVALID_ADDRESSES: &[&[u8]] = &[
223        b"", // empty
224        // Invalid characters.
225        b"test.alice 123",
226        b"test.alice!123",
227        b"test.alice/123",
228        b"test.alic\xF0",
229        // Bad schemes.
230        b"test",        // only a scheme
231        b"what.alice",  // invalid scheme
232        b"test4.alice", // invalid scheme
233        // Invalid separators.
234        b"test.",       // only a prefix
235        b"test.alice.", // ends in a separator
236        b".test.alice", // begins with a separator
237        b"test..alice", // double separator
238    ];
239
240    #[test]
241    fn test_try_from() {
242        for address in VALID_ADDRESSES {
243            assert_eq!(
244                Address::try_from(*address).unwrap(),
245                Address(Bytes::from(*address)),
246                "address: {:?}",
247                String::from_utf8_lossy(address),
248            );
249        }
250
251        let longest_address = &make_address(1023)[..];
252        assert_eq!(
253            Address::try_from(longest_address).unwrap(),
254            Address(Bytes::from(longest_address)),
255        );
256
257        for address in INVALID_ADDRESSES {
258            assert!(
259                Address::try_from(*address).is_err(),
260                "address: {:?}",
261                String::from_utf8_lossy(address),
262            );
263        }
264
265        let too_long_address = &make_address(1024)[..];
266        assert!(Address::try_from(too_long_address).is_err());
267    }
268
269    #[test]
270    fn test_deserialize() {
271        assert_de_tokens(
272            &Address::try_from(Bytes::from("test.alice")).unwrap(),
273            &[Token::BorrowedStr("test.alice")],
274        );
275        assert_de_tokens_error::<Address>(
276            &[Token::BorrowedStr("test.alice ")],
277            "Invalid address format",
278        );
279    }
280
281    #[test]
282    fn test_serialize() {
283        let addr = Address::try_from(Bytes::from("test.alice")).unwrap();
284        assert_ser_tokens(&addr, &[Token::Str("test.alice")]);
285    }
286
287    #[test]
288    fn test_len() {
289        assert_eq!(
290            Address::from_str("test.alice").unwrap().len(),
291            "test.alice".len(),
292        );
293    }
294
295    #[test]
296    fn test_segments() {
297        let addr = Address::from_str("test.alice.1234.5789").unwrap();
298        let expected = vec!["test", "alice", "1234", "5789"];
299        assert!(addr.segments().eq(expected));
300    }
301
302    #[test]
303    fn test_eq() {
304        let addr1 = Address::from_str("test.alice.1234.5789").unwrap();
305        let addr2 = Address::from_str("test.bob").unwrap();
306        assert_ne!(addr1, addr2);
307        assert_eq!(addr1, addr1);
308        assert_eq!(addr2, addr2);
309        assert!(addr1 == addr1.clone());
310        assert!(addr1 != addr2);
311        assert!(addr1.eq(&addr1));
312        assert!(addr1.ne(&addr2));
313    }
314
315    #[test]
316    fn test_with_suffix() {
317        assert_eq!(
318            Address::from_str("test.alice")
319                .unwrap()
320                .with_suffix(b"1234")
321                .unwrap(),
322            Address::from_str("test.alice.1234").unwrap(),
323        );
324        // invalid suffixes error out
325        assert!({
326            Address::from_str("test.alice")
327                .unwrap()
328                .with_suffix(b"12 34")
329                .is_err()
330        });
331        assert!({
332            Address::from_str("test.alice")
333                .unwrap()
334                .with_suffix(b".1234")
335                .is_err()
336        });
337    }
338
339    #[test]
340    fn test_debug() {
341        assert_eq!(
342            format!("{:?}", Address::from_str("test.alice").unwrap()),
343            "Address(\"test.alice\")",
344        );
345    }
346
347    #[test]
348    fn test_display() {
349        assert_eq!(
350            format!("{}", Address::from_str("test.alice").unwrap()),
351            "test.alice",
352        );
353    }
354
355    #[test]
356    fn test_scheme() {
357        assert_eq!(Address::from_str("test.alice").unwrap().scheme(), "test");
358        assert_eq!(
359            Address::from_str("example.node.other").unwrap().scheme(),
360            "example"
361        );
362        assert_eq!(
363            Address::from_str("g.some-node.child-node.with_other_things~and-more")
364                .unwrap()
365                .scheme(),
366            "g"
367        );
368    }
369
370    fn make_address(length: usize) -> Vec<u8> {
371        let mut addr = b"test.".to_vec();
372        addr.resize(length, b'_');
373        addr
374    }
375}