use std::{
io::{Read, Write},
net::{IpAddr, Ipv6Addr, SocketAddrV6},
};
use byteorder::{BigEndian, LittleEndian, ReadBytesExt, WriteBytesExt};
use zebra_chain::serialization::{
DateTime32, SerializationError, TrustedPreallocate, ZcashDeserialize, ZcashDeserializeInto,
ZcashSerialize,
};
use crate::{
meta_addr::MetaAddr,
protocol::external::{types::PeerServices, MAX_PROTOCOL_MESSAGE_LEN},
PeerSocketAddr,
};
use super::canonical_peer_addr;
#[cfg(any(test, feature = "proptest-impl"))]
use proptest_derive::Arbitrary;
#[cfg(any(test, feature = "proptest-impl"))]
use crate::protocol::external::arbitrary::canonical_peer_addr_strategy;
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
#[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary))]
pub(in super::super) struct AddrV1 {
untrusted_last_seen: DateTime32,
untrusted_services: PeerServices,
#[cfg_attr(
any(test, feature = "proptest-impl"),
proptest(strategy = "canonical_peer_addr_strategy()")
)]
addr: PeerSocketAddr,
}
impl From<MetaAddr> for AddrV1 {
fn from(meta_addr: MetaAddr) -> Self {
let addr = canonical_peer_addr(meta_addr.addr);
let untrusted_services = meta_addr.services.expect(
"unexpected MetaAddr with missing peer services: \
MetaAddrs should be sanitized before serialization",
);
let untrusted_last_seen = meta_addr.last_seen().expect(
"unexpected MetaAddr with missing last seen time: \
MetaAddrs should be sanitized before serialization",
);
AddrV1 {
untrusted_last_seen,
untrusted_services,
addr,
}
}
}
impl From<AddrV1> for MetaAddr {
fn from(addr: AddrV1) -> Self {
MetaAddr::new_gossiped_meta_addr(
addr.addr,
addr.untrusted_services,
addr.untrusted_last_seen,
)
}
}
impl ZcashSerialize for AddrV1 {
fn zcash_serialize<W: Write>(&self, mut writer: W) -> Result<(), std::io::Error> {
self.untrusted_last_seen.zcash_serialize(&mut writer)?;
writer.write_u64::<LittleEndian>(self.untrusted_services.bits())?;
let ipv6_addr = ipv6_mapped_ip_addr(self.addr.ip());
ipv6_addr.zcash_serialize(&mut writer)?;
writer.write_u16::<BigEndian>(self.addr.port())?;
Ok(())
}
}
impl ZcashDeserialize for AddrV1 {
fn zcash_deserialize<R: Read>(mut reader: R) -> Result<Self, SerializationError> {
let untrusted_last_seen = (&mut reader).zcash_deserialize_into()?;
let untrusted_services =
PeerServices::from_bits_truncate(reader.read_u64::<LittleEndian>()?);
let ipv6_addr = (&mut reader).zcash_deserialize_into()?;
let port = reader.read_u16::<BigEndian>()?;
let ipv6_addr = SocketAddrV6::new(ipv6_addr, port, 0, 0);
Ok(AddrV1 {
addr: canonical_peer_addr(ipv6_addr),
untrusted_services,
untrusted_last_seen,
})
}
}
pub(in super::super) const ADDR_V1_SIZE: usize = 4 + 8 + 16 + 2;
impl TrustedPreallocate for AddrV1 {
fn max_allocation() -> u64 {
((MAX_PROTOCOL_MESSAGE_LEN - 5) / ADDR_V1_SIZE) as u64
}
}
pub(in super::super) fn ipv6_mapped_ip_addr(ip_addr: IpAddr) -> Ipv6Addr {
use IpAddr::*;
match ip_addr {
V4(v4_addr) => v4_addr.to_ipv6_mapped(),
V6(v6_addr) => v6_addr,
}
}