Merge rust-bitcoin/rust-bitcoin#4521: feat(p2p): add `AddrV2` <> `SocketAddr` conversions

a1d4bc31e5 test(p2p): add tests for `AddrV2` <> `SocketAddr` conversions (Luis Schwab)
64387f566e feat(p2p): add `AddrV2` <> `SocketAddr` conversions (Luis Schwab)

Pull request description:

  Closes #4436.

  Note: I made the `AddrV2::Cjdns` to `SocketAddr` conversion throw the `CjdnsNotRecommended` error. Do we want this behavior or just assume the user knows what he is doing? cc Kixunil

  ### Changelog
  - Implement `From<SocketAddr> for AddrV2`.
  - Implement `TryFrom<AddrV2> for SocketAddr`.
  - Implement `AddrV2ConversionError` enum and it's `fmt::Display`.
  - Tests for these conversions.

ACKs for top commit:
  apoelstra:
    ACK a1d4bc31e5c7cfe0227db64aec8671efcc0c6677; successfully ran local tests
  tcharding:
    ACK a1d4bc31e5

Tree-SHA512: c11f3053428d2c8ca971bbc6bc4ad4619260fe95cba055586f4889d7397733f7d286dcafa111234a6be4a739fd56cdd7e64dbf71b106a71d2483794ca7018105
This commit is contained in:
merge-script 2025-05-17 18:23:56 +00:00
commit e541221950
No known key found for this signature in database
GPG Key ID: C588D63CE41B97C1
1 changed files with 155 additions and 0 deletions

View File

@ -138,6 +138,67 @@ pub enum AddrV2 {
Unknown(u8, Vec<u8>),
}
/// Error types for [`AddrV2`] to [`SocketAddr`] conversion.
#[derive(Debug, PartialEq, Eq)]
pub enum AddrV2ConversionError {
/// A [`AddrV2::TorV3`] address cannot be converted to a [`SocketAddr`].
TorV3NotSupported,
/// A [`AddrV2::I2p`] address cannot be converted to a [`SocketAddr`].
I2pNotSupported,
/// A [`AddrV2::Cjdns`] address can be converted to a [`SocketAddr`],
/// but it won't work with a tradicional socket API.
CjdnsNotRecommended,
/// A [`AddrV2::Unknown`] address cannot be converted to a [`SocketAddr`].
UnknownNotSupported,
}
impl fmt::Display for AddrV2ConversionError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::TorV3NotSupported => write!(f, "TorV3 addresses cannot be converted to SocketAddr"),
Self::I2pNotSupported => write!(f, "I2P addresses cannot be converted to SocketAddr"),
Self::CjdnsNotRecommended => write!(f, "CJDNS addresses can be converted to SocketAddr, but won't work with a traditional socket API"),
Self::UnknownNotSupported => write!(f, "Unknown address type cannot be converted to SocketAddr"),
}
}
}
impl std::error::Error for AddrV2ConversionError {}
impl From<SocketAddr> for AddrV2 {
fn from(addr: SocketAddr) -> Self {
match addr {
SocketAddr::V4(sock) => AddrV2::Ipv4(*sock.ip()),
SocketAddr::V6(sock) => {
// CJDNS uses the IPv6 network `fc00::/8`
// All CJDNS addresses must have `0xfc00` as the first and second octets
let ip = *sock.ip();
if ip.octets()[0] == 0xfc && ip.octets()[1] == 0x00 {
AddrV2::Cjdns(ip)
} else {
AddrV2::Ipv6(ip)
}
}
}
}
}
impl TryFrom<AddrV2> for SocketAddr {
type Error = AddrV2ConversionError;
fn try_from(addr: AddrV2) -> Result<SocketAddr, Self::Error> {
match addr {
AddrV2::Ipv4(ip) => Ok(SocketAddr::V4(SocketAddrV4::new(ip, 0))),
AddrV2::Ipv6(ip) => Ok(SocketAddr::V6(SocketAddrV6::new(ip, 0, 0, 0))),
AddrV2::Cjdns(_) => Err(AddrV2ConversionError::CjdnsNotRecommended),
AddrV2::TorV3(_) => Err(AddrV2ConversionError::TorV3NotSupported),
AddrV2::I2p(_) => Err(AddrV2ConversionError::I2pNotSupported),
AddrV2::Unknown(_, _) => Err(AddrV2ConversionError::UnknownNotSupported),
}
}
}
impl Encodable for AddrV2 {
fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
fn encode_addr<W: Write + ?Sized>(
@ -533,4 +594,98 @@ mod test {
assert_eq!(serialize(&addresses), raw);
}
#[test]
fn socketaddr_to_addrv2_ipv4() {
let socket = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(192, 168, 1, 1), 8333));
let addr = AddrV2::from(socket);
assert_eq!(addr, AddrV2::Ipv4(Ipv4Addr::new(192, 168, 1, 1)));
}
#[test]
fn socketaddr_to_addrv2_ipv6() {
let socket = SocketAddr::V6(SocketAddrV6::new(
Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1),
8333,
0,
0,
));
let addr = AddrV2::from(socket);
assert_eq!(addr, AddrV2::Ipv6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1)));
}
#[test]
fn socketaddr_to_addrv2_cjdns() {
let socket = SocketAddr::V6(SocketAddrV6::new(
Ipv6Addr::new(0xfc00, 0, 0, 0, 0, 0, 0, 1),
8333,
0,
0,
));
let addr = AddrV2::from(socket);
assert_eq!(addr, AddrV2::Cjdns(Ipv6Addr::new(0xfc00, 0, 0, 0, 0, 0, 0, 1)));
}
#[test]
fn addrv2_to_socketaddr_ipv4() {
let addr = AddrV2::Ipv4(Ipv4Addr::new(192, 168, 1, 1));
let socket = SocketAddr::try_from(addr).unwrap();
assert_eq!(socket, SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(192, 168, 1, 1), 0)));
}
#[test]
fn addrv2_to_socketaddr_ipv6() {
let addr = AddrV2::Ipv6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1));
let socket = SocketAddr::try_from(addr).unwrap();
assert_eq!(
socket,
SocketAddr::V6(SocketAddrV6::new(
Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1),
0,
0,
0
))
);
}
#[test]
fn addrv2_to_socketaddr_cjdns() {
let addr = AddrV2::Cjdns(Ipv6Addr::new(0xfc00, 0, 0, 0, 0, 0, 0, 1));
let result = SocketAddr::try_from(addr);
assert!(result.is_err());
assert_eq!(result.unwrap_err(), AddrV2ConversionError::CjdnsNotRecommended);
}
#[test]
fn addrv2_to_socketaddr_torv3() {
let addr = AddrV2::TorV3([0; 32]);
let result = SocketAddr::try_from(addr);
assert!(result.is_err());
assert_eq!(result.unwrap_err(), AddrV2ConversionError::TorV3NotSupported);
}
#[test]
fn addrv2_to_socketaddr_i2p() {
let addr = AddrV2::I2p([0; 32]);
let result = SocketAddr::try_from(addr);
assert!(result.is_err());
assert_eq!(result.unwrap_err(), AddrV2ConversionError::I2pNotSupported);
}
#[test]
fn addrv2_to_socketaddr_unknown() {
let addr = AddrV2::Unknown(42, vec![1, 2, 3, 4]);
let result = SocketAddr::try_from(addr);
assert!(result.is_err());
assert_eq!(result.unwrap_err(), AddrV2ConversionError::UnknownNotSupported);
}
}