commit 26858ca78a7a87746386e386542414bbbcc45f3f
parent e99e6e30b730b01b6a7ba0db6e927a00f1e40bae
Author: William Casarin <jb55@jb55.com>
Date: Fri, 15 Dec 2023 17:13:58 -0800
add ndb.process_event and tests
Diffstat:
7 files changed, 101 insertions(+), 39 deletions(-)
diff --git a/Cargo.toml b/Cargo.toml
@@ -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
diff --git a/build.rs b/build.rs
@@ -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" {
@@ -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();
diff --git a/src/error.rs b/src/error.rs
@@ -3,5 +3,6 @@ pub enum Error {
DbOpenFailed,
NotFound,
DecodeError,
+ NoteProcessFailed,
TransactionFailed,
}
diff --git a/src/ndb.rs b/src/ndb.rs
@@ -1,3 +1,4 @@
+use libc;
use std::ffi::CString;
use std::ptr;
@@ -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 {
@@ -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,
@@ -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 {
@@ -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);
}
}
diff --git a/src/note.rs b/src/note.rs
@@ -3,7 +3,11 @@ 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,
@@ -11,7 +15,7 @@ pub enum Note<'a> {
/// 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,
@@ -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,
@@ -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
@@ -92,6 +106,6 @@ mod tests {
assert!(err == Error::NotFound);
}
- test_util::cleanup_db();
+ test_util::cleanup_db(db);
}
}
diff --git a/src/test_util.rs b/src/test_util.rs
@@ -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"));
}
diff --git a/src/transaction.rs b/src/transaction.rs
@@ -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)]
@@ -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);
}
}