nostrdb-rs

nostrdb in rust!
git clone git://jb55.com/nostrdb-rs
Log | Files | Refs | Submodules | README | LICENSE

commit 017d32014e53f0769fadbd666a0b4703c2eb773d
parent 6956b9f955463404b8eff3b7abe0cc3092cb5958
Author: William Casarin <jb55@jb55.com>
Date:   Thu, 22 Jan 2026 13:39:03 -0800

Merge giftwrap support

William Casarin (7):
      build: add libsodium
      bindings: update posix bindings for giftwrap changes
      ndb: add add_key, process_giftwraps, rumor note fns
      giftwraps: fix double free bug
      nostrdb: fix crash in ingester thread
      win: fix windows build

Diffstat:
MCargo.toml | 1+
Mbuild.rs | 59++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Msrc/bindings_posix.rs | 168+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/bindings_win.rs | 168+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/ndb.rs | 13+++++++++++++
Msrc/note.rs | 42+++++++++++++++++++++++++++++++++++++++++-
6 files changed, 449 insertions(+), 2 deletions(-)

diff --git a/Cargo.toml b/Cargo.toml @@ -24,6 +24,7 @@ thiserror = "2.0.7" futures = "0.3.31" tokio = { version = "1", features = ["rt-multi-thread", "macros", "time"] } tracing = "0.1.40" +libsodium-sys-stable = { version = "1.22.5", features = ["optimized", "minimal"] } [dev-dependencies] hex = "0.4.3" diff --git a/build.rs b/build.rs @@ -1,7 +1,7 @@ // build.rs use cc::Build; use std::env; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; fn secp256k1_build(base_config: &mut Build) { // Actual build @@ -30,6 +30,10 @@ fn secp256k1_build(base_config: &mut Build) { .file("nostrdb/deps/secp256k1/src/precomputed_ecmult.c") .file("nostrdb/deps/secp256k1/src/secp256k1.c"); + // libsodium + //base_config + //.file("nostrdb/deps/libsodium/src/libsodium/crypto_stream/chacha20/stream_chacha20.c"); + if env::var("PROFILE").unwrap() == "debug" { base_config.flag("-O1"); } @@ -69,6 +73,10 @@ fn main() { "nostrdb/src/bolt11/bech32.c", "nostrdb/src/block.c", "nostrdb/src/metadata.c", + "nostrdb/src/nip44.c", + "nostrdb/src/base64.c", + "nostrdb/src/hmac_sha256.c", + "nostrdb/src/hkdf_sha256.c", "nostrdb/src/binmoji.c", "nostrdb/deps/flatcc/src/runtime/json_parser.c", "nostrdb/deps/flatcc/src/runtime/verifier.c", @@ -88,6 +96,46 @@ fn main() { //.flag("-Werror") //.flag("-g") + { + // Provided by libsodium-sys-stable’s build script + let dep_include = std::env::var("DEP_SODIUM_INCLUDE") + .expect("DEP_SODIUM_INCLUDE not set; is libsodium-sys-stable a dependency?"); + + let dep_include_path = Path::new(&dep_include); + + // If DEP_SODIUM_INCLUDE doesn't directly contain `sodium/`, try the known Windows layout: + // <...>/out/installed/libsodium/include + let mut include_root = dep_include_path.to_path_buf(); + + if !include_root.join("sodium").is_dir() { + // try sibling layout relative to the libsodium build output + // (DEP_SODIUM_INCLUDE is often <...>/out/installed/include on Windows) + let candidate = dep_include_path + .parent() // <...>/out/installed + .unwrap_or(dep_include_path) + .join("libsodium") + .join("include"); + + if candidate.join("sodium").is_dir() { + include_root = candidate; + } + } + + build.include(include_root); + + // static libsodium on MSVC: avoid __imp_ dllimport mismatch + if cfg!(target_env = "msvc") { + build.define("SODIUM_STATIC", Some("1")); + } + + // Optionally use DEP_SODIUM_LIB as well + let sodium_lib_dir = std::env::var("DEP_SODIUM_LIB").ok(); + // Make sure the linker knows where libsodium lives + if let Some(lib_dir) = sodium_lib_dir { + println!("cargo:rustc-link-search=native={lib_dir}"); + } + } + // Link Security framework on macOS if !cfg!(target_os = "windows") { build.files(bolt11_deps()); @@ -117,6 +165,15 @@ fn main() { secp256k1_build(&mut build); build.compile("libnostrdb.a"); + build.define("SODIUM_STATIC", Some("1")); + + { + let target_env = std::env::var("CARGO_CFG_TARGET_ENV").unwrap_or_default(); + let is_windows = target_env == "msvc"; + let sodium_name = if is_windows { "libsodium" } else { "sodium" }; + + println!("cargo:rustc-link-lib=static={sodium_name}"); + } println!("cargo:rustc-link-lib=static=nostrdb"); diff --git a/src/bindings_posix.rs b/src/bindings_posix.rs @@ -356,6 +356,9 @@ pub const _STRING_H: u32 = 1; pub const _STRINGS_H: u32 = 1; pub const NDB_PACKED_STR: u32 = 1; pub const NDB_PACKED_ID: u32 = 2; +pub const NDB_NOTE_FLAG_DELETED: u32 = 1; +pub const NDB_NOTE_FLAG_RUMOR: u32 = 2; +pub const NDB_NOTE_FLAG_UNWRAPPED: u32 = 4; pub const NDB_FLAG_NOMIGRATE: u32 = 1; pub const NDB_FLAG_SKIP_NOTE_VERIFY: u32 = 2; pub const NDB_FLAG_NO_FULLTEXT: u32 = 4; @@ -2360,6 +2363,140 @@ pub const ndb_metadata_type_NDB_NOTE_META_RESERVED: ndb_metadata_type = 0; pub const ndb_metadata_type_NDB_NOTE_META_COUNTS: ndb_metadata_type = 100; pub const ndb_metadata_type_NDB_NOTE_META_REACTION: ndb_metadata_type = 200; pub type ndb_metadata_type = ::std::os::raw::c_uint; +pub const ndb_decrypt_result_NIP44_OK: ndb_decrypt_result = 0; +pub const ndb_decrypt_result_NIP44_ERR_UNSUPPORTED_ENCODING: ndb_decrypt_result = 1; +pub const ndb_decrypt_result_NIP44_ERR_INVALID_PAYLOAD: ndb_decrypt_result = 2; +pub const ndb_decrypt_result_NIP44_ERR_BASE64_DECODE: ndb_decrypt_result = 3; +pub const ndb_decrypt_result_NIP44_ERR_SECKEY_VERIFY_FAILED: ndb_decrypt_result = 4; +pub const ndb_decrypt_result_NIP44_ERR_PUBKEY_PARSE_FAILED: ndb_decrypt_result = 5; +pub const ndb_decrypt_result_NIP44_ERR_ECDH_FAILED: ndb_decrypt_result = 6; +pub const ndb_decrypt_result_NIP44_ERR_FILL_RANDOM_FAILED: ndb_decrypt_result = 7; +pub const ndb_decrypt_result_NIP44_ERR_INVALID_MAC: ndb_decrypt_result = 8; +pub const ndb_decrypt_result_NIP44_ERR_INVALID_PADDING: ndb_decrypt_result = 9; +pub const ndb_decrypt_result_NIP44_ERR_BUFFER_TOO_SMALL: ndb_decrypt_result = 10; +pub type ndb_decrypt_result = ::std::os::raw::c_uint; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct nip44_payload { + pub version: ::std::os::raw::c_uchar, + pub nonce: *mut ::std::os::raw::c_uchar, + pub ciphertext: *mut ::std::os::raw::c_uchar, + pub ciphertext_len: usize, + pub mac: *mut ::std::os::raw::c_uchar, +} +#[test] +fn bindgen_test_layout_nip44_payload() { + const UNINIT: ::std::mem::MaybeUninit<nip44_payload> = ::std::mem::MaybeUninit::uninit(); + let ptr = UNINIT.as_ptr(); + assert_eq!( + ::std::mem::size_of::<nip44_payload>(), + 40usize, + concat!("Size of: ", stringify!(nip44_payload)) + ); + assert_eq!( + ::std::mem::align_of::<nip44_payload>(), + 8usize, + concat!("Alignment of ", stringify!(nip44_payload)) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).version) as usize - ptr as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(nip44_payload), + "::", + stringify!(version) + ) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).nonce) as usize - ptr as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(nip44_payload), + "::", + stringify!(nonce) + ) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).ciphertext) as usize - ptr as usize }, + 16usize, + concat!( + "Offset of field: ", + stringify!(nip44_payload), + "::", + stringify!(ciphertext) + ) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).ciphertext_len) as usize - ptr as usize }, + 24usize, + concat!( + "Offset of field: ", + stringify!(nip44_payload), + "::", + stringify!(ciphertext_len) + ) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).mac) as usize - ptr as usize }, + 32usize, + concat!( + "Offset of field: ", + stringify!(nip44_payload), + "::", + stringify!(mac) + ) + ); +} +extern "C" { + pub fn nip44_decrypt( + secp_context: *mut ::std::os::raw::c_void, + sender_pubkey: *const ::std::os::raw::c_uchar, + receiver_seckey: *const ::std::os::raw::c_uchar, + payload: *const ::std::os::raw::c_char, + payload_len: ::std::os::raw::c_int, + buf: *mut ::std::os::raw::c_uchar, + bufsize: usize, + decrypted: *mut *mut ::std::os::raw::c_uchar, + decrypted_len: *mut u16, + ) -> ndb_decrypt_result; +} +extern "C" { + pub fn nip44_encrypt( + secp: *mut ::std::os::raw::c_void, + sender_seckey: *const ::std::os::raw::c_uchar, + receiver_pubkey: *const ::std::os::raw::c_uchar, + plaintext: *const ::std::os::raw::c_uchar, + plaintext_size: u16, + buf: *mut ::std::os::raw::c_uchar, + bufsize: usize, + out: *mut *mut ::std::os::raw::c_char, + out_len: *mut isize, + ) -> ndb_decrypt_result; +} +extern "C" { + pub fn nip44_decrypt_raw( + secp: *mut ::std::os::raw::c_void, + sender_pubkey: *const ::std::os::raw::c_uchar, + receiver_seckey: *const ::std::os::raw::c_uchar, + decoded: *mut nip44_payload, + decrypted: *mut *mut ::std::os::raw::c_uchar, + decrypted_len: *mut u16, + ) -> ndb_decrypt_result; +} +extern "C" { + pub fn nip44_decode_payload( + decoded: *mut nip44_payload, + buf: *mut ::std::os::raw::c_uchar, + bufsize: usize, + payload: *const ::std::os::raw::c_char, + payload_len: usize, + ) -> ndb_decrypt_result; +} +extern "C" { + pub fn nip44_err_msg(res: ndb_decrypt_result) -> *const ::std::os::raw::c_char; +} #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct ndb_tag_ptr { @@ -5295,6 +5432,9 @@ extern "C" { pub fn ndb_db_version(txn: *mut ndb_txn) -> ::std::os::raw::c_int; } extern "C" { + pub fn ndb_add_key(ndb: *mut ndb, key: *mut ::std::os::raw::c_uchar) -> ::std::os::raw::c_int; +} +extern "C" { pub fn ndb_process_event( arg1: *mut ndb, json: *const ::std::os::raw::c_char, @@ -5324,6 +5464,9 @@ extern "C" { ) -> ::std::os::raw::c_int; } extern "C" { + pub fn ndb_process_giftwraps(arg1: *mut ndb, arg2: *mut ndb_txn) -> ::std::os::raw::c_int; +} +extern "C" { pub fn ndb_process_events_with( ndb: *mut ndb, ldjson: *const ::std::os::raw::c_char, @@ -5461,6 +5604,16 @@ extern "C" { ) -> ::std::os::raw::c_int; } extern "C" { + pub fn ndb_note_from_json_custom( + json: *const ::std::os::raw::c_char, + len: ::std::os::raw::c_int, + arg1: *mut *mut ndb_note, + buf: *mut ::std::os::raw::c_uchar, + buflen: ::std::os::raw::c_int, + parse_cond: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int; +} +extern "C" { pub fn ndb_builder_init( builder: *mut ndb_builder, buf: *mut ::std::os::raw::c_uchar, @@ -5880,6 +6033,18 @@ extern "C" { pub fn ndb_str_len(str_: *mut ndb_str) -> ::std::os::raw::c_int; } extern "C" { + pub fn ndb_note_flags(arg1: *mut ndb_note) -> *mut u16; +} +extern "C" { + pub fn ndb_note_is_rumor(note: *mut ndb_note) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn ndb_note_rumor_receiver_pubkey(note: *mut ndb_note) -> *mut ::std::os::raw::c_uchar; +} +extern "C" { + pub fn ndb_note_rumor_giftwrap_id(note: *mut ndb_note) -> *mut ::std::os::raw::c_uchar; +} +extern "C" { #[doc = " write the note as json to a buffer"] pub fn ndb_note_json( arg1: *mut ndb_note, @@ -6403,6 +6568,9 @@ fn bindgen_test_layout_ndb_note_meta() { ) ); } +pub const ndb_note_meta_flags_NDB_NOTE_META_FLAG_DELETED: ndb_note_meta_flags = 0; +pub const ndb_note_meta_flags_NDB_NOTE_META_FLAG_SEEN: ndb_note_meta_flags = 2; +pub type ndb_note_meta_flags = ::std::os::raw::c_uint; pub type __builtin_va_list = [__va_list_tag; 1usize]; #[repr(C)] #[derive(Debug, Copy, Clone)] diff --git a/src/bindings_win.rs b/src/bindings_win.rs @@ -353,6 +353,9 @@ pub const EWOULDBLOCK: u32 = 140; pub const _NLSCMPERROR: u32 = 2147483647; pub const NDB_PACKED_STR: u32 = 1; pub const NDB_PACKED_ID: u32 = 2; +pub const NDB_NOTE_FLAG_DELETED: u32 = 1; +pub const NDB_NOTE_FLAG_RUMOR: u32 = 2; +pub const NDB_NOTE_FLAG_UNWRAPPED: u32 = 4; pub const NDB_FLAG_NOMIGRATE: u32 = 1; pub const NDB_FLAG_SKIP_NOTE_VERIFY: u32 = 2; pub const NDB_FLAG_NO_FULLTEXT: u32 = 4; @@ -2505,6 +2508,140 @@ pub const ndb_metadata_type_NDB_NOTE_META_RESERVED: ndb_metadata_type = 0; pub const ndb_metadata_type_NDB_NOTE_META_COUNTS: ndb_metadata_type = 100; pub const ndb_metadata_type_NDB_NOTE_META_REACTION: ndb_metadata_type = 200; pub type ndb_metadata_type = ::std::os::raw::c_int; +pub const ndb_decrypt_result_NIP44_OK: ndb_decrypt_result = 0; +pub const ndb_decrypt_result_NIP44_ERR_UNSUPPORTED_ENCODING: ndb_decrypt_result = 1; +pub const ndb_decrypt_result_NIP44_ERR_INVALID_PAYLOAD: ndb_decrypt_result = 2; +pub const ndb_decrypt_result_NIP44_ERR_BASE64_DECODE: ndb_decrypt_result = 3; +pub const ndb_decrypt_result_NIP44_ERR_SECKEY_VERIFY_FAILED: ndb_decrypt_result = 4; +pub const ndb_decrypt_result_NIP44_ERR_PUBKEY_PARSE_FAILED: ndb_decrypt_result = 5; +pub const ndb_decrypt_result_NIP44_ERR_ECDH_FAILED: ndb_decrypt_result = 6; +pub const ndb_decrypt_result_NIP44_ERR_FILL_RANDOM_FAILED: ndb_decrypt_result = 7; +pub const ndb_decrypt_result_NIP44_ERR_INVALID_MAC: ndb_decrypt_result = 8; +pub const ndb_decrypt_result_NIP44_ERR_INVALID_PADDING: ndb_decrypt_result = 9; +pub const ndb_decrypt_result_NIP44_ERR_BUFFER_TOO_SMALL: ndb_decrypt_result = 10; +pub type ndb_decrypt_result = ::std::os::raw::c_int; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct nip44_payload { + pub version: ::std::os::raw::c_uchar, + pub nonce: *mut ::std::os::raw::c_uchar, + pub ciphertext: *mut ::std::os::raw::c_uchar, + pub ciphertext_len: usize, + pub mac: *mut ::std::os::raw::c_uchar, +} +#[test] +fn bindgen_test_layout_nip44_payload() { + const UNINIT: ::std::mem::MaybeUninit<nip44_payload> = ::std::mem::MaybeUninit::uninit(); + let ptr = UNINIT.as_ptr(); + assert_eq!( + ::std::mem::size_of::<nip44_payload>(), + 40usize, + concat!("Size of: ", stringify!(nip44_payload)) + ); + assert_eq!( + ::std::mem::align_of::<nip44_payload>(), + 8usize, + concat!("Alignment of ", stringify!(nip44_payload)) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).version) as usize - ptr as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(nip44_payload), + "::", + stringify!(version) + ) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).nonce) as usize - ptr as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(nip44_payload), + "::", + stringify!(nonce) + ) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).ciphertext) as usize - ptr as usize }, + 16usize, + concat!( + "Offset of field: ", + stringify!(nip44_payload), + "::", + stringify!(ciphertext) + ) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).ciphertext_len) as usize - ptr as usize }, + 24usize, + concat!( + "Offset of field: ", + stringify!(nip44_payload), + "::", + stringify!(ciphertext_len) + ) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).mac) as usize - ptr as usize }, + 32usize, + concat!( + "Offset of field: ", + stringify!(nip44_payload), + "::", + stringify!(mac) + ) + ); +} +extern "C" { + pub fn nip44_decrypt( + secp_context: *mut ::std::os::raw::c_void, + sender_pubkey: *const ::std::os::raw::c_uchar, + receiver_seckey: *const ::std::os::raw::c_uchar, + payload: *const ::std::os::raw::c_char, + payload_len: ::std::os::raw::c_int, + buf: *mut ::std::os::raw::c_uchar, + bufsize: usize, + decrypted: *mut *mut ::std::os::raw::c_uchar, + decrypted_len: *mut u16, + ) -> ndb_decrypt_result; +} +extern "C" { + pub fn nip44_encrypt( + secp: *mut ::std::os::raw::c_void, + sender_seckey: *const ::std::os::raw::c_uchar, + receiver_pubkey: *const ::std::os::raw::c_uchar, + plaintext: *const ::std::os::raw::c_uchar, + plaintext_size: u16, + buf: *mut ::std::os::raw::c_uchar, + bufsize: usize, + out: *mut *mut ::std::os::raw::c_char, + out_len: *mut isize, + ) -> ndb_decrypt_result; +} +extern "C" { + pub fn nip44_decrypt_raw( + secp: *mut ::std::os::raw::c_void, + sender_pubkey: *const ::std::os::raw::c_uchar, + receiver_seckey: *const ::std::os::raw::c_uchar, + decoded: *mut nip44_payload, + decrypted: *mut *mut ::std::os::raw::c_uchar, + decrypted_len: *mut u16, + ) -> ndb_decrypt_result; +} +extern "C" { + pub fn nip44_decode_payload( + decoded: *mut nip44_payload, + buf: *mut ::std::os::raw::c_uchar, + bufsize: usize, + payload: *const ::std::os::raw::c_char, + payload_len: usize, + ) -> ndb_decrypt_result; +} +extern "C" { + pub fn nip44_err_msg(res: ndb_decrypt_result) -> *const ::std::os::raw::c_char; +} #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct ndb_tag_ptr { @@ -5439,6 +5576,9 @@ extern "C" { pub fn ndb_db_version(txn: *mut ndb_txn) -> ::std::os::raw::c_int; } extern "C" { + pub fn ndb_add_key(ndb: *mut ndb, key: *mut ::std::os::raw::c_uchar) -> ::std::os::raw::c_int; +} +extern "C" { pub fn ndb_process_event( arg1: *mut ndb, json: *const ::std::os::raw::c_char, @@ -5468,6 +5608,9 @@ extern "C" { ) -> ::std::os::raw::c_int; } extern "C" { + pub fn ndb_process_giftwraps(arg1: *mut ndb, arg2: *mut ndb_txn) -> ::std::os::raw::c_int; +} +extern "C" { pub fn ndb_process_events_with( ndb: *mut ndb, ldjson: *const ::std::os::raw::c_char, @@ -5602,6 +5745,16 @@ extern "C" { ) -> ::std::os::raw::c_int; } extern "C" { + pub fn ndb_note_from_json_custom( + json: *const ::std::os::raw::c_char, + len: ::std::os::raw::c_int, + arg1: *mut *mut ndb_note, + buf: *mut ::std::os::raw::c_uchar, + buflen: ::std::os::raw::c_int, + parse_cond: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int; +} +extern "C" { pub fn ndb_builder_init( builder: *mut ndb_builder, buf: *mut ::std::os::raw::c_uchar, @@ -6021,6 +6174,18 @@ extern "C" { pub fn ndb_str_len(str_: *mut ndb_str) -> ::std::os::raw::c_int; } extern "C" { + pub fn ndb_note_flags(arg1: *mut ndb_note) -> *mut u16; +} +extern "C" { + pub fn ndb_note_is_rumor(note: *mut ndb_note) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn ndb_note_rumor_receiver_pubkey(note: *mut ndb_note) -> *mut ::std::os::raw::c_uchar; +} +extern "C" { + pub fn ndb_note_rumor_giftwrap_id(note: *mut ndb_note) -> *mut ::std::os::raw::c_uchar; +} +extern "C" { #[doc = " write the note as json to a buffer"] pub fn ndb_note_json( arg1: *mut ndb_note, @@ -6543,6 +6708,9 @@ fn bindgen_test_layout_ndb_note_meta() { ) ); } +pub const ndb_note_meta_flags_NDB_NOTE_META_FLAG_DELETED: ndb_note_meta_flags = 0; +pub const ndb_note_meta_flags_NDB_NOTE_META_FLAG_SEEN: ndb_note_meta_flags = 2; +pub type ndb_note_meta_flags = ::std::os::raw::c_int; #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct __crt_locale_data { diff --git a/src/ndb.rs b/src/ndb.rs @@ -159,6 +159,19 @@ impl Ndb { self.process_event_with(json, IngestMetadata::new().client(true)) } + /// Attempt to unwrap any unprocessed giftwraps + pub fn process_giftwraps(&self, txn: &Transaction) { + unsafe { + bindings::ndb_process_giftwraps(self.as_ptr(), txn.as_mut_ptr()); + } + } + + /// Add a secret key to nostrdb's note ingester threads so that + /// nostrdb can unwrap incoming giftwraps. + pub fn add_key(&self, key: &[u8; 32]) -> bool { + unsafe { bindings::ndb_add_key(self.as_ptr(), key as *const u8 as *mut u8) != 0 } + } + pub fn query<'a>( &self, txn: &'a Transaction, diff --git a/src/note.rs b/src/note.rs @@ -189,6 +189,43 @@ impl<'a> Note<'a> { } #[inline] + pub fn flags(&self) -> u16 { + unsafe { *bindings::ndb_note_flags(self.as_ptr()) } + } + + #[inline] + pub fn is_rumor(&self) -> bool { + (self.flags() & (bindings::NDB_NOTE_FLAG_RUMOR as u16)) + == bindings::NDB_NOTE_FLAG_RUMOR as u16 + } + + #[inline] + pub fn rumor_giftwrap_id(&self) -> Option<&'a [u8; 32]> { + unsafe { + let ptr = bindings::ndb_note_rumor_giftwrap_id(self.as_ptr()); + + if ptr.is_null() { + return None; + } + + Some(&*(ptr as *const [u8; 32])) + } + } + + #[inline] + pub fn rumor_receiver_pubkey(&self) -> Option<&'a [u8; 32]> { + unsafe { + let ptr = bindings::ndb_note_rumor_receiver_pubkey(self.as_ptr()); + + if ptr.is_null() { + return None; + } + + Some(&*(ptr as *const [u8; 32])) + } + } + + #[inline] pub fn as_ptr(&self) -> *mut bindings::ndb_note { match self { Note::Owned { ptr, .. } => *ptr, @@ -623,6 +660,9 @@ mod tests { let json = note.json().expect("note json"); // the signature changes so 267 is everything up until the signature - assert_eq!(&json[..267], "{\"id\":\"fb165be22c7b2518b749aabb7140c73f0887fe84475c82785700663be85ba859\",\"pubkey\":\"6c540ed060bfc2b0c5b6f09cd3ebedf980ef7bc836d69582361d20f2ad124f23\",\"created_at\":42,\"kind\":1,\"tags\":[[\"comment\",\"this is a comment\"],[\"blah\",\"something\"]],\"content\":\"this is the content\""); + assert_eq!( + &json[..267], + "{\"id\":\"fb165be22c7b2518b749aabb7140c73f0887fe84475c82785700663be85ba859\",\"pubkey\":\"6c540ed060bfc2b0c5b6f09cd3ebedf980ef7bc836d69582361d20f2ad124f23\",\"created_at\":42,\"kind\":1,\"tags\":[[\"comment\",\"this is a comment\"],[\"blah\",\"something\"]],\"content\":\"this is the content\"" + ); } }