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 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; | ||||||
| DEFINE FIELD price_per_unit ON TABLE deleted_app TYPE int; | 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 mr_enclave ON TABLE deleted_app TYPE string; | ||||||
| DEFINE FIELD package_url 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; | 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 created_at ON TABLE kick TYPE datetime; | ||||||
| DEFINE FIELD reason ON TABLE kick TYPE string; | DEFINE FIELD reason ON TABLE kick TYPE string; | ||||||
| DEFINE FIELD contract ON TABLE kick TYPE record<deleted_vm|deleted_app>; | 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 TABLE report TYPE RELATION FROM account TO vm_node|app_node; | ||||||
| DEFINE FIELD created_at ON TABLE report TYPE datetime; | 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 OLD_BRAIN_DATA_PATH: &str = "./saved_data.yaml"; | ||||||
| 
 | 
 | ||||||
| pub const ACCOUNT: &str = "account"; | pub const ACCOUNT: &str = "account"; | ||||||
|  | pub const KICK: &str = "kick"; | ||||||
|  | 
 | ||||||
| pub const VM_NODE: &str = "vm_node"; | pub const VM_NODE: &str = "vm_node"; | ||||||
| pub const ACTIVE_VM: &str = "active_vm"; | pub const ACTIVE_VM: &str = "active_vm"; | ||||||
| pub const VM_UPDATE_EVENT: &str = "vm_update_event"; | 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', |     'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', | ||||||
|     'V', 'W', 'X', 'Y', 'Z', |     '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 surrealdb::{Notification, RecordId, Surreal}; | ||||||
| use tokio_stream::StreamExt; | use tokio_stream::StreamExt; | ||||||
| 
 | 
 | ||||||
