checkpoint
This commit is contained in:
		@@ -78,7 +78,7 @@ impl ServicesContext {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        // Set up tablestore
 | 
					        // Set up tablestore
 | 
				
			||||||
        trace!("init table store");
 | 
					        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 {
 | 
					        if let Err(e) = table_store.init().await {
 | 
				
			||||||
            error!("failed to init table store: {}", e);
 | 
					            error!("failed to init table store: {}", e);
 | 
				
			||||||
            self.shutdown().await;
 | 
					            self.shutdown().await;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -169,7 +169,10 @@ impl Crypto {
 | 
				
			|||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // load caches if they are valid for this node id
 | 
					        // 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? {
 | 
					        let caches_valid = match db.load(0, b"cache_validity_key").await? {
 | 
				
			||||||
            Some(v) => v == cache_validity_key,
 | 
					            Some(v) => v == cache_validity_key,
 | 
				
			||||||
            None => false,
 | 
					            None => false,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -84,7 +84,7 @@ impl CryptoSystem for CryptoSystemNONE {
 | 
				
			|||||||
    fn random_bytes(&self, len: u32) -> Vec<u8> {
 | 
					    fn random_bytes(&self, len: u32) -> Vec<u8> {
 | 
				
			||||||
        let mut bytes = Vec::<u8>::with_capacity(len as usize);
 | 
					        let mut bytes = Vec::<u8>::with_capacity(len as usize);
 | 
				
			||||||
        bytes.resize(len as usize, 0u8);
 | 
					        bytes.resize(len as usize, 0u8);
 | 
				
			||||||
        random_bytes(bytes.as_mut()).unwrap();
 | 
					        random_bytes(bytes.as_mut());
 | 
				
			||||||
        bytes
 | 
					        bytes
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    fn default_salt_length(&self) -> u32 {
 | 
					    fn default_salt_length(&self) -> u32 {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -53,8 +53,10 @@ pub type TypedKey = CryptoTyped<PublicKey>;
 | 
				
			|||||||
pub type TypedSecret = CryptoTyped<SecretKey>;
 | 
					pub type TypedSecret = CryptoTyped<SecretKey>;
 | 
				
			||||||
pub type TypedKeyPair = CryptoTyped<KeyPair>;
 | 
					pub type TypedKeyPair = CryptoTyped<KeyPair>;
 | 
				
			||||||
pub type TypedSignature = CryptoTyped<Signature>;
 | 
					pub type TypedSignature = CryptoTyped<Signature>;
 | 
				
			||||||
 | 
					pub type TypedSharedSecret = CryptoTyped<SharedSecret>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub type TypedKeySet = CryptoTypedSet<PublicKey>;
 | 
					pub type TypedKeySet = CryptoTypedSet<PublicKey>;
 | 
				
			||||||
pub type TypedSecretSet = CryptoTypedSet<SecretKey>;
 | 
					pub type TypedSecretSet = CryptoTypedSet<SecretKey>;
 | 
				
			||||||
pub type TypedKeyPairSet = CryptoTypedSet<KeyPair>;
 | 
					pub type TypedKeyPairSet = CryptoTypedSet<KeyPair>;
 | 
				
			||||||
pub type TypedSignatureSet = CryptoTypedSet<Signature>;
 | 
					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> {
 | 
					    fn random_bytes(&self, len: u32) -> Vec<u8> {
 | 
				
			||||||
        let mut bytes = Vec::<u8>::with_capacity(len as usize);
 | 
					        let mut bytes = Vec::<u8>::with_capacity(len as usize);
 | 
				
			||||||
        bytes.resize(len as usize, 0u8);
 | 
					        bytes.resize(len as usize, 0u8);
 | 
				
			||||||
        random_bytes(bytes.as_mut()).unwrap();
 | 
					        random_bytes(bytes.as_mut());
 | 
				
			||||||
        bytes
 | 
					        bytes
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    fn default_salt_length(&self) -> u32 {
 | 
					    fn default_salt_length(&self) -> u32 {
 | 
				
			||||||
@@ -134,12 +134,12 @@ impl CryptoSystem for CryptoSystemVLD0 {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    fn random_nonce(&self) -> Nonce {
 | 
					    fn random_nonce(&self) -> Nonce {
 | 
				
			||||||
        let mut nonce = [0u8; NONCE_LENGTH];
 | 
					        let mut nonce = [0u8; NONCE_LENGTH];
 | 
				
			||||||
        random_bytes(&mut nonce).unwrap();
 | 
					        random_bytes(&mut nonce);
 | 
				
			||||||
        Nonce::new(nonce)
 | 
					        Nonce::new(nonce)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    fn random_shared_secret(&self) -> SharedSecret {
 | 
					    fn random_shared_secret(&self) -> SharedSecret {
 | 
				
			||||||
        let mut s = [0u8; SHARED_SECRET_LENGTH];
 | 
					        let mut s = [0u8; SHARED_SECRET_LENGTH];
 | 
				
			||||||
        random_bytes(&mut s).unwrap();
 | 
					        random_bytes(&mut s);
 | 
				
			||||||
        SharedSecret::new(s)
 | 
					        SharedSecret::new(s)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    fn compute_dh(
 | 
					    fn compute_dh(
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,3 @@
 | 
				
			|||||||
mod table_db;
 | 
					 | 
				
			||||||
use super::*;
 | 
					use super::*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[cfg(target_arch = "wasm32")]
 | 
					#[cfg(target_arch = "wasm32")]
 | 
				
			||||||
@@ -10,15 +9,5 @@ mod native;
 | 
				
			|||||||
#[cfg(not(target_arch = "wasm32"))]
 | 
					#[cfg(not(target_arch = "wasm32"))]
 | 
				
			||||||
pub use native::*;
 | 
					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] =
 | 
					pub static KNOWN_PROTECTED_STORE_KEYS: [&'static str; 4] =
 | 
				
			||||||
    ["node_id", "node_id_secret", "_test_key", "RouteSpecStore"];
 | 
					    ["node_id", "node_id_secret", "_test_key", "RouteSpecStore"];
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,12 +1,10 @@
 | 
				
			|||||||
mod block_store;
 | 
					mod block_store;
 | 
				
			||||||
mod protected_store;
 | 
					mod protected_store;
 | 
				
			||||||
mod system;
 | 
					mod system;
 | 
				
			||||||
mod table_store;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub use block_store::*;
 | 
					pub use block_store::*;
 | 
				
			||||||
pub use protected_store::*;
 | 
					pub use protected_store::*;
 | 
				
			||||||
pub use system::*;
 | 
					pub use system::*;
 | 
				
			||||||
pub use table_store::*;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[cfg(target_os = "android")]
 | 
					#[cfg(target_os = "android")]
 | 
				
			||||||
pub mod 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 block_store;
 | 
				
			||||||
mod protected_store;
 | 
					mod protected_store;
 | 
				
			||||||
mod system;
 | 
					mod system;
 | 
				
			||||||
mod table_store;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub use block_store::*;
 | 
					pub use block_store::*;
 | 
				
			||||||
pub use protected_store::*;
 | 
					pub use protected_store::*;
 | 
				
			||||||
pub use system::*;
 | 
					pub use system::*;
 | 
				
			||||||
pub use table_store::*;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
use super::*;
 | 
					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 routing_table;
 | 
				
			||||||
mod rpc_processor;
 | 
					mod rpc_processor;
 | 
				
			||||||
mod storage_manager;
 | 
					mod storage_manager;
 | 
				
			||||||
 | 
					mod table_store;
 | 
				
			||||||
mod veilid_api;
 | 
					mod veilid_api;
 | 
				
			||||||
mod veilid_config;
 | 
					mod veilid_config;
 | 
				
			||||||
mod veilid_layer_filter;
 | 
					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 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 let Some(own_node_info_ts) = own_node_info_ts {
 | 
				
			||||||
                    if !e.has_seen_our_node_info_ts(routing_domain, 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!(
 | 
					                        debug!(
 | 
				
			||||||
                            "!has_seen_our_node_info_ts: {} own_node_info_ts={}",
 | 
					                            "!has_seen_our_node_info_ts: {} own_node_info_ts={}",
 | 
				
			||||||
                            e.best_node_id(),
 | 
					                            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: String,
 | 
				
			||||||
    table_store: TableStore,
 | 
					    table_store: TableStore,
 | 
				
			||||||
    database: Database,
 | 
					    database: Database,
 | 
				
			||||||
 | 
					    encryption_key: Option<TypedSharedSecret>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl fmt::Debug for TableDBUnlockedInner {
 | 
					impl fmt::Debug for TableDBUnlockedInner {
 | 
				
			||||||
@@ -34,12 +35,18 @@ pub struct TableDB {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl 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 {
 | 
					        Self {
 | 
				
			||||||
            unlocked_inner: Arc::new(TableDBUnlockedInner {
 | 
					            unlocked_inner: Arc::new(TableDBUnlockedInner {
 | 
				
			||||||
                table,
 | 
					                table,
 | 
				
			||||||
                table_store,
 | 
					                table_store,
 | 
				
			||||||
                database,
 | 
					                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 crypto::*;
 | 
				
			||||||
pub use intf::BlockStore;
 | 
					pub use intf::BlockStore;
 | 
				
			||||||
pub use intf::ProtectedStore;
 | 
					pub use intf::ProtectedStore;
 | 
				
			||||||
pub use intf::{TableDB, TableDBTransaction, TableStore};
 | 
					 | 
				
			||||||
pub use network_manager::NetworkManager;
 | 
					pub use network_manager::NetworkManager;
 | 
				
			||||||
pub use routing_table::{NodeRef, NodeRefBase};
 | 
					pub use routing_table::{NodeRef, NodeRefBase};
 | 
				
			||||||
 | 
					pub use table_store::{TableDB, TableDBTransaction, TableStore};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::*;
 | 
					use crate::*;
 | 
				
			||||||
use core::fmt;
 | 
					use core::fmt;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -16,13 +16,12 @@ impl RngCore for VeilidRng {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn fill_bytes(&mut self, dest: &mut [u8]) {
 | 
					    fn fill_bytes(&mut self, dest: &mut [u8]) {
 | 
				
			||||||
        if let Err(e) = self.try_fill_bytes(dest) {
 | 
					        random_bytes(dest);
 | 
				
			||||||
            panic!("Error: {}", e);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand::Error> {
 | 
					    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")] {
 | 
					    if #[cfg(target_arch = "wasm32")] {
 | 
				
			||||||
        use js_sys::Math;
 | 
					        use js_sys::Math;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        pub fn random_bytes(dest: &mut [u8]) -> EyreResult<()> {
 | 
					        pub fn random_bytes(dest: &mut [u8]) {
 | 
				
			||||||
            let len = dest.len();
 | 
					            let len = dest.len();
 | 
				
			||||||
            let u32len = len / 4;
 | 
					            let u32len = len / 4;
 | 
				
			||||||
            let remlen = len % 4;
 | 
					            let remlen = len % 4;
 | 
				
			||||||
@@ -49,8 +48,6 @@ cfg_if! {
 | 
				
			|||||||
                    dest[u32len * 4 + n] = ((r >> (n * 8)) & 0xFF) as u8;
 | 
					                    dest[u32len * 4 + n] = ((r >> (n * 8)) & 0xFF) as u8;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					 | 
				
			||||||
            Ok(())
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        pub fn get_random_u32() -> u32 {
 | 
					        pub fn get_random_u32() -> u32 {
 | 
				
			||||||
@@ -65,9 +62,9 @@ cfg_if! {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        pub fn random_bytes(dest: &mut [u8]) -> EyreResult<()> {
 | 
					        pub fn random_bytes(dest: &mut [u8]) {
 | 
				
			||||||
            let mut rng = rand::thread_rng();
 | 
					            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 {
 | 
					        pub fn get_random_u32() -> u32 {
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user