zebra_network/protocol/external/addr/
v1.rs

1//! Zcash `addr` (v1) message node address serialization.
2//!
3//! The [`AddrV1`] format serializes all IP addresses as IPv6 addresses.
4//! IPv4 addresses are converted to an [IPv4-mapped IPv6 address] before serialization.
5//!
6//! [IPv4-mapped IPv6 address]: https://en.wikipedia.org/wiki/IPv6#IPv4-mapped_IPv6_addresses
7
8use std::{
9    io::{Read, Write},
10    net::{IpAddr, Ipv6Addr, SocketAddrV6},
11};
12
13use byteorder::{BigEndian, LittleEndian, ReadBytesExt, WriteBytesExt};
14
15use zebra_chain::serialization::{
16    DateTime32, SerializationError, TrustedPreallocate, ZcashDeserialize, ZcashDeserializeInto,
17    ZcashSerialize,
18};
19
20use crate::{
21    meta_addr::MetaAddr,
22    protocol::external::{types::PeerServices, MAX_PROTOCOL_MESSAGE_LEN},
23    PeerSocketAddr,
24};
25
26use super::canonical_peer_addr;
27
28#[cfg(any(test, feature = "proptest-impl"))]
29use proptest_derive::Arbitrary;
30
31#[cfg(any(test, feature = "proptest-impl"))]
32use crate::protocol::external::arbitrary::canonical_peer_addr_strategy;
33
34/// The first format used for Bitcoin node addresses.
35/// Contains a node address, its advertised services, and last-seen time.
36/// This struct is serialized and deserialized into `addr` (v1) messages.
37///
38/// [Bitcoin reference](https://en.bitcoin.it/wiki/Protocol_documentation#Network_address)
39#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
40#[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary))]
41pub(in super::super) struct AddrV1 {
42    /// The unverified "last seen time" gossiped by the remote peer that sent us
43    /// this address.
44    ///
45    /// See the [`MetaAddr::last_seen`] method for details.
46    untrusted_last_seen: DateTime32,
47
48    /// The unverified services for the peer at `addr`.
49    ///
50    /// These services were advertised by the peer at `addr`,
51    /// then gossiped via another peer.
52    ///
53    /// ## Security
54    ///
55    /// `untrusted_services` on gossiped peers may be invalid due to outdated
56    /// records, older peer versions, or buggy or malicious peers.
57    untrusted_services: PeerServices,
58
59    /// The peer's canonical socket address.
60    /// IPv4 addresses are serialized as an [IPv4-mapped IPv6 address].
61    ///
62    /// [IPv4-mapped IPv6 address]: https://en.wikipedia.org/wiki/IPv6#IPv4-mapped_IPv6_addresses
63    #[cfg_attr(
64        any(test, feature = "proptest-impl"),
65        proptest(strategy = "canonical_peer_addr_strategy()")
66    )]
67    addr: PeerSocketAddr,
68}
69
70impl From<MetaAddr> for AddrV1 {
71    fn from(meta_addr: MetaAddr) -> Self {
72        let addr = canonical_peer_addr(meta_addr.addr);
73
74        let untrusted_services = meta_addr.services.expect(
75            "unexpected MetaAddr with missing peer services: \
76             MetaAddrs should be sanitized before serialization",
77        );
78        let untrusted_last_seen = meta_addr.last_seen().expect(
79            "unexpected MetaAddr with missing last seen time: \
80             MetaAddrs should be sanitized before serialization",
81        );
82
83        AddrV1 {
84            untrusted_last_seen,
85            untrusted_services,
86            addr,
87        }
88    }
89}
90
91impl From<AddrV1> for MetaAddr {
92    fn from(addr: AddrV1) -> Self {
93        MetaAddr::new_gossiped_meta_addr(
94            addr.addr,
95            addr.untrusted_services,
96            addr.untrusted_last_seen,
97        )
98    }
99}
100
101impl ZcashSerialize for AddrV1 {
102    fn zcash_serialize<W: Write>(&self, mut writer: W) -> Result<(), std::io::Error> {
103        self.untrusted_last_seen.zcash_serialize(&mut writer)?;
104        writer.write_u64::<LittleEndian>(self.untrusted_services.bits())?;
105
106        let ipv6_addr = ipv6_mapped_ip_addr(self.addr.ip());
107        ipv6_addr.zcash_serialize(&mut writer)?;
108        writer.write_u16::<BigEndian>(self.addr.port())?;
109
110        Ok(())
111    }
112}
113
114impl ZcashDeserialize for AddrV1 {
115    fn zcash_deserialize<R: Read>(mut reader: R) -> Result<Self, SerializationError> {
116        let untrusted_last_seen = (&mut reader).zcash_deserialize_into()?;
117        let untrusted_services =
118            PeerServices::from_bits_truncate(reader.read_u64::<LittleEndian>()?);
119
120        let ipv6_addr = (&mut reader).zcash_deserialize_into()?;
121        let port = reader.read_u16::<BigEndian>()?;
122
123        // `0` is the default unspecified value for these fields.
124        let ipv6_addr = SocketAddrV6::new(ipv6_addr, port, 0, 0);
125
126        Ok(AddrV1 {
127            addr: canonical_peer_addr(ipv6_addr),
128            untrusted_services,
129            untrusted_last_seen,
130        })
131    }
132}
133
134/// A serialized `addr` (v1) has a 4 byte time, 8 byte services, 16 byte IP addr, and 2 byte port
135pub(in super::super) const ADDR_V1_SIZE: usize = 4 + 8 + 16 + 2;
136
137impl TrustedPreallocate for AddrV1 {
138    fn max_allocation() -> u64 {
139        // Since ADDR_V1_SIZE is less than 2^5, the length of the largest list takes up 5 bytes.
140        ((MAX_PROTOCOL_MESSAGE_LEN - 5) / ADDR_V1_SIZE) as u64
141    }
142}
143
144/// Transform an `IpAddr` into an IPv6-mapped IPv4 addresses.
145///
146/// See [`canonical_ip_addr`] for detailed info on IPv6-mapped IPv4 addresses.
147///
148/// [`canonical_ip_addr`]: super::canonical::canonical_ip_addr
149pub(in super::super) fn ipv6_mapped_ip_addr(ip_addr: IpAddr) -> Ipv6Addr {
150    use IpAddr::*;
151
152    match ip_addr {
153        V4(v4_addr) => v4_addr.to_ipv6_mapped(),
154        V6(v6_addr) => v6_addr,
155    }
156}