diff --git a/src/data.rs b/src/data.rs index fb79913..b5187b2 100644 --- a/src/data.rs +++ b/src/data.rs @@ -40,13 +40,16 @@ pub enum Error { #[error("Could not find contract {0}")] AppContractNotFound(String), + + #[error("Could not find contract {0}")] + ContractNotFound(String), } #[derive(Clone, Default, Serialize, Deserialize, Debug)] pub struct AccountData { pub balance: u64, pub tmp_locked: u64, - // holds reasons why VMs of this account got kicked + // holds reasons why Contracts of this account got kicked pub kicked_for: Vec, pub last_kick: chrono::DateTime, // holds accounts that banned this account @@ -247,7 +250,7 @@ impl From for AppContractPB { } } -#[derive(Eq, Hash, PartialEq, Clone, Debug, Default, Serialize, Deserialize)] +#[derive(Eq, PartialEq, Clone, Debug, Default, Serialize, Deserialize)] pub struct AppNode { pub node_pubkey: String, pub operator_wallet: String, @@ -262,7 +265,8 @@ pub struct AppNode { pub max_ports_per_app: u32, // nanotokens per unit per minute pub price: u64, - + // 1st String is user wallet and 2nd String is report message + pub reports: HashMap, pub offline_minutes: u64, } @@ -276,7 +280,7 @@ impl From for AppNodeListResp { city: value.city, ip: value.ip, price: value.price, - reports: Vec::new(), + reports: value.reports.into_values().collect(), } } } @@ -506,7 +510,6 @@ impl BrainData { nodes.push(node); } - // todo: this should also support Apps /// Receives: operator, contract uuid, reason of kick pub async fn kick_contract( &self, @@ -515,17 +518,48 @@ impl BrainData { reason: &str, ) -> Result { log::debug!("Operator {operator} requested a kick of {uuid} for reason: {reason}"); - let contract = self.find_contract_by_uuid(uuid)?; + + let (admin_pubkey, node_pubkey, updated_at, price_per_mint, is_vm) = + match self.find_any_contract_by_uuid(uuid) { + Ok((Some(vm), _)) => { + let price_per_mint = vm.price_per_minute(); + ( + vm.admin_pubkey, + vm.node_pubkey, + vm.updated_at, + price_per_mint, + true, + ) + } + Ok((_, Some(app))) => { + let price_per_mint = app.price_per_minute(); + + ( + app.admin_pubkey, + app.node_pubkey, + app.updated_at, + price_per_mint, + false, + ) + } + _ => { + log::error!("Could not find contract {uuid}"); + return Err(Error::ContractNotFound(uuid.to_string())); + } + }; + let mut operator_data = self .operators .get_mut(operator) .ok_or(Error::AccessDenied)?; - if !operator_data.vm_nodes.contains(&contract.node_pubkey) { + if !(operator_data.vm_nodes.contains(&node_pubkey) + || operator_data.app_nodes.contains(&node_pubkey)) + { return Err(Error::AccessDenied); } let mut minutes_to_refund = chrono::Utc::now() - .signed_duration_since(contract.updated_at) + .signed_duration_since(updated_at) .num_minutes() .abs() as u64; // cap refund at 1 week @@ -533,10 +567,10 @@ impl BrainData { minutes_to_refund = 10080; } - let mut refund_amount = minutes_to_refund * contract.price_per_minute(); + let mut refund_amount = minutes_to_refund * price_per_mint; let mut admin_account = self .accounts - .get_mut(&contract.admin_pubkey) + .get_mut(&admin_pubkey) .ok_or(Error::ImpossibleError)?; // check if he got kicked within the last day @@ -560,15 +594,22 @@ impl BrainData { admin_account.kicked_for.push(reason.to_string()); operator_data.escrow -= refund_amount; - let admin_pubkey = contract.admin_pubkey.clone(); + let admin_pubkey = admin_pubkey.clone(); drop(admin_account); - drop(contract); - self.delete_vm(grpc::DeleteVmReq { - uuid: uuid.to_string(), - admin_pubkey, - }) - .await?; + if is_vm { + self.delete_vm(grpc::DeleteVmReq { + uuid: uuid.to_string(), + admin_pubkey, + }) + .await?; + } else { + self.send_del_container_req(DelAppReq { + uuid: uuid.to_string(), + admin_pubkey, + }) + .await?; + } Ok(refund_amount) } @@ -594,10 +635,15 @@ impl BrainData { }); } - pub fn report_node(&self, admin_pubkey: String, node: &str, report: String) { - let mut nodes = self.vm_nodes.write().unwrap(); - if let Some(node) = nodes.iter_mut().find(|n| n.public_key == node) { - node.reports.insert(admin_pubkey, report); + pub fn report_any_node(&self, admin_pubkey: String, node: &str, report: String) { + let mut vm_nodes = self.vm_nodes.write().unwrap(); + if let Some(vm_node) = vm_nodes.iter_mut().find(|n| n.public_key == node) { + vm_node.reports.insert(admin_pubkey.clone(), report.clone()); + } + + let mut app_nodes = self.app_nodes.write().unwrap(); + if let Some(app_node) = app_nodes.iter_mut().find(|n| n.node_pubkey == node) { + app_node.reports.insert(admin_pubkey, report); } } @@ -1239,6 +1285,25 @@ impl BrainData { } } +impl BrainData { + pub fn find_any_contract_by_uuid( + &self, + uuid: &str, + ) -> Result<(Option, Option), Error> { + let contracts = self.vm_contracts.read().unwrap(); + if let Some(vm_contract) = contracts.iter().cloned().find(|c| c.uuid == uuid) { + return Ok((Some(vm_contract), None)); + } + + let app_contracts = self.app_contracts.read().unwrap(); + if let Some(app_contract) = app_contracts.iter().cloned().find(|c| c.uuid == uuid) { + return Ok((None, Some(app_contract))); + } + + Err(Error::ContractNotFound(uuid.to_string())) + } +} + impl BrainData { pub fn add_app_daemon_tx(&self, node_pubkey: &str, tx: Sender) { self.app_daemon_tx.insert(node_pubkey.to_string(), tx); diff --git a/src/grpc.rs b/src/grpc.rs index 9eebffa..f378dae 100644 --- a/src/grpc.rs +++ b/src/grpc.rs @@ -99,17 +99,23 @@ impl BrainGeneralCli for BrainGeneraClilMock { async fn report_node(&self, req: Request) -> Result, Status> { let req = check_sig_from_req(req)?; - match self.data.find_contract_by_uuid(&req.contract) { - Ok(contract) - if contract.admin_pubkey == req.admin_pubkey - && contract.node_pubkey == req.node_pubkey => + match self.data.find_any_contract_by_uuid(&req.contract) { + Ok((Some(vm_contract), _)) + if vm_contract.admin_pubkey == req.admin_pubkey + && vm_contract.node_pubkey == req.node_pubkey => + { + () + } + Ok((_, Some(app_contract))) + if app_contract.admin_pubkey == req.admin_pubkey + && app_contract.node_pubkey == req.node_pubkey => { () } _ => return Err(Status::unauthenticated("No contract found by this ID.")), }; self.data - .report_node(req.admin_pubkey, &req.node_pubkey, req.reason); + .report_any_node(req.admin_pubkey, &req.node_pubkey, req.reason); Ok(Response::new(Empty {})) }