Skip to content

Commit

Permalink
add ndb.process_event and tests
Browse files Browse the repository at this point in the history
  • Loading branch information
jb55 committed Dec 16, 2023
1 parent e99e6e3 commit 26858ca
Show file tree
Hide file tree
Showing 7 changed files with 101 additions and 39 deletions.
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,7 @@ cc = "1.0"
env_logger = "0.10.1"
libc = "0.2.151"
log = "0.4.20"

[dev-dependencies]
hex = "0.4.3"
#bindgen = "0.69.1" // re-enable when we update bindings
25 changes: 5 additions & 20 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,22 +17,6 @@ fn secp256k1_build() {
.define("ENABLE_MODULE_SCHNORRSIG", Some("1"))
.define("ENABLE_MODULE_EXTRAKEYS", Some("1"));
//.define("ENABLE_MODULE_ELLSWIFT", Some("1"))
// upstream sometimes introduces calls to printf, which we cannot compile
// with WASM due to its lack of libc. printf is never necessary and we can
// just #define it away.
//.define("printf(...)", Some(""));

//if cfg!(feature = "lowmemory") {
// base_config.define("ECMULT_WINDOW_SIZE", Some("4")); // A low-enough value to consume negligible memory
// base_config.define("ECMULT_GEN_PREC_BITS", Some("2"));
//} else {
// base_config.define("ECMULT_GEN_PREC_BITS", Some("4"));
// base_config.define("ECMULT_WINDOW_SIZE", Some("15")); // This is the default in the configure file (`auto`)
//}

//base_config.define("USE_EXTERNAL_DEFAULT_CALLBACKS", Some("1"));
//#[cfg(feature = "recovery")]
//base_config.define("ENABLE_MODULE_RECOVERY", Some("1"));

// WASM headers and size/align defines.
if env::var("CARGO_CFG_TARGET_ARCH").unwrap() == "wasm32" {
Expand Down Expand Up @@ -78,10 +62,11 @@ fn main() {
// Add other include paths
//.flag("-Wall")
.flag("-Wno-misleading-indentation")
.flag("-Wno-unused-function")
//.flag("-Werror")
//.flag("-g")
.compile("libnostrdb.a");
.flag("-Wno-unused-function");
//.flag("-Werror")
//.flag("-g")

build.compile("libnostrdb.a");

secp256k1_build();

Expand Down
1 change: 1 addition & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@ pub enum Error {
DbOpenFailed,
NotFound,
DecodeError,
NoteProcessFailed,
TransactionFailed,
}
67 changes: 62 additions & 5 deletions src/ndb.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use libc;
use std::ffi::CString;
use std::ptr;

Expand All @@ -7,19 +8,29 @@ use crate::error::Error;
use crate::note::Note;
use crate::result::Result;
use crate::transaction::Transaction;
use std::fs;
use std::path::Path;

/// A nostrdb context. Construct one of these with [Ndb::new].
pub struct Ndb {
ndb: *mut bindings::ndb,
}

impl Ndb {
// Constructor
/// Construct a new nostrdb context. Takes a directory where the database
/// is/will be located and a nostrdb config.
pub fn new(db_dir: &str, config: &Config) -> Result<Self> {
let db_dir_cstr = match CString::new(db_dir) {
Ok(cstr) => cstr,
Err(_) => return Err(Error::DbOpenFailed),
};
let mut ndb: *mut bindings::ndb = ptr::null_mut();

let path = Path::new(db_dir);
if !path.exists() {
let _ = fs::create_dir_all(&path);
}

let result = unsafe { bindings::ndb_init(&mut ndb, db_dir_cstr.as_ptr(), config.as_ptr()) };

if result == 0 {
Expand All @@ -29,6 +40,27 @@ impl Ndb {
Ok(Ndb { ndb })
}

/// Ingest a relay-sent event in the form `["EVENT","subid", {"id:"...}]`
/// This function returns immediately and doesn't provide any information on
/// if ingestion was successful or not.
pub fn process_event(&mut self, json: &str) -> Result<()> {
// Convert the Rust string to a C-style string
let c_json = CString::new(json).expect("CString::new failed");
let c_json_ptr = c_json.as_ptr();

// Get the length of the string
let len = json.len() as libc::c_int;

let res = unsafe { bindings::ndb_process_event(self.ndb, c_json_ptr, len) };

if res == 0 {
return Err(Error::NoteProcessFailed);
}

Ok(())
}

/// Get a note from the database. Takes a [Transaction] and a 32-byte [Note] Id
pub fn get_note_by_id<'a>(
&self,
transaction: &'a mut Transaction,
Expand All @@ -55,11 +87,13 @@ impl Ndb {
Ok(Note::new_transactional(note_ptr, len, primkey, transaction))
}

/// Get the underlying pointer to the context in C
pub fn as_ptr(&self) -> *mut bindings::ndb {
return self.ndb;
}
}

/// The database is automatically closed when [Ndb] is [Drop]ped.
impl Drop for Ndb {
fn drop(&mut self) {
unsafe {
Expand All @@ -73,16 +107,39 @@ mod tests {
use super::*;
use crate::config::Config;
use crate::test_util;
use std::fs;

#[test]
fn ndb_init_works() {
// Initialize ndb
let db = "target/testdbs/init_works";

{
let cfg = Config::new();
let _ = Ndb::new(".", &cfg).expect("ok");
let _ = Ndb::new(db, &cfg).expect("ok");
}

test_util::cleanup_db(db);
}

#[test]
fn process_event_works() {
let db = "target/testdbs/event_works";

{
let mut ndb = Ndb::new(db, &Config::new()).expect("ndb");
ndb.process_event(r#"["EVENT","s",{"id": "702555e52e82cc24ad517ba78c21879f6e47a7c0692b9b20df147916ae8731a3","pubkey": "32bf915904bfde2d136ba45dde32c88f4aca863783999faea2e847a8fafd2f15","created_at": 1702675561,"kind": 1,"tags": [],"content": "hello, world","sig": "2275c5f5417abfd644b7bc74f0388d70feb5d08b6f90fa18655dda5c95d013bfbc5258ea77c05b7e40e0ee51d8a2efa931dc7a0ec1db4c0a94519762c6625675"}]"#).expect("process ok");
}

{
let ndb = Ndb::new(db, &Config::new()).expect("ndb");
let id =
hex::decode("702555e52e82cc24ad517ba78c21879f6e47a7c0692b9b20df147916ae8731a3")
.expect("hex id");
let mut txn = Transaction::new(&ndb).expect("txn");
let id_bytes: [u8; 32] = id.try_into().expect("id bytes");
let note = ndb.get_note_by_id(&mut txn, &id_bytes).expect("note");
assert!(note.kind() == 1);
}

test_util::cleanup_db();
test_util::cleanup_db(&db);
}
}
26 changes: 20 additions & 6 deletions src/note.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,19 @@ use crate::transaction::Transaction;

#[derive(Debug)]
pub enum Note<'a> {
/// A note in-memory outside of nostrdb
/// A note in-memory outside of nostrdb. This note is a pointer to a note in
/// memory and will be free'd when [Drop]ped. Method such as [Note::from_json]
/// will create owned notes in memory.
///
/// [Drop]: std::ops::Drop
Owned {
ptr: *mut bindings::ndb_note,
size: usize,
},

/// A note inside of nostrdb. Tied to the lifetime of a
/// [Transaction] to ensure no reading of data outside
/// of a transaction. Construct these with [Note::new_transactional].
/// of a transaction.
Transactional {
ptr: *mut bindings::ndb_note,
size: usize,
Expand All @@ -21,13 +25,21 @@ pub enum Note<'a> {
}

impl<'a> Note<'a> {
pub fn new_owned(ptr: *mut bindings::ndb_note, size: usize) -> Note<'static> {
/// Constructs an owned `Note`. This note is a pointer to a note in
/// memory and will be free'd when [Drop]ped. You normally wouldn't
/// use this method directly, public consumer would use from_json instead.
///
/// [Drop]: std::ops::Drop
pub(crate) fn new_owned(ptr: *mut bindings::ndb_note, size: usize) -> Note<'static> {
Note::Owned { ptr, size }
}

/// Constructs a `Note` in a transactional context.
/// Use [Note::new_transactional] to create a new transactional note.
pub fn new_transactional(
/// You normally wouldn't use this method directly, it is used by
/// functions that get notes from the database like
/// [ndb_get_note_by_id]
pub(crate) fn new_transactional(
ptr: *mut bindings::ndb_note,
size: usize,
key: u64,
Expand Down Expand Up @@ -80,10 +92,12 @@ mod tests {
use crate::ndb::Ndb;
use crate::test_util;

let db = "target/testdbs/note_query_works";

// Initialize ndb
{
let cfg = Config::new();
let ndb = Ndb::new(".", &cfg).expect("db open");
let ndb = Ndb::new(&db, &cfg).expect("db open");
let mut txn = Transaction::new(&ndb).expect("new txn");

let err = ndb
Expand All @@ -92,6 +106,6 @@ mod tests {
assert!(err == Error::NotFound);
}

test_util::cleanup_db();
test_util::cleanup_db(db);
}
}
8 changes: 5 additions & 3 deletions src/test_util.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use std::fs;
use std::path::Path;

pub fn cleanup_db() {
let _ = fs::remove_file("data.mdb");
let _ = fs::remove_file("lock.mdb");
pub fn cleanup_db(path: &str) {
let p = Path::new(path);
let _ = fs::remove_file(p.join("data.mdb"));
let _ = fs::remove_file(p.join("lock.mdb"));
}
10 changes: 5 additions & 5 deletions src/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ use crate::bindings;
use crate::error::Error;
use crate::ndb::Ndb;
use crate::result::Result;
use log::debug;

/// A `nostrdb` transaction. Only one is allowed to be active per thread.
#[derive(Debug)]
Expand Down Expand Up @@ -64,22 +63,23 @@ mod tests {

#[test]
fn transaction_inheritence_fails() {
let db = "target/testdbs/txn_inheritence_fails";
// Initialize ndb
{
let cfg = Config::new();
let ndb = Ndb::new(".", &cfg).expect("ndb open failed");
let ndb = Ndb::new(db, &cfg).expect("ndb open failed");

{
let txn = Transaction::new(&ndb).expect("txn1 failed");
let _txn = Transaction::new(&ndb).expect("txn1 failed");
let txn2 = Transaction::new(&ndb).expect_err("tx2");
assert!(txn2 == Error::TransactionFailed);
}

{
let txn = Transaction::new(&ndb).expect("txn1 failed");
let _txn = Transaction::new(&ndb).expect("txn1 failed");
}
}

test_util::cleanup_db();
test_util::cleanup_db(db);
}
}

0 comments on commit 26858ca

Please sign in to comment.