use crate::client::account::{Account as ClientAccount, ClientKeys};
#[cfg(feature = "mock-network")]
use crate::client::mock::ConnectionManager;
use crate::client::{req, AuthActions, Client, ClientInner, SafeKey, IMMUT_DATA_CACHE_SIZE};
use crate::config_handler::Config;
#[cfg(not(feature = "mock-network"))]
use crate::connection_manager::ConnectionManager;
use crate::crypto::{shared_box, shared_secretbox, shared_sign};
use crate::errors::CoreError;
use crate::event::NetworkTx;
use crate::event_loop::CoreMsgTx;
use crate::ipc::BootstrapConfig;
use crate::utils;
use lru_cache::LruCache;
use new_rand::rngs::StdRng;
use new_rand::SeedableRng;
use rust_sodium::crypto::sign::Seed;
use rust_sodium::crypto::{box_, sign};
use safe_nd::{
ClientFullId, ClientPublicId, Coins, LoginPacket, PublicId, PublicKey, Request, Response,
};
use std::cell::RefCell;
use std::rc::Rc;
use std::str::FromStr;
use std::time::Duration;
use threshold_crypto::SecretKey as BlsSecretKey;
use tiny_keccak::sha3_256;
use tokio::runtime::current_thread::{block_on_all, Handle};
pub struct CoreClient {
inner: Rc<RefCell<ClientInner<CoreClient, ()>>>,
keys: ClientKeys,
}
impl CoreClient {
pub fn new(
acc_locator: &str,
acc_password: &str,
el_handle: Handle,
core_tx: CoreMsgTx<Self, ()>,
net_tx: NetworkTx,
) -> Result<Self, CoreError> {
Self::new_impl(
acc_locator.as_bytes(),
acc_password.as_bytes(),
el_handle,
core_tx,
net_tx,
None,
|cm| cm,
)
}
fn new_impl<F>(
acc_locator: &[u8],
acc_password: &[u8],
el_handle: Handle,
core_tx: CoreMsgTx<Self, ()>,
net_tx: NetworkTx,
id_seed: Option<&Seed>,
connection_manager_wrapper_fn: F,
) -> Result<Self, CoreError>
where
F: Fn(ConnectionManager) -> ConnectionManager,
{
trace!("Creating an account.");
let (password, keyword, pin) = utils::derive_secrets(acc_locator, acc_password);
let acc_loc = ClientAccount::generate_network_id(&keyword, &pin)?;
let balance_sk = BlsSecretKey::random();
let maid_keys = {
let mut maid_keys = ClientKeys::new(id_seed);
maid_keys.bls_sk = balance_sk.clone();
maid_keys.bls_pk = balance_sk.public_key();
maid_keys
};
let acc = ClientAccount::new(maid_keys.clone())?;
let acc_ciphertext = acc.encrypt(&password, &pin)?;
let (client_pk, client_full_id) = {
let mut seeder: Vec<u8> = Vec::with_capacity(acc_locator.len() + acc_password.len());
seeder.extend_from_slice(acc_locator);
seeder.extend_from_slice(acc_password);
let seed = sha3_256(&seeder);
let mut rng = StdRng::from_seed(seed);
let client_full_id = ClientFullId::new_bls(&mut rng);
(
*client_full_id.public_id().public_key(),
SafeKey::client(client_full_id),
)
};
let sig = client_full_id.sign(&acc_ciphertext);
let new_login_packet = LoginPacket::new(acc_loc, client_pk, acc_ciphertext, sig)?;
let balance_client_id = ClientFullId::with_bls_key(balance_sk);
let new_balance_owner = *balance_client_id.public_id().public_key();
let balance_client_id = SafeKey::client(balance_client_id);
let balance_pub_id = balance_client_id.public_id();
let mut connection_manager =
ConnectionManager::new(Config::new().quic_p2p, &net_tx.clone())?;
connection_manager = connection_manager_wrapper_fn(connection_manager);
{
block_on_all(connection_manager.bootstrap(balance_client_id.clone()))?;
let response = req(
&mut connection_manager,
Request::CreateBalance {
new_balance_owner,
amount: unwrap!(Coins::from_str("10")),
transaction_id: new_rand::random(),
},
&balance_client_id,
)?;
let _ = match response {
Response::Transaction(res) => res?,
_ => return Err(CoreError::from("Unexpected response")),
};
let response = req(
&mut connection_manager,
Request::CreateLoginPacket(new_login_packet),
&balance_client_id,
)?;
match response {
Response::Mutation(res) => res?,
_ => return Err(CoreError::from("Unexpected response")),
};
block_on_all(connection_manager.disconnect(&balance_pub_id))?;
}
block_on_all(connection_manager.bootstrap(maid_keys.client_safe_key()))?;
Ok(Self {
inner: Rc::new(RefCell::new(ClientInner {
el_handle,
connection_manager,
cache: LruCache::new(IMMUT_DATA_CACHE_SIZE),
timeout: Duration::from_secs(180), net_tx,
core_tx,
})),
keys: maid_keys,
})
}
}
impl Client for CoreClient {
type MsgType = ();
fn full_id(&self) -> SafeKey {
self.keys.client_safe_key()
}
fn public_id(&self) -> PublicId {
let client_pk = PublicKey::from(self.public_bls_key());
PublicId::Client(ClientPublicId::new(client_pk.into(), client_pk))
}
fn config(&self) -> Option<BootstrapConfig> {
None
}
fn inner(&self) -> Rc<RefCell<ClientInner<Self, Self::MsgType>>> {
self.inner.clone()
}
fn public_encryption_key(&self) -> box_::PublicKey {
self.keys.enc_pk
}
fn secret_encryption_key(&self) -> shared_box::SecretKey {
self.keys.enc_sk.clone()
}
fn public_signing_key(&self) -> sign::PublicKey {
self.keys.sign_pk
}
fn secret_signing_key(&self) -> shared_sign::SecretKey {
self.keys.sign_sk.clone()
}
fn secret_symmetric_key(&self) -> shared_secretbox::Key {
self.keys.enc_key.clone()
}
fn public_bls_key(&self) -> threshold_crypto::PublicKey {
self.keys.bls_pk
}
fn secret_bls_key(&self) -> threshold_crypto::SecretKey {
self.keys.bls_sk.clone()
}
fn owner_key(&self) -> PublicKey {
PublicKey::from(self.keys.bls_pk)
}
fn public_key(&self) -> PublicKey {
self.keys.bls_pk.into()
}
}
impl AuthActions for CoreClient {}
impl Clone for CoreClient {
fn clone(&self) -> Self {
CoreClient {
inner: Rc::clone(&self.inner),
keys: self.keys.clone(),
}
}
}