diff --git a/Cargo.lock b/Cargo.lock index 202837c..8b3f776 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1184,7 +1184,7 @@ dependencies = [ [[package]] name = "detee-shared" version = "0.1.0" -source = "git+ssh://git@gitea.detee.cloud/testnet/proto.git?branch=credits_app#01e93d3a2e4502c0e8e72026e8a1c55810961815" +source = "git+ssh://git@gitea.detee.cloud/testnet/proto.git?branch=offers#4753a17fa29393b3f99b6dfcdcec48d935e6ebd9" dependencies = [ "bincode", "prost", diff --git a/Cargo.toml b/Cargo.toml index 17cbd36..cd29d81 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,7 +37,7 @@ detee-sgx = { git = "ssh://git@gitea.detee.cloud/testnet/detee-sgx.git", branch shadow-rs = { version = "1.1.1", features = ["metadata"] } serde_default_utils = "0.3.1" -detee-shared = { git = "ssh://git@gitea.detee.cloud/testnet/proto.git", branch = "credits_app" } +detee-shared = { git = "ssh://git@gitea.detee.cloud/testnet/proto.git", branch = "offers" } # detee-shared = { path = "../detee-shared" } [build-dependencies] diff --git a/src/snp/deploy.rs b/src/snp/deploy.rs index b56e8de..7d4dca6 100644 --- a/src/snp/deploy.rs +++ b/src/snp/deploy.rs @@ -1,5 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 +use std::u64::MAX; + use super::grpc::{self, proto}; use super::{injector, Distro, Dtrfs, Error, VmSshArgs, DEFAULT_ARCHLINUX, DEFAULT_DTRFS}; use crate::config::Config; @@ -138,27 +140,39 @@ impl Request { }?; let mut node_list_iter = node_list.iter(); - let mut final_request = self.calculate_vm_request( - Config::get_detee_wallet()?, - node_list_iter.next().ok_or(Error::NoValidNodeFound)?, - ); + let mut final_request: proto::NewVmReq = + proto::NewVmReq { locked_nano: MAX, ..Default::default() }; + while let Some(node) = node_list_iter.next() { - let new_vm_req = self.calculate_vm_request(Config::get_detee_wallet()?, node); - if new_vm_req.locked_nano < final_request.locked_nano { - final_request = new_vm_req; + for offer in node.offers.iter() { + if let Some(new_vm_req) = + self.calculate_vm_request(Config::get_detee_wallet()?, &node.node_pubkey, offer) + { + if new_vm_req.locked_nano < final_request.locked_nano { + final_request = new_vm_req; + } + } } } + if final_request.node_pubkey.is_empty() { + return Err(Error::NoValidNodeFound); + } + Ok(final_request) } fn calculate_vm_request( &self, admin_pubkey: String, - node: &proto::VmNodeListResp, - ) -> proto::NewVmReq { - let memory_per_cpu = node.memory_mib / node.vcpus; - let disk_per_cpu = node.disk_mib / node.vcpus; + node_pubkey: &str, + offer: &proto::VmNodeOffer, + ) -> Option { + if offer.vcpus == 0 { + return None; + } + let memory_per_cpu = offer.memory_mib / offer.vcpus; + let disk_per_cpu = offer.disk_mib / offer.vcpus; let mut vcpus = self.vcpus; if vcpus < (self.memory_gib * 1024).div_ceil(memory_per_cpu as u32) { vcpus = (self.memory_gib * 1024).div_ceil(memory_per_cpu as u32); @@ -170,6 +184,13 @@ impl Request { let memory_mib = vcpus * memory_per_cpu as u32; let disk_size_mib = vcpus * disk_per_cpu as u32; + if memory_mib > offer.memory_mib as u32 + || disk_size_mib > offer.disk_mib as u32 + || vcpus > offer.vcpus as u32 + { + return None; + } + let (extra_ports, public_ipv4): (Vec, bool) = match &self.ipv4 { IPv4Config::PublishPorts(vec) => (vec.to_vec(), false), IPv4Config::PublicIPv4 => (Vec::new(), true), @@ -190,14 +211,14 @@ impl Request { disk_size_mib, public_ipv4, self.hours, - node.price, + offer.price, ); let brain_req = proto::NewVmReq { uuid: String::new(), hostname: self.hostname.clone(), admin_pubkey, - node_pubkey: node.node_pubkey.clone(), + node_pubkey: node_pubkey.to_string(), extra_ports, public_ipv4, public_ipv6: self.public_ipv6, @@ -208,15 +229,15 @@ impl Request { kernel_sha, dtrfs_url, dtrfs_sha, - price_per_unit: node.price, + price_per_unit: offer.price, locked_nano: nanocredits, }; info!( "Node {} can offer the VM at {} nanocredits for {} hours. Spec: {} vCPUs, {} MiB mem, {} MiB disk.", - node.ip, brain_req.locked_nano, self.hours, brain_req.vcpus, brain_req.memory_mib, brain_req.disk_size_mib + node_pubkey, brain_req.locked_nano, self.hours, brain_req.vcpus, brain_req.memory_mib, brain_req.disk_size_mib ); - brain_req + Some(brain_req) } } diff --git a/src/snp/grpc.rs b/src/snp/grpc.rs index 34e4849..9224cf6 100644 --- a/src/snp/grpc.rs +++ b/src/snp/grpc.rs @@ -88,7 +88,15 @@ impl crate::HumanOutput for VmNodeListResp { "This node is located in the city {}, within the region of {}, in {}", self.city, self.region, self.country ); - println!("The price multiplier for the node is {}.", self.price); + if self.offers.len() > 0 { + println!("The node is offering the following offers:"); + } + for offer in self.offers.iter() { + println!( + " - {} vcpus, {} MiB of memory, {} MiB of storage at a price per unit of {}", + offer.vcpus, offer.memory_mib, offer.disk_mib, offer.price + ); + } } } diff --git a/src/snp/mod.rs b/src/snp/mod.rs index b40ff3f..85e77ae 100644 --- a/src/snp/mod.rs +++ b/src/snp/mod.rs @@ -248,17 +248,27 @@ pub struct TabledVmNode { impl From for TabledVmNode { fn from(brain_node: proto::VmNodeListResp) -> Self { + let (mut vcpus, mut memory_mib, mut disk_mib): (u64, u64, u64) = (0, 0, 0); + let mut price = 0; + for offer in brain_node.offers.iter() { + vcpus = vcpus.saturating_add(offer.vcpus); + memory_mib = memory_mib.saturating_add(offer.memory_mib); + disk_mib = disk_mib.saturating_add(offer.disk_mib); + if offer.price > price { + price = offer.price; + } + } Self { operator: brain_node.operator, location: brain_node.city + ", " + &brain_node.region + ", " + &brain_node.country, main_ip: brain_node.ip, - price: format!("{} nano/min", brain_node.price), + price: format!("{} nano/min", price), reports: brain_node.reports.len(), - vcpus: brain_node.vcpus, - memory_mib: brain_node.memory_mib, - disk_mib: brain_node.disk_mib, public_ipv4: brain_node.public_ipv4, public_ipv6: brain_node.public_ipv6, + vcpus, + memory_mib, + disk_mib, } } } @@ -426,31 +436,36 @@ pub fn print_node_offers(location: Location) -> Result, Error> { let node_list = block_on(grpc::get_node_list(req))?; let mut offers: Vec = Vec::new(); for node in node_list.iter() { - let mem_per_cpu = node.memory_mib / node.vcpus; - let disk_per_cpu = node.disk_mib / node.vcpus; - for i in 1..node.vcpus { - let price_per_month = calculate_nanocredits( - (node.vcpus * i) as u32, - (mem_per_cpu * i) as u32, - (disk_per_cpu * i) as u32, - false, - 732, - node.price, - ) as f64 - / 1_000_000_000_f64; - let price_per_hour = price_per_month / 732_f64; - let price_per_month = (price_per_month * 100.0).round() / 100.0; - let price_per_hour = (price_per_hour * 1000.0).round() / 1000.0; - offers.push(NodeOffer { - location: node.city.clone() + ", " + &node.region + ", " + &node.country, - vcpus: i, - mem: i * mem_per_cpu, - disk: i * disk_per_cpu, - cost_h: price_per_hour, - cost_m: price_per_month, - ipv4: node.public_ipv4, - // ipv6: node.public_ipv6, - }); + for offer in node.offers.iter() { + if offer.vcpus == 0 || offer.memory_mib == 0 || offer.disk_mib == 0 { + continue; + } + let mem_per_cpu = offer.memory_mib / offer.vcpus; + let disk_per_cpu = offer.disk_mib / offer.vcpus; + for i in 1..offer.vcpus + 1 { + let price_per_month = calculate_nanocredits( + i as u32, + (mem_per_cpu * i) as u32, + (disk_per_cpu * i) as u32, + false, + 732, + offer.price, + ) as f64 + / 1_000_000_000_f64; + let price_per_hour = price_per_month / 732_f64; + let price_per_month = (price_per_month * 100.0).round() / 100.0; + let price_per_hour = (price_per_hour * 1000.0).round() / 1000.0; + offers.push(NodeOffer { + location: node.city.clone() + ", " + &node.region + ", " + &node.country, + vcpus: i, + mem: i * mem_per_cpu, + disk: i * disk_per_cpu, + cost_h: price_per_hour, + cost_m: price_per_month, + ipv4: node.public_ipv4, + // ipv6: node.public_ipv6, + }); + } } } offers.sort_by_key(|n| n.cost_m as u64);