checkpoint
This commit is contained in:
		| @@ -78,7 +78,7 @@ impl ServicesContext { | ||||
|  | ||||
|         // Set up tablestore | ||||
|         trace!("init table store"); | ||||
|         let table_store = TableStore::new(self.config.clone()); | ||||
|         let table_store = TableStore::new(self.config.clone(), protected_store.clone()); | ||||
|         if let Err(e) = table_store.init().await { | ||||
|             error!("failed to init table store: {}", e); | ||||
|             self.shutdown().await; | ||||
|   | ||||
| @@ -169,7 +169,10 @@ impl Crypto { | ||||
|         }; | ||||
|  | ||||
|         // load caches if they are valid for this node id | ||||
|         let mut db = table_store.open("crypto_caches", 1).await?; | ||||
|         let mut db = table_store | ||||
|             .open("crypto_caches", 1) | ||||
|             .await | ||||
|             .wrap_err("failed to open crypto_caches")?; | ||||
|         let caches_valid = match db.load(0, b"cache_validity_key").await? { | ||||
|             Some(v) => v == cache_validity_key, | ||||
|             None => false, | ||||
|   | ||||
| @@ -84,7 +84,7 @@ impl CryptoSystem for CryptoSystemNONE { | ||||
|     fn random_bytes(&self, len: u32) -> Vec<u8> { | ||||
|         let mut bytes = Vec::<u8>::with_capacity(len as usize); | ||||
|         bytes.resize(len as usize, 0u8); | ||||
|         random_bytes(bytes.as_mut()).unwrap(); | ||||
|         random_bytes(bytes.as_mut()); | ||||
|         bytes | ||||
|     } | ||||
|     fn default_salt_length(&self) -> u32 { | ||||
|   | ||||
| @@ -53,8 +53,10 @@ pub type TypedKey = CryptoTyped<PublicKey>; | ||||
| pub type TypedSecret = CryptoTyped<SecretKey>; | ||||
| pub type TypedKeyPair = CryptoTyped<KeyPair>; | ||||
| pub type TypedSignature = CryptoTyped<Signature>; | ||||
| pub type TypedSharedSecret = CryptoTyped<SharedSecret>; | ||||
|  | ||||
| pub type TypedKeySet = CryptoTypedSet<PublicKey>; | ||||
| pub type TypedSecretSet = CryptoTypedSet<SecretKey>; | ||||
| pub type TypedKeyPairSet = CryptoTypedSet<KeyPair>; | ||||
| pub type TypedSignatureSet = CryptoTypedSet<Signature>; | ||||
| pub type TypedSharedSecretSet = CryptoTypedSet<SharedSecret>; | ||||
|   | ||||
| @@ -78,7 +78,7 @@ impl CryptoSystem for CryptoSystemVLD0 { | ||||
|     fn random_bytes(&self, len: u32) -> Vec<u8> { | ||||
|         let mut bytes = Vec::<u8>::with_capacity(len as usize); | ||||
|         bytes.resize(len as usize, 0u8); | ||||
|         random_bytes(bytes.as_mut()).unwrap(); | ||||
|         random_bytes(bytes.as_mut()); | ||||
|         bytes | ||||
|     } | ||||
|     fn default_salt_length(&self) -> u32 { | ||||
| @@ -134,12 +134,12 @@ impl CryptoSystem for CryptoSystemVLD0 { | ||||
|  | ||||
|     fn random_nonce(&self) -> Nonce { | ||||
|         let mut nonce = [0u8; NONCE_LENGTH]; | ||||
|         random_bytes(&mut nonce).unwrap(); | ||||
|         random_bytes(&mut nonce); | ||||
|         Nonce::new(nonce) | ||||
|     } | ||||
|     fn random_shared_secret(&self) -> SharedSecret { | ||||
|         let mut s = [0u8; SHARED_SECRET_LENGTH]; | ||||
|         random_bytes(&mut s).unwrap(); | ||||
|         random_bytes(&mut s); | ||||
|         SharedSecret::new(s) | ||||
|     } | ||||
|     fn compute_dh( | ||||
|   | ||||
| @@ -1,4 +1,3 @@ | ||||
| mod table_db; | ||||
| use super::*; | ||||
|  | ||||
| #[cfg(target_arch = "wasm32")] | ||||
| @@ -10,15 +9,5 @@ mod native; | ||||
| #[cfg(not(target_arch = "wasm32"))] | ||||
| pub use native::*; | ||||
|  | ||||
| pub static KNOWN_TABLE_NAMES: [&'static str; 7] = [ | ||||
|     "crypto_caches", | ||||
|     "RouteSpecStore", | ||||
|     "routing_table", | ||||
|     "local_records", | ||||
|     "local_subkeys", | ||||
|     "remote_records", | ||||
|     "remote_subkeys", | ||||
| ]; | ||||
|  | ||||
| pub static KNOWN_PROTECTED_STORE_KEYS: [&'static str; 4] = | ||||
|     ["node_id", "node_id_secret", "_test_key", "RouteSpecStore"]; | ||||
|   | ||||
| @@ -1,12 +1,10 @@ | ||||
| mod block_store; | ||||
| mod protected_store; | ||||
| mod system; | ||||
| mod table_store; | ||||
|  | ||||
| pub use block_store::*; | ||||
| pub use protected_store::*; | ||||
| pub use system::*; | ||||
| pub use table_store::*; | ||||
|  | ||||
| #[cfg(target_os = "android")] | ||||
| pub mod android; | ||||
|   | ||||
| @@ -1,148 +0,0 @@ | ||||
| use super::*; | ||||
| use crate::intf::table_db::TableDBUnlockedInner; | ||||
| pub use crate::intf::table_db::{TableDB, TableDBTransaction}; | ||||
| use keyvaluedb_sqlite::*; | ||||
| use std::path::PathBuf; | ||||
|  | ||||
| struct TableStoreInner { | ||||
|     opened: BTreeMap<String, Weak<TableDBUnlockedInner>>, | ||||
| } | ||||
|  | ||||
| /// Veilid Table Storage | ||||
| /// Database for storing key value pairs persistently across runs | ||||
| #[derive(Clone)] | ||||
| pub struct TableStore { | ||||
|     config: VeilidConfig, | ||||
|     inner: Arc<Mutex<TableStoreInner>>, | ||||
| } | ||||
|  | ||||
| impl TableStore { | ||||
|     fn new_inner() -> TableStoreInner { | ||||
|         TableStoreInner { | ||||
|             opened: BTreeMap::new(), | ||||
|         } | ||||
|     } | ||||
|     pub(crate) fn new(config: VeilidConfig) -> Self { | ||||
|         Self { | ||||
|             config, | ||||
|             inner: Arc::new(Mutex::new(Self::new_inner())), | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// Delete all known tables | ||||
|     pub async fn delete_all(&self) { | ||||
|         for ktn in &KNOWN_TABLE_NAMES { | ||||
|             if let Err(e) = self.delete(ktn).await { | ||||
|                 error!("failed to delete '{}': {}", ktn, e); | ||||
|             } else { | ||||
|                 debug!("deleted table '{}'", ktn); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub(crate) async fn init(&self) -> EyreResult<()> { | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     pub(crate) async fn terminate(&self) { | ||||
|         let inner = self.inner.lock(); | ||||
|         if !inner.opened.is_empty() { | ||||
|             panic!( | ||||
|                 "all open databases should have been closed: {:?}", | ||||
|                 inner.opened | ||||
|             ); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub(crate) fn on_table_db_drop(&self, table: String) { | ||||
|         let mut inner = self.inner.lock(); | ||||
|         if inner.opened.remove(&table).is_none() { | ||||
|             unreachable!("should have removed an item"); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn get_dbpath(&self, table: &str) -> EyreResult<PathBuf> { | ||||
|         if !table | ||||
|             .chars() | ||||
|             .all(|c| char::is_alphanumeric(c) || c == '_' || c == '-') | ||||
|         { | ||||
|             bail!("table name '{}' is invalid", table); | ||||
|         } | ||||
|         let c = self.config.get(); | ||||
|         let tablestoredir = c.table_store.directory.clone(); | ||||
|         std::fs::create_dir_all(&tablestoredir).wrap_err("failed to create tablestore path")?; | ||||
|  | ||||
|         let dbpath: PathBuf = [tablestoredir, String::from(table)].iter().collect(); | ||||
|         Ok(dbpath) | ||||
|     } | ||||
|  | ||||
|     fn get_table_name(&self, table: &str) -> EyreResult<String> { | ||||
|         if !table | ||||
|             .chars() | ||||
|             .all(|c| char::is_alphanumeric(c) || c == '_' || c == '-') | ||||
|         { | ||||
|             bail!("table name '{}' is invalid", table); | ||||
|         } | ||||
|         let c = self.config.get(); | ||||
|         let namespace = c.namespace.clone(); | ||||
|         Ok(if namespace.is_empty() { | ||||
|             table.to_string() | ||||
|         } else { | ||||
|             format!("_ns_{}_{}", namespace, table) | ||||
|         }) | ||||
|     } | ||||
|  | ||||
|     /// Get or create a TableDB database table. If the column count is greater than an | ||||
|     /// existing TableDB's column count, the database will be upgraded to add the missing columns | ||||
|     pub async fn open(&self, name: &str, column_count: u32) -> EyreResult<TableDB> { | ||||
|         let table_name = self.get_table_name(name)?; | ||||
|  | ||||
|         let mut inner = self.inner.lock(); | ||||
|         if let Some(table_db_weak_inner) = inner.opened.get(&table_name) { | ||||
|             match TableDB::try_new_from_weak_inner(table_db_weak_inner.clone()) { | ||||
|                 Some(tdb) => { | ||||
|                     return Ok(tdb); | ||||
|                 } | ||||
|                 None => { | ||||
|                     inner.opened.remove(&table_name); | ||||
|                 } | ||||
|             }; | ||||
|         } | ||||
|  | ||||
|         let dbpath = self.get_dbpath(&table_name)?; | ||||
|  | ||||
|         // Ensure permissions are correct | ||||
|         ensure_file_private_owner(&dbpath)?; | ||||
|  | ||||
|         let cfg = DatabaseConfig::with_columns(column_count); | ||||
|         let db = Database::open(&dbpath, cfg).wrap_err("failed to open tabledb")?; | ||||
|  | ||||
|         // Ensure permissions are correct | ||||
|         ensure_file_private_owner(&dbpath)?; | ||||
|  | ||||
|         trace!( | ||||
|             "opened table store '{}' at path '{:?}' with {} columns", | ||||
|             name, | ||||
|             dbpath, | ||||
|             column_count | ||||
|         ); | ||||
|         let table_db = TableDB::new(table_name.clone(), self.clone(), db); | ||||
|  | ||||
|         inner.opened.insert(table_name, table_db.weak_inner()); | ||||
|  | ||||
|         Ok(table_db) | ||||
|     } | ||||
|  | ||||
|     /// Delete a TableDB table by name | ||||
|     pub async fn delete(&self, name: &str) -> EyreResult<bool> { | ||||
|         let table_name = self.get_table_name(name)?; | ||||
|  | ||||
|         let inner = self.inner.lock(); | ||||
|         if inner.opened.contains_key(&table_name) { | ||||
|             bail!("Not deleting table that is still opened"); | ||||
|         } | ||||
|         let dbpath = self.get_dbpath(&table_name)?; | ||||
|         let ret = std::fs::remove_file(dbpath).is_ok(); | ||||
|         Ok(ret) | ||||
|     } | ||||
| } | ||||
| @@ -1,11 +1,9 @@ | ||||
| mod block_store; | ||||
| mod protected_store; | ||||
| mod system; | ||||
| mod table_store; | ||||
|  | ||||
| pub use block_store::*; | ||||
| pub use protected_store::*; | ||||
| pub use system::*; | ||||
| pub use table_store::*; | ||||
|  | ||||
| use super::*; | ||||
|   | ||||
| @@ -1,147 +0,0 @@ | ||||
| use super::*; | ||||
| use crate::intf::table_db::TableDBUnlockedInner; | ||||
| pub use crate::intf::table_db::{TableDB, TableDBTransaction}; | ||||
| use keyvaluedb_web::*; | ||||
|  | ||||
| struct TableStoreInner { | ||||
|     opened: BTreeMap<String, Weak<TableDBUnlockedInner>>, | ||||
| } | ||||
|  | ||||
| #[derive(Clone)] | ||||
| pub struct TableStore { | ||||
|     config: VeilidConfig, | ||||
|     inner: Arc<Mutex<TableStoreInner>>, | ||||
|     async_lock: Arc<AsyncMutex<()>>, | ||||
| } | ||||
|  | ||||
| impl TableStore { | ||||
|     fn new_inner() -> TableStoreInner { | ||||
|         TableStoreInner { | ||||
|             opened: BTreeMap::new(), | ||||
|         } | ||||
|     } | ||||
|     pub(crate) fn new(config: VeilidConfig) -> Self { | ||||
|         Self { | ||||
|             config, | ||||
|             inner: Arc::new(Mutex::new(Self::new_inner())), | ||||
|             async_lock: Arc::new(AsyncMutex::new(())), | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// Delete all known tables | ||||
|     pub async fn delete_all(&self) { | ||||
|         for ktn in &KNOWN_TABLE_NAMES { | ||||
|             if let Err(e) = self.delete(ktn).await { | ||||
|                 error!("failed to delete '{}': {}", ktn, e); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub(crate) async fn init(&self) -> EyreResult<()> { | ||||
|         let _async_guard = self.async_lock.lock().await; | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     pub(crate) async fn terminate(&self) { | ||||
|         let _async_guard = self.async_lock.lock().await; | ||||
|         assert!( | ||||
|             self.inner.lock().opened.len() == 0, | ||||
|             "all open databases should have been closed" | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     pub(crate) fn on_table_db_drop(&self, table: String) { | ||||
|         let mut inner = self.inner.lock(); | ||||
|         match inner.opened.remove(&table) { | ||||
|             Some(_) => (), | ||||
|             None => { | ||||
|                 assert!(false, "should have removed an item"); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn get_table_name(&self, table: &str) -> EyreResult<String> { | ||||
|         if !table | ||||
|             .chars() | ||||
|             .all(|c| char::is_alphanumeric(c) || c == '_' || c == '-') | ||||
|         { | ||||
|             bail!("table name '{}' is invalid", table); | ||||
|         } | ||||
|         let c = self.config.get(); | ||||
|         let namespace = c.namespace.clone(); | ||||
|         Ok(if namespace.len() == 0 { | ||||
|             format!("{}", table) | ||||
|         } else { | ||||
|             format!("_ns_{}_{}", namespace, table) | ||||
|         }) | ||||
|     } | ||||
|  | ||||
|     /// Get or create a TableDB database table. If the column count is greater than an | ||||
|     /// existing TableDB's column count, the database will be upgraded to add the missing columns | ||||
|     pub async fn open(&self, name: &str, column_count: u32) -> EyreResult<TableDB> { | ||||
|         let _async_guard = self.async_lock.lock().await; | ||||
|         let table_name = self.get_table_name(name)?; | ||||
|  | ||||
|         { | ||||
|             let mut inner = self.inner.lock(); | ||||
|             if let Some(table_db_weak_inner) = inner.opened.get(&table_name) { | ||||
|                 match TableDB::try_new_from_weak_inner(table_db_weak_inner.clone()) { | ||||
|                     Some(tdb) => { | ||||
|                         return Ok(tdb); | ||||
|                     } | ||||
|                     None => { | ||||
|                         inner.opened.remove(&table_name); | ||||
|                     } | ||||
|                 }; | ||||
|             } | ||||
|         } | ||||
|         let db = Database::open(table_name.clone(), column_count, false) | ||||
|             .await | ||||
|             .wrap_err("failed to open tabledb")?; | ||||
|         trace!( | ||||
|             "opened table store '{}' with table name '{:?}' with {} columns", | ||||
|             name, | ||||
|             table_name, | ||||
|             column_count | ||||
|         ); | ||||
|  | ||||
|         let table_db = TableDB::new(table_name.clone(), self.clone(), db); | ||||
|  | ||||
|         { | ||||
|             let mut inner = self.inner.lock(); | ||||
|             inner.opened.insert(table_name, table_db.weak_inner()); | ||||
|         } | ||||
|  | ||||
|         Ok(table_db) | ||||
|     } | ||||
|  | ||||
|     /// Delete a TableDB table by name | ||||
|     pub async fn delete(&self, name: &str) -> EyreResult<bool> { | ||||
|         let _async_guard = self.async_lock.lock().await; | ||||
|         trace!("TableStore::delete {}", name); | ||||
|         let table_name = self.get_table_name(name)?; | ||||
|  | ||||
|         { | ||||
|             let inner = self.inner.lock(); | ||||
|             if inner.opened.contains_key(&table_name) { | ||||
|                 trace!( | ||||
|                     "TableStore::delete {}: Not deleting, still open.", | ||||
|                     table_name | ||||
|                 ); | ||||
|                 bail!("Not deleting table that is still opened"); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if is_browser() { | ||||
|             let out = match Database::delete(table_name.clone()).await { | ||||
|                 Ok(_) => true, | ||||
|                 Err(_) => false, | ||||
|             }; | ||||
|             //.map_err(|e| format!("failed to delete tabledb at: {} ({})", table_name, e))?; | ||||
|             trace!("TableStore::deleted {}", table_name); | ||||
|             Ok(out) | ||||
|         } else { | ||||
|             unimplemented!(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -29,6 +29,7 @@ mod receipt_manager; | ||||
| mod routing_table; | ||||
| mod rpc_processor; | ||||
| mod storage_manager; | ||||
| mod table_store; | ||||
| mod veilid_api; | ||||
| mod veilid_config; | ||||
| mod veilid_layer_filter; | ||||
|   | ||||
| @@ -563,7 +563,7 @@ impl RoutingTableInner { | ||||
|                 // If we need a ping because this node hasn't seen our latest node info, then do it | ||||
|                 if let Some(own_node_info_ts) = own_node_info_ts { | ||||
|                     if !e.has_seen_our_node_info_ts(routing_domain, own_node_info_ts) { | ||||
|                         //xxx remove this when we fix | ||||
|                         //xxx remove this when we fix #208 | ||||
|                         debug!( | ||||
|                             "!has_seen_our_node_info_ts: {} own_node_info_ts={}", | ||||
|                             e.best_node_id(), | ||||
|   | ||||
							
								
								
									
										15
									
								
								veilid-core/src/table_store/mod.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								veilid-core/src/table_store/mod.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | ||||
| use super::*; | ||||
|  | ||||
| mod table_db; | ||||
| mod table_store; | ||||
| pub use table_db::*; | ||||
| pub use table_store::*; | ||||
|  | ||||
| #[cfg(target_arch = "wasm32")] | ||||
| mod wasm; | ||||
| #[cfg(target_arch = "wasm32")] | ||||
| use wasm::*; | ||||
| #[cfg(not(target_arch = "wasm32"))] | ||||
| mod native; | ||||
| #[cfg(not(target_arch = "wasm32"))] | ||||
| use native::*; | ||||
							
								
								
									
										53
									
								
								veilid-core/src/table_store/native.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								veilid-core/src/table_store/native.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,53 @@ | ||||
| use super::*; | ||||
| pub use keyvaluedb_sqlite::*; | ||||
| use std::path::PathBuf; | ||||
|  | ||||
| #[derive(Clone)] | ||||
| pub(crate) struct TableStoreDriver { | ||||
|     config: VeilidConfig, | ||||
| } | ||||
|  | ||||
| impl TableStoreDriver { | ||||
|     pub fn new(config: VeilidConfig) -> Self { | ||||
|         Self { config } | ||||
|     } | ||||
|  | ||||
|     fn get_dbpath(&self, table: &str) -> VeilidAPIResult<PathBuf> { | ||||
|         let c = self.config.get(); | ||||
|         let tablestoredir = c.table_store.directory.clone(); | ||||
|         std::fs::create_dir_all(&tablestoredir).map_err(VeilidAPIError::from)?; | ||||
|  | ||||
|         let dbpath: PathBuf = [tablestoredir, String::from(table)].iter().collect(); | ||||
|         Ok(dbpath) | ||||
|     } | ||||
|  | ||||
|     pub async fn open(&self, table_name: &str, column_count: u32) -> VeilidAPIResult<Database> { | ||||
|         let dbpath = self.get_dbpath(&table_name)?; | ||||
|  | ||||
|         // Ensure permissions are correct | ||||
|         ensure_file_private_owner(&dbpath).map_err(VeilidAPIError::internal)?; | ||||
|  | ||||
|         let cfg = DatabaseConfig::with_columns(column_count); | ||||
|         let db = Database::open(&dbpath, cfg).map_err(VeilidAPIError::from)?; | ||||
|  | ||||
|         // Ensure permissions are correct | ||||
|         ensure_file_private_owner(&dbpath).map_err(VeilidAPIError::internal)?; | ||||
|  | ||||
|         trace!( | ||||
|             "opened table store '{}' at path '{:?}' with {} columns", | ||||
|             table_name, | ||||
|             dbpath, | ||||
|             column_count | ||||
|         ); | ||||
|         Ok(db) | ||||
|     } | ||||
|  | ||||
|     pub async fn delete(&self, table_name: &str) -> VeilidAPIResult<bool> { | ||||
|         let dbpath = self.get_dbpath(&table_name)?; | ||||
|         if !dbpath.exists() { | ||||
|             return Ok(false); | ||||
|         } | ||||
|         std::fs::remove_file(dbpath).map_err(VeilidAPIError::from)?; | ||||
|         Ok(true) | ||||
|     } | ||||
| } | ||||
| @@ -14,6 +14,7 @@ pub struct TableDBUnlockedInner { | ||||
|     table: String, | ||||
|     table_store: TableStore, | ||||
|     database: Database, | ||||
|     encryption_key: Option<TypedSharedSecret>, | ||||
| } | ||||
| 
 | ||||
| impl fmt::Debug for TableDBUnlockedInner { | ||||
| @@ -34,12 +35,18 @@ pub struct TableDB { | ||||
| } | ||||
| 
 | ||||
| impl TableDB { | ||||
|     pub(super) fn new(table: String, table_store: TableStore, database: Database) -> Self { | ||||
|     pub(super) fn new( | ||||
|         table: String, | ||||
|         table_store: TableStore, | ||||
|         database: Database, | ||||
|         encryption_key: Option<TypedSharedSecret>, | ||||
|     ) -> Self { | ||||
|         Self { | ||||
|             unlocked_inner: Arc::new(TableDBUnlockedInner { | ||||
|                 table, | ||||
|                 table_store, | ||||
|                 database, | ||||
|                 encryption_key, | ||||
|             }), | ||||
|         } | ||||
|     } | ||||
							
								
								
									
										305
									
								
								veilid-core/src/table_store/table_store.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										305
									
								
								veilid-core/src/table_store/table_store.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,305 @@ | ||||
| use super::*; | ||||
| use keyvaluedb::*; | ||||
|  | ||||
| struct TableStoreInner { | ||||
|     opened: BTreeMap<String, Weak<TableDBUnlockedInner>>, | ||||
|     encryption_key: Option<TypedSharedSecret>, | ||||
|     all_table_names: HashMap<String, String>, | ||||
|     all_tables_db: Option<Database>, | ||||
| } | ||||
|  | ||||
| /// Veilid Table Storage | ||||
| /// Database for storing key value pairs persistently and securely across runs | ||||
| #[derive(Clone)] | ||||
| pub struct TableStore { | ||||
|     config: VeilidConfig, | ||||
|     protected_store: ProtectedStore, | ||||
|     table_store_driver: TableStoreDriver, | ||||
|     inner: Arc<Mutex<TableStoreInner>>, // Sync mutex here because TableDB drops can happen at any time | ||||
|     async_lock: Arc<AsyncMutex<()>>,    // Async mutex for operations | ||||
| } | ||||
|  | ||||
| impl TableStore { | ||||
|     fn new_inner() -> TableStoreInner { | ||||
|         TableStoreInner { | ||||
|             opened: BTreeMap::new(), | ||||
|             encryption_key: None, | ||||
|             all_table_names: HashMap::new(), | ||||
|             all_tables_db: None, | ||||
|         } | ||||
|     } | ||||
|     pub(crate) fn new(config: VeilidConfig, protected_store: ProtectedStore) -> Self { | ||||
|         let inner = Self::new_inner(); | ||||
|         let table_store_driver = TableStoreDriver::new(config.clone()); | ||||
|  | ||||
|         Self { | ||||
|             config, | ||||
|             protected_store, | ||||
|             inner: Arc::new(Mutex::new(inner)), | ||||
|             table_store_driver, | ||||
|             async_lock: Arc::new(AsyncMutex::new(())), | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Flush internal control state | ||||
|     async fn flush(&self) { | ||||
|         let (all_table_names_value, all_tables_db) = { | ||||
|             let inner = self.inner.lock(); | ||||
|             let all_table_names_value = | ||||
|                 to_rkyv(&inner.all_table_names).expect("failed to archive all_table_names"); | ||||
|             (all_table_names_value, inner.all_tables_db.clone().unwrap()) | ||||
|         }; | ||||
|         let mut dbt = DBTransaction::new(); | ||||
|         dbt.put(0, b"all_table_names", &all_table_names_value); | ||||
|         if let Err(e) = all_tables_db.write(dbt).await { | ||||
|             error!("failed to write all tables db: {}", e); | ||||
|         } | ||||
|     } xxx must from_rkyv the all_table_names | ||||
|  | ||||
|     // Internal naming support | ||||
|     // Adds rename capability and ensures names of tables are totally unique and valid | ||||
|  | ||||
|     fn namespaced_name(&self, table: &str) -> VeilidAPIResult<String> { | ||||
|         if !table | ||||
|             .chars() | ||||
|             .all(|c| char::is_alphanumeric(c) || c == '_' || c == '-') | ||||
|         { | ||||
|             apibail_invalid_argument!("table name is invalid", "table", table); | ||||
|         } | ||||
|         let c = self.config.get(); | ||||
|         let namespace = c.namespace.clone(); | ||||
|         Ok(if namespace.is_empty() { | ||||
|             table.to_string() | ||||
|         } else { | ||||
|             format!("_ns_{}_{}", namespace, table) | ||||
|         }) | ||||
|     } | ||||
|  | ||||
|     async fn name_get_or_create(&self, table: &str) -> VeilidAPIResult<String> { | ||||
|         let name = self.namespaced_name(table)?; | ||||
|  | ||||
|         let mut inner = self.inner.lock(); | ||||
|         // Do we have this name yet? | ||||
|         if let Some(real_name) = inner.all_table_names.get(&name) { | ||||
|             return Ok(real_name.clone()); | ||||
|         } | ||||
|  | ||||
|         // If not, make a new low level name mapping | ||||
|         let mut real_name_bytes = [0u8; 32]; | ||||
|         random_bytes(&mut real_name_bytes); | ||||
|         let real_name = data_encoding::BASE64URL_NOPAD.encode(&real_name_bytes); | ||||
|  | ||||
|         if inner | ||||
|             .all_table_names | ||||
|             .insert(name.to_owned(), real_name.clone()) | ||||
|             .is_some() | ||||
|         { | ||||
|             panic!("should not have had some value"); | ||||
|         }; | ||||
|  | ||||
|         Ok(real_name) | ||||
|     } | ||||
|  | ||||
|     async fn name_delete(&self, table: &str) -> VeilidAPIResult<Option<String>> { | ||||
|         let name = self.namespaced_name(table)?; | ||||
|         let mut inner = self.inner.lock(); | ||||
|         let real_name = inner.all_table_names.remove(&name); | ||||
|         Ok(real_name) | ||||
|     } | ||||
|  | ||||
|     async fn name_get(&self, table: &str) -> VeilidAPIResult<Option<String>> { | ||||
|         let name = self.namespaced_name(table)?; | ||||
|         let inner = self.inner.lock(); | ||||
|         let real_name = inner.all_table_names.get(&name).cloned(); | ||||
|         Ok(real_name) | ||||
|     } | ||||
|  | ||||
|     async fn name_rename(&self, old_table: &str, new_table: &str) -> VeilidAPIResult<()> { | ||||
|         let old_name = self.namespaced_name(old_table)?; | ||||
|         let new_name = self.namespaced_name(new_table)?; | ||||
|  | ||||
|         let mut inner = self.inner.lock(); | ||||
|         // Ensure new name doesn't exist | ||||
|         if inner.all_table_names.contains_key(&new_name) { | ||||
|             return Err(VeilidAPIError::generic("new table already exists")); | ||||
|         } | ||||
|         // Do we have this name yet? | ||||
|         let Some(real_name) = inner.all_table_names.remove(&old_name) else { | ||||
|             return Err(VeilidAPIError::generic("table does not exist")); | ||||
|         }; | ||||
|         // Insert with new name | ||||
|         inner.all_table_names.insert(new_name.to_owned(), real_name); | ||||
|  | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     /// Delete all known tables | ||||
|     async fn delete_all(&self) { | ||||
|         // Get all tables | ||||
|         let real_names = { | ||||
|             let mut inner = self.inner.lock(); | ||||
|             let real_names = inner | ||||
|                 .all_table_names | ||||
|                 .values() | ||||
|                 .cloned() | ||||
|                 .collect::<Vec<String>>(); | ||||
|             inner.all_table_names.clear(); | ||||
|             real_names | ||||
|         }; | ||||
|  | ||||
|         // Delete all tables | ||||
|         for table_name in real_names { | ||||
|             if let Err(e) = self.table_store_driver.delete(&table_name).await { | ||||
|                 error!("error deleting table: {}", e); | ||||
|             } | ||||
|         } | ||||
|         self.flush().await; | ||||
|     } | ||||
|  | ||||
|     pub(crate) async fn init(&self) -> EyreResult<()> { | ||||
|         let _async_guard = self.async_lock.lock().await; | ||||
|  | ||||
|         let encryption_key: Option<TypedSharedSecret> = self | ||||
|             .protected_store | ||||
|             .load_user_secret_rkyv("device_encryption_key") | ||||
|             .await?; | ||||
|  | ||||
|         let all_tables_db = self | ||||
|             .table_store_driver | ||||
|             .open("__veilid_all_tables", 1) | ||||
|             .await | ||||
|             .wrap_err("failed to create all tables table")?; | ||||
|  | ||||
|         { | ||||
|             let mut inner = self.inner.lock(); | ||||
|             inner.encryption_key = encryption_key; | ||||
|             inner.all_tables_db = Some(all_tables_db); | ||||
|         } | ||||
|  | ||||
|         let do_delete = { | ||||
|             let c = self.config.get(); | ||||
|             c.table_store.delete | ||||
|         }; | ||||
|  | ||||
|         if do_delete { | ||||
|             self.delete_all().await; | ||||
|         } | ||||
|  | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     pub(crate) async fn terminate(&self) { | ||||
|         let _async_guard = self.async_lock.lock().await; | ||||
|         let mut inner = self.inner.lock(); | ||||
|         if !inner.opened.is_empty() { | ||||
|             panic!( | ||||
|                 "all open databases should have been closed: {:?}", | ||||
|                 inner.opened | ||||
|             ); | ||||
|         } | ||||
|         inner.all_tables_db = None; | ||||
|         inner.encryption_key = None; | ||||
|     } | ||||
|  | ||||
|     pub(crate) fn on_table_db_drop(&self, table: String) { | ||||
|         let mut inner = self.inner.lock(); | ||||
|         if inner.opened.remove(&table).is_none() { | ||||
|             unreachable!("should have removed an item"); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// Get or create a TableDB database table. If the column count is greater than an | ||||
|     /// existing TableDB's column count, the database will be upgraded to add the missing columns | ||||
|     pub async fn open(&self, name: &str, column_count: u32) -> VeilidAPIResult<TableDB> { | ||||
|         let _async_guard = self.async_lock.lock().await; | ||||
|         let table_name = self.name_get_or_create(name).await?; | ||||
|  | ||||
|         // See if this table is already opened | ||||
|         { | ||||
|             let mut inner = self.inner.lock(); | ||||
|             if let Some(table_db_weak_inner) = inner.opened.get(&table_name) { | ||||
|                 match TableDB::try_new_from_weak_inner(table_db_weak_inner.clone()) { | ||||
|                     Some(tdb) => { | ||||
|                         return Ok(tdb); | ||||
|                     } | ||||
|                     None => { | ||||
|                         inner.opened.remove(&table_name); | ||||
|                     } | ||||
|                 }; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // Open table db using platform-specific driver | ||||
|         let db = match self | ||||
|             .table_store_driver | ||||
|             .open(&table_name, column_count) | ||||
|             .await | ||||
|         { | ||||
|             Ok(db) => db, | ||||
|             Err(e) => { | ||||
|                 self.name_delete(name).await.expect("cleanup failed"); | ||||
|                 self.flush().await; | ||||
|                 return Err(e); | ||||
|             } | ||||
|         }; | ||||
|  | ||||
|         // Flush table names to disk | ||||
|         self.flush().await; | ||||
|  | ||||
|         // Wrap low-level Database in TableDB object | ||||
|         let mut inner = self.inner.lock(); | ||||
|         let table_db = TableDB::new( | ||||
|             table_name.clone(), | ||||
|             self.clone(), | ||||
|             db, | ||||
|             inner.encryption_key.clone(), | ||||
|         ); | ||||
|  | ||||
|         // Keep track of opened DBs | ||||
|         inner | ||||
|             .opened | ||||
|             .insert(table_name.clone(), table_db.weak_inner()); | ||||
|  | ||||
|         Ok(table_db) | ||||
|     } | ||||
|  | ||||
|     /// Delete a TableDB table by name | ||||
|     pub async fn delete(&self, name: &str) -> VeilidAPIResult<bool> { | ||||
|         let _async_guard = self.async_lock.lock().await; | ||||
|         let Some(table_name) = self.name_get(name).await? else { | ||||
|             // Did not exist in name table | ||||
|             return Ok(false); | ||||
|         }; | ||||
|  | ||||
|         // See if this table is opened | ||||
|         { | ||||
|             let inner = self.inner.lock(); | ||||
|             if inner.opened.contains_key(&table_name) { | ||||
|                 apibail_generic!("Not deleting table that is still opened"); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // Delete table db using platform-specific driver | ||||
|         let deleted = self.table_store_driver.delete(&table_name).await?; | ||||
|         if !deleted { | ||||
|             // Table missing? Just remove name | ||||
|             self.name_delete(&name) | ||||
|                 .await | ||||
|                 .expect("failed to delete name"); | ||||
|             warn!( | ||||
|                 "table existed in name table but not in storage: {} : {}", | ||||
|                 name, table_name | ||||
|             ); | ||||
|             return Ok(false); | ||||
|         } | ||||
|  | ||||
|         Ok(true) | ||||
|     } | ||||
|  | ||||
|     /// Rename a TableDB table | ||||
|     pub async fn rename(&self, old_name: &str, new_name: &str) -> VeilidAPIResult<()> { | ||||
|         let _async_guard = self.async_lock.lock().await; | ||||
|         trace!("TableStore::rename {} -> {}", old_name, new_name); | ||||
|         self.name_rename(old_name, new_name).await | ||||
|     } | ||||
| } | ||||
							
								
								
									
										40
									
								
								veilid-core/src/table_store/wasm.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								veilid-core/src/table_store/wasm.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,40 @@ | ||||
| use super::*; | ||||
| pub use keyvaluedb_web::*; | ||||
|  | ||||
| #[derive(Clone)] | ||||
| pub struct TableStoreDriver { | ||||
|     _config: VeilidConfig, | ||||
| } | ||||
|  | ||||
| impl TableStoreDriver { | ||||
|     pub(crate) fn new(config: VeilidConfig) -> Self { | ||||
|         Self { _config: config } | ||||
|     } | ||||
|  | ||||
|     pub async fn open(&self, table_name: &str, column_count: u32) -> VeilidAPIResult<Database> { | ||||
|         let db = Database::open(table_name, column_count, false) | ||||
|             .await | ||||
|             .map_err(VeilidAPIError::generic)?; | ||||
|         trace!( | ||||
|             "opened table store '{}' with {} columns", | ||||
|             table_name, | ||||
|             column_count | ||||
|         ); | ||||
|         Ok(db) | ||||
|     } | ||||
|  | ||||
|     /// Delete a TableDB table by name | ||||
|     pub async fn delete(&self, table_name: &str) -> VeilidAPIResult<bool> { | ||||
|         if is_browser() { | ||||
|             let out = Database::delete(table_name).await.is_ok(); | ||||
|             if out { | ||||
|                 trace!("TableStore::delete {} deleted", table_name); | ||||
|             } else { | ||||
|                 debug!("TableStore::delete {} not deleted", table_name); | ||||
|             } | ||||
|             Ok(out) | ||||
|         } else { | ||||
|             unimplemented!(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -20,9 +20,9 @@ pub use core::str::FromStr; | ||||
| pub use crypto::*; | ||||
| pub use intf::BlockStore; | ||||
| pub use intf::ProtectedStore; | ||||
| pub use intf::{TableDB, TableDBTransaction, TableStore}; | ||||
| pub use network_manager::NetworkManager; | ||||
| pub use routing_table::{NodeRef, NodeRefBase}; | ||||
| pub use table_store::{TableDB, TableDBTransaction, TableStore}; | ||||
|  | ||||
| use crate::*; | ||||
| use core::fmt; | ||||
|   | ||||
| @@ -16,13 +16,12 @@ impl RngCore for VeilidRng { | ||||
|     } | ||||
|  | ||||
|     fn fill_bytes(&mut self, dest: &mut [u8]) { | ||||
|         if let Err(e) = self.try_fill_bytes(dest) { | ||||
|             panic!("Error: {}", e); | ||||
|         } | ||||
|         random_bytes(dest); | ||||
|     } | ||||
|  | ||||
|     fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand::Error> { | ||||
|         random_bytes(dest).map_err(rand::Error::new) | ||||
|         random_bytes(dest); | ||||
|         Ok(()) | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -30,7 +29,7 @@ cfg_if! { | ||||
|     if #[cfg(target_arch = "wasm32")] { | ||||
|         use js_sys::Math; | ||||
|  | ||||
|         pub fn random_bytes(dest: &mut [u8]) -> EyreResult<()> { | ||||
|         pub fn random_bytes(dest: &mut [u8]) { | ||||
|             let len = dest.len(); | ||||
|             let u32len = len / 4; | ||||
|             let remlen = len % 4; | ||||
| @@ -49,8 +48,6 @@ cfg_if! { | ||||
|                     dest[u32len * 4 + n] = ((r >> (n * 8)) & 0xFF) as u8; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             Ok(()) | ||||
|         } | ||||
|  | ||||
|         pub fn get_random_u32() -> u32 { | ||||
| @@ -65,9 +62,9 @@ cfg_if! { | ||||
|  | ||||
|     } else { | ||||
|  | ||||
|         pub fn random_bytes(dest: &mut [u8]) -> EyreResult<()> { | ||||
|         pub fn random_bytes(dest: &mut [u8]) { | ||||
|             let mut rng = rand::thread_rng(); | ||||
|             rng.try_fill_bytes(dest).wrap_err("failed to fill bytes") | ||||
|             rng.fill_bytes(dest); | ||||
|         } | ||||
|  | ||||
|         pub fn get_random_u32() -> u32 { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user