use alloc::vec::Vec;
use miden_crypto::dsa::ecdsa_k256_keccak::Signature;
use crate::Word;
use crate::block::ValidatorKeys;
use crate::utils::serde::{
ByteReader,
ByteWriter,
Deserializable,
DeserializationError,
Serializable,
};
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum SignatureVerificationError {
#[error("block has {actual} signatures but the validator set has {expected} keys")]
SignatureCountMismatch { expected: usize, actual: usize },
#[error(
"block signature at position {position} does not verify against the validator key at that position"
)]
InvalidSignatureAtPosition { position: usize },
}
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum BlockSignaturesError {
#[error(
"block signature set contains {count} signatures but must contain at most {max}",
max = ValidatorKeys::MAX,
)]
TooManySignatures { count: usize },
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BlockSignatures {
signatures: Vec<Signature>,
}
impl BlockSignatures {
pub fn new(signatures: Vec<Signature>) -> Result<Self, BlockSignaturesError> {
if signatures.len() > ValidatorKeys::MAX {
return Err(BlockSignaturesError::TooManySignatures { count: signatures.len() });
}
Ok(Self { signatures })
}
pub fn as_signatures(&self) -> &[Signature] {
&self.signatures
}
pub fn len(&self) -> usize {
self.signatures.len()
}
pub fn is_empty(&self) -> bool {
self.signatures.is_empty()
}
pub fn verify_against(
&self,
block_commitment: Word,
validator_keys: &ValidatorKeys,
) -> Result<(), SignatureVerificationError> {
if self.signatures.len() != validator_keys.len() {
return Err(SignatureVerificationError::SignatureCountMismatch {
expected: validator_keys.len(),
actual: self.signatures.len(),
});
}
for (position, (signature, validator_key)) in
self.signatures.iter().zip(validator_keys.as_keys()).enumerate()
{
if !signature.verify(block_commitment, validator_key) {
return Err(SignatureVerificationError::InvalidSignatureAtPosition { position });
}
}
Ok(())
}
}
impl Serializable for BlockSignatures {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
self.signatures.write_into(target);
}
}
impl Deserializable for BlockSignatures {
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
let signatures = Vec::<Signature>::read_from(source)?;
Ok(Self { signatures })
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::testing::random_secret_key::random_secret_key;
use crate::testing::validator_keys::{random_validator_set, sign_all};
#[test]
fn verify_against_accepts_correctly_ordered_signatures() {
let (signers, keys) = random_validator_set(5);
let commitment = Word::empty();
let signatures = sign_all(&keys, &signers, commitment);
assert_eq!(signatures.len(), 5);
signatures.verify_against(commitment, &keys).unwrap();
}
#[test]
fn verify_against_rejects_invalid_signature() {
let (signers, keys) = random_validator_set(3);
let commitment = Word::empty();
let outsider = random_secret_key();
let mut signatures = sign_all(&keys, &signers, commitment).as_signatures().to_vec();
signatures[1] = outsider.sign(commitment);
let signatures = BlockSignatures::new(signatures).unwrap();
assert!(matches!(
signatures.verify_against(commitment, &keys),
Err(SignatureVerificationError::InvalidSignatureAtPosition { position: 1 })
));
}
#[test]
fn verify_against_rejects_mismatched_keys() {
let (signers, keys) = random_validator_set(3);
let commitment = Word::empty();
let signatures = sign_all(&keys, &signers, commitment);
let (_, other_keys) = random_validator_set(3);
assert!(matches!(
signatures.verify_against(commitment, &other_keys),
Err(SignatureVerificationError::InvalidSignatureAtPosition { .. })
));
}
#[test]
fn verify_against_rejects_count_mismatch() {
let (signers, keys) = random_validator_set(3);
let commitment = Word::empty();
let signatures = sign_all(&keys, &signers, commitment);
let (_, other_keys) = random_validator_set(4);
assert!(matches!(
signatures.verify_against(commitment, &other_keys),
Err(SignatureVerificationError::SignatureCountMismatch { expected: 4, actual: 3 })
));
}
#[test]
fn serde_round_trip() {
let (signers, keys) = random_validator_set(3);
let commitment = Word::empty();
let signatures = sign_all(&keys, &signers, commitment);
let bytes = signatures.to_bytes();
let deserialized = BlockSignatures::read_from_bytes(&bytes).unwrap();
assert_eq!(signatures, deserialized);
}
}