1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
/*
*
* Hedera Rust SDK
*
* Copyright (C) 2022 - 2023 Hedera Hashgraph, LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
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))
}
/// The data about a node, including its service endpoints and the Hedera account to be paid for
/// services provided by the node (that is, queries answered and transactions submitted.).
#[derive(Debug, Clone)]
#[cfg_attr(feature = "ffi", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "ffi", serde(rename_all = "camelCase"))]
pub struct NodeAddress {
/// A non-sequential, unique, static identifier for the node
pub node_id: u64,
/// The node's X509 RSA public key used to sign stream files.
#[cfg_attr(feature = "ffi", serde(with = "serde_with::As::<serde_with::base64::Base64>"))]
pub rsa_public_key: Vec<u8>,
/// The account to be paid for queries and transactions sent to this node.
pub node_account_id: AccountId,
/// Hash of the node's TLS certificate.
///
/// Precisely, this field is a string of
/// hexadecimal characters which, translated to binary, are the SHA-384 hash of
/// the UTF-8 NFKD encoding of the node's TLS cert in PEM format.
///
/// Its value can be used to verify the node's certificate it presents during TLS negotiations.
#[cfg_attr(feature = "ffi", serde(with = "serde_with::As::<serde_with::base64::Base64>"))]
pub tls_certificate_hash: Vec<u8>,
/// A node's service IP addresses and ports.
pub service_endpoints: Vec<SocketAddrV4>,
/// A description of the node, up to 100 bytes.
pub description: String,
}
impl FromProtobuf<services::NodeAddress> for NodeAddress {
fn from_protobuf(pb: services::NodeAddress) -> crate::Result<Self>
where
Self: Sized,
{
// sometimes this will be oversized by 1, but that's fine.
let mut addresses = Vec::with_capacity(pb.service_endpoint.len() + 1);
// `ip_address`/`portno` are deprecated, but lets handle them anyway.
#[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(),
// deprecated fields
..Default::default()
}
}
}