use std::{
borrow::Cow,
fmt,
path::{Path, PathBuf},
};
use anyhow::Context;
pub use lexe_common::{env::DeployEnv, ln::network::Network};
use crate::{
types::auth::{CredentialsRef, UserPk},
unstable::SDK_USER_AGENT,
};
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct WalletEnv {
pub deploy_env: DeployEnv,
pub network: Network,
pub use_sgx: bool,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct WalletEnvConfig {
pub wallet_env: WalletEnv,
pub(crate) gateway_url: Cow<'static, str>,
pub(crate) user_agent: Cow<'static, str>,
}
#[derive(Clone)]
pub struct WalletUserConfig {
pub user_pk: UserPk,
pub env_config: WalletEnvConfig,
}
#[derive(Clone)]
pub struct WalletEnvDbConfig {
pub(crate) lexe_data_dir: PathBuf,
pub(crate) env_db_dir: PathBuf,
}
#[derive(Clone)]
pub struct WalletUserDbConfig {
pub(crate) env_db_config: WalletEnvDbConfig,
pub(crate) user_pk: UserPk,
pub(crate) user_db_dir: PathBuf,
}
impl WalletEnv {
pub fn mainnet() -> Self {
Self {
deploy_env: DeployEnv::Prod,
network: Network::Mainnet,
use_sgx: true,
}
}
pub fn testnet3() -> Self {
Self {
deploy_env: DeployEnv::Staging,
network: Network::Testnet3,
use_sgx: true,
}
}
pub fn regtest(use_sgx: bool) -> Self {
Self {
deploy_env: DeployEnv::Dev,
network: Network::Regtest,
use_sgx,
}
}
pub fn seedphrase_path(&self, data_dir: &Path) -> PathBuf {
let filename = if self.deploy_env == DeployEnv::Prod {
Cow::Borrowed("seedphrase.txt")
} else {
Cow::Owned(format!("seedphrase.{self}.txt"))
};
data_dir.join(filename.as_ref())
}
}
impl fmt::Display for WalletEnv {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let deploy_env = self.deploy_env.as_str();
let network = self.network.as_str();
let sgx = if self.use_sgx { "sgx" } else { "dbg" };
write!(f, "{deploy_env}-{network}-{sgx}")
}
}
impl WalletEnvConfig {
pub fn mainnet() -> Self {
let wallet_env = WalletEnv::mainnet();
Self {
gateway_url: wallet_env.deploy_env.gateway_url(None),
user_agent: Cow::Borrowed(*SDK_USER_AGENT),
wallet_env,
}
}
pub fn testnet3() -> Self {
let wallet_env = WalletEnv::testnet3();
Self {
gateway_url: wallet_env.deploy_env.gateway_url(None),
user_agent: Cow::Borrowed(*SDK_USER_AGENT),
wallet_env,
}
}
pub fn regtest(
use_sgx: bool,
gateway_url: Option<impl Into<Cow<'static, str>>>,
) -> Self {
let wallet_env = WalletEnv::regtest(use_sgx);
let gateway_url = gateway_url.map(Into::into);
Self {
gateway_url: wallet_env.deploy_env.gateway_url(gateway_url),
user_agent: Cow::Borrowed(*SDK_USER_AGENT),
wallet_env,
}
}
#[cfg(feature = "unstable")]
pub fn new(
wallet_env: WalletEnv,
gateway_url: Cow<'static, str>,
user_agent: Cow<'static, str>,
) -> Self {
Self {
wallet_env,
gateway_url,
user_agent,
}
}
#[cfg(feature = "unstable")]
pub fn prod() -> Self {
Self::mainnet()
}
#[cfg(feature = "unstable")]
pub fn staging() -> Self {
Self::testnet3()
}
#[cfg(feature = "unstable")]
pub fn dev(
use_sgx: bool,
gateway_url: Option<impl Into<Cow<'static, str>>>,
) -> Self {
Self::regtest(use_sgx, gateway_url)
}
#[cfg(feature = "unstable")]
pub fn gateway_url(&self) -> &str {
&self.gateway_url
}
#[cfg(feature = "unstable")]
pub fn user_agent(&self) -> &str {
&self.user_agent
}
pub fn seedphrase_path(&self, data_dir: &Path) -> PathBuf {
self.wallet_env.seedphrase_path(data_dir)
}
}
impl WalletEnvDbConfig {
pub fn new(wallet_env: WalletEnv, lexe_data_dir: PathBuf) -> Self {
let env_db_dir = lexe_data_dir.join(wallet_env.to_string());
Self {
lexe_data_dir,
env_db_dir,
}
}
pub fn lexe_data_dir(&self) -> &PathBuf {
&self.lexe_data_dir
}
pub fn env_db_dir(&self) -> &PathBuf {
&self.env_db_dir
}
}
impl WalletUserDbConfig {
pub fn new(env_db_config: WalletEnvDbConfig, user_pk: UserPk) -> Self {
let user_db_dir = env_db_config.env_db_dir.join(user_pk.to_string());
Self {
env_db_config,
user_pk,
user_db_dir,
}
}
pub fn from_credentials(
credentials: CredentialsRef<'_>,
env_db_config: WalletEnvDbConfig,
) -> anyhow::Result<Self> {
let user_pk = credentials.user_pk().context(
"Client credentials are out of date. \
Please create a new one from within the Lexe wallet app.",
)?;
Ok(Self::new(env_db_config, user_pk))
}
pub fn env_db_config(&self) -> &WalletEnvDbConfig {
&self.env_db_config
}
pub fn user_pk(&self) -> UserPk {
self.user_pk
}
pub fn lexe_data_dir(&self) -> &PathBuf {
self.env_db_config.lexe_data_dir()
}
pub fn env_db_dir(&self) -> &PathBuf {
self.env_db_config.env_db_dir()
}
pub fn user_db_dir(&self) -> &PathBuf {
&self.user_db_dir
}
pub(crate) fn payments_db_dir(&self) -> PathBuf {
self.user_db_dir.join("payments_db")
}
pub(crate) fn old_payment_db_dirs(&self) -> [PathBuf; 1] {
[
self.user_db_dir.join("payment_db"),
]
}
pub(crate) fn old_provision_db_dir(&self) -> PathBuf {
self.user_db_dir.join("provision_db")
}
}
#[cfg(test)]
mod test {
use std::str::FromStr;
use super::*;
#[test]
fn test_sdk_user_agent() {
let user_agent: &str = &SDK_USER_AGENT;
let (sdk_part, node_part) = user_agent
.split_once(" node/")
.expect("Missing ' node/' separator");
let sdk_version_str = sdk_part
.strip_prefix("lexe/")
.expect("Missing 'lexe/' prefix");
let _sdk_version = semver::Version::from_str(sdk_version_str)
.expect("Invalid SDK semver version");
let _node_version = semver::Version::from_str(node_part)
.expect("Invalid node semver version");
}
}