|
@@ -1,6 +1,7 @@
|
|
-use super::{addr, Addr, Signature};
|
|
|
|
|
|
+use super::{Id, Signature};
|
|
use chrono::{DateTime, Utc};
|
|
use chrono::{DateTime, Utc};
|
|
-use serde::{Deserialize, Serialize};
|
|
|
|
|
|
+use secp256k1::{schnorr, Message, XOnlyPublicKey};
|
|
|
|
+use serde::{de, Deserialize, Serialize};
|
|
use serde_json::json;
|
|
use serde_json::json;
|
|
use sha2::{Digest, Sha256};
|
|
use sha2::{Digest, Sha256};
|
|
use thiserror::Error;
|
|
use thiserror::Error;
|
|
@@ -11,12 +12,19 @@ pub enum Error {
|
|
Serialize(#[from] serde_json::Error),
|
|
Serialize(#[from] serde_json::Error),
|
|
|
|
|
|
#[error("Error serializing id: {0}")]
|
|
#[error("Error serializing id: {0}")]
|
|
- Id(#[from] addr::Error),
|
|
|
|
|
|
+ Id(#[from] hex::FromHexError),
|
|
|
|
+
|
|
|
|
+ #[error("Signature error: {0}")]
|
|
|
|
+ Signature(#[from] secp256k1::Error),
|
|
|
|
+
|
|
|
|
+ #[error("Provided ID is not correct")]
|
|
|
|
+ InvalidProvidedId,
|
|
}
|
|
}
|
|
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
-pub struct EventContent {
|
|
|
|
- pub pub_key: Addr,
|
|
|
|
|
|
+pub struct Metadata {
|
|
|
|
+ #[serde(rename = "pubkey")]
|
|
|
|
+ pub public_key: Id,
|
|
#[serde(with = "super::ts_seconds")]
|
|
#[serde(with = "super::ts_seconds")]
|
|
pub created_at: DateTime<Utc>,
|
|
pub created_at: DateTime<Utc>,
|
|
pub kind: u32,
|
|
pub kind: u32,
|
|
@@ -24,30 +32,38 @@ pub struct EventContent {
|
|
pub content: String,
|
|
pub content: String,
|
|
}
|
|
}
|
|
|
|
|
|
-impl EventContent {
|
|
|
|
|
|
+impl Metadata {
|
|
pub fn new<T>(
|
|
pub fn new<T>(
|
|
- pub_key: Addr,
|
|
|
|
|
|
+ public_key: Id,
|
|
kind: u32,
|
|
kind: u32,
|
|
tags: Vec<super::Tag>,
|
|
tags: Vec<super::Tag>,
|
|
- content_obj: &T,
|
|
|
|
|
|
+ content: &T,
|
|
created_at: Option<DateTime<Utc>>,
|
|
created_at: Option<DateTime<Utc>>,
|
|
) -> Result<Self, Error>
|
|
) -> Result<Self, Error>
|
|
where
|
|
where
|
|
T: ?Sized + Serialize,
|
|
T: ?Sized + Serialize,
|
|
{
|
|
{
|
|
Ok(Self {
|
|
Ok(Self {
|
|
- pub_key,
|
|
|
|
- created_at: created_at.unwrap_or_else(|| Utc::now()),
|
|
|
|
|
|
+ public_key,
|
|
|
|
+ created_at: created_at.unwrap_or_else(Utc::now),
|
|
kind,
|
|
kind,
|
|
tags,
|
|
tags,
|
|
- content: serde_json::to_string(&content_obj)?,
|
|
|
|
|
|
+ content: serde_json::to_string(&content)?,
|
|
})
|
|
})
|
|
}
|
|
}
|
|
|
|
|
|
- pub fn id(&self) -> Result<Addr, Error> {
|
|
|
|
|
|
+ #[inline]
|
|
|
|
+ pub fn content<'a, T>(&'a self) -> Result<T, Error>
|
|
|
|
+ where
|
|
|
|
+ T: de::Deserialize<'a>,
|
|
|
|
+ {
|
|
|
|
+ Ok(serde_json::from_str(&self.content)?)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ pub fn id(&self) -> Result<Id, Error> {
|
|
let data_to_hash = serde_json::to_string(&json!(vec![
|
|
let data_to_hash = serde_json::to_string(&json!(vec![
|
|
json!(0),
|
|
json!(0),
|
|
- json!(self.pub_key.to_public_key()),
|
|
|
|
|
|
+ json!(self.public_key.to_string()),
|
|
json!(self.created_at.timestamp()),
|
|
json!(self.created_at.timestamp()),
|
|
json!(self.kind),
|
|
json!(self.kind),
|
|
json!(self.tags),
|
|
json!(self.tags),
|
|
@@ -55,25 +71,67 @@ impl EventContent {
|
|
]))?;
|
|
]))?;
|
|
let mut hasher = Sha256::new();
|
|
let mut hasher = Sha256::new();
|
|
hasher.update(&data_to_hash);
|
|
hasher.update(&data_to_hash);
|
|
- let id: [u8; 32] = hasher.finalize().into();
|
|
|
|
- Ok(Addr::try_from_bytes(Default::default(), id.to_vec())?)
|
|
|
|
|
|
+ Ok(Id(hasher.finalize().into()))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct Event {
|
|
pub struct Event {
|
|
- pub id: Addr,
|
|
|
|
|
|
+ pub id: Id,
|
|
#[serde(flatten)]
|
|
#[serde(flatten)]
|
|
- pub content: EventContent,
|
|
|
|
|
|
+ pub content: Metadata,
|
|
|
|
+ #[serde(rename = "sig")]
|
|
pub signature: Signature,
|
|
pub signature: Signature,
|
|
}
|
|
}
|
|
|
|
|
|
impl Event {
|
|
impl Event {
|
|
- pub fn new(content: EventContent, signature: Signature) -> Result<Self, Error> {
|
|
|
|
|
|
+ pub fn new(content: Metadata, signature: Signature) -> Result<Self, Error> {
|
|
|
|
+ let id = content.id()?;
|
|
|
|
+ Self::verify_signature(&content.public_key, &signature, &id)?;
|
|
|
|
+
|
|
Ok(Self {
|
|
Ok(Self {
|
|
- id: content.id()?,
|
|
|
|
|
|
+ id,
|
|
content,
|
|
content,
|
|
signature,
|
|
signature,
|
|
})
|
|
})
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ #[inline]
|
|
|
|
+ pub fn content<'a, T>(&'a self) -> Result<T, Error>
|
|
|
|
+ where
|
|
|
|
+ T: de::Deserialize<'a>,
|
|
|
|
+ {
|
|
|
|
+ self.content.content()
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ pub fn is_valid(&self) -> Result<(), Error> {
|
|
|
|
+ let calculated_id = self.content.id()?;
|
|
|
|
+ if calculated_id != self.id {
|
|
|
|
+ return Err(Error::InvalidProvidedId);
|
|
|
|
+ }
|
|
|
|
+ Self::verify_signature(&self.content.public_key, &self.signature, &self.id)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ fn verify_signature(
|
|
|
|
+ public_key: &Id,
|
|
|
|
+ signature: &Signature,
|
|
|
|
+ content_id: &Id,
|
|
|
|
+ ) -> Result<(), Error> {
|
|
|
|
+ let public_key = XOnlyPublicKey::from_slice(&**public_key)?;
|
|
|
|
+ let message = Message::from_slice(&**content_id)?;
|
|
|
|
+ Ok(schnorr::Signature::from_slice(&**signature)?.verify(&message, &public_key)?)
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+#[cfg(test)]
|
|
|
|
+mod test {
|
|
|
|
+ use super::*;
|
|
|
|
+
|
|
|
|
+ #[test]
|
|
|
|
+ fn parse_event_content() {
|
|
|
|
+ let json = "{\"content\":\"{\\\"lud06\\\":\\\"lnbc1p3a4wxvpp5x0pa6gr55fq5s9d3dxs0vz77mqxgdw63hhtgtlfz5zvm65847vnqdqqcqpjsp5402c8rtqxd4j97rnvuejuwl4sg473g6wg08d67fvn7qc4gtpkfks9q7sqqqqqqqqqqqqqqqqqqqsqqqqqysgqmqz9gxqyjw5qrzjqwryaup9lh50kkranzgcdnn2fgvx390wgj5jd07rwr3vxeje0glclleasn65surjcsqqqqlgqqqqqeqqjqyxj968tem9ps6ttm9ukv6ag4yc6qmgj2svrccfgp4n83fpktr3dsx6fq7grfzlqt982aaemahg9q29vzl9f627kh4j8h8xc2z2mtpdqqjlekah\\\",\\\"website\\\":\\\"\\\",\\\"nip05\\\":\\\"cesar@cesar.com.py\\\",\\\"picture\\\":\\\"https://pbs.twimg.com/profile_images/1175432935337537536/_Peu9vuJ_400x400.jpg\\\",\\\"display_name\\\":\\\"C\\\",\\\"about\\\":\\\"Rust and PHP\\\",\\\"name\\\":\\\"c\\\"}\",\"created_at\":1678476588,\"id\":\"3800c787a23288641c0b96cbcc87c26cbd3ea7bee53b7748422fdb100fb7b9f0\",\"kind\":0,\"pubkey\":\"b2815682cfc83fcd2c3add05785cf4573dd388457069974cc6d8cca06b3c3b78\",\"sig\":\"c8a12ce96833e4cd67bce0e9e50f831262ef0f0c0cff5e56c38a0c90867ed1a6621e9692948ef5e85a7ca3726c3f0f43fa7e1992536bc457317123bca8784f5f\",\"tags\":[]}";
|
|
|
|
+
|
|
|
|
+ let obj: Event = serde_json::from_str(json).expect("valid");
|
|
|
|
+ assert!(obj.is_valid().is_ok());
|
|
|
|
+ }
|
|
}
|
|
}
|