adjusting the trait
This commit is contained in:
@@ -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"] }
|
||||
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// 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,
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// 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<P: AsRef<Path>>(database_path: P, fresh: bool) -> Result<Self, StorageError> {
|
||||
// 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 })
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// 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<Option<GatewayDetails>, Self::StorageError> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn all_gateways(&self) -> Result<Vec<GatewayDetails>, Self::StorageError> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn load_gateway_details(
|
||||
&self,
|
||||
gateway_id: &str,
|
||||
) -> Result<GatewayDetails, Self::StorageError> {
|
||||
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!()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
@@ -0,0 +1,69 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// 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<RwLock<InMemStorageInner>>,
|
||||
}
|
||||
|
||||
struct InMemStorageInner {
|
||||
active_gateway: Option<String>,
|
||||
gateways: HashMap<String, GatewayDetails>,
|
||||
}
|
||||
|
||||
#[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<Option<GatewayDetails>, 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<Vec<GatewayDetails>, Self::StorageError> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn load_gateway_details(
|
||||
&self,
|
||||
gateway_id: &str,
|
||||
) -> Result<GatewayDetails, Self::StorageError> {
|
||||
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!()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-gateways-storage"))]
|
||||
pub mod fs_backend;
|
||||
|
||||
pub mod mem_backend;
|
||||
@@ -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 },
|
||||
|
||||
|
||||
@@ -1,28 +1,42 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// 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<error::BadGateway>;
|
||||
|
||||
/// Returns details of the currently active gateway, if available.
|
||||
async fn active_gateway(&self) -> Result<Option<()>, Self::StorageError>;
|
||||
async fn active_gateway(&self) -> Result<Option<GatewayDetails>, Self::StorageError>;
|
||||
|
||||
/// Returns details of all registered gateways.
|
||||
async fn all_gateways(&self) -> Result<Vec<()>, Self::StorageError>;
|
||||
async fn all_gateways(&self) -> Result<Vec<GatewayDetails>, 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<GatewayDetails, Self::StorageError>;
|
||||
|
||||
/// 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>;
|
||||
}
|
||||
|
||||
+61
-17
@@ -1,13 +1,14 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// 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<Self, Self::Err> {
|
||||
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<RawRemoteGatewayDetails> for RemoteGatewayDetails {
|
||||
type Error = GatewaysStorageError;
|
||||
type Error = BadGateway;
|
||||
|
||||
fn try_from(value: RawRemoteGatewayDetails) -> Result<Self, Self::Error> {
|
||||
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<RawRemoteGatewayDetails> 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<RemoteGatewayDetails> 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<SharedKeys>,
|
||||
|
||||
pub gateway_owner_address: AccountId,
|
||||
#[zeroize(skip)]
|
||||
|
||||
pub gateway_listener: Url,
|
||||
}
|
||||
|
||||
@@ -143,12 +187,12 @@ pub struct RawCustomGatewayDetails {
|
||||
}
|
||||
|
||||
impl TryFrom<RawCustomGatewayDetails> for CustomGatewayDetails {
|
||||
type Error = GatewaysStorageError;
|
||||
type Error = BadGateway;
|
||||
|
||||
fn try_from(value: RawCustomGatewayDetails) -> Result<Self, Self::Error> {
|
||||
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,
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user