use crate::errors::AppError;
use crate::AppContext;
use async_trait::async_trait;
use log::trace;
use rand::thread_rng;
use safe_core::client::{attempt_bootstrap, Inner, SafeKey};
use safe_core::config_handler::Config;
use safe_core::core_structs::AppKeys;
use safe_core::crypto::{shared_box, shared_secretbox};
use safe_core::ipc::BootstrapConfig;
use safe_core::{Client, ConnectionManager, NetworkTx};
use safe_nd::{ClientFullId, PublicKey};
use std::fmt;
use futures::lock::Mutex;
use std::sync::Arc;
use std::time::Duration;
pub struct AppClient {
inner: Arc<Mutex<Inner>>,
app_inner: Arc<Mutex<AppInner>>,
}
impl AppClient {
pub(crate) async fn unregistered(
net_tx: NetworkTx,
config: Option<BootstrapConfig>,
) -> Result<Self, AppError> {
trace!("Creating unregistered client.");
let client_id = ClientFullId::new_bls(&mut thread_rng());
let app_keys = AppKeys::new(client_id.public_id().clone());
let pk = app_keys.public_key();
let mut qp2p_config = Config::new().quic_p2p;
if let Some(additional_contacts) = config.clone() {
qp2p_config.hard_coded_contacts = qp2p_config
.hard_coded_contacts
.union(&additional_contacts)
.cloned()
.collect();
}
let connection_manager =
attempt_bootstrap(&qp2p_config, &net_tx, app_keys.app_safe_key()).await?;
Ok(Self {
inner: Arc::new(Mutex::new(Inner::new(
connection_manager,
Duration::from_secs(180), net_tx,
))),
app_inner: Arc::new(Mutex::new(AppInner::new(app_keys, pk, config))),
})
}
pub(crate) async fn from_keys(
keys: AppKeys,
owner: PublicKey,
net_tx: NetworkTx,
config: BootstrapConfig,
) -> Result<Self, AppError> {
Self::from_keys_impl(keys, owner, net_tx, config, |routing| routing).await
}
#[cfg(any(
all(test, feature = "mock-network"),
all(feature = "testing", feature = "mock-network")
))]
pub(crate) async fn from_keys_with_hook<F>(
keys: AppKeys,
owner: PublicKey,
net_tx: NetworkTx,
config: BootstrapConfig,
connection_manager_wrapper_fn: F,
) -> Result<Self, AppError>
where
F: Fn(ConnectionManager) -> ConnectionManager,
{
Self::from_keys_impl(keys, owner, net_tx, config, connection_manager_wrapper_fn).await
}
async fn from_keys_impl<F>(
keys: AppKeys,
owner: PublicKey,
net_tx: NetworkTx,
config: BootstrapConfig,
connection_manager_wrapper_fn: F,
) -> Result<Self, AppError>
where
F: Fn(ConnectionManager) -> ConnectionManager,
{
trace!("Attempting to log into an acc using client keys.");
let mut qp2p_config = Config::new().quic_p2p;
qp2p_config.hard_coded_contacts = qp2p_config
.hard_coded_contacts
.union(&config)
.cloned()
.collect();
let mut connection_manager =
attempt_bootstrap(&qp2p_config, &net_tx, keys.app_safe_key()).await?;
connection_manager = connection_manager_wrapper_fn(connection_manager);
Ok(Self {
inner: Arc::new(Mutex::new(Inner::new(
connection_manager,
Duration::from_secs(180), net_tx,
))),
app_inner: Arc::new(Mutex::new(AppInner::new(keys, owner, Some(config)))),
})
}
}
#[async_trait]
impl Client for AppClient {
type Context = AppContext;
async fn full_id(&self) -> SafeKey {
let app_inner = self.app_inner.lock().await;
app_inner.keys.app_safe_key()
}
async fn owner_key(&self) -> PublicKey {
self.app_inner.lock().await.owner_key
}
async fn config(&self) -> Option<BootstrapConfig> {
let app_inner = self.app_inner.lock().await;
app_inner.config.clone()
}
fn inner(&self) -> Arc<Mutex<Inner>> {
self.inner.clone()
}
async fn public_encryption_key(&self) -> threshold_crypto::PublicKey {
let app_inner = self.app_inner.lock().await;
app_inner.keys.clone().enc_public_key
}
async fn secret_encryption_key(&self) -> shared_box::SecretKey {
let app_inner = self.app_inner.lock().await;
app_inner.keys.clone().enc_secret_key
}
async fn secret_symmetric_key(&self) -> shared_secretbox::Key {
let app_inner = self.app_inner.lock().await;
app_inner.keys.clone().enc_key
}
}
impl Clone for AppClient {
fn clone(&self) -> Self {
Self {
inner: Arc::clone(&self.inner),
app_inner: Arc::clone(&self.app_inner),
}
}
}
impl fmt::Debug for AppClient {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Safe App Client")
}
}
struct AppInner {
keys: AppKeys,
owner_key: PublicKey,
config: Option<BootstrapConfig>,
}
impl AppInner {
pub fn new(keys: AppKeys, owner_key: PublicKey, config: Option<BootstrapConfig>) -> Self {
Self {
keys,
owner_key,
config,
}
}
}