use std::{
io::{Read, Write},
net::SocketAddrV6,
};
use byteorder::{BigEndian, LittleEndian, ReadBytesExt, WriteBytesExt};
use zebra_chain::serialization::{
SerializationError, ZcashDeserialize, ZcashDeserializeInto, ZcashSerialize,
};
use crate::{protocol::external::types::PeerServices, PeerSocketAddr};
#[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;
use super::{canonical_peer_addr, v1::ipv6_mapped_ip_addr};
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
#[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary))]
pub struct AddrInVersion {
untrusted_services: PeerServices,
#[cfg_attr(
any(test, feature = "proptest-impl"),
proptest(strategy = "canonical_peer_addr_strategy()")
)]
addr: PeerSocketAddr,
}
impl AddrInVersion {
pub fn new(socket_addr: impl Into<PeerSocketAddr>, untrusted_services: PeerServices) -> Self {
Self {
untrusted_services,
addr: canonical_peer_addr(socket_addr),
}
}
pub fn addr(&self) -> PeerSocketAddr {
self.addr
}
pub fn untrusted_services(&self) -> PeerServices {
self.untrusted_services
}
}
impl ZcashSerialize for AddrInVersion {
fn zcash_serialize<W: Write>(&self, mut writer: W) -> Result<(), std::io::Error> {
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 AddrInVersion {
fn zcash_deserialize<R: Read>(mut reader: R) -> Result<Self, SerializationError> {
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(AddrInVersion {
addr: canonical_peer_addr(ipv6_addr),
untrusted_services,
})
}
}