mod essence;
mod milestone_id;
use alloc::{boxed::Box, vec::Vec};
use core::ops::RangeInclusive;
use bee_common::packable::{Packable, Read, Write};
use crypto::{
hashes::{blake2b::Blake2b256, Digest},
signatures::ed25519,
Error as CryptoError,
};
pub use self::{
essence::{MilestonePayloadEssence, MILESTONE_MERKLE_PROOF_LENGTH, MILESTONE_PUBLIC_KEY_LENGTH},
milestone_id::{MilestoneId, MILESTONE_ID_LENGTH},
};
use crate::Error;
pub const MILESTONE_SIGNATURE_COUNT_RANGE: RangeInclusive<usize> = 1..=255;
pub const MILESTONE_SIGNATURE_LENGTH: usize = 64;
#[derive(Debug)]
#[allow(missing_docs)]
pub enum MilestoneValidationError {
InvalidMinThreshold,
TooFewSignatures(usize, usize),
InsufficientApplicablePublicKeys(usize, usize),
UnapplicablePublicKey(String),
InvalidSignature(usize, String),
Crypto(CryptoError),
}
impl From<CryptoError> for MilestoneValidationError {
fn from(error: CryptoError) -> Self {
MilestoneValidationError::Crypto(error)
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct MilestonePayload {
essence: MilestonePayloadEssence,
signatures: Vec<Box<[u8]>>,
}
impl MilestonePayload {
pub const KIND: u32 = 1;
pub fn new(
essence: MilestonePayloadEssence,
signatures: Vec<[u8; MILESTONE_SIGNATURE_LENGTH]>,
) -> Result<Self, Error> {
if !MILESTONE_SIGNATURE_COUNT_RANGE.contains(&signatures.len()) {
return Err(Error::MilestoneInvalidSignatureCount(signatures.len()));
}
if essence.public_keys().len() != signatures.len() {
return Err(Error::MilestonePublicKeysSignaturesCountMismatch(
essence.public_keys().len(),
signatures.len(),
));
};
Ok(Self {
essence,
signatures: signatures
.iter()
.map(|s| s.to_vec().into_boxed_slice())
.collect::<Vec<Box<[u8]>>>(),
})
}
pub fn id(&self) -> MilestoneId {
let mut hasher = Blake2b256::new();
hasher.update(Self::KIND.to_le_bytes());
hasher.update(self.pack_new());
MilestoneId::new(hasher.finalize().into())
}
pub fn essence(&self) -> &MilestonePayloadEssence {
&self.essence
}
pub fn signatures(&self) -> &Vec<Box<[u8]>> {
&self.signatures
}
pub fn validate(
&self,
applicable_public_keys: &[String],
min_threshold: usize,
) -> Result<(), MilestoneValidationError> {
if min_threshold == 0 {
return Err(MilestoneValidationError::InvalidMinThreshold);
}
if applicable_public_keys.len() < min_threshold {
return Err(MilestoneValidationError::InsufficientApplicablePublicKeys(
applicable_public_keys.len(),
min_threshold,
));
}
if self.signatures().len() < min_threshold {
return Err(MilestoneValidationError::TooFewSignatures(
min_threshold,
self.signatures().len(),
));
}
let essence_hash = self.essence().hash();
for (index, (public_key, signature)) in self
.essence()
.public_keys()
.iter()
.zip(self.signatures.iter())
.enumerate()
{
if !applicable_public_keys.contains(&hex::encode(public_key)) {
return Err(MilestoneValidationError::UnapplicablePublicKey(hex::encode(public_key)));
}
let ed25519_public_key =
ed25519::PublicKey::try_from_bytes(*public_key).map_err(MilestoneValidationError::Crypto)?;
let ed25519_signature = ed25519::Signature::from_bytes(signature.as_ref().try_into().unwrap());
if !ed25519_public_key.verify(&ed25519_signature, &essence_hash) {
return Err(MilestoneValidationError::InvalidSignature(
index,
hex::encode(public_key),
));
}
}
Ok(())
}
}
impl Packable for MilestonePayload {
type Error = Error;
fn packed_len(&self) -> usize {
self.essence.packed_len() + 0u8.packed_len() + self.signatures.len() * MILESTONE_SIGNATURE_LENGTH
}
fn pack<W: Write>(&self, writer: &mut W) -> Result<(), Self::Error> {
self.essence.pack(writer)?;
(self.signatures.len() as u8).pack(writer)?;
for signature in &self.signatures {
writer.write_all(signature)?;
}
Ok(())
}
fn unpack_inner<R: Read + ?Sized, const CHECK: bool>(reader: &mut R) -> Result<Self, Self::Error> {
let essence = MilestonePayloadEssence::unpack_inner::<R, CHECK>(reader)?;
let signatures_len = u8::unpack_inner::<R, CHECK>(reader)? as usize;
let mut signatures = Vec::with_capacity(signatures_len);
for _ in 0..signatures_len {
signatures.push(<[u8; MILESTONE_SIGNATURE_LENGTH]>::unpack_inner::<R, CHECK>(reader)?);
}
Self::new(essence, signatures)
}
}