diff --git a/gossip-lib/src/storage/mod.rs b/gossip-lib/src/storage/mod.rs index eb1a0a54..52696b0e 100644 --- a/gossip-lib/src/storage/mod.rs +++ b/gossip-lib/src/storage/mod.rs @@ -31,6 +31,7 @@ mod person_lists2; mod person_relays1; mod relationships1; mod relays1; +mod reprel1; mod unindexed_giftwraps1; use crate::dm_channel::{DmChannel, DmChannelData}; @@ -45,8 +46,8 @@ use gossip_relay_picker::Direction; use heed::types::UnalignedSlice; use heed::{Database, Env, EnvFlags, EnvOpenOptions, RwTxn}; use nostr_types::{ - EncryptedPrivateKey, Event, EventKind, EventReference, Id, MilliSatoshi, PublicKey, - RelayUrl, Tag, Unixtime, + EncryptedPrivateKey, Event, EventAddr, EventKind, EventReference, Id, MilliSatoshi, + PublicKey, RelayUrl, Tag, Unixtime, }; use paste::paste; use speedy::{Readable, Writable}; @@ -189,6 +190,7 @@ impl Storage { let _ = self.db_people()?; let _ = self.db_person_relays()?; let _ = self.db_relationships()?; + let _ = self.db_reprel()?; let _ = self.db_relays()?; let _ = self.db_unindexed_giftwraps()?; let _ = self.db_person_lists()?; @@ -267,6 +269,11 @@ impl Storage { self.db_relationships1() } + #[inline] + pub(crate) fn db_reprel(&self) -> Result { + self.db_reprel1() + } + #[inline] pub(crate) fn db_relays(&self) -> Result { self.db_relays1() @@ -344,6 +351,13 @@ impl Storage { Ok(self.db_relationships()?.len(&txn)?) } + /// The number of records in the reprel table + #[inline] + pub fn get_reprel_len(&self) -> Result { + let txn = self.env.read_txn()?; + Ok(self.db_reprel()?.len(&txn)?) + } + /// The number of records in the people table #[inline] pub fn get_people_len(&self) -> Result { @@ -1694,6 +1708,24 @@ impl Storage { self.find_relationships1(id) } + /// Write a relationship between an event and an EventAddr (replaceable) + #[inline] + pub(crate) fn write_reprel<'a>( + &'a self, + addr: EventAddr, + related: Id, + relationship: Relationship, + rw_txn: Option<&mut RwTxn<'a>>, + ) -> Result<(), Error> { + self.write_reprel1(addr, related, relationship, rw_txn) + } + + /// Find relationships belonging to the given event to replaceable events + #[inline] + pub fn find_reprels(&self, addr: &EventAddr) -> Result, Error> { + self.find_reprels1(addr) + } + /// Get replies to the given event pub fn get_replies(&self, id: Id) -> Result, Error> { Ok(self @@ -1709,6 +1741,9 @@ impl Storage { .collect()) } + // Get replies to the given EventAddr + // TODO + /// Returns the list of reactions and whether or not this account has already reacted to this event pub fn get_reactions(&self, id: Id) -> Result<(Vec<(char, usize)>, bool), Error> { // Whether or not the Gossip user already reacted to this event diff --git a/gossip-lib/src/storage/reprel1.rs b/gossip-lib/src/storage/reprel1.rs new file mode 100644 index 00000000..696bb6dd --- /dev/null +++ b/gossip-lib/src/storage/reprel1.rs @@ -0,0 +1,123 @@ +use crate::error::Error; +use crate::relationship::Relationship; +use crate::storage::{RawDatabase, Storage}; +use heed::RwTxn; +use heed::{types::UnalignedSlice, DatabaseFlags}; +use nostr_types::{EventAddr, Id}; +use speedy::{Readable, Writable}; +use std::sync::Mutex; + +// Kind:Pubkey:d-tag -> Relationship:Id + +static REPREL1_DB_CREATE_LOCK: Mutex<()> = Mutex::new(()); +static mut REPREL1_DB: Option = None; + +impl Storage { + pub(super) fn db_reprel1(&self) -> Result { + unsafe { + if let Some(db) = REPREL1_DB { + Ok(db) + } else { + // Lock. This drops when anything returns. + let _lock = REPREL1_DB_CREATE_LOCK.lock(); + + // In case of a race, check again + if let Some(db) = REPREL1_DB { + return Ok(db); + } + + // Create it. We know that nobody else is doing this and that + // it cannot happen twice. + let mut txn = self.env.write_txn()?; + let db = self + .env + .database_options() + .types::, UnalignedSlice>() + .flags(DatabaseFlags::DUP_SORT) // NOT FIXED, Relationship serialized isn't. + .name("reprel1") + .create(&mut txn)?; + txn.commit()?; + REPREL1_DB = Some(db); + Ok(db) + } + } + } + + pub(crate) fn write_reprel1<'a>( + &'a self, + addr: EventAddr, + related: Id, + relationship: Relationship, + rw_txn: Option<&mut RwTxn<'a>>, + ) -> Result<(), Error> { + let key = reprel1_into_key(&addr); + let value = reprel1_into_value(relationship, related)?; + let f = |txn: &mut RwTxn<'a>| -> Result<(), Error> { + self.db_reprel1()?.put(txn, &key, &value)?; + Ok(()) + }; + + match rw_txn { + Some(txn) => f(txn)?, + None => { + let mut txn = self.env.write_txn()?; + f(&mut txn)?; + txn.commit()?; + } + }; + + Ok(()) + } + + pub(crate) fn find_reprels1(&self, addr: &EventAddr) -> Result, Error> { + let key = reprel1_into_key(addr); + let txn = self.env.read_txn()?; + let iter = match self.db_reprel1()?.get_duplicates(&txn, &key)? { + Some(iter) => iter, + None => return Ok(vec![]), + }; + let mut output: Vec<(Id, Relationship)> = Vec::new(); + for result in iter { + let (_key, val) = result?; + let (rel, id) = reprel1_from_value(val)?; + output.push((id, rel)); + } + Ok(output) + } +} + +fn reprel1_into_key(ea: &EventAddr) -> Vec { + let u: u32 = ea.kind.into(); + let mut key: Vec = u.to_be_bytes().as_slice().to_owned(); + key.extend(ea.author.as_bytes()); + key.extend(ea.d.as_bytes()); + key +} + +/* +fn reprel1_from_key(key: &[u8]) -> Result { + let u = u32::from_be_bytes(key[..4].try_into().unwrap()); + let kind: EventKind = u.into(); + let pubkey: PublicKey = PublicKey::from_bytes(&key[4..4+32], true)?; + let d: String = String::from_utf8_lossy(&key[4+32..]).to_string(); + Ok(EventAddr { + d, + relays: vec![], + kind, + author: pubkey + }) +} + */ + +fn reprel1_into_value(relationship: Relationship, id: Id) -> Result, Error> { + let mut value: Vec = relationship.write_to_vec()?; + value.extend(id.as_slice()); + Ok(value) +} + +fn reprel1_from_value(value: &[u8]) -> Result<(Relationship, Id), Error> { + let (result, len) = Relationship::read_with_length_from_buffer(value); + let relationship = result?; + let id = Id(value[len..len + 32].try_into().unwrap()); + Ok((relationship, id)) +}