pub mod account;
#[cfg(any(test, feature = "testing"))]
pub mod core_client;
pub mod mdata_info;
pub mod recoverable_apis;
use async_trait::async_trait;
mod id;
#[cfg(feature = "mock-network")]
mod mock;
pub use self::account::ClientKeys;
pub use self::id::SafeKey;
pub use self::mdata_info::MDataInfo;
#[cfg(feature = "mock-network")]
pub use self::mock::vault::mock_vault_path;
#[cfg(feature = "mock-network")]
pub use self::mock::ConnectionManager as MockConnectionManager;
#[cfg(feature = "mock-network")]
use self::mock::ConnectionManager;
use crate::config_handler::Config;
#[cfg(not(feature = "mock-network"))]
use crate::connection_manager::ConnectionManager;
use crate::crypto::{shared_box, shared_secretbox};
use crate::errors::CoreError;
use crate::ipc::BootstrapConfig;
use crate::network_event::{NetworkEvent, NetworkTx};
use futures::{channel::mpsc, lock::Mutex};
use std::sync::Arc;
use log::trace;
use lru_cache::LruCache;
use quic_p2p::Config as QuicP2pConfig;
use safe_nd::{
AData, ADataAddress, ADataAppendOperation, ADataEntries, ADataEntry, ADataIndex, ADataIndices,
ADataOwner, ADataPermissions, ADataPubPermissionSet, ADataPubPermissions, ADataRequest,
ADataUnpubPermissionSet, ADataUnpubPermissions, ADataUser, AppPermissions, ClientFullId,
ClientRequest, Coins, CoinsRequest, IData, IDataAddress, IDataRequest, LoginPacket,
LoginPacketRequest, MData, MDataAddress, MDataEntries, MDataEntryActions, MDataPermissionSet,
MDataRequest, MDataSeqEntries, MDataSeqEntryActions, MDataSeqValue, MDataUnseqEntryActions,
MDataValue, MDataValues, Message, MessageId, PublicId, PublicKey, Request, RequestType,
Response, SeqMutableData, Transaction, UnseqMutableData, XorName,
};
use std::collections::{BTreeMap, BTreeSet};
use std::time::Duration;
use unwrap::unwrap;
pub const IMMUT_DATA_CACHE_SIZE: usize = 300;
pub const COST_OF_PUT: Coins = Coins::from_nano(1);
pub fn bootstrap_config() -> Result<BootstrapConfig, CoreError> {
Ok(Config::new().quic_p2p.hard_coded_contacts)
}
async fn send(client: &impl Client, request: Request) -> Result<Response, CoreError> {
let sign = request.get_type() != RequestType::PublicGet;
let request = client.compose_message(request, sign).await;
let inner = client.inner();
let cm = &mut inner.lock().await.connection_manager;
cm.send(&client.public_id().await, &request).await
}
async fn send_mutation(client: &impl Client, req: Request) -> Result<(), CoreError> {
let response = send(client, req).await?;
match response {
Response::Mutation(result) => {
trace!("mutation result: {:?}", result);
result.map_err(CoreError::from)
}
_ => Err(CoreError::ReceivedUnexpectedEvent),
}
}
async fn send_as_helper(
client: &impl Client,
request: Request,
client_id: Option<&ClientFullId>,
) -> Result<Response, CoreError> {
let (message, identity) = match client_id {
Some(id) => (sign_request(request, id), SafeKey::client(id.clone())),
None => {
let msg = client.compose_message(request, true).await;
let client_id = client.full_id().await;
(msg, client_id)
}
};
let pub_id = identity.public_id();
let inner = client.inner();
let cm = &mut inner.lock().await.connection_manager;
let _bootstrapped = cm.bootstrap(identity).await;
cm.send(&pub_id, &message).await
}
#[async_trait]
pub trait Client: Clone + Send + Sync {
type Context;
async fn full_id(&self) -> SafeKey;
async fn public_id(&self) -> PublicId {
self.full_id().await.public_id()
}
async fn public_key(&self) -> PublicKey {
self.full_id().await.public_key()
}
async fn owner_key(&self) -> PublicKey;
async fn config(&self) -> Option<BootstrapConfig>;
fn inner(&self) -> Arc<Mutex<Inner>>
where
Self: Sized;
async fn public_encryption_key(&self) -> threshold_crypto::PublicKey;
async fn secret_encryption_key(&self) -> shared_box::SecretKey;
async fn encryption_keypair(&self) -> (threshold_crypto::PublicKey, shared_box::SecretKey) {
let enc_key = self.public_encryption_key().await;
let sec_key = self.secret_encryption_key().await;
(enc_key, sec_key)
}
async fn secret_symmetric_key(&self) -> shared_secretbox::Key;
async fn compose_message(&self, request: Request, sign: bool) -> Message {
let message_id = MessageId::new();
let signature = if sign {
Some(
self.full_id()
.await
.sign(&unwrap!(bincode::serialize(&(&request, message_id)))),
)
} else {
None
};
Message::Request {
request,
message_id,
signature,
}
}
async fn set_timeout(&self, duration: Duration) {
let inner = self.inner();
inner.lock().await.timeout = duration;
}
async fn restart_network(&self) -> Result<(), CoreError> {
trace!("Restarting the network connection");
let inner = self.inner();
let mut inner = inner.lock().await;
inner.connection_manager.restart_network();
inner
.net_tx
.unbounded_send(NetworkEvent::Connected)
.map_err(|error| CoreError::from(format!("{:?}", error)))?;
Ok(())
}
async fn put_unseq_mutable_data(&self, data: UnseqMutableData) -> Result<(), CoreError>
where
Self: Sized,
{
trace!("Put Unsequenced MData at {:?}", data.name());
send_mutation(self, Request::MData(MDataRequest::Put(MData::Unseq(data)))).await
}
async fn transfer_coins(
&self,
client_id: Option<&ClientFullId>,
destination: XorName,
amount: Coins,
transaction_id: Option<u64>,
) -> Result<Transaction, CoreError>
where
Self: Sized,
{
trace!("Transfer {} coins to {:?}", amount, destination);
match send_as_helper(
self,
Request::Coins(CoinsRequest::Transfer {
destination,
amount,
transaction_id: transaction_id.unwrap_or_else(rand::random),
}),
client_id,
)
.await
{
Ok(res) => match res {
Response::Transaction(result) => match result {
Ok(transaction) => Ok(transaction),
Err(error) => Err(CoreError::from(error)),
},
_ => Err(CoreError::ReceivedUnexpectedEvent),
},
Err(_error) => Err(CoreError::ReceivedUnexpectedEvent),
}
}
async fn create_balance(
&self,
client_id: Option<&ClientFullId>,
new_balance_owner: PublicKey,
amount: Coins,
transaction_id: Option<u64>,
) -> Result<Transaction, CoreError>
where
Self: Sized,
{
trace!(
"Create a new balance for {:?} with {} coins.",
new_balance_owner,
amount
);
match send_as_helper(
self,
Request::Coins(CoinsRequest::CreateBalance {
new_balance_owner,
amount,
transaction_id: transaction_id.unwrap_or_else(rand::random),
}),
client_id,
)
.await
{
Ok(res) => match res {
Response::Transaction(result) => match result {
Ok(transaction) => Ok(transaction),
Err(error) => Err(CoreError::from(error)),
},
_ => Err(CoreError::ReceivedUnexpectedEvent),
},
Err(error) => Err(error),
}
}
async fn insert_login_packet_for(
&self,
client_id: Option<&ClientFullId>,
new_owner: PublicKey,
amount: Coins,
transaction_id: Option<u64>,
new_login_packet: LoginPacket,
) -> Result<Transaction, CoreError>
where
Self: Sized,
{
trace!(
"Insert a login packet for {:?} preloading the wallet with {} coins.",
new_owner,
amount
);
let transaction_id = transaction_id.unwrap_or_else(rand::random);
match send_as_helper(
self,
Request::LoginPacket(LoginPacketRequest::CreateFor {
new_owner,
amount,
transaction_id,
new_login_packet,
}),
client_id,
)
.await
{
Ok(res) => match res {
Response::Transaction(result) => match result {
Ok(transaction) => Ok(transaction),
Err(error) => Err(CoreError::from(error)),
},
_ => Err(CoreError::ReceivedUnexpectedEvent),
},
Err(error) => Err(error),
}
}
async fn get_balance(&self, client_id: Option<&ClientFullId>) -> Result<Coins, CoreError>
where
Self: Sized,
{
trace!("Get balance for {:?}", client_id);
match send_as_helper(self, Request::Coins(CoinsRequest::GetBalance), client_id).await {
Ok(res) => match res {
Response::GetBalance(result) => match result {
Ok(coins) => Ok(coins),
Err(error) => Err(CoreError::from(error)),
},
_ => Err(CoreError::ReceivedUnexpectedEvent),
},
Err(error) => Err(error),
}
}
async fn put_idata<D: Into<IData> + Send>(&self, data: D) -> Result<(), CoreError>
where
Self: Sized + Send,
{
let idata: IData = data.into();
trace!("Put IData at {:?}", idata.name());
send_mutation(self, Request::IData(IDataRequest::Put(idata))).await
}
async fn get_idata(&self, address: IDataAddress) -> Result<IData, CoreError>
where
Self: Sized,
{
trace!("Fetch Immutable Data");
let inner = self.inner();
if let Some(data) = inner.lock().await.cache.get_mut(&address) {
trace!("ImmutableData found in cache.");
return Ok(data.clone());
}
let inner = Arc::downgrade(&self.inner());
let res = send(self, Request::IData(IDataRequest::Get(address))).await?;
let data = match res {
Response::GetIData(res) => res.map_err(CoreError::from),
_ => return Err(CoreError::ReceivedUnexpectedEvent),
}?;
if let Some(inner) = inner.upgrade() {
let _ = inner
.lock()
.await
.cache
.insert(*data.address(), data.clone());
};
Ok(data)
}
async fn del_unpub_idata(&self, name: XorName) -> Result<(), CoreError>
where
Self: Sized,
{
let inner = self.inner();
if inner
.lock()
.await
.cache
.remove(&IDataAddress::Unpub(name))
.is_some()
{
trace!("Deleted UnpubImmutableData from cache.");
}
let inner = self.inner().clone();
let _ = Arc::downgrade(&inner);
trace!("Delete Unpublished IData at {:?}", name);
send_mutation(
self,
Request::IData(IDataRequest::DeleteUnpub(IDataAddress::Unpub(name))),
)
.await
}
async fn put_seq_mutable_data(&self, data: SeqMutableData) -> Result<(), CoreError>
where
Self: Sized,
{
trace!("Put Sequenced MData at {:?}", data.name());
send_mutation(self, Request::MData(MDataRequest::Put(MData::Seq(data)))).await
}
async fn get_unseq_mdata(&self, name: XorName, tag: u64) -> Result<UnseqMutableData, CoreError>
where
Self: Sized,
{
trace!("Fetch Unsequenced Mutable Data");
match send(
self,
Request::MData(MDataRequest::Get(MDataAddress::Unseq { name, tag })),
)
.await?
{
Response::GetMData(res) => res.map_err(CoreError::from).and_then(|mdata| match mdata {
MData::Unseq(data) => Ok(data),
MData::Seq(_) => Err(CoreError::ReceivedUnexpectedData),
}),
_ => Err(CoreError::ReceivedUnexpectedEvent),
}
}
async fn get_seq_mdata_value(
&self,
name: XorName,
tag: u64,
key: Vec<u8>,
) -> Result<MDataSeqValue, CoreError>
where
Self: Sized,
{
trace!("Fetch MDataValue for {:?}", name);
match send(
self,
Request::MData(MDataRequest::GetValue {
address: MDataAddress::Seq { name, tag },
key,
}),
)
.await?
{
Response::GetMDataValue(res) => {
res.map_err(CoreError::from).and_then(|value| match value {
MDataValue::Seq(val) => Ok(val),
MDataValue::Unseq(_) => Err(CoreError::ReceivedUnexpectedData),
})
}
_ => Err(CoreError::ReceivedUnexpectedEvent),
}
}
async fn get_unseq_mdata_value(
&self,
name: XorName,
tag: u64,
key: Vec<u8>,
) -> Result<Vec<u8>, CoreError>
where
Self: Sized,
{
trace!("Fetch MDataValue for {:?}", name);
match send(
self,
Request::MData(MDataRequest::GetValue {
address: MDataAddress::Unseq { name, tag },
key,
}),
)
.await?
{
Response::GetMDataValue(res) => {
res.map_err(CoreError::from).and_then(|value| match value {
MDataValue::Unseq(val) => Ok(val),
MDataValue::Seq(_) => Err(CoreError::ReceivedUnexpectedData),
})
}
_ => Err(CoreError::ReceivedUnexpectedEvent),
}
}
async fn get_seq_mdata(&self, name: XorName, tag: u64) -> Result<SeqMutableData, CoreError>
where
Self: Sized,
{
trace!("Fetch Sequenced Mutable Data");
match send(
self,
Request::MData(MDataRequest::Get(MDataAddress::Seq { name, tag })),
)
.await?
{
Response::GetMData(res) => res.map_err(CoreError::from).and_then(|mdata| match mdata {
MData::Seq(data) => Ok(data),
MData::Unseq(_) => Err(CoreError::ReceivedUnexpectedData),
}),
_ => Err(CoreError::ReceivedUnexpectedEvent),
}
}
async fn mutate_seq_mdata_entries(
&self,
name: XorName,
tag: u64,
actions: MDataSeqEntryActions,
) -> Result<(), CoreError>
where
Self: Sized,
{
trace!("Mutate MData for {:?}", name);
send_mutation(
self,
Request::MData(MDataRequest::MutateEntries {
address: MDataAddress::Seq { name, tag },
actions: MDataEntryActions::Seq(actions),
}),
)
.await
}
async fn mutate_unseq_mdata_entries(
&self,
name: XorName,
tag: u64,
actions: MDataUnseqEntryActions,
) -> Result<(), CoreError>
where
Self: Sized,
{
trace!("Mutate MData for {:?}", name);
send_mutation(
self,
Request::MData(MDataRequest::MutateEntries {
address: MDataAddress::Unseq { name, tag },
actions: MDataEntryActions::Unseq(actions),
}),
)
.await
}
async fn get_seq_mdata_shell(
&self,
name: XorName,
tag: u64,
) -> Result<SeqMutableData, CoreError>
where
Self: Sized,
{
trace!("GetMDataShell for {:?}", name);
match send(
self,
Request::MData(MDataRequest::GetShell(MDataAddress::Seq { name, tag })),
)
.await?
{
Response::GetMDataShell(res) => {
res.map_err(CoreError::from).and_then(|mdata| match mdata {
MData::Seq(data) => Ok(data),
_ => Err(CoreError::ReceivedUnexpectedData),
})
}
_ => Err(CoreError::ReceivedUnexpectedEvent),
}
}
async fn get_unseq_mdata_shell(
&self,
name: XorName,
tag: u64,
) -> Result<UnseqMutableData, CoreError>
where
Self: Sized,
{
trace!("GetMDataShell for {:?}", name);
match send(
self,
Request::MData(MDataRequest::GetShell(MDataAddress::Unseq { name, tag })),
)
.await?
{
Response::GetMDataShell(res) => {
res.map_err(CoreError::from).and_then(|mdata| match mdata {
MData::Unseq(data) => Ok(data),
_ => Err(CoreError::ReceivedUnexpectedData),
})
}
_ => Err(CoreError::ReceivedUnexpectedEvent),
}
}
async fn get_mdata_version(&self, address: MDataAddress) -> Result<u64, CoreError>
where
Self: Sized,
{
trace!("GetMDataVersion for {:?}", address);
match send(self, Request::MData(MDataRequest::GetVersion(address))).await? {
Response::GetMDataVersion(res) => res.map_err(CoreError::from),
_ => Err(CoreError::ReceivedUnexpectedEvent),
}
}
async fn list_unseq_mdata_entries(
&self,
name: XorName,
tag: u64,
) -> Result<BTreeMap<Vec<u8>, Vec<u8>>, CoreError>
where
Self: Sized,
{
trace!("ListMDataEntries for {:?}", name);
match send(
self,
Request::MData(MDataRequest::ListEntries(MDataAddress::Unseq { name, tag })),
)
.await?
{
Response::ListMDataEntries(res) => {
res.map_err(CoreError::from)
.and_then(|entries| match entries {
MDataEntries::Unseq(data) => Ok(data),
MDataEntries::Seq(_) => Err(CoreError::ReceivedUnexpectedData),
})
}
_ => Err(CoreError::ReceivedUnexpectedEvent),
}
}
async fn list_seq_mdata_entries(
&self,
name: XorName,
tag: u64,
) -> Result<MDataSeqEntries, CoreError>
where
Self: Sized,
{
trace!("ListSeqMDataEntries for {:?}", name);
match send(
self,
Request::MData(MDataRequest::ListEntries(MDataAddress::Seq { name, tag })),
)
.await?
{
Response::ListMDataEntries(res) => {
res.map_err(CoreError::from)
.and_then(|entries| match entries {
MDataEntries::Seq(data) => Ok(data),
MDataEntries::Unseq(_) => Err(CoreError::ReceivedUnexpectedData),
})
}
_ => Err(CoreError::ReceivedUnexpectedEvent),
}
}
async fn list_mdata_keys(&self, address: MDataAddress) -> Result<BTreeSet<Vec<u8>>, CoreError>
where
Self: Sized,
{
trace!("ListMDataKeys for {:?}", address);
match send(self, Request::MData(MDataRequest::ListKeys(address))).await? {
Response::ListMDataKeys(res) => res.map_err(CoreError::from),
_ => Err(CoreError::ReceivedUnexpectedEvent),
}
}
async fn list_seq_mdata_values(
&self,
name: XorName,
tag: u64,
) -> Result<Vec<MDataSeqValue>, CoreError>
where
Self: Sized,
{
trace!("List MDataValues for {:?}", name);
match send(
self,
Request::MData(MDataRequest::ListValues(MDataAddress::Seq { name, tag })),
)
.await?
{
Response::ListMDataValues(res) => {
res.map_err(CoreError::from)
.and_then(|values| match values {
MDataValues::Seq(data) => Ok(data),
MDataValues::Unseq(_) => Err(CoreError::ReceivedUnexpectedData),
})
}
_ => Err(CoreError::ReceivedUnexpectedEvent),
}
}
async fn list_mdata_user_permissions(
&self,
address: MDataAddress,
user: PublicKey,
) -> Result<MDataPermissionSet, CoreError>
where
Self: Sized,
{
trace!("GetMDataUserPermissions for {:?}", address);
match send(
self,
Request::MData(MDataRequest::ListUserPermissions { address, user }),
)
.await?
{
Response::ListMDataUserPermissions(res) => res.map_err(CoreError::from),
_ => Err(CoreError::ReceivedUnexpectedEvent),
}
}
async fn list_unseq_mdata_values(
&self,
name: XorName,
tag: u64,
) -> Result<Vec<Vec<u8>>, CoreError>
where
Self: Sized,
{
trace!("List MDataValues for {:?}", name);
match send(
self,
Request::MData(MDataRequest::ListValues(MDataAddress::Unseq { name, tag })),
)
.await?
{
Response::ListMDataValues(res) => {
res.map_err(CoreError::from)
.and_then(|values| match values {
MDataValues::Unseq(data) => Ok(data),
MDataValues::Seq(_) => Err(CoreError::ReceivedUnexpectedData),
})
}
_ => Err(CoreError::ReceivedUnexpectedEvent),
}
}
async fn put_adata(&self, data: AData) -> Result<(), CoreError>
where
Self: Sized,
{
trace!("Put AppendOnly Data {:?}", data.name());
send_mutation(self, Request::AData(ADataRequest::Put(data))).await
}
async fn get_adata(&self, address: ADataAddress) -> Result<AData, CoreError>
where
Self: Sized,
{
trace!("Get AppendOnly Data at {:?}", address.name());
match send(self, Request::AData(ADataRequest::Get(address))).await? {
Response::GetAData(res) => res.map_err(CoreError::from),
_ => Err(CoreError::ReceivedUnexpectedEvent),
}
}
async fn get_adata_shell(
&self,
data_index: ADataIndex,
address: ADataAddress,
) -> Result<AData, CoreError>
where
Self: Sized,
{
trace!("Get AppendOnly Data at {:?}", address.name());
match send(
self,
Request::AData(ADataRequest::GetShell {
address,
data_index,
}),
)
.await?
{
Response::GetADataShell(res) => res.map_err(CoreError::from),
_ => Err(CoreError::ReceivedUnexpectedEvent),
}
}
async fn get_adata_value(
&self,
address: ADataAddress,
key: Vec<u8>,
) -> Result<Vec<u8>, CoreError>
where
Self: Sized,
{
trace!(
"Fetch Value for the provided key from AppendOnly Data at {:?}",
address.name()
);
match send(
self,
Request::AData(ADataRequest::GetValue { address, key }),
)
.await?
{
Response::GetADataValue(res) => res.map_err(CoreError::from),
_ => Err(CoreError::ReceivedUnexpectedEvent),
}
}
async fn get_adata_range(
&self,
address: ADataAddress,
range: (ADataIndex, ADataIndex),
) -> Result<ADataEntries, CoreError>
where
Self: Sized,
{
trace!(
"Get Range of entries from AppendOnly Data at {:?}",
address.name()
);
match send(
self,
Request::AData(ADataRequest::GetRange { address, range }),
)
.await?
{
Response::GetADataRange(res) => res.map_err(CoreError::from),
_ => Err(CoreError::ReceivedUnexpectedEvent),
}
}
async fn get_adata_indices(&self, address: ADataAddress) -> Result<ADataIndices, CoreError>
where
Self: Sized,
{
trace!(
"Get latest indices from AppendOnly Data at {:?}",
address.name()
);
match send(self, Request::AData(ADataRequest::GetIndices(address))).await? {
Response::GetADataIndices(res) => res.map_err(CoreError::from),
_ => Err(CoreError::ReceivedUnexpectedEvent),
}
}
async fn get_adata_last_entry(&self, address: ADataAddress) -> Result<ADataEntry, CoreError>
where
Self: Sized,
{
trace!(
"Get latest indices from AppendOnly Data at {:?}",
address.name()
);
match send(self, Request::AData(ADataRequest::GetLastEntry(address))).await? {
Response::GetADataLastEntry(res) => res.map_err(CoreError::from),
_ => Err(CoreError::ReceivedUnexpectedEvent),
}
}
async fn get_unpub_adata_permissions_at_index(
&self,
address: ADataAddress,
permissions_index: ADataIndex,
) -> Result<ADataUnpubPermissions, CoreError>
where
Self: Sized,
{
trace!(
"Get latest indices from AppendOnly Data at {:?}",
address.name()
);
match send(
self,
Request::AData(ADataRequest::GetPermissions {
address,
permissions_index,
}),
)
.await?
{
Response::GetADataPermissions(res) => {
res.map_err(CoreError::from)
.and_then(|permissions| match permissions {
ADataPermissions::Unpub(data) => Ok(data),
ADataPermissions::Pub(_) => Err(CoreError::ReceivedUnexpectedData),
})
}
_ => Err(CoreError::ReceivedUnexpectedEvent),
}
}
async fn get_pub_adata_permissions_at_index(
&self,
address: ADataAddress,
permissions_index: ADataIndex,
) -> Result<ADataPubPermissions, CoreError>
where
Self: Sized,
{
trace!(
"Get latest indices from AppendOnly Data at {:?}",
address.name()
);
match send(
self,
Request::AData(ADataRequest::GetPermissions {
address,
permissions_index,
}),
)
.await?
{
Response::GetADataPermissions(res) => {
res.map_err(CoreError::from)
.and_then(|permissions| match permissions {
ADataPermissions::Pub(data) => Ok(data),
ADataPermissions::Unpub(_) => Err(CoreError::ReceivedUnexpectedData),
})
}
_ => Err(CoreError::ReceivedUnexpectedEvent),
}
}
async fn get_pub_adata_user_permissions(
&self,
address: ADataAddress,
permissions_index: ADataIndex,
user: ADataUser,
) -> Result<ADataPubPermissionSet, CoreError>
where
Self: Sized,
{
trace!(
"Get permissions for a specified user(s) from AppendOnly Data at {:?}",
address.name()
);
match send(
self,
Request::AData(ADataRequest::GetPubUserPermissions {
address,
permissions_index,
user,
}),
)
.await?
{
Response::GetPubADataUserPermissions(res) => res.map_err(CoreError::from),
_ => Err(CoreError::ReceivedUnexpectedEvent),
}
}
async fn get_unpub_adata_user_permissions(
&self,
address: ADataAddress,
permissions_index: ADataIndex,
public_key: PublicKey,
) -> Result<ADataUnpubPermissionSet, CoreError>
where
Self: Sized,
{
trace!(
"Get permissions for a specified user(s) from AppendOnly Data at {:?}",
address.name()
);
match send(
self,
Request::AData(ADataRequest::GetUnpubUserPermissions {
address,
permissions_index,
public_key,
}),
)
.await?
{
Response::GetUnpubADataUserPermissions(res) => res.map_err(CoreError::from),
_ => Err(CoreError::ReceivedUnexpectedEvent),
}
}
async fn add_unpub_adata_permissions(
&self,
address: ADataAddress,
permissions: ADataUnpubPermissions,
permissions_index: u64,
) -> Result<(), CoreError>
where
Self: Sized,
{
trace!(
"Add Permissions to UnPub AppendOnly Data {:?}",
address.name()
);
send_mutation(
self,
Request::AData(ADataRequest::AddUnpubPermissions {
address,
permissions,
permissions_index,
}),
)
.await
}
async fn add_pub_adata_permissions(
&self,
address: ADataAddress,
permissions: ADataPubPermissions,
permissions_index: u64,
) -> Result<(), CoreError>
where
Self: Sized,
{
trace!("Add Permissions to AppendOnly Data {:?}", address.name());
send_mutation(
self,
Request::AData(ADataRequest::AddPubPermissions {
address,
permissions,
permissions_index,
}),
)
.await
}
async fn set_adata_owners(
&self,
address: ADataAddress,
owner: ADataOwner,
owners_index: u64,
) -> Result<(), CoreError>
where
Self: Sized,
{
trace!("Set Owners to AppendOnly Data {:?}", address.name());
send_mutation(
self,
Request::AData(ADataRequest::SetOwner {
address,
owner,
owners_index,
}),
)
.await
}
async fn get_adata_owners(
&self,
address: ADataAddress,
owners_index: ADataIndex,
) -> Result<ADataOwner, CoreError>
where
Self: Sized,
{
trace!("Get Owners from AppendOnly Data at {:?}", address.name());
match send(
self,
Request::AData(ADataRequest::GetOwners {
address,
owners_index,
}),
)
.await?
{
Response::GetADataOwners(res) => res.map_err(CoreError::from),
_ => Err(CoreError::ReceivedUnexpectedEvent),
}
}
async fn append_seq_adata(
&self,
append: ADataAppendOperation,
index: u64,
) -> Result<(), CoreError>
where
Self: Sized,
{
send_mutation(
self,
Request::AData(ADataRequest::AppendSeq { append, index }),
)
.await
}
async fn append_unseq_adata(&self, append: ADataAppendOperation) -> Result<(), CoreError>
where
Self: Sized,
{
send_mutation(self, Request::AData(ADataRequest::AppendUnseq(append))).await
}
async fn list_mdata_permissions(
&self,
address: MDataAddress,
) -> Result<BTreeMap<PublicKey, MDataPermissionSet>, CoreError>
where
Self: Sized,
{
trace!("List MDataPermissions for {:?}", address);
match send(self, Request::MData(MDataRequest::ListPermissions(address))).await? {
Response::ListMDataPermissions(res) => res.map_err(CoreError::from),
_ => Err(CoreError::ReceivedUnexpectedEvent),
}
}
async fn set_mdata_user_permissions(
&self,
address: MDataAddress,
user: PublicKey,
permissions: MDataPermissionSet,
version: u64,
) -> Result<(), CoreError>
where
Self: Sized,
{
trace!("SetMDataUserPermissions for {:?}", address);
send_mutation(
self,
Request::MData(MDataRequest::SetUserPermissions {
address,
user,
permissions,
version,
}),
)
.await
}
async fn del_mdata_user_permissions(
&self,
address: MDataAddress,
user: PublicKey,
version: u64,
) -> Result<(), CoreError>
where
Self: Sized,
{
trace!("DelMDataUserPermissions for {:?}", address);
send_mutation(
self,
Request::MData(MDataRequest::DelUserPermissions {
address,
user,
version,
}),
)
.await
}
#[allow(unused)]
fn change_mdata_owner(
&self,
name: XorName,
tag: u64,
new_owner: PublicKey,
version: u64,
) -> Result<(), CoreError> {
unimplemented!();
}
#[cfg(any(
all(test, feature = "mock-network"),
all(feature = "testing", feature = "mock-network")
))]
#[doc(hidden)]
async fn set_network_limits(&self, max_ops_count: Option<u64>) {
let inner = self.inner();
inner
.lock()
.await
.connection_manager
.set_network_limits(max_ops_count);
}
#[cfg(any(
all(test, feature = "mock-network"),
all(feature = "testing", feature = "mock-network")
))]
#[doc(hidden)]
async fn simulate_network_disconnect(&self) {
let inner = self.inner();
inner
.lock()
.await
.connection_manager
.simulate_disconnect()
.await;
}
#[cfg(any(
all(test, feature = "mock-network"),
all(feature = "testing", feature = "mock-network")
))]
#[doc(hidden)]
async fn set_simulate_timeout(&self, enabled: bool) {
let inner = self.inner();
inner
.lock()
.await
.connection_manager
.set_simulate_timeout(enabled);
}
#[cfg(any(test, feature = "testing"))]
async fn test_set_balance(
&self,
client_id: Option<&ClientFullId>,
amount: Coins,
) -> Result<(), CoreError>
where
Self: Sized,
{
let new_balance_owner = match client_id {
None => self.public_key().await,
Some(client_id) => *client_id.public_id().public_key(),
};
trace!(
"Set the coin balance of {:?} to {:?}",
new_balance_owner,
amount,
);
match send_as_helper(
self,
Request::Coins(CoinsRequest::CreateBalance {
new_balance_owner,
amount,
transaction_id: rand::random(),
}),
client_id,
)
.await
{
Ok(res) => match res {
Response::Transaction(result) => match result {
Ok(_) => Ok(()),
Err(error) => Err(CoreError::from(error)),
},
_ => Err(CoreError::ReceivedUnexpectedEvent),
},
Err(error) => Err(error),
}
}
}
async fn temp_client<F, R>(identity: &ClientFullId, mut func: F) -> Result<R, CoreError>
where
F: FnMut(&mut ConnectionManager, &SafeKey) -> Result<R, CoreError>,
{
let full_id = SafeKey::client(identity.clone());
let (net_tx, _net_rx) = mpsc::unbounded();
let mut cm = attempt_bootstrap(&Config::new().quic_p2p, &net_tx, full_id.clone()).await?;
let res = func(&mut cm, &full_id);
cm.disconnect(&full_id.public_id()).await?;
res
}
pub async fn test_create_balance(owner: &ClientFullId, amount: Coins) -> Result<(), CoreError> {
trace!("Create test balance of {} for {:?}", amount, owner);
temp_client(owner, move |mut cm, full_id| {
let new_balance_owner = match full_id.public_id() {
PublicId::Client(id) => *id.public_key(),
x => return Err(CoreError::from(format!("Unexpected ID type {:?}", x))),
};
let response = futures::executor::block_on(req(
&mut cm,
Request::Coins(CoinsRequest::CreateBalance {
new_balance_owner,
amount,
transaction_id: rand::random(),
}),
&full_id,
))?;
match response {
Response::Transaction(res) => res.map(|_| Ok(()))?,
_ => Err(CoreError::from("Unexpected response")),
}
})
.await
}
pub async fn wallet_get_balance(wallet_sk: &ClientFullId) -> Result<Coins, CoreError> {
trace!("Get balance for {:?}", wallet_sk);
temp_client(
wallet_sk,
move |mut cm, full_id| match futures::executor::block_on(req(
&mut cm,
Request::Coins(CoinsRequest::GetBalance),
&full_id,
))? {
Response::GetBalance(res) => res.map_err(CoreError::from),
_ => Err(CoreError::from("Unexpected response")),
},
)
.await
}
pub async fn wallet_create_balance(
client_id: &ClientFullId,
new_balance_owner: PublicKey,
amount: Coins,
transaction_id: Option<u64>,
) -> Result<Transaction, CoreError> {
trace!(
"Create a new coin balance for {:?} with {} coins.",
new_balance_owner,
amount
);
let transaction_id = transaction_id.unwrap_or_else(rand::random);
temp_client(client_id, move |mut cm, full_id| {
let response = futures::executor::block_on(req(
&mut cm,
Request::Coins(CoinsRequest::CreateBalance {
new_balance_owner,
amount,
transaction_id,
}),
&full_id,
))?;
match response {
Response::Transaction(res) => res.map_err(CoreError::from),
_ => Err(CoreError::from("Unexpected response")),
}
})
.await
}
pub async fn wallet_transfer_coins(
client_id: &ClientFullId,
destination: XorName,
amount: Coins,
transaction_id: Option<u64>,
) -> Result<Transaction, CoreError> {
trace!("Transfer {} coins to {:?}", amount, destination);
let transaction_id = transaction_id.unwrap_or_else(rand::random);
temp_client(client_id, move |mut cm, full_id| {
let response = futures::executor::block_on(req(
&mut cm,
Request::Coins(CoinsRequest::Transfer {
destination,
amount,
transaction_id,
}),
&full_id,
))?;
match response {
Response::Transaction(res) => res.map_err(CoreError::from),
_ => Err(CoreError::from("Unexpected response")),
}
})
.await
}
#[async_trait]
pub trait AuthActions: Client + Clone + 'static {
async fn list_auth_keys_and_version(
&self,
) -> Result<(BTreeMap<PublicKey, AppPermissions>, u64), CoreError>
where
Self: Sized,
{
trace!("ListAuthKeysAndVersion");
match send(self, Request::Client(ClientRequest::ListAuthKeysAndVersion)).await? {
Response::ListAuthKeysAndVersion(res) => res.map_err(CoreError::from),
_ => Err(CoreError::ReceivedUnexpectedEvent),
}
}
async fn ins_auth_key(
&self,
key: PublicKey,
permissions: AppPermissions,
version: u64,
) -> Result<(), CoreError>
where
Self: Sized,
{
trace!("InsAuthKey ({:?})", key);
send_mutation(
self,
Request::Client(ClientRequest::InsAuthKey {
key,
permissions,
version,
}),
)
.await
}
async fn del_auth_key(&self, key: PublicKey, version: u64) -> Result<(), CoreError>
where
Self: Sized,
{
trace!("DelAuthKey ({:?})", key);
send_mutation(
self,
Request::Client(ClientRequest::DelAuthKey { key, version }),
)
.await
}
async fn delete_mdata(&self, address: MDataAddress) -> Result<(), CoreError>
where
Self: Sized,
{
trace!("Delete entire Mutable Data at {:?}", address);
send_mutation(self, Request::MData(MDataRequest::Delete(address))).await
}
async fn delete_adata(&self, address: ADataAddress) -> Result<(), CoreError>
where
Self: Sized,
{
trace!("Delete entire Unpublished AppendOnly Data at {:?}", address);
send_mutation(self, Request::AData(ADataRequest::Delete(address))).await
}
}
fn sign_request(request: Request, client_id: &ClientFullId) -> Message {
let message_id = MessageId::new();
let signature = Some(client_id.sign(&unwrap!(bincode::serialize(&(&request, message_id)))));
Message::Request {
request,
message_id,
signature,
}
}
#[allow(unused)]
pub struct Inner {
connection_manager: ConnectionManager,
cache: LruCache<IDataAddress, IData>,
timeout: Duration,
net_tx: NetworkTx,
}
impl Inner {
#[allow(clippy::too_many_arguments)]
pub fn new(
connection_manager: ConnectionManager,
cache: LruCache<IDataAddress, IData>,
timeout: Duration,
net_tx: NetworkTx,
) -> Inner
where
Self: Sized,
{
Self {
connection_manager,
cache,
timeout,
net_tx,
}
}
pub fn cm(&mut self) -> &mut ConnectionManager
where
Self: Sized,
{
&mut self.connection_manager
}
}
pub async fn req(
cm: &mut ConnectionManager,
request: Request,
full_id_new: &SafeKey,
) -> Result<Response, CoreError> {
let message_id = MessageId::new();
let signature = full_id_new.sign(&unwrap!(bincode::serialize(&(&request, message_id))));
cm.send(
&full_id_new.public_id(),
&Message::Request {
request,
message_id,
signature: Some(signature),
},
)
.await
}
pub async fn attempt_bootstrap(
qp2p_config: &QuicP2pConfig,
net_tx: &NetworkTx,
safe_key: SafeKey,
) -> Result<ConnectionManager, CoreError> {
let mut attempts: u32 = 0;
loop {
let mut connection_manager = ConnectionManager::new(qp2p_config.clone(), &net_tx.clone())?;
let res = connection_manager.bootstrap(safe_key.clone()).await;
match res {
Ok(()) => return Ok(connection_manager),
Err(err) => {
attempts += 1;
if attempts < 3 {
trace!("Error connecting to network! Retrying... ({})", attempts);
} else {
return Err(err);
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::utils::generate_random_vector;
use crate::utils::test_utils::{
calculate_new_balance, gen_bls_keypair, gen_client_id, random_client,
};
use safe_nd::{
ADataAction, ADataEntry, ADataKind, ADataOwner, ADataUnpubPermissionSet,
ADataUnpubPermissions, AppendOnlyData, Coins, Error as SndError, MDataAction, MDataKind,
PubImmutableData, PubSeqAppendOnlyData, SeqAppendOnly, UnpubImmutableData,
UnpubSeqAppendOnlyData, UnpubUnseqAppendOnlyData, UnseqAppendOnly, XorName,
};
use std::str::FromStr;
#[tokio::test]
async fn pub_idata_test() -> Result<(), CoreError> {
let client = random_client()?;
let start_bal = unwrap!(Coins::from_str("10"));
let value = unwrap!(generate_random_vector::<u8>(10));
let data = PubImmutableData::new(value.clone());
let address = *data.address();
let pk = gen_bls_keypair().public_key();
let test_data = UnpubImmutableData::new(value, pk);
let res = client
.get_idata(address)
.await;
match res {
Ok(data) => panic!("Pub idata should not exist yet: {:?}", data),
Err(CoreError::DataError(SndError::NoSuchData)) => (),
Err(e) => panic!("Unexpected: {:?}", e),
}
client.put_idata(data.clone()).await?;
let res = client.put_idata(test_data.clone()).await;
match res {
Ok(_) => panic!("Unexpected Success: Validating owners should fail"),
Err(CoreError::DataError(SndError::InvalidOwners)) => (),
Err(e) => panic!("Unexpected: {:?}", e),
}
let balance = client.get_balance(None).await?;
let expected_bal = calculate_new_balance(start_bal, Some(2), None);
assert_eq!(balance, expected_bal);
let fetched_data = client.get_idata(address).await?;
assert_eq!(*fetched_data.address(), address);
Ok(())
}
#[tokio::test]
async fn unpub_idata_test() -> Result<(), CoreError> {
crate::utils::test_utils::init_log();
let start_bal = unwrap!(Coins::from_str("10"));
let client = random_client()?;
let client9 = client.clone();
let value = unwrap!(generate_random_vector::<u8>(10));
let data = UnpubImmutableData::new(value.clone(), client.public_key().await);
let data2 = data.clone();
let data3 = data.clone();
let address = *data.address();
assert_eq!(address, *data2.address());
let pub_data = PubImmutableData::new(value);
let res = client
.get_idata(address)
.await;
match res {
Ok(_) => panic!("Unpub idata should not exist yet"),
Err(CoreError::DataError(SndError::NoSuchData)) => (),
Err(e) => panic!("Unexpected: {:?}", e),
}
client.put_idata(data.clone()).await?;
let res = client.put_idata(data2.clone()).await;
match res {
Err(CoreError::DataError(SndError::DataExists)) => (),
res => panic!("Unexpected: {:?}", res),
}
let balance = client.get_balance(None).await?;
let expected_bal = calculate_new_balance(start_bal, Some(2), None);
assert_eq!(balance, expected_bal);
client.put_idata(pub_data).await?;
let fetched_data = client.get_idata(address).await?;
assert_eq!(*fetched_data.address(), address);
client.del_unpub_idata(*address.name()).await?;
let res = client.get_idata(address).await;
match res {
Ok(_) => panic!("Unpub idata still exists after deletion"),
Err(CoreError::DataError(SndError::NoSuchData)) => (),
Err(e) => panic!("Unexpected: {:?}", e),
}
client9.put_idata(data3.clone()).await?;
Ok(())
}
#[tokio::test]
pub async fn unseq_mdata_test() -> Result<(), CoreError> {
let client = random_client()?;
let name = XorName(rand::random());
let tag = 15001;
let mut entries: BTreeMap<Vec<u8>, Vec<u8>> = Default::default();
let mut permissions: BTreeMap<_, _> = Default::default();
let permission_set = MDataPermissionSet::new().allow(MDataAction::Read);
let _ = permissions.insert(client.public_key().await, permission_set);
let _ = entries.insert(b"key".to_vec(), b"value".to_vec());
let entries_keys = entries.keys().cloned().collect();
let entries_values: Vec<Vec<u8>> = entries.values().cloned().collect();
let data = UnseqMutableData::new_with_data(
name,
tag,
entries.clone(),
permissions,
client.public_key().await,
);
client.put_unseq_mutable_data(data.clone()).await?;
println!("Put unseq. MData successfully");
let version = client
.get_mdata_version(MDataAddress::Unseq { name, tag })
.await?;
assert_eq!(version, 0);
let fetched_entries = client.list_unseq_mdata_entries(name, tag).await?;
assert_eq!(fetched_entries, entries);
let keys = client
.list_mdata_keys(MDataAddress::Unseq { name, tag })
.await?;
assert_eq!(keys, entries_keys);
let values = client.list_unseq_mdata_values(name, tag).await?;
assert_eq!(values, entries_values);
let fetched_data = client.get_unseq_mdata(*data.name(), data.tag()).await?;
assert_eq!(fetched_data.name(), data.name());
assert_eq!(fetched_data.tag(), data.tag());
Ok(())
}
#[tokio::test]
pub async fn seq_mdata_test() -> Result<(), CoreError> {
let client = random_client()?;
let name = XorName(rand::random());
let tag = 15001;
let mut entries: MDataSeqEntries = Default::default();
let _ = entries.insert(
b"key".to_vec(),
MDataSeqValue {
data: b"value".to_vec(),
version: 0,
},
);
let entries_keys = entries.keys().cloned().collect();
let entries_values: Vec<MDataSeqValue> = entries.values().cloned().collect();
let mut permissions: BTreeMap<_, _> = Default::default();
let permission_set = MDataPermissionSet::new().allow(MDataAction::Read);
let _ = permissions.insert(client.public_key().await, permission_set);
let data = SeqMutableData::new_with_data(
name,
tag,
entries.clone(),
permissions,
client.public_key().await,
);
client.put_seq_mutable_data(data.clone()).await?;
println!("Put seq. MData successfully");
let fetched_entries = client.list_seq_mdata_entries(name, tag).await?;
assert_eq!(fetched_entries, entries);
let mdata_shell = client.get_seq_mdata_shell(name, tag).await?;
assert_eq!(*mdata_shell.name(), name);
assert_eq!(mdata_shell.tag(), tag);
assert_eq!(mdata_shell.entries().len(), 0);
let keys = client
.list_mdata_keys(MDataAddress::Seq { name, tag })
.await?;
assert_eq!(keys, entries_keys);
let values = client.list_seq_mdata_values(name, tag).await?;
assert_eq!(values, entries_values);
let fetched_data = client.get_seq_mdata(name, tag).await?;
assert_eq!(fetched_data.name(), data.name());
assert_eq!(fetched_data.tag(), data.tag());
assert_eq!(fetched_data.entries().len(), 1);
Ok(())
}
#[tokio::test]
pub async fn del_seq_mdata_test() -> Result<(), CoreError> {
let client = random_client()?;
let name = XorName(rand::random());
let tag = 15001;
let mdataref = MDataAddress::Seq { name, tag };
let data = SeqMutableData::new_with_data(
name,
tag,
Default::default(),
Default::default(),
client.public_key().await,
);
client.put_seq_mutable_data(data.clone()).await?;
client.delete_mdata(mdataref).await?;
let res = client.get_unseq_mdata(*data.name(), data.tag()).await;
match res {
Err(CoreError::DataError(SndError::NoSuchData)) => (),
_ => panic!("Unexpected success"),
}
Ok(())
}
#[tokio::test]
pub async fn del_unseq_mdata_test() -> Result<(), CoreError> {
let client = random_client()?;
let name = XorName(rand::random());
let tag = 15001;
let mdataref = MDataAddress::Unseq { name, tag };
let data = UnseqMutableData::new_with_data(
name,
tag,
Default::default(),
Default::default(),
client.public_key().await,
);
client.put_unseq_mutable_data(data.clone()).await?;
client.delete_mdata(mdataref).await?;
let res = client.get_unseq_mdata(*data.name(), data.tag()).await;
match res {
Err(CoreError::DataError(SndError::NoSuchData)) => (),
_ => panic!("Unexpected success"),
}
Ok(())
}
#[tokio::test]
async fn coin_permissions() -> Result<(), CoreError> {
let client = random_client()?;
let wallet_a_addr: XorName = client.public_key().await.into();
let res = client
.transfer_coins(None, rand::random(), unwrap!(Coins::from_str("5.0")), None)
.await;
match res {
Err(CoreError::DataError(SndError::NoSuchBalance)) => (),
res => panic!("Unexpected result: {:?}", res),
}
let client = random_client()?;
let res = client.get_balance(None).await;
let expected_amt = unwrap!(Coins::from_str("10")
.ok()
.and_then(|x| x.checked_sub(COST_OF_PUT)));
match res {
Ok(fetched_amt) => assert_eq!(expected_amt, fetched_amt),
res => panic!("Unexpected result: {:?}", res),
}
client
.test_set_balance(None, unwrap!(Coins::from_str("50.0")))
.await?;
let res = client
.transfer_coins(None, wallet_a_addr, unwrap!(Coins::from_str("10")), None)
.await;
match res {
Ok(transaction) => assert_eq!(transaction.amount, unwrap!(Coins::from_str("10"))),
res => panic!("Unexpected error: {:?}", res),
}
let res = client.get_balance(None).await;
let expected_amt = unwrap!(Coins::from_str("40"));
match res {
Ok(fetched_amt) => assert_eq!(expected_amt, fetched_amt),
res => panic!("Unexpected result: {:?}", res),
}
Ok(())
}
#[tokio::test]
async fn anonymous_wallet() -> Result<(), CoreError> {
let client = random_client()?;
let wallet1: XorName = client.owner_key().await.into();
let init_bal = unwrap!(Coins::from_str("500.0"));
let client_id = gen_client_id();
let bls_pk = *client_id.public_id().public_key();
client.test_set_balance(None, init_bal).await?;
let transaction = client
.create_balance(None, bls_pk, unwrap!(Coins::from_str("100.0")), None)
.await?;
assert_eq!(transaction.amount, unwrap!(Coins::from_str("100")));
let transaction = client
.transfer_coins(
Some(&client_id.clone()),
wallet1,
unwrap!(Coins::from_str("5.0")),
None,
)
.await?;
assert_eq!(transaction.amount, unwrap!(Coins::from_str("5.0")));
let balance = client.get_balance(Some(&client_id)).await?;
assert_eq!(balance, unwrap!(Coins::from_str("95.0")));
let balance = client.get_balance(None).await?;
let expected =
calculate_new_balance(init_bal, Some(1), Some(unwrap!(Coins::from_str("95"))));
assert_eq!(balance, expected);
let random_pk = gen_bls_keypair().public_key();
let random_source = gen_client_id();
let res = client
.create_balance(
Some(&random_source),
random_pk,
unwrap!(Coins::from_str("100.0")),
None,
)
.await;
match res {
Err(CoreError::DataError(SndError::NoSuchBalance)) => {}
res => panic!("Unexpected result: {:?}", res),
}
Ok(())
}
#[tokio::test]
async fn coin_balance_transfer() -> Result<(), CoreError> {
let client = random_client()?;
let owner_key = client.owner_key().await;
let wallet1: XorName = owner_key.into();
client
.test_set_balance(None, unwrap!(Coins::from_str("100.0")))
.await?;
let balance = client.get_balance(None).await?;
assert_eq!(balance, unwrap!(Coins::from_str("100.0")));
let client = random_client()?;
let init_bal = unwrap!(Coins::from_str("10"));
let orig_balance = client.get_balance(None).await?;
let _ = client
.transfer_coins(None, wallet1, unwrap!(Coins::from_str("5.0")), None)
.await?;
let new_balance = client.get_balance(None).await?;
assert_eq!(
new_balance,
unwrap!(orig_balance.checked_sub(unwrap!(Coins::from_str("5.0")))),
);
let res = client
.transfer_coins(None, wallet1, unwrap!(Coins::from_str("5000")), None)
.await;
match res {
Err(CoreError::DataError(SndError::InsufficientBalance)) => (),
res => panic!("Unexpected result: {:?}", res),
};
let balance = client.get_balance(None).await?;
let expected =
calculate_new_balance(init_bal, Some(1), Some(unwrap!(Coins::from_str("5"))));
assert_eq!(balance, expected);
let res = client
.transfer_coins(None, wallet1, unwrap!(Coins::from_str("0")), None)
.await;
match res {
Err(CoreError::DataError(SndError::InvalidOperation)) => (),
res => panic!("Unexpected result: {:?}", res),
}
client
.test_set_balance(None, unwrap!(Coins::from_str("0")))
.await?;
let data = PubImmutableData::new(unwrap!(generate_random_vector::<u8>(10)));
let res = client.put_idata(data).await;
match res {
Err(CoreError::DataError(SndError::InsufficientBalance)) => (),
res => panic!("Unexpected result: {:?}", res),
};
Ok(())
}
#[tokio::test]
pub async fn del_unseq_mdata_permission_test() -> Result<(), CoreError> {
let name = XorName(rand::random());
let tag = 15001;
let mdataref = MDataAddress::Unseq { name, tag };
let client = random_client()?;
let data = UnseqMutableData::new_with_data(
name,
tag,
Default::default(),
Default::default(),
client.public_key().await,
);
client.put_unseq_mutable_data(data).await?;
let client = random_client()?;
let res = client.delete_mdata(mdataref).await;
match res {
Err(CoreError::DataError(SndError::AccessDenied)) => (),
res => panic!("Unexpected result: {:?}", res),
}
Ok(())
}
#[tokio::test]
pub async fn mdata_permissions_test() -> Result<(), CoreError> {
let client = random_client()?;
let start_bal = unwrap!(Coins::from_str("10"));
let name = XorName(rand::random());
let tag = 15001;
let mut permissions: BTreeMap<_, _> = Default::default();
let permission_set = MDataPermissionSet::new()
.allow(MDataAction::Read)
.allow(MDataAction::Insert)
.allow(MDataAction::ManagePermissions);
let user = client.public_key().await;
let random_user = gen_bls_keypair().public_key();
let random_pk = gen_bls_keypair().public_key();
let _ = permissions.insert(user, permission_set.clone());
let _ = permissions.insert(random_user, permission_set);
let data = SeqMutableData::new_with_data(
name,
tag,
Default::default(),
permissions.clone(),
client.public_key().await,
);
let test_data = SeqMutableData::new_with_data(
XorName(rand::random()),
15000,
Default::default(),
permissions,
random_pk,
);
client.put_seq_mutable_data(data).await?;
let res = client.put_seq_mutable_data(test_data.clone()).await;
match res {
Err(CoreError::DataError(SndError::InvalidOwners)) => (),
Ok(_) => panic!("Unexpected Success: Validating owners should fail"),
Err(e) => panic!("Unexpected: {:?}", e),
};
let balance = client.get_balance(None).await?;
let expected_bal = calculate_new_balance(start_bal, Some(2), None);
assert_eq!(balance, expected_bal);
let new_perm_set = MDataPermissionSet::new()
.allow(MDataAction::ManagePermissions)
.allow(MDataAction::Read);
client
.set_mdata_user_permissions(MDataAddress::Seq { name, tag }, user, new_perm_set, 1)
.await?;
println!("Modified user permissions");
let permissions = client
.list_mdata_user_permissions(MDataAddress::Seq { name, tag }, user)
.await?;
assert!(!permissions.is_allowed(MDataAction::Insert));
assert!(permissions.is_allowed(MDataAction::Read));
assert!(permissions.is_allowed(MDataAction::ManagePermissions));
println!("Verified new permissions");
client
.del_mdata_user_permissions(MDataAddress::Seq { name, tag }, random_user, 2)
.await?;
println!("Deleted permissions");
let permissions = client
.list_mdata_permissions(MDataAddress::Seq { name, tag })
.await?;
assert_eq!(permissions.len(), 1);
println!("Permission set verified");
Ok(())
}
#[tokio::test]
pub async fn mdata_mutations_test() -> Result<(), CoreError> {
let client = random_client()?;
let name = XorName(rand::random());
let tag = 15001;
let mut permissions: BTreeMap<_, _> = Default::default();
let permission_set = MDataPermissionSet::new()
.allow(MDataAction::Read)
.allow(MDataAction::Insert)
.allow(MDataAction::Update)
.allow(MDataAction::Delete);
let user = client.public_key().await;
let _ = permissions.insert(user, permission_set);
let mut entries: MDataSeqEntries = Default::default();
let _ = entries.insert(
b"key1".to_vec(),
MDataSeqValue {
data: b"value".to_vec(),
version: 0,
},
);
let _ = entries.insert(
b"key2".to_vec(),
MDataSeqValue {
data: b"value".to_vec(),
version: 0,
},
);
let data = SeqMutableData::new_with_data(
name,
tag,
entries.clone(),
permissions,
client.public_key().await,
);
client.put_seq_mutable_data(data).await?;
println!("Put seq. MData successfully");
let fetched_entries = client.list_seq_mdata_entries(name, tag).await?;
assert_eq!(fetched_entries, entries);
let entry_actions: MDataSeqEntryActions = MDataSeqEntryActions::new()
.update(b"key1".to_vec(), b"newValue".to_vec(), 1)
.del(b"key2".to_vec(), 1)
.ins(b"key3".to_vec(), b"value".to_vec(), 0);
client
.mutate_seq_mdata_entries(name, tag, entry_actions)
.await?;
let fetched_entries = client.list_seq_mdata_entries(name, tag).await?;
let mut expected_entries: BTreeMap<_, _> = Default::default();
let _ = expected_entries.insert(
b"key1".to_vec(),
MDataSeqValue {
data: b"newValue".to_vec(),
version: 1,
},
);
let _ = expected_entries.insert(
b"key3".to_vec(),
MDataSeqValue {
data: b"value".to_vec(),
version: 0,
},
);
assert_eq!(fetched_entries, expected_entries);
let fetched_value = client
.get_seq_mdata_value(name, tag, b"key3".to_vec())
.await?;
assert_eq!(
fetched_value,
MDataSeqValue {
data: b"value".to_vec(),
version: 0
}
);
let res = client
.get_seq_mdata_value(name, tag, b"wrongKey".to_vec())
.await;
match res {
Ok(_) => panic!("Unexpected: Entry should not exist"),
Err(CoreError::DataError(SndError::NoSuchEntry)) => (),
Err(err) => panic!("Unexpected error: {:?}", err),
};
let client = random_client()?;
let name = XorName(rand::random());
let tag = 15001;
let mut permissions: BTreeMap<_, _> = Default::default();
let permission_set = MDataPermissionSet::new()
.allow(MDataAction::Read)
.allow(MDataAction::Insert)
.allow(MDataAction::Update)
.allow(MDataAction::Delete);
let user = client.public_key().await;
let _ = permissions.insert(user, permission_set);
let mut entries: BTreeMap<Vec<u8>, Vec<u8>> = Default::default();
let _ = entries.insert(b"key1".to_vec(), b"value".to_vec());
let _ = entries.insert(b"key2".to_vec(), b"value".to_vec());
let data = UnseqMutableData::new_with_data(
name,
tag,
entries.clone(),
permissions,
client.public_key().await,
);
client.put_unseq_mutable_data(data).await?;
println!("Put unseq. MData successfully");
let fetched_entries = client.list_unseq_mdata_entries(name, tag).await?;
assert_eq!(fetched_entries, entries);
let entry_actions: MDataUnseqEntryActions = MDataUnseqEntryActions::new()
.update(b"key1".to_vec(), b"newValue".to_vec())
.del(b"key2".to_vec())
.ins(b"key3".to_vec(), b"value".to_vec());
client
.mutate_unseq_mdata_entries(name, tag, entry_actions)
.await?;
let fetched_entries = client.list_unseq_mdata_entries(name, tag).await?;
let mut expected_entries: BTreeMap<_, _> = Default::default();
let _ = expected_entries.insert(b"key1".to_vec(), b"newValue".to_vec());
let _ = expected_entries.insert(b"key3".to_vec(), b"value".to_vec());
assert_eq!(fetched_entries, expected_entries);
let fetched_value = client
.get_unseq_mdata_value(name, tag, b"key1".to_vec())
.await?;
assert_eq!(fetched_value, b"newValue".to_vec());
let res = client
.get_unseq_mdata_value(name, tag, b"wrongKey".to_vec())
.await;
match res {
Ok(_) => panic!("Unexpected: Entry should not exist"),
Err(CoreError::DataError(SndError::NoSuchEntry)) => Ok(()),
Err(err) => panic!("Unexpected error: {:?}", err),
}
}
#[tokio::test]
pub async fn adata_basics_test() -> Result<(), CoreError> {
let client = random_client()?;
let name = XorName(rand::random());
let tag = 15000;
let mut data = UnpubSeqAppendOnlyData::new(name, tag);
let mut perms = BTreeMap::<PublicKey, ADataUnpubPermissionSet>::new();
let set = ADataUnpubPermissionSet::new(true, true, true);
let index = ADataIndex::FromStart(0);
let _ = perms.insert(client.public_key().await, set);
let address = ADataAddress::UnpubSeq { name, tag };
unwrap!(data.append_permissions(
ADataUnpubPermissions {
permissions: perms,
entries_index: 0,
owners_index: 0,
},
0
));
let owner = ADataOwner {
public_key: client.public_key().await,
entries_index: 0,
permissions_index: 1,
};
unwrap!(data.append_owner(owner, 0));
client.put_adata(AData::UnpubSeq(data)).await?;
let data = client.get_adata(address).await?;
match data {
AData::UnpubSeq(adata) => assert_eq!(*adata.name(), name),
_ => panic!("Unexpected data found"),
}
let data = client.get_adata_shell(index, address).await?;
match data {
AData::UnpubSeq(adata) => {
assert_eq!(*adata.name(), name);
assert_eq!(adata.tag(), tag);
assert_eq!(adata.permissions_index(), 1);
assert_eq!(adata.owners_index(), 1);
}
_ => panic!("Unexpected data found"),
}
client.delete_adata(address).await?;
let res = client.get_adata(address).await;
match res {
Ok(_) => panic!("AData was not deleted"),
Err(CoreError::DataError(SndError::NoSuchData)) => Ok(()),
Err(e) => panic!("Unexpected error: {:?}", e),
}
}
#[tokio::test]
pub async fn adata_permissions_test() -> Result<(), CoreError> {
let client = random_client()?;
let name = XorName(rand::random());
let tag = 15000;
let adataref = ADataAddress::UnpubSeq { name, tag };
let mut data = UnpubSeqAppendOnlyData::new(name, tag);
let mut perms = BTreeMap::<PublicKey, ADataUnpubPermissionSet>::new();
let set = ADataUnpubPermissionSet::new(true, true, true);
let _ = perms.insert(client.public_key().await, set);
let key1 = b"KEY1".to_vec();
let key2 = b"KEY2".to_vec();
let key3 = b"KEY3".to_vec();
let key4 = b"KEY4".to_vec();
let val1 = b"VALUE1".to_vec();
let val2 = b"VALUE2".to_vec();
let val3 = b"VALUE3".to_vec();
let val4 = b"VALUE4".to_vec();
let kvdata = vec![
ADataEntry::new(key1, val1),
ADataEntry::new(key2, val2),
ADataEntry::new(key3, val3),
];
unwrap!(data.append(kvdata, 0));
unwrap!(data.append(vec![ADataEntry::new(key4, val4)], 3));
unwrap!(data.append_permissions(
ADataUnpubPermissions {
permissions: perms,
entries_index: 4,
owners_index: 0,
},
0
));
let index_start = ADataIndex::FromStart(0);
let index_end = ADataIndex::FromEnd(2);
let perm_index = ADataIndex::FromStart(1);
let sim_client = gen_bls_keypair().public_key();
let sim_client1 = sim_client;
let mut perms2 = BTreeMap::<PublicKey, ADataUnpubPermissionSet>::new();
let set2 = ADataUnpubPermissionSet::new(true, true, false);
let _ = perms2.insert(sim_client, set2);
let perm_set = ADataUnpubPermissions {
permissions: perms2,
entries_index: 4,
owners_index: 1,
};
let owner = ADataOwner {
public_key: client.public_key().await,
entries_index: 4,
permissions_index: 1,
};
unwrap!(data.append_owner(owner, 0));
let mut test_data = UnpubSeqAppendOnlyData::new(XorName(rand::random()), 15000);
let test_owner = ADataOwner {
public_key: gen_bls_keypair().public_key(),
entries_index: 0,
permissions_index: 0,
};
unwrap!(test_data.append_owner(test_owner, 0));
client.put_adata(AData::UnpubSeq(data)).await?;
let res = client.put_adata(AData::UnpubSeq(test_data.clone())).await;
match res {
Ok(_) => panic!("Unexpected Success: Validating owners should fail"),
Err(CoreError::DataError(SndError::InvalidOwners)) => (),
Err(e) => panic!("Unexpected: {:?}", e),
};
let data = client
.get_adata_range(adataref, (index_start, index_end))
.await?;
assert_eq!(
unwrap!(std::str::from_utf8(&unwrap!(data.last()).key)),
"KEY2"
);
assert_eq!(
unwrap!(std::str::from_utf8(&unwrap!(data.last()).value)),
"VALUE2"
);
let data = client.get_adata_indices(adataref).await?;
assert_eq!(data.entries_index(), 4);
assert_eq!(data.owners_index(), 1);
assert_eq!(data.permissions_index(), 1);
let data = client.get_adata_value(adataref, b"KEY1".to_vec()).await?;
assert_eq!(unwrap!(std::str::from_utf8(data.as_slice())), "VALUE1");
let data = client.get_adata_last_entry(adataref).await?;
assert_eq!(unwrap!(std::str::from_utf8(data.key.as_slice())), "KEY4");
assert_eq!(
unwrap!(std::str::from_utf8(data.value.as_slice())),
"VALUE4"
);
client
.add_unpub_adata_permissions(adataref, perm_set, 1)
.await?;
let data = client
.get_unpub_adata_permissions_at_index(adataref, perm_index)
.await?;
let set = unwrap!(data.permissions.get(&sim_client1));
assert!(set.is_allowed(ADataAction::Append));
let set = client
.get_unpub_adata_user_permissions(adataref, index_start, client.public_key().await)
.await?;
assert!(set.is_allowed(ADataAction::Append));
Ok(())
}
#[tokio::test]
pub async fn append_seq_adata_test() -> Result<(), CoreError> {
let name = XorName(rand::random());
let tag = 10;
let client = random_client()?;
let adataref = ADataAddress::PubSeq { name, tag };
let mut data = PubSeqAppendOnlyData::new(name, tag);
let mut perms = BTreeMap::<ADataUser, ADataPubPermissionSet>::new();
let set = ADataPubPermissionSet::new(true, true);
let usr = ADataUser::Key(client.public_key().await);
let _ = perms.insert(usr, set);
unwrap!(data.append_permissions(
ADataPubPermissions {
permissions: perms,
entries_index: 0,
owners_index: 0,
},
0
));
let key1 = b"KEY1".to_vec();
let val1 = b"VALUE1".to_vec();
let key2 = b"KEY2".to_vec();
let val2 = b"VALUE2".to_vec();
let tup = vec![ADataEntry::new(key1, val1), ADataEntry::new(key2, val2)];
let append = ADataAppendOperation {
address: adataref,
values: tup,
};
let owner = ADataOwner {
public_key: client.public_key().await,
entries_index: 0,
permissions_index: 1,
};
unwrap!(data.append_owner(owner, 0));
client.put_adata(AData::PubSeq(data)).await?;
client.append_seq_adata(append, 0).await?;
let data = client.get_adata(adataref).await?;
match data {
AData::PubSeq(adata) => assert_eq!(
unwrap!(std::str::from_utf8(&unwrap!(adata.last_entry()).key)),
"KEY2"
),
_ => panic!("UNEXPECTED DATA!"),
}
Ok(())
}
#[tokio::test]
pub async fn append_unseq_adata_test() -> Result<(), CoreError> {
let name = XorName(rand::random());
let tag = 10;
let client = random_client()?;
let adataref = ADataAddress::UnpubUnseq { name, tag };
let mut data = UnpubUnseqAppendOnlyData::new(name, tag);
let mut perms = BTreeMap::<PublicKey, ADataUnpubPermissionSet>::new();
let set = ADataUnpubPermissionSet::new(true, true, true);
let _ = perms.insert(client.public_key().await, set);
unwrap!(data.append_permissions(
ADataUnpubPermissions {
permissions: perms,
entries_index: 0,
owners_index: 0,
},
0
));
let key1 = b"KEY1".to_vec();
let val1 = b"VALUE1".to_vec();
let key2 = b"KEY2".to_vec();
let val2 = b"VALUE2".to_vec();
let tup = vec![ADataEntry::new(key1, val1), ADataEntry::new(key2, val2)];
let append = ADataAppendOperation {
address: adataref,
values: tup,
};
let owner = ADataOwner {
public_key: client.public_key().await,
entries_index: 0,
permissions_index: 1,
};
unwrap!(data.append_owner(owner, 0));
client.put_adata(AData::UnpubUnseq(data)).await?;
client.append_unseq_adata(append).await?;
let data = client.get_adata(adataref).await?;
match data {
AData::UnpubUnseq(adata) => assert_eq!(
unwrap!(std::str::from_utf8(&unwrap!(adata.last_entry()).key)),
"KEY2"
),
_ => panic!("UNEXPECTED DATA!"),
}
Ok(())
}
#[tokio::test]
pub async fn set_and_get_owner_adata_test() -> Result<(), CoreError> {
let name = XorName(rand::random());
let tag = 10;
let client = random_client()?;
let adataref = ADataAddress::UnpubUnseq { name, tag };
let mut data = UnpubUnseqAppendOnlyData::new(name, tag);
let mut perms = BTreeMap::<PublicKey, ADataUnpubPermissionSet>::new();
let set = ADataUnpubPermissionSet::new(true, true, true);
let _ = perms.insert(client.public_key().await, set);
data.append_permissions(
ADataUnpubPermissions {
permissions: perms,
entries_index: 0,
owners_index: 0,
},
0,
)?;
let key1 = b"KEY1".to_vec();
let key2 = b"KEY2".to_vec();
let val1 = b"VALUE1".to_vec();
let val2 = b"VALUE2".to_vec();
let kvdata = vec![ADataEntry::new(key1, val1), ADataEntry::new(key2, val2)];
data.append(kvdata)?;
let owner = ADataOwner {
public_key: client.public_key().await,
entries_index: 2,
permissions_index: 1,
};
data.append_owner(owner, 0)?;
let owner2 = ADataOwner {
public_key: client.public_key().await,
entries_index: 2,
permissions_index: 1,
};
let owner3 = ADataOwner {
public_key: client.public_key().await,
entries_index: 2,
permissions_index: 1,
};
client.put_adata(AData::UnpubUnseq(data)).await?;
client.set_adata_owners(adataref, owner2, 1).await?;
client.set_adata_owners(adataref, owner3, 2).await?;
let data = client.get_adata(adataref).await?;
match data {
AData::UnpubUnseq(adata) => assert_eq!(adata.owners_index(), 3),
_ => panic!("UNEXPECTED DATA!"),
}
Ok(())
}
#[tokio::test]
pub async fn wallet_transactions_without_client() -> Result<(), CoreError> {
let client_id = gen_client_id();
test_create_balance(&client_id, unwrap!(Coins::from_str("50"))).await?;
let balance = wallet_get_balance(&client_id).await?;
let ten_coins = unwrap!(Coins::from_str("10"));
assert_eq!(balance, unwrap!(Coins::from_str("50")));
let new_client_id = gen_client_id();
let new_client_pk = new_client_id.public_id().public_key();
let new_wallet: XorName = *new_client_id.public_id().name();
let txn = wallet_create_balance(&client_id, *new_client_pk, ten_coins, None).await?;
assert_eq!(txn.amount, ten_coins);
let txn2 = wallet_transfer_coins(&client_id, new_wallet, ten_coins, None).await?;
assert_eq!(txn2.amount, ten_coins);
let client_balance = wallet_get_balance(&client_id).await?;
let expected = unwrap!(Coins::from_str("30"));
let expected = unwrap!(expected.checked_sub(COST_OF_PUT));
assert_eq!(client_balance, expected);
let new_client_balance = wallet_get_balance(&new_client_id).await?;
assert_eq!(new_client_balance, unwrap!(Coins::from_str("20")));
Ok(())
}
#[tokio::test]
pub async fn deletions_should_be_free() -> Result<(), CoreError> {
let name = XorName(rand::random());
let tag = 10;
let client = random_client()?;
let idata = UnpubImmutableData::new(
unwrap!(generate_random_vector::<u8>(10)),
client.public_key().await,
);
let address = *idata.name();
client.put_idata(idata).await?;
let mut adata = UnpubSeqAppendOnlyData::new(name, tag);
let owner = ADataOwner {
public_key: client.public_key().await,
entries_index: 0,
permissions_index: 0,
};
unwrap!(adata.append_owner(owner, 0));
client.put_adata(adata.into()).await?;
let mdata = UnseqMutableData::new(name, tag, client.public_key().await);
client.put_unseq_mutable_data(mdata).await?;
let balance = client.get_balance(None).await?;
client
.delete_adata(ADataAddress::from_kind(ADataKind::UnpubSeq, name, tag))
.await?;
client
.delete_mdata(MDataAddress::from_kind(MDataKind::Unseq, name, tag))
.await?;
client.del_unpub_idata(address).await?;
let new_balance = client.get_balance(None).await?;
assert_eq!(new_balance, balance);
Ok(())
}
}