use alloc::boxed::Box;
use alloc::string::String;
use alloc::string::ToString;
use core::fmt;
use k256::ecdsa::recoverable;
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
use serde_with::serde_as;
use sha3::{Digest, Keccak256};
use signature::digest::Update;
use umbral_pre::{serde_bytes, PublicKey, Signature, Signer};
use crate::address::Address;
use crate::fleet_state::FleetStateChecksum;
use crate::versioning::{
messagepack_deserialize, messagepack_serialize, ProtocolObject, ProtocolObjectInner,
};
use crate::VerificationError;
#[derive(Serialize, Deserialize)]
struct SerializableSignature(#[serde(with = "serde_bytes::as_base64")] Box<[u8]>);
impl serde_with::SerializeAs<recoverable::Signature> for SerializableSignature {
fn serialize_as<S>(source: &recoverable::Signature, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
SerializableSignature(source.as_ref().into()).serialize(serializer)
}
}
impl<'de> serde_with::DeserializeAs<'de, recoverable::Signature> for SerializableSignature {
fn deserialize_as<D>(deserializer: D) -> Result<recoverable::Signature, D::Error>
where
D: Deserializer<'de>,
{
let sig_bytes = SerializableSignature::deserialize(deserializer)?;
recoverable::Signature::try_from(sig_bytes.0.as_ref()).map_err(de::Error::custom)
}
}
pub enum AddressDerivationError {
NoSignatureInPayload,
RecoveryFailed(signature::Error),
}
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)
}
pub const RECOVERABLE_SIGNATURE_SIZE: usize = recoverable::SIZE;
#[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 certificate_der: Box<[u8]>,
pub host: String,
pub port: u16,
#[serde_as(as = "Option<SerializableSignature>")]
pub operator_signature: Option<recoverable::Signature>,
}
impl NodeMetadataPayload {
fn to_bytes(&self) -> Box<[u8]> {
messagepack_serialize(self)
}
pub fn derive_operator_address(&self) -> Result<Address, AddressDerivationError> {
let signature = self
.operator_signature
.ok_or(AddressDerivationError::NoSignatureInPayload)?;
let message = encode_defunct(&self.verifying_key.to_compressed_bytes());
let key = signature
.recover_verifying_key_from_digest(message)
.map_err(AddressDerivationError::RecoveryFailed)?;
Ok(Address::from_k256_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) {
(2, 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) {
(2, 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) {
(2, 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 {}