zebra_network/protocol/external/addr/v2.rs
1//! Zcash `addrv2` message node address serialization.
2//!
3//! Zebra parses received IPv4 and IPv6 addresses in the [`AddrV2`] format.
4//! But it ignores all other address types.
5//!
6//! Zebra never sends `addrv2` messages, because peers still accept `addr` (v1) messages.
7
8use std::{
9 io::Read,
10 net::{IpAddr, SocketAddr},
11};
12
13use byteorder::{BigEndian, ReadBytesExt};
14use thiserror::Error;
15
16use zebra_chain::serialization::{
17 zcash_deserialize_bytes_external_count, CompactSize64, CompactSizeMessage, DateTime32,
18 SerializationError, TrustedPreallocate, ZcashDeserialize, ZcashDeserializeInto,
19};
20
21use crate::{
22 meta_addr::MetaAddr,
23 protocol::external::{types::PeerServices, MAX_PROTOCOL_MESSAGE_LEN},
24 PeerSocketAddr,
25};
26
27use super::canonical_peer_addr;
28
29#[cfg(any(test, feature = "proptest-impl"))]
30use proptest_derive::Arbitrary;
31
32#[cfg(test)]
33use byteorder::WriteBytesExt;
34#[cfg(test)]
35use std::io::Write;
36#[cfg(test)]
37use zebra_chain::serialization::{zcash_serialize_bytes, ZcashSerialize};
38
39/// The maximum permitted size of the `addr` field in `addrv2` messages.
40///
41/// > Field addr has a variable length, with a maximum of 512 bytes (4096 bits).
42/// > Clients MUST reject messages with a longer addr field, irrespective of the network ID.
43///
44/// <https://zips.z.cash/zip-0155#specification>
45pub const MAX_ADDR_V2_ADDR_SIZE: usize = 512;
46
47/// The network ID of [`Ipv4Addr`]s in `addrv2` messages.
48///
49/// > 0x01 IPV4 4 IPv4 address (globally routed internet)
50///
51/// <https://zips.z.cash/zip-0155#specification>
52///
53/// [`Ipv4Addr`]: std::net::Ipv4Addr
54pub const ADDR_V2_IPV4_NETWORK_ID: u8 = 0x01;
55
56/// The size of [`Ipv4Addr`]s in `addrv2` messages.
57///
58/// <https://zips.z.cash/zip-0155#specification>
59///
60/// [`Ipv4Addr`]: std::net::Ipv4Addr
61pub const ADDR_V2_IPV4_ADDR_SIZE: usize = 4;
62
63/// The network ID of [`Ipv6Addr`]s in `addrv2` messages.
64///
65/// > 0x02 IPV6 16 IPv6 address (globally routed internet)
66///
67/// <https://zips.z.cash/zip-0155#specification>
68///
69/// [`Ipv6Addr`]: std::net::Ipv6Addr
70pub const ADDR_V2_IPV6_NETWORK_ID: u8 = 0x02;
71
72/// The size of [`Ipv6Addr`]s in `addrv2` messages.
73///
74/// <https://zips.z.cash/zip-0155#specification>
75///
76/// [`Ipv6Addr`]: std::net::Ipv6Addr
77pub const ADDR_V2_IPV6_ADDR_SIZE: usize = 16;
78
79/// The second format used for Bitcoin node addresses.
80/// Contains a node address, its advertised services, and last-seen time.
81/// This struct is serialized and deserialized into `addrv2` messages.
82///
83/// [ZIP 155](https://zips.z.cash/zip-0155#specification)
84#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
85#[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary))]
86pub(in super::super) enum AddrV2 {
87 /// An IPv4 or IPv6 node address, in `addrv2` format.
88 IpAddr {
89 /// The unverified "last seen time" gossiped by the remote peer that sent us
90 /// this address.
91 ///
92 /// See the [`MetaAddr::last_seen`] method for details.
93 untrusted_last_seen: DateTime32,
94
95 /// The unverified services for the peer at `ip_addr`:`port`.
96 ///
97 /// These services were advertised by the peer at that address,
98 /// then gossiped via another peer.
99 ///
100 /// ## Security
101 ///
102 /// `untrusted_services` on gossiped peers may be invalid due to outdated
103 /// records, older peer versions, or buggy or malicious peers.
104 untrusted_services: PeerServices,
105
106 /// The peer's canonical IP address and port.
107 ///
108 /// Unlike [`AddrV1`], this can be an IPv4 or IPv6 address.
109 ///
110 /// [`AddrV1`]: super::v1::AddrV1
111 addr: PeerSocketAddr,
112 },
113
114 /// A node address with an unsupported `networkID`, in `addrv2` format.
115 //
116 // TODO: when we add more address types, make sure their addresses aren't logged,
117 // in a similar way to `PeerSocketAddr`
118 Unsupported,
119}
120
121// Just serialize in the tests for now.
122//
123// We can't guarantee that peers support addrv2 until it activates,
124// and outdated peers are excluded from the network by a network upgrade.
125// (Likely NU5 on mainnet, and NU6 on testnet.)
126// https://zips.z.cash/zip-0155#deployment
127//
128// And Zebra doesn't use different codecs for different peer versions.
129#[cfg(test)]
130impl From<MetaAddr> for AddrV2 {
131 fn from(meta_addr: MetaAddr) -> Self {
132 let untrusted_services = meta_addr.services.expect(
133 "unexpected MetaAddr with missing peer services: \
134 MetaAddrs should be sanitized before serialization",
135 );
136 let untrusted_last_seen = meta_addr.last_seen().expect(
137 "unexpected MetaAddr with missing last seen time: \
138 MetaAddrs should be sanitized before serialization",
139 );
140
141 AddrV2::IpAddr {
142 untrusted_last_seen,
143 untrusted_services,
144 addr: canonical_peer_addr(meta_addr.addr()),
145 }
146 }
147}
148
149/// The error returned when converting `AddrV2::Unsupported` fails.
150#[derive(Error, Copy, Clone, Debug, Eq, PartialEq, Hash)]
151#[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary))]
152#[error("can not parse this addrv2 variant: unimplemented or unrecognised AddrV2 network ID")]
153pub struct UnsupportedAddrV2NetworkIdError;
154
155impl TryFrom<AddrV2> for MetaAddr {
156 type Error = UnsupportedAddrV2NetworkIdError;
157
158 fn try_from(addr: AddrV2) -> Result<MetaAddr, UnsupportedAddrV2NetworkIdError> {
159 if let AddrV2::IpAddr {
160 untrusted_last_seen,
161 untrusted_services,
162 addr,
163 } = addr
164 {
165 Ok(MetaAddr::new_gossiped_meta_addr(
166 addr,
167 untrusted_services,
168 untrusted_last_seen,
169 ))
170 } else {
171 Err(UnsupportedAddrV2NetworkIdError)
172 }
173 }
174}
175
176impl AddrV2 {
177 /// Deserialize `addr_bytes` as an IPv4 or IPv6 address, using the `addrv2` format.
178 /// Returns the corresponding [`IpAddr`].
179 ///
180 /// The returned IP version is chosen based on `IP_ADDR_SIZE`,
181 /// which should be [`ADDR_V2_IPV4_ADDR_SIZE`] or [`ADDR_V2_IPV6_ADDR_SIZE`].
182 #[allow(clippy::unwrap_in_result)]
183 fn ip_addr_from_bytes<const IP_ADDR_SIZE: usize>(
184 addr_bytes: Vec<u8>,
185 ) -> Result<IpAddr, SerializationError>
186 where
187 IpAddr: From<[u8; IP_ADDR_SIZE]>,
188 {
189 // > Clients MUST reject messages that contain addresses that have
190 // > a different length than specified in this table for a specific network ID,
191 // > as these are meaningless.
192 if addr_bytes.len() != IP_ADDR_SIZE {
193 let error_msg = if IP_ADDR_SIZE == ADDR_V2_IPV4_ADDR_SIZE {
194 "IP address field length did not match expected IPv4 address size in addrv2 message"
195 } else if IP_ADDR_SIZE == ADDR_V2_IPV6_ADDR_SIZE {
196 "IP address field length did not match expected IPv6 address size in addrv2 message"
197 } else {
198 unreachable!("unexpected IP address size when converting from bytes");
199 };
200
201 return Err(SerializationError::Parse(error_msg));
202 };
203
204 // > The IPV4 and IPV6 network IDs use addresses encoded in the usual way
205 // > for binary IPv4 and IPv6 addresses in network byte order (big endian).
206 let ip: [u8; IP_ADDR_SIZE] = addr_bytes.try_into().expect("just checked length");
207
208 Ok(IpAddr::from(ip))
209 }
210}
211
212// Just serialize in the tests for now.
213//
214// See the detailed note about ZIP-155 activation above.
215#[cfg(test)]
216impl ZcashSerialize for AddrV2 {
217 fn zcash_serialize<W: Write>(&self, mut writer: W) -> Result<(), std::io::Error> {
218 if let AddrV2::IpAddr {
219 untrusted_last_seen,
220 untrusted_services,
221 addr,
222 } = self
223 {
224 // > uint32 Time that this node was last seen as connected to the network.
225 untrusted_last_seen.zcash_serialize(&mut writer)?;
226
227 // > Service bits. A CompactSize-encoded bit field that is 64 bits wide.
228 let untrusted_services: CompactSize64 = untrusted_services.bits().into();
229 untrusted_services.zcash_serialize(&mut writer)?;
230
231 match addr.ip() {
232 IpAddr::V4(ip) => {
233 // > Network identifier. An 8-bit value that specifies which network is addressed.
234 writer.write_u8(ADDR_V2_IPV4_NETWORK_ID)?;
235
236 // > The IPV4 and IPV6 network IDs use addresses encoded in the usual way
237 // > for binary IPv4 and IPv6 addresses in network byte order (big endian).
238 let ip: [u8; ADDR_V2_IPV4_ADDR_SIZE] = ip.octets();
239 // > CompactSize The length in bytes of addr.
240 // > uint8[sizeAddr] Network address. The interpretation depends on networkID.
241 zcash_serialize_bytes(&ip.to_vec(), &mut writer)?;
242
243 // > uint16 Network port. If not relevant for the network this MUST be 0.
244 writer.write_u16::<BigEndian>(addr.port())?;
245 }
246 IpAddr::V6(ip) => {
247 writer.write_u8(ADDR_V2_IPV6_NETWORK_ID)?;
248
249 let ip: [u8; ADDR_V2_IPV6_ADDR_SIZE] = ip.octets();
250 zcash_serialize_bytes(&ip.to_vec(), &mut writer)?;
251
252 writer.write_u16::<BigEndian>(addr.port())?;
253 }
254 }
255 } else {
256 unreachable!("unexpected AddrV2 variant: {:?}", self);
257 }
258
259 Ok(())
260 }
261}
262
263/// Deserialize an `addrv2` entry according to:
264/// <https://zips.z.cash/zip-0155#specification>
265///
266/// Unimplemented and unrecognised addresses are deserialized as [`AddrV2::Unsupported`].
267/// (Deserialization consumes the correct number of bytes for unsupported addresses.)
268impl ZcashDeserialize for AddrV2 {
269 fn zcash_deserialize<R: Read>(mut reader: R) -> Result<Self, SerializationError> {
270 // > uint32 Time that this node was last seen as connected to the network.
271 let untrusted_last_seen = (&mut reader).zcash_deserialize_into()?;
272
273 // > Service bits. A CompactSize-encoded bit field that is 64 bits wide.
274 let untrusted_services: CompactSize64 = (&mut reader).zcash_deserialize_into()?;
275 let untrusted_services = PeerServices::from_bits_truncate(untrusted_services.into());
276
277 // > Network identifier. An 8-bit value that specifies which network is addressed.
278 //
279 // See the list of reserved network IDs in ZIP 155.
280 let network_id = reader.read_u8()?;
281
282 // > CompactSize The length in bytes of addr.
283 let addr_len: CompactSizeMessage = (&mut reader).zcash_deserialize_into()?;
284 let addr_len: usize = addr_len.into();
285 if addr_len > MAX_ADDR_V2_ADDR_SIZE {
286 return Err(SerializationError::Parse(
287 "addr field longer than MAX_ADDR_V2_ADDR_SIZE in addrv2 message",
288 ));
289 }
290
291 // > uint8[sizeAddr] Network address. The interpretation depends on networkID.
292 let addr: Vec<u8> = zcash_deserialize_bytes_external_count(addr_len, &mut reader)?;
293
294 // > uint16 Network port. If not relevant for the network this MUST be 0.
295 let port = reader.read_u16::<BigEndian>()?;
296
297 let ip = if network_id == ADDR_V2_IPV4_NETWORK_ID {
298 AddrV2::ip_addr_from_bytes::<ADDR_V2_IPV4_ADDR_SIZE>(addr)?
299 } else if network_id == ADDR_V2_IPV6_NETWORK_ID {
300 AddrV2::ip_addr_from_bytes::<ADDR_V2_IPV6_ADDR_SIZE>(addr)?
301 } else {
302 // unimplemented or unrecognised network ID, just consume the bytes
303 //
304 // > Clients MUST NOT gossip addresses from unknown networks,
305 // > because they have no means to validate those addresses
306 // > and so can be tricked to gossip invalid addresses.
307
308 return Ok(AddrV2::Unsupported);
309 };
310
311 Ok(AddrV2::IpAddr {
312 untrusted_last_seen,
313 untrusted_services,
314 addr: canonical_peer_addr(SocketAddr::new(ip, port)),
315 })
316 }
317}
318
319/// A serialized `addrv2` has:
320/// * 4 byte time,
321/// * 1-9 byte services,
322/// * 1 byte networkID,
323/// * 1-9 byte sizeAddr,
324/// * 0-512 bytes addr,
325/// * 2 bytes port.
326#[allow(clippy::identity_op)]
327pub(in super::super) const ADDR_V2_MIN_SIZE: usize = 4 + 1 + 1 + 1 + 0 + 2;
328
329impl TrustedPreallocate for AddrV2 {
330 fn max_allocation() -> u64 {
331 // Since ADDR_V2_MIN_SIZE is less than 2^5, the length of the largest list takes up 5 bytes.
332 ((MAX_PROTOCOL_MESSAGE_LEN - 5) / ADDR_V2_MIN_SIZE) as u64
333 }
334}