use crate::storage::types::WasmRawRegisteredGateway;
use async_trait::async_trait;
use nym_client_core::client::base_client::storage::gateways_storage::RawActiveGateway;
use nym_crypto::asymmetric::{ed25519, x25519};
use nym_sphinx_acknowledgements::AckKey;
use nym_wasm_storage::traits::BaseWasmStorage;
use std::error::Error;
use thiserror::Error;
use wasm_bindgen::JsValue;
pub(crate) mod v1 {
pub const KEYS_STORE: &str = "keys";
pub const CORE_STORE: &str = "core";
pub const ED25519_IDENTITY_KEYPAIR: &str = "ed25519_identity_keypair";
pub const X25519_ENCRYPTION_KEYPAIR: &str = "x25519_encryption_keypair";
pub const AES128CTR_ACK_KEY: &str = "aes128ctr_ack_key";
}
pub(crate) mod v2 {
pub const GATEWAY_REGISTRATIONS_ACTIVE_GATEWAY_STORE: &str = "active_gateway";
pub const ACTIVE_GATEWAY_KEY: &str = "active_gateway";
pub const GATEWAY_REGISTRATIONS_REGISTERED_GATEWAYS_STORE: &str = "gateway_registrations";
}
#[derive(Debug, Error)]
pub enum WasmClientStorageError {
#[error("{typ} cryptographic key is not available in storage")]
CryptoKeyNotInStorage { typ: String },
#[error(
"the prior gateway details for gateway {gateway_id:?} are not available in the storage"
)]
GatewayDetailsNotInStorage { gateway_id: String },
}
#[async_trait(?Send)]
#[async_trait]
pub trait WasmClientStorage: BaseWasmStorage {
type StorageError: Error
+ From<<Self as BaseWasmStorage>::StorageError>
+ From<WasmClientStorageError>;
async fn may_read_identity_keypair(
&self,
) -> Result<Option<ed25519::KeyPair>, <Self as WasmClientStorage>::StorageError> {
self.read_value(
v1::KEYS_STORE,
JsValue::from_str(v1::ED25519_IDENTITY_KEYPAIR),
)
.await
.map_err(Into::into)
}
async fn may_read_encryption_keypair(
&self,
) -> Result<Option<x25519::KeyPair>, <Self as WasmClientStorage>::StorageError> {
self.read_value(
v1::KEYS_STORE,
JsValue::from_str(v1::X25519_ENCRYPTION_KEYPAIR),
)
.await
.map_err(Into::into)
}
async fn may_read_ack_key(
&self,
) -> Result<Option<AckKey>, <Self as WasmClientStorage>::StorageError> {
self.read_value(v1::KEYS_STORE, JsValue::from_str(v1::AES128CTR_ACK_KEY))
.await
.map_err(Into::into)
}
async fn must_read_identity_keypair(
&self,
) -> Result<ed25519::KeyPair, <Self as WasmClientStorage>::StorageError> {
self.may_read_identity_keypair()
.await?
.ok_or(WasmClientStorageError::CryptoKeyNotInStorage {
typ: v1::ED25519_IDENTITY_KEYPAIR.to_string(),
})
.map_err(Into::into)
}
async fn must_read_encryption_keypair(
&self,
) -> Result<x25519::KeyPair, <Self as WasmClientStorage>::StorageError> {
self.may_read_encryption_keypair()
.await?
.ok_or(WasmClientStorageError::CryptoKeyNotInStorage {
typ: v1::X25519_ENCRYPTION_KEYPAIR.to_string(),
})
.map_err(Into::into)
}
async fn must_read_ack_key(&self) -> Result<AckKey, <Self as WasmClientStorage>::StorageError> {
self.may_read_ack_key()
.await?
.ok_or(WasmClientStorageError::CryptoKeyNotInStorage {
typ: v1::AES128CTR_ACK_KEY.to_string(),
})
.map_err(Into::into)
}
async fn has_identity_key(&self) -> Result<bool, <Self as WasmClientStorage>::StorageError> {
self.has_value(
v1::KEYS_STORE,
JsValue::from_str(v1::ED25519_IDENTITY_KEYPAIR),
)
.await
.map_err(Into::into)
}
async fn store_identity_keypair(
&self,
keypair: &ed25519::KeyPair,
) -> Result<(), <Self as WasmClientStorage>::StorageError> {
self.store_value(
v1::KEYS_STORE,
JsValue::from_str(v1::ED25519_IDENTITY_KEYPAIR),
keypair,
)
.await
.map_err(Into::into)
}
async fn store_encryption_keypair(
&self,
keypair: &x25519::KeyPair,
) -> Result<(), <Self as WasmClientStorage>::StorageError> {
self.store_value(
v1::KEYS_STORE,
JsValue::from_str(v1::X25519_ENCRYPTION_KEYPAIR),
keypair,
)
.await
.map_err(Into::into)
}
async fn store_ack_key(
&self,
key: &AckKey,
) -> Result<(), <Self as WasmClientStorage>::StorageError> {
self.store_value(
v1::KEYS_STORE,
JsValue::from_str(v1::AES128CTR_ACK_KEY),
key,
)
.await
.map_err(Into::into)
}
async fn get_active_gateway_id(
&self,
) -> Result<RawActiveGateway, <Self as WasmClientStorage>::StorageError> {
let maybe_active: Option<RawActiveGateway> = self
.read_value(
v2::GATEWAY_REGISTRATIONS_ACTIVE_GATEWAY_STORE,
JsValue::from_str(v2::ACTIVE_GATEWAY_KEY),
)
.await?;
Ok(RawActiveGateway {
active_gateway_id_bs58: maybe_active.and_then(|a| a.active_gateway_id_bs58),
})
}
async fn set_active_gateway(
&self,
gateway_id: Option<&str>,
) -> Result<(), <Self as WasmClientStorage>::StorageError> {
self.store_value(
v2::GATEWAY_REGISTRATIONS_ACTIVE_GATEWAY_STORE,
JsValue::from_str(v2::ACTIVE_GATEWAY_KEY),
&RawActiveGateway {
active_gateway_id_bs58: gateway_id.map(|id| id.to_string()),
},
)
.await
.map_err(Into::into)
}
async fn maybe_get_registered_gateway(
&self,
gateway_id: &str,
) -> Result<Option<WasmRawRegisteredGateway>, <Self as WasmClientStorage>::StorageError> {
self.read_value(
v2::GATEWAY_REGISTRATIONS_REGISTERED_GATEWAYS_STORE,
JsValue::from_str(gateway_id),
)
.await
.map_err(Into::into)
}
async fn must_get_registered_gateway(
&self,
gateway_id: &str,
) -> Result<WasmRawRegisteredGateway, <Self as WasmClientStorage>::StorageError> {
self.maybe_get_registered_gateway(gateway_id)
.await?
.ok_or(WasmClientStorageError::GatewayDetailsNotInStorage {
gateway_id: gateway_id.to_string(),
})
.map_err(Into::into)
}
async fn store_registered_gateway(
&self,
registered_gateway: &WasmRawRegisteredGateway,
) -> Result<(), <Self as WasmClientStorage>::StorageError> {
self.store_value(
v2::GATEWAY_REGISTRATIONS_REGISTERED_GATEWAYS_STORE,
JsValue::from_str(®istered_gateway.gateway_id_bs58),
registered_gateway,
)
.await
.map_err(Into::into)
}
async fn remove_registered_gateway(
&self,
gateway_id: &str,
) -> Result<(), <Self as WasmClientStorage>::StorageError> {
self.remove_value(
v2::GATEWAY_REGISTRATIONS_REGISTERED_GATEWAYS_STORE,
JsValue::from_str(gateway_id),
)
.await
.map_err(Into::into)
}
async fn has_registered_gateway(
&self,
gateway_id: &str,
) -> Result<bool, <Self as WasmClientStorage>::StorageError> {
self.has_value(
v2::GATEWAY_REGISTRATIONS_REGISTERED_GATEWAYS_STORE,
JsValue::from_str(gateway_id),
)
.await
.map_err(Into::into)
}
async fn registered_gateways(
&self,
) -> Result<Vec<String>, <Self as WasmClientStorage>::StorageError> {
self.get_all_keys(v2::GATEWAY_REGISTRATIONS_REGISTERED_GATEWAYS_STORE)
.await
.map_err(Into::into)
.map(|arr| {
arr.iter()
.cloned()
.filter_map(|key| key.as_string())
.collect()
})
}
}