zebra_network/protocol/external/addr/
in_version.rs

1//! Zcash `version` message node address serialization.
2//!
3//! The [`AddrInVersion`] format is the same as the `addr` ([`super::v1`]) message,
4//! but without the timestamp field.
5
6use std::{
7    io::{Read, Write},
8    net::SocketAddrV6,
9};
10
11use byteorder::{BigEndian, LittleEndian, ReadBytesExt, WriteBytesExt};
12
13use zebra_chain::serialization::{
14    SerializationError, ZcashDeserialize, ZcashDeserializeInto, ZcashSerialize,
15};
16
17use crate::{protocol::external::types::PeerServices, PeerSocketAddr};
18
19#[cfg(any(test, feature = "proptest-impl"))]
20use proptest_derive::Arbitrary;
21
22#[cfg(any(test, feature = "proptest-impl"))]
23use crate::protocol::external::arbitrary::canonical_peer_addr_strategy;
24
25use super::{canonical_peer_addr, v1::ipv6_mapped_ip_addr};
26
27/// The format used for Bitcoin node addresses in `version` messages.
28/// Contains a node address and services, without a last-seen time.
29///
30/// [Bitcoin reference](https://en.bitcoin.it/wiki/Protocol_documentation#Network_address)
31#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
32#[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary))]
33pub struct AddrInVersion {
34    /// The unverified services for the peer at `addr`.
35    ///
36    /// These services were advertised by the peer at `addr`,
37    /// then gossiped via another peer.
38    ///
39    /// ## Security
40    ///
41    /// `untrusted_services` on gossiped peers may be invalid due to outdated
42    /// records, older peer versions, or buggy or malicious peers.
43    untrusted_services: PeerServices,
44
45    /// The peer's canonical socket address.
46    /// IPv4 addresses are serialized as an [IPv4-mapped IPv6 address].
47    ///
48    /// [IPv4-mapped IPv6 address]: https://en.wikipedia.org/wiki/IPv6#IPv4-mapped_IPv6_addresses
49    #[cfg_attr(
50        any(test, feature = "proptest-impl"),
51        proptest(strategy = "canonical_peer_addr_strategy()")
52    )]
53    addr: PeerSocketAddr,
54}
55
56impl AddrInVersion {
57    /// Returns a new `version` message address based on its fields.
58    pub fn new(socket_addr: impl Into<PeerSocketAddr>, untrusted_services: PeerServices) -> Self {
59        Self {
60            untrusted_services,
61            addr: canonical_peer_addr(socket_addr),
62        }
63    }
64
65    /// Returns the canonical address for this peer.
66    pub fn addr(&self) -> PeerSocketAddr {
67        self.addr
68    }
69
70    /// Returns the services for this peer.
71    pub fn untrusted_services(&self) -> PeerServices {
72        self.untrusted_services
73    }
74}
75
76impl ZcashSerialize for AddrInVersion {
77    fn zcash_serialize<W: Write>(&self, mut writer: W) -> Result<(), std::io::Error> {
78        writer.write_u64::<LittleEndian>(self.untrusted_services.bits())?;
79
80        let ipv6_addr = ipv6_mapped_ip_addr(self.addr.ip());
81        ipv6_addr.zcash_serialize(&mut writer)?;
82        writer.write_u16::<BigEndian>(self.addr.port())?;
83
84        Ok(())
85    }
86}
87
88impl ZcashDeserialize for AddrInVersion {
89    fn zcash_deserialize<R: Read>(mut reader: R) -> Result<Self, SerializationError> {
90        let untrusted_services =
91            PeerServices::from_bits_truncate(reader.read_u64::<LittleEndian>()?);
92
93        let ipv6_addr = (&mut reader).zcash_deserialize_into()?;
94        let port = reader.read_u16::<BigEndian>()?;
95
96        // `0` is the default unspecified value for these fields.
97        let ipv6_addr = SocketAddrV6::new(ipv6_addr, port, 0, 0);
98
99        Ok(AddrInVersion {
100            addr: canonical_peer_addr(ipv6_addr),
101            untrusted_services,
102        })
103    }
104}