| #[derive(Debug, Serialize, Deserialize)] | #[derive(Debug, Serialize, Deserialize, Clone)] | ||||||
| pub struct AppNode { | pub struct AppNode { | ||||||
|     pub id: RecordId, |     pub id: RecordId, | ||||||
|     pub operator: RecordId, |     pub operator: RecordId, | ||||||
| @ -32,8 +32,9 @@ pub struct AppNode { | |||||||
| impl AppNode { | impl AppNode { | ||||||
|     pub async fn register(self, db: &Surreal<Client>) -> Result<AppNode, Error> { |     pub async fn register(self, db: &Surreal<Client>) -> Result<AppNode, Error> { | ||||||
|         db::Account::get_or_create(db, &self.operator.key().to_string()).await?; |         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?; |         let app_node_id = self.id.clone(); | ||||||
|         app_node.ok_or(Error::FailedToCreateDBEntry) |         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 struct NewAppReq { | ||||||
|     pub id: RecordId, |     pub id: RecordId, | ||||||
|     #[serde(rename = "in")] |     #[serde(rename = "in")] | ||||||
| @ -97,6 +98,7 @@ impl NewAppReq { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub async fn submit(self, db: &Surreal<Client>) -> Result<Vec<Self>, Error> { |     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?; |         let new_app_req: Vec<Self> = db.insert(NEW_APP_REQ).relation(self).await?; | ||||||
|         Ok(new_app_req) |         Ok(new_app_req) | ||||||
|     } |     } | ||||||
| @ -164,7 +166,7 @@ impl AppNodeWithReports { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[derive(Debug, Serialize, Deserialize)] | #[derive(Debug, Serialize, Deserialize, Clone)] | ||||||
| pub struct ActiveApp { | pub struct ActiveApp { | ||||||
|     pub id: RecordId, |     pub id: RecordId, | ||||||
|     #[serde(rename = "in")] |     #[serde(rename = "in")] | ||||||
| @ -210,6 +212,15 @@ impl From<ActiveApp> for DeletedApp { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl ActiveApp { | 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> { |     pub async fn activate(db: &Surreal<Client>, id: &str) -> Result<(), Error> { | ||||||
|         let new_app_req = match NewAppReq::get(db, id).await? { |         let new_app_req = match NewAppReq::get(db, id).await? { | ||||||
|             Some(r) => r, |             Some(r) => r, | ||||||
| @ -293,7 +304,7 @@ impl ActiveApp { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[derive(Debug, Serialize, Deserialize)] | #[derive(Debug, Serialize, Deserialize, Clone)] | ||||||
| pub struct ActiveAppWithNode { | pub struct ActiveAppWithNode { | ||||||
|     pub id: RecordId, |     pub id: RecordId, | ||||||
|     #[serde(rename = "in")] |     #[serde(rename = "in")] | ||||||
| @ -315,6 +326,29 @@ pub struct ActiveAppWithNode { | |||||||
|     pub hratls_pubkey: String, |     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 { | impl ActiveAppWithNode { | ||||||
|     pub async fn get_by_uuid(db: &Surreal<Client>, uuid: &str) -> Result<Option<Self>, Error> { |     pub async fn get_by_uuid(db: &Surreal<Client>, uuid: &str) -> Result<Option<Self>, Error> { | ||||||
|         let contract: Option<Self> = |         let contract: Option<Self> = | ||||||
|  | |||||||
| @ -1,7 +1,6 @@ | |||||||
| use crate::constants::ACCOUNT; |  | ||||||
| use crate::db::prelude::*; |  | ||||||
| 
 |  | ||||||
| use super::Error; | use super::Error; | ||||||
|  | use crate::constants::{ACCOUNT, KICK, MIN_ESCROW, TOKEN_DECIMAL}; | ||||||
|  | use crate::db::prelude::*; | ||||||
| use crate::old_brain; | use crate::old_brain; | ||||||
| use serde::{Deserialize, Serialize}; | use serde::{Deserialize, Serialize}; | ||||||
| use surrealdb::engine::remote::ws::Client; | use surrealdb::engine::remote::ws::Client; | ||||||
| @ -37,7 +36,7 @@ impl Account { | |||||||
|             Some(account) => Ok(account), |             Some(account) => Ok(account), | ||||||
|             None => { |             None => { | ||||||
|                 let account: Option<Self> = db.create(id).await?; |                 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?; |             .await?; | ||||||
|         Ok(()) |         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 { | impl Account { | ||||||
| @ -120,6 +146,24 @@ pub struct Kick { | |||||||
|     created_at: Datetime, |     created_at: Datetime, | ||||||
|     reason: String, |     reason: String, | ||||||
|     contract: RecordId, |     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)] | #[derive(Debug, Serialize, Deserialize, Clone)] | ||||||
| @ -158,7 +202,7 @@ impl Report { | |||||||
| 
 | 
 | ||||||
| /// This is the operator obtained from the DB,
 | /// This is the operator obtained from the DB,
 | ||||||
| /// however the relation is defined using OperatorRelation
 | /// however the relation is defined using OperatorRelation
 | ||||||
| #[derive(Debug, Serialize, Deserialize)] | #[derive(Debug, Serialize, Deserialize, Clone)] | ||||||
| pub struct Operator { | pub struct Operator { | ||||||
|     pub account: RecordId, |     pub account: RecordId, | ||||||
|     pub app_nodes: u64, |     pub app_nodes: u64, | ||||||
| @ -231,3 +275,149 @@ impl Operator { | |||||||
|         Ok((operator, vm_nodes, app_nodes)) |         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 general; | ||||||
| pub mod vm; | 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 crate::old_brain; | ||||||
| use prelude::*; | use prelude::*; | ||||||
| use serde::{Deserialize, Serialize}; | use serde::{Deserialize, Serialize}; | ||||||
| @ -22,14 +24,24 @@ pub enum Error { | |||||||
|     StdIo(#[from] std::io::Error), |     StdIo(#[from] std::io::Error), | ||||||
|     #[error(transparent)] |     #[error(transparent)] | ||||||
|     TimeOut(#[from] tokio::time::error::Elapsed), |     TimeOut(#[from] tokio::time::error::Elapsed), | ||||||
|     #[error("Failed to create account")] |     #[error("Failed to create {0}")] | ||||||
|     FailedToCreateDBEntry, |     FailedToCreateDBEntry(String), | ||||||
|     #[error("Unknown Table: {0}")] |     #[error("Unknown Table: {0}")] | ||||||
|     UnknownTable(String), |     UnknownTable(String), | ||||||
|     #[error("Daemon channel got closed: {0}")] |     #[error("Daemon channel got closed: {0}")] | ||||||
|     AppDaemonConnection(#[from] tokio::sync::mpsc::error::SendError<AppDaemonMsg>), |     AppDaemonConnection(#[from] tokio::sync::mpsc::error::SendError<AppDaemonMsg>), | ||||||
|     #[error("AppDaemon Error {0}")] |     #[error("AppDaemon Error {0}")] | ||||||
|     NewAppDaemonResp(String), |     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 { | pub mod prelude { | ||||||
|  | |||||||
							
								
								
									
										23
									
								
								src/db/vm.rs
									
									
									
									
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										23
									
								
								src/db/vm.rs
									
									
									
									
									
								
							| @ -665,6 +665,29 @@ pub struct ActiveVmWithNode { | |||||||
|     pub collected_at: Datetime, |     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 { | impl ActiveVmWithNode { | ||||||
|     pub async fn get_by_uuid(db: &Surreal<Client>, uuid: &str) -> Result<Option<Self>, Error> { |     pub async fn get_by_uuid(db: &Surreal<Client>, uuid: &str) -> Result<Option<Self>, Error> { | ||||||
|         let contract: Option<Self> = |         let contract: Option<Self> = | ||||||
|  | |||||||
| @ -107,24 +107,52 @@ impl BrainGeneralCli for GeneralCliServer { | |||||||
| 
 | 
 | ||||||
|     async fn register_operator( |     async fn register_operator( | ||||||
|         &self, |         &self, | ||||||
|         _req: Request<RegOperatorReq>, |         req: Request<RegOperatorReq>, | ||||||
|     ) -> Result<Response<Empty>, Status> { |     ) -> Result<Response<Empty>, Status> { | ||||||
|         todo!(); |         let req = check_sig_from_req(req)?; | ||||||
|         // let req = check_sig_from_req(req)?;
 |         log::info!("Regitering new operator: {req:?}"); | ||||||
|         // info!("Regitering new operator: {req:?}");
 |         match db::Account::operator_reg(&self.db, &req.pubkey, &req.email, req.escrow).await { | ||||||
|         // match self.data.register_operator(req) {
 |             Ok(()) => Ok(Response::new(Empty {})), | ||||||
|         //     Ok(()) => Ok(Response::new(Empty {})),
 |             Err(e) if matches!(e, db::Error::InsufficientFunds | db::Error::MinimalEscrow) => { | ||||||
|         //     Err(e) => Err(Status::failed_precondition(e.to_string())),
 |                 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> { |     async fn kick_contract(&self, req: Request<KickReq>) -> Result<Response<KickResp>, Status> { | ||||||
|         todo!(); |         let req = check_sig_from_req(req)?; | ||||||
|         // let req = check_sig_from_req(req)?;
 |         match db::WrapperContract::kick_contract( | ||||||
|         // match self.data.kick_contract(&req.operator_wallet, &req.contract_uuid, &req.reason).await {
 |             &self.db, | ||||||
|         //     Ok(nano_lp) => Ok(Response::new(KickResp { nano_lp })),
 |             &req.operator_wallet, | ||||||
|         //     Err(e) => Err(Status::permission_denied(e.to_string())),
 |             &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> { |     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!(!inspect_response.vm_nodes.is_empty()); | ||||||
|     assert_eq!(&inspect_response.vm_nodes[0].operator, &operator_key.pubkey); |     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