|
@@ -1,7 +1,10 @@
|
|
|
-use super::{Id, Signature};
|
|
|
+use super::{content, Content, Id, Signature};
|
|
|
use chrono::{DateTime, Utc};
|
|
|
use secp256k1::{schnorr, Message, XOnlyPublicKey};
|
|
|
-use serde::{de, Deserialize, Serialize};
|
|
|
+use serde::{
|
|
|
+ de::{self, Deserializer},
|
|
|
+ Deserialize, Serialize,
|
|
|
+};
|
|
|
use serde_json::json;
|
|
|
use sha2::{Digest, Sha256};
|
|
|
use thiserror::Error;
|
|
@@ -19,55 +22,74 @@ pub enum Error {
|
|
|
|
|
|
#[error("Provided ID is not correct")]
|
|
|
InvalidProvidedId,
|
|
|
+
|
|
|
+ #[error("Internal error: {0}")]
|
|
|
+ Internal(#[from] content::Error),
|
|
|
}
|
|
|
|
|
|
-#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
|
-pub struct Metadata {
|
|
|
+#[derive(Debug, Clone, Serialize)]
|
|
|
+pub struct Data {
|
|
|
#[serde(rename = "pubkey")]
|
|
|
pub public_key: Id,
|
|
|
#[serde(with = "super::ts_seconds")]
|
|
|
pub created_at: DateTime<Utc>,
|
|
|
pub kind: u32,
|
|
|
pub tags: Vec<super::Tag>,
|
|
|
- pub content: String,
|
|
|
+ pub content: Content,
|
|
|
+}
|
|
|
+
|
|
|
+impl<'de> de::Deserialize<'de> for Data {
|
|
|
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
|
+ where
|
|
|
+ D: Deserializer<'de>,
|
|
|
+ {
|
|
|
+ #[derive(Deserialize)]
|
|
|
+ struct MetadataInternal {
|
|
|
+ #[serde(rename = "pubkey")]
|
|
|
+ pub public_key: Id,
|
|
|
+ #[serde(with = "super::ts_seconds")]
|
|
|
+ pub created_at: DateTime<Utc>,
|
|
|
+ pub kind: u32,
|
|
|
+ pub tags: Vec<super::Tag>,
|
|
|
+ pub content: String,
|
|
|
+ }
|
|
|
+ let s: MetadataInternal = MetadataInternal::deserialize(deserializer)?;
|
|
|
+ let content = Content::deserialize(s.kind, &s.content)
|
|
|
+ .map_err(|d| de::Error::custom(d.to_string()))?;
|
|
|
+ Ok(Self {
|
|
|
+ public_key: s.public_key,
|
|
|
+ created_at: s.created_at,
|
|
|
+ kind: s.kind,
|
|
|
+ tags: s.tags,
|
|
|
+ content,
|
|
|
+ })
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
-impl Metadata {
|
|
|
- pub fn new<T>(
|
|
|
+impl Data {
|
|
|
+ pub fn new(
|
|
|
public_key: Id,
|
|
|
- kind: u32,
|
|
|
tags: Vec<super::Tag>,
|
|
|
- content: &T,
|
|
|
+ content: Content,
|
|
|
created_at: Option<DateTime<Utc>>,
|
|
|
- ) -> Result<Self, Error>
|
|
|
- where
|
|
|
- T: ?Sized + Serialize,
|
|
|
- {
|
|
|
+ ) -> Result<Self, Error> {
|
|
|
Ok(Self {
|
|
|
public_key,
|
|
|
created_at: created_at.unwrap_or_else(Utc::now),
|
|
|
- kind,
|
|
|
tags,
|
|
|
- content: serde_json::to_string(&content)?,
|
|
|
+ kind: content.kind(),
|
|
|
+ content,
|
|
|
})
|
|
|
}
|
|
|
|
|
|
- #[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![
|
|
|
json!(0),
|
|
|
json!(self.public_key.to_string()),
|
|
|
json!(self.created_at.timestamp()),
|
|
|
- json!(self.kind),
|
|
|
+ json!(self.content.kind()),
|
|
|
json!(self.tags),
|
|
|
- json!(self.content),
|
|
|
+ json!(self.content.try_to_string()?),
|
|
|
]))?;
|
|
|
let mut hasher = Sha256::new();
|
|
|
hasher.update(&data_to_hash);
|
|
@@ -79,37 +101,29 @@ impl Metadata {
|
|
|
pub struct Event {
|
|
|
pub id: Id,
|
|
|
#[serde(flatten)]
|
|
|
- pub content: Metadata,
|
|
|
+ pub data: Data,
|
|
|
#[serde(rename = "sig")]
|
|
|
pub signature: Signature,
|
|
|
}
|
|
|
|
|
|
impl Event {
|
|
|
- pub fn new(content: Metadata, signature: Signature) -> Result<Self, Error> {
|
|
|
- let id = content.id()?;
|
|
|
- Self::verify_signature(&content.public_key, &signature, &id)?;
|
|
|
+ pub fn new(data: Data, signature: Signature) -> Result<Self, Error> {
|
|
|
+ let id = data.id()?;
|
|
|
+ Self::verify_signature(&data.public_key, &signature, &id)?;
|
|
|
|
|
|
Ok(Self {
|
|
|
id,
|
|
|
- content,
|
|
|
+ data,
|
|
|
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()?;
|
|
|
+ let calculated_id = self.data.id()?;
|
|
|
if calculated_id != self.id {
|
|
|
return Err(Error::InvalidProvidedId);
|
|
|
}
|
|
|
- Self::verify_signature(&self.content.public_key, &self.signature, &self.id)
|
|
|
+ Self::verify_signature(&self.data.public_key, &self.signature, &self.id)
|
|
|
}
|
|
|
|
|
|
fn verify_signature(
|
|
@@ -132,6 +146,7 @@ mod test {
|
|
|
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");
|
|
|
+ obj.is_valid().unwrap();
|
|
|
assert!(obj.is_valid().is_ok());
|
|
|
}
|
|
|
}
|