diff --git a/Cargo.lock b/Cargo.lock index 266eea79..bc4caebe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1051,6 +1051,7 @@ name = "enostr" version = "0.1.0" dependencies = [ "ewebsock", + "hex", "serde", "serde_derive", "serde_json", diff --git a/enostr/Cargo.lock b/enostr/Cargo.lock index 6bd877a8..ffa0a17c 100644 --- a/enostr/Cargo.lock +++ b/enostr/Cargo.lock @@ -108,6 +108,7 @@ name = "enostr" version = "0.1.0" dependencies = [ "ewebsock", + "hex", "serde", "serde_derive", "serde_json", @@ -258,6 +259,12 @@ dependencies = [ "wasi", ] +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + [[package]] name = "http" version = "0.2.8" diff --git a/enostr/Cargo.toml b/enostr/Cargo.toml index 37b261e7..639b53b6 100644 --- a/enostr/Cargo.toml +++ b/enostr/Cargo.toml @@ -11,3 +11,4 @@ serde_derive = "1" serde = { version = "1", features = ["derive"] } # You only need this if you want app persistence serde_json = "1.0.89" tracing = "0.1.37" +hex = "0.4.3" diff --git a/enostr/src/error.rs b/enostr/src/error.rs index 6f93a8dd..dd15b03b 100644 --- a/enostr/src/error.rs +++ b/enostr/src/error.rs @@ -6,6 +6,7 @@ pub enum Error { MessageDecodeFailed, InvalidSignature, Json(serde_json::Error), + Hex(hex::FromHexError), Generic(String), } @@ -36,3 +37,9 @@ impl From for Error { Error::Json(e) } } + +impl From for Error { + fn from(e: hex::FromHexError) -> Self { + Error::Hex(e) + } +} diff --git a/enostr/src/event.rs b/enostr/src/event.rs index 9bdac548..1f2c327d 100644 --- a/enostr/src/event.rs +++ b/enostr/src/event.rs @@ -1,4 +1,5 @@ use crate::{Error, Pubkey, Result}; +use hex; use serde_derive::{Deserialize, Serialize}; use std::hash::{Hash, Hasher}; @@ -6,7 +7,7 @@ use std::hash::{Hash, Hasher}; #[derive(Serialize, Deserialize, Debug, Clone)] pub struct Event { /// 32-bytes sha256 of the the serialized event data - pub id: EventId, + pub id: NoteId, /// 32-bytes hex-encoded public key of the event creator #[serde(rename = "pubkey")] pub pubkey: Pubkey, @@ -59,7 +60,7 @@ impl Event { sig: &str, ) -> Result { let event = Event { - id: id.to_string().into(), + id: id.try_into()?, pubkey: pubkey.to_string().into(), created_at, kind, @@ -80,17 +81,57 @@ impl std::str::FromStr for Event { } } -#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone, Hash)] -pub struct EventId(String); +#[derive(Serialize, Debug, Eq, PartialEq, Clone, Hash)] +pub struct NoteId([u8; 32]); -impl From for EventId { - fn from(s: String) -> Self { - EventId(s) +// Implement `Deserialize` for `NoteId`. +impl<'de> serde::Deserialize<'de> for NoteId { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + // Deserialize the JSON string + let s = String::deserialize(deserializer)?; + + // Convert the hex string to bytes + let bytes = hex::decode(&s).map_err(serde::de::Error::custom)?; + + // Check that the length is exactly 32 + if bytes.len() != 32 { + return Err(serde::de::Error::custom("Expected exactly 32 bytes")); + } + + // Convert the Vec to [u8; 32] + let mut array = [0; 32]; + array.copy_from_slice(&bytes); + + Ok(NoteId(array)) } } -impl From for String { - fn from(evid: EventId) -> Self { - evid.0 +impl TryFrom for NoteId { + type Error = hex::FromHexError; + + fn try_from(s: String) -> std::result::Result { + let s: &str = &s; + NoteId::try_from(s) + } +} + +impl From<[u8; 32]> for NoteId { + fn from(s: [u8; 32]) -> Self { + NoteId(s) + } +} + +impl TryFrom<&str> for NoteId { + type Error = hex::FromHexError; + + fn try_from(s: &str) -> std::result::Result { + let decoded = hex::decode(s)?; + match decoded.try_into() { + Ok(bs) => Ok(NoteId(bs)), + Err(_) => Err(hex::FromHexError::InvalidStringLength), + } } } diff --git a/enostr/src/filter.rs b/enostr/src/filter.rs index 0fabc14a..e56b3b6c 100644 --- a/enostr/src/filter.rs +++ b/enostr/src/filter.rs @@ -1,17 +1,17 @@ -use crate::{EventId, Pubkey}; +use crate::{NoteId, Pubkey}; use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)] pub struct Filter { #[serde(skip_serializing_if = "Option::is_none")] - ids: Option>, + ids: Option>, #[serde(skip_serializing_if = "Option::is_none")] authors: Option>, #[serde(skip_serializing_if = "Option::is_none")] kinds: Option>, #[serde(rename = "#e")] #[serde(skip_serializing_if = "Option::is_none")] - events: Option>, + events: Option>, #[serde(rename = "#p")] #[serde(skip_serializing_if = "Option::is_none")] pubkeys: Option>, @@ -37,7 +37,7 @@ impl Filter { } } - pub fn ids(mut self, ids: Vec) -> Self { + pub fn ids(mut self, ids: Vec) -> Self { self.ids = Some(ids); self } @@ -52,7 +52,7 @@ impl Filter { self } - pub fn events(mut self, events: Vec) -> Self { + pub fn events(mut self, events: Vec) -> Self { self.events = Some(events); self } diff --git a/enostr/src/lib.rs b/enostr/src/lib.rs index 1749d539..fba598dc 100644 --- a/enostr/src/lib.rs +++ b/enostr/src/lib.rs @@ -8,7 +8,7 @@ mod relay; pub use client::ClientMessage; pub use error::Error; -pub use event::{Event, EventId}; +pub use event::{Event, NoteId}; pub use ewebsock; pub use filter::Filter; pub use profile::Profile; diff --git a/src/app.rs b/src/app.rs index 1276754d..47d23aad 100644 --- a/src/app.rs +++ b/src/app.rs @@ -5,8 +5,8 @@ use crate::ui::padding; use crate::Result; use egui::containers::scroll_area::ScrollBarVisibility; use egui::widgets::Spinner; -use egui::{Color32, Context, Frame, TextureHandle, TextureId}; -use enostr::{ClientMessage, EventId, Filter, Profile, Pubkey, RelayEvent, RelayMessage}; +use egui::{Context, Frame, TextureHandle, TextureId}; +use enostr::{ClientMessage, Filter, NoteId, Profile, Pubkey, RelayEvent, RelayMessage}; use poll_promise::Promise; use std::collections::{HashMap, HashSet}; use std::hash::{Hash, Hasher}; @@ -44,11 +44,10 @@ pub struct Damus { pool: RelayPool, - all_events: HashMap, - events: Vec, + all_events: HashMap, + events: Vec, img_cache: ImageCache, - bg_color: Color32, } impl Default for Damus { @@ -58,10 +57,9 @@ impl Default for Damus { contacts: Contacts::new(), all_events: HashMap::new(), pool: RelayPool::default(), - events: vec![], + events: Vec::with_capacity(2000), img_cache: HashMap::new(), n_panels: 1, - bg_color: Color32::from_rgb(31, 31, 31), } } } @@ -459,8 +457,8 @@ fn add_test_events(damus: &mut Damus) { // For inspiration and more examples, go to https://emilk.github.io/egui let test_event = Event { - id: "6938e3cd841f3111dbdbd909f87fd52c3d1f1e4a07fd121d1243196e532811cb".to_string().into(), - pubkey: "f0a6ff7f70b872de6d82c8daec692a433fd23b6a49f25923c6f034df715cdeec".to_string().into(), + id: "6938e3cd841f3111dbdbd909f87fd52c3d1f1e4a07fd121d1243196e532811cb".try_into().unwrap(), + pubkey: "f0a6ff7f70b872de6d82c8daec692a433fd23b6a49f25923c6f034df715cdeec".try_into().unwrap(), created_at: 1667781968, kind: 1, tags: vec![], @@ -469,8 +467,8 @@ fn add_test_events(damus: &mut Damus) { }; let test_event2 = Event { - id: "6938e3cd841f3111dbdbd909f87fd52c3d1f1e4a07fd121d1243196e532811cb".to_string().into(), - pubkey: "32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245".to_string().into(), + id: "6938e3cd841f3111dbdbd909f87fd52c3d1f1e4a07fd121d1243196e532811cb".try_into().unwrap(), + pubkey: "32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245".try_into().unwrap(), created_at: 1667781968, kind: 1, tags: vec![], diff --git a/src/contacts.rs b/src/contacts.rs index 626248f2..f977680d 100644 --- a/src/contacts.rs +++ b/src/contacts.rs @@ -1,8 +1,8 @@ -use enostr::{EventId, Profile, Pubkey}; +use enostr::{NoteId, Profile, Pubkey}; use std::collections::HashMap; pub struct Contacts { - pub events: HashMap, + pub events: HashMap, pub profiles: HashMap, }