Introduces new environment variable to set public endpoint of the brain refactor redirect validation to db module validating pubsub node on all vm fixed some vm tests
456 lines
16 KiB
Rust
456 lines
16 KiB
Rust
use common::prepare_test_env::{
|
|
prepare_test_db, run_service_for_stream, run_service_in_background,
|
|
};
|
|
use common::test_utils::{admin_keys, airdrop, Key};
|
|
use common::vm_cli_utils::{
|
|
create_new_vm, list_accounts, list_all_app_contracts, list_all_vm_contracts, register_operator,
|
|
report_node,
|
|
};
|
|
use common::vm_daemon_utils::{mock_vm_daemon, register_vm_node};
|
|
use detee_shared::common_proto::{Empty, Pubkey};
|
|
use detee_shared::general_proto::brain_general_cli_client::BrainGeneralCliClient;
|
|
use detee_shared::general_proto::{AirdropReq, BanUserReq, SlashReq};
|
|
use detee_shared::vm_proto::brain_vm_daemon_client::BrainVmDaemonClient;
|
|
use futures::StreamExt;
|
|
use surreal_brain::constants::{ACCOUNT, ACTIVE_APP, ACTIVE_VM, BAN, TOKEN_DECIMAL, VM_NODE};
|
|
use surreal_brain::db::prelude as db;
|
|
use surreal_brain::db::vm::VmNodeWithReports;
|
|
|
|
mod common;
|
|
|
|
#[tokio::test]
|
|
async fn test_general_balance() {
|
|
/*
|
|
env_logger::builder()
|
|
.filter_level(log::LevelFilter::Info)
|
|
.filter_module("tungstenite", log::LevelFilter::Debug)
|
|
.filter_module("tokio_tungstenite", log::LevelFilter::Debug)
|
|
.init();
|
|
*/
|
|
prepare_test_db().await.unwrap();
|
|
|
|
let addr = run_service_in_background().await.unwrap();
|
|
let mut client = BrainGeneralCliClient::connect(format!("http://{}", addr)).await.unwrap();
|
|
|
|
let key = Key::new();
|
|
let pubkey = key.pubkey.clone();
|
|
let req_data = Pubkey { pubkey };
|
|
|
|
let req = key.sign_request(req_data).unwrap();
|
|
|
|
let acc_bal = client.get_balance(req).await.unwrap().into_inner();
|
|
|
|
assert_eq!(acc_bal.balance, 0);
|
|
assert_eq!(acc_bal.tmp_locked, 0);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_general_airdrop() {
|
|
// env_logger::builder().filter_level(log::LevelFilter::Trace).init();
|
|
prepare_test_db().await.unwrap();
|
|
|
|
let airdrop_amount = 10;
|
|
|
|
let addr = run_service_in_background().await.unwrap();
|
|
let mut client = BrainGeneralCliClient::connect(format!("http://{}", addr)).await.unwrap();
|
|
|
|
let admin_keys = admin_keys();
|
|
|
|
let user_01_key = Key::new();
|
|
let user_01_pubkey = user_01_key.pubkey.clone();
|
|
|
|
let airdrop_req =
|
|
AirdropReq { pubkey: user_01_pubkey.clone(), tokens: airdrop_amount * TOKEN_DECIMAL };
|
|
|
|
// user airdroping himself
|
|
let err =
|
|
client.airdrop(user_01_key.sign_request(airdrop_req.clone()).unwrap()).await.err().unwrap();
|
|
assert_eq!(err.message(), "This operation is reserved to admin accounts");
|
|
|
|
// other user airdroping
|
|
let err =
|
|
client.airdrop(Key::new().sign_request(airdrop_req.clone()).unwrap()).await.err().unwrap();
|
|
assert_eq!(err.message(), "This operation is reserved to admin accounts");
|
|
|
|
let _ = client.airdrop(admin_keys[0].sign_request(airdrop_req.clone()).unwrap()).await.unwrap();
|
|
|
|
let bal_req_data = Pubkey { pubkey: user_01_pubkey };
|
|
let bal_req = user_01_key.sign_request(bal_req_data.clone()).unwrap();
|
|
let acc_bal_user_01 = client.get_balance(bal_req).await.unwrap().into_inner();
|
|
|
|
assert_eq!(acc_bal_user_01.balance, airdrop_amount * TOKEN_DECIMAL);
|
|
assert_eq!(acc_bal_user_01.tmp_locked, 0);
|
|
|
|
// second airdrop from same admin
|
|
let _ = client.airdrop(admin_keys[0].sign_request(airdrop_req.clone()).unwrap()).await.unwrap();
|
|
|
|
let acc_bal_user_01 = client
|
|
.get_balance(user_01_key.sign_request(bal_req_data.clone()).unwrap())
|
|
.await
|
|
.unwrap()
|
|
.into_inner();
|
|
|
|
assert_eq!(acc_bal_user_01.balance, 2 * airdrop_amount * TOKEN_DECIMAL);
|
|
|
|
// third airdrop from another admin
|
|
let _ = client.airdrop(admin_keys[1].sign_request(airdrop_req.clone()).unwrap()).await.unwrap();
|
|
|
|
let acc_bal_user_01 = client
|
|
.get_balance(user_01_key.sign_request(bal_req_data).unwrap())
|
|
.await
|
|
.unwrap()
|
|
.into_inner();
|
|
|
|
assert_eq!(acc_bal_user_01.balance, 3 * airdrop_amount * TOKEN_DECIMAL);
|
|
|
|
// self airdrop
|
|
let airdrop_req =
|
|
AirdropReq { pubkey: admin_keys[2].pubkey.clone(), tokens: airdrop_amount * TOKEN_DECIMAL };
|
|
|
|
let _ = client.airdrop(admin_keys[2].sign_request(airdrop_req.clone()).unwrap()).await.unwrap();
|
|
|
|
let bal_req_data = Pubkey { pubkey: admin_keys[2].pubkey.clone() };
|
|
let acc_bal_admin_3 = client
|
|
.get_balance(admin_keys[2].sign_request(bal_req_data.clone()).unwrap())
|
|
.await
|
|
.unwrap()
|
|
.into_inner();
|
|
|
|
assert_eq!(acc_bal_admin_3.balance, airdrop_amount * TOKEN_DECIMAL);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_report_node() {
|
|
let db = prepare_test_db().await.unwrap();
|
|
|
|
let brain_channel = run_service_for_stream().await.unwrap();
|
|
let daemon_key = mock_vm_daemon(&brain_channel, None).await.unwrap();
|
|
|
|
let key = Key::new();
|
|
|
|
let report_error =
|
|
report_node(&key, &brain_channel, &daemon_key, "uuid", "reason").await.err().unwrap();
|
|
|
|
log::info!("Report error: {:?}", report_error);
|
|
assert!(report_error.to_string().contains("No contract found by this ID."));
|
|
|
|
airdrop(&brain_channel, &key.pubkey, 10).await.unwrap();
|
|
|
|
let active_vm_id = create_new_vm(&db, &key, &daemon_key, &brain_channel).await.unwrap();
|
|
|
|
let reason = String::from("something went wrong on vm");
|
|
|
|
let _ = report_node(&key, &brain_channel, &daemon_key, &active_vm_id, &reason)
|
|
.await
|
|
.unwrap()
|
|
.into_inner();
|
|
|
|
let vm_nodes: Vec<VmNodeWithReports> = db
|
|
.query(format!(
|
|
"SELECT *, <-report.* as reports FROM {VM_NODE} WHERE id = {VM_NODE}:{daemon_key};"
|
|
))
|
|
.await
|
|
.unwrap()
|
|
.take(0)
|
|
.unwrap();
|
|
|
|
let vm_node_with_report = &vm_nodes[0];
|
|
|
|
assert!(vm_node_with_report.reports[0].reason == reason);
|
|
}
|
|
|
|
#[tokio::test]
|
|
// TODO: register some operators before testing this
|
|
async fn test_list_operators() {
|
|
prepare_test_db().await.unwrap();
|
|
|
|
let channel = run_service_for_stream().await.unwrap();
|
|
|
|
let mut client = BrainGeneralCliClient::new(channel);
|
|
|
|
let key = Key::new();
|
|
|
|
let mut grpc_stream =
|
|
client.list_operators(key.sign_request(Empty {}).unwrap()).await.unwrap().into_inner();
|
|
|
|
let mut operators = Vec::new();
|
|
while let Some(stream_update) = grpc_stream.next().await {
|
|
match stream_update {
|
|
Ok(op) => {
|
|
operators.push(op);
|
|
}
|
|
Err(e) => {
|
|
panic!("Received error instead of operators: {e:?}");
|
|
}
|
|
}
|
|
}
|
|
|
|
assert!(!operators.is_empty())
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_inspect_operator() {
|
|
prepare_test_db().await.unwrap();
|
|
|
|
let brain_channel = run_service_for_stream().await.unwrap();
|
|
let mut cli_client = BrainGeneralCliClient::new(brain_channel.clone());
|
|
let mut daemon_client = BrainVmDaemonClient::new(brain_channel.clone());
|
|
let key = Key::new();
|
|
let daemon_key = Key::new();
|
|
let operator_key = Key::new();
|
|
|
|
let err = cli_client
|
|
.inspect_operator(key.sign_request(Pubkey { pubkey: operator_key.pubkey.clone() }).unwrap())
|
|
.await
|
|
.err()
|
|
.unwrap();
|
|
|
|
assert_eq!(err.message(), "The wallet you specified is not an operator");
|
|
|
|
// TODO: test with app node also
|
|
register_vm_node(&mut daemon_client, &daemon_key, &operator_key.pubkey).await.unwrap();
|
|
|
|
let inspect_response = cli_client
|
|
.inspect_operator(key.sign_request(Pubkey { pubkey: operator_key.pubkey.clone() }).unwrap())
|
|
.await
|
|
.unwrap()
|
|
.into_inner();
|
|
|
|
assert!(inspect_response.app_nodes.is_empty());
|
|
assert!(!inspect_response.vm_nodes.is_empty());
|
|
assert_eq!(&inspect_response.vm_nodes[0].operator, &operator_key.pubkey);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_register_operator() {
|
|
let db_conn = prepare_test_db().await.unwrap();
|
|
|
|
let brain_channel = run_service_for_stream().await.unwrap();
|
|
let key = Key::new();
|
|
|
|
let min_escrew_error = register_operator(&brain_channel, &key, 10).await.err().unwrap();
|
|
assert!(min_escrew_error.to_string().contains("Minimum escrow amount is 5000"));
|
|
|
|
let no_balance =
|
|
register_operator(&brain_channel, &key, 5000 * TOKEN_DECIMAL).await.err().unwrap();
|
|
assert!(no_balance.to_string().contains("Insufficient funds, deposit more tokens"));
|
|
|
|
airdrop(&brain_channel, &key.pubkey, 1000).await.unwrap();
|
|
|
|
let no_balance =
|
|
register_operator(&brain_channel, &key, 5000 * TOKEN_DECIMAL).await.err().unwrap();
|
|
assert!(no_balance.to_string().contains("Insufficient funds, deposit more tokens"));
|
|
|
|
airdrop(&brain_channel, &key.pubkey, 7000).await.unwrap();
|
|
|
|
register_operator(&brain_channel, &key, 6000 * TOKEN_DECIMAL).await.unwrap();
|
|
|
|
let operator_account: Option<db::Account> =
|
|
db_conn.select((ACCOUNT, key.pubkey)).await.unwrap();
|
|
assert!(operator_account.is_some());
|
|
let account = operator_account.unwrap();
|
|
assert_eq!(account.escrow, 6000 * TOKEN_DECIMAL);
|
|
assert_eq!(account.balance, 2000 * TOKEN_DECIMAL);
|
|
assert_eq!(account.tmp_locked, 0);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_kick_contract() {
|
|
// TODO: implement seed data to test
|
|
// possibilities
|
|
// 1. vm contract
|
|
// 2. app contract
|
|
// 3. non existent contract
|
|
// 4. other operator's contract
|
|
// 5. contract collected more than a week
|
|
// 6. refund amount calculation
|
|
// 7. refund of multiple contract kick in a day for same user
|
|
|
|
/*
|
|
env_logger::builder()
|
|
.filter_level(log::LevelFilter::Info)
|
|
.filter_module("tungstenite", log::LevelFilter::Debug)
|
|
.filter_module("tokio_tungstenite", log::LevelFilter::Debug)
|
|
.init();
|
|
*/
|
|
|
|
let db_conn = prepare_test_db().await.unwrap();
|
|
let contract_uuid = "5af49a714c64a82ef50e574b023b2a0ef0405ed";
|
|
let operator_wallet = "7V3rEuh6j8VuwMVB5PyGqWKLmjJ4fYSv6WtrTL51NZTB";
|
|
let reason = "'; THROW 'Injected error'; --"; // sql injection query
|
|
|
|
let kick_response =
|
|
surreal_brain::db::general::kick_contract(&db_conn, operator_wallet, contract_uuid, reason)
|
|
.await;
|
|
match kick_response {
|
|
Ok(refund_amount) => {
|
|
println!("Refund amount: {}", refund_amount);
|
|
}
|
|
Err(e) => {
|
|
println!("Error: {}", e);
|
|
}
|
|
}
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_ban_user() {
|
|
let db_conn = prepare_test_db().await.unwrap();
|
|
|
|
let brain_channel = run_service_for_stream().await.unwrap();
|
|
let mut cli_client = BrainGeneralCliClient::new(brain_channel.clone());
|
|
let op_key = Key::from("6f3WcQuv8sPrxAsSh24TdXkauwVd8vFUyCCfbNqxszvR"); // "4qFJJJdRrSB9hCn8rrvYTXHLJg371ab36PJmZ4uxHjGQ"
|
|
let user_key = Key::new();
|
|
let node_pubkey = "7fujZQeTme52RdXTLmQST5jBgAbvzic5iERtH5EWoYjk";
|
|
|
|
/*
|
|
base58 = "6f3WcQuv8sPrxAsSh24TdXkauwVd8vFUyCCfbNqxszvR"
|
|
pubkey = "4qFJJJdRrSB9hCn8rrvYTXHLJg371ab36PJmZ4uxHjGQ"
|
|
base58 = "7fujZQeTme52RdXTLmQST5jBgAbvzic5iERtH5EWoYjk"
|
|
pubkey = "G2PetsFM8peG77rf9UTfVD7HEHNPmdjV3cebBpKuR3QT"
|
|
*/
|
|
|
|
let user_wallet = user_key.pubkey.clone();
|
|
let operator_wallet = op_key.pubkey.clone();
|
|
|
|
let _ = cli_client
|
|
.ban_user(
|
|
op_key
|
|
.sign_request(BanUserReq {
|
|
operator_wallet: operator_wallet.clone(),
|
|
user_wallet: user_wallet.clone(),
|
|
})
|
|
.unwrap(),
|
|
)
|
|
.await
|
|
.unwrap();
|
|
|
|
let ban_reconrd: Option<db::Ban> = db_conn
|
|
.query(format!("SELECT * FROM {BAN} WHERE in = account:{operator_wallet} AND out = account:{user_wallet};"))
|
|
.await.unwrap().take(0).unwrap();
|
|
|
|
assert!(ban_reconrd.is_some());
|
|
|
|
let already_banned = cli_client
|
|
.ban_user(
|
|
op_key
|
|
.sign_request(BanUserReq {
|
|
operator_wallet: operator_wallet.clone(),
|
|
user_wallet: user_wallet.clone(),
|
|
})
|
|
.unwrap(),
|
|
)
|
|
.await
|
|
.err()
|
|
.unwrap();
|
|
|
|
assert!(already_banned.message().contains("Already banned"));
|
|
|
|
airdrop(&brain_channel, &user_wallet, 10).await.unwrap();
|
|
|
|
let operator_banned_you =
|
|
create_new_vm(&db_conn, &user_key, node_pubkey, &brain_channel).await.err().unwrap();
|
|
|
|
assert!(operator_banned_you.to_string().contains("This operator banned you"));
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_slash_operator() {
|
|
let db_conn = prepare_test_db().await.unwrap();
|
|
|
|
let brain_channel = run_service_for_stream().await.unwrap();
|
|
let mut cli_client = BrainGeneralCliClient::new(brain_channel.clone());
|
|
let op_key = Key::new();
|
|
|
|
let admin = admin_keys()[0].clone();
|
|
let escrew = 5500;
|
|
let slash_amt = 2500;
|
|
|
|
airdrop(&brain_channel, &op_key.pubkey, 10000).await.unwrap();
|
|
register_operator(&brain_channel, &op_key, escrew * TOKEN_DECIMAL).await.unwrap();
|
|
|
|
let raw_slash_req = SlashReq { pubkey: op_key.pubkey.clone(), tokens: 2500 * TOKEN_DECIMAL };
|
|
|
|
let other_key = Key::new();
|
|
let admin_error = cli_client
|
|
.slash(other_key.sign_request(raw_slash_req.clone()).unwrap())
|
|
.await
|
|
.err()
|
|
.unwrap();
|
|
assert!(admin_error.message().contains("This operation is reserved to admin accounts"));
|
|
|
|
let admin_error =
|
|
cli_client.slash(op_key.sign_request(raw_slash_req.clone()).unwrap()).await.err().unwrap();
|
|
assert!(admin_error.message().contains("This operation is reserved to admin accounts"));
|
|
|
|
cli_client.slash(admin.sign_request(raw_slash_req.clone()).unwrap()).await.unwrap();
|
|
|
|
let operator_account: Option<db::Account> =
|
|
db_conn.select((ACCOUNT, op_key.pubkey)).await.unwrap();
|
|
assert!(operator_account.is_some());
|
|
let account = operator_account.unwrap();
|
|
assert_eq!(account.escrow, (escrew - slash_amt) * TOKEN_DECIMAL);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_admin_list_account() {
|
|
let db_conn = prepare_test_db().await.unwrap();
|
|
let brain_channel = run_service_for_stream().await.unwrap();
|
|
|
|
let admin_key = admin_keys()[0].clone();
|
|
|
|
let unauthenticated = list_accounts(&brain_channel, &Key::new()).await.err().unwrap();
|
|
assert!(unauthenticated.to_string().contains("reserved to admin accounts"));
|
|
|
|
tokio::time::sleep(tokio::time::Duration::from_millis(200)).await; // sync time for other db opeartion
|
|
|
|
let acc_in_db = db_conn.select::<Vec<db::Account>>(ACCOUNT).await.unwrap();
|
|
let accounts = list_accounts(&brain_channel, &admin_key).await.unwrap();
|
|
assert_eq!(accounts.len(), acc_in_db.len());
|
|
|
|
airdrop(&brain_channel, &Key::new().pubkey, 10).await.unwrap();
|
|
|
|
let accounts = list_accounts(&brain_channel, &admin_key).await.unwrap();
|
|
let acc_in_db = db_conn.select::<Vec<db::Account>>(ACCOUNT).await.unwrap();
|
|
|
|
assert_eq!(accounts.len(), acc_in_db.len());
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_admin_list_all_vm_contracts() {
|
|
let db_conn = prepare_test_db().await.unwrap();
|
|
let brain_channel = run_service_for_stream().await.unwrap();
|
|
|
|
let admin_key = admin_keys()[0].clone();
|
|
|
|
let unauthenticated = list_all_vm_contracts(&brain_channel, &Key::new()).await.err().unwrap();
|
|
assert!(unauthenticated.to_string().contains("reserved to admin accounts"));
|
|
|
|
let vm_in_db = db_conn.select::<Vec<db::ActiveVm>>(ACTIVE_VM).await.unwrap();
|
|
|
|
let vm_contracts = list_all_vm_contracts(&brain_channel, &admin_key).await.unwrap();
|
|
assert_eq!(vm_contracts.len(), vm_in_db.len());
|
|
|
|
// TODO: mock vm daemon and deploy a new vm, then list again
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_admin_list_all_app_contracts() {
|
|
let db_conn = prepare_test_db().await.unwrap();
|
|
let brain_channel = run_service_for_stream().await.unwrap();
|
|
|
|
let admin_key = admin_keys()[0].clone();
|
|
|
|
let unauthenticated = list_all_app_contracts(&brain_channel, &Key::new()).await.err().unwrap();
|
|
assert!(unauthenticated.to_string().contains("reserved to admin accounts"));
|
|
|
|
let app_contracts = list_all_app_contracts(&brain_channel, &admin_key).await.unwrap();
|
|
assert_eq!(app_contracts.len(), 1);
|
|
|
|
let app_in_db = db_conn.select::<Vec<db::ActiveApp>>(ACTIVE_APP).await.unwrap();
|
|
|
|
let app_contracts = list_all_app_contracts(&brain_channel, &admin_key).await.unwrap();
|
|
assert_eq!(app_contracts.len(), app_in_db.len());
|
|
|
|
// TODO: mock app daemon and deploy a new app, then list again
|
|
}
|