notedeck

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

commit 1ba597fc0a748b3e4040b9d6a82720b590015029
parent 7b269481010cb583552a8196d4f24ba0c9673d03
Author: William Casarin <jb55@jb55.com>
Date:   Thu,  6 Jul 2023 17:09:39 -0700

json: deserialize note ids into bytes

Diffstat:
MCargo.lock | 1+
Menostr/Cargo.lock | 7+++++++
Menostr/Cargo.toml | 1+
Menostr/src/error.rs | 7+++++++
Menostr/src/event.rs | 61+++++++++++++++++++++++++++++++++++++++++++++++++++----------
Menostr/src/filter.rs | 10+++++-----
Menostr/src/lib.rs | 2+-
Msrc/app.rs | 20+++++++++-----------
Msrc/contacts.rs | 4++--
9 files changed, 84 insertions(+), 29 deletions(-)

diff --git 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 @@ -108,6 +108,7 @@ name = "enostr" version = "0.1.0" dependencies = [ "ewebsock", + "hex", "serde", "serde_derive", "serde_json", @@ -259,6 +260,12 @@ dependencies = [ ] [[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" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git 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 @@ -6,6 +6,7 @@ pub enum Error { MessageDecodeFailed, InvalidSignature, Json(serde_json::Error), + Hex(hex::FromHexError), Generic(String), } @@ -36,3 +37,9 @@ impl From<serde_json::Error> for Error { Error::Json(e) } } + +impl From<hex::FromHexError> for Error { + fn from(e: hex::FromHexError) -> Self { + Error::Hex(e) + } +} diff --git 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<Self> { 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<String> for EventId { - fn from(s: String) -> Self { - EventId(s) +// Implement `Deserialize` for `NoteId`. +impl<'de> serde::Deserialize<'de> for NoteId { + fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error> + 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<u8> to [u8; 32] + let mut array = [0; 32]; + array.copy_from_slice(&bytes); + + Ok(NoteId(array)) + } +} + +impl TryFrom<String> for NoteId { + type Error = hex::FromHexError; + + fn try_from(s: String) -> std::result::Result<Self, Self::Error> { + let s: &str = &s; + NoteId::try_from(s) + } +} + +impl From<[u8; 32]> for NoteId { + fn from(s: [u8; 32]) -> Self { + NoteId(s) } } -impl From<EventId> for String { - fn from(evid: EventId) -> Self { - evid.0 +impl TryFrom<&str> for NoteId { + type Error = hex::FromHexError; + + fn try_from(s: &str) -> std::result::Result<Self, Self::Error> { + 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 @@ -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<Vec<EventId>>, + ids: Option<Vec<NoteId>>, #[serde(skip_serializing_if = "Option::is_none")] authors: Option<Vec<Pubkey>>, #[serde(skip_serializing_if = "Option::is_none")] kinds: Option<Vec<u64>>, #[serde(rename = "#e")] #[serde(skip_serializing_if = "Option::is_none")] - events: Option<Vec<EventId>>, + events: Option<Vec<NoteId>>, #[serde(rename = "#p")] #[serde(skip_serializing_if = "Option::is_none")] pubkeys: Option<Vec<Pubkey>>, @@ -37,7 +37,7 @@ impl Filter { } } - pub fn ids(mut self, ids: Vec<EventId>) -> Self { + pub fn ids(mut self, ids: Vec<NoteId>) -> Self { self.ids = Some(ids); self } @@ -52,7 +52,7 @@ impl Filter { self } - pub fn events(mut self, events: Vec<EventId>) -> Self { + pub fn events(mut self, events: Vec<NoteId>) -> Self { self.events = Some(events); self } diff --git 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 @@ -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<EventId, Event>, - events: Vec<EventId>, + all_events: HashMap<NoteId, Event>, + events: Vec<NoteId>, 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 @@ -1,8 +1,8 @@ -use enostr::{EventId, Profile, Pubkey}; +use enostr::{NoteId, Profile, Pubkey}; use std::collections::HashMap; pub struct Contacts { - pub events: HashMap<Pubkey, EventId>, + pub events: HashMap<Pubkey, NoteId>, pub profiles: HashMap<Pubkey, Profile>, }