notedeck

One damus client to rule them all
git clone git://jb55.com/notedeck
Log | Files | Refs | README | LICENSE

note.rs (3964B)


      1 use crate::{Error, Pubkey};
      2 
      3 use serde::{Deserialize, Deserializer, Serialize, Serializer};
      4 use std::fmt;
      5 use std::hash::{Hash, Hasher};
      6 
      7 #[derive(Clone, Copy, Eq, PartialEq, Hash)]
      8 pub struct NoteId([u8; 32]);
      9 
     10 impl fmt::Debug for NoteId {
     11     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
     12         write!(f, "NoteId({})", self.hex())
     13     }
     14 }
     15 
     16 static HRP_NOTE: bech32::Hrp = bech32::Hrp::parse_unchecked("note");
     17 
     18 impl NoteId {
     19     pub fn new(bytes: [u8; 32]) -> Self {
     20         NoteId(bytes)
     21     }
     22 
     23     pub fn bytes(&self) -> &[u8; 32] {
     24         &self.0
     25     }
     26 
     27     pub fn hex(&self) -> String {
     28         hex::encode(self.bytes())
     29     }
     30 
     31     pub fn from_hex(hex_str: &str) -> Result<Self, Error> {
     32         let evid = NoteId(hex::decode(hex_str)?.as_slice().try_into()?);
     33         Ok(evid)
     34     }
     35 
     36     pub fn to_bech(&self) -> Option<String> {
     37         bech32::encode::<bech32::Bech32>(HRP_NOTE, &self.0).ok()
     38     }
     39 
     40     pub fn from_bech(bech: &str) -> Option<Self> {
     41         let (hrp, data) = bech32::decode(bech).ok()?;
     42 
     43         if hrp != HRP_NOTE {
     44             return None;
     45         }
     46 
     47         Some(NoteId::new(data.try_into().ok()?))
     48     }
     49 
     50     /// Parse a NIP-19 nevent1 bech32 string and extract the event ID.
     51     pub fn from_nevent_bech(bech: &str) -> Option<Self> {
     52         use nostr::nips::nip19::{FromBech32, Nip19Event};
     53         let nip19_event = Nip19Event::from_bech32(bech).ok()?;
     54         Some(NoteId::new(nip19_event.event_id.to_bytes()))
     55     }
     56 }
     57 
     58 /// Event is the struct used to represent a Nostr event
     59 #[derive(Serialize, Deserialize, Debug, Clone)]
     60 pub struct Note {
     61     /// 32-bytes sha256 of the the serialized event data
     62     pub id: NoteId,
     63     /// 32-bytes hex-encoded public key of the event creator
     64     pub pubkey: Pubkey,
     65     /// unix timestamp in seconds
     66     pub created_at: u64,
     67     /// integer
     68     /// 0: NostrEvent
     69     pub kind: u64,
     70     /// Tags
     71     pub tags: Vec<Vec<String>>,
     72     /// arbitrary string
     73     pub content: String,
     74     /// 64-bytes signature of the sha256 hash of the serialized event data, which is the same as the "id" field
     75     pub sig: String,
     76 }
     77 
     78 // Implement Hash trait
     79 impl Hash for Note {
     80     fn hash<H: Hasher>(&self, state: &mut H) {
     81         self.id.0.hash(state);
     82     }
     83 }
     84 
     85 impl PartialEq for Note {
     86     fn eq(&self, other: &Self) -> bool {
     87         self.id == other.id
     88     }
     89 }
     90 
     91 impl Eq for Note {}
     92 
     93 impl Note {
     94     pub fn from_json(s: &str) -> Result<Self, Error> {
     95         serde_json::from_str(s).map_err(Into::into)
     96     }
     97 
     98     pub fn verify(&self) -> Result<Self, Error> {
     99         Err(Error::InvalidSignature)
    100     }
    101 
    102     /// This is just for serde sanity checking
    103     #[allow(dead_code)]
    104     pub(crate) fn new_dummy(
    105         id: &str,
    106         pubkey: &str,
    107         created_at: u64,
    108         kind: u64,
    109         tags: Vec<Vec<String>>,
    110         content: &str,
    111         sig: &str,
    112     ) -> Result<Self, Error> {
    113         Ok(Note {
    114             id: NoteId::from_hex(id)?,
    115             pubkey: Pubkey::from_hex(pubkey)?,
    116             created_at,
    117             kind,
    118             tags,
    119             content: content.to_string(),
    120             sig: sig.to_string(),
    121         })
    122     }
    123 }
    124 
    125 impl std::str::FromStr for Note {
    126     type Err = Error;
    127 
    128     fn from_str(s: &str) -> Result<Self, Error> {
    129         Note::from_json(s)
    130     }
    131 }
    132 
    133 // Custom serialize function for Pubkey
    134 impl Serialize for NoteId {
    135     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    136     where
    137         S: Serializer,
    138     {
    139         serializer.serialize_str(&self.hex())
    140     }
    141 }
    142 
    143 // Custom deserialize function for Pubkey
    144 impl<'de> Deserialize<'de> for NoteId {
    145     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    146     where
    147         D: Deserializer<'de>,
    148     {
    149         let s = String::deserialize(deserializer)?;
    150         NoteId::from_hex(&s).map_err(serde::de::Error::custom)
    151     }
    152 }
    153 
    154 impl hashbrown::Equivalent<NoteId> for &[u8; 32] {
    155     fn equivalent(&self, key: &NoteId) -> bool {
    156         self.as_slice() == key.bytes()
    157     }
    158 }