use std::net::SocketAddrV4;
use hedera_proto::services;
use crate::protobuf::ToProtobuf;
use crate::{
AccountId,
Error,
FromProtobuf,
};
fn parse_socket_addr_v4(ip: Vec<u8>, port: i32) -> crate::Result<SocketAddrV4> {
let octets: Result<[u8; 4], _> = ip.try_into();
let octets = octets.map_err(|v| {
Error::from_protobuf(format!("expected 4 byte ip address, got `{}` bytes", v.len()))
})?;
let port = u16::try_from(port).map_err(|_| {
Error::from_protobuf(format!(
"expected 16 bit non-negative port number, but the port was actually `{port}`",
))
})?;
Ok(SocketAddrV4::new(octets.into(), port))
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "ffi", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "ffi", serde(rename_all = "camelCase"))]
pub struct NodeAddress {
pub node_id: u64,
#[cfg_attr(feature = "ffi", serde(with = "serde_with::As::<serde_with::base64::Base64>"))]
pub rsa_public_key: Vec<u8>,
pub node_account_id: AccountId,
#[cfg_attr(feature = "ffi", serde(with = "serde_with::As::<serde_with::base64::Base64>"))]
pub tls_certificate_hash: Vec<u8>,
pub service_endpoints: Vec<SocketAddrV4>,
pub description: String,
}
impl FromProtobuf<services::NodeAddress> for NodeAddress {
fn from_protobuf(pb: services::NodeAddress) -> crate::Result<Self>
where
Self: Sized,
{
let mut addresses = Vec::with_capacity(pb.service_endpoint.len() + 1);
#[allow(deprecated)]
if !pb.ip_address.is_empty() {
addresses.push(parse_socket_addr_v4(pb.ip_address, pb.portno)?);
}
for address in pb.service_endpoint {
addresses.push(parse_socket_addr_v4(address.ip_address_v4, address.port)?);
}
let node_account_id = AccountId::from_protobuf(pb_getf!(pb, node_account_id)?)?;
Ok(Self {
description: pb.description,
rsa_public_key: hex::decode(pb.rsa_pub_key).map_err(Error::from_protobuf)?,
node_id: pb.node_id as u64,
service_endpoints: addresses,
tls_certificate_hash: pb.node_cert_hash,
node_account_id,
})
}
}
impl ToProtobuf for NodeAddress {
type Protobuf = services::NodeAddress;
fn to_protobuf(&self) -> Self::Protobuf {
let service_endpoint = self
.service_endpoints
.iter()
.map(|it| services::ServiceEndpoint {
ip_address_v4: it.ip().octets().to_vec(),
port: i32::from(it.port()),
})
.collect();
services::NodeAddress {
rsa_pub_key: hex::encode(&self.rsa_public_key),
node_id: self.node_id as i64,
node_account_id: Some(self.node_account_id.to_protobuf()),
node_cert_hash: self.tls_certificate_hash.clone(),
service_endpoint,
description: self.description.clone(),
..Default::default()
}
}
}