1use alloc::boxed::Box;
2use alloc::string::String;
3use alloc::string::ToString;
4use core::fmt;
5
6use ferveo::api::ValidatorPublicKey as FerveoPublicKey;
7use serde::{Deserialize, Serialize};
8use serde_with::serde_as;
9use sha3::{digest::Update, Digest, Keccak256};
10use umbral_pre::{serde_bytes, PublicKey, RecoverableSignature, Signature, Signer};
11
12use crate::address::Address;
13use crate::fleet_state::FleetStateChecksum;
14use crate::versioning::{
15 messagepack_deserialize, messagepack_serialize, ProtocolObject, ProtocolObjectInner,
16};
17use crate::VerificationError;
18
19pub enum AddressDerivationError {
21 NoSignatureInPayload,
23 RecoveryFailed(String),
25}
26
27impl fmt::Display for AddressDerivationError {
28 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
29 match self {
30 Self::NoSignatureInPayload => write!(f, "Signature is missing from the payload"),
31 Self::RecoveryFailed(err) => write!(
32 f,
33 "Failed to recover the public key from the signature: {err}"
34 ),
35 }
36 }
37}
38
39fn encode_defunct(message: &[u8]) -> Keccak256 {
41 Keccak256::new()
42 .chain(b"\x19")
43 .chain(b"E") .chain(b"thereum Signed Message:\n") .chain(message.len().to_string().as_bytes())
46 .chain(message)
47}
48
49#[serde_as]
51#[derive(PartialEq, Eq, Debug, Serialize, Deserialize, Clone)]
52pub struct NodeMetadataPayload {
53 pub staking_provider_address: Address,
55 pub domain: String,
57 pub timestamp_epoch: u32,
59 pub verifying_key: PublicKey,
61 pub encrypting_key: PublicKey,
63 pub ferveo_public_key: FerveoPublicKey,
65 #[serde(with = "serde_bytes::as_base64")]
67 pub certificate_der: Box<[u8]>,
68 pub host: String,
70 pub port: u16,
72 pub operator_signature: RecoverableSignature,
74}
75
76impl NodeMetadataPayload {
77 fn to_bytes(&self) -> Box<[u8]> {
79 messagepack_serialize(self)
80 }
81
82 pub fn derive_operator_address(&self) -> Result<Address, AddressDerivationError> {
85 let digest = encode_defunct(&self.verifying_key.to_compressed_bytes());
86 let key = PublicKey::recover_from_prehash(&digest.finalize(), &self.operator_signature)
87 .map_err(AddressDerivationError::RecoveryFailed)?;
88 Ok(Address::from_public_key(&key))
89 }
90}
91
92#[derive(PartialEq, Eq, Debug, Serialize, Deserialize, Clone)]
94pub struct NodeMetadata {
95 signature: Signature,
96 pub payload: NodeMetadataPayload,
98}
99
100impl NodeMetadata {
101 pub fn new(signer: &Signer, payload: &NodeMetadataPayload) -> Self {
103 Self {
105 signature: signer.sign(&payload.to_bytes()),
106 payload: payload.clone(),
107 }
108 }
109
110 pub fn verify(&self) -> bool {
112 self.signature
120 .verify(&self.payload.verifying_key, &self.payload.to_bytes())
121 }
122}
123
124impl<'a> ProtocolObjectInner<'a> for NodeMetadata {
125 fn brand() -> [u8; 4] {
126 *b"NdMd"
127 }
128
129 fn version() -> (u16, u16) {
130 (4, 0)
135 }
136
137 fn unversioned_to_bytes(&self) -> Box<[u8]> {
138 messagepack_serialize(&self)
139 }
140
141 fn unversioned_from_bytes(minor_version: u16, bytes: &[u8]) -> Option<Result<Self, String>> {
142 if minor_version == 0 {
143 Some(messagepack_deserialize(bytes))
144 } else {
145 None
146 }
147 }
148}
149
150impl<'a> ProtocolObject<'a> for NodeMetadata {}
151
152#[derive(PartialEq, Eq, Debug, Serialize, Deserialize, Clone)]
154pub struct MetadataRequest {
155 pub fleet_state_checksum: FleetStateChecksum,
157 pub announce_nodes: Box<[NodeMetadata]>,
159}
160
161impl MetadataRequest {
162 pub fn new(fleet_state_checksum: &FleetStateChecksum, announce_nodes: &[NodeMetadata]) -> Self {
164 Self {
165 fleet_state_checksum: *fleet_state_checksum,
166 announce_nodes: announce_nodes.to_vec().into_boxed_slice(),
167 }
168 }
169}
170
171impl<'a> ProtocolObjectInner<'a> for MetadataRequest {
172 fn brand() -> [u8; 4] {
173 *b"MdRq"
174 }
175
176 fn version() -> (u16, u16) {
177 (3, 0)
178 }
179
180 fn unversioned_to_bytes(&self) -> Box<[u8]> {
181 messagepack_serialize(&self)
182 }
183
184 fn unversioned_from_bytes(minor_version: u16, bytes: &[u8]) -> Option<Result<Self, String>> {
185 if minor_version == 0 {
186 Some(messagepack_deserialize(bytes))
187 } else {
188 None
189 }
190 }
191}
192
193impl<'a> ProtocolObject<'a> for MetadataRequest {}
194
195#[derive(PartialEq, Eq, Debug, Serialize, Deserialize, Clone)]
197pub struct MetadataResponsePayload {
198 pub timestamp_epoch: u32,
201 pub announce_nodes: Box<[NodeMetadata]>,
203}
204
205impl MetadataResponsePayload {
206 pub fn new(timestamp_epoch: u32, announce_nodes: &[NodeMetadata]) -> Self {
208 Self {
209 timestamp_epoch,
210 announce_nodes: announce_nodes.to_vec().into_boxed_slice(),
211 }
212 }
213
214 fn to_bytes(&self) -> Box<[u8]> {
216 messagepack_serialize(self)
217 }
218}
219
220#[derive(PartialEq, Eq, Debug, Serialize, Deserialize, Clone)]
222pub struct MetadataResponse {
223 signature: Signature,
224 payload: MetadataResponsePayload,
225}
226
227impl MetadataResponse {
228 pub fn new(signer: &Signer, payload: &MetadataResponsePayload) -> Self {
230 Self {
231 signature: signer.sign(&payload.to_bytes()),
232 payload: payload.clone(),
233 }
234 }
235
236 pub fn verify(
238 self,
239 verifying_pk: &PublicKey,
240 ) -> Result<MetadataResponsePayload, VerificationError> {
241 if self
242 .signature
243 .verify(verifying_pk, &self.payload.to_bytes())
244 {
245 Ok(self.payload)
246 } else {
247 Err(VerificationError)
248 }
249 }
250}
251
252impl<'a> ProtocolObjectInner<'a> for MetadataResponse {
253 fn brand() -> [u8; 4] {
254 *b"MdRs"
255 }
256
257 fn version() -> (u16, u16) {
258 (3, 0)
264 }
265
266 fn unversioned_to_bytes(&self) -> Box<[u8]> {
267 messagepack_serialize(&self)
268 }
269
270 fn unversioned_from_bytes(minor_version: u16, bytes: &[u8]) -> Option<Result<Self, String>> {
271 if minor_version == 0 {
272 Some(messagepack_deserialize(bytes))
273 } else {
274 None
275 }
276 }
277}
278
279impl<'a> ProtocolObject<'a> for MetadataResponse {}