use serde::{Deserialize, Serialize};
use crate::error::{IdentityError, Result};
use crate::registry::SignedRevocationEntry;
pub const IDENTITY_TOPIC: &str = "tenzro/identity";
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum IdentityGossipMessage {
RevocationBroadcast(SignedRevocationEntry),
}
pub fn encode_revocation_broadcast(signed: &SignedRevocationEntry) -> Result<Vec<u8>> {
let msg = IdentityGossipMessage::RevocationBroadcast(signed.clone());
bincode::serialize(&msg).map_err(|e| {
IdentityError::SerializationError(format!("encode RevocationBroadcast: {}", e))
})
}
pub fn decode_identity_for_topic(topic: &str, bytes: &[u8]) -> Result<IdentityGossipMessage> {
if topic != IDENTITY_TOPIC {
return Err(IdentityError::SerializationError(format!(
"identity gossip payload arrived on unexpected topic '{}'",
topic
)));
}
bincode::deserialize(bytes).map_err(|e| {
IdentityError::SerializationError(format!("decode IdentityGossipMessage: {}", e))
})
}
#[cfg(test)]
mod tests {
use super::*;
use crate::identity::RevocationEntry;
use chrono::Utc;
use tenzro_crypto::composite::InMemoryHybridSigner;
use tenzro_crypto::pq::MlDsaSigningKey;
use tenzro_crypto::signatures::Ed25519SignerImpl;
use tenzro_crypto::{KeyPair, KeyType};
fn sample_signed_entry() -> SignedRevocationEntry {
let kp = KeyPair::generate(KeyType::Ed25519).expect("keypair");
let classical = Ed25519SignerImpl::new(kp).expect("signer");
let signer = InMemoryHybridSigner::new(Box::new(classical), MlDsaSigningKey::generate());
let entry = RevocationEntry {
did: "did:tenzro:human:test".to_string(),
revoked_at: Utc::now(),
reason: "test".to_string(),
revoked_by: "did:tenzro:system:tenzro-network".to_string(),
};
SignedRevocationEntry::sign(entry, &signer).expect("sign")
}
#[test]
fn round_trip_revocation_broadcast() {
let signed = sample_signed_entry();
let bytes = encode_revocation_broadcast(&signed).expect("encode");
let decoded = decode_identity_for_topic(IDENTITY_TOPIC, &bytes).expect("decode");
let IdentityGossipMessage::RevocationBroadcast(inner) = decoded;
assert_eq!(inner.entry.did, signed.entry.did);
assert!(inner.verify().is_ok());
}
#[test]
fn wrong_topic_rejected() {
let signed = sample_signed_entry();
let bytes = encode_revocation_broadcast(&signed).expect("encode");
assert!(decode_identity_for_topic("tenzro/blocks", &bytes).is_err());
}
}