From fe2edd56a8b1fbd73bf81d647b833c2409faf01b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Stuczy=C5=84ski?= Date: Tue, 5 Mar 2024 15:39:56 +0000 Subject: [PATCH] adjusting the trait --- .../client-core/gateways-storage/Cargo.toml | 1 + .../src/backend/fs_backend/error.rs | 38 +++++++++ .../src/backend/fs_backend/manager.rs | 53 +++++++++++++ .../src/backend/fs_backend/mod.rs | 46 +++++++++++ .../src/backend/fs_backend/models.rs | 2 + .../src/backend/mem_backend.rs | 69 ++++++++++++++++ .../gateways-storage/src/backend/mod.rs | 7 ++ .../client-core/gateways-storage/src/error.rs | 2 +- .../client-core/gateways-storage/src/lib.rs | 28 +++++-- .../src/{models.rs => types.rs} | 78 +++++++++++++++---- .../src/backend/fs_backend/error.rs | 2 +- 11 files changed, 300 insertions(+), 26 deletions(-) create mode 100644 common/client-core/gateways-storage/src/backend/fs_backend/error.rs create mode 100644 common/client-core/gateways-storage/src/backend/fs_backend/manager.rs create mode 100644 common/client-core/gateways-storage/src/backend/fs_backend/mod.rs create mode 100644 common/client-core/gateways-storage/src/backend/fs_backend/models.rs create mode 100644 common/client-core/gateways-storage/src/backend/mem_backend.rs create mode 100644 common/client-core/gateways-storage/src/backend/mod.rs rename common/client-core/gateways-storage/src/{models.rs => types.rs} (72%) diff --git a/common/client-core/gateways-storage/Cargo.toml b/common/client-core/gateways-storage/Cargo.toml index a4c3f44fa4..3968df079e 100644 --- a/common/client-core/gateways-storage/Cargo.toml +++ b/common/client-core/gateways-storage/Cargo.toml @@ -12,6 +12,7 @@ log.workspace = true serde = { workspace = true, features = ["derive"] } thiserror.workspace = true time.workspace = true +tokio = { workspace = true, features = ["sync"] } url.workspace = true zeroize = { workspace = true, features = ["zeroize_derive"] } diff --git a/common/client-core/gateways-storage/src/backend/fs_backend/error.rs b/common/client-core/gateways-storage/src/backend/fs_backend/error.rs new file mode 100644 index 0000000000..0a39dbbe06 --- /dev/null +++ b/common/client-core/gateways-storage/src/backend/fs_backend/error.rs @@ -0,0 +1,38 @@ +// Copyright 2024 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use std::io; +use std::path::PathBuf; +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum StorageError { + #[error("the provided database path doesn't have a filename defined")] + DatabasePathWithoutFilename { provided_path: PathBuf }, + + #[error("unable to create the directory for the database at {}: {source}", provided_path.display())] + DatabasePathUnableToCreateParentDirectory { + provided_path: PathBuf, + source: io::Error, + }, + + #[error("failed to perform sqlx migration: {source}")] + MigrationError { + #[source] + #[from] + source: sqlx::migrate::MigrateError, + }, + + #[error("failed to connect to the underlying connection pool: {source}")] + DatabaseConnectionError { + #[source] + source: sqlx::error::Error, + }, + + #[error("failed to run the SQL query: {source}")] + QueryError { + #[source] + #[from] + source: sqlx::error::Error, + }, +} diff --git a/common/client-core/gateways-storage/src/backend/fs_backend/manager.rs b/common/client-core/gateways-storage/src/backend/fs_backend/manager.rs new file mode 100644 index 0000000000..299b16ea8b --- /dev/null +++ b/common/client-core/gateways-storage/src/backend/fs_backend/manager.rs @@ -0,0 +1,53 @@ +// Copyright 2024 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::backend::fs_backend::error::StorageError; +use crate::backend::fs_backend::models::{ + ReplySurbStorageMetadata, StoredReplyKey, StoredReplySurb, StoredSenderTag, StoredSurbSender, +}; +use log::{error, info}; +use sqlx::ConnectOptions; +use std::path::Path; + +#[derive(Debug, Clone)] +pub struct StorageManager { + pub connection_pool: sqlx::SqlitePool, +} + +// all SQL goes here +impl StorageManager { + pub async fn init>(database_path: P, fresh: bool) -> Result { + // ensure the whole directory structure exists + if let Some(parent_dir) = database_path.as_ref().parent() { + std::fs::create_dir_all(parent_dir).map_err(|source| { + StorageError::DatabasePathUnableToCreateParentDirectory { + provided_path: database_path.as_ref().to_path_buf(), + source, + } + })?; + } + + let mut opts = sqlx::sqlite::SqliteConnectOptions::new() + .filename(database_path) + .create_if_missing(fresh); + + opts.disable_statement_logging(); + + let connection_pool = sqlx::SqlitePool::connect_with(opts) + .await + .map_err(|source| { + error!("Failed to connect to SQLx database: {err}"); + StorageError::DatabaseConnectionError { source } + })?; + + sqlx::migrate!("./fs_gateways_migrations") + .run(&connection_pool) + .await + .inspect_err(|err| { + error!("Failed to initialize SQLx database: {err}"); + })?; + + info!("Database migration finished!"); + Ok(StorageManager { connection_pool }) + } +} diff --git a/common/client-core/gateways-storage/src/backend/fs_backend/mod.rs b/common/client-core/gateways-storage/src/backend/fs_backend/mod.rs new file mode 100644 index 0000000000..e8ddc05d83 --- /dev/null +++ b/common/client-core/gateways-storage/src/backend/fs_backend/mod.rs @@ -0,0 +1,46 @@ +// Copyright 2024 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::types::GatewayDetails; +use crate::GatewayDetailsStore; +use async_trait::async_trait; +use manager::StorageManager; + +mod error; +mod manager; +mod models; + +pub struct OnDiskGatewayDetails { + manager: StorageManager, +} + +#[async_trait] +impl GatewayDetailsStore for OnDiskGatewayDetails { + type StorageError = error::StorageError; + + async fn active_gateway(&self) -> Result, Self::StorageError> { + todo!() + } + + async fn all_gateways(&self) -> Result, Self::StorageError> { + todo!() + } + + async fn load_gateway_details( + &self, + gateway_id: &str, + ) -> Result { + todo!() + } + + async fn store_gateway_details( + &self, + details: GatewayDetails, + ) -> Result<(), Self::StorageError> { + todo!() + } + + async fn remove_gateway_details(&self, gateway_id: &str) -> Result<(), Self::StorageError> { + todo!() + } +} diff --git a/common/client-core/gateways-storage/src/backend/fs_backend/models.rs b/common/client-core/gateways-storage/src/backend/fs_backend/models.rs new file mode 100644 index 0000000000..755fb6cc8b --- /dev/null +++ b/common/client-core/gateways-storage/src/backend/fs_backend/models.rs @@ -0,0 +1,2 @@ +// Copyright 2024 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 diff --git a/common/client-core/gateways-storage/src/backend/mem_backend.rs b/common/client-core/gateways-storage/src/backend/mem_backend.rs new file mode 100644 index 0000000000..53afb6007b --- /dev/null +++ b/common/client-core/gateways-storage/src/backend/mem_backend.rs @@ -0,0 +1,69 @@ +// Copyright 2024 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::types::GatewayDetails; +use crate::{BadGateway, GatewayDetailsStore}; +use async_trait::async_trait; +use std::collections::HashMap; +use std::sync::Arc; +use thiserror::Error; +use tokio::sync::RwLock; + +#[derive(Debug, Error)] +pub enum InMemStorageError { + #[error("gateway {gateway_id} does not exist")] + GatewayDoesNotExist { gateway_id: String }, + + #[error(transparent)] + MalformedGateway(#[from] BadGateway), +} + +pub struct InMemStorage { + inner: Arc>, +} + +struct InMemStorageInner { + active_gateway: Option, + gateways: HashMap, +} + +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +impl GatewayDetailsStore for InMemStorage { + type StorageError = InMemStorageError; + + async fn active_gateway(&self) -> Result, Self::StorageError> { + // let guard = self.inner.read().await; + // + // let foo = guard.active_gateway.map(|id| { + // // SAFETY: if particular gateway is set as active, its details MUST exist + // #[allow(clippy::unwrap_used)] + // guard.gateways.get(&id).unwrap() + // }); + + todo!() + // foo.cloned() + } + + async fn all_gateways(&self) -> Result, Self::StorageError> { + todo!() + } + + async fn load_gateway_details( + &self, + gateway_id: &str, + ) -> Result { + todo!() + } + + async fn store_gateway_details( + &self, + details: GatewayDetails, + ) -> Result<(), Self::StorageError> { + todo!() + } + + async fn remove_gateway_details(&self, gateway_id: &str) -> Result<(), Self::StorageError> { + todo!() + } +} diff --git a/common/client-core/gateways-storage/src/backend/mod.rs b/common/client-core/gateways-storage/src/backend/mod.rs new file mode 100644 index 0000000000..9414bb4f14 --- /dev/null +++ b/common/client-core/gateways-storage/src/backend/mod.rs @@ -0,0 +1,7 @@ +// Copyright 2024 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +#[cfg(all(not(target_arch = "wasm32"), feature = "fs-gateways-storage"))] +pub mod fs_backend; + +pub mod mem_backend; diff --git a/common/client-core/gateways-storage/src/error.rs b/common/client-core/gateways-storage/src/error.rs index 287ffb4567..f547164022 100644 --- a/common/client-core/gateways-storage/src/error.rs +++ b/common/client-core/gateways-storage/src/error.rs @@ -6,7 +6,7 @@ use nym_gateway_requests::registration::handshake::shared_key::SharedKeyConversi use thiserror::Error; #[derive(Debug, Error)] -pub enum GatewaysStorageError { +pub enum BadGateway { #[error("{typ} is not a valid gateway type")] InvalidGatewayType { typ: String }, diff --git a/common/client-core/gateways-storage/src/lib.rs b/common/client-core/gateways-storage/src/lib.rs index adda95c055..393e6438ff 100644 --- a/common/client-core/gateways-storage/src/lib.rs +++ b/common/client-core/gateways-storage/src/lib.rs @@ -1,28 +1,42 @@ // Copyright 2024 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 +#![warn(clippy::expect_used)] +#![warn(clippy::unwrap_used)] + use async_trait::async_trait; use std::error::Error; +mod backend; pub mod error; -pub mod models; +pub mod types; -pub use error::GatewaysStorageError; +use crate::types::GatewayDetails; +pub use error::BadGateway; #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] #[cfg_attr(not(target_arch = "wasm32"), async_trait)] pub trait GatewayDetailsStore { - type StorageError: Error; + type StorageError: Error + From; /// Returns details of the currently active gateway, if available. - async fn active_gateway(&self) -> Result, Self::StorageError>; + async fn active_gateway(&self) -> Result, Self::StorageError>; /// Returns details of all registered gateways. - async fn all_gateways(&self) -> Result, Self::StorageError>; + async fn all_gateways(&self) -> Result, Self::StorageError>; /// Returns details of the particular gateway. - async fn load_gateway_details(&self) -> Result<(), Self::StorageError>; + async fn load_gateway_details( + &self, + gateway_id: &str, + ) -> Result; /// Store the provided gateway details. - async fn store_gateway_details(&self) -> Result<(), Self::StorageError>; + async fn store_gateway_details( + &self, + details: GatewayDetails, + ) -> Result<(), Self::StorageError>; + + /// Remove given gateway details from the underlying store. + async fn remove_gateway_details(&self, gateway_id: &str) -> Result<(), Self::StorageError>; } diff --git a/common/client-core/gateways-storage/src/models.rs b/common/client-core/gateways-storage/src/types.rs similarity index 72% rename from common/client-core/gateways-storage/src/models.rs rename to common/client-core/gateways-storage/src/types.rs index ac8f884dd5..383500284f 100644 --- a/common/client-core/gateways-storage/src/models.rs +++ b/common/client-core/gateways-storage/src/types.rs @@ -1,13 +1,14 @@ // Copyright 2024 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use crate::GatewaysStorageError; +use crate::BadGateway; use cosmrs::AccountId; use nym_crypto::asymmetric::identity; use nym_gateway_requests::registration::handshake::SharedKeys; use serde::{Deserialize, Serialize}; use std::fmt::{Display, Formatter}; use std::str::FromStr; +use std::sync::Arc; use time::OffsetDateTime; use url::Url; use zeroize::{Zeroize, ZeroizeOnDrop}; @@ -15,6 +16,37 @@ use zeroize::{Zeroize, ZeroizeOnDrop}; pub const REMOTE_GATEWAY_TYPE: &str = "remote"; pub const CUSTOM_GATEWAY_TYPE: &str = "custom"; +#[derive(Debug)] +pub struct GatewayRegistration { + pub details: GatewayDetails, + pub registration_timestamp: OffsetDateTime, +} + +#[derive(Debug)] +pub enum GatewayDetails { + /// Standard details of a remote gateway + Remote(RemoteGatewayDetails), + + /// Custom gateway setup, such as for a client embedded inside gateway itself + Custom(CustomGatewayDetails), +} + +impl GatewayDetails { + pub fn gateway_id(&self) -> identity::PublicKey { + match self { + GatewayDetails::Remote(details) => details.gateway_id, + GatewayDetails::Custom(details) => details.gateway_id, + } + } + + pub fn shared_key(&self) -> Option<&SharedKeys> { + match self { + GatewayDetails::Remote(details) => Some(&details.derived_aes128_ctr_blake3_hmac_keys), + GatewayDetails::Custom(_) => None, + } + } +} + #[derive(Debug, Copy, Clone, Default)] pub enum GatewayType { #[default] @@ -24,13 +56,13 @@ pub enum GatewayType { } impl FromStr for GatewayType { - type Err = GatewaysStorageError; + type Err = BadGateway; fn from_str(s: &str) -> Result { match s { REMOTE_GATEWAY_TYPE => Ok(GatewayType::Remote), CUSTOM_GATEWAY_TYPE => Ok(GatewayType::Custom), - other => Err(GatewaysStorageError::InvalidGatewayType { + other => Err(BadGateway::InvalidGatewayType { typ: other.to_string(), }), } @@ -57,6 +89,15 @@ pub struct RawRegisteredGateway { pub gateway_type: String, } +#[derive(Debug, Clone, Copy)] +pub struct RegisteredGateway { + pub gateway_id: identity::PublicKey, + + pub registration_timestamp: OffsetDateTime, + + pub gateway_type: GatewayType, +} + #[derive(Debug, Zeroize, ZeroizeOnDrop, Serialize, Deserialize)] #[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))] pub struct RawRemoteGatewayDetails { @@ -67,27 +108,28 @@ pub struct RawRemoteGatewayDetails { } impl TryFrom for RemoteGatewayDetails { - type Error = GatewaysStorageError; + type Error = BadGateway; fn try_from(value: RawRemoteGatewayDetails) -> Result { let gateway_id = identity::PublicKey::from_base58_string(&value.gateway_id_bs58).map_err(|source| { - GatewaysStorageError::MalformedGatewayIdentity { + BadGateway::MalformedGatewayIdentity { gateway_id: value.gateway_id_bs58.clone(), source, } })?; - let derived_aes128_ctr_blake3_hmac_keys = + let derived_aes128_ctr_blake3_hmac_keys = Arc::new( SharedKeys::try_from_base58_string(&value.derived_aes128_ctr_blake3_hmac_keys_bs58) - .map_err(|source| GatewaysStorageError::MalformedSharedKeys { + .map_err(|source| BadGateway::MalformedSharedKeys { gateway_id: value.gateway_id_bs58.clone(), source, - })?; + })?, + ); let gateway_owner_address = AccountId::from_str(&value.gateway_owner_address).map_err(|source| { - GatewaysStorageError::MalformedGatewayOwnerAccountAddress { + BadGateway::MalformedGatewayOwnerAccountAddress { gateway_id: value.gateway_id_bs58.clone(), raw_owner: value.gateway_owner_address.clone(), source, @@ -95,7 +137,7 @@ impl TryFrom for RemoteGatewayDetails { })?; let gateway_listener = Url::parse(&value.gateway_listener).map_err(|source| { - GatewaysStorageError::MalformedListener { + BadGateway::MalformedListener { gateway_id: value.gateway_id_bs58.clone(), raw_listener: value.gateway_listener.clone(), source, @@ -124,14 +166,16 @@ impl From for RawRemoteGatewayDetails { } } -#[derive(Debug, Zeroize, ZeroizeOnDrop)] +#[derive(Debug)] pub struct RemoteGatewayDetails { - #[zeroize(skip)] pub gateway_id: identity::PublicKey, - pub derived_aes128_ctr_blake3_hmac_keys: SharedKeys, - #[zeroize(skip)] + + // note: `SharedKeys` implement ZeroizeOnDrop, meaning when `RemoteGatewayDetails` is dropped, + // the keys will be zeroized + pub derived_aes128_ctr_blake3_hmac_keys: Arc, + pub gateway_owner_address: AccountId, - #[zeroize(skip)] + pub gateway_listener: Url, } @@ -143,12 +187,12 @@ pub struct RawCustomGatewayDetails { } impl TryFrom for CustomGatewayDetails { - type Error = GatewaysStorageError; + type Error = BadGateway; fn try_from(value: RawCustomGatewayDetails) -> Result { let gateway_id = identity::PublicKey::from_base58_string(&value.gateway_id_bs58).map_err(|source| { - GatewaysStorageError::MalformedGatewayIdentity { + BadGateway::MalformedGatewayIdentity { gateway_id: value.gateway_id_bs58.clone(), source, } diff --git a/common/client-core/surb-storage/src/backend/fs_backend/error.rs b/common/client-core/surb-storage/src/backend/fs_backend/error.rs index 84cac2f7ec..02742758ae 100644 --- a/common/client-core/surb-storage/src/backend/fs_backend/error.rs +++ b/common/client-core/surb-storage/src/backend/fs_backend/error.rs @@ -10,7 +10,7 @@ pub enum StorageError { #[error("the provided database path doesn't have a filename defined")] DatabasePathWithoutFilename { provided_path: PathBuf }, - #[error("unable to create the directory for the database")] + #[error("unable to create the directory for the database at {}: {source}", provided_path.display())] DatabasePathUnableToCreateParentDirectory { provided_path: PathBuf, source: io::Error,