Skip to content

Commit

Permalink
add nostrdb_net, split into crates
Browse files Browse the repository at this point in the history
This is just enostr from notedeck!

Fixes: #29
  • Loading branch information
jb55 committed Dec 19, 2024
1 parent 4a094b4 commit bca28aa
Show file tree
Hide file tree
Showing 42 changed files with 1,295 additions and 26 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ tags
target/
.build-result
.buildcmd
build.log
2 changes: 1 addition & 1 deletion .gitmodules
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
[submodule "nostrdb"]
path = nostrdb
path = nostrdb_rs/nostrdb
url = https://github.com/damus-io/nostrdb
47 changes: 22 additions & 25 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,30 +1,27 @@
[package]
name = "nostrdb"
authors = ["William Casarin <[email protected]>"]
description = "An unfairly fast embedded nostr database backed by lmdb"
readme = "README.md"
version = "0.5.1"
edition = "2021"
build = "build.rs"
license = "GPL-3.0-or-later"
homepage = "https://github.com/damus-io/nostrdb-rs/"
repository = "https://github.com/damus-io/nostrdb-rs/"
[workspace]
resolver = "2"
members = [
"nostrdb_rs",
"nostrdb_net",
]

[build-dependencies]
cc = "1.0"
bindgen = "0.69.1"

[features]
bindgen = []

[dependencies]
flatbuffers = "23.5.26"
[workspace.dependencies]
bech32 = { version = "0.11", default-features = false }
dirs = "5.0.1"
hex = "0.4.3"
libc = "0.2.151"
nostrdb_net = { path = "nostrdb_net" }
nostrdb = { path = "nostrdb_rs" }
nostr = { version = "0.37.0", default-features = false, features = ["std", "nip49"] }
serde_derive = "1"
serde_json = "1.0.89"
serde = { version = "1", features = ["derive"] }
tempfile = "3.13.0"
thiserror = "2.0.7"
futures = "0.3.31"
tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
tokio = { version = "1.16", features = ["macros", "rt-multi-thread"] }
#tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
tracing = "0.1.40"
tracing-subscriber = "0.3.18"

[dev-dependencies]
hex = "0.4.3"
url = "2.5.2"
urlencoding = "2.1.3"
uuid = { version = "1.10.0", features = ["v4"] }
19 changes: 19 additions & 0 deletions nostrdb_net/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[package]
name = "nostrdb_net"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
ewebsock = { version = "0.8.0", features = ["tls"] }
serde_derive = "1"
serde = { version = "1", features = ["derive"] } # You only need this if you want app persistence
serde_json = { workspace = true }
nostr = { workspace = true }
bech32 = { workspace = true }
nostrdb = { workspace = true }
hex = { workspace = true }
tracing = { workspace = true }
thiserror = { workspace = true }
url = { workspace = true }
59 changes: 59 additions & 0 deletions nostrdb_net/src/client/message.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
use crate::{Error, Note};
use nostrdb::Filter;
use serde_json::json;

/// Messages sent by clients, received by relays
#[derive(Debug)]
pub enum ClientMessage {
Event {
note: Note,
},
Req {
sub_id: String,
filters: Vec<Filter>,
},
Close {
sub_id: String,
},
Raw(String),
}

impl ClientMessage {
pub fn event(note: Note) -> Self {
ClientMessage::Event { note }
}

pub fn raw(raw: String) -> Self {
ClientMessage::Raw(raw)
}

pub fn req(sub_id: String, filters: Vec<Filter>) -> Self {
ClientMessage::Req { sub_id, filters }
}

pub fn close(sub_id: String) -> Self {
ClientMessage::Close { sub_id }
}

pub fn to_json(&self) -> Result<String, Error> {
Ok(match self {
Self::Event { note } => json!(["EVENT", note]).to_string(),
Self::Raw(raw) => raw.clone(),
Self::Req { sub_id, filters } => {
if filters.is_empty() {
format!("[\"REQ\",\"{}\",{{ }}]", sub_id)
} else if filters.len() == 1 {
let filters_json_str = filters[0].json()?;
format!("[\"REQ\",\"{}\",{}]", sub_id, filters_json_str)
} else {
let filters_json_str: Result<Vec<String>, Error> = filters
.iter()
.map(|f| f.json().map_err(Into::<Error>::into))
.collect();
format!("[\"REQ\",\"{}\",{}]", sub_id, filters_json_str?.join(","))
}
}
Self::Close { sub_id } => json!(["CLOSE", sub_id]).to_string(),
})
}
}
3 changes: 3 additions & 0 deletions nostrdb_net/src/client/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
mod message;

pub use message::ClientMessage;
55 changes: 55 additions & 0 deletions nostrdb_net/src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
//use nostr::prelude::secp256k1;
use std::array::TryFromSliceError;
use thiserror::Error;

