Compare commits
3 Commits
57d3807a17
...
810c1b1966
| Author | SHA1 | Date | |
|---|---|---|---|
| 810c1b1966 | |||
| 16fd64ac13 | |||
| 4295b3ae1d |
@ -130,8 +130,6 @@ DEFINE FIELD disk_size_gb ON TABLE deleted_app TYPE int;
|
||||
DEFINE FIELD created_at ON TABLE deleted_app TYPE datetime;
|
||||
DEFINE FIELD deleted_at ON TABLE deleted_app TYPE datetime;
|
||||
DEFINE FIELD price_per_unit ON TABLE deleted_app TYPE int;
|
||||
DEFINE FIELD locked_nano ON TABLE deleted_app TYPE int;
|
||||
DEFINE FIELD collected_at ON TABLE deleted_app TYPE datetime;
|
||||
DEFINE FIELD mr_enclave ON TABLE deleted_app TYPE string;
|
||||
DEFINE FIELD package_url ON TABLE deleted_app TYPE string;
|
||||
DEFINE FIELD hratls_pubkey ON TABLE deleted_app TYPE string;
|
||||
@ -143,6 +141,7 @@ DEFINE TABLE kick TYPE RELATION FROM account TO account;
|
||||
DEFINE FIELD created_at ON TABLE kick TYPE datetime;
|
||||
DEFINE FIELD reason ON TABLE kick TYPE string;
|
||||
DEFINE FIELD contract ON TABLE kick TYPE record<deleted_vm|deleted_app>;
|
||||
DEFINE FIELD node ON TABLE kick TYPE record<vm_node|app_name>;
|
||||
|
||||
DEFINE TABLE report TYPE RELATION FROM account TO vm_node|app_node;
|
||||
DEFINE FIELD created_at ON TABLE report TYPE datetime;
|
||||
|
||||
@ -23,6 +23,8 @@ pub static ADMIN_ACCOUNTS: LazyLock<Vec<String>> = LazyLock::new(|| {
|
||||
pub const OLD_BRAIN_DATA_PATH: &str = "./saved_data.yaml";
|
||||
|
||||
pub const ACCOUNT: &str = "account";
|
||||
pub const KICK: &str = "kick";
|
||||
|
||||
pub const VM_NODE: &str = "vm_node";
|
||||
pub const ACTIVE_VM: &str = "active_vm";
|
||||
pub const VM_UPDATE_EVENT: &str = "vm_update_event";
|
||||
@ -42,3 +44,6 @@ pub const ID_ALPHABET: [char; 62] = [
|
||||
'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U',
|
||||
'V', 'W', 'X', 'Y', 'Z',
|
||||
];
|
||||
|
||||
pub const MIN_ESCROW: u64 = 5000;
|
||||
pub const TOKEN_DECIMAL: u64 = 1_000_000_000;
|
||||
|
||||
@ -12,7 +12,7 @@ use surrealdb::sql::Datetime;
|
||||
use surrealdb::{Notification, RecordId, Surreal};
|
||||
use tokio_stream::StreamExt;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct AppNode {
|
||||
pub id: RecordId,
|
||||
pub operator: RecordId,
|
||||
@ -32,8 +32,9 @@ pub struct AppNode {
|
||||
impl AppNode {
|
||||
pub async fn register(self, db: &Surreal<Client>) -> Result<AppNode, Error> {
|
||||
db::Account::get_or_create(db, &self.operator.key().to_string()).await?;
|
||||
let app_node: Option<AppNode> = db.upsert(self.id.clone()).content(self).await?;
|
||||
app_node.ok_or(Error::FailedToCreateDBEntry)
|
||||
let app_node_id = self.id.clone();
|
||||
let app_node: Option<AppNode> = db.upsert(app_node_id.clone()).content(self).await?;
|
||||
app_node.ok_or(Error::FailedToCreateDBEntry(format!("{APP_NODE}:{app_node_id}")))
|
||||
}
|
||||
}
|
||||
|
||||
@ -54,7 +55,7 @@ impl From<DeletedApp> for AppDaemonMsg {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct NewAppReq {
|
||||
pub id: RecordId,
|
||||
#[serde(rename = "in")]
|
||||
@ -97,6 +98,7 @@ impl NewAppReq {
|
||||
}
|
||||
|
||||
pub async fn submit(self, db: &Surreal<Client>) -> Result<Vec<Self>, Error> {
|
||||
// TODO: handle financial transaction
|
||||
let new_app_req: Vec<Self> = db.insert(NEW_APP_REQ).relation(self).await?;
|
||||
Ok(new_app_req)
|
||||
}
|
||||
@ -164,7 +166,7 @@ impl AppNodeWithReports {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct ActiveApp {
|
||||
pub id: RecordId,
|
||||
#[serde(rename = "in")]
|
||||
@ -210,6 +212,15 @@ impl From<ActiveApp> for DeletedApp {
|
||||
}
|
||||
|
||||
impl ActiveApp {
|
||||
pub fn price_per_minute(&self) -> u64 {
|
||||
(self.total_units() * self.price_per_unit as f64) as u64
|
||||
}
|
||||
|
||||
fn total_units(&self) -> f64 {
|
||||
// TODO: Optimize this based on price of hardware.
|
||||
(self.vcpus as f64 * 5f64) + (self.memory_mb as f64 / 200f64) + (self.disk_size_gb as f64)
|
||||
}
|
||||
|
||||
pub async fn activate(db: &Surreal<Client>, id: &str) -> Result<(), Error> {
|
||||
let new_app_req = match NewAppReq::get(db, id).await? {
|
||||
Some(r) => r,
|
||||
@ -293,7 +304,7 @@ impl ActiveApp {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct ActiveAppWithNode {
|
||||
pub id: RecordId,
|
||||
#[serde(rename = "in")]
|
||||
@ -315,6 +326,29 @@ pub struct ActiveAppWithNode {
|
||||
pub hratls_pubkey: String,
|
||||
}
|
||||
|
||||
impl From<ActiveAppWithNode> for ActiveApp {
|
||||
fn from(val: ActiveAppWithNode) -> Self {
|
||||
Self {
|
||||
id: val.id,
|
||||
admin: val.admin,
|
||||
app_node: val.app_node.id,
|
||||
app_name: val.app_name,
|
||||
mapped_ports: val.mapped_ports,
|
||||
host_ipv4: val.host_ipv4,
|
||||
vcpus: val.vcpus,
|
||||
memory_mb: val.memory_mb,
|
||||
disk_size_gb: val.disk_size_gb,
|
||||
created_at: val.created_at,
|
||||
price_per_unit: val.price_per_unit,
|
||||
locked_nano: val.locked_nano,
|
||||
collected_at: val.collected_at,
|
||||
mr_enclave: val.mr_enclave,
|
||||
package_url: val.package_url,
|
||||
hratls_pubkey: val.hratls_pubkey,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ActiveAppWithNode {
|
||||
pub async fn get_by_uuid(db: &Surreal<Client>, uuid: &str) -> Result<Option<Self>, Error> {
|
||||
let contract: Option<Self> =
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
use crate::constants::ACCOUNT;
|
||||
use crate::db::prelude::*;
|
||||
|
||||
use super::Error;
|
||||
use crate::constants::{ACCOUNT, KICK, MIN_ESCROW, TOKEN_DECIMAL};
|
||||
use crate::db::prelude::*;
|
||||
use crate::old_brain;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use surrealdb::engine::remote::ws::Client;
|
||||
@ -37,7 +36,7 @@ impl Account {
|
||||
Some(account) => Ok(account),
|
||||
None => {
|
||||
let account: Option<Self> = db.create(id).await?;
|
||||
account.ok_or(Error::FailedToCreateDBEntry)
|
||||
account.ok_or(Error::FailedToCreateDBEntry(ACCOUNT.to_string()))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -49,6 +48,33 @@ impl Account {
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn save(self, db: &Surreal<Client>) -> Result<Option<Self>, Error> {
|
||||
let account: Option<Self> = db.upsert(self.id.clone()).content(self).await?;
|
||||
Ok(account)
|
||||
}
|
||||
|
||||
pub async fn operator_reg(
|
||||
db: &Surreal<Client>,
|
||||
wallet: &str,
|
||||
email: &str,
|
||||
escrow: u64,
|
||||
) -> Result<(), Error> {
|
||||
if escrow < MIN_ESCROW {
|
||||
return Err(Error::MinimalEscrow);
|
||||
}
|
||||
let mut op_account = Self::get(db, wallet).await?;
|
||||
let escrow = escrow.saturating_mul(TOKEN_DECIMAL);
|
||||
let op_total_balance = op_account.balance.saturating_add(op_account.escrow);
|
||||
if op_total_balance < escrow {
|
||||
return Err(Error::InsufficientFunds);
|
||||
}
|
||||
op_account.email = email.to_string();
|
||||
op_account.balance = op_total_balance.saturating_sub(escrow);
|
||||
op_account.escrow = escrow;
|
||||
op_account.save(db).await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Account {
|
||||
@ -120,6 +146,24 @@ pub struct Kick {
|
||||
created_at: Datetime,
|
||||
reason: String,
|
||||
contract: RecordId,
|
||||
node: RecordId,
|
||||
}
|
||||
|
||||
impl Kick {
|
||||
pub async fn kicked_in_a_day(db: &Surreal<Client>, account: &str) -> Result<Vec<Self>, Error> {
|
||||
let mut result = db
|
||||
.query(format!(
|
||||
"select * from {KICK} where out = {ACCOUNT}:{account} and created_at > time::now() - 24h;"
|
||||
))
|
||||
.await?;
|
||||
let kicks: Vec<Self> = result.take(0)?;
|
||||
Ok(kicks)
|
||||
}
|
||||
|
||||
pub async fn submit(self, db: &Surreal<Client>) -> Result<(), Error> {
|
||||
let _: Vec<Self> = db.insert(KICK).relation(self).await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
@ -158,7 +202,7 @@ impl Report {
|
||||
|
||||
/// This is the operator obtained from the DB,
|
||||
/// however the relation is defined using OperatorRelation
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct Operator {
|
||||
pub account: RecordId,
|
||||
pub app_nodes: u64,
|
||||
@ -231,3 +275,149 @@ impl Operator {
|
||||
Ok((operator, vm_nodes, app_nodes))
|
||||
}
|
||||
}
|
||||
|
||||
pub enum WrapperContract {
|
||||
Vm(ActiveVmWithNode),
|
||||
App(ActiveAppWithNode),
|
||||
}
|
||||
|
||||
impl WrapperContract {
|
||||
pub async fn kick_contract(
|
||||
db: &Surreal<Client>,
|
||||
operator_wallet: &str,
|
||||
contract_uuid: &str,
|
||||
reason: &str,
|
||||
) -> Result<u64, Error> {
|
||||
let (
|
||||
operator_id,
|
||||
admin_id,
|
||||
contract_id,
|
||||
collected_at,
|
||||
price_per_mint,
|
||||
deleted_table,
|
||||
platform_specific_query,
|
||||
) = if let Some(active_vm) = ActiveVmWithNode::get_by_uuid(db, contract_uuid).await? {
|
||||
let price_per_minute = active_vm.price_per_minute();
|
||||
|
||||
(
|
||||
active_vm.vm_node.operator,
|
||||
active_vm.admin,
|
||||
active_vm.id,
|
||||
active_vm.collected_at,
|
||||
price_per_minute,
|
||||
"deleted_vm",
|
||||
"
|
||||
hostname = $contract.hostname,
|
||||
public_ipv4 = $contract.public_ipv4,
|
||||
public_ipv6 = $contract.public_ipv6,
|
||||
dtrfs_sha = $contract.dtrfs_sha,
|
||||
kernel_sha = $contract.kernel_sha,
|
||||
",
|
||||
)
|
||||
} else if let Some(active_app) = ActiveAppWithNode::get_by_uuid(db, contract_uuid).await? {
|
||||
let price_per_minute = Into::<ActiveApp>::into(active_app.clone()).price_per_minute();
|
||||
|
||||
(
|
||||
active_app.app_node.operator,
|
||||
active_app.admin,
|
||||
active_app.id,
|
||||
active_app.collected_at,
|
||||
price_per_minute,
|
||||
"deleted_app",
|
||||
"
|
||||
app_name = $contract.app_name,
|
||||
host_ipv4 = $contract.host_ipv4,
|
||||
mr_enclave = $contract.mr_enclave,
|
||||
package_url= $contract.package_url,
|
||||
hratls_pubkey = $contract.hratls_pubkey,
|
||||
",
|
||||
)
|
||||
} else {
|
||||
return Err(Error::ContractNotFound);
|
||||
};
|
||||
|
||||
let operator = operator_id.key().to_string();
|
||||
let admin = admin_id.key().to_string();
|
||||
|
||||
if operator != operator_wallet {
|
||||
return Err(Error::AccessDenied);
|
||||
}
|
||||
let mut minutes_to_refund =
|
||||
chrono::Utc::now().signed_duration_since(*collected_at).num_minutes().unsigned_abs();
|
||||
|
||||
let one_week_minute = 10080;
|
||||
|
||||
if minutes_to_refund > one_week_minute {
|
||||
minutes_to_refund = one_week_minute;
|
||||
}
|
||||
|
||||
let refund_amount = minutes_to_refund * price_per_mint;
|
||||
|
||||
log::debug!("Removing {refund_amount} escrow from {} and giving it to {}", operator, admin);
|
||||
|
||||
let transaction_query = format!(
|
||||
"
|
||||
BEGIN TRANSACTION;
|
||||
LET $contract = {contract_id};
|
||||
LET $operator_account = {operator_id};
|
||||
LET $reason = '{reason}';
|
||||
LET $refund_amount = {refund_amount};
|
||||
LET $deleted_contract = {deleted_table}:{contract_uuid};
|
||||
LET $id = record::id($contract.id);
|
||||
LET $admin = $contract.in;
|
||||
LET $node = $contract.out;
|
||||
|
||||
-- move contract into deleted state
|
||||
|
||||
RELATE $admin->{deleted_table}->$node
|
||||
SET id = $id,
|
||||
{platform_specific_query}
|
||||
mapped_ports = $contract.mapped_ports,
|
||||
disk_size_gb = $contract.disk_size_gb,
|
||||
vcpus = $contract.vcpus,
|
||||
memory_mb = $contract.memory_mb,
|
||||
created_at = $contract.created_at,
|
||||
deleted_at = time::now(),
|
||||
price_per_unit = $contract.price_per_unit
|
||||
;
|
||||
|
||||
DELETE $contract;
|
||||
|
||||
-- calculating refund amount
|
||||
LET $refund = IF SELECT * FROM {KICK} WHERE out = $admin.id AND created_at > time::now() - 24h {{
|
||||
0
|
||||
}} ELSE IF $operator_account.escrow <= $refund_amount {{
|
||||
$operator_account.escrow
|
||||
}} ELSE {{
|
||||
$refund_amount;
|
||||
}};
|
||||
|
||||
|
||||
RELATE $operator_account->{KICK}->$admin
|
||||
SET id = $id,
|
||||
reason = $reason,
|
||||
contract = $deleted_contract,
|
||||
node = $node,
|
||||
created_at = time::now()
|
||||
;
|
||||
|
||||
-- update balances
|
||||
UPDATE $operator_account SET escrow -= $refund;
|
||||
IF $operator_account.escrow < 0 {{
|
||||
THROW 'Insufficient funds.'
|
||||
}};
|
||||
UPDATE $admin SET balance += $refund;
|
||||
|
||||
SELECT * FROM $refund;
|
||||
|
||||
COMMIT TRANSACTION;
|
||||
",
|
||||
);
|
||||
|
||||
log::trace!("kick_contract transaction_query: {}", &transaction_query);
|
||||
let refunded: Option<u64> = db.query(transaction_query).await?.take(14)?;
|
||||
let refunded_amount = refunded.ok_or(Error::FailedToCreateDBEntry("Refund".to_string()))?;
|
||||
|
||||
Ok(refunded_amount)
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,7 +2,9 @@ pub mod app;
|
||||
pub mod general;
|
||||
pub mod vm;
|
||||
|
||||
use crate::constants::{APP_NODE, DELETED_APP, DELETED_VM, NEW_APP_REQ, NEW_VM_REQ, UPDATE_VM_REQ};
|
||||
use crate::constants::{
|
||||
APP_NODE, DELETED_APP, DELETED_VM, MIN_ESCROW, NEW_APP_REQ, NEW_VM_REQ, UPDATE_VM_REQ,
|
||||
};
|
||||
use crate::old_brain;
|
||||
use prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@ -22,14 +24,24 @@ pub enum Error {
|
||||
StdIo(#[from] std::io::Error),
|
||||
#[error(transparent)]
|
||||
TimeOut(#[from] tokio::time::error::Elapsed),
|
||||
#[error("Failed to create account")]
|
||||
FailedToCreateDBEntry,
|
||||
#[error("Failed to create {0}")]
|
||||
FailedToCreateDBEntry(String),
|
||||
#[error("Unknown Table: {0}")]
|
||||
UnknownTable(String),
|
||||
#[error("Daemon channel got closed: {0}")]
|
||||
AppDaemonConnection(#[from] tokio::sync::mpsc::error::SendError<AppDaemonMsg>),
|
||||
#[error("AppDaemon Error {0}")]
|
||||
NewAppDaemonResp(String),
|
||||
#[error("Minimum escrow amount is {MIN_ESCROW}")]
|
||||
MinimalEscrow,
|
||||
#[error("Insufficient funds, deposit more tokens")]
|
||||
InsufficientFunds,
|
||||
#[error("Contract not found")]
|
||||
ContractNotFound,
|
||||
#[error("Access denied")]
|
||||
AccessDenied,
|
||||
#[error("Failed to delete contract {0}")]
|
||||
FailedToDeleteContract(String),
|
||||
}
|
||||
|
||||
pub mod prelude {
|
||||
|
||||
23
src/db/vm.rs
23
src/db/vm.rs
@ -665,6 +665,29 @@ pub struct ActiveVmWithNode {
|
||||
pub collected_at: Datetime,
|
||||
}
|
||||
|
||||
impl From<ActiveVmWithNode> for ActiveVm {
|
||||
fn from(val: ActiveVmWithNode) -> Self {
|
||||
Self {
|
||||
id: val.id,
|
||||
admin: val.admin,
|
||||
vm_node: val.vm_node.id,
|
||||
hostname: val.hostname,
|
||||
mapped_ports: val.mapped_ports,
|
||||
public_ipv4: val.public_ipv4,
|
||||
public_ipv6: val.public_ipv6,
|
||||
disk_size_gb: val.disk_size_gb,
|
||||
vcpus: val.vcpus,
|
||||
memory_mb: val.memory_mb,
|
||||
dtrfs_sha: val.dtrfs_sha,
|
||||
kernel_sha: val.kernel_sha,
|
||||
created_at: val.created_at,
|
||||
price_per_unit: val.price_per_unit,
|
||||
locked_nano: val.locked_nano,
|
||||
collected_at: val.collected_at,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ActiveVmWithNode {
|
||||
pub async fn get_by_uuid(db: &Surreal<Client>, uuid: &str) -> Result<Option<Self>, Error> {
|
||||
let contract: Option<Self> =
|
||||
|
||||
@ -107,24 +107,52 @@ impl BrainGeneralCli for GeneralCliServer {
|
||||
|
||||
async fn register_operator(
|
||||
&self,
|
||||
_req: Request<RegOperatorReq>,
|
||||
req: Request<RegOperatorReq>,
|
||||
) -> Result<Response<Empty>, Status> {
|
||||
todo!();
|
||||
// let req = check_sig_from_req(req)?;
|
||||
// info!("Regitering new operator: {req:?}");
|
||||
// match self.data.register_operator(req) {
|
||||
// Ok(()) => Ok(Response::new(Empty {})),
|
||||
// Err(e) => Err(Status::failed_precondition(e.to_string())),
|
||||
// }
|
||||
let req = check_sig_from_req(req)?;
|
||||
log::info!("Regitering new operator: {req:?}");
|
||||
match db::Account::operator_reg(&self.db, &req.pubkey, &req.email, req.escrow).await {
|
||||
Ok(()) => Ok(Response::new(Empty {})),
|
||||
Err(e) if matches!(e, db::Error::InsufficientFunds | db::Error::MinimalEscrow) => {
|
||||
Err(Status::failed_precondition(e.to_string()))
|
||||
}
|
||||
Err(e) => {
|
||||
log::info!("Failed to register operator: {e:?}");
|
||||
Err(Status::unknown(
|
||||
"Unknown error. Please try again or contact the DeTEE devs team.",
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn kick_contract(&self, _req: Request<KickReq>) -> Result<Response<KickResp>, Status> {
|
||||
todo!();
|
||||
// let req = check_sig_from_req(req)?;
|
||||
// match self.data.kick_contract(&req.operator_wallet, &req.contract_uuid, &req.reason).await {
|
||||
// Ok(nano_lp) => Ok(Response::new(KickResp { nano_lp })),
|
||||
// Err(e) => Err(Status::permission_denied(e.to_string())),
|
||||
// }
|
||||
async fn kick_contract(&self, req: Request<KickReq>) -> Result<Response<KickResp>, Status> {
|
||||
let req = check_sig_from_req(req)?;
|
||||
match db::WrapperContract::kick_contract(
|
||||
&self.db,
|
||||
&req.operator_wallet,
|
||||
&req.contract_uuid,
|
||||
&req.reason,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(nano_lp) => Ok(Response::new(KickResp { nano_lp })),
|
||||
Err(e)
|
||||
if matches!(
|
||||
e,
|
||||
db::Error::ContractNotFound
|
||||
| db::Error::AccessDenied
|
||||
| db::Error::FailedToDeleteContract(_)
|
||||
) =>
|
||||
{
|
||||
Err(Status::failed_precondition(e.to_string()))
|
||||
}
|
||||
Err(e) => {
|
||||
log::info!("Failed to kick contract: {e:?}");
|
||||
Err(Status::unknown(
|
||||
"Unknown error. Please try again or contact the DeTEE devs team.",
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn ban_user(&self, _req: Request<BanUserReq>) -> Result<Response<Empty>, Status> {
|
||||
|
||||
@ -211,3 +211,37 @@ async fn test_inspect_operator() {
|
||||
assert!(!inspect_response.vm_nodes.is_empty());
|
||||
assert_eq!(&inspect_response.vm_nodes[0].operator, &operator_key.pubkey);
|
||||
}
|
||||
|
||||
#[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::Trace)
|
||||
.filter_module("tungstenite", log::LevelFilter::Debug)
|
||||
.filter_module("tokio_tungstenite", log::LevelFilter::Debug)
|
||||
.init();
|
||||
|
||||
let db_conn = prepare_test_db().await.unwrap();
|
||||
let operator_wallet = "BFopWmwcZAMF1h2PFECZNdEucdZfnZZ32p6R9ZaBiVsS";
|
||||
let contract_uuid = "26577f1c98674a1780a86cf0490f1270";
|
||||
let reason = "test reason";
|
||||
|
||||
let kick_response = surreal_brain::db::general::WrapperContract::kick_contract(
|
||||
&db_conn,
|
||||
&operator_wallet,
|
||||
&contract_uuid,
|
||||
&reason,
|
||||
)
|
||||
.await;
|
||||
|
||||
dbg!(kick_response.unwrap());
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user