From 41d9bd104f48620cc9724d89709a00e026ef4cd3 Mon Sep 17 00:00:00 2001 From: Noor Date: Wed, 11 Jun 2025 19:12:14 +0530 Subject: [PATCH 1/7] WIP on implementing redirect Refactors brain connection logic to use custom endpoint while runtime Implements redirect handling in the `create_vm` function to gracefully handle server moves by reconnecting to the new URL --- src/config.rs | 10 ++++++---- src/constants.rs | 1 + src/general/grpc.rs | 3 ++- src/sgx/grpc_brain.rs | 3 ++- src/snp/grpc.rs | 36 ++++++++++++++++++++++++++++++++---- 5 files changed, 43 insertions(+), 10 deletions(-) diff --git a/src/config.rs b/src/config.rs index 26c848a..283ece6 100644 --- a/src/config.rs +++ b/src/config.rs @@ -309,20 +309,22 @@ impl Config { pub fn get_brain_info() -> (String, String) { match Self::init_config().network.as_str() { - "staging" => ("https://184.107.169.199:49092".to_string(), "staging-brain".to_string()), + "staging" => ("https://10.254.254.8:31337".to_string(), "staging-brain".to_string()), "localhost" => ("https://localhost:31337".to_string(), "staging-brain".to_string()), _ => ("https://173.234.17.2:39477".to_string(), "testnet-brain".to_string()), } } - pub async fn get_brain_channel() -> Result { - let (brain_url, brain_san) = Self::get_brain_info(); - + pub async fn connect_brain_channel( + brain_url: String, + ) -> Result { use hyper_rustls::HttpsConnectorBuilder; use rustls::pki_types::pem::PemObject; use rustls::pki_types::CertificateDer; use rustls::{ClientConfig, RootCertStore}; + let brain_san = Config::get_brain_info().1; + let mut detee_root_ca_store = RootCertStore::empty(); detee_root_ca_store .add(CertificateDer::from_pem_file(Config::get_root_ca_path()?).map_err(|e| { diff --git a/src/constants.rs b/src/constants.rs index e39d4fd..dd466f0 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -1 +1,2 @@ pub const HRATLS_APP_PORT: u32 = 34500; +pub const MAX_REDIRECTS: u16 = 3; diff --git a/src/general/grpc.rs b/src/general/grpc.rs index 18b39fb..014fa50 100644 --- a/src/general/grpc.rs +++ b/src/general/grpc.rs @@ -35,7 +35,8 @@ pub enum Error { } async fn client() -> Result, Error> { - Ok(BrainGeneralCliClient::new(Config::get_brain_channel().await?)) + let default_brain_url = Config::get_brain_info().0; + Ok(BrainGeneralCliClient::new(Config::connect_brain_channel(default_brain_url).await?)) } pub async fn get_balance(account: &str) -> Result { diff --git a/src/sgx/grpc_brain.rs b/src/sgx/grpc_brain.rs index c2b4678..f5e07f8 100644 --- a/src/sgx/grpc_brain.rs +++ b/src/sgx/grpc_brain.rs @@ -66,7 +66,8 @@ impl crate::HumanOutput for AppContract { } async fn client() -> Result> { - Ok(BrainAppCliClient::new(Config::get_brain_channel().await?)) + let default_brain_url = Config::get_brain_info().0; + Ok(BrainAppCliClient::new(Config::connect_brain_channel(default_brain_url).await?)) } pub async fn new_app(app_deploy_config: AppDeployConfig) -> Result { diff --git a/src/snp/grpc.rs b/src/snp/grpc.rs index a2fc231..27b9ee4 100644 --- a/src/snp/grpc.rs +++ b/src/snp/grpc.rs @@ -4,6 +4,7 @@ pub mod proto { } use crate::config::Config; +use crate::constants::MAX_REDIRECTS; use lazy_static::lazy_static; use log::{debug, info, warn}; use proto::{ @@ -41,6 +42,8 @@ pub enum Error { CorruptedRootCa(#[from] std::io::Error), #[error("Internal app error: could not parse Brain URL")] CorruptedBrainUrl, + #[error("Max redirects exceeded: {0}")] + MaxRedirectsExceeded(String), } impl crate::HumanOutput for VmContract { @@ -84,7 +87,14 @@ impl crate::HumanOutput for VmNodeListResp { } async fn client() -> Result, Error> { - Ok(BrainVmCliClient::new(Config::get_brain_channel().await?)) + let default_brain_url = Config::get_brain_info().0; + Ok(BrainVmCliClient::new(Config::connect_brain_channel(default_brain_url).await?)) +} + +async fn client_from_endpoint( + reconnect_endpoint: String, +) -> Result, Error> { + Ok(BrainVmCliClient::new(Config::connect_brain_channel(reconnect_endpoint).await?)) } fn sign_request(req: T) -> Result, Error> { @@ -130,10 +140,28 @@ pub async fn get_one_node(req: VmNodeFilters) -> Result { pub async fn create_vm(req: NewVmReq) -> Result { let mut client = client().await?; debug!("Sending NewVmReq to brain: {req:?}"); - match client.new_vm(sign_request(req)?).await { - Ok(resp) => Ok(resp.into_inner()), - Err(e) => Err(e.into()), + for attempt in 0..MAX_REDIRECTS { + match client.new_vm(sign_request(req.clone())?).await { + Ok(resp) => return Ok(resp.into_inner()), + Err(status) + if status.code() == tonic::Code::Unavailable + && status.message() == "moved" + && status.metadata().contains_key("location") => + { + let redirect_url = status + .metadata() + .get("location") + .and_then(|v| v.to_str().ok().map(|s| format!("https://{s}"))) + .unwrap_or_default(); + // TODO: change this println to log::info!() + println!("{attempt}) server moved to a different URL, trying to reconnect... ({redirect_url})"); + client = client_from_endpoint(redirect_url).await?; + continue; + } + Err(e) => return Err(e.into()), + }; } + Err(Error::MaxRedirectsExceeded(MAX_REDIRECTS.to_string())) } pub async fn list_contracts(req: ListVmContractsReq) -> Result, Error> { -- 2.43.0 From a25c53d70934d81cd857b35e0bb7afbf1b0e02a2 Mon Sep 17 00:00:00 2001 From: Noor Date: Fri, 13 Jun 2025 17:08:52 +0530 Subject: [PATCH 2/7] Implement macro for grpc redirect redirect handling in gRPC client and add macro for follow redirects random brain url selection for staging environment. cleanup duplicate sign_request function --- src/config.rs | 8 +++++++- src/snp/grpc.rs | 50 +++++++++++-------------------------------------- src/utils.rs | 46 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 64 insertions(+), 40 deletions(-) diff --git a/src/config.rs b/src/config.rs index 283ece6..0495f5f 100644 --- a/src/config.rs +++ b/src/config.rs @@ -309,7 +309,13 @@ impl Config { pub fn get_brain_info() -> (String, String) { match Self::init_config().network.as_str() { - "staging" => ("https://10.254.254.8:31337".to_string(), "staging-brain".to_string()), + "staging" => { + let url1 = "https://149.22.95.1:47855".to_string(); // staging brain 2 + let url2 = "https://149.36.48.99:48843".to_string(); // staging brain 3 + + let url = if rand::random::() { url1 } else { url2 }; + (url, "staging-brain".to_string()) + } "localhost" => ("https://localhost:31337".to_string(), "staging-brain".to_string()), _ => ("https://173.234.17.2:39477".to_string(), "testnet-brain".to_string()), } diff --git a/src/snp/grpc.rs b/src/snp/grpc.rs index 27b9ee4..9be8310 100644 --- a/src/snp/grpc.rs +++ b/src/snp/grpc.rs @@ -3,8 +3,9 @@ pub mod proto { pub use detee_shared::vm_proto::*; } +use crate::call_with_follow_redirect; use crate::config::Config; -use crate::constants::MAX_REDIRECTS; +use crate::utils::{self, sign_request}; use lazy_static::lazy_static; use log::{debug, info, warn}; use proto::{ @@ -13,9 +14,7 @@ use proto::{ }; use tokio_stream::StreamExt; use tonic::metadata::errors::InvalidMetadataValue; -use tonic::metadata::AsciiMetadataValue; use tonic::transport::Channel; -use tonic::Request; lazy_static! { static ref SECURE_PUBLIC_KEY: String = use_default_string(); @@ -44,6 +43,10 @@ pub enum Error { CorruptedBrainUrl, #[error("Max redirects exceeded: {0}")] MaxRedirectsExceeded(String), + #[error("Redirect error: {0}")] + RedirectError(String), + #[error(transparent)] + InternalError(#[from] utils::Error), } impl crate::HumanOutput for VmContract { @@ -97,20 +100,6 @@ async fn client_from_endpoint( Ok(BrainVmCliClient::new(Config::connect_brain_channel(reconnect_endpoint).await?)) } -fn sign_request(req: T) -> Result, Error> { - let pubkey = Config::get_detee_wallet()?; - let timestamp = chrono::Utc::now().to_rfc3339(); - let signature = Config::try_sign_message(&format!("{timestamp}{req:?}"))?; - let timestamp: AsciiMetadataValue = timestamp.parse()?; - let pubkey: AsciiMetadataValue = pubkey.parse()?; - let signature: AsciiMetadataValue = signature.parse()?; - let mut req = Request::new(req); - req.metadata_mut().insert("timestamp", timestamp); - req.metadata_mut().insert("pubkey", pubkey); - req.metadata_mut().insert("request-signature", signature); - Ok(req) -} - pub async fn get_node_list(req: VmNodeFilters) -> Result, Error> { debug!("Getting nodes from brain..."); let mut client = client().await?; @@ -138,30 +127,13 @@ pub async fn get_one_node(req: VmNodeFilters) -> Result { } pub async fn create_vm(req: NewVmReq) -> Result { - let mut client = client().await?; debug!("Sending NewVmReq to brain: {req:?}"); - for attempt in 0..MAX_REDIRECTS { - match client.new_vm(sign_request(req.clone())?).await { - Ok(resp) => return Ok(resp.into_inner()), - Err(status) - if status.code() == tonic::Code::Unavailable - && status.message() == "moved" - && status.metadata().contains_key("location") => - { - let redirect_url = status - .metadata() - .get("location") - .and_then(|v| v.to_str().ok().map(|s| format!("https://{s}"))) - .unwrap_or_default(); - // TODO: change this println to log::info!() - println!("{attempt}) server moved to a different URL, trying to reconnect... ({redirect_url})"); - client = client_from_endpoint(redirect_url).await?; - continue; - } - Err(e) => return Err(e.into()), - }; + + let client = client().await?; + match call_with_follow_redirect!(client, req, new_vm).await { + Ok(resp) => Ok(resp.into_inner()), + Err(e) => Err(e.into()), } - Err(Error::MaxRedirectsExceeded(MAX_REDIRECTS.to_string())) } pub async fn list_contracts(req: ListVmContractsReq) -> Result, Error> { diff --git a/src/utils.rs b/src/utils.rs index 022da20..5d58dcc 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -42,3 +42,49 @@ pub fn shorten_string(my_string: &String) -> String { format!("{}", first_part) } } + +#[macro_export] +macro_rules! call_with_follow_redirect { + ( + $client:expr, + $req_data:expr, + $method:ident + ) => { + async { + let mut client = $client; + + for attempt in 0..crate::constants::MAX_REDIRECTS { + debug!("Attempt #{}: Calling method '{}'...", attempt + 1, stringify!($method)); + + let req_data_clone = $req_data.clone(); + let signed_req = crate::utils::sign_request(req_data_clone)?; + + match client.$method(signed_req).await { + Ok(resp) => return Ok(resp), + + Err(status) + if status.code() == tonic::Code::Unavailable + && status.message() == "moved" => + { + let redirect_url = status + .metadata() + .get("location") + .and_then(|v| v.to_str().ok()) + .ok_or_else(|| { + Error::RedirectError( + "Server indicated a move but provided no location".into(), + ) + })?; + + info!("Server moved. Redirecting to {}...", redirect_url); + + client = client_from_endpoint(format!("https://{}", redirect_url)).await?; + continue; + } + Err(e) => return Err(Error::ResponseStatus(e)), + } + } + Err(Error::MaxRedirectsExceeded(crate::constants::MAX_REDIRECTS.to_string())) + } + }; +} -- 2.43.0 From ddc5d248578e74cc2e2074e76461287150d2737c Mon Sep 17 00:00:00 2001 From: Noor Date: Fri, 13 Jun 2025 17:21:52 +0530 Subject: [PATCH 3/7] Implement follow redirect on delete and update vm --- src/snp/grpc.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/snp/grpc.rs b/src/snp/grpc.rs index 9be8310..10fd953 100644 --- a/src/snp/grpc.rs +++ b/src/snp/grpc.rs @@ -157,10 +157,9 @@ pub async fn list_contracts(req: ListVmContractsReq) -> Result, } pub async fn delete_vm(uuid: &str) -> Result<(), Error> { - let mut client = client().await?; + let client = client().await?; let req = DeleteVmReq { uuid: uuid.to_string(), admin_pubkey: Config::get_detee_wallet()? }; - let result = client.delete_vm(sign_request(req)?).await; - match result { + match call_with_follow_redirect!(client, req, delete_vm).await { Ok(confirmation) => { log::debug!("VM deletion confirmation: {confirmation:?}"); } @@ -195,9 +194,8 @@ pub async fn extend_vm(uuid: String, admin_pubkey: String, locked_nano: u64) -> pub async fn update_vm(req: UpdateVmReq) -> Result { info!("Updating VM {req:?}"); - let mut client = client().await?; - let result = client.update_vm(sign_request(req)?).await; - match result { + let client = client().await?; + match call_with_follow_redirect!(client, req, update_vm).await { Ok(resp) => { let resp = resp.into_inner(); if resp.error.is_empty() { -- 2.43.0 From 420653bcb65ac06dd36381a9d3d1bf961e8cb0cc Mon Sep 17 00:00:00 2001 From: Noor Date: Mon, 16 Jun 2025 13:53:00 +0530 Subject: [PATCH 4/7] Improves brain URL selection for different networks list of 3 brain urls for staging and testnet refactor brain URL selection for staging and testnet environments. --- src/config.rs | 18 ++++++++++++------ src/constants.rs | 11 +++++++++++ src/snp/grpc.rs | 1 + 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/config.rs b/src/config.rs index 0495f5f..eedd637 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,3 +1,4 @@ +use crate::constants::{STAGING_BRAIN_URLS, TESTNET_BRAIN_URLS}; use crate::{general, utils::block_on}; use ed25519_dalek::SigningKey; use log::{debug, info, warn}; @@ -5,6 +6,7 @@ use openssl::bn::BigNum; use openssl::hash::{Hasher, MessageDigest}; use openssl::pkey::{PKey, Private}; use openssl::rsa::Rsa; +use rand::Rng; use serde::{Deserialize, Serialize}; use std::{fs::File, io::Write, path::Path}; @@ -309,15 +311,19 @@ impl Config { pub fn get_brain_info() -> (String, String) { match Self::init_config().network.as_str() { + "localhost" => ("https://localhost:31337".to_string(), "staging-brain".to_string()), "staging" => { - let url1 = "https://149.22.95.1:47855".to_string(); // staging brain 2 - let url2 = "https://149.36.48.99:48843".to_string(); // staging brain 3 - - let url = if rand::random::() { url1 } else { url2 }; + let url = STAGING_BRAIN_URLS + [rand::thread_rng().gen_range(0..STAGING_BRAIN_URLS.len())] + .to_string(); (url, "staging-brain".to_string()) } - "localhost" => ("https://localhost:31337".to_string(), "staging-brain".to_string()), - _ => ("https://173.234.17.2:39477".to_string(), "testnet-brain".to_string()), + _ => { + let url = TESTNET_BRAIN_URLS + [rand::thread_rng().gen_range(0..TESTNET_BRAIN_URLS.len())] + .to_string(); + (url, "testnet-brain".to_string()) + } } } diff --git a/src/constants.rs b/src/constants.rs index dd466f0..ff3da5d 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -1,2 +1,13 @@ pub const HRATLS_APP_PORT: u32 = 34500; pub const MAX_REDIRECTS: u16 = 3; +pub const STAGING_BRAIN_URLS: [&str; 3] = [ + "https://184.107.169.199:49092", // staging brain 1 + "https://149.22.95.1:47855", // staging brain 2 + "https://149.36.48.99:48843", // staging brain 3 +]; + +pub const TESTNET_BRAIN_URLS: [&str; 3] = [ + "https://184.107.169.199:45223", // testnet brain 1 + "https://149.22.95.1:44522", // testnet brain 2 + "https://149.36.48.99:48638", // testnet brain 3 +]; diff --git a/src/snp/grpc.rs b/src/snp/grpc.rs index 10fd953..34b1cba 100644 --- a/src/snp/grpc.rs +++ b/src/snp/grpc.rs @@ -91,6 +91,7 @@ impl crate::HumanOutput for VmNodeListResp { async fn client() -> Result, Error> { let default_brain_url = Config::get_brain_info().0; + info!("brain_url: {default_brain_url}"); Ok(BrainVmCliClient::new(Config::connect_brain_channel(default_brain_url).await?)) } -- 2.43.0 From 25eeab6098487b8c22bce1059fdc28e50eed24b9 Mon Sep 17 00:00:00 2001 From: Noor Date: Mon, 16 Jun 2025 15:47:53 +0530 Subject: [PATCH 5/7] Refactor app resource disk size to GB updated proto and changed accordingly --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/bin/detee-cli.rs | 6 +++--- src/sgx/cli_handler.rs | 6 +++--- src/sgx/grpc_brain.rs | 13 +++++++------ src/sgx/mod.rs | 23 +++++++++++------------ src/sgx/utils.rs | 6 +++--- 7 files changed, 29 insertions(+), 29 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5fdbe40..a005cd3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1182,7 +1182,7 @@ dependencies = [ [[package]] name = "detee-shared" version = "0.1.0" -source = "git+ssh://git@gitea.detee.cloud/testnet/proto.git?branch=surreal_brain#fb38352e1b47837b14f32d8df5ae7f6b17202aae" +source = "git+ssh://git@gitea.detee.cloud/testnet/proto.git?branch=surreal_brain_app#0b195b4589e4ec689af7ddca27dc051716ecee78" dependencies = [ "bincode", "prost", diff --git a/Cargo.toml b/Cargo.toml index 33dde0d..c1e2546 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,7 +34,7 @@ tokio-retry = "0.3.0" detee-sgx = { git = "ssh://git@gitea.detee.cloud/testnet/detee-sgx.git", branch = "hratls", features=["hratls", "qvl"] } shadow-rs = { version = "1.1.1", features = ["metadata"] } -detee-shared = { git = "ssh://git@gitea.detee.cloud/testnet/proto.git", branch = "surreal_brain" } +detee-shared = { git = "ssh://git@gitea.detee.cloud/testnet/proto.git", branch = "surreal_brain_app" } # detee-shared = { path = "../detee-shared" } [build-dependencies] diff --git a/src/bin/detee-cli.rs b/src/bin/detee-cli.rs index a5e53b3..d331a23 100644 --- a/src/bin/detee-cli.rs +++ b/src/bin/detee-cli.rs @@ -127,9 +127,9 @@ fn clap_cmd() -> Command { .arg( Arg::new("disk") .long("disk") - .default_value("1000") - .value_parser(clap::value_parser!(u32).range(300..8000)) - .help("disk size in MB") + .default_value("2") + .value_parser(clap::value_parser!(u32).range(1..100)) + .help("disk size in GB") ) .arg( Arg::new("port") diff --git a/src/sgx/cli_handler.rs b/src/sgx/cli_handler.rs index 1c21a61..05e0308 100644 --- a/src/sgx/cli_handler.rs +++ b/src/sgx/cli_handler.rs @@ -80,9 +80,9 @@ fn handle_deploy( // TODO: maybe add launch config on deploy command with --launch-config flag (AppDeployConfig::from_path(file_path).unwrap(), None) } else { - let vcpu = *deploy_match.get_one::("vcpus").unwrap(); + let vcpus = *deploy_match.get_one::("vcpus").unwrap(); let memory_mb = *deploy_match.get_one::("memory").unwrap(); - let disk_mb = *deploy_match.get_one::("disk").unwrap(); + let disk_size_gb = *deploy_match.get_one::("disk").unwrap(); let port = deploy_match.get_many::("port").unwrap_or_default().cloned().collect::>(); let package_name = deploy_match.get_one::("package").unwrap().clone(); @@ -98,7 +98,7 @@ fn handle_deploy( let private_package = false; - let resource = Resource { vcpu, memory_mb, disk_mb, port }; + let resource = Resource { vcpus, memory_mb, disk_size_gb, port }; let node_pubkey = match block_on(get_app_node(resource.clone(), location.into())) { Ok(node) => node.node_pubkey, Err(e) => { diff --git a/src/sgx/grpc_brain.rs b/src/sgx/grpc_brain.rs index f5e07f8..9881ab2 100644 --- a/src/sgx/grpc_brain.rs +++ b/src/sgx/grpc_brain.rs @@ -48,7 +48,7 @@ impl crate::HumanOutput for AppContract { .mapped_ports .clone() .iter() - .map(|p| format!("({},{})", p.host_port, p.app_port)) + .map(|p| format!("({},{})", p.host_port, p.guest_port)) .collect::>() .join(", "); println!( @@ -57,8 +57,8 @@ impl crate::HumanOutput for AppContract { ); println!("The app has mapped ports by the node are: {mapped_ports}"); println!( - "The App has {} vCPUS, {}MB of memory and a disk of {} MB.", - app_resource.vcpu, app_resource.memory_mb, app_resource.disk_mb + "The App has {} vCPUS, {}MB of memory and a disk of {} GB.", + app_resource.vcpus, app_resource.memory_mb, app_resource.disk_size_gb ); println!("You have locked {} nanoLP in the contract, that get collected at a rate of {} nanoLP per minute.", self.locked_nano, self.nano_per_minute); @@ -75,9 +75,9 @@ pub async fn new_app(app_deploy_config: AppDeployConfig) -> Result { let mut req: NewAppReq = app_deploy_config.clone().into(); let locked_nano = calculate_nanolp_for_app( - resource.vcpu, + resource.vcpus, resource.memory_mb, - resource.disk_mb, + resource.disk_size_gb, app_deploy_config.hours, req.price_per_unit, ); @@ -87,7 +87,8 @@ pub async fn new_app(app_deploy_config: AppDeployConfig) -> Result { req.admin_pubkey = Config::get_detee_wallet()?; req.hratls_pubkey = Config::get_hratls_pubkey_hex()?; - let res = client().await?.deploy_app(sign_request(req)?).await?; + let res = client().await?.new_app(sign_request(req)?).await?; + Ok(res.into_inner()) } diff --git a/src/sgx/mod.rs b/src/sgx/mod.rs index 149211f..a313a8c 100644 --- a/src/sgx/mod.rs +++ b/src/sgx/mod.rs @@ -41,11 +41,11 @@ pub struct AppContract { pub uuid: String, pub name: String, #[tabled(rename = "Cores")] - pub vcpu: u32, + pub vcpus: u32, #[tabled(rename = "Mem (MB)")] pub memory_mb: u32, - #[tabled(rename = "Disk (MB)")] - pub disk_mb: u32, + #[tabled(rename = "Disk (GB)")] + pub disk_size_gb: u32, #[tabled(rename = "LP/h")] pub cost_h: String, #[tabled(rename = "time left", display_with = "display_mins")] @@ -137,22 +137,22 @@ impl From for AppContract { } }; - let AppResource { vcpu, memory_mb, disk_mb, .. } = + let AppResource { vcpus, memory_mb, disk_size_gb, .. } = brain_app_contract.resource.unwrap_or_default(); let exposed_host_ports = brain_app_contract .mapped_ports .iter() - .map(|port| (port.host_port, port.app_port)) + .map(|port| (port.host_port, port.guest_port)) .collect::>(); Self { location, uuid: brain_app_contract.uuid, name: brain_app_contract.app_name, - vcpu, + vcpus, memory_mb, - disk_mb, + disk_size_gb, cost_h: format!( "{:.4}", (brain_app_contract.nano_per_minute * 60) as f64 / 1_000_000_000.0 @@ -181,7 +181,6 @@ pub async fn get_one_contract(uuid: &str) -> Result { #[derive(Debug, Serialize, Deserialize)] pub struct AppDeployResponse { - pub status: String, pub uuid: String, pub name: String, pub node_ip: String, @@ -198,14 +197,13 @@ impl crate::HumanOutput for AppDeployResponse { impl From<(NewAppRes, String)> for AppDeployResponse { fn from((value, name): (NewAppRes, String)) -> Self { Self { - status: value.status, uuid: value.uuid, name, node_ip: value.ip_address, hratls_port: value .mapped_ports .iter() - .find(|port| port.app_port == HRATLS_APP_PORT) + .find(|port| port.guest_port == HRATLS_APP_PORT) .map(|port| port.host_port) .unwrap_or(HRATLS_APP_PORT), error: value.error, @@ -230,14 +228,15 @@ pub async fn get_app_node( location: snp::deploy::Location, ) -> Result { let app_node_filter = AppNodeFilters { - vcpus: resource.vcpu, + vcpus: resource.vcpus, memory_mb: resource.memory_mb, - storage_mb: resource.disk_mb, + storage_gb: resource.disk_size_gb, country: location.country.clone().unwrap_or_default(), region: location.region.clone().unwrap_or_default(), city: location.city.clone().unwrap_or_default(), ip: location.node_ip.clone().unwrap_or_default(), node_pubkey: String::new(), + free_ports: (resource.port.len() + 1) as u32, }; get_one_app_node(app_node_filter).await } diff --git a/src/sgx/utils.rs b/src/sgx/utils.rs index 7d87e9d..9bddc11 100644 --- a/src/sgx/utils.rs +++ b/src/sgx/utils.rs @@ -48,7 +48,7 @@ pub async fn hratls_url_and_mr_enclave_from_app_id( let dtpm_port = app_contract .mapped_ports .iter() - .find(|port| port.app_port == HRATLS_APP_PORT) + .find(|port| port.guest_port == HRATLS_APP_PORT) .ok_or(crate::sgx::grpc_dtpm::Error::Dtpm("Could not find DTMP port".to_string()))? .host_port; @@ -71,13 +71,13 @@ pub async fn fetch_config(package_name: &str) -> Result { pub fn calculate_nanolp_for_app( vcpus: u32, memory_mb: u32, - disk_size_mb: u32, + disk_size_gb: u32, hours: u64, node_price: u64, ) -> u64 { // this calculation needs to match the calculation of the network let total_units = - (vcpus as f64 * 5f64) + (memory_mb as f64 / 200f64) + (disk_size_mb as f64 / 10000f64); + (vcpus as f64 * 5f64) + (memory_mb as f64 / 200f64) + (disk_size_gb as f64 / 10f64); let locked_nano = (hours as f64 * 60f64 * total_units * node_price as f64) as u64; eprintln!( "Node price: {}/unit/minute. Total Units for hardware requested: {:.4}. Locking {} LP (offering the App for {} hours).", -- 2.43.0 From 9630cd5f951e1665c37f2fd75b77122913180c53 Mon Sep 17 00:00:00 2001 From: Noor Date: Wed, 18 Jun 2025 19:19:28 +0530 Subject: [PATCH 6/7] Brain redirect on app deploy and delete Improve macro with full crate path for log debug Simplifies brain URL selection by using lazy static variables for staging and testing environments. --- src/config.rs | 13 +++++-------- src/constants.rs | 9 +++++++++ src/sgx/grpc_brain.rs | 23 +++++++++++++++++++---- src/sgx/grpc_dtpm.rs | 2 +- src/utils.rs | 8 ++++++-- 5 files changed, 40 insertions(+), 15 deletions(-) diff --git a/src/config.rs b/src/config.rs index eedd637..78a52a0 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,4 +1,4 @@ -use crate::constants::{STAGING_BRAIN_URLS, TESTNET_BRAIN_URLS}; +use crate::constants::{BRAIN_STAGING, BRAIN_TESTING}; use crate::{general, utils::block_on}; use ed25519_dalek::SigningKey; use log::{debug, info, warn}; @@ -6,7 +6,6 @@ use openssl::bn::BigNum; use openssl::hash::{Hasher, MessageDigest}; use openssl::pkey::{PKey, Private}; use openssl::rsa::Rsa; -use rand::Rng; use serde::{Deserialize, Serialize}; use std::{fs::File, io::Write, path::Path}; @@ -313,15 +312,13 @@ impl Config { match Self::init_config().network.as_str() { "localhost" => ("https://localhost:31337".to_string(), "staging-brain".to_string()), "staging" => { - let url = STAGING_BRAIN_URLS - [rand::thread_rng().gen_range(0..STAGING_BRAIN_URLS.len())] - .to_string(); + let url = BRAIN_STAGING.to_string(); + log::info!("Using staging brain URL: {url}"); (url, "staging-brain".to_string()) } _ => { - let url = TESTNET_BRAIN_URLS - [rand::thread_rng().gen_range(0..TESTNET_BRAIN_URLS.len())] - .to_string(); + let url = BRAIN_TESTING.to_string(); + log::info!("Using testnet brain URL: {url}"); (url, "testnet-brain".to_string()) } } diff --git a/src/constants.rs b/src/constants.rs index ff3da5d..83b2e09 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -1,3 +1,6 @@ +use rand::Rng; +use std::sync::LazyLock; + pub const HRATLS_APP_PORT: u32 = 34500; pub const MAX_REDIRECTS: u16 = 3; pub const STAGING_BRAIN_URLS: [&str; 3] = [ @@ -11,3 +14,9 @@ pub const TESTNET_BRAIN_URLS: [&str; 3] = [ "https://149.22.95.1:44522", // testnet brain 2 "https://149.36.48.99:48638", // testnet brain 3 ]; + +pub static BRAIN_STAGING: LazyLock<&str> = + LazyLock::new(|| STAGING_BRAIN_URLS[rand::thread_rng().gen_range(0..STAGING_BRAIN_URLS.len())]); + +pub static BRAIN_TESTING: LazyLock<&str> = + LazyLock::new(|| TESTNET_BRAIN_URLS[rand::thread_rng().gen_range(0..TESTNET_BRAIN_URLS.len())]); diff --git a/src/sgx/grpc_brain.rs b/src/sgx/grpc_brain.rs index 9881ab2..67fedc6 100644 --- a/src/sgx/grpc_brain.rs +++ b/src/sgx/grpc_brain.rs @@ -7,6 +7,7 @@ use detee_shared::sgx::types::brain::AppDeployConfig; use tokio_stream::StreamExt; use tonic::transport::Channel; +use crate::call_with_follow_redirect; use crate::config::Config; use crate::sgx::utils::calculate_nanolp_for_app; use crate::utils::{self, sign_request}; @@ -25,6 +26,10 @@ pub enum Error { CorruptedRootCa(#[from] std::io::Error), #[error("Internal app error: could not parse Brain URL")] CorruptedBrainUrl, + #[error("Max redirects exceeded: {0}")] + MaxRedirectsExceeded(String), + #[error("Redirect error: {0}")] + RedirectError(String), } type Result = std::result::Result; @@ -70,6 +75,10 @@ async fn client() -> Result> { Ok(BrainAppCliClient::new(Config::connect_brain_channel(default_brain_url).await?)) } +async fn client_from_endpoint(reconnect_endpoint: String) -> Result> { + Ok(BrainAppCliClient::new(Config::connect_brain_channel(reconnect_endpoint).await?)) +} + pub async fn new_app(app_deploy_config: AppDeployConfig) -> Result { let resource = app_deploy_config.clone().resource; let mut req: NewAppReq = app_deploy_config.clone().into(); @@ -87,15 +96,21 @@ pub async fn new_app(app_deploy_config: AppDeployConfig) -> Result { req.admin_pubkey = Config::get_detee_wallet()?; req.hratls_pubkey = Config::get_hratls_pubkey_hex()?; - let res = client().await?.new_app(sign_request(req)?).await?; - - Ok(res.into_inner()) + let client = client().await?; + match call_with_follow_redirect!(client, req, new_app).await { + Ok(res) => Ok(res.into_inner()), + Err(e) => { + log::error!("Failed to create new app: {}", e); + Err(e.into()) + } + } } pub async fn delete_app(app_uuid: String) -> Result<()> { let admin_pubkey = Config::get_detee_wallet()?; let delete_req = DelAppReq { uuid: app_uuid, admin_pubkey }; - let _ = client().await?.delete_app(sign_request(delete_req)?).await?; + let client = client().await?; + let _ = call_with_follow_redirect!(client, delete_req, delete_app).await?; Ok(()) } diff --git a/src/sgx/grpc_dtpm.rs b/src/sgx/grpc_dtpm.rs index 9a27269..8891e2a 100644 --- a/src/sgx/grpc_dtpm.rs +++ b/src/sgx/grpc_dtpm.rs @@ -46,7 +46,7 @@ pub async fn connect_app_dtpm_client(app_uuid: &str) -> Result Date: Wed, 18 Jun 2025 17:46:52 +0300 Subject: [PATCH 7/7] switch to new staging brain --- src/constants.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/constants.rs b/src/constants.rs index 83b2e09..f28deeb 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -4,9 +4,9 @@ use std::sync::LazyLock; pub const HRATLS_APP_PORT: u32 = 34500; pub const MAX_REDIRECTS: u16 = 3; pub const STAGING_BRAIN_URLS: [&str; 3] = [ - "https://184.107.169.199:49092", // staging brain 1 - "https://149.22.95.1:47855", // staging brain 2 - "https://149.36.48.99:48843", // staging brain 3 + "https://156.146.63.216:31337", // staging brain 1 + "https://156.146.63.216:31337", // staging brain 2 + "https://156.146.63.216:31337", // staging brain 3 ]; pub const TESTNET_BRAIN_URLS: [&str; 3] = [ -- 2.43.0