add support for multiple offers per node #9

Merged
valy merged 1 commits from offers into main 2025-07-07 15:10:32 +00:00
5 changed files with 92 additions and 48 deletions
Showing only changes of commit 6d2b5ed377 - Show all commits

2
Cargo.lock generated

@ -1184,7 +1184,7 @@ dependencies = [
[[package]] [[package]]
name = "detee-shared" name = "detee-shared"
version = "0.1.0" 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 = [ dependencies = [
"bincode", "bincode",
"prost", "prost",

@ -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"] } shadow-rs = { version = "1.1.1", features = ["metadata"] }
serde_default_utils = "0.3.1" 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" } # detee-shared = { path = "../detee-shared" }
[build-dependencies] [build-dependencies]

@ -1,5 +1,7 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use std::u64::MAX;
use super::grpc::{self, proto}; use super::grpc::{self, proto};
use super::{injector, Distro, Dtrfs, Error, VmSshArgs, DEFAULT_ARCHLINUX, DEFAULT_DTRFS}; use super::{injector, Distro, Dtrfs, Error, VmSshArgs, DEFAULT_ARCHLINUX, DEFAULT_DTRFS};
use crate::config::Config; use crate::config::Config;
@ -138,16 +140,24 @@ impl Request {
}?; }?;
let mut node_list_iter = node_list.iter(); let mut node_list_iter = node_list.iter();
let mut final_request = self.calculate_vm_request( let mut final_request: proto::NewVmReq =
Config::get_detee_wallet()?, proto::NewVmReq { locked_nano: MAX, ..Default::default() };
node_list_iter.next().ok_or(Error::NoValidNodeFound)?,
);
while let Some(node) = node_list_iter.next() { while let Some(node) = node_list_iter.next() {
let new_vm_req = self.calculate_vm_request(Config::get_detee_wallet()?, node); 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 { if new_vm_req.locked_nano < final_request.locked_nano {
final_request = new_vm_req; final_request = new_vm_req;
} }
} }
}
}
if final_request.node_pubkey.is_empty() {
return Err(Error::NoValidNodeFound);
}
Ok(final_request) Ok(final_request)
} }
@ -155,10 +165,14 @@ impl Request {
fn calculate_vm_request( fn calculate_vm_request(
&self, &self,
admin_pubkey: String, admin_pubkey: String,
node: &proto::VmNodeListResp, node_pubkey: &str,
) -> proto::NewVmReq { offer: &proto::VmNodeOffer,
let memory_per_cpu = node.memory_mib / node.vcpus; ) -> Option<proto::NewVmReq> {
let disk_per_cpu = node.disk_mib / node.vcpus; 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; let mut vcpus = self.vcpus;
if vcpus < (self.memory_gib * 1024).div_ceil(memory_per_cpu as u32) { 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); 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 memory_mib = vcpus * memory_per_cpu as u32;
let disk_size_mib = vcpus * disk_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<u32>, bool) = match &self.ipv4 { let (extra_ports, public_ipv4): (Vec<u32>, bool) = match &self.ipv4 {
IPv4Config::PublishPorts(vec) => (vec.to_vec(), false), IPv4Config::PublishPorts(vec) => (vec.to_vec(), false),
IPv4Config::PublicIPv4 => (Vec::new(), true), IPv4Config::PublicIPv4 => (Vec::new(), true),
@ -190,14 +211,14 @@ impl Request {
disk_size_mib, disk_size_mib,
public_ipv4, public_ipv4,
self.hours, self.hours,
node.price, offer.price,
); );
let brain_req = proto::NewVmReq { let brain_req = proto::NewVmReq {
uuid: String::new(), uuid: String::new(),
hostname: self.hostname.clone(), hostname: self.hostname.clone(),
admin_pubkey, admin_pubkey,
node_pubkey: node.node_pubkey.clone(), node_pubkey: node_pubkey.to_string(),
extra_ports, extra_ports,
public_ipv4, public_ipv4,
public_ipv6: self.public_ipv6, public_ipv6: self.public_ipv6,
@ -208,15 +229,15 @@ impl Request {
kernel_sha, kernel_sha,
dtrfs_url, dtrfs_url,
dtrfs_sha, dtrfs_sha,
price_per_unit: node.price, price_per_unit: offer.price,
locked_nano: nanocredits, locked_nano: nanocredits,
}; };
info!( info!(
"Node {} can offer the VM at {} nanocredits for {} hours. Spec: {} vCPUs, {} MiB mem, {} MiB disk.", "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)
} }
} }

@ -88,7 +88,15 @@ impl crate::HumanOutput for VmNodeListResp {
"This node is located in the city {}, within the region of {}, in {}", "This node is located in the city {}, within the region of {}, in {}",
self.city, self.region, self.country 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
);
}
} }
} }

@ -248,17 +248,27 @@ pub struct TabledVmNode {
impl From<proto::VmNodeListResp> for TabledVmNode { impl From<proto::VmNodeListResp> for TabledVmNode {
fn from(brain_node: proto::VmNodeListResp) -> Self { 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 { Self {
operator: brain_node.operator, operator: brain_node.operator,
location: brain_node.city + ", " + &brain_node.region + ", " + &brain_node.country, location: brain_node.city + ", " + &brain_node.region + ", " + &brain_node.country,
main_ip: brain_node.ip, main_ip: brain_node.ip,
price: format!("{} nano/min", brain_node.price), price: format!("{} nano/min", price),
reports: brain_node.reports.len(), 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_ipv4: brain_node.public_ipv4,
public_ipv6: brain_node.public_ipv6, public_ipv6: brain_node.public_ipv6,
vcpus,
memory_mib,
disk_mib,
} }
} }
} }
@ -426,16 +436,20 @@ pub fn print_node_offers(location: Location) -> Result<Vec<NodeOffer>, Error> {
let node_list = block_on(grpc::get_node_list(req))?; let node_list = block_on(grpc::get_node_list(req))?;
let mut offers: Vec<NodeOffer> = Vec::new(); let mut offers: Vec<NodeOffer> = Vec::new();
for node in node_list.iter() { for node in node_list.iter() {
let mem_per_cpu = node.memory_mib / node.vcpus; for offer in node.offers.iter() {
let disk_per_cpu = node.disk_mib / node.vcpus; if offer.vcpus == 0 || offer.memory_mib == 0 || offer.disk_mib == 0 {
for i in 1..node.vcpus { 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( let price_per_month = calculate_nanocredits(
(node.vcpus * i) as u32, i as u32,
(mem_per_cpu * i) as u32, (mem_per_cpu * i) as u32,
(disk_per_cpu * i) as u32, (disk_per_cpu * i) as u32,
false, false,
732, 732,
node.price, offer.price,
) as f64 ) as f64
/ 1_000_000_000_f64; / 1_000_000_000_f64;
let price_per_hour = price_per_month / 732_f64; let price_per_hour = price_per_month / 732_f64;
@ -453,6 +467,7 @@ pub fn print_node_offers(location: Location) -> Result<Vec<NodeOffer>, Error> {
}); });
} }
} }
}
offers.sort_by_key(|n| n.cost_m as u64); offers.sort_by_key(|n| n.cost_m as u64);
Ok(offers) Ok(offers)
} }