use alloc::boxed::Box;
use alloc::string::String;
use alloc::string::ToString;
use core::fmt;
use serde::{Deserialize, Serialize};
use serde_with::serde_as;
use sha3::{digest::Update, Digest, Keccak256};
use umbral_pre::{serde_bytes, PublicKey, RecoverableSignature, Signature, Signer};
use crate::address::Address;
use crate::fleet_state::FleetStateChecksum;
use crate::versioning::{
messagepack_deserialize, messagepack_serialize, ProtocolObject, ProtocolObjectInner,
};
use crate::VerificationError;
pub enum AddressDerivationError {
NoSignatureInPayload,
RecoveryFailed(String),
}
impl fmt::Display for AddressDerivationError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::NoSignatureInPayload => write!(f, "Signature is missing from the payload"),
Self::RecoveryFailed(err) => write!(
f,
"Failed to recover the public key from the signature: {}",
err
),
}
}
}
fn encode_defunct(message: &[u8]) -> Keccak256 {
Keccak256::new()
.chain(b"\x19")
.chain(b"E") .chain(b"thereum Signed Message:\n") .chain(message.len().to_string().as_bytes())
.chain(message)
}
#[serde_as]
#[derive(PartialEq, Eq, Debug, Serialize, Deserialize, Clone)]
pub struct NodeMetadataPayload {
pub staking_provider_address: Address,
pub domain: String,
pub timestamp_epoch: u32,
pub verifying_key: PublicKey,
pub encrypting_key: PublicKey,
#[serde(with = "serde_bytes::as_base64")]
pub ferveo_public_key: Box<[u8]>, #[serde(with = "serde_bytes::as_base64")]
pub certificate_der: Box<[u8]>,
pub host: String,
pub port: u16,
pub operator_signature: RecoverableSignature,
}
impl NodeMetadataPayload {
fn to_bytes(&self) -> Box<[u8]> {
messagepack_serialize(self)
}
pub fn derive_operator_address(&self) -> Result<Address, AddressDerivationError> {
let digest = encode_defunct(&self.verifying_key.to_compressed_bytes());
let key = PublicKey::recover_from_prehash(&digest.finalize(), &self.operator_signature)
.map_err(AddressDerivationError::RecoveryFailed)?;
Ok(Address::from_public_key(&key))
}
}
#[derive(PartialEq, Eq, Debug, Serialize, Deserialize, Clone)]
pub struct NodeMetadata {
signature: Signature,
pub payload: NodeMetadataPayload,
}
impl NodeMetadata {
pub fn new(signer: &Signer, payload: &NodeMetadataPayload) -> Self {
Self {
signature: signer.sign(&payload.to_bytes()),
payload: payload.clone(),
}
}
pub fn verify(&self) -> bool {
self.signature
.verify(&self.payload.verifying_key, &self.payload.to_bytes())
}
}
impl<'a> ProtocolObjectInner<'a> for NodeMetadata {
fn brand() -> [u8; 4] {
*b"NdMd"
}
fn version() -> (u16, u16) {
(4, 0)
}
fn unversioned_to_bytes(&self) -> Box<[u8]> {
messagepack_serialize(&self)
}
fn unversioned_from_bytes(minor_version: u16, bytes: &[u8]) -> Option<Result<Self, String>> {
if minor_version == 0 {
Some(messagepack_deserialize(bytes))
} else {
None
}
}
}
impl<'a> ProtocolObject<'a> for NodeMetadata {}
#[derive(PartialEq, Eq, Debug, Serialize, Deserialize, Clone)]
pub struct MetadataRequest {
pub fleet_state_checksum: FleetStateChecksum,
pub announce_nodes: Box<[NodeMetadata]>,
}
impl MetadataRequest {
pub fn new(fleet_state_checksum: &FleetStateChecksum, announce_nodes: &[NodeMetadata]) -> Self {
Self {
fleet_state_checksum: *fleet_state_checksum,
announce_nodes: announce_nodes.to_vec().into_boxed_slice(),
}
}
}
impl<'a> ProtocolObjectInner<'a> for MetadataRequest {
fn brand() -> [u8; 4] {
*b"MdRq"
}
fn version() -> (u16, u16) {
(3, 0)
}
fn unversioned_to_bytes(&self) -> Box<[u8]> {
messagepack_serialize(&self)
}
fn unversioned_from_bytes(minor_version: u16, bytes: &[u8]) -> Option<Result<Self, String>> {
if minor_version == 0 {
Some(messagepack_deserialize(bytes))
} else {
None
}
}
}
impl<'a> ProtocolObject<'a> for MetadataRequest {}
#[derive(PartialEq, Eq, Debug, Serialize, Deserialize, Clone)]
pub struct MetadataResponsePayload {
pub timestamp_epoch: u32,
pub announce_nodes: Box<[NodeMetadata]>,
}
impl MetadataResponsePayload {
pub fn new(timestamp_epoch: u32, announce_nodes: &[NodeMetadata]) -> Self {
Self {
timestamp_epoch,
announce_nodes: announce_nodes.to_vec().into_boxed_slice(),
}
}
fn to_bytes(&self) -> Box<[u8]> {
messagepack_serialize(self)
}
}
#[derive(PartialEq, Eq, Debug, Serialize, Deserialize, Clone)]
pub struct MetadataResponse {
signature: Signature,
payload: MetadataResponsePayload,
}
impl MetadataResponse {
pub fn new(signer: &Signer, payload: &MetadataResponsePayload) -> Self {
Self {
signature: signer.sign(&payload.to_bytes()),
payload: payload.clone(),
}
}
pub fn verify(
self,
verifying_pk: &PublicKey,
) -> Result<MetadataResponsePayload, VerificationError> {
if self
.signature
.verify(verifying_pk, &self.payload.to_bytes())
{
Ok(self.payload)
} else {
Err(VerificationError)
}
}
}
impl<'a> ProtocolObjectInner<'a> for MetadataResponse {
fn brand() -> [u8; 4] {
*b"MdRs"
}
fn version() -> (u16, u16) {
(3, 0)
}
fn unversioned_to_bytes(&self) -> Box<[u8]> {
messagepack_serialize(&self)
}
fn unversioned_from_bytes(minor_version: u16, bytes: &[u8]) -> Option<Result<Self, String>> {
if minor_version == 0 {
Some(messagepack_deserialize(bytes))
} else {
None
}
}
}
impl<'a> ProtocolObject<'a> for MetadataResponse {}