use crate::error::Error;
use chrono::{DateTime, FixedOffset, SecondsFormat, Utc};
use keri_core::{
event::sections::seal::EventSeal,
event_message::{msg::TypedEvent, Typeable},
prefix::IdentifierPrefix,
};
use said::version::format::SerializationFormats;
use said::{derivation::HashFunctionCode, SelfAddressingIdentifier};
use serde::{de, Deserialize, Serialize, Serializer};
use serde_hex::{Compact, SerHex};
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct TimestampedVCEvent {
#[serde(flatten)]
pub data: VCEvent,
#[serde(
rename = "dt",
serialize_with = "timestamp_serialize",
deserialize_with = "timestamp_deserialize"
)]
timestamp: DateTime<FixedOffset>,
}
impl Typeable for TimestampedVCEvent {
type TypeTag = TelEventType;
fn get_type(&self) -> Self::TypeTag {
match self.data.event_type {
VCEventType::Iss(_) => TelEventType::Iss,
VCEventType::Rev(_) => TelEventType::Rev,
VCEventType::Bis(_) => TelEventType::Bis,
VCEventType::Brv(_) => TelEventType::Brv,
}
}
}
fn timestamp_serialize<S>(x: &DateTime<FixedOffset>, s: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
s.serialize_str(&x.to_rfc3339_opts(SecondsFormat::Micros, false))
}
fn timestamp_deserialize<'de, D>(deserializer: D) -> Result<DateTime<FixedOffset>, D::Error>
where
D: de::Deserializer<'de>,
{
let s: &str = de::Deserialize::deserialize(deserializer)?;
let dt: DateTime<FixedOffset> = chrono::DateTime::parse_from_rfc3339(s).unwrap();
Ok(dt)
}
impl TimestampedVCEvent {
pub fn new(event: VCEvent) -> Self {
Self {
timestamp: Utc::now().into(),
data: event,
}
}
}
impl From<TimestampedVCEvent> for VCEvent {
fn from(item: TimestampedVCEvent) -> Self {
item.data
}
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum TelEventType {
Iss,
Rev,
Bis,
Brv,
}
pub type VCEventMessage = TypedEvent<TelEventType, TimestampedVCEvent>;
impl Typeable for VCEvent {
type TypeTag = TelEventType;
fn get_type(&self) -> TelEventType {
match self.event_type {
VCEventType::Iss(_) => TelEventType::Iss,
VCEventType::Rev(_) => TelEventType::Rev,
VCEventType::Bis(_) => TelEventType::Bis,
VCEventType::Brv(_) => TelEventType::Brv,
}
}
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct VCEvent {
#[serde(rename = "i")]
pub prefix: IdentifierPrefix,
#[serde(rename = "s", with = "SerHex::<Compact>")]
pub sn: u64,
#[serde(flatten)]
pub event_type: VCEventType,
}
impl VCEvent {
pub fn new(prefix: IdentifierPrefix, sn: u64, event_type: VCEventType) -> Self {
Self {
prefix,
sn,
event_type,
}
}
pub fn to_message(
self,
format: SerializationFormats,
derivation: HashFunctionCode,
) -> Result<VCEventMessage, Error> {
let timestamped = TimestampedVCEvent::new(self);
Ok(TypedEvent::<TelEventType, TimestampedVCEvent>::new(
format,
derivation.into(),
timestamped,
))
}
pub fn registry_id(&self) -> Result<IdentifierPrefix, Error> {
Ok(match &self.event_type {
VCEventType::Rev(rev) => rev.registry_id.clone(),
VCEventType::Iss(iss) => iss.registry_id.clone(),
VCEventType::Bis(iss) => iss.registry_anchor.prefix.clone(),
VCEventType::Brv(rev) => rev
.registry_anchor
.as_ref()
.ok_or(Error::Generic("No registry id".into()))?
.prefix
.clone(),
})
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(untagged, rename_all = "lowercase")]
pub enum VCEventType {
Rev(SimpleRevocation),
Iss(SimpleIssuance),
Bis(Issuance),
Brv(Revocation),
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Issuance {
#[serde(rename = "ii")]
issuer_id: IdentifierPrefix,
#[serde(rename = "ra")]
registry_anchor: EventSeal,
}
impl Issuance {
pub fn new(issuer_id: IdentifierPrefix, registry_anchor: EventSeal) -> Self {
Self {
issuer_id,
registry_anchor,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SimpleIssuance {
#[serde(rename = "ri")]
pub registry_id: IdentifierPrefix,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SimpleRevocation {
#[serde(rename = "ri")]
pub registry_id: IdentifierPrefix,
#[serde(rename = "p")]
pub prev_event_hash: SelfAddressingIdentifier,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Revocation {
#[serde(rename = "p")]
pub prev_event_hash: SelfAddressingIdentifier,
#[serde(rename = "ra")]
pub registry_anchor: Option<EventSeal>,
}
#[test]
fn test_tel_event_serialization() -> Result<(), Error> {
let iss_raw = r#"{"v":"KERI10JSON0000ed_","t":"iss","d":"EELqqdELW6CUVWfmsbt5sxfQfEOykyOWdUV12biBR4TH","i":"DAtNTPnDFBnmlO6J44LXCrzZTAmpe-82b7BmQGtL4QhM","s":"0","ri":"EE3Xv6CWwEMpW-99rhPD9IHFCR2LN5ienLVI8yG5faBw","dt":"2021-01-01T00:00:00.000000+00:00"}"#;
let iss_ev: VCEventMessage = serde_json::from_str(&iss_raw).unwrap();
assert_eq!(serde_json::to_string(&iss_ev).unwrap(), iss_raw);
let rev_raw = r#"{"v":"KERI10JSON000120_","t":"rev","d":"EGtAthwVjf0O9qsSz0HR-C63DSEBhn3kRoxvmuRFECOQ","i":"DAtNTPnDFBnmlO6J44LXCrzZTAmpe-82b7BmQGtL4QhM","s":"1","ri":"EE3Xv6CWwEMpW-99rhPD9IHFCR2LN5ienLVI8yG5faBw","p":"EB2L3ycqK9645aEeQKP941xojSiuiHsw4Y6yTW-PmsBg","dt":"2021-01-01T00:00:00.000000+00:00"}"#;
let rev_ev: VCEventMessage = serde_json::from_str(&rev_raw).unwrap();
assert_eq!(serde_json::to_string(&rev_ev).unwrap(), rev_raw);
let bis_raw = r#"{"v":"KERI10JSON000162_","t":"bis","d":"EF60WQClTmmJqbuHFHBAwmKiCT8RdE4rs6sIVC3s2_AH","i":"EC8Oej-3HAUpBY_kxzBK3B-0RV9j4dXw1H0NRKxJg7g-","s":"0","ii":"EKKJ0FoLxO1TYmyuprguKO7kJ7Hbn0m0Wuk5aMtSrMtY","ra":{"i":"EIZlA3TANi3p8vEu4VQMjPnY0sPFAag1ekIwyyR6lAsq","s":"0","d":"EFSL6HebpbWsxKxfdS4t6NbKTdO4hAUIAxvhmWVf3Z8o"},"dt":"2023-01-10T10:33:57.273969+00:00"}"#; let bis_ev: VCEventMessage = serde_json::from_str(&bis_raw).unwrap();
assert_eq!(serde_json::to_string(&bis_ev).unwrap(), bis_raw);
let brv_raw = r#"{"v":"KERI10JSON00015f_","t":"brv","d":"EAIZZ8ujQQl4XGMh8XPzxokkzqrWh8M6FtxqkezbVtDu","i":"DAtNTPnDFBnmlO6J44LXCrzZTAmpe-82b7BmQGtL4QhM","s":"1","p":"EC2L3ycqK9645aEeQKP941xojSiuiHsw4Y6yTW-PmsBg","ra":{"i":"EE3Xv6CWwEMpW-99rhPD9IHFCR2LN5ienLVI8yG5faBw","s":"3","d":"EBpq06UecHwzy-K9FpNoRxCJp2wIGM9u2Edk-PLMZ1H4"},"dt":"2021-01-01T00:00:00.000000+00:00"}"#;
let brv_ev: VCEventMessage = serde_json::from_str(&brv_raw).unwrap();
assert_eq!(serde_json::to_string(&brv_ev).unwrap(), brv_raw);
Ok(())
}