Feat slashing escrow
improved logging tests for slashing and register operator
This commit is contained in:
parent
0c0bf0b6fa
commit
ff03ec6085
@ -75,6 +75,38 @@ impl Account {
|
||||
op_account.save(db).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn slash_account(
|
||||
db: &Surreal<Client>,
|
||||
account: &str,
|
||||
slash_amount: u64,
|
||||
) -> Result<(), Error> {
|
||||
let tx_query = "
|
||||
BEGIN TRANSACTION;
|
||||
LET $account = $account_input;
|
||||
|
||||
UPDATE $account SET escrow -= $slash_amount;
|
||||
IF $account.escrow < 0 {{
|
||||
THROW 'Insufficient escrow.'
|
||||
}};
|
||||
|
||||
COMMIT TRANSACTION;";
|
||||
|
||||
let mut query_resp = db
|
||||
.query(tx_query)
|
||||
.bind(("account_input", RecordId::from((ACCOUNT, account))))
|
||||
.bind(("slash_amount", slash_amount.saturating_mul(TOKEN_DECIMAL)))
|
||||
.await?;
|
||||
|
||||
log::trace!("query_resp: {query_resp:?}");
|
||||
|
||||
let query_error = query_resp.take_errors();
|
||||
if !query_error.is_empty() {
|
||||
log::error!("slash_account query error: {query_error:?}");
|
||||
return Err(Error::FailedToSlashOperator(account.to_string()));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Account {
|
||||
@ -105,6 +137,9 @@ impl Account {
|
||||
let vm_node_ban: Option<Ban> = query_response.take(0).unwrap();
|
||||
let app_node_ban: Option<Ban> = query_response.take(1).unwrap();
|
||||
|
||||
log::trace!("vm_node_ban: {vm_node_ban:?}");
|
||||
log::trace!("app_node_ban: {app_node_ban:?}");
|
||||
|
||||
Ok(vm_node_ban.is_some() || app_node_ban.is_some())
|
||||
}
|
||||
}
|
||||
@ -162,6 +197,7 @@ impl Ban {
|
||||
user_wallet: &str,
|
||||
) -> Result<(), Error> {
|
||||
if Self::get_record_by_wallets(db, op_wallet, user_wallet).await?.is_some() {
|
||||
log::error!("User {user_wallet} is already banned by {op_wallet}");
|
||||
return Err(Error::AlreadyBanned(op_wallet.to_string()));
|
||||
}
|
||||
let _: Vec<Self> = db
|
||||
|
@ -47,6 +47,8 @@ pub enum Error {
|
||||
FailedKickContract(String),
|
||||
#[error("Already banned {0}")]
|
||||
AlreadyBanned(String),
|
||||
#[error("Failed to slash operator {0}")]
|
||||
FailedToSlashOperator(String),
|
||||
}
|
||||
|
||||
pub mod prelude {
|
||||
|
@ -154,6 +154,7 @@ impl BrainGeneralCli for GeneralCliServer {
|
||||
|
||||
async fn ban_user(&self, req: Request<BanUserReq>) -> Result<Response<Empty>, Status> {
|
||||
let req = check_sig_from_req(req)?;
|
||||
log::info!("Banning user: {}, by: {}", req.user_wallet, req.operator_wallet);
|
||||
db::Ban::ban_a_user(&self.db, &req.operator_wallet, &req.user_wallet).await?;
|
||||
Ok(Response::new(Empty {}))
|
||||
}
|
||||
@ -163,16 +164,16 @@ impl BrainGeneralCli for GeneralCliServer {
|
||||
async fn airdrop(&self, req: Request<AirdropReq>) -> Result<Response<Empty>, Status> {
|
||||
check_admin_key(&req)?;
|
||||
let req = check_sig_from_req(req)?;
|
||||
log::info!("Airdropping {} tokens to {}", req.tokens, req.pubkey);
|
||||
db::Account::airdrop(&self.db, &req.pubkey, req.tokens).await?;
|
||||
Ok(Response::new(Empty {}))
|
||||
}
|
||||
|
||||
async fn slash(&self, _req: Request<SlashReq>) -> Result<Response<Empty>, Status> {
|
||||
todo!();
|
||||
// check_admin_key(&req)?;
|
||||
// let req = check_sig_from_req(req)?;
|
||||
// self.data.slash_account(&req.pubkey, req.tokens);
|
||||
// Ok(Response::new(Empty {}))
|
||||
async fn slash(&self, req: Request<SlashReq>) -> Result<Response<Empty>, Status> {
|
||||
check_admin_key(&req)?;
|
||||
let req = check_sig_from_req(req)?;
|
||||
db::Account::slash_account(&self.db, &req.pubkey, req.tokens).await?;
|
||||
Ok(Response::new(Empty {}))
|
||||
}
|
||||
|
||||
async fn list_accounts(
|
||||
|
@ -2,7 +2,7 @@ use super::test_utils::{admin_keys, Key};
|
||||
use anyhow::{anyhow, Result};
|
||||
use detee_shared::common_proto::Empty;
|
||||
use detee_shared::general_proto::brain_general_cli_client::BrainGeneralCliClient;
|
||||
use detee_shared::general_proto::{AirdropReq, ReportNodeReq};
|
||||
use detee_shared::general_proto::{AirdropReq, RegOperatorReq, ReportNodeReq};
|
||||
use detee_shared::vm_proto;
|
||||
use detee_shared::vm_proto::brain_vm_cli_client::BrainVmCliClient;
|
||||
use surreal_brain::constants::{ACTIVE_VM, NEW_VM_REQ};
|
||||
@ -77,3 +77,12 @@ pub async fn report_node(
|
||||
|
||||
Ok(client_gen_cli.report_node(key.sign_request(report_req)?).await?)
|
||||
}
|
||||
|
||||
pub async fn register_operator(brain_channel: &Channel, key: &Key, escrow: u64) -> Result<()> {
|
||||
let mut cli_client = BrainGeneralCliClient::new(brain_channel.clone());
|
||||
let reg_req =
|
||||
RegOperatorReq { pubkey: key.pubkey.clone(), escrow, email: "foo@bar.com".to_string() };
|
||||
|
||||
cli_client.register_operator(key.sign_request(reg_req.clone()).unwrap()).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
@ -2,14 +2,14 @@ use common::prepare_test_env::{
|
||||
prepare_test_db, run_service_for_stream, run_service_in_background,
|
||||
};
|
||||
use common::test_utils::{admin_keys, Key};
|
||||
use common::vm_cli_utils::{airdrop, create_new_vm, report_node};
|
||||
use common::vm_cli_utils::{airdrop, create_new_vm, 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};
|
||||
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::{BAN, TOKEN_DECIMAL, VM_NODE};
|
||||
use surreal_brain::constants::{ACCOUNT, BAN, TOKEN_DECIMAL, VM_NODE};
|
||||
use surreal_brain::db::prelude as db;
|
||||
use surreal_brain::db::vm::VmNodeWithReports;
|
||||
|
||||
@ -210,6 +210,37 @@ async fn test_inspect_operator() {
|
||||
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).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).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).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
|
||||
@ -308,3 +339,40 @@ async fn test_ban_user() {
|
||||
|
||||
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).await.unwrap();
|
||||
|
||||
let raw_slash_req = SlashReq { pubkey: op_key.pubkey.clone(), tokens: 2500 };
|
||||
|
||||
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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user