#[derive(Error, Debug)]
pub enum Error {
#[error("message is empty")]
Empty,

#[error("decoding failed")]
DecodeFailed,

#[error("hex decoding failed")]
HexDecodeFailed,

#[error("invalid bech32")]
InvalidBech32,

#[error("invalid byte size")]
InvalidByteSize,

#[error("invalid signature")]
InvalidSignature,

#[error("invalid public key")]
InvalidPublicKey,

// Secp(secp256k1::Error),
#[error("json error: {0}")]
Json(#[from] serde_json::Error),

#[error("nostrdb error: {0}")]
Nostrdb(#[from] nostrdb::Error),

#[error("{0}")]
Generic(String),
}

impl From<String> for Error {
fn from(s: String) -> Self {
Error::Generic(s)
}
}

impl From<TryFromSliceError> for Error {
fn from(_e: TryFromSliceError) -> Self {
Error::InvalidByteSize
}
}

impl From<hex::FromHexError> for Error {
fn from(_e: hex::FromHexError) -> Self {
Error::HexDecodeFailed
}
}
1 change: 1 addition & 0 deletions nostrdb_net/src/filter.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub type Filter = nostrdb::Filter;
139 changes: 139 additions & 0 deletions nostrdb_net/src/keypair.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
use nostr::nips::nip49::EncryptedSecretKey;
use serde::Deserialize;
use serde::Serialize;

use crate::Pubkey;
use crate::SecretKey;

#[derive(Debug, Eq, PartialEq, Clone)]
pub struct Keypair {
pub pubkey: Pubkey,
pub secret_key: Option<SecretKey>,
}

impl Keypair {
pub fn from_secret(secret_key: SecretKey) -> Self {
let cloned_secret_key = secret_key.clone();
let nostr_keys = nostr::Keys::new(secret_key);
Keypair {
pubkey: Pubkey::new(nostr_keys.public_key().to_bytes()),
secret_key: Some(cloned_secret_key),
}
}

pub fn new(pubkey: Pubkey, secret_key: Option<SecretKey>) -> Self {
Keypair { pubkey, secret_key }
}

pub fn only_pubkey(pubkey: Pubkey) -> Self {
Keypair {
pubkey,
secret_key: None,
}
}

pub fn to_full(&self) -> Option<FilledKeypair<'_>> {
self.secret_key.as_ref().map(|secret_key| FilledKeypair {
pubkey: &self.pubkey,
secret_key,
})
}
}

#[derive(Debug, Eq, PartialEq, Clone)]
pub struct FullKeypair {
pub pubkey: Pubkey,
pub secret_key: SecretKey,
}

#[derive(Debug, Eq, PartialEq, Clone, Copy)]
pub struct FilledKeypair<'a> {
pub pubkey: &'a Pubkey,
pub secret_key: &'a SecretKey,
}

impl<'a> FilledKeypair<'a> {
pub fn new(pubkey: &'a Pubkey, secret_key: &'a SecretKey) -> Self {
FilledKeypair { pubkey, secret_key }
}

pub fn to_full(&self) -> FullKeypair {
FullKeypair {
pubkey: self.pubkey.to_owned(),
secret_key: self.secret_key.to_owned(),
}
}
}

impl FullKeypair {
pub fn new(pubkey: Pubkey, secret_key: SecretKey) -> Self {
FullKeypair { pubkey, secret_key }
}

pub fn to_filled(&self) -> FilledKeypair<'_> {
FilledKeypair::new(&self.pubkey, &self.secret_key)
}

pub fn generate() -> Self {
let mut rng = nostr::secp256k1::rand::rngs::OsRng;
let (secret_key, _) = &nostr::SECP256K1.generate_keypair(&mut rng);
let (xopk, _) = secret_key.x_only_public_key(&nostr::SECP256K1);
let secret_key = nostr::SecretKey::from(*secret_key);
FullKeypair {
pubkey: Pubkey::new(xopk.serialize()),
secret_key,
}
}

pub fn to_keypair(self) -> Keypair {
Keypair {
pubkey: self.pubkey,
secret_key: Some(self.secret_key),
}
}
}

impl std::fmt::Display for Keypair {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Keypair:\n\tpublic: {}\n\tsecret: {}",
self.pubkey,
match self.secret_key {
Some(_) => "Some(<hidden>)",
None => "None",
}
)
}
}

impl std::fmt::Display for FullKeypair {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Keypair:\n\tpublic: {}\n\tsecret: <hidden>", self.pubkey)
}
}

#[derive(Debug, Eq, PartialEq, Serialize, Deserialize)]
pub struct SerializableKeypair {
pub pubkey: Pubkey,
pub encrypted_secret_key: Option<EncryptedSecretKey>,
}

impl SerializableKeypair {
pub fn from_keypair(kp: &Keypair, pass: &str, log_n: u8) -> Self {
Self {
pubkey: kp.pubkey,
encrypted_secret_key: kp.secret_key.clone().and_then(|s| {
EncryptedSecretKey::new(&s, pass, log_n, nostr::nips::nip49::KeySecurity::Weak).ok()
}),
}
}

pub fn to_keypair(&self, pass: &str) -> Keypair {
Keypair::new(
self.pubkey,
self.encrypted_secret_key
.and_then(|e| e.to_secret_key(pass).ok()),
)
}
}
23 changes: 23 additions & 0 deletions nostrdb_net/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
mod client;
mod error;
mod filter;
mod keypair;
mod note;
mod profile;
mod pubkey;
mod relay;

pub use client::ClientMessage;
pub use error::Error;
pub use ewebsock;
pub use filter::Filter;
pub use keypair::{FilledKeypair, FullKeypair, Keypair, SerializableKeypair};
pub use nostr::SecretKey;
pub use note::{Note, NoteId};
pub use profile::Profile;
pub use pubkey::Pubkey;
pub use relay::message::{RelayEvent, RelayMessage};
pub use relay::pool::{PoolEvent, RelayPool};
pub use relay::{Relay, RelayStatus};

pub type Result<T> = std::result::Result<T, error::Error>;
Loading

0 comments on commit bca28aa

Please sign in to comment.