use super::{EventKind, Id, Metadata, PrivateKey, PublicKey, Signature, Tag, Unixtime};
use crate::Error;
use k256::sha2::{Digest, Sha256};
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct Event {
pub id: Id,
pub pubkey: PublicKey,
pub created_at: Unixtime,
pub kind: EventKind,
pub tags: Vec<Tag>,
pub content: String,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default)]
pub ots: Option<String>,
pub sig: Signature,
}
macro_rules! serialize_inner_event {
($pubkey:expr, $created_at:expr, $kind:expr, $tags:expr,
$content:expr) => {{
format!(
"[0,{},{},{},{},{}]",
serde_json::to_string(&$pubkey)?,
serde_json::to_string(&$created_at)?,
serde_json::to_string(&$kind)?,
serde_json::to_string(&$tags)?,
serde_json::to_string(&$content)?
)
}};
}
#[derive(Clone, Debug)]
pub struct PreEvent {
pub pubkey: PublicKey,
pub created_at: Unixtime,
pub kind: EventKind,
pub tags: Vec<Tag>,
pub content: String,
pub ots: Option<String>,
}
impl Event {
pub fn new(input: PreEvent, privkey: PrivateKey) -> Result<Event, Error> {
let serialized: String = serialize_inner_event!(
input.pubkey,
input.created_at,
input.kind,
input.tags,
input.content
);
let mut hasher = Sha256::new();
hasher.update(serialized.as_bytes());
let id = hasher.finalize();
let id: [u8; 32] = id.into();
let id: Id = Id(id);
let signature = privkey.sign_id(id)?;
Ok(Event {
id,
pubkey: input.pubkey,
created_at: input.created_at,
kind: input.kind,
tags: input.tags,
content: input.content,
ots: input.ots,
sig: signature,
})
}
pub fn verify(&self, maxtime: Option<Unixtime>) -> Result<(), Error> {
use k256::schnorr::signature::Verifier;
let serialized: String = serialize_inner_event!(
self.pubkey,
self.created_at,
self.kind,
self.tags,
self.content
);
self.pubkey.0.verify(serialized.as_bytes(), &self.sig.0)?;
let mut hasher = Sha256::new();
hasher.update(serialized.as_bytes());
let id = hasher.finalize();
if let Some(mt) = maxtime {
if self.created_at > mt {
return Err(Error::EventInFuture);
}
}
if *id != self.id.0 {
Err(Error::HashMismatch)
} else {
Ok(())
}
}
#[allow(dead_code)]
pub(crate) fn mock() -> Event {
let private_key = PrivateKey::mock();
let public_key = private_key.public_key();
let pre = PreEvent {
pubkey: public_key,
created_at: Unixtime::mock(),
kind: EventKind::mock(),
tags: vec![Tag::mock(), Tag::mock()],
content: "This is a test".to_string(),
ots: None,
};
Event::new(pre, private_key).unwrap()
}
pub fn new_set_metadata(
mut input: PreEvent,
privkey: PrivateKey,
name: Option<String>,
about: Option<String>,
picture: Option<String>,
nip05: Option<String>,
) -> Result<Event, Error> {
input.kind = EventKind::Metadata;
let metadata = Metadata {
name,
about,
picture,
nip05,
};
input.content = serde_json::to_string(&metadata)?;
Event::new(input, privkey)
}
pub fn is_reply(&self) -> bool {
if self.kind != EventKind::TextNote {
return false;
}
for tag in self.tags.iter() {
if let Tag::Event { .. } = tag {
return true;
}
}
false
}
}
#[cfg(test)]
mod test {
use crate::types::*;
test_serde! {Event, test_event_serde}
#[test]
fn test_event_new_and_verify() {
let privkey = PrivateKey::mock();
let pubkey = privkey.public_key();
let preevent = PreEvent {
pubkey: pubkey.clone(),
created_at: Unixtime::mock(),
kind: EventKind::TextNote,
tags: vec![Tag::Event {
id: Id::mock(),
recommended_relay_url: Some(Url::mock()),
marker: None,
}],
content: "Hello World!".to_string(),
ots: None,
};
let mut event = Event::new(preevent, privkey).unwrap();
assert!(event.verify(None).is_ok());
event.content = "I'm changing this message".to_string();
let result = event.verify(None);
assert!(result.is_err());
event.content = "Hello World!".to_string();
let result = event.verify(None);
assert!(result.is_ok());
event.id = Id([
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
24, 25, 26, 27, 28, 29, 30, 31,
]);
let result = event.verify(None);
assert!(result.is_err());
}
}