From d1e5244a185dfc378cf8a8d54b07f639357c1793 Mon Sep 17 00:00:00 2001 From: Noor Date: Thu, 15 May 2025 12:52:23 +0530 Subject: [PATCH 1/6] register operator --- src/constants.rs | 3 +++ src/db/general.rs | 29 ++++++++++++++++++++++++++++- src/db/mod.rs | 6 +++++- src/grpc/general.rs | 23 +++++++++++++++-------- 4 files changed, 51 insertions(+), 10 deletions(-) diff --git a/src/constants.rs b/src/constants.rs index 751f441..94df60b 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -42,3 +42,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; diff --git a/src/db/general.rs b/src/db/general.rs index 62774ae..86c49df 100644 --- a/src/db/general.rs +++ b/src/db/general.rs @@ -1,4 +1,4 @@ -use crate::constants::ACCOUNT; +use crate::constants::{ACCOUNT, MIN_ESCROW, TOKEN_DECIMAL}; use crate::db::prelude::*; use super::Error; @@ -49,6 +49,33 @@ impl Account { .await?; Ok(()) } + + pub async fn save(self, db: &Surreal) -> Result, Error> { + let account: Option = db.upsert(self.id.clone()).content(self).await?; + Ok(account) + } + + pub async fn operator_reg( + db: &Surreal, + 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 { diff --git a/src/db/mod.rs b/src/db/mod.rs index 500f7b3..eabb36c 100644 --- a/src/db/mod.rs +++ b/src/db/mod.rs @@ -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}; @@ -30,6 +32,8 @@ pub enum Error { AppDaemonConnection(#[from] tokio::sync::mpsc::error::SendError), #[error("AppDaemon Error {0}")] NewAppDaemonResp(String), + #[error("Minimum escrow amount is {MIN_ESCROW}")] + MinimalEscrow, #[error("Insufficient funds, deposit more tokens")] InsufficientFunds, } diff --git a/src/grpc/general.rs b/src/grpc/general.rs index 925e6e0..2976314 100644 --- a/src/grpc/general.rs +++ b/src/grpc/general.rs @@ -107,15 +107,22 @@ impl BrainGeneralCli for GeneralCliServer { async fn register_operator( &self, - _req: Request, + req: Request, ) -> Result, 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) -> Result, Status> { -- 2.43.0 From dd8710f4a05a760811929eb4d04ba6b6479dad04 Mon Sep 17 00:00:00 2001 From: Noor Date: Thu, 15 May 2025 19:15:44 +0530 Subject: [PATCH 2/6] kick contract implemented app pricing calculation add node in kick schema and type improved handling Clone on all app types handle expected error on kick contract validate both app and vm contracts --- src/constants.rs | 2 + src/db/app.rs | 45 +++++++++-- src/db/general.rs | 132 ++++++++++++++++++++++++++++++-- src/db/mod.rs | 10 ++- src/db/vm.rs | 23 ++++++ src/grpc/general.rs | 35 +++++++-- surql/tables.sql | 1 + tests/common/vm_daemon_utils.rs | 2 +- 8 files changed, 229 insertions(+), 21 deletions(-) diff --git a/src/constants.rs b/src/constants.rs index 94df60b..ae7b5e5 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -23,6 +23,8 @@ pub static ADMIN_ACCOUNTS: LazyLock> = 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"; diff --git a/src/db/app.rs b/src/db/app.rs index 478d87d..b4dade4 100644 --- a/src/db/app.rs +++ b/src/db/app.rs @@ -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) -> Result { db::Account::get_or_create(db, &self.operator.key().to_string()).await?; - let app_node: Option = 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 = 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 for AppDaemonMsg { } } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize, Clone)] pub struct NewAppReq { pub id: RecordId, #[serde(rename = "in")] @@ -164,7 +165,7 @@ impl AppNodeWithReports { } } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize, Clone)] pub struct ActiveApp { pub id: RecordId, #[serde(rename = "in")] @@ -210,6 +211,15 @@ impl From 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, id: &str) -> Result<(), Error> { let new_app_req = match NewAppReq::get(db, id).await? { Some(r) => r, @@ -293,7 +303,7 @@ impl ActiveApp { } } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize, Clone)] pub struct ActiveAppWithNode { pub id: RecordId, #[serde(rename = "in")] @@ -315,6 +325,29 @@ pub struct ActiveAppWithNode { pub hratls_pubkey: String, } +impl From 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, uuid: &str) -> Result, Error> { let contract: Option = diff --git a/src/db/general.rs b/src/db/general.rs index 86c49df..4ffd49c 100644 --- a/src/db/general.rs +++ b/src/db/general.rs @@ -1,7 +1,6 @@ -use crate::constants::{ACCOUNT, MIN_ESCROW, TOKEN_DECIMAL}; -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 = db.create(id).await?; - account.ok_or(Error::FailedToCreateDBEntry) + account.ok_or(Error::FailedToCreateDBEntry(ACCOUNT.to_string())) } } } @@ -147,6 +146,25 @@ pub struct Kick { created_at: Datetime, reason: String, contract: RecordId, + node: RecordId, +} + +impl Kick { + pub async fn kicked_in_a_day(db: &Surreal, account: &str) -> Result, Error> { + let yesterday = chrono::Utc::now() - chrono::Duration::days(1); + let mut result = db + .query(format!( + "select * from {KICK} where in = {ACCOUNT}:{account} and created_at > {yesterday};" + )) + .await?; + let kicks: Vec = result.take(0)?; + Ok(kicks) + } + + pub async fn submit(self, db: &Surreal) -> Result<(), Error> { + let _: Vec = db.insert(KICK).relation(self).await?; + Ok(()) + } } #[derive(Debug, Serialize, Deserialize, Clone)] @@ -185,7 +203,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, @@ -258,3 +276,107 @@ impl Operator { Ok((operator, vm_nodes, app_nodes)) } } + +pub enum WrapperContract { + Vm(ActiveVmWithNode), + App(ActiveAppWithNode), +} + +impl WrapperContract { + pub async fn kick_contract( + db: &Surreal, + operator_wallet: &str, + contract_uuid: &str, + reason: &str, + ) -> Result { + let (node_operator, admin, contract_id, node_id, collected_at, price_per_mint, is_vm) = + 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.to_string(), + active_vm.admin.key().to_string(), + active_vm.id, + active_vm.vm_node.id, + active_vm.collected_at, + price_per_minute, + true, + ) + } else if let Some(active_app) = + ActiveAppWithNode::get_by_uuid(db, contract_uuid).await? + { + let price_per_minute = + Into::::into(active_app.clone()).price_per_minute(); + + ( + active_app.app_node.operator.to_string(), + active_app.admin.key().to_string(), + active_app.id, + active_app.app_node.id, + active_app.collected_at, + price_per_minute, + false, + ) + } else { + return Err(Error::ContractNotFound); + }; + + if node_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 mut refund_amount = minutes_to_refund * price_per_mint; + + if !Kick::kicked_in_a_day(db, &admin).await?.is_empty() { + refund_amount = 0; + } + + let mut operator_account = Account::get(db, &node_operator).await?; + let mut admin_account = Account::get(db, &admin).await?; + + if operator_account.escrow < refund_amount { + refund_amount = operator_account.escrow; + } + + log::debug!( + "Removing {refund_amount} escrow from {} and giving it to {}", + node_operator, + admin + ); + + admin_account.balance = admin_account.balance.saturating_add(refund_amount); + operator_account.escrow = operator_account.escrow.saturating_sub(refund_amount); + + let kick = Kick { + id: RecordId::from((KICK, contract_uuid)), + from_account: operator_account.id.clone(), + to_account: admin_account.id.clone(), + created_at: Datetime::default(), + reason: reason.to_string(), + contract: contract_id.clone(), + node: node_id, + }; + + kick.submit(db).await?; + operator_account.save(db).await?; + admin_account.save(db).await?; + + let contract_id = contract_id.to_string(); + + if is_vm && !ActiveVm::delete(db, &contract_id).await? { + return Err(Error::FailedToDeleteContract(format!("vm:{contract_id}"))); + } else if !is_vm && !ActiveApp::delete(db, &contract_id).await? { + return Err(Error::FailedToDeleteContract(format!("app:{contract_id}"))); + } + + Ok(refund_amount) + } +} diff --git a/src/db/mod.rs b/src/db/mod.rs index eabb36c..2129d0e 100644 --- a/src/db/mod.rs +++ b/src/db/mod.rs @@ -24,8 +24,8 @@ 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}")] @@ -36,6 +36,12 @@ pub enum Error { 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 { diff --git a/src/db/vm.rs b/src/db/vm.rs index 82acfc2..ff5e3fe 100644 --- a/src/db/vm.rs +++ b/src/db/vm.rs @@ -728,6 +728,29 @@ pub struct ActiveVmWithNode { pub collected_at: Datetime, } +impl From 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, uuid: &str) -> Result, Error> { let contract: Option = diff --git a/src/grpc/general.rs b/src/grpc/general.rs index 2976314..35e15b9 100644 --- a/src/grpc/general.rs +++ b/src/grpc/general.rs @@ -125,13 +125,34 @@ impl BrainGeneralCli for GeneralCliServer { } } - async fn kick_contract(&self, _req: Request) -> Result, 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) -> Result, 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) -> Result, Status> { diff --git a/surql/tables.sql b/surql/tables.sql index 278c7a1..4e9c02e 100644 --- a/surql/tables.sql +++ b/surql/tables.sql @@ -144,6 +144,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; +DEFINE FIELD node ON TABLE kick TYPE record; DEFINE TABLE report TYPE RELATION FROM account TO vm_node|app_node; DEFINE FIELD created_at ON TABLE report TYPE datetime; diff --git a/tests/common/vm_daemon_utils.rs b/tests/common/vm_daemon_utils.rs index 58a9446..2842b85 100644 --- a/tests/common/vm_daemon_utils.rs +++ b/tests/common/vm_daemon_utils.rs @@ -37,7 +37,7 @@ pub async fn register_vm_node( client: &mut BrainVmDaemonClient, key: &Key, operator_wallet: &str, -) -> Result> { +) -> Result> { log::info!("Registering vm_node: {}", key.pubkey); let node_pubkey = key.pubkey.clone(); -- 2.43.0 From 952ea971ca2bec6961db1ccdc8b1fc467a57e8b8 Mon Sep 17 00:00:00 2001 From: Noor Date: Tue, 20 May 2025 14:04:07 +0530 Subject: [PATCH 3/6] kick contract transaction one single transaction query to execute all the kick operation Remove unused fields from deleted_app schema a basic test for kick and possibilities --- src/db/app.rs | 1 + src/db/general.rs | 181 +++++++++++++++++++++++-------------- surql/tables.sql | 2 - tests/grpc_general_test.rs | 34 +++++++ 4 files changed, 146 insertions(+), 72 deletions(-) diff --git a/src/db/app.rs b/src/db/app.rs index b4dade4..b11955b 100644 --- a/src/db/app.rs +++ b/src/db/app.rs @@ -98,6 +98,7 @@ impl NewAppReq { } pub async fn submit(self, db: &Surreal) -> Result, Error> { + // TODO: handle financial transaction let new_app_req: Vec = db.insert(NEW_APP_REQ).relation(self).await?; Ok(new_app_req) } diff --git a/src/db/general.rs b/src/db/general.rs index 4ffd49c..b31beb0 100644 --- a/src/db/general.rs +++ b/src/db/general.rs @@ -151,10 +151,9 @@ pub struct Kick { impl Kick { pub async fn kicked_in_a_day(db: &Surreal, account: &str) -> Result, Error> { - let yesterday = chrono::Utc::now() - chrono::Duration::days(1); let mut result = db .query(format!( - "select * from {KICK} where in = {ACCOUNT}:{account} and created_at > {yesterday};" + "select * from {KICK} where out = {ACCOUNT}:{account} and created_at > time::now() - 24h;" )) .await?; let kicks: Vec = result.take(0)?; @@ -289,39 +288,58 @@ impl WrapperContract { contract_uuid: &str, reason: &str, ) -> Result { - let (node_operator, admin, contract_id, node_id, collected_at, price_per_mint, is_vm) = - if let Some(active_vm) = ActiveVmWithNode::get_by_uuid(db, contract_uuid).await? { - let price_per_minute = active_vm.price_per_minute(); + 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.to_string(), - active_vm.admin.key().to_string(), - active_vm.id, - active_vm.vm_node.id, - active_vm.collected_at, - price_per_minute, - true, - ) - } else if let Some(active_app) = - ActiveAppWithNode::get_by_uuid(db, contract_uuid).await? - { - let price_per_minute = - Into::::into(active_app.clone()).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::::into(active_app.clone()).price_per_minute(); - ( - active_app.app_node.operator.to_string(), - active_app.admin.key().to_string(), - active_app.id, - active_app.app_node.id, - active_app.collected_at, - price_per_minute, - false, - ) - } else { - return Err(Error::ContractNotFound); - }; + ( + 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); + }; - if node_operator != operator_wallet { + 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 = @@ -333,50 +351,73 @@ impl WrapperContract { minutes_to_refund = one_week_minute; } - let mut refund_amount = minutes_to_refund * price_per_mint; + let refund_amount = minutes_to_refund * price_per_mint; - if !Kick::kicked_in_a_day(db, &admin).await?.is_empty() { - refund_amount = 0; - } + log::debug!("Removing {refund_amount} escrow from {} and giving it to {}", operator, admin); - let mut operator_account = Account::get(db, &node_operator).await?; - let mut admin_account = Account::get(db, &admin).await?; + 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; - if operator_account.escrow < refund_amount { - refund_amount = operator_account.escrow; - } + -- move contract into deleted state - log::debug!( - "Removing {refund_amount} escrow from {} and giving it to {}", - node_operator, - admin + 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; + ", ); - admin_account.balance = admin_account.balance.saturating_add(refund_amount); - operator_account.escrow = operator_account.escrow.saturating_sub(refund_amount); + log::trace!("kick_contract transaction_query: {}", &transaction_query); + let refunded: Option = db.query(transaction_query).await?.take(14)?; + let refunded_amount = refunded.ok_or(Error::FailedToCreateDBEntry("Refund".to_string()))?; - let kick = Kick { - id: RecordId::from((KICK, contract_uuid)), - from_account: operator_account.id.clone(), - to_account: admin_account.id.clone(), - created_at: Datetime::default(), - reason: reason.to_string(), - contract: contract_id.clone(), - node: node_id, - }; - - kick.submit(db).await?; - operator_account.save(db).await?; - admin_account.save(db).await?; - - let contract_id = contract_id.to_string(); - - if is_vm && !ActiveVm::delete(db, &contract_id).await? { - return Err(Error::FailedToDeleteContract(format!("vm:{contract_id}"))); - } else if !is_vm && !ActiveApp::delete(db, &contract_id).await? { - return Err(Error::FailedToDeleteContract(format!("app:{contract_id}"))); - } - - Ok(refund_amount) + Ok(refunded_amount) } } diff --git a/surql/tables.sql b/surql/tables.sql index 4e9c02e..35ecc1c 100644 --- a/surql/tables.sql +++ b/surql/tables.sql @@ -131,8 +131,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; diff --git a/tests/grpc_general_test.rs b/tests/grpc_general_test.rs index a5b9537..299fff8 100644 --- a/tests/grpc_general_test.rs +++ b/tests/grpc_general_test.rs @@ -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()); +} -- 2.43.0 From 933e3cb893a4472037ae8080055ceb478a2446e5 Mon Sep 17 00:00:00 2001 From: Noor Date: Tue, 20 May 2025 16:18:27 +0530 Subject: [PATCH 4/6] Fix db migration migrating all db schemas integrate app data in migration fix vm node registration to new architecture --- saved_data.yaml | 567 +++++++++++++++++-------------- src/constants.rs | 3 +- src/db/app.rs | 72 +++- src/db/general.rs | 2 +- src/db/mod.rs | 17 +- tests/common/prepare_test_env.rs | 5 +- tests/common/vm_daemon_utils.rs | 10 +- tests/grpc_general_test.rs | 16 +- 8 files changed, 400 insertions(+), 292 deletions(-) diff --git a/saved_data.yaml b/saved_data.yaml index cd4dcab..9b9dc16 100644 --- a/saved_data.yaml +++ b/saved_data.yaml @@ -113,275 +113,328 @@ operators: email: first_on_detee@proton.me banned_users: [] vm_nodes: - - HiyMp21zaBVbRCjDsD5hEjQnHeHv4e1gpUR6pVfHTKqv - - 3zRxiGRnf46vd3zAEmpaYBJocTV9oJB6yXf5GZFR1Sq4 - - Du3UfPSUUZmA5thQmc9Vrxdy7UimpygcpDsQNnwRQPtu - - 4QbUXDM915RUFnHm3NiysLXFLk1WRGZvABwLNzx4tTEW - - DgkbsrwttkZXvzxY5kDwQQoDd79GLmZ5tc7fYJUFkQQb + - HiyMp21zaBVbRCjDsD5hEjQnHeHv4e1gpUR6pVfHTKqv + - 3zRxiGRnf46vd3zAEmpaYBJocTV9oJB6yXf5GZFR1Sq4 + - Du3UfPSUUZmA5thQmc9Vrxdy7UimpygcpDsQNnwRQPtu + - 4QbUXDM915RUFnHm3NiysLXFLk1WRGZvABwLNzx4tTEW + - DgkbsrwttkZXvzxY5kDwQQoDd79GLmZ5tc7fYJUFkQQb app_nodes: [] x52w7jARC5erhWWK65VZmjdGXzBK6ZDgfv1A283d8XK: escrow: 5499700480000 email: gheo@detee.ltd banned_users: [] vm_nodes: - - 2Uf5pxhxKTUm6gRMnpbJHYDuyA6BWUfFsdmPyWfbMV1f - - 7Xw3RxbP5pvfjZ8U6yA3HHVSS9YXjKH5Vkas3JRbQYd9 + - 2Uf5pxhxKTUm6gRMnpbJHYDuyA6BWUfFsdmPyWfbMV1f + - 7Xw3RxbP5pvfjZ8U6yA3HHVSS9YXjKH5Vkas3JRbQYd9 app_nodes: [] 7V3rEuh6j8VuwMVB5PyGqWKLmjJ4fYSv6WtrTL51NZTB: escrow: 0 - email: '' + email: "" banned_users: [] vm_nodes: [] app_nodes: - - BiqoPUEoAxYxMRXUmyofoS9H1TBQgQqvLJ6MbWh88AQg + - BiqoPUEoAxYxMRXUmyofoS9H1TBQgQqvLJ6MbWh88AQg vm_nodes: -- public_key: 7Xw3RxbP5pvfjZ8U6yA3HHVSS9YXjKH5Vkas3JRbQYd9 - operator_wallet: x52w7jARC5erhWWK65VZmjdGXzBK6ZDgfv1A283d8XK - country: GB - region: England - city: London - ip: 173.234.17.2 - avail_mem_mb: 26000 - avail_vcpus: 28 - avail_storage_gbs: 680 - avail_ipv4: 2 - avail_ipv6: 65516 - avail_ports: 19999 - max_ports_per_vm: 5 - price: 20000 - reports: {} - offline_minutes: 0 -- public_key: Du3UfPSUUZmA5thQmc9Vrxdy7UimpygcpDsQNnwRQPtu - operator_wallet: BFopWmwcZAMF1h2PFECZNdEucdZfnZZ32p6R9ZaBiVsS - country: FR - region: Île-de-France - city: Paris - ip: 156.146.63.215 - avail_mem_mb: 123000 - avail_vcpus: 46 - avail_storage_gbs: 440 - avail_ipv4: 2 - avail_ipv6: 0 - avail_ports: 20000 - max_ports_per_vm: 5 - price: 20000 - reports: {} - offline_minutes: 0 -- public_key: 2Uf5pxhxKTUm6gRMnpbJHYDuyA6BWUfFsdmPyWfbMV1f - operator_wallet: x52w7jARC5erhWWK65VZmjdGXzBK6ZDgfv1A283d8XK - country: CA - region: Quebec - city: Montréal - ip: 184.107.169.199 - avail_mem_mb: 30000 - avail_vcpus: 31 - avail_storage_gbs: 700 - avail_ipv4: 0 - avail_ipv6: 0 - avail_ports: 20000 - max_ports_per_vm: 5 - price: 18000 - reports: {} - offline_minutes: 0 -- public_key: DgkbsrwttkZXvzxY5kDwQQoDd79GLmZ5tc7fYJUFkQQb - operator_wallet: BFopWmwcZAMF1h2PFECZNdEucdZfnZZ32p6R9ZaBiVsS - country: CA - region: British Columbia - city: Vancouver - ip: 149.22.95.1 - avail_mem_mb: 109000 - avail_vcpus: 45 - avail_storage_gbs: 400 - avail_ipv4: 25 - avail_ipv6: 0 - avail_ports: 20000 - max_ports_per_vm: 5 - price: 20000 - reports: {} - offline_minutes: 0 -- public_key: 3zRxiGRnf46vd3zAEmpaYBJocTV9oJB6yXf5GZFR1Sq4 - operator_wallet: BFopWmwcZAMF1h2PFECZNdEucdZfnZZ32p6R9ZaBiVsS - country: US - region: California - city: San Jose - ip: 149.36.48.99 - avail_mem_mb: 120000 - avail_vcpus: 41 - avail_storage_gbs: 390 - avail_ipv4: 23 - avail_ipv6: 0 - avail_ports: 19999 - max_ports_per_vm: 5 - price: 20000 - reports: {} - offline_minutes: 0 -- public_key: HiyMp21zaBVbRCjDsD5hEjQnHeHv4e1gpUR6pVfHTKqv - operator_wallet: BFopWmwcZAMF1h2PFECZNdEucdZfnZZ32p6R9ZaBiVsS - country: CA - region: British Columbia - city: Vancouver - ip: 149.22.95.28 - avail_mem_mb: 125000 - avail_vcpus: 46 - avail_storage_gbs: 400 - avail_ipv4: 26 - avail_ipv6: 0 - avail_ports: 20000 - max_ports_per_vm: 5 - price: 20000 - reports: {} - offline_minutes: 0 + - public_key: 7Xw3RxbP5pvfjZ8U6yA3HHVSS9YXjKH5Vkas3JRbQYd9 + operator_wallet: x52w7jARC5erhWWK65VZmjdGXzBK6ZDgfv1A283d8XK + country: GB + region: England + city: London + ip: 173.234.17.2 + avail_mem_mb: 26000 + avail_vcpus: 28 + avail_storage_gbs: 680 + avail_ipv4: 2 + avail_ipv6: 65516 + avail_ports: 19999 + max_ports_per_vm: 5 + price: 20000 + reports: {} + offline_minutes: 0 + - public_key: Du3UfPSUUZmA5thQmc9Vrxdy7UimpygcpDsQNnwRQPtu + operator_wallet: BFopWmwcZAMF1h2PFECZNdEucdZfnZZ32p6R9ZaBiVsS + country: FR + region: Île-de-France + city: Paris + ip: 156.146.63.215 + avail_mem_mb: 123000 + avail_vcpus: 46 + avail_storage_gbs: 440 + avail_ipv4: 2 + avail_ipv6: 0 + avail_ports: 20000 + max_ports_per_vm: 5 + price: 20000 + reports: {} + offline_minutes: 0 + - public_key: 2Uf5pxhxKTUm6gRMnpbJHYDuyA6BWUfFsdmPyWfbMV1f + operator_wallet: x52w7jARC5erhWWK65VZmjdGXzBK6ZDgfv1A283d8XK + country: CA + region: Quebec + city: Montréal + ip: 184.107.169.199 + avail_mem_mb: 30000 + avail_vcpus: 31 + avail_storage_gbs: 700 + avail_ipv4: 0 + avail_ipv6: 0 + avail_ports: 20000 + max_ports_per_vm: 5 + price: 18000 + reports: {} + offline_minutes: 0 + - public_key: DgkbsrwttkZXvzxY5kDwQQoDd79GLmZ5tc7fYJUFkQQb + operator_wallet: BFopWmwcZAMF1h2PFECZNdEucdZfnZZ32p6R9ZaBiVsS + country: CA + region: British Columbia + city: Vancouver + ip: 149.22.95.1 + avail_mem_mb: 109000 + avail_vcpus: 45 + avail_storage_gbs: 400 + avail_ipv4: 25 + avail_ipv6: 0 + avail_ports: 20000 + max_ports_per_vm: 5 + price: 20000 + reports: {} + offline_minutes: 0 + - public_key: 3zRxiGRnf46vd3zAEmpaYBJocTV9oJB6yXf5GZFR1Sq4 + operator_wallet: BFopWmwcZAMF1h2PFECZNdEucdZfnZZ32p6R9ZaBiVsS + country: US + region: California + city: San Jose + ip: 149.36.48.99 + avail_mem_mb: 120000 + avail_vcpus: 41 + avail_storage_gbs: 390 + avail_ipv4: 23 + avail_ipv6: 0 + avail_ports: 19999 + max_ports_per_vm: 5 + price: 20000 + reports: {} + offline_minutes: 0 + - public_key: HiyMp21zaBVbRCjDsD5hEjQnHeHv4e1gpUR6pVfHTKqv + operator_wallet: BFopWmwcZAMF1h2PFECZNdEucdZfnZZ32p6R9ZaBiVsS + country: CA + region: British Columbia + city: Vancouver + ip: 149.22.95.28 + avail_mem_mb: 125000 + avail_vcpus: 46 + avail_storage_gbs: 400 + avail_ipv4: 26 + avail_ipv6: 0 + avail_ports: 20000 + max_ports_per_vm: 5 + price: 20000 + reports: {} + offline_minutes: 0 vm_contracts: -- uuid: 958165e3-dea8-407d-8c42-dd17002ef79c - hostname: detee-landing-fr - admin_pubkey: FHuecMbeC1PfjkW2JKyoicJAuiU7khgQT16QUB3Q1XdL - node_pubkey: Du3UfPSUUZmA5thQmc9Vrxdy7UimpygcpDsQNnwRQPtu - exposed_ports: [] - public_ipv4: 156.146.63.216 - public_ipv6: '' - disk_size_gb: 10 - vcpus: 2 - memory_mb: 3000 - kernel_sha: 3ec4fc5aa5729f515967ec71be4a851622785c0080f7191b1b07717149840151 - dtrfs_sha: 3f6b3e5740f249eedfb2f7248c521a551be8b2676f7fcb040f3f3bc840a5004b - created_at: 2025-02-28T23:19:41.769423466Z - updated_at: 2025-04-12T12:11:58.516768949Z - price_per_unit: 20000 - locked_nano: 14875500000 - collected_at: 2025-04-20T00:34:15.461165181Z -- uuid: e807a2fd-cf90-4a14-bc3a-89ce6dc59033 - hostname: detee-landing-gb - admin_pubkey: FHuecMbeC1PfjkW2JKyoicJAuiU7khgQT16QUB3Q1XdL - node_pubkey: 7Xw3RxbP5pvfjZ8U6yA3HHVSS9YXjKH5Vkas3JRbQYd9 - exposed_ports: [] - public_ipv4: 173.234.136.154 - public_ipv6: '' - disk_size_gb: 10 - vcpus: 2 - memory_mb: 3000 - kernel_sha: 3ec4fc5aa5729f515967ec71be4a851622785c0080f7191b1b07717149840151 - dtrfs_sha: 3f6b3e5740f249eedfb2f7248c521a551be8b2676f7fcb040f3f3bc840a5004b - created_at: 2025-03-06T19:51:39.595163157Z - updated_at: 2025-03-06T19:51:39.595163842Z - price_per_unit: 20000 - locked_nano: 14875500000 - collected_at: 2025-04-20T00:34:15.461181545Z -- uuid: 23094406-2307-4332-a642-acee718d0186 - hostname: heroic-door - admin_pubkey: DwfL5iFu32xh2YMCUxg63oEAThLRqehDAumiP9q6zuuX - node_pubkey: 7Xw3RxbP5pvfjZ8U6yA3HHVSS9YXjKH5Vkas3JRbQYd9 - exposed_ports: - - 38288 - public_ipv4: '' - public_ipv6: '' - disk_size_gb: 10 - vcpus: 1 - memory_mb: 1000 - kernel_sha: 14e225e4aaf84cc2e0b5f64206121186ddebc4b378b886da3b2f7515dfd41692 - dtrfs_sha: 03ce24dbbe917fdd4f6347e61036805ddbdded5044c272bab188ef9333093bee - created_at: 2025-03-12T16:28:24.749161605Z - updated_at: 2025-03-12T16:28:24.749162477Z - price_per_unit: 20000 - locked_nano: 14134140000 - collected_at: 2025-04-20T00:34:15.461191231Z -- uuid: 1f49a71c-f68c-4c64-a82e-f50e0ba0b574 - hostname: astromech-wrench - admin_pubkey: FHuecMbeC1PfjkW2JKyoicJAuiU7khgQT16QUB3Q1XdL - node_pubkey: DgkbsrwttkZXvzxY5kDwQQoDd79GLmZ5tc7fYJUFkQQb - exposed_ports: [] - public_ipv4: 149.22.95.2 - public_ipv6: '' - disk_size_gb: 10 - vcpus: 2 - memory_mb: 3000 - kernel_sha: 3a68709138bed09c16671949cf1f03acee95a08381ba84fc70fb586001fa6767 - dtrfs_sha: 0bb93443f65c9f4379ed469f94794f5c1bf14d8905b0b2c56a125df4a9ebe83e - created_at: 2025-03-20T14:40:25.557753393Z - updated_at: 2025-03-20T14:40:25.557754242Z - price_per_unit: 20000 - locked_nano: 11865620000 - collected_at: 2025-04-20T00:34:15.461201690Z -- uuid: 16577f1c-9867-4a17-80a8-6cf0490f1270 - hostname: sofenty - admin_pubkey: FHuecMbeC1PfjkW2JKyoicJAuiU7khgQT16QUB3Q1XdL - node_pubkey: Du3UfPSUUZmA5thQmc9Vrxdy7UimpygcpDsQNnwRQPtu - exposed_ports: [] - public_ipv4: 156.146.63.217 - public_ipv6: '' - disk_size_gb: 10 - vcpus: 2 - memory_mb: 3000 - kernel_sha: e49c8587287b21df7600c04326fd7393524453918c14d67f73757dc769a13542 - dtrfs_sha: b5f408d00e2b93dc594fed3a7f2466a9878802ff1c7ae502247471cd06728a45 - created_at: 2025-04-07T22:57:57.646151746Z - updated_at: 2025-04-07T22:57:57.646152630Z - price_per_unit: 20000 - locked_nano: 11867500000 - collected_at: 2025-04-20T00:34:15.461211040Z -- uuid: 4b6e25ca-87ac-478b-8f16-aa8f5c44c704 - hostname: cloaked-mailbox - admin_pubkey: DwfL5iFu32xh2YMCUxg63oEAThLRqehDAumiP9q6zuuX - node_pubkey: DgkbsrwttkZXvzxY5kDwQQoDd79GLmZ5tc7fYJUFkQQb - exposed_ports: [] - public_ipv4: 149.22.95.2 - public_ipv6: '' - disk_size_gb: 30 - vcpus: 1 - memory_mb: 1000 - kernel_sha: e49c8587287b21df7600c04326fd7393524453918c14d67f73757dc769a13542 - dtrfs_sha: b5f408d00e2b93dc594fed3a7f2466a9878802ff1c7ae502247471cd06728a45 - created_at: 2025-04-12T13:44:56.957037550Z - updated_at: 2025-04-12T13:44:56.957038546Z - price_per_unit: 20000 - locked_nano: 11177760000 - collected_at: 2025-04-20T00:34:15.461219779Z -- uuid: eb1a13ed-d782-4b71-8860-73540129cb7d - hostname: twenty - admin_pubkey: FHuecMbeC1PfjkW2JKyoicJAuiU7khgQT16QUB3Q1XdL - node_pubkey: 3zRxiGRnf46vd3zAEmpaYBJocTV9oJB6yXf5GZFR1Sq4 - exposed_ports: [] - public_ipv4: 149.36.48.100 - public_ipv6: '' - disk_size_gb: 10 - vcpus: 4 - memory_mb: 4000 - kernel_sha: e49c8587287b21df7600c04326fd7393524453918c14d67f73757dc769a13542 - dtrfs_sha: b5f408d00e2b93dc594fed3a7f2466a9878802ff1c7ae502247471cd06728a45 - created_at: 2025-04-15T00:46:35.622165457Z - updated_at: 2025-04-15T00:46:35.622166372Z - price_per_unit: 20000 - locked_nano: 15570720000 - collected_at: 2025-04-20T00:34:15.461230948Z -- uuid: 1bf36309-3774-4825-b023-b2a0ef0405ed - hostname: shadowy-hobo - admin_pubkey: x52w7jARC5erhWWK65VZmjdGXzBK6ZDgfv1A283d8XK - node_pubkey: 3zRxiGRnf46vd3zAEmpaYBJocTV9oJB6yXf5GZFR1Sq4 - exposed_ports: - - 46393 - public_ipv4: '' - public_ipv6: '' - disk_size_gb: 10 - vcpus: 1 - memory_mb: 1000 - kernel_sha: e765e56166ef321b53399b9638584d1279821dbe3d46191c1f66bbaa075e7919 - dtrfs_sha: d207644ee60d54009b6ecdfb720e2ec251cde31774dd249fcc7435aca0377990 - created_at: 2025-04-16T20:37:57.176592933Z - updated_at: 2025-04-16T20:37:57.176594069Z - price_per_unit: 20000 - locked_nano: 12730960000 - collected_at: 2025-04-20T00:34:15.461240342Z + - uuid: 958165e3-dea8-407d-8c42-dd17002ef79c + hostname: detee-landing-fr + admin_pubkey: FHuecMbeC1PfjkW2JKyoicJAuiU7khgQT16QUB3Q1XdL + node_pubkey: Du3UfPSUUZmA5thQmc9Vrxdy7UimpygcpDsQNnwRQPtu + exposed_ports: [] + public_ipv4: 156.146.63.216 + public_ipv6: "" + disk_size_gb: 10 + vcpus: 2 + memory_mb: 3000 + kernel_sha: 3ec4fc5aa5729f515967ec71be4a851622785c0080f7191b1b07717149840151 + dtrfs_sha: 3f6b3e5740f249eedfb2f7248c521a551be8b2676f7fcb040f3f3bc840a5004b + created_at: 2025-02-28T23:19:41.769423466Z + updated_at: 2025-04-12T12:11:58.516768949Z + price_per_unit: 20000 + locked_nano: 14875500000 + collected_at: 2025-04-20T00:34:15.461165181Z + - uuid: e807a2fd-cf90-4a14-bc3a-89ce6dc59033 + hostname: detee-landing-gb + admin_pubkey: FHuecMbeC1PfjkW2JKyoicJAuiU7khgQT16QUB3Q1XdL + node_pubkey: 7Xw3RxbP5pvfjZ8U6yA3HHVSS9YXjKH5Vkas3JRbQYd9 + exposed_ports: [] + public_ipv4: 173.234.136.154 + public_ipv6: "" + disk_size_gb: 10 + vcpus: 2 + memory_mb: 3000 + kernel_sha: 3ec4fc5aa5729f515967ec71be4a851622785c0080f7191b1b07717149840151 + dtrfs_sha: 3f6b3e5740f249eedfb2f7248c521a551be8b2676f7fcb040f3f3bc840a5004b + created_at: 2025-03-06T19:51:39.595163157Z + updated_at: 2025-03-06T19:51:39.595163842Z + price_per_unit: 20000 + locked_nano: 14875500000 + collected_at: 2025-04-20T00:34:15.461181545Z + - uuid: 23094406-2307-4332-a642-acee718d0186 + hostname: heroic-door + admin_pubkey: DwfL5iFu32xh2YMCUxg63oEAThLRqehDAumiP9q6zuuX + node_pubkey: 7Xw3RxbP5pvfjZ8U6yA3HHVSS9YXjKH5Vkas3JRbQYd9 + exposed_ports: + - 38288 + public_ipv4: "" + public_ipv6: "" + disk_size_gb: 10 + vcpus: 1 + memory_mb: 1000 + kernel_sha: 14e225e4aaf84cc2e0b5f64206121186ddebc4b378b886da3b2f7515dfd41692 + dtrfs_sha: 03ce24dbbe917fdd4f6347e61036805ddbdded5044c272bab188ef9333093bee + created_at: 2025-03-12T16:28:24.749161605Z + updated_at: 2025-03-12T16:28:24.749162477Z + price_per_unit: 20000 + locked_nano: 14134140000 + collected_at: 2025-04-20T00:34:15.461191231Z + - uuid: 1f49a71c-f68c-4c64-a82e-f50e0ba0b574 + hostname: astromech-wrench + admin_pubkey: FHuecMbeC1PfjkW2JKyoicJAuiU7khgQT16QUB3Q1XdL + node_pubkey: DgkbsrwttkZXvzxY5kDwQQoDd79GLmZ5tc7fYJUFkQQb + exposed_ports: [] + public_ipv4: 149.22.95.2 + public_ipv6: "" + disk_size_gb: 10 + vcpus: 2 + memory_mb: 3000 + kernel_sha: 3a68709138bed09c16671949cf1f03acee95a08381ba84fc70fb586001fa6767 + dtrfs_sha: 0bb93443f65c9f4379ed469f94794f5c1bf14d8905b0b2c56a125df4a9ebe83e + created_at: 2025-03-20T14:40:25.557753393Z + updated_at: 2025-03-20T14:40:25.557754242Z + price_per_unit: 20000 + locked_nano: 11865620000 + collected_at: 2025-04-20T00:34:15.461201690Z + - uuid: 16577f1c-9867-4a17-80a8-6cf0490f1270 + hostname: sofenty + admin_pubkey: FHuecMbeC1PfjkW2JKyoicJAuiU7khgQT16QUB3Q1XdL + node_pubkey: Du3UfPSUUZmA5thQmc9Vrxdy7UimpygcpDsQNnwRQPtu + exposed_ports: [] + public_ipv4: 156.146.63.217 + public_ipv6: "" + disk_size_gb: 10 + vcpus: 2 + memory_mb: 3000 + kernel_sha: e49c8587287b21df7600c04326fd7393524453918c14d67f73757dc769a13542 + dtrfs_sha: b5f408d00e2b93dc594fed3a7f2466a9878802ff1c7ae502247471cd06728a45 + created_at: 2025-04-07T22:57:57.646151746Z + updated_at: 2025-04-07T22:57:57.646152630Z + price_per_unit: 20000 + locked_nano: 11867500000 + collected_at: 2025-04-20T00:34:15.461211040Z + - uuid: 4b6e25ca-87ac-478b-8f16-aa8f5c44c704 + hostname: cloaked-mailbox + admin_pubkey: DwfL5iFu32xh2YMCUxg63oEAThLRqehDAumiP9q6zuuX + node_pubkey: DgkbsrwttkZXvzxY5kDwQQoDd79GLmZ5tc7fYJUFkQQb + exposed_ports: [] + public_ipv4: 149.22.95.2 + public_ipv6: "" + disk_size_gb: 30 + vcpus: 1 + memory_mb: 1000 + kernel_sha: e49c8587287b21df7600c04326fd7393524453918c14d67f73757dc769a13542 + dtrfs_sha: b5f408d00e2b93dc594fed3a7f2466a9878802ff1c7ae502247471cd06728a45 + created_at: 2025-04-12T13:44:56.957037550Z + updated_at: 2025-04-12T13:44:56.957038546Z + price_per_unit: 20000 + locked_nano: 11177760000 + collected_at: 2025-04-20T00:34:15.461219779Z + - uuid: eb1a13ed-d782-4b71-8860-73540129cb7d + hostname: twenty + admin_pubkey: FHuecMbeC1PfjkW2JKyoicJAuiU7khgQT16QUB3Q1XdL + node_pubkey: 3zRxiGRnf46vd3zAEmpaYBJocTV9oJB6yXf5GZFR1Sq4 + exposed_ports: [] + public_ipv4: 149.36.48.100 + public_ipv6: "" + disk_size_gb: 10 + vcpus: 4 + memory_mb: 4000 + kernel_sha: e49c8587287b21df7600c04326fd7393524453918c14d67f73757dc769a13542 + dtrfs_sha: b5f408d00e2b93dc594fed3a7f2466a9878802ff1c7ae502247471cd06728a45 + created_at: 2025-04-15T00:46:35.622165457Z + updated_at: 2025-04-15T00:46:35.622166372Z + price_per_unit: 20000 + locked_nano: 15570720000 + collected_at: 2025-04-20T00:34:15.461230948Z + - uuid: 1bf36309-3774-4825-b023-b2a0ef0405ed + hostname: shadowy-hobo + admin_pubkey: x52w7jARC5erhWWK65VZmjdGXzBK6ZDgfv1A283d8XK + node_pubkey: 3zRxiGRnf46vd3zAEmpaYBJocTV9oJB6yXf5GZFR1Sq4 + exposed_ports: + - 46393 + public_ipv4: "" + public_ipv6: "" + disk_size_gb: 10 + vcpus: 1 + memory_mb: 1000 + kernel_sha: e765e56166ef321b53399b9638584d1279821dbe3d46191c1f66bbaa075e7919 + dtrfs_sha: d207644ee60d54009b6ecdfb720e2ec251cde31774dd249fcc7435aca0377990 + created_at: 2025-04-16T20:37:57.176592933Z + updated_at: 2025-04-16T20:37:57.176594069Z + price_per_unit: 20000 + locked_nano: 12730960000 + collected_at: 2025-04-20T00:34:15.461240342Z app_nodes: -- node_pubkey: BiqoPUEoAxYxMRXUmyofoS9H1TBQgQqvLJ6MbWh88AQg - operator_wallet: 7V3rEuh6j8VuwMVB5PyGqWKLmjJ4fYSv6WtrTL51NZTB - country: DE - region: Hesse - city: Frankfurt am Main - ip: 212.95.45.139 - avail_mem_mb: 16000 - avail_vcpus: 16 - avail_storage_mb: 200000 - avail_no_of_port: 20000 - max_ports_per_app: 9 - price: 20000 - offline_minutes: 0 -app_contracts: [] + - node_pubkey: BiqoPUEoAxYxMRXUmyofoS9H1TBQgQqvLJ6MbWh88AQg + operator_wallet: 7V3rEuh6j8VuwMVB5PyGqWKLmjJ4fYSv6WtrTL51NZTB + country: DE + region: Hesse + city: Frankfurt am Main + ip: 212.95.45.139 + avail_mem_mb: 16000 + avail_vcpus: 16 + avail_storage_mb: 200000 + avail_no_of_port: 20000 + max_ports_per_app: 9 + price: 20000 + offline_minutes: 0 +app_contracts: + - uuid: e3d01f25-2b2a-410b-80e3-12f44e474334 + package_url: https://registry.detee.ltd/sgx/packages/base_package_2025-04-17_11-01-08.tar.gz + admin_pubkey: H21Shi4iE7vgfjWEQNvzmpmBMJSaiZ17PYUcdNoAoKNc + node_pubkey: BiqoPUEoAxYxMRXUmyofoS9H1TBQgQqvLJ6MbWh88AQg + mapped_ports: + - - 27158 + - 34500 + - - 28667 + - 8080 + host_ipv4: 212.95.45.139 + disk_size_mb: 1000 + vcpus: 1 + memory_mb: 1000 + created_at: 2025-04-21T11:27:28.833236909Z + updated_at: 2025-04-21T11:27:28.833237729Z + price_per_unit: 200000 + locked_nano: 121200000 + collected_at: 2025-04-21T11:28:24.905665571Z + hratls_pubkey: 7E0F887AA6BB9104EEC1066F454D4C2D9063D676715F55F919D3FBCEDC63240B + public_package_mr_enclave: + - 52 + - 183 + - 102 + - 210 + - 251 + - 219 + - 218 + - 140 + - 168 + - 118 + - 10 + - 193 + - 98 + - 240 + - 147 + - 124 + - 240 + - 189 + - 46 + - 95 + - 138 + - 172 + - 15 + - 246 + - 227 + - 114 + - 70 + - 159 + - 232 + - 212 + - 9 + - 234 + app_name: diligent-seahorse diff --git a/src/constants.rs b/src/constants.rs index ae7b5e5..87f6210 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -5,7 +5,8 @@ pub const CERT_PATH: &str = "/etc/detee/brain/brain-crt.pem"; pub const CERT_KEY_PATH: &str = "/etc/detee/brain/brain-key.pem"; pub const CONFIG_PATH: &str = "/etc/detee/brain/config.ini"; -pub const DB_SCHEMA_FILE: &str = "interim_tables.surql"; +pub const DB_SCHEMA_FILES: [&str; 3] = + ["surql/tables.sql", "surql/timer.sql", "surql/functions.sql"]; pub static ADMIN_ACCOUNTS: LazyLock> = LazyLock::new(|| { let default_admin_keys = vec![ diff --git a/src/db/app.rs b/src/db/app.rs index b11955b..2ce99ae 100644 --- a/src/db/app.rs +++ b/src/db/app.rs @@ -174,11 +174,11 @@ pub struct ActiveApp { #[serde(rename = "out")] pub app_node: RecordId, pub app_name: String, - pub mapped_ports: Vec<(u64, u64)>, + pub mapped_ports: Vec<(u32, u32)>, pub host_ipv4: String, - pub vcpus: u64, - pub memory_mb: u64, - pub disk_size_gb: u64, + pub vcpus: u32, + pub memory_mb: u32, + pub disk_size_gb: u32, pub created_at: Datetime, pub price_per_unit: u64, pub locked_nano: u64, @@ -234,9 +234,9 @@ impl ActiveApp { app_name: new_app_req.app_name, mapped_ports: vec![], host_ipv4: String::new(), - vcpus: new_app_req.vcpu as u64, - memory_mb: new_app_req.memory_mb as u64, - disk_size_gb: new_app_req.disk_mb as u64, + vcpus: new_app_req.vcpu, + memory_mb: new_app_req.memory_mb, + disk_size_gb: new_app_req.disk_mb, created_at: new_app_req.created_at.clone(), price_per_unit: new_app_req.price_per_unit, locked_nano: new_app_req.locked_nano, @@ -312,11 +312,11 @@ pub struct ActiveAppWithNode { #[serde(rename = "out")] pub app_node: AppNode, pub app_name: String, - pub mapped_ports: Vec<(u64, u64)>, + pub mapped_ports: Vec<(u32, u32)>, pub host_ipv4: String, - pub vcpus: u64, - pub memory_mb: u64, - pub disk_size_gb: u64, + pub vcpus: u32, + pub memory_mb: u32, + pub disk_size_gb: u32, pub created_at: Datetime, pub price_per_unit: u64, pub locked_nano: u64, @@ -427,7 +427,7 @@ impl From<&old_brain::BrainData> for Vec { let mut nodes = Vec::new(); for old_node in old_data.app_nodes.iter() { nodes.push(AppNode { - id: RecordId::from(("app_node", old_node.node_pubkey.clone())), + id: RecordId::from((APP_NODE, old_node.node_pubkey.clone())), operator: RecordId::from((ACCOUNT, old_node.operator_wallet.clone())), country: old_node.country.clone(), region: old_node.region.clone(), @@ -446,6 +446,46 @@ impl From<&old_brain::BrainData> for Vec { } } +impl From<&old_brain::BrainData> for Vec { + fn from(old_data: &old_brain::BrainData) -> Self { + let mut contracts = Vec::new(); + for old_c in old_data.app_contracts.iter() { + let mut mapped_ports = Vec::new(); + for port in old_c.mapped_ports.clone().into_iter().map(|(b, c)| (b as u32, c as u32)) { + mapped_ports.push(port); + } + + let mr_enclave_hex = old_c + .public_package_mr_enclave + .clone() + .unwrap_or_default() + .iter() + .map(|byte| format!("{:02X}", byte)) + .collect(); + + contracts.push(ActiveApp { + id: RecordId::from((ACTIVE_APP, old_c.uuid.replace("-", ""))), + admin: RecordId::from((ACCOUNT, old_c.admin_pubkey.clone())), + app_node: RecordId::from((APP_NODE, old_c.node_pubkey.clone())), + mapped_ports, + host_ipv4: old_c.host_ipv4.clone(), + disk_size_gb: old_c.disk_size_mb * 1024, + vcpus: old_c.vcpus, + memory_mb: old_c.memory_mb, + price_per_unit: old_c.price_per_unit, + locked_nano: old_c.locked_nano, + created_at: old_c.created_at.into(), + collected_at: old_c.collected_at.into(), + app_name: old_c.app_name.clone(), + mr_enclave: mr_enclave_hex, + package_url: old_c.package_url.clone(), + hratls_pubkey: old_c.hratls_pubkey.clone(), + }); + } + contracts + } +} + #[derive(Debug, Serialize, Deserialize)] pub struct DeletedApp { pub id: RecordId, @@ -454,11 +494,11 @@ pub struct DeletedApp { #[serde(rename = "out")] pub app_node: RecordId, pub app_name: String, - pub mapped_ports: Vec<(u64, u64)>, + pub mapped_ports: Vec<(u32, u32)>, pub host_ipv4: String, - pub vcpus: u64, - pub memory_mb: u64, - pub disk_size_gb: u64, + pub vcpus: u32, + pub memory_mb: u32, + pub disk_size_gb: u32, pub created_at: Datetime, pub price_per_unit: u64, pub locked_nano: u64, diff --git a/src/db/general.rs b/src/db/general.rs index b31beb0..ce7fe0a 100644 --- a/src/db/general.rs +++ b/src/db/general.rs @@ -110,7 +110,7 @@ impl From<&old_brain::BrainData> for Vec { let mut accounts = Vec::new(); for old_account in old_data.accounts.iter() { let mut a = Account { - id: RecordId::from(("account", old_account.key())), + id: RecordId::from((ACCOUNT, old_account.key())), balance: old_account.value().balance, tmp_locked: old_account.value().tmp_locked, escrow: 0, diff --git a/src/db/mod.rs b/src/db/mod.rs index 2129d0e..818f6c3 100644 --- a/src/db/mod.rs +++ b/src/db/mod.rs @@ -3,7 +3,8 @@ pub mod general; pub mod vm; use crate::constants::{ - APP_NODE, DELETED_APP, DELETED_VM, MIN_ESCROW, NEW_APP_REQ, NEW_VM_REQ, UPDATE_VM_REQ, + APP_NODE, DB_SCHEMA_FILES, DELETED_APP, DELETED_VM, MIN_ESCROW, NEW_APP_REQ, NEW_VM_REQ, + UPDATE_VM_REQ, }; use crate::old_brain; use prelude::*; @@ -73,10 +74,12 @@ pub async fn migration0( let accounts: Vec = old_data.into(); let vm_nodes: Vec = old_data.into(); let app_nodes: Vec = old_data.into(); - let vm_contracts: Vec = old_data.into(); + let active_vm: Vec = old_data.into(); + let active_app: Vec = old_data.into(); - let schema = std::fs::read_to_string(crate::constants::DB_SCHEMA_FILE)?; - db.query(schema).await?; + for schema in DB_SCHEMA_FILES.map(std::fs::read_to_string) { + db.query(schema?).await?; + } println!("Inserting accounts..."); let _: Vec = db.insert(()).content(accounts).await?; @@ -84,8 +87,10 @@ pub async fn migration0( let _: Vec = db.insert(()).content(vm_nodes).await?; println!("Inserting app nodes..."); let _: Vec = db.insert(()).content(app_nodes).await?; - println!("Inserting vm contracts..."); - let _: Vec = db.insert("vm_contract").relation(vm_contracts).await?; + println!("Inserting active vm contracts..."); + let _: Vec = db.insert(()).relation(active_vm).await?; + println!("Inserting app contracts..."); + let _: Vec = db.insert(()).relation(active_app).await?; Ok(()) } diff --git a/tests/common/prepare_test_env.rs b/tests/common/prepare_test_env.rs index 7a1c825..96a8587 100644 --- a/tests/common/prepare_test_env.rs +++ b/tests/common/prepare_test_env.rs @@ -6,6 +6,7 @@ use dotenv::dotenv; use hyper_util::rt::TokioIo; use std::net::SocketAddr; use std::sync::Arc; +use surreal_brain::constants::DB_SCHEMA_FILES; use surreal_brain::grpc::general::GeneralCliServer; use surreal_brain::grpc::vm::{VmCliServer, VmDaemonServer}; use surrealdb::engine::remote::ws::Client; @@ -34,7 +35,9 @@ pub async fn prepare_test_db() -> Result> { .map_err(|e| anyhow!(e.to_string()))?; db.query(format!("REMOVE DATABASE {db_name}")).await?; - db.query(std::fs::read_to_string("interim_tables.surql")?).await?; + for schema in DB_SCHEMA_FILES.map(std::fs::read_to_string) { + db.query(schema?).await?; + } surreal_brain::db::migration0(&db, &old_brain_data).await?; Ok::<(), anyhow::Error>(()) }) diff --git a/tests/common/vm_daemon_utils.rs b/tests/common/vm_daemon_utils.rs index 2842b85..be49c3e 100644 --- a/tests/common/vm_daemon_utils.rs +++ b/tests/common/vm_daemon_utils.rs @@ -53,18 +53,18 @@ pub async fn register_vm_node( let mut grpc_stream = client.register_vm_node(key.sign_request(req)?).await?.into_inner(); - let mut vm_contracts = Vec::new(); + let mut deleted_vm_reqs = Vec::new(); while let Some(stream_update) = grpc_stream.next().await { match stream_update { - Ok(vm_c) => { - vm_contracts.push(vm_c); + Ok(del_vm_rq) => { + deleted_vm_reqs.push(del_vm_rq); } Err(e) => { - panic!("Received error instead of vm_contracts: {e:?}"); + panic!("Received error instead of deleted_vm_reqs: {e:?}"); } } } - Ok(vm_contracts) + Ok(deleted_vm_reqs) } pub async fn daemon_listener( diff --git a/tests/grpc_general_test.rs b/tests/grpc_general_test.rs index 299fff8..6fa4ad6 100644 --- a/tests/grpc_general_test.rs +++ b/tests/grpc_general_test.rs @@ -237,11 +237,17 @@ async fn test_kick_contract() { let kick_response = surreal_brain::db::general::WrapperContract::kick_contract( &db_conn, - &operator_wallet, - &contract_uuid, - &reason, + operator_wallet, + contract_uuid, + reason, ) .await; - - dbg!(kick_response.unwrap()); + match kick_response { + Ok(refund_amount) => { + println!("Refund amount: {}", refund_amount); + } + Err(e) => { + println!("Error: {}", e); + } + } } -- 2.43.0 From 540e5bfa4c5f319ae3ba29ac8bc07684a62b88ed Mon Sep 17 00:00:00 2001 From: Noor Date: Wed, 21 May 2025 17:16:44 +0530 Subject: [PATCH 5/6] fixed all tests enhanced airdrop and handling admin in test airdroping to wallet before creating vm --- tests/common/test_utils.rs | 15 +++++++++++++++ tests/common/vm_cli_utils.rs | 7 +++---- tests/grpc_general_test.rs | 23 ++++++++++------------- tests/grpc_vm_cli_test.rs | 12 ++++++++++-- tests/grpc_vm_daemon_test.rs | 4 +++- 5 files changed, 41 insertions(+), 20 deletions(-) diff --git a/tests/common/test_utils.rs b/tests/common/test_utils.rs index af25ecd..5ae3fae 100644 --- a/tests/common/test_utils.rs +++ b/tests/common/test_utils.rs @@ -1,9 +1,24 @@ use anyhow::Result; use detee_shared::vm_proto as snp_proto; use ed25519_dalek::{Signer, SigningKey}; +use itertools::Itertools; +use std::sync::OnceLock; use tonic::metadata::AsciiMetadataValue; use tonic::Request; +pub static ADMIN_KEYS: OnceLock> = OnceLock::new(); + +pub fn admin_keys() -> Vec { + let admin_keys = ADMIN_KEYS.get_or_init(|| { + let admin_keys = vec![Key::new(), Key::new(), Key::new()]; + let admin_pub_keys = admin_keys.iter().map(|k| k.pubkey.clone()).join(", "); + std::env::set_var("ADMIN_PUB_KEYS", admin_pub_keys); + admin_keys.clone() + }); + + admin_keys.clone() +} + #[derive(Debug, Clone)] pub struct Key { pub sg_key: SigningKey, diff --git a/tests/common/vm_cli_utils.rs b/tests/common/vm_cli_utils.rs index 2d7c2c3..3a1bb40 100644 --- a/tests/common/vm_cli_utils.rs +++ b/tests/common/vm_cli_utils.rs @@ -1,4 +1,4 @@ -use super::test_utils::Key; +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; @@ -11,12 +11,11 @@ use surrealdb::engine::remote::ws::Client; use surrealdb::Surreal; use tonic::transport::Channel; -async fn airdrop(brain_channel: &Channel, wallet: &str, amount: u64) -> Result<()> { +pub async fn airdrop(brain_channel: &Channel, wallet: &str, amount: u64) -> Result<()> { let mut client = BrainGeneralCliClient::new(brain_channel.clone()); let airdrop_req = AirdropReq { pubkey: wallet.to_string(), tokens: amount }; - let admin_key = Key::new(); - std::env::set_var("ADMIN_PUB_KEYS", &admin_key.pubkey); + let admin_key = admin_keys()[0].clone(); client.airdrop(admin_key.sign_request(airdrop_req.clone())?).await?; diff --git a/tests/grpc_general_test.rs b/tests/grpc_general_test.rs index 6fa4ad6..2c601a0 100644 --- a/tests/grpc_general_test.rs +++ b/tests/grpc_general_test.rs @@ -1,17 +1,15 @@ use common::prepare_test_env::{ prepare_test_db, run_service_for_stream, run_service_in_background, }; -use common::test_utils::Key; -use common::vm_cli_utils::{create_new_vm, report_node}; +use common::test_utils::{admin_keys, Key}; +use common::vm_cli_utils::{airdrop, create_new_vm, 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; use detee_shared::vm_proto::brain_vm_daemon_client::BrainVmDaemonClient; use futures::StreamExt; -use itertools::Itertools; -use std::vec; -use surreal_brain::constants::VM_NODE; +use surreal_brain::constants::{TOKEN_DECIMAL, VM_NODE}; use surreal_brain::db::vm::VmNodeWithReports; mod common; @@ -41,15 +39,12 @@ async fn test_general_airdrop() { // env_logger::builder().filter_level(log::LevelFilter::Trace).init(); prepare_test_db().await.unwrap(); - const AIRDROP_MULTIPLE: u64 = 1_000_000_000; 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 = vec![Key::new(), Key::new(), Key::new()]; - let admin_pub_keys = admin_keys.iter().map(|k| k.pubkey.clone()).join(", "); - std::env::set_var("ADMIN_PUB_KEYS", admin_pub_keys); + let admin_keys = admin_keys(); let user_01_key = Key::new(); let user_01_pubkey = user_01_key.pubkey.clone(); @@ -72,7 +67,7 @@ async fn test_general_airdrop() { 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 * AIRDROP_MULTIPLE); + 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 @@ -84,7 +79,7 @@ async fn test_general_airdrop() { .unwrap() .into_inner(); - assert_eq!(acc_bal_user_01.balance, 2 * airdrop_amount * AIRDROP_MULTIPLE); + 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(); @@ -95,7 +90,7 @@ async fn test_general_airdrop() { .unwrap() .into_inner(); - assert_eq!(acc_bal_user_01.balance, 3 * airdrop_amount * AIRDROP_MULTIPLE); + 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 }; @@ -109,7 +104,7 @@ async fn test_general_airdrop() { .unwrap() .into_inner(); - assert_eq!(acc_bal_admin_3.balance, airdrop_amount * AIRDROP_MULTIPLE); + assert_eq!(acc_bal_admin_3.balance, airdrop_amount * TOKEN_DECIMAL); } #[tokio::test] @@ -127,6 +122,8 @@ async fn test_report_node() { 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"); diff --git a/tests/grpc_vm_cli_test.rs b/tests/grpc_vm_cli_test.rs index 4b6069d..b6d4c71 100644 --- a/tests/grpc_vm_cli_test.rs +++ b/tests/grpc_vm_cli_test.rs @@ -1,12 +1,14 @@ use common::prepare_test_env::{prepare_test_db, run_service_for_stream}; use common::test_utils::Key; -use common::vm_cli_utils::create_new_vm; +use common::vm_cli_utils::{airdrop, create_new_vm}; use common::vm_daemon_utils::{mock_vm_daemon, register_vm_node}; use detee_shared::vm_proto::brain_vm_cli_client::BrainVmCliClient; use detee_shared::vm_proto::brain_vm_daemon_client::BrainVmDaemonClient; use detee_shared::vm_proto::{ListVmContractsReq, NewVmReq}; use futures::StreamExt; use std::vec; +use surreal_brain::constants::ACTIVE_VM; +use surreal_brain::db::vm::ActiveVm; mod common; @@ -26,7 +28,11 @@ async fn test_vm_creation() { let grpc_error_message = new_vm_resp.err().unwrap().to_string(); assert!(grpc_error_message.contains("Insufficient funds")); - // TODO: Airdrop the user and try creating the VM again + airdrop(&brain_channel, &key.pubkey, 10).await.unwrap(); + + let new_vm_id = create_new_vm(&db, &key, &daemon_key, &brain_channel).await.unwrap(); + let active_vm: Option = db.select((ACTIVE_VM, new_vm_id)).await.unwrap(); + assert!(active_vm.is_some()); } #[tokio::test] @@ -51,6 +57,8 @@ async fn test_timeout_vm_creation() { ..Default::default() }; + airdrop(&brain_channel, &key.pubkey, 10).await.unwrap(); + let mut client_vm_cli = BrainVmCliClient::new(brain_channel.clone()); let timeout_error = client_vm_cli.new_vm(key.sign_request(new_vm_req).unwrap()).await.err().unwrap(); diff --git a/tests/grpc_vm_daemon_test.rs b/tests/grpc_vm_daemon_test.rs index 89d7983..c68c5da 100644 --- a/tests/grpc_vm_daemon_test.rs +++ b/tests/grpc_vm_daemon_test.rs @@ -2,6 +2,7 @@ use common::prepare_test_env::{ prepare_test_db, run_service_for_stream, run_service_in_background, }; use common::test_utils::Key; +use common::vm_cli_utils::airdrop; use common::vm_daemon_utils::{mock_vm_daemon, register_vm_node}; use detee_shared::vm_proto; use detee_shared::vm_proto::brain_vm_cli_client::BrainVmCliClient; @@ -29,7 +30,7 @@ async fn test_brain_message() { let brain_channel = run_service_for_stream().await.unwrap(); let daemon_key = mock_vm_daemon(&brain_channel).await.unwrap(); - let mut cli_client = BrainVmCliClient::new(brain_channel); + let mut cli_client = BrainVmCliClient::new(brain_channel.clone()); let cli_key = Key::new(); @@ -41,6 +42,7 @@ async fn test_brain_message() { locked_nano: 0, ..Default::default() }; + airdrop(&brain_channel, &cli_key.pubkey, 10).await.unwrap(); let new_vm_resp = cli_client.new_vm(cli_key.sign_request(req).unwrap()).await.unwrap().into_inner(); -- 2.43.0 From db60a3f8077c4574b58ae62f1345d83470b430d4 Mon Sep 17 00:00:00 2001 From: Noor Date: Fri, 23 May 2025 00:42:54 +0530 Subject: [PATCH 6/6] Enhanced kick contract calculating refund inside transaction query db function to calculate price per minute for app binded all kick contract query to prevent sql injection proper logging for kick contract fix table schema app_node typo and default time on deleted_app removed wrapper contract struct removed migrating timer.sql fixed some clippy warnings --- saved_data.yaml | 2 +- src/constants.rs | 3 +- src/db/app.rs | 10 ++- src/db/general.rs | 168 ++++++++++++++----------------------- src/db/mod.rs | 9 +- src/db/vm.rs | 23 ++--- src/grpc/app.rs | 2 +- src/grpc/general.rs | 13 ++- src/grpc/mod.rs | 8 +- src/grpc/types.rs | 14 ++-- surql/functions.sql | 10 +++ surql/tables.sql | 4 +- tests/grpc_general_test.rs | 18 ++-- 13 files changed, 126 insertions(+), 158 deletions(-) diff --git a/saved_data.yaml b/saved_data.yaml index 9b9dc16..26195ad 100644 --- a/saved_data.yaml +++ b/saved_data.yaml @@ -128,7 +128,7 @@ operators: - 7Xw3RxbP5pvfjZ8U6yA3HHVSS9YXjKH5Vkas3JRbQYd9 app_nodes: [] 7V3rEuh6j8VuwMVB5PyGqWKLmjJ4fYSv6WtrTL51NZTB: - escrow: 0 + escrow: 888888888899999 email: "" banned_users: [] vm_nodes: [] diff --git a/src/constants.rs b/src/constants.rs index 87f6210..33cc61f 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -5,8 +5,7 @@ pub const CERT_PATH: &str = "/etc/detee/brain/brain-crt.pem"; pub const CERT_KEY_PATH: &str = "/etc/detee/brain/brain-key.pem"; pub const CONFIG_PATH: &str = "/etc/detee/brain/config.ini"; -pub const DB_SCHEMA_FILES: [&str; 3] = - ["surql/tables.sql", "surql/timer.sql", "surql/functions.sql"]; +pub const DB_SCHEMA_FILES: [&str; 2] = ["surql/tables.sql", "surql/functions.sql"]; pub static ADMIN_ACCOUNTS: LazyLock> = LazyLock::new(|| { let default_admin_keys = vec![ diff --git a/src/db/app.rs b/src/db/app.rs index 2ce99ae..2245313 100644 --- a/src/db/app.rs +++ b/src/db/app.rs @@ -351,8 +351,12 @@ impl From for ActiveApp { impl ActiveAppWithNode { pub async fn get_by_uuid(db: &Surreal, uuid: &str) -> Result, Error> { - let contract: Option = - db.query(format!("select * from {ACTIVE_APP}:{uuid} fetch out;")).await?.take(0)?; + let contract: Option = db + .query(format!("select * from {ACTIVE_APP} where id = $uuid_input fetch out;")) + .bind(("uuid_input", RecordId::from((ACTIVE_APP, uuid)))) + .await? + .take(0)?; + Ok(contract) } @@ -460,7 +464,7 @@ impl From<&old_brain::BrainData> for Vec { .clone() .unwrap_or_default() .iter() - .map(|byte| format!("{:02X}", byte)) + .map(|byte| format!("{byte:02X}")) .collect(); contracts.push(ActiveApp { diff --git a/src/db/general.rs b/src/db/general.rs index ce7fe0a..7597e7a 100644 --- a/src/db/general.rs +++ b/src/db/general.rs @@ -276,114 +276,60 @@ impl Operator { } } -pub enum WrapperContract { - Vm(ActiveVmWithNode), - App(ActiveAppWithNode), -} - -impl WrapperContract { - pub async fn kick_contract( - db: &Surreal, - operator_wallet: &str, - contract_uuid: &str, - reason: &str, - ) -> Result { - 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, - ", - ) +pub async fn kick_contract( + db: &Surreal, + operator_wallet: &str, + contract_uuid: &str, + reason: &str, +) -> Result { + let (contract_id, operator_id, admin_id, app_or_vm) = + if let Some(active_vm) = ActiveVmWithNode::get_by_uuid(db, contract_uuid).await? { + (active_vm.id, active_vm.vm_node.operator, active_vm.admin, "vm") } else if let Some(active_app) = ActiveAppWithNode::get_by_uuid(db, contract_uuid).await? { - let price_per_minute = Into::::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, - ", - ) + (active_app.id, active_app.app_node.operator, active_app.admin, "app") } else { return Err(Error::ContractNotFound); }; - let operator = operator_id.key().to_string(); - let admin = admin_id.key().to_string(); + if operator_id.key().to_string() != operator_wallet { + return Err(Error::AccessDenied); + } - 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(); + log::debug!("Kicking contract {contract_id} by operator {operator_id} for reason: '{reason}'",); - 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 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 $reason = $reason_str_input; + LET $contract_id = record::id($contract.id); LET $admin = $contract.in; LET $node = $contract.out; - -- move contract into deleted state + LET $active_contract = (select * from $contract)[0]; + LET $deleted_contract = $active_contract.patch([{{ + 'op': 'replace', + 'path': 'id', + 'value': type::record('deleted_{app_or_vm}:' + $contract_id) + }}]); + LET $deleted_contract = (INSERT RELATION INTO deleted_{app_or_vm} ( $deleted_contract ) RETURN AFTER)[0]; - 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 minutes + LET $one_week_minutes = duration::mins(1w); + LET $uncollected_minutes = (time::now() - $active_contract.collected_at).mins(); + + LET $minutes_to_refund = if $uncollected_minutes > $one_week_minutes {{ + $one_week_minutes; + }} ELSE {{ + $uncollected_minutes; + }}; -- calculating refund amount + LET $prince_per_minute = fn::{app_or_vm}_price_per_minute($active_contract.id); + + LET $refund_amount = $prince_per_minute * $minutes_to_refund; + LET $refund = IF SELECT * FROM {KICK} WHERE out = $admin.id AND created_at > time::now() - 24h {{ 0 }} ELSE IF $operator_account.escrow <= $refund_amount {{ @@ -392,14 +338,14 @@ impl WrapperContract { $refund_amount; }}; - RELATE $operator_account->{KICK}->$admin - SET id = $id, + SET id = $contract_id, reason = $reason, - contract = $deleted_contract, - node = $node, + contract = $deleted_contract.id, + node = $node.id, created_at = time::now() ; + DELETE $active_contract.id; -- update balances UPDATE $operator_account SET escrow -= $refund; @@ -408,16 +354,28 @@ impl WrapperContract { }}; UPDATE $admin SET balance += $refund; - SELECT * FROM $refund; + $refund; - COMMIT TRANSACTION; - ", - ); + COMMIT TRANSACTION; + ", + ); - log::trace!("kick_contract transaction_query: {}", &transaction_query); - let refunded: Option = db.query(transaction_query).await?.take(14)?; - let refunded_amount = refunded.ok_or(Error::FailedToCreateDBEntry("Refund".to_string()))?; + log::trace!("kick_contract transaction_query: {}", &transaction_query); - Ok(refunded_amount) + let mut query_res = + db.query(transaction_query).bind(("reason_str_input", reason.to_string())).await?; + + log::trace!("transaction_query response: {:?}", &query_res); + + let query_error = query_res.take_errors(); + if !query_error.is_empty() { + log::error!("kick_contract query error: {query_error:?}"); + return Err(Error::FailedKickContract(contract_id.to_string())); } + + let refunded: Option = query_res.take(20)?; + let refunded_amount = refunded.ok_or(Error::FailedToCreateDBEntry("Refund".to_string()))?; + log::info!("Refunded: {refunded_amount} to {admin_id}"); + + Ok(refunded_amount) } diff --git a/src/db/mod.rs b/src/db/mod.rs index 818f6c3..cd06be1 100644 --- a/src/db/mod.rs +++ b/src/db/mod.rs @@ -43,6 +43,8 @@ pub enum Error { AccessDenied, #[error("Failed to delete contract {0}")] FailedToDeleteContract(String), + #[error("Failed to kick contract {0}")] + FailedKickContract(String), } pub mod prelude { @@ -77,8 +79,11 @@ pub async fn migration0( let active_vm: Vec = old_data.into(); let active_app: Vec = old_data.into(); - for schema in DB_SCHEMA_FILES.map(std::fs::read_to_string) { - db.query(schema?).await?; + for schema_data in DB_SCHEMA_FILES.map(|path| (std::fs::read_to_string(path), path)) { + let schema_file = schema_data.1; + println!("Loading schema from {schema_file}"); + let schema = schema_data.0?; + db.query(schema).await?; } println!("Inserting accounts..."); diff --git a/src/db/vm.rs b/src/db/vm.rs index ff5e3fe..c9ddcee 100644 --- a/src/db/vm.rs +++ b/src/db/vm.rs @@ -219,7 +219,7 @@ impl NewVmReq { ->vm_node:{vm_node} CONTENT {{ created_at: time::now(), hostname: '{}', vcpus: {}, memory_mb: {}, disk_size_gb: {}, - extra_ports: {}, public_ipv4: {:?}, public_ipv6: {:?}, + extra_ports: {:?}, public_ipv4: {:?}, public_ipv6: {:?}, dtrfs_url: '{}', dtrfs_sha: '{}', kernel_url: '{}', kernel_sha: '{}', price_per_unit: {}, locked_nano: {locked_nano}, error: '' }}; @@ -229,7 +229,7 @@ impl NewVmReq { self.vcpus, self.memory_mb, self.disk_size_gb, - format!("{:?}", self.extra_ports,), + self.extra_ports, self.public_ipv4, self.public_ipv6, self.dtrfs_url, @@ -242,13 +242,11 @@ impl NewVmReq { let mut query_resp = db.query(query).await?; let resp_err = query_resp.take_errors(); - if let Some(insufficient_funds_error) = resp_err.get(&1) { - if let surrealdb::Error::Api(surrealdb::error::Api::Query(tx_query_error)) = - insufficient_funds_error - { - log::error!("Transaction error: {}", tx_query_error); - return Err(Error::InsufficientFunds); - } + if let Some(surrealdb::Error::Api(surrealdb::error::Api::Query(tx_query_error))) = + resp_err.get(&1) + { + log::error!("Transaction error: {tx_query_error}"); + return Err(Error::InsufficientFunds); } Ok(()) @@ -753,8 +751,11 @@ impl From for ActiveVm { impl ActiveVmWithNode { pub async fn get_by_uuid(db: &Surreal, uuid: &str) -> Result, Error> { - let contract: Option = - db.query(format!("select * from {ACTIVE_VM}:{uuid} fetch out;")).await?.take(0)?; + let contract: Option = db + .query(format!("select * from {ACTIVE_VM} where id = $uuid_input fetch out;")) + .bind(("uuid_input", RecordId::from((ACTIVE_VM, uuid)))) + .await? + .take(0)?; Ok(contract) } diff --git a/src/grpc/app.rs b/src/grpc/app.rs index 1cf31ee..78be5af 100644 --- a/src/grpc/app.rs +++ b/src/grpc/app.rs @@ -115,7 +115,7 @@ impl BrainAppDaemon for AppDaemonServer { let mut req_stream = req.into_inner(); let pubkey: String; if let Some(Ok(msg)) = req_stream.next().await { - log::debug!("App daemon_messages received auth message: {:?}", msg); + log::debug!("App daemon_messages received auth message: {msg:?}"); if let Some(daemon_message_app::Msg::Auth(auth)) = msg.msg { pubkey = auth.pubkey.clone(); check_sig_from_parts( diff --git a/src/grpc/general.rs b/src/grpc/general.rs index 35e15b9..cf3bf2b 100644 --- a/src/grpc/general.rs +++ b/src/grpc/general.rs @@ -127,13 +127,9 @@ impl BrainGeneralCli for GeneralCliServer { async fn kick_contract(&self, req: Request) -> Result, Status> { let req = check_sig_from_req(req)?; - match db::WrapperContract::kick_contract( - &self.db, - &req.operator_wallet, - &req.contract_uuid, - &req.reason, - ) - .await + log::info!("Kicking contract: {}, by: {}", req.contract_uuid, req.operator_wallet); + match db::kick_contract(&self.db, &req.operator_wallet, &req.contract_uuid, &req.reason) + .await { Ok(nano_lp) => Ok(Response::new(KickResp { nano_lp })), Err(e) @@ -144,10 +140,11 @@ impl BrainGeneralCli for GeneralCliServer { | db::Error::FailedToDeleteContract(_) ) => { + log::warn!("Failed to kick contract: {e:?}"); Err(Status::failed_precondition(e.to_string())) } Err(e) => { - log::info!("Failed to kick contract: {e:?}"); + log::error!("Failed to kick contract: {e:?}"); Err(Status::unknown( "Unknown error. Please try again or contact the DeTEE devs team.", )) diff --git a/src/grpc/mod.rs b/src/grpc/mod.rs index 4fbf0d4..c78c346 100644 --- a/src/grpc/mod.rs +++ b/src/grpc/mod.rs @@ -58,7 +58,7 @@ impl_pubkey_getter!(RegisterAppNodeReq); impl_pubkey_getter!(AppNodeFilters); pub fn check_sig_from_req(req: Request) -> Result { - log::trace!("Checking signature from request: {:?}", req); + log::trace!("Checking signature from request: {req:?}"); let time = match req.metadata().get("timestamp") { Some(t) => t.clone(), None => return Err(Status::unauthenticated("Timestamp not found in metadata.")), @@ -73,8 +73,7 @@ pub fn check_sig_from_req(req: Request) -> let seconds_elapsed = now.signed_duration_since(parsed_time).num_seconds(); if !(-4..=4).contains(&seconds_elapsed) { return Err(Status::unauthenticated(format!( - "Date is not within 4 sec of the time of the server: CLI {} vs Server {}", - parsed_time, now + "Date is not within 4 sec of the time of the server: CLI {parsed_time} vs Server {now}", ))); } @@ -131,8 +130,7 @@ pub fn check_sig_from_parts(pubkey: &str, time: &str, msg: &str, sig: &str) -> R let seconds_elapsed = now.signed_duration_since(parsed_time).num_seconds(); if !(-4..=4).contains(&seconds_elapsed) { return Err(Status::unauthenticated(format!( - "Date is not within 4 sec of the time of the server: CLI {} vs Server {}", - parsed_time, now + "Date is not within 4 sec of the time of the server: CLI {parsed_time} vs Server {now}", ))); } diff --git a/src/grpc/types.rs b/src/grpc/types.rs index dfde396..ee7e716 100644 --- a/src/grpc/types.rs +++ b/src/grpc/types.rs @@ -263,15 +263,15 @@ impl From for AppContract { node_pubkey: value.app_node.id.key().to_string(), public_ipv4: value.host_ipv4, resource: Some(AppResource { - memory_mb: value.memory_mb as u32, - disk_mb: value.disk_size_gb as u32, - vcpu: value.vcpus as u32, - ports: value.mapped_ports.iter().map(|(_, g)| *g as u32).collect(), + memory_mb: value.memory_mb, + disk_mb: value.disk_size_gb, + vcpu: value.vcpus, + ports: value.mapped_ports.iter().map(|(_, g)| *g).collect(), }), mapped_ports: value .mapped_ports .iter() - .map(|(h, g)| MappedPort { host_port: *h as u32, guest_port: *g as u32 }) + .map(|(h, g)| MappedPort { host_port: *h, guest_port: *g }) .collect(), created_at: value.created_at.to_rfc3339(), @@ -294,7 +294,7 @@ impl From for db::NewAppReq { .public_package_mr_enclave .unwrap_or_default() .iter() - .fold(String::new(), |acc, x| acc + &format!("{:02x?}", x)); + .fold(String::new(), |acc, x| acc + &format!("{x:02x?}")); Self { id: RecordId::from((NEW_APP_REQ, nanoid!(40, &ID_ALPHABET))), @@ -377,7 +377,7 @@ impl From for NewAppRes { let mapped_ports = val .mapped_ports .iter() - .map(|(h, g)| MappedPort { host_port: *h as u32, guest_port: *g as u32 }) + .map(|(h, g)| MappedPort { host_port: *h, guest_port: *g }) .collect(); Self { uuid: val.id.key().to_string(), diff --git a/surql/functions.sql b/surql/functions.sql index 689635c..062b62d 100644 --- a/surql/functions.sql +++ b/surql/functions.sql @@ -25,3 +25,13 @@ DEFINE FUNCTION OVERWRITE fn::delete_vm( DELETE $vm.id; }; +DEFINE FUNCTION OVERWRITE fn::app_price_per_minute( + $app_id: record +) { + LET $app = (select * from $app_id)[0]; + RETURN + (($app.vcpus * 5) + + ($app.memory_mb / 200) + + ($app.disk_size_gb / 10)) + * $app.price_per_unit; +}; \ No newline at end of file diff --git a/surql/tables.sql b/surql/tables.sql index 35ecc1c..0071da4 100644 --- a/surql/tables.sql +++ b/surql/tables.sql @@ -129,7 +129,7 @@ DEFINE FIELD vcpus ON TABLE deleted_app TYPE int; DEFINE FIELD memory_mb ON TABLE deleted_app TYPE int; 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 deleted_at ON TABLE deleted_app TYPE datetime DEFAULT time::now();; DEFINE FIELD price_per_unit ON TABLE deleted_app TYPE int; DEFINE FIELD mr_enclave ON TABLE deleted_app TYPE string; DEFINE FIELD package_url ON TABLE deleted_app TYPE string; @@ -142,7 +142,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; -DEFINE FIELD node ON TABLE kick TYPE record; +DEFINE FIELD node ON TABLE kick TYPE record; DEFINE TABLE report TYPE RELATION FROM account TO vm_node|app_node; DEFINE FIELD created_at ON TABLE report TYPE datetime; diff --git a/tests/grpc_general_test.rs b/tests/grpc_general_test.rs index 2c601a0..e57c35b 100644 --- a/tests/grpc_general_test.rs +++ b/tests/grpc_general_test.rs @@ -222,23 +222,19 @@ async fn test_kick_contract() { // 7. refund of multiple contract kick in a day for same user env_logger::builder() - .filter_level(log::LevelFilter::Trace) + .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 operator_wallet = "BFopWmwcZAMF1h2PFECZNdEucdZfnZZ32p6R9ZaBiVsS"; - let contract_uuid = "26577f1c98674a1780a86cf0490f1270"; - let reason = "test reason"; + let contract_uuid = "e3d01f252b2a410b80e312f44e474334"; + let operator_wallet = "7V3rEuh6j8VuwMVB5PyGqWKLmjJ4fYSv6WtrTL51NZTB"; + let reason = "'; THROW 'Injected error'; --"; // sql injection query - let kick_response = surreal_brain::db::general::WrapperContract::kick_contract( - &db_conn, - operator_wallet, - contract_uuid, - reason, - ) - .await; + 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); -- 2.43.0