rusted_nostr_tools/functions/
event_methods.rs

1use chrono::NaiveDateTime;
2use secp256k1::{
3    schnorr::Signature, Error, KeyPair, Message, Secp256k1, SecretKey, XOnlyPublicKey,
4};
5use serde::{Deserialize, Serialize};
6use serde_json::json;
7use sha2::{Digest, Sha256};
8
9#[derive(Debug, Serialize)]
10pub struct UnsignedEvent {
11    pub content: String,
12    pub created_at: i64,
13    pub kind: u64,
14    pub pubkey: String,
15    pub tags: Vec<Vec<String>>,
16}
17
18#[derive(Debug, Deserialize, Serialize)]
19pub struct SignedEvent {
20    pub content: String,
21    pub created_at: i64,
22    pub id: String,
23    pub kind: u64,
24    pub pubkey: String,
25    pub sig: String,
26    pub tags: Vec<Vec<String>>,
27}
28
29pub fn get_event_hash(event: &UnsignedEvent) -> Result<String, String> {
30    let commitment_string = serialize_event(&event)?;
31
32    let mut hasher = Sha256::new();
33
34    hasher.update(commitment_string.as_bytes());
35
36    let hash = hasher.finalize();
37    Ok(hex::encode(hash))
38}
39
40pub fn serialize_event(evt: &UnsignedEvent) -> Result<String, String> {
41    if !validate_event(evt) {
42        return Err("Invalid event".to_string());
43    }
44    Ok(json!([
45        0,
46        evt.pubkey,
47        evt.created_at,
48        evt.kind,
49        evt.tags,
50        evt.content
51    ])
52    .to_string())
53}
54
55pub fn sign_event(event: &UnsignedEvent, key: &str) -> Result<SignedEvent, Error> {
56    let secp = Secp256k1::new();
57    let secret_key =
58        SecretKey::from_slice(&hex::decode(key).expect("FailedToDecodeHexPrivateKey"))?;
59    let pair = KeyPair::from_seckey_slice(&secp, &secret_key.secret_bytes())
60        .expect("Failed to generate keypair from secret key");
61
62    let message = Message::from_slice(
63        Sha256::digest(&serialize_event(event).unwrap().as_bytes()).as_slice(),
64    )?;
65    let sig = hex::encode(secp.sign_schnorr_no_aux_rand(&message, &pair).as_ref());
66
67    let id = get_event_hash(event).unwrap();
68
69    Ok(SignedEvent {
70        content: event.content.clone(),
71        created_at: event.created_at,
72        id,
73        kind: event.kind,
74        pubkey: event.pubkey.clone(),
75        sig,
76        tags: event.tags.clone(),
77    })
78}
79
80pub fn validate_event(event: &UnsignedEvent) -> bool {
81    if !matches!(event, &UnsignedEvent { .. }) {
82        return false;
83    }
84
85    // Check if created_at is a valid Unix timestamp in seconds
86    let datetime_opt = NaiveDateTime::from_timestamp_opt(event.created_at as i64, 0);
87    if datetime_opt.is_none() {
88        return false;
89    }
90
91    if !matches!(event.pubkey, _ if event.pubkey.len() == 64 && event.pubkey.chars().all(|c| c.is_ascii_hexdigit()))
92    {
93        return false;
94    }
95
96    true
97}
98
99pub fn verify_signature(signature: &str, pubkey: &str, id: &str) -> Result<(), Error> {
100    let secp = Secp256k1::new();
101
102    let public_key =
103        XOnlyPublicKey::from_slice(&hex::decode(pubkey).expect("FailedToDecodePubkey"))?;
104    let message =
105        Message::from_slice(&hex::decode(id).expect("UnableToDecodeHexMessageForSigning"))?;
106
107    let sig = Signature::from_slice(&hex::decode(signature).expect("FailedToDecodeSignature"))
108        .expect("FailedToParseSignature");
109
110    secp.verify_schnorr(&sig, &message, &public_key)
111}