Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add nostrdb_net, split into crates #32

Draft
wants to merge 4 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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"] }
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

tags: fake
find src -name '*.rs' | xargs ctags
find . -name '*.rs' | xargs ctags

.PHONY: fake
24 changes: 24 additions & 0 deletions nostrdb_net/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
[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 }
tokio-tungstenite = { version = "0.26.1", optional = true }
tungstenite = { version = "0.26.1", optional = true }

[features]
tokio = ["ewebsock/tokio", "tokio-tungstenite", "tungstenite"]
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
Loading