From 4b0b0e1602cd5d92fea1121d87df472ee0cbd49e Mon Sep 17 00:00:00 2001 From: Mike Dilger Date: Tue, 17 Oct 2023 10:09:53 +1300 Subject: [PATCH] storage: person_list_2 database, records if entry is 'public' --- gossip-lib/src/storage/mod.rs | 1 + gossip-lib/src/storage/person_lists2.rs | 158 +++++++++++++++++++ gossip-lib/src/storage/types/person_list1.rs | 4 +- 3 files changed, 162 insertions(+), 1 deletion(-) create mode 100644 gossip-lib/src/storage/person_lists2.rs diff --git a/gossip-lib/src/storage/mod.rs b/gossip-lib/src/storage/mod.rs index b635258e..350a8c4c 100644 --- a/gossip-lib/src/storage/mod.rs +++ b/gossip-lib/src/storage/mod.rs @@ -27,6 +27,7 @@ mod hashtags1; mod people1; mod people2; mod person_lists1; +mod person_lists2; mod person_relays1; mod relationships1; mod relays1; diff --git a/gossip-lib/src/storage/person_lists2.rs b/gossip-lib/src/storage/person_lists2.rs new file mode 100644 index 00000000..fd306b44 --- /dev/null +++ b/gossip-lib/src/storage/person_lists2.rs @@ -0,0 +1,158 @@ +use super::types::PersonList1; +use crate::error::Error; +use crate::storage::{RawDatabase, Storage}; +use heed::types::UnalignedSlice; +use heed::RwTxn; +use nostr_types::PublicKey; +use speedy::{Readable, Writable}; +use std::collections::HashMap; +use std::sync::Mutex; + +// Pubkey -> Vec<(PersonList, bool)> // bool is if private or not +// key: pubkey.as_bytes() + +static PERSON_LISTS2_DB_CREATE_LOCK: Mutex<()> = Mutex::new(()); +static mut PERSON_LISTS2_DB: Option = None; + +impl Storage { + pub(super) fn db_person_lists2(&self) -> Result { + unsafe { + if let Some(db) = PERSON_LISTS2_DB { + Ok(db) + } else { + // Lock. This drops when anything returns. + let _lock = PERSON_LISTS2_DB_CREATE_LOCK.lock(); + + // In case of a race, check again + if let Some(db) = PERSON_LISTS2_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>() + // no .flags needed + .name("person_lists_2") + .create(&mut txn)?; + txn.commit()?; + PERSON_LISTS2_DB = Some(db); + Ok(db) + } + } + } + + pub(crate) fn read_person_lists2( + &self, + pubkey: &PublicKey, + ) -> Result, Error> { + let key: Vec = pubkey.to_bytes(); + let txn = self.env.read_txn()?; + Ok(match self.db_person_lists2()?.get(&txn, &key)? { + None => HashMap::new(), + Some(bytes) => HashMap::::read_from_buffer(bytes)?, + }) + } + + pub(crate) fn write_person_lists2<'a>( + &'a self, + pubkey: &PublicKey, + map: HashMap, + rw_txn: Option<&mut RwTxn<'a>>, + ) -> Result<(), Error> { + let key: Vec = pubkey.to_bytes(); + let bytes: Vec = map.write_to_vec()?; + + let f = |txn: &mut RwTxn<'a>| -> Result<(), Error> { + self.db_person_lists2()?.put(txn, &key, &bytes)?; + 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 get_people_in_list2( + &self, + list: PersonList1, + public: Option, + ) -> Result, Error> { + let txn = self.env.read_txn()?; + let mut pubkeys: Vec = Vec::new(); + for result in self.db_person_lists2()?.iter(&txn)? { + let (key, val) = result?; + let pubkey = PublicKey::from_bytes(key, true)?; + let map = HashMap::::read_from_buffer(val)?; + if let Some(p) = map.get(&list) { + match public { + Some(p2) => { + if p2 == *p { + pubkeys.push(pubkey); + } + } + None => { + pubkeys.push(pubkey); + } + } + } + } + Ok(pubkeys) + } + + pub(crate) fn clear_person_list2<'a>( + &'a self, + list: PersonList1, + rw_txn: Option<&mut RwTxn<'a>>, + ) -> Result<(), Error> { + let f = |txn: &mut RwTxn<'a>| -> Result<(), Error> { + let mut fixed: Vec<(PublicKey, HashMap)> = Vec::new(); + + // Collect records that require changing + // (lmdb doesn't like changing them in place while iterating) + for result in self.db_person_lists2()?.iter(txn)? { + let (key, val) = result?; + let pubkey = PublicKey::from_bytes(key, true)?; + let mut map = HashMap::::read_from_buffer(val)?; + if map.contains_key(&list) { + map.remove(&list); + fixed.push((pubkey, map)); + } + } + + // Change them + for (pubkey, map) in fixed.drain(..) { + let key: Vec = pubkey.to_bytes(); + if map.is_empty() { + self.db_person_lists2()?.delete(txn, &key)?; + } else { + let bytes: Vec = map.write_to_vec()?; + self.db_person_lists2()?.put(txn, &key, &bytes)?; + } + } + + Ok(()) + }; + + match rw_txn { + Some(txn) => f(txn)?, + None => { + let mut txn = self.env.write_txn()?; + f(&mut txn)?; + txn.commit()?; + } + }; + + Ok(()) + } +} diff --git a/gossip-lib/src/storage/types/person_list1.rs b/gossip-lib/src/storage/types/person_list1.rs index b3467ebc..b002c691 100644 --- a/gossip-lib/src/storage/types/person_list1.rs +++ b/gossip-lib/src/storage/types/person_list1.rs @@ -1,5 +1,7 @@ +use speedy::{Readable, Writable}; + /// Lists people can be added to -#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Readable, Writable)] #[repr(u8)] pub enum PersonList1 { Muted = 0,