pub mod account;
#[macro_use]
pub mod core_client;
pub mod mdata_info;
pub mod recovery;
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, shared_sign};
use crate::errors::CoreError;
use crate::event::{NetworkEvent, NetworkTx};
use crate::event_loop::{CoreFuture, CoreMsgTx};
use crate::ipc::BootstrapConfig;
use crate::utils::FutureExt;
use futures::{future, sync::mpsc, Future};
use lazy_static::lazy_static;
use lru_cache::LruCache;
use rust_sodium::crypto::{box_, sign};
use safe_nd::{
AData, ADataAddress, ADataAppendOperation, ADataEntries, ADataEntry, ADataIndex, ADataIndices,
ADataOwner, ADataPermissions, ADataPubPermissionSet, ADataPubPermissions,
ADataUnpubPermissionSet, ADataUnpubPermissions, ADataUser, AppPermissions, Coins, IData,
IDataAddress, LoginPacket, MData, MDataAddress, MDataEntries, MDataEntryActions,
MDataPermissionSet, MDataSeqEntries, MDataSeqEntryActions, MDataSeqValue,
MDataUnseqEntryActions, MDataValue, MDataValues, Message, MessageId, PublicId, PublicKey,
Request, Response, SeqMutableData, Signature, Transaction, UnseqMutableData, XorName,
};
use std::cell::RefCell;
use std::collections::{BTreeMap, BTreeSet};
use std::rc::Rc;
use std::time::Duration;
use threshold_crypto::SecretKey as BlsSecretKey;
use tokio::runtime::current_thread::{block_on_all, Handle};
pub const IMMUT_DATA_CACHE_SIZE: usize = 300;
lazy_static! {
pub static ref COST_OF_PUT: Coins = unwrap!(Coins::from_nano(1));
}
pub fn bootstrap_config() -> Result<BootstrapConfig, CoreError> {
Ok(Config::new().quic_p2p.hard_coded_contacts)
}
pub trait Client: Clone + 'static {
type MsgType;
fn full_id(&self) -> SafeKey;
fn public_id(&self) -> PublicId;
fn config(&self) -> Option<BootstrapConfig>;
fn inner(&self) -> Rc<RefCell<ClientInner<Self, Self::MsgType>>>;
fn public_encryption_key(&self) -> box_::PublicKey;
fn secret_encryption_key(&self) -> shared_box::SecretKey;
fn encryption_keypair(&self) -> (box_::PublicKey, shared_box::SecretKey) {
(self.public_encryption_key(), self.secret_encryption_key())
}
fn secret_symmetric_key(&self) -> shared_secretbox::Key;
fn public_signing_key(&self) -> sign::PublicKey;
fn secret_signing_key(&self) -> shared_sign::SecretKey;
fn public_bls_key(&self) -> threshold_crypto::PublicKey;
fn secret_bls_key(&self) -> BlsSecretKey;
fn compose_message(&self, request: Request, sign: bool) -> Message {
let message_id = MessageId::new();
let signature = if sign {
Some(Signature::from(
self.secret_bls_key()
.sign(&unwrap!(bincode::serialize(&(&request, message_id)))),
))
} else {
None
};
Message::Request {
request,
message_id,
signature,
}
}
fn signing_keypair(&self) -> (sign::PublicKey, shared_sign::SecretKey) {
(self.public_signing_key(), self.secret_signing_key())
}
fn owner_key(&self) -> PublicKey;
fn public_key(&self) -> PublicKey;
fn set_timeout(&self, duration: Duration) {
let inner = self.inner();
inner.borrow_mut().timeout = duration;
}
fn restart_network(&self) -> Result<(), CoreError> {
trace!("Restarting the network connection");
let inner = self.inner();
let mut inner = inner.borrow_mut();
inner.connection_manager.restart_network();
inner.net_tx.unbounded_send(NetworkEvent::Connected)?;
Ok(())
}
fn put_unseq_mutable_data(&self, data: UnseqMutableData) -> Box<CoreFuture<()>> {
trace!("Put Unsequenced MData at {:?}", data.name());
send_mutation(self, Request::PutMData(MData::Unseq(data.clone())))
}
fn transfer_coins(
&self,
secret_key: Option<&BlsSecretKey>,
destination: XorName,
amount: Coins,
transaction_id: Option<u64>,
) -> Box<CoreFuture<Transaction>> {
trace!("Transfer {} coins to {:?}", amount, destination);
send_as(
self,
Request::TransferCoins {
destination,
amount,
transaction_id: transaction_id.unwrap_or_else(rand::random),
},
secret_key,
)
.and_then(|res| match res {
Response::Transaction(res) => res.map_err(CoreError::from),
_ => Err(CoreError::ReceivedUnexpectedEvent),
})
.into_box()
}
fn create_balance(
&self,
secret_key: Option<&BlsSecretKey>,
new_balance_owner: PublicKey,
amount: Coins,
transaction_id: Option<u64>,
) -> Box<CoreFuture<Transaction>> {
trace!(
"Create a new balance for {:?} with {} coins.",
new_balance_owner,
amount
);
send_as(
self,
Request::CreateBalance {
new_balance_owner,
amount,
transaction_id: transaction_id.unwrap_or_else(rand::random),
},
secret_key,
)
.and_then(|res| match res {
Response::Transaction(res) => res.map_err(CoreError::from),
_ => Err(CoreError::ReceivedUnexpectedEvent),
})
.into_box()
}
fn insert_login_packet_for(
&self,
secret_key: Option<&BlsSecretKey>,
new_owner: PublicKey,
amount: Coins,
transaction_id: Option<u64>,
new_login_packet: LoginPacket,
) -> Box<CoreFuture<Transaction>> {
trace!(
"Insert a login packet for {:?} preloading the wallet with {} coins.",
new_owner,
amount
);
let transaction_id = transaction_id.unwrap_or_else(rand::random);
send_as(
self,
Request::CreateLoginPacketFor {
new_owner,
amount,
transaction_id,
new_login_packet,
},
secret_key,
)
.and_then(|res| match res {
Response::Transaction(res) => res.map_err(CoreError::from),
_ => Err(CoreError::ReceivedUnexpectedEvent),
})
.into_box()
}
fn get_balance(
&self,
secret_key: Option<&BlsSecretKey>,
) -> Box<CoreFuture<Coins>> {
trace!("Get balance for {:?}", secret_key);
send_as(self, Request::GetBalance, secret_key)
.and_then(|res| match res {
Response::GetBalance(res) => res.map_err(CoreError::from),
_ => Err(CoreError::ReceivedUnexpectedEvent),
})
.into_box()
}
fn put_idata(&self, data: impl Into<IData>) -> Box<CoreFuture<()>> {
let idata: IData = data.into();
trace!("Put IData at {:?}", idata.name());
send_mutation(self, Request::PutIData(idata))
}
fn get_idata(&self, address: IDataAddress) -> Box<CoreFuture<IData>> {
trace!("Fetch Immutable Data");
let inner = self.inner();
if let Some(data) = inner.borrow_mut().cache.get_mut(&address) {
trace!("ImmutableData found in cache.");
return future::ok(data.clone()).into_box();
}
let inner = Rc::downgrade(&self.inner());
send(self, Request::GetIData(address), address.is_unpub())
.and_then(|res| match res {
Response::GetIData(res) => res.map_err(CoreError::from),
_ => Err(CoreError::ReceivedUnexpectedEvent),
})
.map(move |data| {
if let Some(inner) = inner.upgrade() {
let _ = inner
.borrow_mut()
.cache
.insert(*data.address(), data.clone());
}
data
})
.into_box()
}
fn del_unpub_idata(&self, name: XorName) -> Box<CoreFuture<()>> {
let inner = self.inner();
if inner
.borrow_mut()
.cache
.remove(&IDataAddress::Unpub(name))
.is_some()
{
trace!("Deleted UnpubImmutableData from cache.");
}
let _ = Rc::downgrade(&self.inner());
trace!("Delete Unpublished IData at {:?}", name);
send_mutation(self, Request::DeleteUnpubIData(IDataAddress::Unpub(name)))
}
fn put_seq_mutable_data(&self, data: SeqMutableData) -> Box<CoreFuture<()>> {
trace!("Put Sequenced MData at {:?}", data.name());
send_mutation(self, Request::PutMData(MData::Seq(data)))
}
fn get_unseq_mdata(&self, name: XorName, tag: u64) -> Box<CoreFuture<UnseqMutableData>> {
trace!("Fetch Unsequenced Mutable Data");
send(
self,
Request::GetMData(MDataAddress::Unseq { name, tag }),
true,
)
.and_then(|res| match res {
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),
})
.into_box()
}
fn get_seq_mdata_value(
&self,
name: XorName,
tag: u64,
key: Vec<u8>,
) -> Box<CoreFuture<MDataSeqValue>> {
trace!("Fetch MDataValue for {:?}", name);
send(
self,
Request::GetMDataValue {
address: MDataAddress::Seq { name, tag },
key,
},
true,
)
.and_then(|res| match res {
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),
})
.into_box()
}
fn get_unseq_mdata_value(
&self,
name: XorName,
tag: u64,
key: Vec<u8>,
) -> Box<CoreFuture<Vec<u8>>> {
trace!("Fetch MDataValue for {:?}", name);
send(
self,
Request::GetMDataValue {
address: MDataAddress::Unseq { name, tag },
key,
},
true,
)
.and_then(|res| match res {
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),
})
.into_box()
}
fn get_seq_mdata(&self, name: XorName, tag: u64) -> Box<CoreFuture<SeqMutableData>> {
trace!("Fetch Sequenced Mutable Data");
send(
self,
Request::GetMData(MDataAddress::Seq { name, tag }),
true,
)
.and_then(|res| match res {
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),
})
.into_box()
}
fn mutate_seq_mdata_entries(
&self,
name: XorName,
tag: u64,
actions: MDataSeqEntryActions,
) -> Box<CoreFuture<()>> {
trace!("Mutate MData for {:?}", name);
send_mutation(
self,
Request::MutateMDataEntries {
address: MDataAddress::Seq { name, tag },
actions: MDataEntryActions::Seq(actions),
},
)
}
fn mutate_unseq_mdata_entries(
&self,
name: XorName,
tag: u64,
actions: MDataUnseqEntryActions,
) -> Box<CoreFuture<()>> {
trace!("Mutate MData for {:?}", name);
send_mutation(
self,
Request::MutateMDataEntries {
address: MDataAddress::Unseq { name, tag },
actions: MDataEntryActions::Unseq(actions),
},
)
}
fn get_seq_mdata_shell(&self, name: XorName, tag: u64) -> Box<CoreFuture<SeqMutableData>> {
trace!("GetMDataShell for {:?}", name);
send(
self,
Request::GetMDataShell(MDataAddress::Seq { name, tag }),
true,
)
.and_then(|res| match res {
Response::GetMDataShell(res) => {
res.map_err(CoreError::from).and_then(|mdata| match mdata {
MData::Seq(data) => Ok(data),
_ => Err(CoreError::ReceivedUnexpectedData),
})
}
_ => Err(CoreError::ReceivedUnexpectedEvent),
})
.into_box()
}
fn get_unseq_mdata_shell(&self, name: XorName, tag: u64) -> Box<CoreFuture<UnseqMutableData>> {
trace!("GetMDataShell for {:?}", name);
send(
self,
Request::GetMDataShell(MDataAddress::Unseq { name, tag }),
true,
)
.and_then(|res| match res {
Response::GetMDataShell(res) => {
res.map_err(CoreError::from).and_then(|mdata| match mdata {
MData::Unseq(data) => Ok(data),
_ => Err(CoreError::ReceivedUnexpectedData),
})
}
_ => Err(CoreError::ReceivedUnexpectedEvent),
})
.into_box()
}
fn get_mdata_version(&self, address: MDataAddress) -> Box<CoreFuture<u64>> {
trace!("GetMDataVersion for {:?}", address);
send(self, Request::GetMDataVersion(address), true)
.and_then(|res| match res {
Response::GetMDataVersion(res) => res.map_err(CoreError::from),
_ => Err(CoreError::ReceivedUnexpectedEvent),
})
.into_box()
}
fn list_unseq_mdata_entries(
&self,
name: XorName,
tag: u64,
) -> Box<CoreFuture<BTreeMap<Vec<u8>, Vec<u8>>>> {
trace!("ListMDataEntries for {:?}", name);
send(
self,
Request::ListMDataEntries(MDataAddress::Unseq { name, tag }),
true,
)
.and_then(|res| match res {
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),
})
.into_box()
}
fn list_seq_mdata_entries(&self, name: XorName, tag: u64) -> Box<CoreFuture<MDataSeqEntries>> {
trace!("ListSeqMDataEntries for {:?}", name);
send(
self,
Request::ListMDataEntries(MDataAddress::Seq { name, tag }),
true,
)
.and_then(|res| match res {
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),
})
.into_box()
}
fn list_mdata_keys(&self, address: MDataAddress) -> Box<CoreFuture<BTreeSet<Vec<u8>>>> {
trace!("ListMDataKeys for {:?}", address);
send(self, Request::ListMDataKeys(address), true)
.and_then(|res| match res {
Response::ListMDataKeys(res) => res.map_err(CoreError::from),
_ => Err(CoreError::ReceivedUnexpectedEvent),
})
.into_box()
}
fn list_seq_mdata_values(
&self,
name: XorName,
tag: u64,
) -> Box<CoreFuture<Vec<MDataSeqValue>>> {
trace!("List MDataValues for {:?}", name);
send(
self,
Request::ListMDataValues(MDataAddress::Seq { name, tag }),
true,
)
.and_then(|res| match res {
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),
})
.into_box()
}
fn list_mdata_user_permissions(
&self,
address: MDataAddress,
user: PublicKey,
) -> Box<CoreFuture<MDataPermissionSet>> {
trace!("GetMDataUserPermissions for {:?}", address);
send(
self,
Request::ListMDataUserPermissions { address, user },
true,
)
.and_then(|res| match res {
Response::ListMDataUserPermissions(res) => res.map_err(CoreError::from),
_ => Err(CoreError::ReceivedUnexpectedEvent),
})
.into_box()
}
fn list_unseq_mdata_values(&self, name: XorName, tag: u64) -> Box<CoreFuture<Vec<Vec<u8>>>> {
trace!("List MDataValues for {:?}", name);
send(
self,
Request::ListMDataValues(MDataAddress::Unseq { name, tag }),
true,
)
.and_then(|res| match res {
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),
})
.into_box()
}
fn put_adata(&self, data: AData) -> Box<CoreFuture<()>> {
trace!("Put AppendOnly Data {:?}", data.name());
send_mutation(self, Request::PutAData(data))
}
fn get_adata(&self, address: ADataAddress) -> Box<CoreFuture<AData>> {
trace!("Get AppendOnly Data at {:?}", address.name());
send(self, Request::GetAData(address), address.is_unpub())
.and_then(|res| match res {
Response::GetAData(res) => res.map_err(CoreError::from),
_ => Err(CoreError::ReceivedUnexpectedEvent),
})
.into_box()
}
fn get_adata_shell(
&self,
data_index: ADataIndex,
address: ADataAddress,
) -> Box<CoreFuture<AData>> {
trace!("Get AppendOnly Data at {:?}", address.name());
send(
self,
Request::GetADataShell {
address,
data_index,
},
address.is_unpub(),
)
.and_then(|res| match res {
Response::GetADataShell(res) => res.map_err(CoreError::from),
_ => Err(CoreError::ReceivedUnexpectedEvent),
})
.into_box()
}
fn get_adata_value(&self, address: ADataAddress, key: Vec<u8>) -> Box<CoreFuture<Vec<u8>>> {
trace!(
"Fetch Value for the provided key from AppendOnly Data at {:?}",
address.name()
);
send(
self,
Request::GetADataValue { address, key },
address.is_unpub(),
)
.and_then(|res| match res {
Response::GetADataValue(res) => res.map_err(CoreError::from),
_ => Err(CoreError::ReceivedUnexpectedEvent),
})
.into_box()
}
fn get_adata_range(
&self,
address: ADataAddress,
range: (ADataIndex, ADataIndex),
) -> Box<CoreFuture<ADataEntries>> {
trace!(
"Get Range of entries from AppendOnly Data at {:?}",
address.name()
);
send(
self,
Request::GetADataRange { address, range },
address.is_unpub(),
)
.and_then(|res| match res {
Response::GetADataRange(res) => res.map_err(CoreError::from),
_ => Err(CoreError::ReceivedUnexpectedEvent),
})
.into_box()
}
fn get_adata_indices(&self, address: ADataAddress) -> Box<CoreFuture<ADataIndices>> {
trace!(
"Get latest indices from AppendOnly Data at {:?}",
address.name()
);
send(self, Request::GetADataIndices(address), address.is_unpub())
.and_then(|res| match res {
Response::GetADataIndices(res) => res.map_err(CoreError::from),
_ => Err(CoreError::ReceivedUnexpectedEvent),
})
.into_box()
}
fn get_adata_last_entry(&self, address: ADataAddress) -> Box<CoreFuture<ADataEntry>> {
trace!(
"Get latest indices from AppendOnly Data at {:?}",
address.name()
);
send(
self,
Request::GetADataLastEntry(address),
address.is_unpub(),
)
.and_then(|res| match res {
Response::GetADataLastEntry(res) => res.map_err(CoreError::from),
_ => Err(CoreError::ReceivedUnexpectedEvent),
})
.into_box()
}
fn get_unpub_adata_permissions_at_index(
&self,
address: ADataAddress,
permissions_index: ADataIndex,
) -> Box<CoreFuture<ADataUnpubPermissions>> {
trace!(
"Get latest indices from AppendOnly Data at {:?}",
address.name()
);
send(
self,
Request::GetADataPermissions {
address,
permissions_index,
},
true,
)
.and_then(|res| match res {
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),
})
.into_box()
}
fn get_pub_adata_permissions_at_index(
&self,
address: ADataAddress,
permissions_index: ADataIndex,
) -> Box<CoreFuture<ADataPubPermissions>> {
trace!(
"Get latest indices from AppendOnly Data at {:?}",
address.name()
);
send(
self,
Request::GetADataPermissions {
address,
permissions_index,
},
false,
)
.and_then(|res| match res {
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),
})
.into_box()
}
fn get_pub_adata_user_permissions(
&self,
address: ADataAddress,
permissions_index: ADataIndex,
user: ADataUser,
) -> Box<CoreFuture<ADataPubPermissionSet>> {
trace!(
"Get permissions for a specified user(s) from AppendOnly Data at {:?}",
address.name()
);
send(
self,
Request::GetPubADataUserPermissions {
address,
permissions_index,
user,
},
false,
)
.and_then(|res| match res {
Response::GetPubADataUserPermissions(res) => res.map_err(CoreError::from),
_ => Err(CoreError::ReceivedUnexpectedEvent),
})
.into_box()
}
fn get_unpub_adata_user_permissions(
&self,
address: ADataAddress,
permissions_index: ADataIndex,
public_key: PublicKey,
) -> Box<CoreFuture<ADataUnpubPermissionSet>> {
trace!(
"Get permissions for a specified user(s) from AppendOnly Data at {:?}",
address.name()
);
send(
self,
Request::GetUnpubADataUserPermissions {
address,
permissions_index,
public_key,
},
true,
)
.and_then(|res| match res {
Response::GetUnpubADataUserPermissions(res) => res.map_err(CoreError::from),
_ => Err(CoreError::ReceivedUnexpectedEvent),
})
.into_box()
}
fn add_unpub_adata_permissions(
&self,
address: ADataAddress,
permissions: ADataUnpubPermissions,
permissions_index: u64,
) -> Box<CoreFuture<()>> {
trace!(
"Add Permissions to UnPub AppendOnly Data {:?}",
address.name()
);
send_mutation(
self,
Request::AddUnpubADataPermissions {
address,
permissions,
permissions_index,
},
)
}
fn add_pub_adata_permissions(
&self,
address: ADataAddress,
permissions: ADataPubPermissions,
permissions_index: u64,
) -> Box<CoreFuture<()>> {
trace!("Add Permissions to AppendOnly Data {:?}", address.name());
send_mutation(
self,
Request::AddPubADataPermissions {
address,
permissions,
permissions_index,
},
)
}
fn set_adata_owners(
&self,
address: ADataAddress,
owner: ADataOwner,
owners_index: u64,
) -> Box<CoreFuture<()>> {
trace!("Set Owners to AppendOnly Data {:?}", address.name());
send_mutation(
self,
Request::SetADataOwner {
address,
owner,
owners_index,
},
)
}
fn get_adata_owners(
&self,
address: ADataAddress,
owners_index: ADataIndex,
) -> Box<CoreFuture<ADataOwner>> {
trace!("Get Owners from AppendOnly Data at {:?}", address.name());
send(
self,
Request::GetADataOwners {
address,
owners_index,
},
address.is_unpub(),
)
.and_then(|res| match res {
Response::GetADataOwners(res) => res.map_err(CoreError::from),
_ => Err(CoreError::ReceivedUnexpectedEvent),
})
.into_box()
}
fn append_seq_adata(&self, append: ADataAppendOperation, index: u64) -> Box<CoreFuture<()>> {
send_mutation(self, Request::AppendSeq { append, index })
}
fn append_unseq_adata(&self, append: ADataAppendOperation) -> Box<CoreFuture<()>> {
send_mutation(self, Request::AppendUnseq(append))
}
fn list_mdata_permissions(
&self,
address: MDataAddress,
) -> Box<CoreFuture<BTreeMap<PublicKey, MDataPermissionSet>>> {
trace!("List MDataPermissions for {:?}", address);
send(self, Request::ListMDataPermissions(address), true)
.and_then(|res| match res {
Response::ListMDataPermissions(res) => res.map_err(CoreError::from),
_ => Err(CoreError::ReceivedUnexpectedEvent),
})
.into_box()
}
fn set_mdata_user_permissions(
&self,
address: MDataAddress,
user: PublicKey,
permissions: MDataPermissionSet,
version: u64,
) -> Box<CoreFuture<()>> {
trace!("SetMDataUserPermissions for {:?}", address);
send_mutation(
self,
Request::SetMDataUserPermissions {
address,
user,
permissions: permissions.clone(),
version,
},
)
}
fn del_mdata_user_permissions(
&self,
address: MDataAddress,
user: PublicKey,
version: u64,
) -> Box<CoreFuture<()>> {
trace!("DelMDataUserPermissions for {:?}", address);
send_mutation(
self,
Request::DelMDataUserPermissions {
address,
user,
version,
},
)
}
#[allow(unused)]
fn change_mdata_owner(
&self,
name: XorName,
tag: u64,
new_owner: PublicKey,
version: u64,
) -> Box<CoreFuture<()>> {
unimplemented!();
}
#[cfg(any(
all(test, feature = "mock-network"),
all(feature = "testing", feature = "mock-network")
))]
#[doc(hidden)]
fn set_network_limits(&self, max_ops_count: Option<u64>) {
let inner = self.inner();
inner
.borrow_mut()
.connection_manager
.set_network_limits(max_ops_count);
}
#[cfg(any(
all(test, feature = "mock-network"),
all(feature = "testing", feature = "mock-network")
))]
#[doc(hidden)]
fn simulate_network_disconnect(&self) {
let inner = self.inner();
inner.borrow_mut().connection_manager.simulate_disconnect();
}
#[cfg(any(
all(test, feature = "mock-network"),
all(feature = "testing", feature = "mock-network")
))]
#[doc(hidden)]
fn set_simulate_timeout(&self, enabled: bool) {
let inner = self.inner();
inner
.borrow_mut()
.connection_manager
.set_simulate_timeout(enabled);
}
#[cfg(any(test, feature = "testing"))]
fn test_set_balance(
&self,
secret_key: Option<&BlsSecretKey>,
amount: Coins,
) -> Box<CoreFuture<Transaction>> {
let new_balance_owner =
secret_key.map_or_else(|| self.public_key(), |sk| sk.public_key().into());
trace!(
"Set the coin balance of {:?} to {:?}",
new_balance_owner,
amount,
);
send_as(
self,
Request::CreateBalance {
new_balance_owner,
amount,
transaction_id: new_rand::random(),
},
secret_key,
)
.and_then(|res| match res {
Response::Transaction(res) => res.map_err(CoreError::from),
_ => Err(CoreError::ReceivedUnexpectedEvent),
})
.into_box()
}
}
fn temp_client<F, R>(identity: &BlsSecretKey, mut func: F) -> Result<R, CoreError>
where
F: FnMut(&mut ConnectionManager, &SafeKey) -> Result<R, CoreError>,
{
let full_id = SafeKey::client_from_bls_key(identity.clone());
let (net_tx, _net_rx) = mpsc::unbounded();
let mut cm = ConnectionManager::new(Config::new().quic_p2p, &net_tx.clone())?;
block_on_all(cm.bootstrap(full_id.clone()).map_err(CoreError::from))?;
let res = func(&mut cm, &full_id);
block_on_all(cm.disconnect(&full_id.public_id()))?;
res
}
pub fn test_create_balance(owner: &BlsSecretKey, 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 = req(
&mut cm,
Request::CreateBalance {
new_balance_owner,
amount,
transaction_id: new_rand::random(),
},
&full_id,
)?;
match response {
Response::Transaction(res) => res.map(|_| Ok(()))?,
_ => Err(CoreError::from("Unexpected response")),
}
})
}
pub fn wallet_get_balance(wallet_sk: &BlsSecretKey) -> Result<Coins, CoreError> {
trace!("Get balance for {:?}", wallet_sk);
temp_client(wallet_sk, move |mut cm, full_id| {
match req(&mut cm, Request::GetBalance, &full_id)? {
Response::GetBalance(res) => res.map_err(CoreError::from),
_ => Err(CoreError::from("Unexpected response")),
}
})
}
pub fn wallet_create_balance(
secret_key: &BlsSecretKey,
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(secret_key, move |mut cm, full_id| {
let response = req(
&mut cm,
Request::CreateBalance {
new_balance_owner,
amount,
transaction_id,
},
&full_id,
)?;
match response {
Response::Transaction(res) => res.map_err(CoreError::from),
_ => Err(CoreError::from("Unexpected response")),
}
})
}
pub fn wallet_transfer_coins(
secret_key: &BlsSecretKey,
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(secret_key, move |mut cm, full_id| {
let response = req(
&mut cm,
Request::TransferCoins {
destination,
amount,
transaction_id,
},
&full_id,
)?;
match response {
Response::Transaction(res) => res.map_err(CoreError::from),
_ => Err(CoreError::from("Unexpected response")),
}
})
}
pub trait AuthActions: Client + Clone + 'static {
fn list_auth_keys_and_version(
&self,
) -> Box<CoreFuture<(BTreeMap<PublicKey, AppPermissions>, u64)>> {
trace!("ListAuthKeysAndVersion");
send(self, Request::ListAuthKeysAndVersion, true)
.and_then(|res| match res {
Response::ListAuthKeysAndVersion(res) => res.map_err(CoreError::from),
_ => Err(CoreError::ReceivedUnexpectedEvent),
})
.into_box()
}
fn ins_auth_key(
&self,
key: PublicKey,
permissions: AppPermissions,
version: u64,
) -> Box<CoreFuture<()>> {
trace!("InsAuthKey ({:?})", key);
send_mutation(
self,
Request::InsAuthKey {
key,
permissions,
version,
},
)
}
fn del_auth_key(&self, key: PublicKey, version: u64) -> Box<CoreFuture<()>> {
trace!("DelAuthKey ({:?})", key);
send_mutation(self, Request::DelAuthKey { key, version })
}
fn delete_mdata(&self, address: MDataAddress) -> Box<CoreFuture<()>> {
trace!("Delete entire Mutable Data at {:?}", address);
send_mutation(self, Request::DeleteMData(address))
}
fn delete_adata(&self, address: ADataAddress) -> Box<CoreFuture<()>> {
trace!("Delete entire Unpublished AppendOnly Data at {:?}", address);
send_mutation(self, Request::DeleteAData(address))
}
}
fn sign_request_with_key(request: Request, key: &BlsSecretKey) -> Message {
let message_id = MessageId::new();
let signature = Some(Signature::from(
key.sign(&unwrap!(bincode::serialize(&(&request, message_id)))),
));
Message::Request {
request,
message_id,
signature,
}
}
#[allow(unused)]
pub struct ClientInner<C: Client, T> {
connection_manager: ConnectionManager,
el_handle: Handle,
cache: LruCache<IDataAddress, IData>,
timeout: Duration,
core_tx: CoreMsgTx<C, T>,
net_tx: NetworkTx,
}
impl<C: Client, T> ClientInner<C, T> {
#[allow(clippy::too_many_arguments)]
pub fn new(
el_handle: Handle,
connection_manager: ConnectionManager,
cache: LruCache<IDataAddress, IData>,
timeout: Duration,
core_tx: CoreMsgTx<C, T>,
net_tx: NetworkTx,
) -> ClientInner<C, T> {
ClientInner {
el_handle,
connection_manager,
cache,
timeout,
core_tx,
net_tx,
}
}
pub fn cm(&mut self) -> &mut ConnectionManager {
&mut self.connection_manager
}
}
fn send_as(
client: &impl Client,
request: Request,
secret_key: Option<&BlsSecretKey>,
) -> Box<CoreFuture<Response>> {
let (message, identity) = match secret_key {
Some(key) => (
sign_request_with_key(request, key),
SafeKey::client_from_bls_key(key.clone()),
),
None => (client.compose_message(request, true), client.full_id()),
};
let pub_id = identity.public_id();
let inner = client.inner();
let cm = &mut inner.borrow_mut().connection_manager;
let mut cm2 = cm.clone();
Box::new(
cm.bootstrap(identity.clone())
.and_then(move |_| cm2.send(&pub_id, &message)),
)
}
fn send(client: &impl Client, request: Request, sign: bool) -> Box<CoreFuture<Response>> {
let request = client.compose_message(request, sign);
let inner = client.inner();
let cm = &mut inner.borrow_mut().connection_manager;
cm.send(&client.public_id(), &request)
}
fn send_mutation(client: &impl Client, req: Request) -> Box<CoreFuture<()>> {
Box::new(send(client, req, true).and_then(move |res| {
trace!("mutation res: {:?}", res);
match res {
Response::Mutation(res) => res.map_err(CoreError::from),
_ => Err(CoreError::ReceivedUnexpectedEvent),
}
}))
}
pub 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))));
block_on_all(cm.send(
&full_id_new.public_id(),
&Message::Request {
request,
message_id,
signature: Some(signature),
},
))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::utils::generate_random_vector;
use crate::utils::test_utils::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;
use BlsSecretKey;
#[test]
fn pub_idata_test() {
random_client(move |client| {
let client2 = client.clone();
let client3 = client.clone();
let client4 = client.clone();
let value = unwrap!(generate_random_vector::<u8>(10));
let data = PubImmutableData::new(value.clone());
let address = *data.address();
let test_data = UnpubImmutableData::new(
value.clone(),
PublicKey::Bls(BlsSecretKey::random().public_key()),
);
client
.get_idata(address)
.then(|res| -> Result<(), CoreError> {
match res {
Ok(data) => panic!("Pub idata should not exist yet: {:?}", data),
Err(CoreError::DataError(SndError::NoSuchData)) => Ok(()),
Err(e) => panic!("Unexpected: {:?}", e),
}
})
.and_then(move |_| {
client2.put_idata(data.clone())
})
.and_then(move |_| {
client3.put_idata(test_data.clone()).then(|res| match res {
Ok(_) => panic!("Unexpected Success: Validating owners should fail"),
Err(CoreError::DataError(SndError::InvalidOwners)) => Ok(()),
Err(e) => panic!("Unexpected: {:?}", e),
})
})
.and_then(move |_| {
client4.get_idata(address).map(move |fetched_data| {
assert_eq!(*fetched_data.address(), address);
})
})
})
}
#[test]
fn unpub_idata_test() {
random_client(move |client| {
let client2 = client.clone();
let client3 = client.clone();
let client4 = client.clone();
let client5 = client.clone();
let client6 = client.clone();
let client7 = client.clone();
let client8 = client.clone();
let value = unwrap!(generate_random_vector::<u8>(10));
let data = UnpubImmutableData::new(value.clone(), client.public_key());
let data2 = data.clone();
let data3 = data.clone();
let address = *data.address();
assert_eq!(address, *data2.address());
let pub_data = PubImmutableData::new(value);
client
.get_idata(address)
.then(|res| -> Result<(), CoreError> {
match res {
Ok(_) => panic!("Unpub idata should not exist yet"),
Err(CoreError::DataError(SndError::NoSuchData)) => Ok(()),
Err(e) => panic!("Unexpected: {:?}", e),
}
})
.and_then(move |_| {
client2.put_idata(data.clone())
})
.and_then(move |_| {
client3.put_idata(data2.clone())
})
.then(|res| -> Result<(), CoreError> {
match res {
Err(CoreError::DataError(SndError::DataExists)) => Ok(()),
res => panic!("Unexpected: {:?}", res),
}
})
.and_then(move |_| {
client4.put_idata(pub_data)
})
.and_then(move |_| {
client5.get_idata(address).map(move |fetched_data| {
assert_eq!(*fetched_data.address(), address);
})
})
.and_then(move |()| {
client6.del_unpub_idata(*address.name())
})
.and_then(move |()| {
client7.get_idata(address)
})
.then(|res| -> Result<(), CoreError> {
match res {
Ok(_) => panic!("Unpub idata still exists after deletion"),
Err(CoreError::DataError(SndError::NoSuchData)) => Ok(()),
Err(e) => panic!("Unexpected: {:?}", e),
}
})
.and_then(move |_| {
client8.put_idata(data3.clone())
})
});
}
#[test]
pub fn unseq_mdata_test() {
let _ = random_client(move |client| {
let client2 = client.clone();
let client3 = client.clone();
let client4 = client.clone();
let client5 = client.clone();
let client6 = client.clone();
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(), permission_set.clone());
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(),
);
client
.put_unseq_mutable_data(data.clone())
.and_then(move |_| {
println!("Put unseq. MData successfully");
client3
.get_mdata_version(MDataAddress::Unseq { name, tag })
.map(move |version| assert_eq!(version, 0))
})
.and_then(move |_| {
client4
.list_unseq_mdata_entries(name, tag)
.map(move |fetched_entries| {
assert_eq!(fetched_entries, entries);
})
})
.and_then(move |_| {
client5
.list_mdata_keys(MDataAddress::Unseq { name, tag })
.map(move |keys| assert_eq!(keys, entries_keys))
})
.and_then(move |_| {
client6
.list_unseq_mdata_values(name, tag)
.map(move |values| assert_eq!(values, entries_values))
})
.and_then(move |_| {
client2
.get_unseq_mdata(*data.name(), data.tag())
.map(move |fetched_data| {
assert_eq!(fetched_data.name(), data.name());
assert_eq!(fetched_data.tag(), data.tag());
fetched_data
})
})
.then(|res| res)
});
}
#[test]
pub fn seq_mdata_test() {
let _ = random_client(move |client| {
let client2 = client.clone();
let client3 = client.clone();
let client4 = client.clone();
let client5 = client.clone();
let client6 = client.clone();
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(), permission_set.clone());
let data = SeqMutableData::new_with_data(
name,
tag,
entries.clone(),
permissions,
client.public_key(),
);
client
.put_seq_mutable_data(data.clone())
.and_then(move |_| {
println!("Put seq. MData successfully");
client4
.list_seq_mdata_entries(name, tag)
.map(move |fetched_entries| {
assert_eq!(fetched_entries, entries);
})
})
.and_then(move |_| {
client3
.get_seq_mdata_shell(name, tag)
.map(move |mdata_shell| {
assert_eq!(*mdata_shell.name(), name);
assert_eq!(mdata_shell.tag(), tag);
assert_eq!(mdata_shell.entries().len(), 0);
})
})
.and_then(move |_| {
client5
.list_mdata_keys(MDataAddress::Seq { name, tag })
.map(move |keys| assert_eq!(keys, entries_keys))
})
.and_then(move |_| {
client6
.list_seq_mdata_values(name, tag)
.map(move |values| assert_eq!(values, entries_values))
})
.and_then(move |_| {
client2.get_seq_mdata(name, tag).map(move |fetched_data| {
assert_eq!(fetched_data.name(), data.name());
assert_eq!(fetched_data.tag(), data.tag());
assert_eq!(fetched_data.entries().len(), 1);
fetched_data
})
})
.then(|res| res)
});
}
#[test]
pub fn del_seq_mdata_test() {
random_client(move |client| {
let client2 = client.clone();
let client3 = client.clone();
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(),
);
client
.put_seq_mutable_data(data.clone())
.and_then(move |_| {
client2.delete_mdata(mdataref).map(move |result| {
assert_eq!(result, ());
})
})
.then(move |_| {
client3
.get_unseq_mdata(*data.name(), data.tag())
.then(move |res| {
match res {
Err(CoreError::DataError(SndError::NoSuchData)) => (),
_ => panic!("Unexpected success"),
}
Ok::<_, SndError>(())
})
})
});
}
#[test]
pub fn del_unseq_mdata_test() {
random_client(move |client| {
let client2 = client.clone();
let client3 = client.clone();
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(),
);
client
.put_unseq_mutable_data(data.clone())
.and_then(move |_| {
client2.delete_mdata(mdataref).and_then(move |result| {
assert_eq!(result, ());
Ok(())
})
})
.then(move |_| {
client3
.get_unseq_mdata(*data.name(), data.tag())
.then(move |res| {
match res {
Err(CoreError::DataError(SndError::NoSuchData)) => (),
_ => panic!("Unexpected success"),
}
Ok::<_, SndError>(())
})
})
});
}
#[test]
fn coin_permissions() {
let wallet_a_addr = random_client(move |client| {
let wallet_a_addr: XorName = client.owner_key().into();
client
.transfer_coins(
None,
new_rand::random(),
unwrap!(Coins::from_str("5.0")),
None,
)
.then(move |res| {
match res {
Err(CoreError::DataError(SndError::NoSuchBalance)) => (),
res => panic!("Unexpected result: {:?}", res),
}
Ok::<_, SndError>(wallet_a_addr)
})
});
random_client(move |client| {
let c2 = client.clone();
let c3 = client.clone();
let c4 = client.clone();
client
.get_balance(None)
.then(move |res| {
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),
}
c2.test_set_balance(None, unwrap!(Coins::from_str("50.0")))
})
.and_then(move |_| {
c3.transfer_coins(None, wallet_a_addr, unwrap!(Coins::from_str("10")), None)
})
.then(move |res| {
match res {
Ok(transaction) => {
assert_eq!(transaction.amount, unwrap!(Coins::from_str("10")))
}
res => panic!("Unexpected error: {:?}", res),
}
c4.get_balance(None)
})
.then(move |res| {
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::<_, SndError>(())
})
});
}
#[test]
fn anonymous_wallet() {
random_client(move |client| {
let client1 = client.clone();
let client2 = client.clone();
let client3 = client.clone();
let client4 = client.clone();
let client5 = client.clone();
let wallet1: XorName = client.owner_key().into();
client
.test_set_balance(None, unwrap!(Coins::from_str("500.0")))
.and_then(move |_| {
let bls_sk = BlsSecretKey::random();
client1
.create_balance(
None,
PublicKey::from(bls_sk.public_key()),
unwrap!(Coins::from_str("100.0")),
None,
)
.map(|txn| (txn, bls_sk))
})
.and_then(move |(transaction, bls_sk)| {
assert_eq!(transaction.amount, unwrap!(Coins::from_str("100")));
client2
.transfer_coins(
Some(&bls_sk),
wallet1,
unwrap!(Coins::from_str("5.0")),
None,
)
.map(|txn| (txn, bls_sk))
})
.and_then(move |(transaction, bls_sk)| {
assert_eq!(transaction.amount, unwrap!(Coins::from_str("5.0")));
client3.get_balance(Some(&bls_sk)).and_then(|balance| {
assert_eq!(balance, unwrap!(Coins::from_str("95.0")));
Ok(())
})
})
.and_then(move |_| {
client4.get_balance(None).and_then(|balance| {
let mut expected = unwrap!(Coins::from_str("405.0"));
expected = unwrap!(expected.checked_sub(*COST_OF_PUT));
assert_eq!(balance, expected);
Ok(())
})
})
.and_then(move |_| {
let random_key = BlsSecretKey::random();
let random_source = BlsSecretKey::random();
let random_pk = PublicKey::from(random_key.public_key());
client5
.create_balance(
Some(&random_source),
random_pk,
unwrap!(Coins::from_str("100.0")),
None,
)
.then(|res| {
match res {
Err(CoreError::DataError(SndError::NoSuchBalance)) => {}
res => panic!("Unexpected result: {:?}", res),
}
Ok(())
})
})
});
}
#[test]
fn coin_balance_transfer() {
let wallet1: XorName = random_client(move |client| {
let client1 = client.clone();
let owner_key = client.owner_key();
let wallet1: XorName = owner_key.into();
client
.test_set_balance(None, unwrap!(Coins::from_str("100.0")))
.and_then(move |_| client1.get_balance(None))
.and_then(move |balance| {
assert_eq!(balance, unwrap!(Coins::from_str("100.0")));
Ok(wallet1)
})
});
random_client(move |client| {
let c2 = client.clone();
let c3 = client.clone();
let c4 = client.clone();
let c5 = client.clone();
let c6 = client.clone();
let c7 = client.clone();
client
.get_balance(None)
.and_then(move |orig_balance| {
c2.transfer_coins(None, wallet1, unwrap!(Coins::from_str("5.0")), None)
.map(move |_| orig_balance)
})
.and_then(move |orig_balance| {
c3.get_balance(None)
.map(move |new_balance| (new_balance, orig_balance))
})
.and_then(move |(new_balance, orig_balance)| {
assert_eq!(
new_balance,
unwrap!(orig_balance.checked_sub(unwrap!(Coins::from_str("5.0")))),
);
c4.transfer_coins(None, wallet1, unwrap!(Coins::from_str("5000")), None)
})
.then(move |res| {
match res {
Err(CoreError::DataError(SndError::InsufficientBalance)) => (),
res => panic!("Unexpected result: {:?}", res),
}
c5.transfer_coins(None, wallet1, unwrap!(Coins::from_str("0")), None)
})
.then(move |res| {
match res {
Err(CoreError::DataError(SndError::InvalidOperation)) => (),
res => panic!("Unexpected result: {:?}", res),
}
c6.test_set_balance(None, unwrap!(Coins::from_str("0")))
})
.and_then(move |_| {
let data = PubImmutableData::new(unwrap!(generate_random_vector::<u8>(10)));
c7.put_idata(data)
})
.then(move |res| {
match res {
Err(CoreError::DataError(SndError::InsufficientBalance)) => (),
res => panic!("Unexpected result: {:?}", res),
}
Ok::<_, SndError>(())
})
});
}
#[test]
pub fn del_unseq_mdata_permission_test() {
let name = XorName(rand::random());
let tag = 15001;
let mdataref = MDataAddress::Unseq { name, tag };
random_client(move |client| {
let data = UnseqMutableData::new_with_data(
name,
tag,
Default::default(),
Default::default(),
client.public_key(),
);
client.put_unseq_mutable_data(data.clone()).then(|res| res)
});
random_client(move |client| {
client.delete_mdata(mdataref).then(|res| {
match res {
Err(CoreError::DataError(SndError::AccessDenied)) => (),
res => panic!("Unexpected result: {:?}", res),
}
Ok::<_, SndError>(())
})
});
}
#[test]
pub fn mdata_permissions_test() {
random_client(|client| {
let client1 = client.clone();
let client2 = client.clone();
let client3 = client.clone();
let client4 = client.clone();
let client5 = client.clone();
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();
let user2 = user;
let random_user = PublicKey::Bls(BlsSecretKey::random().public_key());
let _ = permissions.insert(user, permission_set.clone());
let _ = permissions.insert(random_user, permission_set.clone());
let data = SeqMutableData::new_with_data(
name,
tag,
Default::default(),
permissions.clone(),
client.public_key(),
);
let test_data = SeqMutableData::new_with_data(
XorName(rand::random()),
15000,
Default::default(),
permissions,
PublicKey::Bls(BlsSecretKey::random().public_key()),
);
client
.put_seq_mutable_data(data.clone())
.and_then(move |res| {
assert_eq!(res, ());
Ok(())
})
.and_then(move |_| {
client1
.put_seq_mutable_data(test_data.clone())
.then(|res| match res {
Ok(_) => panic!("Unexpected Success: Validating owners should fail"),
Err(CoreError::DataError(SndError::InvalidOwners)) => Ok(()),
Err(e) => panic!("Unexpected: {:?}", e),
})
})
.and_then(move |_| {
let new_perm_set = MDataPermissionSet::new()
.allow(MDataAction::ManagePermissions)
.allow(MDataAction::Read);
client2
.set_mdata_user_permissions(
MDataAddress::Seq { name, tag },
user,
new_perm_set,
1,
)
.then(move |res| {
assert_eq!(unwrap!(res), ());
Ok(())
})
})
.and_then(move |_| {
println!("Modified user permissions");
client3
.list_mdata_user_permissions(MDataAddress::Seq { name, tag }, user2)
.and_then(|permissions| {
assert!(!permissions.is_allowed(MDataAction::Insert));
assert!(permissions.is_allowed(MDataAction::Read));
assert!(permissions.is_allowed(MDataAction::ManagePermissions));
println!("Verified new permissions");
Ok(())
})
})
.and_then(move |_| {
client4
.del_mdata_user_permissions(MDataAddress::Seq { name, tag }, random_user, 2)
.then(move |res| {
assert_eq!(unwrap!(res), ());
Ok(())
})
})
.and_then(move |_| {
println!("Deleted permissions");
client5
.list_mdata_permissions(MDataAddress::Seq { name, tag })
.and_then(|permissions| {
assert_eq!(permissions.len(), 1);
println!("Permission set verified");
Ok(())
})
})
})
}
#[test]
pub fn mdata_mutations_test() {
random_client(|client| {
let client2 = client.clone();
let client3 = client.clone();
let client4 = client.clone();
let client5 = client.clone();
let client6 = client.clone();
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();
let _ = permissions.insert(user, permission_set.clone());
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(),
);
client
.put_seq_mutable_data(data.clone())
.and_then(move |_| {
println!("Put seq. MData successfully");
client2
.list_seq_mdata_entries(name, tag)
.map(move |fetched_entries| {
assert_eq!(fetched_entries, entries);
})
})
.and_then(move |_| {
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);
client3
.mutate_seq_mdata_entries(name, tag, entry_actions.clone())
.then(move |res| {
assert_eq!(unwrap!(res), ());
Ok(())
})
})
.and_then(move |_| {
client4
.list_seq_mdata_entries(name, tag)
.map(move |fetched_entries| {
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);
})
})
.and_then(move |_| {
client5
.get_seq_mdata_value(name, tag, b"key3".to_vec())
.and_then(|fetched_value| {
assert_eq!(
fetched_value,
MDataSeqValue {
data: b"value".to_vec(),
version: 0
}
);
Ok(())
})
})
.then(move |_| {
client6
.get_seq_mdata_value(name, tag, b"wrongKey".to_vec())
.then(|res| {
match res {
Ok(_) => panic!("Unexpected: Entry should not exist"),
Err(CoreError::DataError(SndError::NoSuchEntry)) => (),
Err(err) => panic!("Unexpected error: {:?}", err),
}
Ok::<_, SndError>(())
})
})
});
random_client(|client| {
let client2 = client.clone();
let client3 = client.clone();
let client4 = client.clone();
let client5 = client.clone();
let client6 = client.clone();
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();
let _ = permissions.insert(user, permission_set.clone());
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(),
);
client
.put_unseq_mutable_data(data.clone())
.and_then(move |_| {
println!("Put unseq. MData successfully");
client2
.list_unseq_mdata_entries(name, tag)
.map(move |fetched_entries| {
assert_eq!(fetched_entries, entries);
})
})
.and_then(move |_| {
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());
client3
.mutate_unseq_mdata_entries(name, tag, entry_actions.clone())
.then(move |res| {
assert_eq!(unwrap!(res), ());
Ok(())
})
})
.and_then(move |_| {
client4
.list_unseq_mdata_entries(name, tag)
.map(move |fetched_entries| {
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);
})
})
.and_then(move |_| {
client5
.get_unseq_mdata_value(name, tag, b"key1".to_vec())
.and_then(|fetched_value| {
assert_eq!(fetched_value, b"newValue".to_vec());
Ok(())
})
})
.then(move |_| {
client6
.get_unseq_mdata_value(name, tag, b"wrongKey".to_vec())
.then(|res| {
match res {
Ok(_) => panic!("Unexpected: Entry should not exist"),
Err(CoreError::DataError(SndError::NoSuchEntry)) => (),
Err(err) => panic!("Unexpected error: {:?}", err),
}
Ok::<_, SndError>(())
})
})
});
}
#[test]
pub fn adata_basics_test() {
random_client(move |client| {
let client1 = client.clone();
let client2 = client.clone();
let client3 = client.clone();
let client4 = client.clone();
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(), 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(),
entries_index: 0,
permissions_index: 1,
};
unwrap!(data.append_owner(owner, 0));
client
.put_adata(AData::UnpubSeq(data.clone()))
.and_then(move |_| {
client1.get_adata(address).map(move |data| match data {
AData::UnpubSeq(adata) => assert_eq!(*adata.name(), name),
_ => panic!("Unexpected data found"),
})
})
.and_then(move |_| {
client2
.get_adata_shell(index, address)
.map(move |data| 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"),
})
})
.and_then(move |_| client3.delete_adata(address))
.and_then(move |_| {
client4.get_adata(address).then(|res| match res {
Ok(_) => panic!("AData was not deleted"),
Err(CoreError::DataError(SndError::NoSuchData)) => Ok(()),
Err(e) => panic!("Unexpected error: {:?}", e),
})
})
.then(move |res| res)
});
}
#[test]
pub fn adata_permissions_test() {
random_client(move |client| {
let client1 = client.clone();
let client2 = client.clone();
let client3 = client.clone();
let client4 = client.clone();
let client5 = client.clone();
let client6 = client.clone();
let client7 = client.clone();
let client8 = client.clone();
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(), 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 = PublicKey::Bls(BlsSecretKey::random().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(),
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: PublicKey::Bls(BlsSecretKey::random().public_key()),
entries_index: 0,
permissions_index: 0,
};
unwrap!(test_data.append_owner(test_owner, 0));
client
.put_adata(AData::UnpubSeq(data.clone()))
.map(move |res| {
assert_eq!(res, ());
})
.and_then(move |_| {
client1
.put_adata(AData::UnpubSeq(test_data.clone()))
.then(|res| match res {
Ok(_) => panic!("Unexpected Success: Validating owners should fail"),
Err(CoreError::DataError(SndError::InvalidOwners)) => Ok(()),
Err(e) => panic!("Unexpected: {:?}", e),
})
})
.and_then(move |_| {
client2
.get_adata_range(adataref, (index_start, index_end))
.map(move |data| {
assert_eq!(
unwrap!(std::str::from_utf8(&unwrap!(data.last()).key)),
"KEY2"
);
assert_eq!(
unwrap!(std::str::from_utf8(&unwrap!(data.last()).value)),
"VALUE2"
);
})
})
.and_then(move |_| {
client3.get_adata_indices(adataref).map(move |data| {
assert_eq!(data.entries_index(), 4);
assert_eq!(data.owners_index(), 1);
assert_eq!(data.permissions_index(), 1);
})
})
.and_then(move |_| {
client4
.get_adata_value(adataref, b"KEY1".to_vec())
.map(move |data| {
assert_eq!(unwrap!(std::str::from_utf8(data.as_slice())), "VALUE1");
})
})
.and_then(move |_| {
client5.get_adata_last_entry(adataref).map(move |data| {
assert_eq!(unwrap!(std::str::from_utf8(data.key.as_slice())), "KEY4");
assert_eq!(
unwrap!(std::str::from_utf8(data.value.as_slice())),
"VALUE4"
);
})
})
.and_then(move |_| {
client6
.add_unpub_adata_permissions(adataref, perm_set, 1)
.then(move |res| {
assert_eq!(unwrap!(res), ());
Ok(())
})
})
.and_then(move |_| {
client7
.get_unpub_adata_permissions_at_index(adataref, perm_index)
.map(move |data| {
let set = unwrap!(data.permissions.get(&sim_client1));
assert!(set.is_allowed(ADataAction::Append));
})
})
.and_then(move |_| {
client8
.get_unpub_adata_user_permissions(
adataref,
index_start,
client8.public_key(),
)
.map(move |set| {
assert!(set.is_allowed(ADataAction::Append));
})
})
.then(|res| res)
});
}
#[test]
pub fn append_seq_adata_test() {
let name = XorName(rand::random());
let tag = 10;
random_client(move |client| {
let client1 = client.clone();
let client2 = client.clone();
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());
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(),
entries_index: 0,
permissions_index: 1,
};
unwrap!(data.append_owner(owner, 0));
client
.put_adata(AData::PubSeq(data.clone()))
.and_then(move |_| {
client1.append_seq_adata(append, 0).then(move |res| {
assert_eq!(unwrap!(res), ());
Ok(())
})
})
.and_then(move |_| {
client2.get_adata(adataref).map(move |data| match data {
AData::PubSeq(adata) => assert_eq!(
unwrap!(std::str::from_utf8(&unwrap!(adata.last_entry()).key)),
"KEY2"
),
_ => panic!("UNEXPECTED DATA!"),
})
})
.then(|res| res)
});
}
#[test]
pub fn append_unseq_adata_test() {
let name = XorName(rand::random());
let tag = 10;
random_client(move |client| {
let client1 = client.clone();
let client2 = client.clone();
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(), 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(),
entries_index: 0,
permissions_index: 1,
};
unwrap!(data.append_owner(owner, 0));
client
.put_adata(AData::UnpubUnseq(data.clone()))
.and_then(move |_| {
client1.append_unseq_adata(append).then(move |res| {
assert_eq!(unwrap!(res), ());
Ok(())
})
})
.and_then(move |_| {
client2.get_adata(adataref).map(move |data| match data {
AData::UnpubUnseq(adata) => assert_eq!(
unwrap!(std::str::from_utf8(&unwrap!(adata.last_entry()).key)),
"KEY2"
),
_ => panic!("UNEXPECTED DATA!"),
})
})
.then(|res| res)
});
}
#[test]
pub fn set_and_get_owner_adata_test() {
let name = XorName(rand::random());
let tag = 10;
random_client(move |client| {
let client1 = client.clone();
let client2 = client.clone();
let client3 = client.clone();
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(), set);
unwrap!(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)];
unwrap!(data.append(kvdata));
let owner = ADataOwner {
public_key: client.public_key(),
entries_index: 2,
permissions_index: 1,
};
unwrap!(data.append_owner(owner, 0));
let owner2 = ADataOwner {
public_key: client1.public_key(),
entries_index: 2,
permissions_index: 1,
};
let owner3 = ADataOwner {
public_key: client2.public_key(),
entries_index: 2,
permissions_index: 1,
};
client
.put_adata(AData::UnpubUnseq(data.clone()))
.and_then(move |_| {
client1
.set_adata_owners(adataref, owner2, 1)
.then(move |res| {
assert_eq!(unwrap!(res), ());
Ok(())
})
})
.and_then(move |_| {
client2
.set_adata_owners(adataref, owner3, 2)
.map(move |data| {
assert_eq!(data, ());
})
})
.and_then(move |_| {
client3.get_adata(adataref).map(move |data| match data {
AData::UnpubUnseq(adata) => assert_eq!(adata.owners_index(), 3),
_ => panic!("UNEXPECTED DATA!"),
})
})
.then(|res| res)
});
}
#[test]
pub fn wallet_transactions_without_client() {
let bls_sk = BlsSecretKey::random();
unwrap!(test_create_balance(&bls_sk, unwrap!(Coins::from_str("50"))));
let balance = unwrap!(wallet_get_balance(&bls_sk));
let ten_coins = unwrap!(Coins::from_str("10"));
assert_eq!(balance, unwrap!(Coins::from_str("50")));
let new_bls_sk = BlsSecretKey::random();
let new_client_pk = PublicKey::from(new_bls_sk.public_key());
let new_wallet: XorName = new_client_pk.into();
let txn = unwrap!(wallet_create_balance(
&bls_sk,
new_client_pk,
ten_coins,
None
));
assert_eq!(txn.amount, ten_coins);
let txn2 = unwrap!(wallet_transfer_coins(&bls_sk, new_wallet, ten_coins, None));
assert_eq!(txn2.amount, ten_coins);
let client_balance = unwrap!(wallet_get_balance(&bls_sk));
let mut expected = unwrap!(Coins::from_str("30"));
expected = unwrap!(expected.checked_sub(*COST_OF_PUT));
assert_eq!(client_balance, expected);
let new_client_balance = unwrap!(wallet_get_balance(&new_bls_sk));
assert_eq!(new_client_balance, unwrap!(Coins::from_str("20")));
}
#[test]
pub fn deletions_should_be_free() {
let name = XorName(rand::random());
let tag = 10;
random_client(move |client| {
let c2 = client.clone();
let c3 = client.clone();
let c4 = client.clone();
let c5 = client.clone();
let c6 = client.clone();
let c7 = client.clone();
let c8 = client.clone();
let idata = UnpubImmutableData::new(
unwrap!(generate_random_vector::<u8>(10)),
client.public_key(),
);
let address = *idata.name();
client
.put_idata(idata)
.and_then(move |_| {
let mut adata = UnpubSeqAppendOnlyData::new(name, tag);
let owner = ADataOwner {
public_key: c2.public_key(),
entries_index: 0,
permissions_index: 0,
};
unwrap!(adata.append_owner(owner, 0));
c2.put_adata(adata.into())
})
.and_then(move |_| {
let mdata = UnseqMutableData::new(name, tag, c3.public_key());
c3.put_unseq_mutable_data(mdata)
})
.and_then(move |_| c4.get_balance(None))
.and_then(move |balance| {
c5.delete_adata(ADataAddress::from_kind(ADataKind::UnpubSeq, name, tag))
.map(move |_| balance)
})
.and_then(move |balance| {
c6.delete_mdata(MDataAddress::from_kind(MDataKind::Unseq, name, tag))
.map(move |_| balance)
})
.and_then(move |balance| c7.del_unpub_idata(address).map(move |_| balance))
.and_then(move |balance| {
c8.get_balance(None)
.map(move |bal| assert_eq!(bal, balance))
})
});
}
}