#![allow(clippy::cognitive_complexity)]
#![allow(unused_imports)]
use crate::client::mock::vault::Vault;
use crate::client::{SafeKey, COST_OF_PUT};
use crate::config_handler::{Config, DevConfig};
use crate::utils::test_utils::{gen_app_id, gen_client_id};
use crate::{utils, NetworkEvent, QuicP2pConfig};
use super::connection_manager::ConnectionManager;
use crate::btree_map;
use bincode::serialize;
use futures::channel::mpsc::{self, UnboundedReceiver};
use futures::Future;
use rand::thread_rng;
use safe_nd::{
AppFullId, AppPermissions, ClientFullId, ClientRequest, Coins, CoinsRequest, Error, IData,
IDataRequest, LoginPacketRequest, MData, MDataAction, MDataAddress, MDataEntries,
MDataEntryActions, MDataPermissionSet, MDataRequest, MDataSeqEntryAction, MDataSeqEntryActions,
MDataSeqValue, MDataValue, MDataValues, Message, MessageId, PubImmutableData, PublicId,
PublicKey, Request, RequestType, Response, SeqMutableData, UnpubImmutableData,
UnseqMutableData,
};
use std::collections::{BTreeMap, BTreeSet};
use std::convert::TryInto;
use std::str::FromStr;
use std::sync::mpsc as std_mpsc;
use std::sync::{Arc, Mutex};
use std::time::Duration;
use threshold_crypto::SecretKey;
use unwrap::unwrap;
use xor_name::XorName;
macro_rules! send_req_expect_failure {
($cm:expr, $sender:expr, $req:expr, $err:path) => {
let expected_response = $req.error_response($err);
let response = process_request($cm, $sender, $req).await;
assert_eq!(response, expected_response);
};
}
macro_rules! send_req_expect_ok {
($cm:expr, $sender:expr, $req:expr, $res:expr) => {
let response = process_request($cm, $sender, $req).await;
assert_eq!($res, unwrap!(response.try_into()));
};
}
async fn process_request(
connection_manager: &mut ConnectionManager,
sender: &SafeKey,
request: Request,
) -> Response {
let sign = request.get_type() != RequestType::PublicGet;
let message_id = MessageId::new();
let signature = if sign {
Some(sender.sign(&unwrap!(serialize(&(&request, message_id)))))
} else {
None
};
let message = Message::Request {
request,
message_id,
signature,
};
unwrap!(connection_manager.send(&sender.public_id(), &message).await)
}
#[tokio::test]
async fn immutable_data_basics() {
let (mut connection_manager, _, client_safe_key, _) = setup(None).await;
let orig_data: IData =
PubImmutableData::new(unwrap!(utils::generate_random_vector(100))).into();
let get_request = Request::IData(IDataRequest::Get(*orig_data.address()));
send_req_expect_failure!(
&mut connection_manager,
&client_safe_key,
get_request.clone(),
Error::NoSuchData
);
let put_request = Request::IData(IDataRequest::Put(orig_data.clone()));
send_req_expect_ok!(
&mut connection_manager,
&client_safe_key,
put_request.clone(),
()
);
send_req_expect_ok!(
&mut connection_manager,
&client_safe_key,
get_request.clone(),
orig_data
);
let balance = unwrap!(Coins::from_str("10"));
let balance = unwrap!(balance.checked_sub(COST_OF_PUT));
send_req_expect_ok!(
&mut connection_manager,
&client_safe_key,
Request::Coins(CoinsRequest::GetBalance),
balance
);
send_req_expect_ok!(&mut connection_manager, &client_safe_key, put_request, ());
send_req_expect_ok!(
&mut connection_manager,
&client_safe_key,
get_request,
orig_data
);
let balance = unwrap!(balance.checked_sub(COST_OF_PUT));
send_req_expect_ok!(
&mut connection_manager,
&client_safe_key,
Request::Coins(CoinsRequest::GetBalance),
balance
);
}
#[tokio::test]
async fn mutable_data_basics() {
let (mut connection_manager, _, client_safe_key, owner_key) = setup(None).await;
let name = rand::random();
let tag = 1000u64;
let data = SeqMutableData::new(name, tag, owner_key);
let data1_address = *data.address();
send_req_expect_failure!(
&mut connection_manager,
&client_safe_key,
Request::MData(MDataRequest::GetVersion(data1_address)),
Error::NoSuchData
);
send_req_expect_failure!(
&mut connection_manager,
&client_safe_key,
Request::MData(MDataRequest::ListEntries(data1_address)),
Error::NoSuchData
);
send_req_expect_ok!(
&mut connection_manager,
&client_safe_key,
Request::MData(MDataRequest::Put(data.into())),
()
);
let tag2 = 1001u64;
let data2: MData = SeqMutableData::new(name, tag2, owner_key).into();
let data2_address = *data2.address();
send_req_expect_ok!(
&mut connection_manager,
&client_safe_key,
Request::MData(MDataRequest::Put(data2.clone())),
()
);
let response = process_request(
&mut connection_manager,
&client_safe_key,
Request::MData(MDataRequest::GetVersion(data2_address)),
)
.await;
assert_eq!(response, Response::GetMDataVersion(Ok(0)));
send_req_expect_ok!(
&mut connection_manager,
&client_safe_key,
Request::MData(MDataRequest::Get(data2_address)),
data2
);
send_req_expect_ok!(
&mut connection_manager,
&client_safe_key,
Request::MData(MDataRequest::ListEntries(data2_address)),
MDataEntries::from(BTreeMap::<_, MDataSeqValue>::new())
);
send_req_expect_ok!(
&mut connection_manager,
&client_safe_key,
Request::MData(MDataRequest::ListKeys(data2_address)),
BTreeSet::new()
);
send_req_expect_ok!(
&mut connection_manager,
&client_safe_key,
Request::MData(MDataRequest::ListValues(data2_address)),
MDataValues::from(Vec::<MDataSeqValue>::new())
);
let key0 = b"key0";
let key1 = b"key1";
let value0_v0 = unwrap!(utils::generate_random_vector(10));
let value1_v0 = unwrap!(utils::generate_random_vector(10));
let actions: MDataSeqEntryActions = btree_map![
key0.to_vec() => MDataSeqEntryAction::Ins(MDataSeqValue {
data: value0_v0.clone(),
version: 0,
}),
key1.to_vec() => MDataSeqEntryAction::Ins(MDataSeqValue {
data: value1_v0.clone(),
version: 0,
})
]
.into();
send_req_expect_ok!(
&mut connection_manager,
&client_safe_key,
Request::MData(MDataRequest::MutateEntries {
address: data2_address,
actions: actions.into()
}),
()
);
let response = process_request(
&mut connection_manager,
&client_safe_key,
Request::MData(MDataRequest::ListEntries(data2_address)),
)
.await;
let entries: MDataEntries = unwrap!(response.try_into());
match entries {
MDataEntries::Seq(entries) => {
assert_eq!(entries.len(), 2);
let entry = unwrap!(entries.get(&key0[..]));
assert_eq!(entry.data, value0_v0);
assert_eq!(entry.version, 0);
let entry = unwrap!(entries.get(&key1[..]));
assert_eq!(entry.data, value1_v0);
assert_eq!(entry.version, 0);
}
_ => panic!("MData type mismatch"),
}
send_req_expect_ok!(
&mut connection_manager,
&client_safe_key,
Request::MData(MDataRequest::ListEntries(data1_address)),
MDataEntries::from(BTreeMap::<_, MDataSeqValue>::new())
);
let response = process_request(
&mut connection_manager,
&client_safe_key,
Request::MData(MDataRequest::ListKeys(data2_address)),
)
.await;
match response {
Response::ListMDataKeys(Ok(keys)) => {
assert_eq!(keys.len(), 2);
assert!(keys.contains(&key0[..]));
assert!(keys.contains(&key1[..]));
}
Response::ListMDataKeys(err) => panic!("Unexpected error: {:?}", err),
res => panic!("Unexpected response: {:?}", res),
}
let response = process_request(
&mut connection_manager,
&client_safe_key,
Request::MData(MDataRequest::ListValues(data2_address)),
)
.await;
match response {
Response::ListMDataValues(Ok(values)) => match values {
MDataValues::Seq(seq_values) => assert_eq!(seq_values.len(), 2),
_ => panic!("MData type mismatch"),
},
Response::ListMDataValues(err) => panic!("Unexpected error: {:?}", err),
res => panic!("Unexpected response: {:?}", res),
}
send_req_expect_ok!(
&mut connection_manager,
&client_safe_key,
Request::MData(MDataRequest::GetValue {
address: data2_address,
key: key0.to_vec()
}),
MDataValue::Seq(MDataSeqValue {
data: value0_v0,
version: 0,
})
);
let key2 = b"key2";
send_req_expect_failure!(
&mut connection_manager,
&client_safe_key,
Request::MData(MDataRequest::GetValue {
address: data2_address,
key: key2.to_vec()
}),
Error::NoSuchEntry
);
let value0_v1 = unwrap!(utils::generate_random_vector(10));
let value2_v0 = unwrap!(utils::generate_random_vector(10));
let actions: MDataSeqEntryActions = btree_map![
key0.to_vec() => MDataSeqEntryAction::Update(MDataSeqValue {
data: value0_v1.clone(),
version: 1,
}),
key1.to_vec() => MDataSeqEntryAction::Del(1),
key2.to_vec() => MDataSeqEntryAction::Ins(MDataSeqValue {
data: value2_v0.clone(),
version: 0,
})
]
.into();
send_req_expect_ok!(
&mut connection_manager,
&client_safe_key,
Request::MData(MDataRequest::MutateEntries {
address: data2_address,
actions: actions.into()
}),
()
);
let response = process_request(
&mut connection_manager,
&client_safe_key,
Request::MData(MDataRequest::ListEntries(data2_address)),
)
.await;
let entries: MDataEntries = unwrap!(response.try_into());
match entries {
MDataEntries::Seq(entries) => {
assert_eq!(entries.len(), 2);
let entry = unwrap!(entries.get(&key0[..]));
assert_eq!(entry.data, value0_v1);
assert_eq!(entry.version, 1);
let entry = entries.get(&key1[..]);
assert!(entry.is_none());
let entry = unwrap!(entries.get(&key2[..]));
assert_eq!(entry.data, value2_v0);
assert_eq!(entry.version, 0);
}
_ => panic!("MData type mismatch"),
}
}
#[tokio::test]
async fn mutable_data_reclaim() {
let (mut connection_manager, _, client_safe_key, owner_key) = setup(None).await;
let name = rand::random();
let tag = 1000u64;
let data = SeqMutableData::new(name, tag, owner_key);
let address: MDataAddress = *data.address();
send_req_expect_ok!(
&mut connection_manager,
&client_safe_key,
Request::MData(MDataRequest::Put(data.into())),
()
);
let key0 = b"key0";
let value0 = unwrap!(utils::generate_random_vector(10));
let actions: MDataSeqEntryActions = btree_map![
key0.to_vec() => MDataSeqEntryAction::Ins(MDataSeqValue {
data: value0.clone(),
version: 0,
}),
]
.into();
send_req_expect_ok!(
&mut connection_manager,
&client_safe_key,
Request::MData(MDataRequest::MutateEntries {
address,
actions: actions.into()
}),
()
);
let actions: MDataSeqEntryActions = btree_map![
key0.to_vec() => MDataSeqEntryAction::Update(MDataSeqValue {
data: value0,
version: 1,
})
]
.into();
send_req_expect_ok!(
&mut connection_manager,
&client_safe_key,
Request::MData(MDataRequest::MutateEntries {
address,
actions: actions.into()
}),
()
);
let response = process_request(
&mut connection_manager,
&client_safe_key,
Request::MData(MDataRequest::GetVersion(address)),
)
.await;
assert_eq!(response, Response::GetMDataVersion(Ok(0)));
let actions: MDataSeqEntryActions = btree_map![
key0.to_vec() => MDataSeqEntryAction::Del(3),
]
.into();
let response = process_request(
&mut connection_manager,
&client_safe_key,
Request::MData(MDataRequest::MutateEntries {
address,
actions: actions.into(),
}),
)
.await;
match response {
Response::Mutation(Err(Error::InvalidEntryActions(_))) => (),
Response::Mutation(Ok(())) => panic!("Unexpected success"),
res => panic!("Unexpected response: {:?}", res),
}
let actions: MDataSeqEntryActions = btree_map![
key0.to_vec() => MDataSeqEntryAction::Del(2),
]
.into();
send_req_expect_ok!(
&mut connection_manager,
&client_safe_key,
Request::MData(MDataRequest::MutateEntries {
address,
actions: actions.into()
}),
()
);
}
#[tokio::test]
async fn mutable_data_entry_versioning() {
let (mut connection_manager, _, client_safe_key, owner_key) = setup(None).await;
let name = rand::random();
let tag = 1000u64;
let data = SeqMutableData::new(name, tag, owner_key);
let address = *data.address();
send_req_expect_ok!(
&mut connection_manager,
&client_safe_key,
Request::MData(MDataRequest::Put(data.into())),
()
);
let key = b"key0";
let value_v0 = unwrap!(utils::generate_random_vector(10));
let actions: MDataSeqEntryActions = btree_map![
key.to_vec() => MDataSeqEntryAction::Ins(MDataSeqValue {
data: value_v0,
version: 0,
})
]
.into();
send_req_expect_ok!(
&mut connection_manager,
&client_safe_key,
Request::MData(MDataRequest::MutateEntries {
address,
actions: actions.into(),
}),
()
);
let value_v1 = unwrap!(utils::generate_random_vector(10));
let actions: MDataSeqEntryActions = btree_map![
key.to_vec() => MDataSeqEntryAction::Update(MDataSeqValue {
data: value_v1.clone(),
version: 0,
})
]
.into();
let response = process_request(
&mut connection_manager,
&client_safe_key,
Request::MData(MDataRequest::MutateEntries {
address,
actions: actions.into(),
}),
)
.await;
match response {
Response::Mutation(Err(Error::InvalidEntryActions(_))) => (),
Response::Mutation(Ok(())) => panic!("Unexpected success"),
res => panic!("Unexpected response: {:?}", res),
}
let actions: MDataSeqEntryActions =
MDataSeqEntryActions::new().update(key.to_vec(), value_v1.clone(), 314_159_265);
let response = process_request(
&mut connection_manager,
&client_safe_key,
Request::MData(MDataRequest::MutateEntries {
address,
actions: actions.into(),
}),
)
.await;
match response {
Response::Mutation(Err(Error::InvalidEntryActions(_))) => (),
Response::Mutation(Ok(())) => panic!("Unexpected success"),
res => panic!("Unexpected response: {:?}", res),
}
let actions: MDataSeqEntryActions = btree_map![
key.to_vec() => MDataSeqEntryAction::Update(MDataSeqValue {
data: value_v1,
version: 1,
})
]
.into();
send_req_expect_ok!(
&mut connection_manager,
&client_safe_key,
Request::MData(MDataRequest::MutateEntries {
address,
actions: actions.into(),
}),
()
);
let actions: MDataSeqEntryActions = btree_map![
key.to_vec() => MDataSeqEntryAction::Del(1)
]
.into();
let response = process_request(
&mut connection_manager,
&client_safe_key,
Request::MData(MDataRequest::MutateEntries {
address,
actions: actions.into(),
}),
)
.await;
match response {
Response::Mutation(Err(Error::InvalidEntryActions(_))) => (),
Response::Mutation(Ok(())) => panic!("Unexpected success"),
res => panic!("Unexpected response: {:?}", res),
}
let actions: MDataSeqEntryActions = btree_map![
key.to_vec() => MDataSeqEntryAction::Del(2)
]
.into();
send_req_expect_ok!(
&mut connection_manager,
&client_safe_key,
Request::MData(MDataRequest::MutateEntries {
address,
actions: actions.into(),
}),
()
);
}
#[tokio::test]
async fn mutable_data_permissions() {
let (mut connection_manager, _, client_safe_key, owner_key) = setup(None).await;
let name = rand::random();
let tag = 1000u64;
let key0 = b"key0";
let value0_v0 = unwrap!(utils::generate_random_vector(10));
let entries = btree_map![
key0.to_vec() => MDataSeqValue { data: value0_v0, version: 0 }
];
let data = SeqMutableData::new_with_data(name, tag, entries, Default::default(), owner_key);
let address: MDataAddress = *data.address();
send_req_expect_ok!(
&mut connection_manager,
&client_safe_key,
Request::MData(MDataRequest::Put(data.into())),
()
);
let response = process_request(
&mut connection_manager,
&client_safe_key,
Request::MData(MDataRequest::ListPermissions(address)),
)
.await;
let permissions: BTreeMap<PublicKey, MDataPermissionSet> = unwrap!(response.try_into());
assert!(permissions.is_empty());
let value0_v1 = unwrap!(utils::generate_random_vector(10));
let actions = MDataSeqEntryActions::new().update(key0.to_vec(), value0_v1, 1);
send_req_expect_ok!(
&mut connection_manager,
&client_safe_key,
Request::MData(MDataRequest::MutateEntries {
address,
actions: actions.into()
}),
()
);
let (app_safe_key, mut connection_manager2, _) = register_new_app(
&mut connection_manager,
&client_safe_key,
AppPermissions {
get_balance: true,
transfer_coins: true,
perform_mutations: true,
},
)
.await;
let value0_v2 = unwrap!(utils::generate_random_vector(10));
let actions = MDataSeqEntryActions::new().update(key0.to_vec(), value0_v2.clone(), 2);
let mutation_request = Request::MData(MDataRequest::MutateEntries {
address,
actions: actions.into(),
});
send_req_expect_failure!(
&mut connection_manager2,
&app_safe_key,
mutation_request.clone(),
Error::AccessDenied
);
let permissions = MDataPermissionSet::new()
.allow(MDataAction::Update)
.allow(MDataAction::Read);
let update_perms_req = Request::MData(MDataRequest::SetUserPermissions {
address,
user: app_safe_key.public_key(),
permissions,
version: 1,
});
send_req_expect_failure!(
&mut connection_manager,
&app_safe_key,
update_perms_req.clone(),
Error::AccessDenied
);
send_req_expect_failure!(
&mut connection_manager2,
&app_safe_key,
mutation_request.clone(),
Error::AccessDenied
);
send_req_expect_ok!(
&mut connection_manager,
&client_safe_key,
update_perms_req,
()
);
let response = process_request(
&mut connection_manager,
&client_safe_key,
Request::MData(MDataRequest::GetVersion(address)),
)
.await;
assert_eq!(response, Response::GetMDataVersion(Ok(1)));
let key1 = b"key1";
let value1_v0 = unwrap!(utils::generate_random_vector(10));
let actions: MDataSeqEntryActions = btree_map![
key1.to_vec() => MDataSeqEntryAction::Ins(MDataSeqValue {
data: value1_v0,
version: 0,
})
]
.into();
let insertion_request = Request::MData(MDataRequest::MutateEntries {
address,
actions: actions.into(),
});
send_req_expect_failure!(
&mut connection_manager2,
&app_safe_key,
insertion_request.clone(),
Error::AccessDenied
);
let actions: MDataSeqEntryActions = btree_map![
key0.to_vec() => MDataSeqEntryAction::Update(MDataSeqValue {
data: value0_v2,
version: 2,
})
]
.into();
send_req_expect_ok!(
&mut connection_manager2,
&app_safe_key,
Request::MData(MDataRequest::MutateEntries {
address,
actions: actions.into(),
}),
()
);
let permissions = MDataPermissionSet::new()
.allow(MDataAction::Read)
.allow(MDataAction::Insert)
.allow(MDataAction::Update);
let invalid_update_perms_req = Request::MData(MDataRequest::SetUserPermissions {
address,
user: app_safe_key.public_key(),
permissions: permissions.clone(),
version: 1,
});
let error = Error::InvalidSuccessor(1);
send_req_expect_failure!(
&mut connection_manager,
&client_safe_key,
invalid_update_perms_req,
error
);
let valid_update_perms_req = Request::MData(MDataRequest::SetUserPermissions {
address,
user: app_safe_key.public_key(),
permissions,
version: 2,
});
send_req_expect_ok!(
&mut connection_manager,
&client_safe_key,
valid_update_perms_req,
()
);
send_req_expect_ok!(
&mut connection_manager2,
&app_safe_key,
insertion_request,
()
);
send_req_expect_ok!(
&mut connection_manager2,
&client_safe_key,
Request::MData(MDataRequest::DelUserPermissions {
address,
user: app_safe_key.public_key(),
version: 3
}),
()
);
send_req_expect_failure!(
&mut connection_manager2,
&app_safe_key,
mutation_request.clone(),
Error::AccessDenied
);
let permissions = MDataPermissionSet::new().allow(MDataAction::ManagePermissions);
send_req_expect_ok!(
&mut connection_manager,
&client_safe_key,
Request::MData(MDataRequest::SetUserPermissions {
address,
user: app_safe_key.public_key(),
permissions,
version: 4
}),
()
);
send_req_expect_failure!(
&mut connection_manager2,
&app_safe_key,
mutation_request,
Error::AccessDenied
);
let permissions = MDataPermissionSet::new().allow(MDataAction::Update);
send_req_expect_ok!(
&mut connection_manager2,
&app_safe_key,
Request::MData(MDataRequest::SetUserPermissions {
address,
user: app_safe_key.public_key(),
permissions,
version: 5
}),
()
);
let value1_v1 = unwrap!(utils::generate_random_vector(10));
let actions = MDataSeqEntryActions::new().update(key1.to_vec(), value1_v1, 1);
send_req_expect_ok!(
&mut connection_manager2,
&app_safe_key,
Request::MData(MDataRequest::MutateEntries {
address,
actions: actions.into()
}),
()
);
}
#[tokio::test]
async fn mutable_data_ownership() {
let (mut connection_manager, _, client_safe_key, owner_key) = setup(None).await;
let (app_safe_key, mut connection_manager2, _) = register_new_app(
&mut connection_manager,
&client_safe_key,
AppPermissions {
get_balance: true,
transfer_coins: true,
perform_mutations: true,
},
)
.await;
let name = rand::random();
let tag = 1000u64;
send_req_expect_failure!(
&mut connection_manager2,
&app_safe_key,
Request::MData(MDataRequest::Put(
SeqMutableData::new(name, tag, app_safe_key.public_key()).into()
)),
Error::InvalidOwners
);
let data: MData = SeqMutableData::new(name, tag, owner_key).into();
send_req_expect_ok!(
&mut connection_manager,
&app_safe_key,
Request::MData(MDataRequest::Put(data)),
()
);
}
#[tokio::test]
async fn pub_idata_rpc() {
let (mut connection_manager, _, client_safe_key, _) = setup(None).await;
let (mut connection_manager2, _, client2_safe_key, _) = setup(None).await;
let orig_data: IData =
PubImmutableData::new(unwrap!(utils::generate_random_vector(100))).into();
let get_request = Request::IData(IDataRequest::Get(*orig_data.address()));
{
let put_request = Request::IData(IDataRequest::Put(orig_data.clone()));
send_req_expect_ok!(&mut connection_manager, &client_safe_key, put_request, ());
}
{
send_req_expect_ok!(
&mut connection_manager,
&client_safe_key,
get_request.clone(),
orig_data.clone()
);
}
let app_perms = AppPermissions {
transfer_coins: true,
get_balance: true,
perform_mutations: true,
};
let (app_key, mut app_conn_manager, _) =
register_new_app(&mut connection_manager2, &client2_safe_key, app_perms).await;
{
send_req_expect_ok!(&mut app_conn_manager, &app_key, get_request, orig_data);
}
}
#[tokio::test]
async fn unpub_idata_rpc() {
let (mut connection_manager, _, client_safe_key, _) = setup(None).await;
let value = unwrap!(utils::generate_random_vector::<u8>(10));
let data: IData = UnpubImmutableData::new(value, client_safe_key.public_key()).into();
let address = *data.address();
{
let put_request = Request::IData(IDataRequest::Put(data.clone()));
send_req_expect_ok!(&mut connection_manager, &client_safe_key, put_request, ());
}
let get_request = Request::IData(IDataRequest::Get(address));
send_req_expect_ok!(
&mut connection_manager,
&client_safe_key,
get_request.clone(),
data
);
let app_perms = AppPermissions {
transfer_coins: true,
get_balance: true,
perform_mutations: true,
};
let (mut conn_manager2, _, client2_safe_key, _) = setup(None).await;
let (app_key, mut app_conn_manager, _) =
register_new_app(&mut conn_manager2, &client2_safe_key, app_perms).await;
send_req_expect_failure!(
&mut app_conn_manager,
&app_key,
get_request,
Error::AccessDenied
);
let del_request = Request::IData(IDataRequest::DeleteUnpub(address));
send_req_expect_failure!(
&mut app_conn_manager,
&app_key,
del_request,
Error::AccessDenied
);
}
#[tokio::test]
async fn unpub_md() {
let (mut connection_manager, _, client_safe_key, _) = setup(None).await;
let name = XorName(rand::random());
let tag = 15001;
let data: MData = UnseqMutableData::new(name, tag, client_safe_key.public_key()).into();
send_req_expect_ok!(
&mut connection_manager,
&client_safe_key,
Request::MData(MDataRequest::Put(data.clone())),
()
);
send_req_expect_ok!(
&mut connection_manager,
&client_safe_key,
Request::MData(MDataRequest::Get(*data.address())),
data
);
}
#[tokio::test]
async fn auth_keys() {
let (mut connection_manager, _, client_safe_key, _) = setup(None).await;
let mut response = process_request(
&mut connection_manager,
&client_safe_key,
Request::Client(ClientRequest::ListAuthKeysAndVersion),
)
.await;
let (keys, version): (BTreeMap<_, _>, u64) = unwrap!(response.try_into());
assert_eq!(keys.len(), 0);
assert_eq!(version, 0);
let app_key = PublicKey::from(SecretKey::random().public_key());
let test_ins_auth_key_req = Request::Client(ClientRequest::InsAuthKey {
key: app_key,
version: 0,
permissions: AppPermissions {
transfer_coins: true,
get_balance: true,
perform_mutations: true,
},
});
let error = Error::InvalidSuccessor(0);
send_req_expect_failure!(
&mut connection_manager,
&client_safe_key,
test_ins_auth_key_req,
error
);
let ins_auth_key_req = Request::Client(ClientRequest::InsAuthKey {
key: app_key,
version: 1,
permissions: AppPermissions {
transfer_coins: true,
get_balance: true,
perform_mutations: true,
},
});
send_req_expect_ok!(
&mut connection_manager,
&client_safe_key,
ins_auth_key_req,
()
);
response = process_request(
&mut connection_manager,
&client_safe_key,
Request::Client(ClientRequest::ListAuthKeysAndVersion),
)
.await;
match response {
Response::ListAuthKeysAndVersion(res) => match res {
Ok(keys) => {
assert_eq!(unwrap!(keys.0.get(&app_key)).transfer_coins, true);
assert_eq!(unwrap!(keys.0.get(&app_key)).get_balance, true);
assert_eq!(unwrap!(keys.0.get(&app_key)).perform_mutations, true);
assert_eq!(keys.1, 1);
}
Err(e) => panic!("Unexpected error: {:?}", e),
},
res => panic!("Unexpected Response {:?}", res),
}
let test_del_auth_key_req = Request::Client(ClientRequest::DelAuthKey {
key: app_key,
version: 0,
});
let error = Error::InvalidSuccessor(1);
send_req_expect_failure!(
&mut connection_manager,
&client_safe_key,
test_del_auth_key_req,
error
);
let test_auth_key = PublicKey::from(SecretKey::random().public_key());
let test1_del_auth_key_req = Request::Client(ClientRequest::DelAuthKey {
key: test_auth_key,
version: 2,
});
send_req_expect_failure!(
&mut connection_manager,
&client_safe_key,
test1_del_auth_key_req,
Error::NoSuchKey
);
let del_auth_key_req = Request::Client(ClientRequest::DelAuthKey {
key: app_key,
version: 2,
});
send_req_expect_ok!(
&mut connection_manager,
&client_safe_key,
del_auth_key_req,
()
);
response = process_request(
&mut connection_manager,
&client_safe_key,
Request::Client(ClientRequest::ListAuthKeysAndVersion),
)
.await;
match response {
Response::ListAuthKeysAndVersion(res) => match res {
Ok(keys) => {
assert_eq!(keys.0.len(), 0);
assert_eq!(keys.1, 2);
}
Err(e) => panic!("Unexpected error: {:?}", e),
},
res => panic!("Unexpected Response {:?}", res),
}
}
#[tokio::test]
async fn auth_actions_from_app() {
let (mut connection_manager, _, client_safe_key, owner_key) = setup(None).await;
let app_perms = AppPermissions {
transfer_coins: true,
get_balance: true,
perform_mutations: true,
};
let (app_key, mut app_conn_manager, _) =
register_new_app(&mut connection_manager, &client_safe_key, app_perms).await;
let name = XorName(rand::random());
let tag = 15002;
let mut permissions: BTreeMap<_, _> = Default::default();
let _ = permissions.insert(
app_key.public_key(),
MDataPermissionSet::new().allow(MDataAction::Read),
);
let data: MData =
UnseqMutableData::new_with_data(name, tag, Default::default(), permissions, owner_key)
.into();
let address = *data.address();
send_req_expect_ok!(
&mut connection_manager,
&client_safe_key,
Request::MData(MDataRequest::Put(data.clone())),
()
);
send_req_expect_ok!(
&mut connection_manager,
&client_safe_key,
Request::MData(MDataRequest::Get(address)),
data
);
send_req_expect_failure!(
&mut app_conn_manager,
&app_key,
Request::MData(MDataRequest::Delete(address)),
Error::AccessDenied
);
send_req_expect_failure!(
&mut app_conn_manager,
&app_key,
Request::Client(ClientRequest::ListAuthKeysAndVersion),
Error::AccessDenied
);
send_req_expect_failure!(
&mut app_conn_manager,
&app_key,
Request::Client(ClientRequest::DelAuthKey {
key: app_key.public_key(),
version: 1,
}),
Error::AccessDenied
);
}
#[tokio::test]
async fn low_balance_check() {
for unlimited in &[true, false] {
let (mut connection_manager, _, client_safe_key, owner_key) = setup(Some(Config {
quic_p2p: QuicP2pConfig::default(),
dev: Some(DevConfig {
mock_unlimited_coins: *unlimited,
mock_in_memory_storage: false,
mock_vault_path: None,
}),
}))
.await;
let name: XorName = rand::random();
let tag = 1000u64;
let data: MData = UnseqMutableData::new(name, tag, owner_key).into();
send_req_expect_ok!(
&mut connection_manager,
&client_safe_key,
Request::MData(MDataRequest::Put(data.clone())),
()
);
let vec_data = unwrap!(utils::generate_random_vector(10));
let idata: IData = PubImmutableData::new(vec_data).into();
let rpc_response = process_request(
&mut connection_manager,
&client_safe_key,
Request::Coins(CoinsRequest::GetBalance),
)
.await;
let balance: Coins = match rpc_response {
Response::GetBalance(res) => unwrap!(res),
_ => panic!("Unexpected response"),
};
let new_balance_owner: PublicKey = SecretKey::random().public_key().into();
let response = process_request(
&mut connection_manager,
&client_safe_key,
Request::Coins(CoinsRequest::CreateBalance {
new_balance_owner,
amount: unwrap!(balance.checked_sub(COST_OF_PUT)),
transaction_id: rand::random(),
}),
)
.await;
match response {
Response::Transaction(Ok(_)) => (),
x => panic!("Unexpected Error {:?}", x),
}
let response = process_request(
&mut connection_manager,
&client_safe_key,
Request::IData(IDataRequest::Put(idata.clone())),
)
.await;
match response {
Response::Mutation(res) => assert_eq!(*unlimited, res.is_ok()), res => panic!("Unexpected response {:?}", res),
}
send_req_expect_ok!(
&mut connection_manager,
&client_safe_key,
Request::MData(MDataRequest::Get(*data.address())),
data
);
}
}
#[tokio::test]
#[should_panic]
async fn invalid_config_mock_vault_path() {
if std::env::var("SAFE_MOCK_IN_MEMORY_STORAGE").is_ok()
|| std::env::var("SAFE_MOCK_VAULT_PATH").is_ok()
{
panic!("This test should run without SAFE env vars set.");
}
let (mut _conn_manager, _, _client_safe_key, _owner_key) = setup(Some(Config {
quic_p2p: QuicP2pConfig::default(),
dev: Some(DevConfig {
mock_unlimited_coins: false,
mock_in_memory_storage: false,
mock_vault_path: Some(String::from("./this_path_should_not_exist")),
}),
}))
.await;
}
#[tokio::test]
async fn config_mock_vault_path() {
if std::env::var("SAFE_MOCK_IN_MEMORY_STORAGE").is_ok() {
return;
}
match std::fs::create_dir("./tmp") {
Ok(_) => (),
Err(ref e) if e.kind() == std::io::ErrorKind::AlreadyExists => (),
_ => panic!("Error creating directory"),
}
let (mut conn_manager, _, client_safe_key, owner_key) = setup(Some(Config {
quic_p2p: QuicP2pConfig::default(),
dev: Some(DevConfig {
mock_unlimited_coins: false,
mock_in_memory_storage: false,
mock_vault_path: Some(String::from("./tmp")),
}),
}))
.await;
let name = rand::random();
let tag = 1000u64;
let data: MData = UnseqMutableData::new(name, tag, owner_key).into();
send_req_expect_ok!(
&mut conn_manager,
&client_safe_key,
Request::MData(MDataRequest::Put(data.clone())),
()
);
send_req_expect_ok!(
&mut conn_manager,
&client_safe_key,
Request::MData(MDataRequest::Get(*data.address())),
data
);
unwrap!(std::fs::remove_dir_all("./tmp"));
}
#[tokio::test]
async fn request_hooks() {
let (mut conn_manager, _, client_safe_key, owner_key) = setup(None).await;
let custom_error: Error = Error::NetworkOther("hello world".to_string());
let expected_error = custom_error.clone();
conn_manager.set_request_hook(move |req| {
match *req {
Request::MData(MDataRequest::Put(ref data)) if data.tag() == 10_000u64 => {
Some(Response::Mutation(Ok(())))
}
Request::MData(MDataRequest::MutateEntries { address, .. })
if address.tag() == 12_345u64 =>
{
Some(Response::Mutation(Err(custom_error.clone())))
}
_ => None,
}
});
let name = rand::random();
let tag = 10_000u64;
let data = SeqMutableData::new(name, tag, owner_key);
send_req_expect_ok!(
&mut conn_manager,
&client_safe_key,
Request::MData(MDataRequest::Put(data.clone().into())),
()
);
send_req_expect_failure!(
&mut conn_manager,
&client_safe_key,
Request::MData(MDataRequest::GetVersion(*data.address())),
Error::NoSuchData
);
let name2 = rand::random();
let tag2 = 12_345u64;
let data2 = SeqMutableData::new(name2, tag2, owner_key);
send_req_expect_ok!(
&mut conn_manager,
&client_safe_key,
Request::MData(MDataRequest::Put(data2.clone().into())),
()
);
let key0 = b"key0";
let value0_v0 = unwrap!(utils::generate_random_vector(10));
let mut seq_actions = MDataSeqEntryActions::new();
seq_actions.add_action(
key0.to_vec(),
MDataSeqEntryAction::Ins(MDataSeqValue {
data: value0_v0,
version: 0,
}),
);
let actions: MDataEntryActions = seq_actions.into();
send_req_expect_failure!(
&mut conn_manager,
&client_safe_key,
Request::MData(MDataRequest::MutateEntries {
address: *data2.address(),
actions: actions.clone(),
}),
expected_error
);
conn_manager.remove_request_hook();
send_req_expect_ok!(
&mut conn_manager,
&client_safe_key,
Request::MData(MDataRequest::MutateEntries {
address: *data2.address(),
actions,
}),
()
);
}
async fn setup(
vault_config: Option<Config>,
) -> (
ConnectionManager,
UnboundedReceiver<NetworkEvent>,
SafeKey,
PublicKey,
) {
let client_id = gen_client_id();
let (conn_manager_tx, conn_manager_rx) = mpsc::unbounded();
let mut conn_manager = if let Some(given_config) = vault_config {
unwrap!(ConnectionManager::new_with_vault(
given_config,
&conn_manager_tx
))
} else {
unwrap!(ConnectionManager::new(Default::default(), &conn_manager_tx))
};
let coins = unwrap!(Coins::from_str("10"));
let client_safe_key = register_client(&mut conn_manager, coins, client_id).await;
let owner_key = client_safe_key.public_key();
(conn_manager, conn_manager_rx, client_safe_key, owner_key)
}
async fn register_client(
conn_manager: &mut ConnectionManager,
coins: Coins,
client_id: ClientFullId,
) -> SafeKey {
let client_public_key = client_id.public_id().public_key();
conn_manager.create_balance(*client_public_key, coins).await;
SafeKey::client(client_id)
}
async fn register_new_app(
conn_manager: &mut ConnectionManager,
client_safe_key: &SafeKey,
permissions: AppPermissions,
) -> (SafeKey, ConnectionManager, UnboundedReceiver<NetworkEvent>) {
let client_id = unwrap!(client_safe_key.public_id().client_public_id()).clone();
let app_full_id = gen_app_id(client_id);
let response = process_request(
conn_manager,
client_safe_key,
Request::Client(ClientRequest::ListAuthKeysAndVersion),
)
.await;
let (_, version): (_, u64) = unwrap!(response.try_into());
send_req_expect_ok!(
conn_manager,
client_safe_key,
Request::Client(ClientRequest::InsAuthKey {
key: *app_full_id.public_id().public_key(),
version: version + 1,
permissions
}),
()
);
let (conn_manager_tx, conn_manager_rx) = mpsc::unbounded();
let connection_manager = unwrap!(ConnectionManager::new(Default::default(), &conn_manager_tx));
(
SafeKey::app(app_full_id),
connection_manager,
conn_manager_rx,
)
}