use chia_bls::{PublicKey, Signature};
use dig_protocol::Bytes32;
use serde::{Deserialize, Serialize};
use crate::constants::{BLS_SIGNATURE_SIZE, MAX_VALIDATORS_PER_COMMITTEE};
use crate::error::SlashingError;
use crate::evidence::attestation_data::AttestationData;
use crate::traits::PublicKeyLookup;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct IndexedAttestation {
pub attesting_indices: Vec<u32>,
pub data: AttestationData,
#[serde(with = "serde_bytes")]
pub signature: Vec<u8>,
}
impl IndexedAttestation {
pub fn validate_structure(&self) -> Result<(), SlashingError> {
if self.attesting_indices.is_empty() {
return Err(SlashingError::InvalidIndexedAttestation(
"empty attesting indices".into(),
));
}
if self.attesting_indices.len() > MAX_VALIDATORS_PER_COMMITTEE {
return Err(SlashingError::InvalidIndexedAttestation(format!(
"attesting indices length {} exceeds MAX_VALIDATORS_PER_COMMITTEE ({})",
self.attesting_indices.len(),
MAX_VALIDATORS_PER_COMMITTEE,
)));
}
if self.signature.len() != BLS_SIGNATURE_SIZE {
return Err(SlashingError::InvalidIndexedAttestation(format!(
"signature width {} != BLS_SIGNATURE_SIZE ({})",
self.signature.len(),
BLS_SIGNATURE_SIZE,
)));
}
for w in self.attesting_indices.windows(2) {
if w[0] >= w[1] {
return Err(SlashingError::InvalidIndexedAttestation(
"attesting indices not strictly ascending (non-ascending or duplicate)".into(),
));
}
}
Ok(())
}
pub fn verify_signature(
&self,
pks: &dyn PublicKeyLookup,
network_id: &Bytes32,
) -> Result<(), SlashingError> {
let sig_bytes: &[u8; BLS_SIGNATURE_SIZE] = self
.signature
.as_slice()
.try_into()
.map_err(|_| SlashingError::BlsVerifyFailed)?;
let sig = Signature::from_bytes(sig_bytes).map_err(|_| SlashingError::BlsVerifyFailed)?;
let mut pubkeys: Vec<PublicKey> = Vec::with_capacity(self.attesting_indices.len());
for idx in &self.attesting_indices {
match pks.pubkey_of(*idx) {
Some(pk) => pubkeys.push(*pk),
None => return Err(SlashingError::BlsVerifyFailed),
}
}
let signing_root = self.data.signing_root(network_id);
let msg: &[u8] = signing_root.as_ref();
let pairs = pubkeys.iter().map(|pk| (pk, msg));
if !chia_bls::aggregate_verify(&sig, pairs) {
return Err(SlashingError::BlsVerifyFailed);
}
Ok(())
}
}