From 3e4672e9e4359f3759d0d119ebc3ac3656dd83d3 Mon Sep 17 00:00:00 2001 From: Noor Date: Tue, 28 Jan 2025 13:11:42 +0530 Subject: [PATCH 01/29] Sgx cli grpc integration add detee-shared dependency and implement BrainSgxCliMock --- Cargo.lock | 45 +++++++++++++++++++++++++++++++++++++ Cargo.toml | 3 +++ README.md | 3 +++ src/grpc.rs | 65 ++++++++++++++++++++++++++++++++++++++++++++++++----- src/main.rs | 6 +++++ 5 files changed, 117 insertions(+), 5 deletions(-) create mode 100644 README.md diff --git a/Cargo.lock b/Cargo.lock index af7229c..bcf8af9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -221,6 +221,7 @@ version = "0.1.0" dependencies = [ "chrono", "dashmap", + "detee-shared", "env_logger", "log", "prost", @@ -325,6 +326,19 @@ dependencies = [ "parking_lot_core", ] +[[package]] +name = "detee-shared" +version = "0.1.0" +source = "git+ssh://git@gitea.detee.cloud/noormohammedb/detee-shared#96501831509839a45d91ac793af905006b7612d5" +dependencies = [ + "base64", + "prost", + "serde", + "serde_yml", + "tonic", + "tonic-build", +] + [[package]] name = "displaydoc" version = "0.2.5" @@ -894,6 +908,16 @@ version = "0.2.168" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d" +[[package]] +name = "libyml" +version = "0.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3302702afa434ffa30847a83305f0a69d6abd74293b6554c18ec85c7ef30c980" +dependencies = [ + "anyhow", + "version_check", +] + [[package]] name = "linux-raw-sys" version = "0.4.14" @@ -1486,6 +1510,21 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_yml" +version = "0.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59e2dd588bf1597a252c3b920e0143eb99b0f76e4e082f4c92ce34fbc9e71ddd" +dependencies = [ + "indexmap 2.7.0", + "itoa", + "libyml", + "memchr", + "ryu", + "serde", + "version_check", +] + [[package]] name = "shlex" version = "1.3.0" @@ -1885,6 +1924,12 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + [[package]] name = "want" version = "0.3.1" diff --git a/Cargo.toml b/Cargo.toml index 5ef1cd6..fe8407d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,5 +19,8 @@ tokio-stream = "0.1.17" tonic = "0.12" uuid = { version = "1.11.0", features = ["v4"] } +detee-shared = { git = "ssh://git@gitea.detee.cloud/noormohammedb/detee-shared" } +# detee-shared = { path = "../detee-shared" } + [build-dependencies] tonic-build = "0.12" diff --git a/README.md b/README.md new file mode 100644 index 0000000..a818250 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# Brain mock + +eval "$(ssh-agent -s)" && ssh-add ~/.ssh/id_ed25519 \ No newline at end of file diff --git a/src/grpc.rs b/src/grpc.rs index fcad76d..90a236e 100644 --- a/src/grpc.rs +++ b/src/grpc.rs @@ -15,6 +15,11 @@ use tokio::sync::mpsc; use tokio_stream::{wrappers::ReceiverStream, Stream, StreamExt}; use tonic::{Request, Response, Status, Streaming}; +use detee_shared::pb::daemon::{ + brain_sgx_cli_server::BrainSgxCli, ContainerFilters, ContainerInspectResp, ContainerListResp, + DeleteContainerRes, NewContainerRes, +}; + pub struct BrainDaemonMock { data: Arc, } @@ -35,6 +40,16 @@ impl BrainCliMock { } } +pub struct BrainSgxCliMock { + data: Arc, +} + +impl BrainSgxCliMock { + pub fn new(data: Arc) -> Self { + Self { data } + } +} + #[tonic::async_trait] impl BrainDaemon for BrainDaemonMock { type RegisterNodeStream = Pin> + Send>>; @@ -162,11 +177,9 @@ impl BrainCli for BrainCliMock { info!("Sending UpdateVMResp: {response:?}"); Ok(Response::new(response)) } - Err(e) => { - Err(Status::unknown( - "Update VM request failed due to error: {e}", - )) - } + Err(_) => Err(Status::unknown( + "Update VM request failed due to error: {e}", + )), } } @@ -248,3 +261,45 @@ impl BrainCli for BrainCliMock { Ok(Response::new(Empty {})) } } + +#[tonic::async_trait] +impl BrainSgxCli for BrainSgxCliMock { + async fn create_container( + &self, + req: tonic::Request, + ) -> Result, Status> { + dbg!(req); + Ok(Response::new(NewContainerRes { + ..Default::default() + })) + } + + async fn delete_container( + &self, + req: tonic::Request, + ) -> Result, Status> { + dbg!(req); + Ok(Response::new(DeleteContainerRes { + ..Default::default() + })) + } + + async fn inspect_container( + &self, + req: tonic::Request, + ) -> Result, Status> { + dbg!(req); + Ok(Response::new(ContainerInspectResp { + ..Default::default() + })) + } + async fn list_containers( + &self, + req: tonic::Request, + ) -> Result, Status> { + dbg!(req); + Ok(Response::new(ContainerListResp { + ..Default::default() + })) + } +} diff --git a/src/main.rs b/src/main.rs index 4bf30f7..d29b42c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,9 +6,12 @@ use grpc::snp_proto::brain_cli_server::BrainCliServer; use grpc::snp_proto::brain_daemon_server::BrainDaemonServer; use grpc::BrainCliMock; use grpc::BrainDaemonMock; +use grpc::BrainSgxCliMock; use std::sync::Arc; use tonic::transport::Server; +use detee_shared::pb::daemon::brain_sgx_cli_server::BrainSgxCliServer; + #[tokio::main] async fn main() { env_logger::builder() @@ -27,9 +30,12 @@ async fn main() { let daemon_server = BrainDaemonServer::new(BrainDaemonMock::new(data.clone())); let cli_server = BrainCliServer::new(BrainCliMock::new(data.clone())); + let sgx_cli_server = BrainSgxCliServer::new(BrainSgxCliMock::new(data.clone())); + Server::builder() .add_service(daemon_server) .add_service(cli_server) + .add_service(sgx_cli_server) .serve(addr) .await .unwrap(); -- 2.43.0 From b1258ac7c724e3233ced7b1f53d4b0374e7120cd Mon Sep 17 00:00:00 2001 From: Noor Date: Wed, 29 Jan 2025 17:58:43 +0530 Subject: [PATCH 02/29] Sgx daemon grpc integration --- Cargo.lock | 2 +- src/grpc.rs | 80 +++++++++++++++++++++++++++++++++++++++++++++-------- src/main.rs | 4 +++ 3 files changed, 74 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bcf8af9..38d60b0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -329,7 +329,7 @@ dependencies = [ [[package]] name = "detee-shared" version = "0.1.0" -source = "git+ssh://git@gitea.detee.cloud/noormohammedb/detee-shared#96501831509839a45d91ac793af905006b7612d5" +source = "git+ssh://git@gitea.detee.cloud/noormohammedb/detee-shared#3e783b11bab6894b6f98bd3c6ce44e8bf5b1f78b" dependencies = [ "base64", "prost", diff --git a/src/grpc.rs b/src/grpc.rs index 90a236e..7b4c1d5 100644 --- a/src/grpc.rs +++ b/src/grpc.rs @@ -16,8 +16,8 @@ use tokio_stream::{wrappers::ReceiverStream, Stream, StreamExt}; use tonic::{Request, Response, Status, Streaming}; use detee_shared::pb::daemon::{ - brain_sgx_cli_server::BrainSgxCli, ContainerFilters, ContainerInspectResp, ContainerListResp, - DeleteContainerRes, NewContainerRes, + brain_sgx_cli_server::BrainSgxCli, brain_sgx_daemon_server::BrainSgxDaemon, ContainerFilters, + ContainerListResp, DeleteContainerRes, NewContainerRes, }; pub struct BrainDaemonMock { @@ -50,6 +50,16 @@ impl BrainSgxCliMock { } } +pub struct BrainSgxDaemonMock { + data: Arc, +} + +impl BrainSgxDaemonMock { + pub fn new(data: Arc) -> Self { + Self { data } + } +} + #[tonic::async_trait] impl BrainDaemon for BrainDaemonMock { type RegisterNodeStream = Pin> + Send>>; @@ -284,15 +294,6 @@ impl BrainSgxCli for BrainSgxCliMock { })) } - async fn inspect_container( - &self, - req: tonic::Request, - ) -> Result, Status> { - dbg!(req); - Ok(Response::new(ContainerInspectResp { - ..Default::default() - })) - } async fn list_containers( &self, req: tonic::Request, @@ -302,4 +303,61 @@ impl BrainSgxCli for BrainSgxCliMock { ..Default::default() })) } + + // async fn inspect_container( + // &self, + // req: tonic::Request, + // ) -> Result, Status> { + // dbg!(req); + // Ok(Response::new(ContainerInspectResp { + // ..Default::default() + // })) + // } +} + +#[tonic::async_trait] +impl BrainSgxDaemon for BrainSgxDaemonMock { + type RegisterNodeStream = Pin< + Box> + Send>, + >; + type BrainMessagesStream = + Pin> + Send>>; + + async fn register_node( + &self, + req: tonic::Request, + ) -> Result, Status> { + dbg!(req); + let (tx, rx) = mpsc::channel(6); + + tokio::spawn(async move { + for _ in 0..5 { + let _ = tx + .send(detee_shared::pb::shared::ContainerContracts { + ..Default::default() + }) + .await; + tokio::time::sleep(tokio::time::Duration::from_secs(3)).await; + } + }); + let output_stream = ReceiverStream::new(rx).map(|msg| Ok(msg)); + Ok(Response::new(Box::pin(output_stream))) + } + + async fn daemon_messages( + &self, + req: tonic::Request>, + ) -> Result, Status> { + dbg!(req); + Ok(Response::new(detee_shared::pb::shared::Empty {})) + } + async fn brain_messages( + &self, + req: tonic::Request, + ) -> Result, Status> { + dbg!(req); + Ok(Response::new( + Box::pin(tokio_stream::empty()) as Self::BrainMessagesStream + )) + } } diff --git a/src/main.rs b/src/main.rs index d29b42c..7305cc0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,11 +2,13 @@ mod data; mod grpc; use data::BrainData; +use detee_shared::pb::daemon::brain_sgx_daemon_server::BrainSgxDaemonServer; use grpc::snp_proto::brain_cli_server::BrainCliServer; use grpc::snp_proto::brain_daemon_server::BrainDaemonServer; use grpc::BrainCliMock; use grpc::BrainDaemonMock; use grpc::BrainSgxCliMock; +use grpc::BrainSgxDaemonMock; use std::sync::Arc; use tonic::transport::Server; @@ -31,11 +33,13 @@ async fn main() { let cli_server = BrainCliServer::new(BrainCliMock::new(data.clone())); let sgx_cli_server = BrainSgxCliServer::new(BrainSgxCliMock::new(data.clone())); + let sgx_daemon_server = BrainSgxDaemonServer::new(BrainSgxDaemonMock::new(data.clone())); Server::builder() .add_service(daemon_server) .add_service(cli_server) .add_service(sgx_cli_server) + .add_service(sgx_daemon_server) .serve(addr) .await .unwrap(); -- 2.43.0 From 8f40adcbf80f53b01bc677e9c6d20c4fdbc6146d Mon Sep 17 00:00:00 2001 From: Noor Date: Thu, 30 Jan 2025 18:31:10 +0530 Subject: [PATCH 03/29] redirect sgx container create call to daemon and response to cli --- Cargo.lock | 2 +- src/data.rs | 81 +++++++++++++++++++++++++++++++++++++++++++++++++ src/grpc.rs | 86 ++++++++++++++++++++++++++++++++++++++--------------- 3 files changed, 144 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 38d60b0..41262fc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -329,7 +329,7 @@ dependencies = [ [[package]] name = "detee-shared" version = "0.1.0" -source = "git+ssh://git@gitea.detee.cloud/noormohammedb/detee-shared#3e783b11bab6894b6f98bd3c6ce44e8bf5b1f78b" +source = "git+ssh://git@gitea.detee.cloud/noormohammedb/detee-shared#a2899ba5a25794aec60a093695675ff24f967484" dependencies = [ "base64", "prost", diff --git a/src/data.rs b/src/data.rs index b2d0ce3..bdff0b9 100644 --- a/src/data.rs +++ b/src/data.rs @@ -8,6 +8,10 @@ use std::sync::RwLock; use tokio::sync::mpsc::Sender; use tokio::sync::oneshot::Sender as OneshotSender; +use detee_shared::pb::daemon::{BrainMessage as BrainMessageSgx, NewContainerRes}; +use detee_shared::pb::shared as sharedPb; +use detee_shared::pb::shared::Container; + #[derive(thiserror::Error, Debug)] pub enum Error { #[error("We do not allow locking of more than 100000 tokens.")] @@ -143,6 +147,9 @@ pub struct BrainData { tmp_newvm_reqs: DashMap)>, tmp_updatevm_reqs: DashMap)>, daemon_tx: DashMap>, + + sgx_daemon_tx: DashMap>, + tmp_new_container_reqs: DashMap)>, } #[derive(Debug)] @@ -161,6 +168,9 @@ impl BrainData { tmp_newvm_reqs: DashMap::new(), tmp_updatevm_reqs: DashMap::new(), daemon_tx: DashMap::new(), + + sgx_daemon_tx: DashMap::new(), + tmp_new_container_reqs: DashMap::new(), } } @@ -669,3 +679,74 @@ impl BrainData { .collect() } } + +impl BrainData { + pub fn add_sgx_daemon_tx(&self, node_pubkey: &str, tx: Sender) { + self.sgx_daemon_tx.insert(node_pubkey.to_string(), tx); + } + + pub fn del_sgx_daemon_tx(&self, node_pubkey: &str) { + self.sgx_daemon_tx.remove(node_pubkey); + } + + pub async fn send_new_container_req( + &self, + mut req: Container, + tx: OneshotSender, + ) { + req.uuid = uuid::Uuid::new_v4().to_string(); + + info!("Inserting new container request in memory: {req:?}"); + self.tmp_new_container_reqs + .insert(req.uuid.clone(), (req.clone(), tx)); + + if let Some(sgx_daemon_tx) = self.sgx_daemon_tx.get(&req.node_pubkey) { + debug!( + "Found daemon TX for {}. Sending newVMReq {}", + req.node_pubkey, req.uuid + ); + let msg = BrainMessageSgx { + msg: Some( + detee_shared::pb::daemon::brain_message::Msg::NewContainerReq(req.clone()), + ), + }; + if let Err(e) = sgx_daemon_tx.send(msg).await { + warn!( + "Failed to send new container request to {} due to error: {e:?}", + req.node_pubkey + ); + info!("Deleting daemon TX for {}", req.node_pubkey); + self.del_sgx_daemon_tx(&req.node_pubkey); + self.send_new_container_resp(NewContainerRes { + uuid: req.uuid, + status: "Failed".to_string(), + error: "Daemon is offline.".to_string(), + ..Default::default() + }) + .await; + } + } + } + + pub async fn send_new_container_resp(&self, new_container_resp: NewContainerRes) { + dbg!(&new_container_resp.uuid); + dbg!(&self.tmp_new_container_reqs); + let new_container_req = match self.tmp_new_container_reqs.remove(&new_container_resp.uuid) { + Some((_, r)) => r, + None => { + log::error!( + "Received confirmation for ghost new container req {}", + new_container_resp.uuid + ); + return; + } + }; + if let Err(_) = new_container_req.1.send(new_container_resp.clone()) { + log::error!( + "CLI RX for {} dropped before receiving confirmation {:?}.", + &new_container_req.0.admin_pubkey, + new_container_resp, + ); + } + } +} diff --git a/src/grpc.rs b/src/grpc.rs index 7b4c1d5..dd98efe 100644 --- a/src/grpc.rs +++ b/src/grpc.rs @@ -16,8 +16,9 @@ use tokio_stream::{wrappers::ReceiverStream, Stream, StreamExt}; use tonic::{Request, Response, Status, Streaming}; use detee_shared::pb::daemon::{ - brain_sgx_cli_server::BrainSgxCli, brain_sgx_daemon_server::BrainSgxDaemon, ContainerFilters, - ContainerListResp, DeleteContainerRes, NewContainerRes, + brain_sgx_cli_server::BrainSgxCli, brain_sgx_daemon_server::BrainSgxDaemon, + daemon_message as sgx_daemon_message, ContainerFilters, ContainerListResp, DeleteContainerRes, + NewContainerRes, }; pub struct BrainDaemonMock { @@ -278,10 +279,24 @@ impl BrainSgxCli for BrainSgxCliMock { &self, req: tonic::Request, ) -> Result, Status> { - dbg!(req); - Ok(Response::new(NewContainerRes { - ..Default::default() - })) + let req = req.into_inner(); + log::info!("Creating new container: {req:?}"); + let admin_pubkey = req.admin_pubkey.clone(); + let (oneshot_tx, oneshot_rx) = tokio::sync::oneshot::channel(); + self.data.send_new_container_req(req, oneshot_tx).await; + + match oneshot_rx.await { + Ok(response) => { + info!("responding container confirmation to {admin_pubkey}: {response:?}"); + Ok(Response::new(response)) + } + Err(e) => { + log::error!("Something went wrong. Reached error {e:?}"); + Err(Status::unknown( + "Request failed due to unknown error. Please try again or contact the DeTEE devs team.", + )) + } + } } async fn delete_container( @@ -331,33 +346,56 @@ impl BrainSgxDaemon for BrainSgxDaemonMock { let (tx, rx) = mpsc::channel(6); tokio::spawn(async move { - for _ in 0..5 { - let _ = tx - .send(detee_shared::pb::shared::ContainerContracts { - ..Default::default() - }) - .await; - tokio::time::sleep(tokio::time::Duration::from_secs(3)).await; - } + let _ = tx + .send(detee_shared::pb::shared::ContainerContracts { + ..Default::default() + }) + .await; }); let output_stream = ReceiverStream::new(rx).map(|msg| Ok(msg)); Ok(Response::new(Box::pin(output_stream))) } + async fn brain_messages( + &self, + req: tonic::Request, + ) -> Result, Status> { + let req = req.into_inner(); + info!("Daemon {} connected to receive brain messages", req.pubkey); + let (tx, rx) = mpsc::channel(6); + self.data.add_sgx_daemon_tx(&req.pubkey, tx); + let output_stream = ReceiverStream::new(rx).map(|msg| Ok(msg)); + Ok(Response::new( + Box::pin(output_stream) as Self::BrainMessagesStream + )) + } + async fn daemon_messages( &self, req: tonic::Request>, ) -> Result, Status> { - dbg!(req); + dbg!(&req); + let mut req_stream = req.into_inner(); + + while let Some(daemon_message) = req_stream.next().await { + match daemon_message { + Ok(msg) => match msg.msg { + Some(sgx_daemon_message::Msg::NewContainerResp(new_cont)) => { + dbg!(&new_cont); + self.data.send_new_container_resp(new_cont).await; + } + Some(something) => { + dbg!(something); + } + None => { + dbg!("None"); + } + }, + Err(_) => todo!(), + } + // + } + Ok(Response::new(detee_shared::pb::shared::Empty {})) } - async fn brain_messages( - &self, - req: tonic::Request, - ) -> Result, Status> { - dbg!(req); - Ok(Response::new( - Box::pin(tokio_stream::empty()) as Self::BrainMessagesStream - )) - } } -- 2.43.0 From c6c44da0e82dd3c65994af7e1548e4565b3ebeb8 Mon Sep 17 00:00:00 2001 From: Noor Date: Fri, 31 Jan 2025 12:59:20 +0000 Subject: [PATCH 04/29] manageed container contract and node registraion --- Cargo.lock | 2 +- src/data.rs | 117 ++++++++++++++++++++++++++++++++++++++++++++++------ src/grpc.rs | 32 ++++++++++---- 3 files changed, 129 insertions(+), 22 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 41262fc..5db03a2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -329,7 +329,7 @@ dependencies = [ [[package]] name = "detee-shared" version = "0.1.0" -source = "git+ssh://git@gitea.detee.cloud/noormohammedb/detee-shared#a2899ba5a25794aec60a093695675ff24f967484" +source = "git+ssh://git@gitea.detee.cloud/noormohammedb/detee-shared#6e1b1853838905c44d535d984d1221dd5d0dc2bc" dependencies = [ "base64", "prost", diff --git a/src/data.rs b/src/data.rs index bdff0b9..c3e3a7f 100644 --- a/src/data.rs +++ b/src/data.rs @@ -10,7 +10,6 @@ use tokio::sync::oneshot::Sender as OneshotSender; use detee_shared::pb::daemon::{BrainMessage as BrainMessageSgx, NewContainerRes}; use detee_shared::pb::shared as sharedPb; -use detee_shared::pb::shared::Container; #[derive(thiserror::Error, Debug)] pub enum Error { @@ -138,6 +137,60 @@ impl Into for Contract { } } +#[derive(Clone, Debug, Default)] +pub struct AppContract { + pub uuid: String, + pub package_url: String, + pub admin_pubkey: String, + pub node_pubkey: String, + pub mapped_ports: Vec<(u16, u16)>, + pub host_ipv4: String, + pub disk_size_gb: u32, + pub vcpus: u32, + pub memory_mb: u32, + pub created_at: chrono::DateTime, + pub updated_at: chrono::DateTime, + // price per unit per minute + // recommended value is 20000 + pub price_per_unit: u64, + pub locked_nano: u64, + pub collected_at: chrono::DateTime, +} + +impl From for sharedPb::ContainerContracts { + fn from(value: AppContract) -> Self { + Self { + uuid: value.uuid, + admin_pubkey: value.admin_pubkey, + node_pubkey: value.node_pubkey, + package_url: value.package_url, + exposed_ports: value + .mapped_ports + .into_iter() + .map(sharedPb::MappedPort::from) + .collect(), + ..Default::default() + } + } +} + +#[derive(Eq, Hash, PartialEq, Clone, Debug, Default)] +pub struct AppNode { + pub public_key: String, + pub owner_key: String, + pub country: String, + pub region: String, + pub city: String, + pub ip: String, + pub avail_mem_mb: u32, + pub avail_vcpus: u32, + pub avail_storage_gbs: u32, + pub avail_ports: u32, + pub max_ports_per_app: u32, + // nanotokens per unit per minute + pub price: u64, +} + #[derive(Default)] pub struct BrainData { // amount of nanotokens in each account @@ -148,8 +201,10 @@ pub struct BrainData { tmp_updatevm_reqs: DashMap)>, daemon_tx: DashMap>, - sgx_daemon_tx: DashMap>, + app_nodes: RwLock>, + app_daemon_tx: DashMap>, tmp_new_container_reqs: DashMap)>, + app_contracts: RwLock>, } #[derive(Debug)] @@ -169,8 +224,10 @@ impl BrainData { tmp_updatevm_reqs: DashMap::new(), daemon_tx: DashMap::new(), - sgx_daemon_tx: DashMap::new(), + app_daemon_tx: DashMap::new(), tmp_new_container_reqs: DashMap::new(), + app_contracts: RwLock::new(Vec::new()), + app_nodes: RwLock::new(Vec::new()), } } @@ -681,17 +738,40 @@ impl BrainData { } impl BrainData { - pub fn add_sgx_daemon_tx(&self, node_pubkey: &str, tx: Sender) { - self.sgx_daemon_tx.insert(node_pubkey.to_string(), tx); + pub fn add_app_daemon_tx(&self, node_pubkey: &str, tx: Sender) { + self.app_daemon_tx.insert(node_pubkey.to_string(), tx); } - pub fn del_sgx_daemon_tx(&self, node_pubkey: &str) { - self.sgx_daemon_tx.remove(node_pubkey); + pub fn del_app_daemon_tx(&self, node_pubkey: &str) { + self.app_daemon_tx.remove(node_pubkey); + } + + pub fn insert_app_node(&self, node: AppNode) { + info!("Registering app node {node:?}"); + let mut nodes = self.app_nodes.write().unwrap(); + for n in nodes.iter_mut() { + if n.public_key == node.public_key { + // TODO: figure what to do in this case. + warn!("Node {} already exists. Updating data.", n.public_key); + *n = node; + return; + } + } + nodes.push(node); + } + + pub fn find_app_contracts_by_node_pubkey(&self, node_pubkey: &str) -> Vec { + let app_contracts = self.app_contracts.read().unwrap(); + app_contracts + .iter() + .filter(|c| c.node_pubkey == node_pubkey) + .cloned() + .collect() } pub async fn send_new_container_req( &self, - mut req: Container, + mut req: sharedPb::Container, tx: OneshotSender, ) { req.uuid = uuid::Uuid::new_v4().to_string(); @@ -700,7 +780,7 @@ impl BrainData { self.tmp_new_container_reqs .insert(req.uuid.clone(), (req.clone(), tx)); - if let Some(sgx_daemon_tx) = self.sgx_daemon_tx.get(&req.node_pubkey) { + if let Some(app_daemon_tx) = self.app_daemon_tx.get(&req.node_pubkey) { debug!( "Found daemon TX for {}. Sending newVMReq {}", req.node_pubkey, req.uuid @@ -710,13 +790,13 @@ impl BrainData { detee_shared::pb::daemon::brain_message::Msg::NewContainerReq(req.clone()), ), }; - if let Err(e) = sgx_daemon_tx.send(msg).await { + if let Err(e) = app_daemon_tx.send(msg).await { warn!( "Failed to send new container request to {} due to error: {e:?}", req.node_pubkey ); info!("Deleting daemon TX for {}", req.node_pubkey); - self.del_sgx_daemon_tx(&req.node_pubkey); + self.del_app_daemon_tx(&req.node_pubkey); self.send_new_container_resp(NewContainerRes { uuid: req.uuid, status: "Failed".to_string(), @@ -741,12 +821,23 @@ impl BrainData { return; } }; - if let Err(_) = new_container_req.1.send(new_container_resp.clone()) { + if let Err(err) = new_container_req.1.send(new_container_resp.clone()) { log::error!( - "CLI RX for {} dropped before receiving confirmation {:?}.", + "CLI RX for {} dropped before receiving confirmation {:?}.\n{:?}", &new_container_req.0.admin_pubkey, new_container_resp, + err ); } + + let app_contracts = AppContract { + uuid: new_container_req.0.uuid, + node_pubkey: new_container_req.0.node_pubkey.clone(), + package_url: new_container_req.0.package_url, + admin_pubkey: new_container_req.0.admin_pubkey, + ..Default::default() + }; + log::info!("Created new app contract: {app_contracts:?}"); + self.app_contracts.write().unwrap().push(app_contracts); } } diff --git a/src/grpc.rs b/src/grpc.rs index dd98efe..5353f35 100644 --- a/src/grpc.rs +++ b/src/grpc.rs @@ -342,15 +342,31 @@ impl BrainSgxDaemon for BrainSgxDaemonMock { &self, req: tonic::Request, ) -> Result, Status> { - dbg!(req); - let (tx, rx) = mpsc::channel(6); + log::info!("registering app node : {:?}", &req); + let req_data = req.into_inner(); + let app_node = crate::data::AppNode { + public_key: req_data.node_pubkey.clone(), + owner_key: req_data.owner_pubkey, + ip: req_data.main_ip, + city: req_data.city, + region: req_data.region, + country: req_data.country, + ..Default::default() + }; + + self.data.insert_app_node(app_node); + log::info!("Sending existing contracts to {}", &req_data.node_pubkey); + + let app_contracts = self + .data + .find_app_contracts_by_node_pubkey(&req_data.node_pubkey); + + let (tx, rx) = mpsc::channel(6); tokio::spawn(async move { - let _ = tx - .send(detee_shared::pb::shared::ContainerContracts { - ..Default::default() - }) - .await; + for contract in app_contracts { + let _ = tx.send(contract.into()).await; + } }); let output_stream = ReceiverStream::new(rx).map(|msg| Ok(msg)); Ok(Response::new(Box::pin(output_stream))) @@ -363,7 +379,7 @@ impl BrainSgxDaemon for BrainSgxDaemonMock { let req = req.into_inner(); info!("Daemon {} connected to receive brain messages", req.pubkey); let (tx, rx) = mpsc::channel(6); - self.data.add_sgx_daemon_tx(&req.pubkey, tx); + self.data.add_app_daemon_tx(&req.pubkey, tx); let output_stream = ReceiverStream::new(rx).map(|msg| Ok(msg)); Ok(Response::new( Box::pin(output_stream) as Self::BrainMessagesStream -- 2.43.0 From 230eb8cfbd52a80f658eaab790f27bf65fa972be Mon Sep 17 00:00:00 2001 From: Noor Date: Mon, 3 Feb 2025 18:20:21 +0530 Subject: [PATCH 05/29] delete container implemented refactor daemon message --- Cargo.lock | 2 +- src/data.rs | 81 +++++++++++++++++++++++++++++++++++++++++++++-------- src/grpc.rs | 42 +++++++++++++++++---------- 3 files changed, 98 insertions(+), 27 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5db03a2..7509f23 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -329,7 +329,7 @@ dependencies = [ [[package]] name = "detee-shared" version = "0.1.0" -source = "git+ssh://git@gitea.detee.cloud/noormohammedb/detee-shared#6e1b1853838905c44d535d984d1221dd5d0dc2bc" +source = "git+ssh://git@gitea.detee.cloud/noormohammedb/detee-shared#7c9f66a7394c06ad8af0934e34b113f9c965bc98" dependencies = [ "base64", "prost", diff --git a/src/data.rs b/src/data.rs index c3e3a7f..cf095ad 100644 --- a/src/data.rs +++ b/src/data.rs @@ -8,7 +8,7 @@ use std::sync::RwLock; use tokio::sync::mpsc::Sender; use tokio::sync::oneshot::Sender as OneshotSender; -use detee_shared::pb::daemon::{BrainMessage as BrainMessageSgx, NewContainerRes}; +use detee_shared::pb::daemon as daemonPb; use detee_shared::pb::shared as sharedPb; #[derive(thiserror::Error, Debug)] @@ -202,8 +202,14 @@ pub struct BrainData { daemon_tx: DashMap>, app_nodes: RwLock>, - app_daemon_tx: DashMap>, - tmp_new_container_reqs: DashMap)>, + app_daemon_tx: DashMap>, + tmp_new_container_reqs: DashMap< + String, + ( + sharedPb::Container, + OneshotSender, + ), + >, app_contracts: RwLock>, } @@ -738,7 +744,7 @@ impl BrainData { } impl BrainData { - pub fn add_app_daemon_tx(&self, node_pubkey: &str, tx: Sender) { + pub fn add_app_daemon_tx(&self, node_pubkey: &str, tx: Sender) { self.app_daemon_tx.insert(node_pubkey.to_string(), tx); } @@ -747,7 +753,6 @@ impl BrainData { } pub fn insert_app_node(&self, node: AppNode) { - info!("Registering app node {node:?}"); let mut nodes = self.app_nodes.write().unwrap(); for n in nodes.iter_mut() { if n.public_key == node.public_key { @@ -760,6 +765,25 @@ impl BrainData { nodes.push(node); } + pub fn find_app_contract_by_uuid(&self, uuid: &str) -> Option { + let contracts = self.app_contracts.read().unwrap(); + contracts.iter().cloned().find(|c| c.uuid == uuid) + } + + pub fn find_app_contracts_by_admin_pubkey(&self, admin_pubkey: &str) -> Vec { + debug!("Searching contracts for admin pubkey {admin_pubkey}"); + let contracts: Vec = self + .app_contracts + .read() + .unwrap() + .iter() + .filter(|c| c.admin_pubkey == admin_pubkey) + .cloned() + .collect(); + debug!("Found {} contracts or {admin_pubkey}.", contracts.len()); + contracts + } + pub fn find_app_contracts_by_node_pubkey(&self, node_pubkey: &str) -> Vec { let app_contracts = self.app_contracts.read().unwrap(); app_contracts @@ -772,7 +796,7 @@ impl BrainData { pub async fn send_new_container_req( &self, mut req: sharedPb::Container, - tx: OneshotSender, + tx: OneshotSender, ) { req.uuid = uuid::Uuid::new_v4().to_string(); @@ -785,7 +809,7 @@ impl BrainData { "Found daemon TX for {}. Sending newVMReq {}", req.node_pubkey, req.uuid ); - let msg = BrainMessageSgx { + let msg = daemonPb::BrainMessage { msg: Some( detee_shared::pb::daemon::brain_message::Msg::NewContainerReq(req.clone()), ), @@ -797,7 +821,7 @@ impl BrainData { ); info!("Deleting daemon TX for {}", req.node_pubkey); self.del_app_daemon_tx(&req.node_pubkey); - self.send_new_container_resp(NewContainerRes { + self.send_new_container_resp(daemonPb::NewContainerRes { uuid: req.uuid, status: "Failed".to_string(), error: "Daemon is offline.".to_string(), @@ -808,9 +832,44 @@ impl BrainData { } } - pub async fn send_new_container_resp(&self, new_container_resp: NewContainerRes) { - dbg!(&new_container_resp.uuid); - dbg!(&self.tmp_new_container_reqs); + pub async fn send_del_container_req( + &self, + req: daemonPb::ContainerFilters, + ) -> Result<(), Box> { + if let Some(app_contract) = self.find_app_contract_by_uuid(req.uuid()) { + info!("Found app contract {}. Deleting...", req.uuid()); + if let Some(app_daemon_tx) = self.app_daemon_tx.get(&app_contract.node_pubkey) { + debug!( + "TX for daemon {} found. Informing daemon about deletion of {}.", + app_contract.node_pubkey, + req.uuid() + ); + let msg = daemonPb::BrainMessage { + msg: Some( + detee_shared::pb::daemon::brain_message::Msg::DeleteContainer(req.clone()), + ), + }; + + if let Err(e) = app_daemon_tx.send(msg).await { + warn!( + "Failed to send deletion request to {} due to error: {e:?}", + app_contract.node_pubkey + ); + info!("Deleting daemon TX for {}", app_contract.node_pubkey); + self.del_app_daemon_tx(&app_contract.node_pubkey); + } + } + + let mut app_contracts = self.app_contracts.write().unwrap(); + app_contracts.retain(|c| c.uuid != req.uuid()); + + return Ok(()); + } else { + Err("Contract not found".into()) + } + } + + pub async fn send_new_container_resp(&self, new_container_resp: daemonPb::NewContainerRes) { let new_container_req = match self.tmp_new_container_reqs.remove(&new_container_resp.uuid) { Some((_, r)) => r, None => { diff --git a/src/grpc.rs b/src/grpc.rs index 5353f35..b62e9d4 100644 --- a/src/grpc.rs +++ b/src/grpc.rs @@ -17,8 +17,7 @@ use tonic::{Request, Response, Status, Streaming}; use detee_shared::pb::daemon::{ brain_sgx_cli_server::BrainSgxCli, brain_sgx_daemon_server::BrainSgxDaemon, - daemon_message as sgx_daemon_message, ContainerFilters, ContainerListResp, DeleteContainerRes, - NewContainerRes, + daemon_message as sgx_daemon_message, ContainerFilters, ContainerListResp, NewContainerRes, }; pub struct BrainDaemonMock { @@ -302,11 +301,18 @@ impl BrainSgxCli for BrainSgxCliMock { async fn delete_container( &self, req: tonic::Request, - ) -> Result, Status> { - dbg!(req); - Ok(Response::new(DeleteContainerRes { - ..Default::default() - })) + ) -> Result, Status> { + let req = req.into_inner(); + log::info!( + "deleting container: {}", + req.uuid.clone().unwrap_or_default() + ); + if let Err(er) = self.data.send_del_container_req(req).await { + info!("Could not delete container: {er}"); + return Err(Status::not_found("Could not find container")); + }; + + Ok(Response::new(detee_shared::pb::shared::Empty {})) } async fn list_containers( @@ -342,8 +348,12 @@ impl BrainSgxDaemon for BrainSgxDaemonMock { &self, req: tonic::Request, ) -> Result, Status> { - log::info!("registering app node : {:?}", &req); let req_data = req.into_inner(); + log::info!( + "registering app node_key : {}, owner_key: {}", + &req_data.node_pubkey, + &req_data.owner_pubkey + ); let app_node = crate::data::AppNode { public_key: req_data.node_pubkey.clone(), @@ -390,24 +400,26 @@ impl BrainSgxDaemon for BrainSgxDaemonMock { &self, req: tonic::Request>, ) -> Result, Status> { - dbg!(&req); let mut req_stream = req.into_inner(); + let mut pubkey = String::new(); while let Some(daemon_message) = req_stream.next().await { match daemon_message { Ok(msg) => match msg.msg { - Some(sgx_daemon_message::Msg::NewContainerResp(new_cont)) => { - dbg!(&new_cont); - self.data.send_new_container_resp(new_cont).await; + Some(sgx_daemon_message::Msg::Pubkey(something)) => { + pubkey = something.pubkey; } - Some(something) => { - dbg!(something); + Some(sgx_daemon_message::Msg::NewContainerResp(new_cont)) => { + self.data.send_new_container_resp(new_cont).await; } None => { dbg!("None"); } }, - Err(_) => todo!(), + Err(e) => { + log::warn!("Daemon disconnected: {e:?}"); + self.data.del_app_daemon_tx(&pubkey); + } } // } -- 2.43.0 From 5ae69dee5209776dd0d2862aa761db8ce05272e1 Mon Sep 17 00:00:00 2001 From: Noor Date: Tue, 4 Feb 2025 18:15:53 +0530 Subject: [PATCH 06/29] integrating new proto structure --- Cargo.lock | 1 - src/data.rs | 61 ++++++++++++++++++++++++----------------------------- src/grpc.rs | 51 +++++++++++++++++++++++++------------------- src/main.rs | 13 ++++++------ 4 files changed, 62 insertions(+), 64 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7509f23..deb88e7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -329,7 +329,6 @@ dependencies = [ [[package]] name = "detee-shared" version = "0.1.0" -source = "git+ssh://git@gitea.detee.cloud/noormohammedb/detee-shared#7c9f66a7394c06ad8af0934e34b113f9c965bc98" dependencies = [ "base64", "prost", diff --git a/src/data.rs b/src/data.rs index cf095ad..7632bb9 100644 --- a/src/data.rs +++ b/src/data.rs @@ -2,14 +2,18 @@ use crate::grpc::snp_proto::{self as grpc}; use chrono::Utc; use dashmap::DashMap; +use detee_shared::pb::brain::DelAppReq; use log::{debug, info, warn}; use std::str::FromStr; use std::sync::RwLock; use tokio::sync::mpsc::Sender; use tokio::sync::oneshot::Sender as OneshotSender; -use detee_shared::pb::daemon as daemonPb; -use detee_shared::pb::shared as sharedPb; +use detee_shared::pb::brain::AppContract as AppContractPB; +use detee_shared::pb::brain::BrainMessageApp; +use detee_shared::pb::brain::MappedPort; +use detee_shared::pb::brain::NewAppReq; +use detee_shared::pb::brain::NewAppRes; #[derive(thiserror::Error, Debug)] pub enum Error { @@ -157,7 +161,7 @@ pub struct AppContract { pub collected_at: chrono::DateTime, } -impl From for sharedPb::ContainerContracts { +impl From for AppContractPB { fn from(value: AppContract) -> Self { Self { uuid: value.uuid, @@ -167,7 +171,7 @@ impl From for sharedPb::ContainerContracts { exposed_ports: value .mapped_ports .into_iter() - .map(sharedPb::MappedPort::from) + .map(MappedPort::from) .collect(), ..Default::default() } @@ -202,14 +206,8 @@ pub struct BrainData { daemon_tx: DashMap>, app_nodes: RwLock>, - app_daemon_tx: DashMap>, - tmp_new_container_reqs: DashMap< - String, - ( - sharedPb::Container, - OneshotSender, - ), - >, + app_daemon_tx: DashMap>, + tmp_new_container_reqs: DashMap)>, app_contracts: RwLock>, } @@ -744,7 +742,7 @@ impl BrainData { } impl BrainData { - pub fn add_app_daemon_tx(&self, node_pubkey: &str, tx: Sender) { + pub fn add_app_daemon_tx(&self, node_pubkey: &str, tx: Sender) { self.app_daemon_tx.insert(node_pubkey.to_string(), tx); } @@ -767,7 +765,7 @@ impl BrainData { pub fn find_app_contract_by_uuid(&self, uuid: &str) -> Option { let contracts = self.app_contracts.read().unwrap(); - contracts.iter().cloned().find(|c| c.uuid == uuid) + contracts.iter().find(|c| c.uuid == uuid).cloned() } pub fn find_app_contracts_by_admin_pubkey(&self, admin_pubkey: &str) -> Vec { @@ -793,11 +791,7 @@ impl BrainData { .collect() } - pub async fn send_new_container_req( - &self, - mut req: sharedPb::Container, - tx: OneshotSender, - ) { + pub async fn send_new_container_req(&self, mut req: NewAppReq, tx: OneshotSender) { req.uuid = uuid::Uuid::new_v4().to_string(); info!("Inserting new container request in memory: {req:?}"); @@ -809,10 +803,10 @@ impl BrainData { "Found daemon TX for {}. Sending newVMReq {}", req.node_pubkey, req.uuid ); - let msg = daemonPb::BrainMessage { - msg: Some( - detee_shared::pb::daemon::brain_message::Msg::NewContainerReq(req.clone()), - ), + let msg = BrainMessageApp { + msg: Some(detee_shared::pb::brain::brain_message_app::Msg::NewAppReq( + req.clone(), + )), }; if let Err(e) = app_daemon_tx.send(msg).await { warn!( @@ -821,7 +815,7 @@ impl BrainData { ); info!("Deleting daemon TX for {}", req.node_pubkey); self.del_app_daemon_tx(&req.node_pubkey); - self.send_new_container_resp(daemonPb::NewContainerRes { + self.send_new_container_resp(NewAppRes { uuid: req.uuid, status: "Failed".to_string(), error: "Daemon is offline.".to_string(), @@ -834,19 +828,18 @@ impl BrainData { pub async fn send_del_container_req( &self, - req: daemonPb::ContainerFilters, + req: DelAppReq, ) -> Result<(), Box> { - if let Some(app_contract) = self.find_app_contract_by_uuid(req.uuid()) { - info!("Found app contract {}. Deleting...", req.uuid()); + if let Some(app_contract) = self.find_app_contract_by_uuid(&req.uuid) { + info!("Found app contract {}. Deleting...", &req.uuid); if let Some(app_daemon_tx) = self.app_daemon_tx.get(&app_contract.node_pubkey) { debug!( "TX for daemon {} found. Informing daemon about deletion of {}.", - app_contract.node_pubkey, - req.uuid() + app_contract.node_pubkey, &req.uuid ); - let msg = daemonPb::BrainMessage { + let msg = BrainMessageApp { msg: Some( - detee_shared::pb::daemon::brain_message::Msg::DeleteContainer(req.clone()), + detee_shared::pb::brain::brain_message_app::Msg::DeleteAppReq(req.clone()), ), }; @@ -861,15 +854,15 @@ impl BrainData { } let mut app_contracts = self.app_contracts.write().unwrap(); - app_contracts.retain(|c| c.uuid != req.uuid()); + app_contracts.retain(|c| c.uuid != req.uuid); - return Ok(()); + Ok(()) } else { Err("Contract not found".into()) } } - pub async fn send_new_container_resp(&self, new_container_resp: daemonPb::NewContainerRes) { + pub async fn send_new_container_resp(&self, new_container_resp: NewAppRes) { let new_container_req = match self.tmp_new_container_reqs.remove(&new_container_resp.uuid) { Some((_, r)) => r, None => { diff --git a/src/grpc.rs b/src/grpc.rs index b62e9d4..e56b9c4 100644 --- a/src/grpc.rs +++ b/src/grpc.rs @@ -15,9 +15,10 @@ use tokio::sync::mpsc; use tokio_stream::{wrappers::ReceiverStream, Stream, StreamExt}; use tonic::{Request, Response, Status, Streaming}; -use detee_shared::pb::daemon::{ - brain_sgx_cli_server::BrainSgxCli, brain_sgx_daemon_server::BrainSgxDaemon, - daemon_message as sgx_daemon_message, ContainerFilters, ContainerListResp, NewContainerRes, +use detee_shared::pb::brain::brain_app_cli_server::BrainAppCli; +use detee_shared::pb::brain::brain_app_daemon_server::BrainAppDaemon; +use detee_shared::pb::brain::{ + AppContract, BrainMessageApp, NewAppReq, NewAppRes, RegisterAppNodeReq, }; pub struct BrainDaemonMock { @@ -40,21 +41,21 @@ impl BrainCliMock { } } -pub struct BrainSgxCliMock { +pub struct BrainAppCliMock { data: Arc, } -impl BrainSgxCliMock { +impl BrainAppCliMock { pub fn new(data: Arc) -> Self { Self { data } } } -pub struct BrainSgxDaemonMock { +pub struct BrainAppDaemonMock { data: Arc, } -impl BrainSgxDaemonMock { +impl BrainAppDaemonMock { pub fn new(data: Arc) -> Self { Self { data } } @@ -273,11 +274,11 @@ impl BrainCli for BrainCliMock { } #[tonic::async_trait] -impl BrainSgxCli for BrainSgxCliMock { - async fn create_container( +impl BrainAppCli for BrainAppCliMock { + async fn create_app( &self, - req: tonic::Request, - ) -> Result, Status> { + req: tonic::Request, + ) -> Result, Status> { let req = req.into_inner(); log::info!("Creating new container: {req:?}"); let admin_pubkey = req.admin_pubkey.clone(); @@ -298,6 +299,8 @@ impl BrainSgxCli for BrainSgxCliMock { } } + /* + async fn delete_container( &self, req: tonic::Request, @@ -319,12 +322,17 @@ impl BrainSgxCli for BrainSgxCliMock { &self, req: tonic::Request, ) -> Result, Status> { - dbg!(req); + let req_data = req.into_inner(); + dbg!(&req_data); + + // let containers = self.data.find_app_contracts_by_admin_pubkey(&req_data.admin_pubkey).into(); Ok(Response::new(ContainerListResp { ..Default::default() })) } + */ + // async fn inspect_container( // &self, // req: tonic::Request, @@ -337,16 +345,13 @@ impl BrainSgxCli for BrainSgxCliMock { } #[tonic::async_trait] -impl BrainSgxDaemon for BrainSgxDaemonMock { - type RegisterNodeStream = Pin< - Box> + Send>, - >; - type BrainMessagesStream = - Pin> + Send>>; +impl BrainAppDaemon for BrainAppDaemonMock { + type RegisterNodeStream = Pin> + Send>>; + type BrainMessagesStream = Pin> + Send>>; async fn register_node( &self, - req: tonic::Request, + req: tonic::Request, ) -> Result, Status> { let req_data = req.into_inner(); log::info!( @@ -378,24 +383,25 @@ impl BrainSgxDaemon for BrainSgxDaemonMock { let _ = tx.send(contract.into()).await; } }); - let output_stream = ReceiverStream::new(rx).map(|msg| Ok(msg)); + let output_stream = ReceiverStream::new(rx).map(Ok); Ok(Response::new(Box::pin(output_stream))) } async fn brain_messages( &self, - req: tonic::Request, + req: tonic::Request, ) -> Result, Status> { let req = req.into_inner(); info!("Daemon {} connected to receive brain messages", req.pubkey); let (tx, rx) = mpsc::channel(6); self.data.add_app_daemon_tx(&req.pubkey, tx); - let output_stream = ReceiverStream::new(rx).map(|msg| Ok(msg)); + let output_stream = ReceiverStream::new(rx).map(Ok); Ok(Response::new( Box::pin(output_stream) as Self::BrainMessagesStream )) } + /* async fn daemon_messages( &self, req: tonic::Request>, @@ -426,4 +432,5 @@ impl BrainSgxDaemon for BrainSgxDaemonMock { Ok(Response::new(detee_shared::pb::shared::Empty {})) } + */ } diff --git a/src/main.rs b/src/main.rs index 7305cc0..18a4f00 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,18 +2,17 @@ mod data; mod grpc; use data::BrainData; -use detee_shared::pb::daemon::brain_sgx_daemon_server::BrainSgxDaemonServer; +use detee_shared::pb::brain::brain_app_cli_server::BrainAppCliServer; +use detee_shared::pb::brain::brain_app_daemon_server::BrainAppDaemonServer; use grpc::snp_proto::brain_cli_server::BrainCliServer; use grpc::snp_proto::brain_daemon_server::BrainDaemonServer; +use grpc::BrainAppCliMock; +use grpc::BrainAppDaemonMock; use grpc::BrainCliMock; use grpc::BrainDaemonMock; -use grpc::BrainSgxCliMock; -use grpc::BrainSgxDaemonMock; use std::sync::Arc; use tonic::transport::Server; -use detee_shared::pb::daemon::brain_sgx_cli_server::BrainSgxCliServer; - #[tokio::main] async fn main() { env_logger::builder() @@ -32,8 +31,8 @@ async fn main() { let daemon_server = BrainDaemonServer::new(BrainDaemonMock::new(data.clone())); let cli_server = BrainCliServer::new(BrainCliMock::new(data.clone())); - let sgx_cli_server = BrainSgxCliServer::new(BrainSgxCliMock::new(data.clone())); - let sgx_daemon_server = BrainSgxDaemonServer::new(BrainSgxDaemonMock::new(data.clone())); + let sgx_cli_server = BrainAppCliServer::new(BrainAppCliMock::new(data.clone())); + let sgx_daemon_server = BrainAppDaemonServer::new(BrainAppDaemonMock::new(data.clone())); Server::builder() .add_service(daemon_server) -- 2.43.0 From c7b4d408461173319c7dbbd4f55e437d9db47f19 Mon Sep 17 00:00:00 2001 From: Noor Date: Wed, 5 Feb 2025 12:08:48 +0530 Subject: [PATCH 07/29] delete app and list contracts --- Cargo.lock | 1 + src/grpc.rs | 73 +++++++++++++++++++++++++---------------------------- 2 files changed, 36 insertions(+), 38 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index deb88e7..218065f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -329,6 +329,7 @@ dependencies = [ [[package]] name = "detee-shared" version = "0.1.0" +source = "git+ssh://git@gitea.detee.cloud/noormohammedb/detee-shared#7f431e7180ef77e6f66f434866bebc90608a9c82" dependencies = [ "base64", "prost", diff --git a/src/grpc.rs b/src/grpc.rs index e56b9c4..d195dcb 100644 --- a/src/grpc.rs +++ b/src/grpc.rs @@ -18,7 +18,8 @@ use tonic::{Request, Response, Status, Streaming}; use detee_shared::pb::brain::brain_app_cli_server::BrainAppCli; use detee_shared::pb::brain::brain_app_daemon_server::BrainAppDaemon; use detee_shared::pb::brain::{ - AppContract, BrainMessageApp, NewAppReq, NewAppRes, RegisterAppNodeReq, + AppContract, BrainMessageApp, DaemonMessageApp, DelAppReq, ListAppContractsReq, NewAppReq, + NewAppRes, RegisterAppNodeReq, }; pub struct BrainDaemonMock { @@ -275,6 +276,8 @@ impl BrainCli for BrainCliMock { #[tonic::async_trait] impl BrainAppCli for BrainAppCliMock { + type ListAppContractsStream = Pin> + Send>>; + async fn create_app( &self, req: tonic::Request, @@ -299,49 +302,42 @@ impl BrainAppCli for BrainAppCliMock { } } - /* - - async fn delete_container( + async fn delete_app( &self, - req: tonic::Request, - ) -> Result, Status> { + req: tonic::Request, + ) -> Result, Status> { let req = req.into_inner(); - log::info!( - "deleting container: {}", - req.uuid.clone().unwrap_or_default() - ); + log::info!("deleting container: {}", req.uuid.clone()); if let Err(er) = self.data.send_del_container_req(req).await { info!("Could not delete container: {er}"); return Err(Status::not_found("Could not find container")); }; - Ok(Response::new(detee_shared::pb::shared::Empty {})) + Ok(Response::new(detee_shared::pb::brain::Empty {})) } - async fn list_containers( + async fn list_app_contracts( &self, - req: tonic::Request, - ) -> Result, Status> { + req: tonic::Request, + ) -> Result, Status> { let req_data = req.into_inner(); dbg!(&req_data); - // let containers = self.data.find_app_contracts_by_admin_pubkey(&req_data.admin_pubkey).into(); - Ok(Response::new(ContainerListResp { - ..Default::default() - })) + let app_contracts = self + .data + .find_app_contracts_by_admin_pubkey(&req_data.admin_pubkey); + + let (tx, rx) = mpsc::channel(6); + tokio::spawn(async move { + for contract in app_contracts { + let _ = tx.send(contract.into()).await; + } + }); + let output_stream = ReceiverStream::new(rx).map(Ok); + Ok(Response::new( + Box::pin(output_stream) as Self::ListAppContractsStream + )) } - - */ - - // async fn inspect_container( - // &self, - // req: tonic::Request, - // ) -> Result, Status> { - // dbg!(req); - // Ok(Response::new(ContainerInspectResp { - // ..Default::default() - // })) - // } } #[tonic::async_trait] @@ -401,23 +397,25 @@ impl BrainAppDaemon for BrainAppDaemonMock { )) } - /* async fn daemon_messages( &self, - req: tonic::Request>, - ) -> Result, Status> { + req: tonic::Request>, + ) -> Result, Status> { let mut req_stream = req.into_inner(); let mut pubkey = String::new(); while let Some(daemon_message) = req_stream.next().await { match daemon_message { Ok(msg) => match msg.msg { - Some(sgx_daemon_message::Msg::Pubkey(something)) => { - pubkey = something.pubkey; + Some(detee_shared::pb::brain::daemon_message_app::Msg::Pubkey(node_pubkey)) => { + pubkey = node_pubkey; } - Some(sgx_daemon_message::Msg::NewContainerResp(new_cont)) => { + Some(detee_shared::pb::brain::daemon_message_app::Msg::NewAppRes(new_cont)) => { self.data.send_new_container_resp(new_cont).await; } + Some(detee_shared::pb::brain::daemon_message_app::Msg::AppNodeResources(_)) => { + todo!("AppNodeResources not implemented yet"); + } None => { dbg!("None"); } @@ -430,7 +428,6 @@ impl BrainAppDaemon for BrainAppDaemonMock { // } - Ok(Response::new(detee_shared::pb::shared::Empty {})) + Ok(Response::new(detee_shared::pb::brain::Empty {})) } - */ } -- 2.43.0 From f4f16228fcc3bd47c0a8d35ef76dd276b0af0017 Mon Sep 17 00:00:00 2001 From: Noor Date: Mon, 10 Feb 2025 13:52:49 +0530 Subject: [PATCH 08/29] update detee-shared dependency to stable branch and add TODO for daemon offline handling --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/data.rs | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 218065f..2a4dabf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -329,7 +329,7 @@ dependencies = [ [[package]] name = "detee-shared" version = "0.1.0" -source = "git+ssh://git@gitea.detee.cloud/noormohammedb/detee-shared#7f431e7180ef77e6f66f434866bebc90608a9c82" +source = "git+ssh://git@gitea.detee.cloud/noormohammedb/detee-shared?branch=stable_01#fce57884937a4ec02acbf2f5b370ab879b1af657" dependencies = [ "base64", "prost", diff --git a/Cargo.toml b/Cargo.toml index fe8407d..d602305 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ tokio-stream = "0.1.17" tonic = "0.12" uuid = { version = "1.11.0", features = ["v4"] } -detee-shared = { git = "ssh://git@gitea.detee.cloud/noormohammedb/detee-shared" } +detee-shared = { git = "ssh://git@gitea.detee.cloud/noormohammedb/detee-shared", branch = "stable_01" } # detee-shared = { path = "../detee-shared" } [build-dependencies] diff --git a/src/data.rs b/src/data.rs index 7632bb9..2909ecb 100644 --- a/src/data.rs +++ b/src/data.rs @@ -824,6 +824,7 @@ impl BrainData { .await; } } + // TODO: implement daemon offline handling } pub async fn send_del_container_req( -- 2.43.0 From b22b08799bb7011ff06d040b3ffdb72f75d3ff7d Mon Sep 17 00:00:00 2001 From: Noor Date: Mon, 10 Feb 2025 17:09:54 +0530 Subject: [PATCH 09/29] Merge branch 'main' into sgx_brain --- Cargo.lock | 480 ++++++++++++++++++++++++++++++++---------- Cargo.toml | 2 + build.rs | 2 +- src/data.rs | 347 ++++++++++++++++-------------- src/grpc.rs | 374 +++++++++++++++++++++++++++----- src/main.rs | 6 +- snp.proto => vm.proto | 82 +++++--- 7 files changed, 928 insertions(+), 365 deletions(-) rename snp.proto => vm.proto (67%) diff --git a/Cargo.lock b/Cargo.lock index 2a4dabf..76631e4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -82,11 +82,12 @@ dependencies = [ [[package]] name = "anstyle-wincon" -version = "3.0.6" +version = "3.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" +checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" dependencies = [ "anstyle", + "once_cell", "windows-sys 0.59.0", ] @@ -120,9 +121,9 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.83" +version = "0.1.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" +checksum = "644dd749086bf3771a2fbc5f256fdb982d53f011c7d5d560304eafeecebce79d" dependencies = [ "proc-macro2", "quote", @@ -210,18 +211,35 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] -name = "bitflags" -version = "2.6.0" +name = "base64ct" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[package]] +name = "bitflags" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] [[package]] name = "brain-mock" version = "0.1.0" dependencies = [ + "bs58", "chrono", "dashmap", "detee-shared", + "ed25519-dalek", "env_logger", "log", "prost", @@ -238,10 +256,19 @@ dependencies = [ ] [[package]] -name = "bumpalo" -version = "3.16.0" +name = "bs58" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "bumpalo" +version = "3.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" [[package]] name = "byteorder" @@ -251,15 +278,15 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" +checksum = "f61dac84819c6588b558454b194026eb1f09c293b9036ae9b159e74e73ab6cf9" [[package]] name = "cc" -version = "1.2.5" +version = "1.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c31a0499c1dc64f458ad13872de75c0eb7e3fdb0e67964610c914b034fc5956e" +checksum = "c7777341816418c02e033934a09f20dc0ccaf65a5201ef8a450ae0105a573fda" dependencies = [ "shlex", ] @@ -290,6 +317,12 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + [[package]] name = "core-foundation" version = "0.9.4" @@ -306,12 +339,58 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + [[package]] name = "crossbeam-utils" version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "digest", + "fiat-crypto", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "dashmap" version = "6.1.0" @@ -326,6 +405,16 @@ dependencies = [ "parking_lot_core", ] +[[package]] +name = "der" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" +dependencies = [ + "const-oid", + "zeroize", +] + [[package]] name = "detee-shared" version = "0.1.0" @@ -339,6 +428,16 @@ dependencies = [ "tonic-build", ] +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + [[package]] name = "displaydoc" version = "0.2.5" @@ -350,6 +449,30 @@ dependencies = [ "syn", ] +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8", + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" +dependencies = [ + "curve25519-dalek", + "ed25519", + "serde", + "sha2", + "subtle", + "zeroize", +] + [[package]] name = "either" version = "1.13.0" @@ -410,6 +533,12 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + [[package]] name = "fixedbitset" version = "0.4.2" @@ -485,6 +614,16 @@ dependencies = [ "pin-utils", ] +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + [[package]] name = "getrandom" version = "0.2.15" @@ -493,7 +632,19 @@ checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.13.3+wasi-0.2.2", + "windows-targets", ] [[package]] @@ -514,7 +665,7 @@ dependencies = [ "futures-core", "futures-sink", "http", - "indexmap 2.7.0", + "indexmap 2.7.1", "slab", "tokio", "tokio-util", @@ -581,9 +732,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.9.5" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" +checksum = "f2d708df4e7140240a16cd6ab0ab65c972d7433ab77819ea693fde9c43811e2a" [[package]] name = "httpdate" @@ -599,9 +750,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "1.5.2" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "256fb8d4bd6413123cc9d91832d78325c48ff41677595be797d90f42969beae0" +checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" dependencies = [ "bytes", "futures-channel", @@ -857,9 +1008,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.7.0" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" +checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" dependencies = [ "equivalent", "hashbrown 0.15.2", @@ -867,9 +1018,9 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.10.1" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" [[package]] name = "is_terminal_polyfill" @@ -894,9 +1045,9 @@ checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" [[package]] name = "js-sys" -version = "0.3.76" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" dependencies = [ "once_cell", "wasm-bindgen", @@ -904,9 +1055,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.168" +version = "0.2.169" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" [[package]] name = "libyml" @@ -920,9 +1071,9 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.4.14" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] name = "litemap" @@ -942,9 +1093,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.22" +version = "0.4.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" [[package]] name = "matchit" @@ -966,9 +1117,9 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "miniz_oxide" -version = "0.8.0" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +checksum = "b8402cab7aefae129c6977bb0ff1b8fd9a04eb5b51efc50a70bea51cda0c7924" dependencies = [ "adler2", ] @@ -980,7 +1131,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" dependencies = [ "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", "windows-sys 0.52.0", ] @@ -992,9 +1143,9 @@ checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03" [[package]] name = "native-tls" -version = "0.2.12" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" +checksum = "0dab59f8e050d5df8e4dd87d9206fb6f65a483e20ac9fda365ade4fab353196c" dependencies = [ "libc", "log", @@ -1018,24 +1169,24 @@ dependencies = [ [[package]] name = "object" -version = "0.36.5" +version = "0.36.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.20.2" +version = "1.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" +checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" [[package]] name = "openssl" -version = "0.10.68" +version = "0.10.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5" +checksum = "61cfb4e166a8bb8c9b55c500bc2308550148ece889be90f609377e58140f42c6" dependencies = [ "bitflags", "cfg-if", @@ -1059,15 +1210,15 @@ dependencies = [ [[package]] name = "openssl-probe" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] name = "openssl-sys" -version = "0.9.104" +version = "0.9.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741" +checksum = "8b22d5b84be05a8d6947c7cb71f7c849aa0f112acd4bf51c2a7c1c988ac0a9dc" dependencies = [ "cc", "libc", @@ -1101,23 +1252,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset", - "indexmap 2.7.0", + "indexmap 2.7.1", ] [[package]] name = "pin-project" -version = "1.1.7" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be57f64e946e500c8ee36ef6331845d40a93055567ec57e8fae13efd33759b95" +checksum = "dfe2e71e1471fe07709406bf725f710b02927c9c54b2b5b2ec0e8087d97c327d" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.7" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c" +checksum = "f6e859e6e5bd50440ab63c47e3ebabc90f26251f7c73c3d3e837b74a1cc3fa67" dependencies = [ "proc-macro2", "quote", @@ -1126,9 +1277,9 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" [[package]] name = "pin-utils" @@ -1136,6 +1287,16 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + [[package]] name = "pkg-config" version = "0.3.31" @@ -1153,9 +1314,9 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.2.25" +version = "0.2.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033" +checksum = "6924ced06e1f7dfe3fa48d57b9f74f55d8915f5036121bef647ef4b204895fac" dependencies = [ "proc-macro2", "syn", @@ -1163,9 +1324,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.92" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" dependencies = [ "unicode-ident", ] @@ -1224,9 +1385,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.37" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" dependencies = [ "proc-macro2", ] @@ -1258,7 +1419,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom", + "getrandom 0.2.15", ] [[package]] @@ -1301,9 +1462,9 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "reqwest" -version = "0.12.10" +version = "0.12.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d3536321cfc54baa8cf3e273d5e1f63f889067829c4b410fcdbac8ca7b80994" +checksum = "43e734407157c3c2034e0258f5e4473ddb361b1e85f95a66690d67264d7cd1da" dependencies = [ "base64", "bytes", @@ -1351,7 +1512,7 @@ checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" dependencies = [ "cc", "cfg-if", - "getrandom", + "getrandom 0.2.15", "libc", "spin", "untrusted", @@ -1365,10 +1526,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] -name = "rustix" -version = "0.38.42" +name = "rustc_version" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "0.38.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ "bitflags", "errno", @@ -1379,9 +1549,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.20" +version = "0.23.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5065c3f250cbd332cd894be57c40fa52387247659b14a2d6041d121547903b1b" +checksum = "9fb9263ab4eb695e42321db096e3b8fbd715a59b154d5c88d82db2175b681ba7" dependencies = [ "once_cell", "rustls-pki-types", @@ -1401,9 +1571,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2bf47e6ff922db3825eb750c4e2ff784c6ff8fb9e13046ef6a1d1c5401b0b37" +checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c" [[package]] name = "rustls-webpki" @@ -1418,15 +1588,15 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.18" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" +checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" [[package]] name = "ryu" -version = "1.0.18" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd" [[package]] name = "schannel" @@ -1458,28 +1628,34 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.13.0" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1863fd3768cd83c56a7f60faa4dc0d403f1b6df0a38c3c25f44b7894e45370d5" +checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" dependencies = [ "core-foundation-sys", "libc", ] [[package]] -name = "serde" -version = "1.0.216" +name = "semver" +version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e" +checksum = "f79dfe2d285b0488816f30e700a7438c5a73d816b5b7d3ac72fbc48b0d185e03" + +[[package]] +name = "serde" +version = "1.0.217" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.216" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e" +checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" dependencies = [ "proc-macro2", "quote", @@ -1488,9 +1664,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.134" +version = "1.0.138" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d00f4175c42ee48b15416f6193a959ba3a0d67fc699a0db9ad12df9f83991c7d" +checksum = "d434192e7da787e94a6ea7e9670b26a036d0ca41e0b7efb2676dd32bae872949" dependencies = [ "itoa", "memchr", @@ -1516,7 +1692,7 @@ version = "0.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59e2dd588bf1597a252c3b920e0143eb99b0f76e4e082f4c92ce34fbc9e71ddd" dependencies = [ - "indexmap 2.7.0", + "indexmap 2.7.1", "itoa", "libyml", "memchr", @@ -1525,12 +1701,32 @@ dependencies = [ "version_check", ] +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "shlex" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "rand_core", +] + [[package]] name = "slab" version = "0.4.9" @@ -1562,6 +1758,16 @@ version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + [[package]] name = "stable_deref_trait" version = "1.2.0" @@ -1576,9 +1782,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.90" +version = "2.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" +checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" dependencies = [ "proc-macro2", "quote", @@ -1628,12 +1834,13 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.14.0" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" +checksum = "38c246215d7d24f48ae091a2902398798e05d978b24315d6efbc00ede9a8bb91" dependencies = [ "cfg-if", "fastrand", + "getrandom 0.3.1", "once_cell", "rustix", "windows-sys 0.59.0", @@ -1670,10 +1877,25 @@ dependencies = [ ] [[package]] -name = "tokio" -version = "1.42.0" +name = "tinyvec" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551" +checksum = "022db8904dfa342efe721985167e9fcd16c29b226db4397ed752a761cfce81e8" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.43.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e" dependencies = [ "backtrace", "bytes", @@ -1687,9 +1909,9 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", @@ -1869,10 +2091,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] -name = "unicode-ident" -version = "1.0.14" +name = "typenum" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicode-ident" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034" [[package]] name = "untrusted" @@ -1911,11 +2139,11 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.11.0" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" +checksum = "ced87ca4be083373936a67f8de945faa23b6b42384bd5b64434850802c6dccd0" dependencies = [ - "getrandom", + "getrandom 0.3.1", ] [[package]] @@ -1946,21 +2174,31 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] -name = "wasm-bindgen" -version = "0.2.99" +name = "wasi" +version = "0.13.3+wasi-0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396" +checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" +dependencies = [ + "wit-bindgen-rt", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ "cfg-if", "once_cell", + "rustversion", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" dependencies = [ "bumpalo", "log", @@ -1972,9 +2210,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.49" +version = "0.4.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38176d9b44ea84e9184eff0bc34cc167ed044f816accfe5922e54d84cf48eca2" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" dependencies = [ "cfg-if", "js-sys", @@ -1985,9 +2223,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1995,9 +2233,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", @@ -2008,15 +2246,18 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] [[package]] name = "web-sys" -version = "0.3.76" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04dd7223427d52553d3702c004d3b2fe07c148165faa56313cb00211e31c12bc" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" dependencies = [ "js-sys", "wasm-bindgen", @@ -2143,6 +2384,15 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "wit-bindgen-rt" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" +dependencies = [ + "bitflags", +] + [[package]] name = "write16" version = "1.0.0" diff --git a/Cargo.toml b/Cargo.toml index d602305..810cbfd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,8 +4,10 @@ version = "0.1.0" edition = "2021" [dependencies] +bs58 = "0.5.1" chrono = "0.4.39" dashmap = "6.1.0" +ed25519-dalek = "2.1.1" env_logger = "0.11.6" log = "0.4.22" prost = "0.13.4" diff --git a/build.rs b/build.rs index d1b9140..469b7d1 100644 --- a/build.rs +++ b/build.rs @@ -1,6 +1,6 @@ fn main() { tonic_build::configure() .build_server(true) - .compile_protos(&["snp.proto"], &["proto"]) + .compile_protos(&["vm.proto"], &["proto"]) .unwrap_or_else(|e| panic!("Failed to compile protos {:?}", e)); } diff --git a/src/data.rs b/src/data.rs index 2909ecb..aaaf130 100644 --- a/src/data.rs +++ b/src/data.rs @@ -1,4 +1,3 @@ -#![allow(dead_code)] use crate::grpc::snp_proto::{self as grpc}; use chrono::Utc; use dashmap::DashMap; @@ -17,24 +16,22 @@ use detee_shared::pb::brain::NewAppRes; #[derive(thiserror::Error, Debug)] pub enum Error { - #[error("We do not allow locking of more than 100000 tokens.")] + #[error("We do not allow locking of more than 100000 LP.")] TxTooBig, #[error("Account has insufficient funds for this operation")] InsufficientFunds, - #[error("I have no idea how this happened. Please report this bug.")] - ImpossibleError, #[error("Could not find contract {0}")] - ContractNotFound(String), + VmContractNotFound(String), } #[derive(Clone)] -pub struct AccountNanoTokens { +pub struct AccountNanoLP { pub balance: u64, pub tmp_locked: u64, } -impl From for grpc::AccountBalance { - fn from(value: AccountNanoTokens) -> Self { +impl From for grpc::AccountBalance { + fn from(value: AccountNanoLP) -> Self { grpc::AccountBalance { balance: value.balance, tmp_locked: value.tmp_locked, @@ -43,7 +40,7 @@ impl From for grpc::AccountBalance { } #[derive(Eq, Hash, PartialEq, Clone, Debug, Default)] -pub struct Node { +pub struct VmNode { pub public_key: String, pub owner_key: String, pub country: String, @@ -57,13 +54,13 @@ pub struct Node { pub avail_ipv6: u32, pub avail_ports: u32, pub max_ports_per_vm: u32, - // nanotokens per unit per minute + // nanoLP per unit per minute pub price: u64, } -impl Into for Node { - fn into(self) -> grpc::NodeListResp { - grpc::NodeListResp { +impl Into for VmNode { + fn into(self) -> grpc::VmNodeListResp { + grpc::VmNodeListResp { node_pubkey: self.public_key, country: self.country, region: self.region, @@ -77,7 +74,7 @@ impl Into for Node { } #[derive(Clone, Debug)] -pub struct Contract { +pub struct VmContract { pub uuid: String, pub hostname: String, pub admin_pubkey: String, @@ -99,27 +96,27 @@ pub struct Contract { pub collected_at: chrono::DateTime, } -impl Contract { +impl VmContract { fn total_units(&self) -> u64 { // TODO: Optimize this based on price of hardware. // I tried, but this can be done better. // Storage cost should also be based on tier (self.vcpus as u64 * 10) - + ((self.memory_mb + 256) as u64 * 4 / 100) + + ((self.memory_mb + 256) as u64 / 200) + (self.disk_size_gb as u64 / 10) + (!self.public_ipv4.is_empty() as u64 * 10) } - // Returns price per minute in nanotokens + // Returns price per minute in nanoLP fn price_per_minute(&self) -> u64 { self.total_units() * self.price_per_unit } } -impl Into for Contract { - fn into(self) -> grpc::Contract { +impl Into for VmContract { + fn into(self) -> grpc::VmContract { let nano_per_minute = self.price_per_minute(); - grpc::Contract { + grpc::VmContract { uuid: self.uuid, hostname: self.hostname, admin_pubkey: self.admin_pubkey, @@ -197,13 +194,13 @@ pub struct AppNode { #[derive(Default)] pub struct BrainData { - // amount of nanotokens in each account - accounts: DashMap, - nodes: RwLock>, - contracts: RwLock>, + // amount of nanoLP in each account + accounts: DashMap, + vm_nodes: RwLock>, + vm_contracts: RwLock>, tmp_newvm_reqs: DashMap)>, tmp_updatevm_reqs: DashMap)>, - daemon_tx: DashMap>, + daemon_tx: DashMap>, app_nodes: RwLock>, app_daemon_tx: DashMap>, @@ -211,19 +208,12 @@ pub struct BrainData { app_contracts: RwLock>, } -#[derive(Debug)] -enum TxType { - CliContract, - DaemonDeleteVm, - DaemonNewVm, -} - impl BrainData { pub fn new() -> Self { Self { accounts: DashMap::new(), - nodes: RwLock::new(Vec::new()), - contracts: RwLock::new(Vec::new()), + vm_nodes: RwLock::new(Vec::new()), + vm_contracts: RwLock::new(Vec::new()), tmp_newvm_reqs: DashMap::new(), tmp_updatevm_reqs: DashMap::new(), daemon_tx: DashMap::new(), @@ -235,11 +225,11 @@ impl BrainData { } } - pub fn get_balance(&self, account: &str) -> AccountNanoTokens { + pub fn get_balance(&self, account: &str) -> AccountNanoLP { if let Some(account) = self.accounts.get(account) { return account.value().clone(); } else { - let balance = AccountNanoTokens { + let balance = AccountNanoLP { balance: 0, tmp_locked: 0, }; @@ -247,54 +237,69 @@ impl BrainData { } } - pub fn get_airdrop(&self, account: &str) { - self.add_nano_to_wallet(account, 1000_000000000); + pub fn give_airdrop(&self, account: &str, tokens: u64) { + self.add_nano_to_wallet(account, tokens.saturating_mul(1_000_000_000)); } - fn add_nano_to_wallet(&self, account: &str, nanotokens: u64) { - log::debug!("Adding {nanotokens} nanotokens to {account}"); + fn add_nano_to_wallet(&self, account: &str, nano_lp: u64) { + log::debug!("Adding {nano_lp} nanoLP to {account}"); self.accounts .entry(account.to_string()) - .and_modify(|tokens| tokens.balance += nanotokens) - .or_insert(AccountNanoTokens { - balance: nanotokens, + .and_modify(|d| d.balance += nano_lp) + .or_insert(AccountNanoLP { + balance: nano_lp, tmp_locked: 0, }); } - pub fn contracts_cron(&self) { + pub async fn vm_contracts_cron(&self) { + let mut deleted_contracts: Vec<(String, String)> = Vec::new(); log::debug!("Running contracts cron..."); - let mut contracts = self.contracts.write().unwrap(); - contracts.retain_mut(|c| { - let owner_key = self - .find_nodes_by_pubkey(&c.node_pubkey) - .unwrap() - .owner_key - .clone(); - let minutes_to_collect = (Utc::now() - c.collected_at).num_minutes() as u64; - c.collected_at = Utc::now(); - log::debug!("{minutes_to_collect}"); - let mut nanotokens_to_collect = c.price_per_minute().saturating_mul(minutes_to_collect); - if nanotokens_to_collect > c.locked_nano { - nanotokens_to_collect = c.locked_nano; + { + let mut contracts = self.vm_contracts.write().unwrap(); + contracts.retain_mut(|c| { + let owner_key = self + .find_nodes_by_pubkey(&c.node_pubkey) + .unwrap() + .owner_key + .clone(); + let minutes_to_collect = (Utc::now() - c.collected_at).num_minutes() as u64; + c.collected_at = Utc::now(); + let mut nanolp_to_collect = c.price_per_minute().saturating_mul(minutes_to_collect); + if nanolp_to_collect > c.locked_nano { + nanolp_to_collect = c.locked_nano; + } + log::debug!("Removing {nanolp_to_collect} nanoLP from {}", c.uuid); + c.locked_nano -= nanolp_to_collect; + self.add_nano_to_wallet(&owner_key, nanolp_to_collect); + if c.locked_nano == 0 { + deleted_contracts.push((c.uuid.clone(), c.node_pubkey.clone())); + } + c.locked_nano > 0 + }); + } + // inform daemons of the deletion of the contracts + for (uuid, node_pubkey) in deleted_contracts.iter() { + if let Some(daemon_tx) = self.daemon_tx.get(&node_pubkey.clone()) { + let msg = grpc::BrainVmMessage { + msg: Some(grpc::brain_vm_message::Msg::DeleteVm(grpc::DeleteVmReq { + uuid: uuid.to_string(), + admin_pubkey: String::new(), + })), + }; + let daemon_tx = daemon_tx.clone(); + let _ = daemon_tx.send(msg).await; } - log::debug!( - "Removing {nanotokens_to_collect} nanotokens from {}", - c.uuid - ); - c.locked_nano -= nanotokens_to_collect; - self.add_nano_to_wallet(&owner_key, nanotokens_to_collect); - c.locked_nano > 0 - }); + } } - pub fn insert_node(&self, node: Node) { + pub fn insert_node(&self, node: VmNode) { info!("Registering node {node:?}"); - let mut nodes = self.nodes.write().unwrap(); + let mut nodes = self.vm_nodes.write().unwrap(); for n in nodes.iter_mut() { if n.public_key == node.public_key { // TODO: figure what to do in this case. - warn!("Node {} already exists. Updating data.", n.public_key); + warn!("VM Node {} already exists. Updating data.", n.public_key); *n = node; return; } @@ -302,69 +307,59 @@ impl BrainData { nodes.push(node); } - pub fn lock_nanotockens(&self, account: &str, nanotokens: u64) -> Result<(), Error> { - if nanotokens > 100_000_000_000_000 { + pub fn lock_nanotockens(&self, account: &str, nano_lp: u64) -> Result<(), Error> { + if nano_lp > 100_000_000_000_000 { return Err(Error::TxTooBig); } if let Some(mut account) = self.accounts.get_mut(account) { - if nanotokens > account.balance { + if nano_lp > account.balance { return Err(Error::InsufficientFunds); } - account.balance = account.balance.saturating_sub(nanotokens); - account.tmp_locked = account.tmp_locked.saturating_add(nanotokens); + account.balance = account.balance.saturating_sub(nano_lp); + account.tmp_locked = account.tmp_locked.saturating_add(nano_lp); Ok(()) } else { Err(Error::InsufficientFunds) } } - pub fn unlock_nanotockens(&self, account: &str, nanotokens: u64) -> Result<(), Error> { - if let Some(mut account) = self.accounts.get_mut(account) { - if nanotokens > account.tmp_locked { - return Err(Error::ImpossibleError); - } - account.balance = account.balance.saturating_add(nanotokens); - account.tmp_locked = account.tmp_locked.saturating_sub(nanotokens); - Ok(()) - } else { - Err(Error::ImpossibleError) - } - } - - pub fn extend_contract_time( + pub fn extend_vm_contract_time( &self, uuid: &str, - account: &str, - nanotokens: u64, + wallet: &str, + nano_lp: u64, ) -> Result<(), Error> { - if nanotokens > 100_000_000_000_000 { + if nano_lp > 100_000_000_000_000 { return Err(Error::TxTooBig); } - let mut account = match self.accounts.get_mut(account) { + let mut account = match self.accounts.get_mut(wallet) { Some(account) => account, None => return Err(Error::InsufficientFunds), }; match self - .contracts + .vm_contracts .write() .unwrap() .iter_mut() .find(|c| c.uuid == uuid) { Some(contract) => { - if account.balance + contract.locked_nano < nanotokens { + if contract.admin_pubkey != wallet { + return Err(Error::VmContractNotFound(uuid.to_string())); + } + if account.balance + contract.locked_nano < nano_lp { return Err(Error::InsufficientFunds); } - account.balance = account.balance + contract.locked_nano - nanotokens; - contract.locked_nano = nanotokens; + account.balance = account.balance + contract.locked_nano - nano_lp; + contract.locked_nano = nano_lp; Ok(()) } - None => Err(Error::ContractNotFound(uuid.to_string())), + None => Err(Error::VmContractNotFound(uuid.to_string())), } } - pub fn submit_node_resources(&self, res: grpc::NodeResources) { - let mut nodes = self.nodes.write().unwrap(); + pub fn submit_node_resources(&self, res: grpc::VmNodeResources) { + let mut nodes = self.vm_nodes.write().unwrap(); for n in nodes.iter_mut() { if n.public_key == res.node_pubkey { debug!( @@ -382,13 +377,13 @@ impl BrainData { } } debug!( - "Node {} not found when trying to update resources.", + "VM Node {} not found when trying to update resources.", res.node_pubkey ); - debug!("Node list:\n{:?}", nodes); + debug!("VM Node list:\n{:?}", nodes); } - pub fn add_daemon_tx(&self, node_pubkey: &str, tx: Sender) { + pub fn add_daemon_tx(&self, node_pubkey: &str, tx: Sender) { self.daemon_tx.insert(node_pubkey.to_string(), tx); } @@ -396,33 +391,44 @@ impl BrainData { self.daemon_tx.remove(node_pubkey); } - pub async fn delete_vm(&self, delete_vm: grpc::DeleteVmReq) { - if let Some(contract) = self.find_contract_by_uuid(&delete_vm.uuid) { - info!("Found vm {}. Deleting...", delete_vm.uuid); - if let Some(daemon_tx) = self.daemon_tx.get(&contract.node_pubkey) { - debug!( - "TX for daemon {} found. Informing daemon about deletion of {}.", - contract.node_pubkey, delete_vm.uuid - ); - let msg = grpc::BrainMessage { - msg: Some(grpc::brain_message::Msg::DeleteVm(delete_vm.clone())), - }; - if let Err(e) = daemon_tx.send(msg).await { - warn!( - "Failed to send deletion request to {} due to error: {e:?}", - contract.node_pubkey - ); - info!("Deleting daemon TX for {}", contract.node_pubkey); - self.del_daemon_tx(&contract.node_pubkey); + pub async fn delete_vm(&self, delete_vm: grpc::DeleteVmReq) -> Result<(), Error> { + let contract = match self.find_contract_by_uuid(&delete_vm.uuid) { + Some(contract) => { + if contract.admin_pubkey != delete_vm.admin_pubkey { + return Err(Error::VmContractNotFound(delete_vm.uuid)); } + contract + } + None => { + return Err(Error::VmContractNotFound(delete_vm.uuid)); + } + }; + info!("Found vm {}. Deleting...", delete_vm.uuid); + if let Some(daemon_tx) = self.daemon_tx.get(&contract.node_pubkey) { + debug!( + "TX for daemon {} found. Informing daemon about deletion of {}.", + contract.node_pubkey, delete_vm.uuid + ); + let msg = grpc::BrainVmMessage { + msg: Some(grpc::brain_vm_message::Msg::DeleteVm(delete_vm.clone())), + }; + if let Err(e) = daemon_tx.send(msg).await { + warn!( + "Failed to send deletion request to {} due to error: {e:?}", + contract.node_pubkey + ); + info!("Deleting daemon TX for {}", contract.node_pubkey); + self.del_daemon_tx(&contract.node_pubkey); } - - self.add_nano_to_wallet(&contract.admin_pubkey, contract.locked_nano); - let mut contracts = self.contracts.write().unwrap(); - contracts.retain(|c| c.uuid != delete_vm.uuid); } + + self.add_nano_to_wallet(&contract.admin_pubkey, contract.locked_nano); + let mut contracts = self.vm_contracts.write().unwrap(); + contracts.retain(|c| c.uuid != delete_vm.uuid); + Ok(()) } + // also unlocks nanotokens in case VM creation failed pub async fn submit_newvm_resp(&self, new_vm_resp: grpc::NewVmResp) { let new_vm_req = match self.tmp_newvm_reqs.remove(&new_vm_resp.uuid) { Some((_, r)) => r, @@ -469,7 +475,7 @@ impl BrainData { admin_wallet.tmp_locked -= new_vm_req.0.locked_nano; } - let contract = Contract { + let contract = VmContract { uuid: new_vm_resp.uuid, exposed_ports: args.exposed_ports.clone(), public_ipv4, @@ -489,7 +495,7 @@ impl BrainData { collected_at: Utc::now(), }; info!("Created new contract: {contract:?}"); - self.contracts.write().unwrap().push(contract); + self.vm_contracts.write().unwrap().push(contract); } pub async fn submit_updatevm_resp(&self, mut update_vm_resp: grpc::UpdateVmResp) { @@ -508,7 +514,7 @@ impl BrainData { if update_vm_resp.error != "" { return; } - let mut contracts = self.contracts.write().unwrap(); + let mut contracts = self.vm_contracts.write().unwrap(); match contracts.iter_mut().find(|c| c.uuid == update_vm_resp.uuid) { Some(contract) => { if update_vm_req.0.vcpus != 0 { @@ -537,8 +543,8 @@ impl BrainData { contract.updated_at = Utc::now(); } None => { - log::error!("Contract not found for {}.", update_vm_req.0.uuid); - update_vm_resp.error = "Contract not found.".to_string(); + log::error!("VM Contract not found for {}.", update_vm_req.0.uuid); + update_vm_resp.error = "VM Contract not found.".to_string(); } } if let Err(e) = update_vm_req.1.send(update_vm_resp.clone()) { @@ -570,8 +576,8 @@ impl BrainData { "Found daemon TX for {}. Sending newVMReq {}", req.node_pubkey, req.uuid ); - let msg = grpc::BrainMessage { - msg: Some(grpc::brain_message::Msg::NewVmReq(req.clone())), + let msg = grpc::BrainVmMessage { + msg: Some(grpc::brain_vm_message::Msg::NewVmReq(req.clone())), }; if let Err(e) = daemon_tx.send(msg).await { warn!( @@ -587,6 +593,13 @@ impl BrainData { }) .await; } + } else { + self.submit_newvm_resp(grpc::NewVmResp { + error: "Daemon is offline.".to_string(), + uuid: req.uuid, + args: None, + }) + .await; } } @@ -598,7 +611,17 @@ impl BrainData { let uuid = req.uuid.clone(); info!("Inserting new vm update request in memory: {req:?}"); let node_pubkey = match self.find_contract_by_uuid(&req.uuid) { - Some(contract) => contract.node_pubkey, + Some(contract) => { + if contract.admin_pubkey != req.admin_pubkey { + let _ = tx.send(grpc::UpdateVmResp { + uuid, + error: "VM Contract does not exist.".to_string(), + args: None, + }); + return; + } + contract.node_pubkey + } None => { log::warn!( "Received UpdateVMReq for a contract that does not exist: {}", @@ -606,7 +629,7 @@ impl BrainData { ); let _ = tx.send(grpc::UpdateVmResp { uuid, - error: "Contract does not exist.".to_string(), + error: "VM Contract does not exist.".to_string(), args: None, }); return; @@ -619,8 +642,8 @@ impl BrainData { "Found daemon TX for {}. Sending updateVMReq {}", node_pubkey, req.uuid ); - let msg = grpc::BrainMessage { - msg: Some(grpc::brain_message::Msg::UpdateVmReq(req.clone())), + let msg = grpc::BrainVmMessage { + msg: Some(grpc::brain_vm_message::Msg::UpdateVmReq(req.clone())), }; match server_tx.send(msg).await { Ok(_) => { @@ -650,26 +673,16 @@ impl BrainData { } } - pub fn insert_contract(&self, contract: Contract) { - let mut contracts = self.contracts.write().unwrap(); - contracts.push(contract); - } - - pub fn find_nodes_by_pubkey(&self, public_key: &str) -> Option { - let nodes = self.nodes.read().unwrap(); + pub fn find_nodes_by_pubkey(&self, public_key: &str) -> Option { + let nodes = self.vm_nodes.read().unwrap(); nodes.iter().cloned().find(|n| n.public_key == public_key) } - pub fn find_ns_by_owner_key(&self, owner_key: &str) -> Option { - let nodes = self.nodes.read().unwrap(); - nodes.iter().cloned().find(|n| n.owner_key == owner_key) - } - pub fn find_nodes_by_filters( &self, - filters: &crate::grpc::snp_proto::NodeFilters, - ) -> Vec { - let nodes = self.nodes.read().unwrap(); + filters: &crate::grpc::snp_proto::VmNodeFilters, + ) -> Vec { + let nodes = self.vm_nodes.read().unwrap(); nodes .iter() .filter(|n| { @@ -691,9 +704,9 @@ impl BrainData { // TODO: sort by rating pub fn get_one_node_by_filters( &self, - filters: &crate::grpc::snp_proto::NodeFilters, - ) -> Option { - let nodes = self.nodes.read().unwrap(); + filters: &crate::grpc::snp_proto::VmNodeFilters, + ) -> Option { + let nodes = self.vm_nodes.read().unwrap(); nodes .iter() .find(|n| { @@ -712,15 +725,31 @@ impl BrainData { .cloned() } - pub fn find_contract_by_uuid(&self, uuid: &str) -> Option { - let contracts = self.contracts.read().unwrap(); + pub fn find_contract_by_uuid(&self, uuid: &str) -> Option { + let contracts = self.vm_contracts.read().unwrap(); contracts.iter().cloned().find(|c| c.uuid == uuid) } - pub fn find_contracts_by_admin_pubkey(&self, admin_pubkey: &str) -> Vec { + pub fn list_all_contracts(&self) -> Vec { + let contracts = self.vm_contracts.read().unwrap(); + contracts.iter().cloned().collect() + } + + pub fn list_accounts(&self) -> Vec { + self.accounts + .iter() + .map(|a| grpc::Account { + pubkey: a.key().to_string(), + balance: a.balance, + tmp_locked: a.tmp_locked, + }) + .collect() + } + + pub fn find_contracts_by_admin_pubkey(&self, admin_pubkey: &str) -> Vec { debug!("Searching contracts for admin pubkey {admin_pubkey}"); - let contracts: Vec = self - .contracts + let contracts: Vec = self + .vm_contracts .read() .unwrap() .iter() @@ -731,8 +760,8 @@ impl BrainData { contracts } - pub fn find_contracts_by_node_pubkey(&self, node_pubkey: &str) -> Vec { - let contracts = self.contracts.read().unwrap(); + pub fn find_contracts_by_node_pubkey(&self, node_pubkey: &str) -> Vec { + let contracts = self.vm_contracts.read().unwrap(); contracts .iter() .filter(|c| c.node_pubkey == node_pubkey) diff --git a/src/grpc.rs b/src/grpc.rs index d195dcb..6e69c69 100644 --- a/src/grpc.rs +++ b/src/grpc.rs @@ -1,13 +1,14 @@ #![allow(dead_code)] pub mod snp_proto { - tonic::include_proto!("snp_proto"); + tonic::include_proto!("vm_proto"); } use crate::data::BrainData; +use crate::grpc::vm_daemon_message; use log::info; use snp_proto::brain_cli_server::BrainCli; -use snp_proto::brain_daemon_server::BrainDaemon; +use snp_proto::brain_vm_daemon_server::BrainVmDaemon; use snp_proto::*; use std::pin::Pin; use std::sync::Arc; @@ -21,6 +22,10 @@ use detee_shared::pb::brain::{ AppContract, BrainMessageApp, DaemonMessageApp, DelAppReq, ListAppContractsReq, NewAppReq, NewAppRes, RegisterAppNodeReq, }; +const ADMIN_ACCOUNTS: &[&str] = &[ + "x52w7jARC5erhWWK65VZmjdGXzBK6ZDgfv1A283d8XK", + "FHuecMbeC1PfjkW2JKyoicJAuiU7khgQT16QUB3Q1XdL", +]; pub struct BrainDaemonMock { data: Arc, @@ -63,15 +68,15 @@ impl BrainAppDaemonMock { } #[tonic::async_trait] -impl BrainDaemon for BrainDaemonMock { - type RegisterNodeStream = Pin> + Send>>; - async fn register_node( +impl BrainVmDaemon for BrainDaemonMock { + type RegisterVmNodeStream = Pin> + Send>>; + async fn register_vm_node( &self, - req: Request, - ) -> Result, Status> { - let req = req.into_inner(); + req: Request, + ) -> Result, Status> { + let req = check_sig_from_req(req)?; info!("Starting registration process for {:?}", req); - let node = crate::data::Node { + let node = crate::data::VmNode { public_key: req.node_pubkey.clone(), owner_key: req.owner_pubkey, country: req.country, @@ -93,19 +98,26 @@ impl BrainDaemon for BrainDaemonMock { }); let output_stream = ReceiverStream::new(rx); Ok(Response::new( - Box::pin(output_stream) as Self::RegisterNodeStream + Box::pin(output_stream) as Self::RegisterVmNodeStream )) } - type BrainMessagesStream = Pin> + Send>>; + type BrainMessagesStream = Pin> + Send>>; async fn brain_messages( &self, - req: Request, + req: Request, ) -> Result, Status> { - let req = req.into_inner(); - info!("Daemon {} connected to receive brain messages", req.pubkey); + let auth = req.into_inner(); + let pubkey = auth.pubkey.clone(); + check_sig_from_parts( + &pubkey, + &auth.timestamp, + &format!("{:?}", auth.contracts), + &auth.signature, + )?; + info!("Daemon {} connected to receive brain messages", pubkey); let (tx, rx) = mpsc::channel(6); - self.data.add_daemon_tx(&req.pubkey, tx); + self.data.add_daemon_tx(&pubkey, tx); let output_stream = ReceiverStream::new(rx).map(|msg| Ok(msg)); Ok(Response::new( Box::pin(output_stream) as Self::BrainMessagesStream @@ -114,27 +126,46 @@ impl BrainDaemon for BrainDaemonMock { async fn daemon_messages( &self, - req: Request>, + req: Request>, ) -> Result, Status> { let mut req_stream = req.into_inner(); - let mut pubkey = String::new(); + let pubkey: String; + if let Some(Ok(msg)) = req_stream.next().await { + log::debug!( + "demon_messages received the following auth message: {:?}", + msg.msg + ); + if let Some(vm_daemon_message::Msg::Auth(auth)) = msg.msg { + pubkey = auth.pubkey.clone(); + check_sig_from_parts( + &pubkey, + &auth.timestamp, + &format!("{:?}", auth.contracts), + &auth.signature, + )?; + } else { + return Err(Status::unauthenticated( + "Could not authenticate the daemon: could not extract auth signature", + )); + } + } else { + return Err(Status::unauthenticated("Could not authenticate the daemon")); + } + + // info!("Received a message from daemon {pubkey}: {daemon_message:?}"); while let Some(daemon_message) = req_stream.next().await { - info!("Received a message from daemon {pubkey}: {daemon_message:?}"); match daemon_message { Ok(msg) => match msg.msg { - Some(daemon_message::Msg::Pubkey(p)) => { - pubkey = p.pubkey; - } - Some(daemon_message::Msg::NewVmResp(new_vm_resp)) => { + Some(vm_daemon_message::Msg::NewVmResp(new_vm_resp)) => { self.data.submit_newvm_resp(new_vm_resp).await; } - Some(daemon_message::Msg::UpdateVmResp(update_vm_resp)) => { + Some(vm_daemon_message::Msg::UpdateVmResp(update_vm_resp)) => { self.data.submit_updatevm_resp(update_vm_resp).await; } - Some(daemon_message::Msg::NodeResources(node_resources)) => { + Some(vm_daemon_message::Msg::VmNodeResources(node_resources)) => { self.data.submit_node_resources(node_resources); } - None => {} + _ => {} }, Err(e) => { log::warn!("Daemon disconnected: {e:?}"); @@ -149,18 +180,12 @@ impl BrainDaemon for BrainDaemonMock { #[tonic::async_trait] impl BrainCli for BrainCliMock { async fn get_balance(&self, req: Request) -> Result, Status> { - Ok(Response::new( - self.data.get_balance(&req.into_inner().pubkey).into(), - )) - } - - async fn get_airdrop(&self, req: Request) -> Result, Status> { - self.data.get_airdrop(&req.into_inner().pubkey); - Ok(Response::new(Empty {})) + let req = check_sig_from_req(req)?; + Ok(Response::new(self.data.get_balance(&req.pubkey).into())) } async fn new_vm(&self, req: Request) -> Result, Status> { - let req = req.into_inner(); + let req = check_sig_from_req(req)?; info!("New VM requested via CLI: {req:?}"); let admin_pubkey = req.admin_pubkey.clone(); let (oneshot_tx, oneshot_rx) = tokio::sync::oneshot::channel(); @@ -180,7 +205,7 @@ impl BrainCli for BrainCliMock { } async fn update_vm(&self, req: Request) -> Result, Status> { - let req = req.into_inner(); + let req = check_sig_from_req(req)?; info!("Update VM requested via CLI: {req:?}"); let (oneshot_tx, oneshot_rx) = tokio::sync::oneshot::channel(); self.data.submit_updatevm_req(req, oneshot_tx).await; @@ -196,23 +221,23 @@ impl BrainCli for BrainCliMock { } async fn extend_vm(&self, req: Request) -> Result, Status> { - let req = req.into_inner(); + let req = check_sig_from_req(req)?; match self .data - .extend_contract_time(&req.uuid, &req.admin_pubkey, req.locked_nano) + .extend_vm_contract_time(&req.uuid, &req.admin_pubkey, req.locked_nano) { Ok(()) => Ok(Response::new(Empty {})), Err(e) => Err(Status::unknown(format!("Could not extend contract: {e}"))), } } - type ListContractsStream = Pin> + Send>>; - async fn list_contracts( + type ListVmContractsStream = Pin> + Send>>; + async fn list_vm_contracts( &self, - req: Request, - ) -> Result, Status> { - let req = req.into_inner(); - info!("CLI {} requested ListVMContractsStream", req.admin_pubkey); + req: Request, + ) -> Result, Status> { + let req = check_sig_from_req(req)?; + info!("CLI {} requested ListVMVmContractsStream", req.admin_pubkey); let contracts = match req.uuid.is_empty() { false => match self.data.find_contract_by_uuid(&req.uuid) { Some(contract) => vec![contract], @@ -228,17 +253,17 @@ impl BrainCli for BrainCliMock { }); let output_stream = ReceiverStream::new(rx); Ok(Response::new( - Box::pin(output_stream) as Self::ListContractsStream + Box::pin(output_stream) as Self::ListVmContractsStream )) } - type ListNodesStream = Pin> + Send>>; - async fn list_nodes( + type ListVmNodesStream = Pin> + Send>>; + async fn list_vm_nodes( &self, - req: Request, - ) -> Result, tonic::Status> { - let req = req.into_inner(); - info!("Unknown CLI requested ListNodesStream: {req:?}"); + req: Request, + ) -> Result, tonic::Status> { + let req = check_sig_from_req(req)?; + info!("CLI requested ListVmNodesStream: {req:?}"); let nodes = self.data.find_nodes_by_filters(&req); let (tx, rx) = mpsc::channel(6); tokio::spawn(async move { @@ -248,16 +273,16 @@ impl BrainCli for BrainCliMock { }); let output_stream = ReceiverStream::new(rx); Ok(Response::new( - Box::pin(output_stream) as Self::ListNodesStream + Box::pin(output_stream) as Self::ListVmNodesStream )) } - async fn get_one_node( + async fn get_one_vm_node( &self, - req: Request, - ) -> Result, Status> { - let req = req.into_inner(); - info!("Unknown CLI requested ListNodesStream: {req:?}"); + req: Request, + ) -> Result, Status> { + let req = check_sig_from_req(req)?; + info!("Unknown CLI requested ListVmNodesStream: {req:?}"); match self.data.get_one_node_by_filters(&req) { Some(node) => Ok(Response::new(node.into())), None => Err(Status::not_found( @@ -267,11 +292,70 @@ impl BrainCli for BrainCliMock { } async fn delete_vm(&self, req: Request) -> Result, Status> { - let req = req.into_inner(); + let req = check_sig_from_req(req)?; info!("Unknown CLI requested to delete vm {}", req.uuid); - self.data.delete_vm(req).await; + match self.data.delete_vm(req).await { + Ok(()) => Ok(Response::new(Empty {})), + Err(e) => Err(Status::not_found(e.to_string())), + } + } + + async fn airdrop(&self, req: Request) -> Result, Status> { + check_admin_key(&req)?; + let req = check_sig_from_req(req)?; + self.data.give_airdrop(&req.pubkey, req.tokens); Ok(Response::new(Empty {})) } + + type ListAllVmContractsStream = Pin> + Send>>; + async fn list_all_vm_contracts( + &self, + req: Request, + ) -> Result, Status> { + check_admin_key(&req)?; + let _ = check_sig_from_req(req)?; + let contracts = self.data.list_all_contracts(); + let (tx, rx) = mpsc::channel(6); + tokio::spawn(async move { + for contract in contracts { + let _ = tx.send(Ok(contract.into())).await; + } + }); + let output_stream = ReceiverStream::new(rx); + Ok(Response::new( + Box::pin(output_stream) as Self::ListVmContractsStream + )) + } + + type ListAccountsStream = Pin> + Send>>; + async fn list_accounts( + &self, + req: Request, + ) -> Result, Status> { + check_admin_key(&req)?; + let _ = check_sig_from_req(req)?; + let accounts = self.data.list_accounts(); + let (tx, rx) = mpsc::channel(6); + tokio::spawn(async move { + for account in accounts { + let _ = tx.send(Ok(account.into())).await; + } + }); + let output_stream = ReceiverStream::new(rx); + Ok(Response::new( + Box::pin(output_stream) as Self::ListAccountsStream + )) + } +} + +trait PubkeyGetter { + fn get_pubkey(&self) -> Option; +} + +impl PubkeyGetter for Pubkey { + fn get_pubkey(&self) -> Option { + Some(self.pubkey.clone()) + } } #[tonic::async_trait] @@ -431,3 +515,177 @@ impl BrainAppDaemon for BrainAppDaemonMock { Ok(Response::new(detee_shared::pb::brain::Empty {})) } } +impl PubkeyGetter for NewVmReq { + fn get_pubkey(&self) -> Option { + Some(self.admin_pubkey.clone()) + } +} + +impl PubkeyGetter for DeleteVmReq { + fn get_pubkey(&self) -> Option { + Some(self.admin_pubkey.clone()) + } +} + +impl PubkeyGetter for UpdateVmReq { + fn get_pubkey(&self) -> Option { + Some(self.admin_pubkey.clone()) + } +} + +impl PubkeyGetter for ExtendVmReq { + fn get_pubkey(&self) -> Option { + Some(self.admin_pubkey.clone()) + } +} + +impl PubkeyGetter for ListVmContractsReq { + fn get_pubkey(&self) -> Option { + Some(self.admin_pubkey.clone()) + } +} + +impl PubkeyGetter for VmNodeFilters { + fn get_pubkey(&self) -> Option { + None + } +} + +impl PubkeyGetter for RegisterVmNodeReq { + fn get_pubkey(&self) -> Option { + Some(self.node_pubkey.clone()) + } +} + +impl PubkeyGetter for Empty { + fn get_pubkey(&self) -> Option { + None + } +} + +impl PubkeyGetter for AirdropReq { + fn get_pubkey(&self) -> Option { + None + } +} + +fn check_sig_from_req(req: Request) -> Result { + let time = match req.metadata().get("timestamp") { + Some(t) => t.clone(), + None => return Err(Status::unauthenticated("Timestamp not found in metadata.")), + }; + let time = time + .to_str() + .map_err(|_| Status::unauthenticated("Timestamp in metadata is not a string"))?; + + let now = chrono::Utc::now(); + let parsed_time = chrono::DateTime::parse_from_rfc3339(time) + .map_err(|_| Status::unauthenticated("Coult not parse timestamp"))?; + let seconds_elapsed = now.signed_duration_since(parsed_time).num_seconds(); + if seconds_elapsed > 1 || seconds_elapsed < -1 { + return Err(Status::unauthenticated(format!( + "Date is not within 1 sec of the time of the server: CLI {} vs Server {}", + parsed_time, now + ))); + } + + let signature = match req.metadata().get("request-signature") { + Some(t) => t, + None => return Err(Status::unauthenticated("signature not found in metadata.")), + }; + let signature = bs58::decode(signature) + .into_vec() + .map_err(|_| Status::unauthenticated("signature is not a bs58 string"))?; + let signature = ed25519_dalek::Signature::from_bytes( + signature + .as_slice() + .try_into() + .map_err(|_| Status::unauthenticated("could not parse ed25519 signature"))?, + ); + + let pubkey_value = match req.metadata().get("pubkey") { + Some(p) => p.clone(), + None => return Err(Status::unauthenticated("pubkey not found in metadata.")), + }; + let pubkey = ed25519_dalek::VerifyingKey::from_bytes( + &bs58::decode(&pubkey_value) + .into_vec() + .map_err(|_| Status::unauthenticated("pubkey is not a bs58 string"))? + .try_into() + .map_err(|_| Status::unauthenticated("pubkey does not have the correct size."))?, + ) + .map_err(|_| Status::unauthenticated("could not parse ed25519 pubkey"))?; + + let req = req.into_inner(); + let message = format!("{time}{req:?}"); + use ed25519_dalek::Verifier; + pubkey + .verify(message.as_bytes(), &signature) + .map_err(|_| Status::unauthenticated("the signature is not valid"))?; + if let Some(req_pubkey) = req.get_pubkey() { + if pubkey_value.to_str().unwrap().to_string() != req_pubkey { + return Err(Status::unauthenticated( + "pubkey of signature does not match pubkey of request", + )); + } + } + Ok(req) +} + +fn check_sig_from_parts(pubkey: &str, time: &str, msg: &str, sig: &str) -> Result<(), Status> { + let now = chrono::Utc::now(); + let parsed_time = chrono::DateTime::parse_from_rfc3339(time) + .map_err(|_| Status::unauthenticated("Coult not parse timestamp"))?; + let seconds_elapsed = now.signed_duration_since(parsed_time).num_seconds(); + if seconds_elapsed > 1 || seconds_elapsed < -1 { + return Err(Status::unauthenticated(format!( + "Date is not within 1 sec of the time of the server: CLI {} vs Server {}", + parsed_time, now + ))); + } + + let signature = bs58::decode(sig) + .into_vec() + .map_err(|_| Status::unauthenticated("signature is not a bs58 string"))?; + let signature = ed25519_dalek::Signature::from_bytes( + signature + .as_slice() + .try_into() + .map_err(|_| Status::unauthenticated("could not parse ed25519 signature"))?, + ); + + let pubkey = ed25519_dalek::VerifyingKey::from_bytes( + &bs58::decode(&pubkey) + .into_vec() + .map_err(|_| Status::unauthenticated("pubkey is not a bs58 string"))? + .try_into() + .map_err(|_| Status::unauthenticated("pubkey does not have the correct size."))?, + ) + .map_err(|_| Status::unauthenticated("could not parse ed25519 pubkey"))?; + + let msg = time.to_string() + msg; + use ed25519_dalek::Verifier; + pubkey + .verify(msg.as_bytes(), &signature) + .map_err(|_| Status::unauthenticated("the signature is not valid"))?; + + Ok(()) +} + +fn check_admin_key(req: &Request) -> Result<(), Status> { + let pubkey = match req.metadata().get("pubkey") { + Some(p) => p.clone(), + None => return Err(Status::unauthenticated("pubkey not found in metadata.")), + }; + let pubkey = pubkey + .to_str() + .map_err(|_| Status::unauthenticated("could not parse pubkey metadata to str"))?; + + if !ADMIN_ACCOUNTS.contains(&pubkey) { + return Err(Status::unauthenticated( + "This operation is reserved to admin accounts", + )); + } + + Ok(()) +} diff --git a/src/main.rs b/src/main.rs index 18a4f00..b4b5e96 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,7 +5,7 @@ use data::BrainData; use detee_shared::pb::brain::brain_app_cli_server::BrainAppCliServer; use detee_shared::pb::brain::brain_app_daemon_server::BrainAppDaemonServer; use grpc::snp_proto::brain_cli_server::BrainCliServer; -use grpc::snp_proto::brain_daemon_server::BrainDaemonServer; +use grpc::snp_proto::brain_vm_daemon_server::BrainVmDaemonServer; use grpc::BrainAppCliMock; use grpc::BrainAppDaemonMock; use grpc::BrainCliMock; @@ -23,12 +23,12 @@ async fn main() { tokio::spawn(async move { loop { tokio::time::sleep(tokio::time::Duration::from_secs(60)).await; - data_clone.contracts_cron(); + data_clone.vm_contracts_cron().await; } }); let addr = "0.0.0.0:31337".parse().unwrap(); - let daemon_server = BrainDaemonServer::new(BrainDaemonMock::new(data.clone())); + let daemon_server = BrainVmDaemonServer::new(BrainDaemonMock::new(data.clone())); let cli_server = BrainCliServer::new(BrainCliMock::new(data.clone())); let sgx_cli_server = BrainAppCliServer::new(BrainAppCliMock::new(data.clone())); diff --git a/snp.proto b/vm.proto similarity index 67% rename from snp.proto rename to vm.proto index 1d6a2f7..e755772 100644 --- a/snp.proto +++ b/vm.proto @@ -1,5 +1,5 @@ syntax = "proto3"; -package snp_proto; +package vm_proto; message Empty { } @@ -13,7 +13,7 @@ message AccountBalance { uint64 tmp_locked = 2; } -message Contract { +message VmContract { string uuid = 1; string hostname = 2; string admin_pubkey = 3; @@ -28,7 +28,7 @@ message Contract { string dtrfs_sha = 12; string created_at = 13; string updated_at = 14; - // total nanotoken cost per minute (for all units) + // total nanoLP cost per minute (for all units) uint64 nano_per_minute = 15; uint64 locked_nano = 16; string collected_at = 17; @@ -52,18 +52,19 @@ message MeasurementIP { string gateway = 4; } -message RegisterNodeReq { +// This should also include a block hash or similar, for auth +message RegisterVmNodeReq { string node_pubkey = 1; string owner_pubkey = 2; string main_ip = 3; string country = 4; string region = 5; string city = 6; - // nanotokens per unit per minute + // nanoLP per unit per minute uint64 price = 7; } -message NodeResources { +message VmNodeResources { string node_pubkey = 1; uint32 avail_ports = 2; uint32 avail_ipv4 = 3; @@ -101,13 +102,14 @@ message NewVmResp { message UpdateVmReq { string uuid = 1; - uint32 disk_size_gb = 2; - uint32 vcpus = 3; - uint32 memory_mb = 4; - string kernel_url = 5; - string kernel_sha = 6; - string dtrfs_url = 7; - string dtrfs_sha = 8; + string admin_pubkey = 2; + uint32 disk_size_gb = 3; + uint32 vcpus = 4; + uint32 memory_mb = 5; + string kernel_url = 6; + string kernel_sha = 7; + string dtrfs_url = 8; + string dtrfs_sha = 9; } message UpdateVmResp { @@ -118,9 +120,10 @@ message UpdateVmResp { message DeleteVmReq { string uuid = 1; + string admin_pubkey = 2; } -message BrainMessage { +message BrainVmMessage { oneof Msg { NewVmReq new_vm_req = 1; UpdateVmReq update_vm_req = 2; @@ -128,28 +131,35 @@ message BrainMessage { } } -message DaemonMessage { +message DaemonStreamAuth { + string timestamp = 1; + string pubkey = 2; + repeated string contracts = 3; + string signature = 4; +} + +message VmDaemonMessage { oneof Msg { - Pubkey pubkey = 1; + DaemonStreamAuth auth = 1; NewVmResp new_vm_resp = 2; UpdateVmResp update_vm_resp = 3; - NodeResources node_resources = 4; + VmNodeResources vm_node_resources = 4; } } -service BrainDaemon { - rpc RegisterNode (RegisterNodeReq) returns (stream Contract); - rpc BrainMessages (Pubkey) returns (stream BrainMessage); - rpc DaemonMessages (stream DaemonMessage) returns (Empty); +service BrainVmDaemon { + rpc RegisterVmNode (RegisterVmNodeReq) returns (stream VmContract); + rpc BrainMessages (DaemonStreamAuth) returns (stream BrainVmMessage); + rpc DaemonMessages (stream VmDaemonMessage) returns (Empty); } -message ListContractsReq { +message ListVmContractsReq { string admin_pubkey = 1; string node_pubkey = 2; string uuid = 3; } -message NodeFilters { +message VmNodeFilters { uint32 free_ports = 1; bool offers_ipv4 = 2; bool offers_ipv6 = 3; @@ -163,7 +173,7 @@ message NodeFilters { string node_pubkey = 11; } -message NodeListResp { +message VmNodeListResp { string node_pubkey = 1; string country = 2; string region = 3; @@ -171,7 +181,7 @@ message NodeListResp { string ip = 5; // required for latency test uint32 server_rating = 6; uint32 provider_rating = 7; - // nanotokens per unit per minute + // nanoLP per unit per minute uint64 price = 8; } @@ -181,14 +191,28 @@ message ExtendVmReq { uint64 locked_nano = 3; } +message AirdropReq { + string pubkey = 1; + uint64 tokens = 2; +} + +message Account { + string pubkey = 1; + uint64 balance = 2; + uint64 tmp_locked = 3; +} + service BrainCli { - rpc GetAirdrop (Pubkey) returns (Empty); rpc GetBalance (Pubkey) returns (AccountBalance); rpc NewVm (NewVmReq) returns (NewVmResp); - rpc ListContracts (ListContractsReq) returns (stream Contract); - rpc ListNodes (NodeFilters) returns (stream NodeListResp); - rpc GetOneNode (NodeFilters) returns (NodeListResp); + rpc ListVmContracts (ListVmContractsReq) returns (stream VmContract); + rpc ListVmNodes (VmNodeFilters) returns (stream VmNodeListResp); + rpc GetOneVmNode (VmNodeFilters) returns (VmNodeListResp); rpc DeleteVm (DeleteVmReq) returns (Empty); rpc UpdateVm (UpdateVmReq) returns (UpdateVmResp); rpc ExtendVm (ExtendVmReq) returns (Empty); + // admin commands + rpc Airdrop (AirdropReq) returns (Empty); + rpc ListAllVmContracts (Empty) returns (stream VmContract); + rpc ListAccounts (Empty) returns (stream Account); } -- 2.43.0 From f1a527e22d6f085284d723a910d947ac5d7520e8 Mon Sep 17 00:00:00 2001 From: Noor Date: Mon, 10 Feb 2025 19:56:39 +0530 Subject: [PATCH 10/29] Integrating authendication rename register_node to register_app_node --- Cargo.lock | 2 +- src/grpc.rs | 14 ++++++++++---- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 76631e4..b77319b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -418,7 +418,7 @@ dependencies = [ [[package]] name = "detee-shared" version = "0.1.0" -source = "git+ssh://git@gitea.detee.cloud/noormohammedb/detee-shared?branch=stable_01#fce57884937a4ec02acbf2f5b370ab879b1af657" +source = "git+ssh://git@gitea.detee.cloud/noormohammedb/detee-shared?branch=stable_01#65bfa113a44251dd13e5d895014f4a773e367d2e" dependencies = [ "base64", "prost", diff --git a/src/grpc.rs b/src/grpc.rs index 6e69c69..1f6c355 100644 --- a/src/grpc.rs +++ b/src/grpc.rs @@ -426,14 +426,14 @@ impl BrainAppCli for BrainAppCliMock { #[tonic::async_trait] impl BrainAppDaemon for BrainAppDaemonMock { - type RegisterNodeStream = Pin> + Send>>; + type RegisterAppNodeStream = Pin> + Send>>; type BrainMessagesStream = Pin> + Send>>; - async fn register_node( + async fn register_app_node( &self, req: tonic::Request, - ) -> Result, Status> { - let req_data = req.into_inner(); + ) -> Result, Status> { + let req_data = check_sig_from_req(req)?; log::info!( "registering app node_key : {}, owner_key: {}", &req_data.node_pubkey, @@ -569,6 +569,12 @@ impl PubkeyGetter for AirdropReq { } } +impl PubkeyGetter for RegisterAppNodeReq { + fn get_pubkey(&self) -> Option { + None + } +} + fn check_sig_from_req(req: Request) -> Result { let time = match req.metadata().get("timestamp") { Some(t) => t.clone(), -- 2.43.0 From 3b3221099fe6142283403ce427f7838e1caab783 Mon Sep 17 00:00:00 2001 From: Noor Date: Tue, 11 Feb 2025 08:06:14 +0000 Subject: [PATCH 11/29] refactor: rename admin_pubkey to owner_wallet in AppContract and related functions --- Cargo.lock | 1 - src/data.rs | 16 ++++++++-------- src/grpc.rs | 6 +++--- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b77319b..94db7b0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -418,7 +418,6 @@ dependencies = [ [[package]] name = "detee-shared" version = "0.1.0" -source = "git+ssh://git@gitea.detee.cloud/noormohammedb/detee-shared?branch=stable_01#65bfa113a44251dd13e5d895014f4a773e367d2e" dependencies = [ "base64", "prost", diff --git a/src/data.rs b/src/data.rs index aaaf130..89855ae 100644 --- a/src/data.rs +++ b/src/data.rs @@ -142,7 +142,7 @@ impl Into for VmContract { pub struct AppContract { pub uuid: String, pub package_url: String, - pub admin_pubkey: String, + pub owner_wallet: String, pub node_pubkey: String, pub mapped_ports: Vec<(u16, u16)>, pub host_ipv4: String, @@ -162,7 +162,7 @@ impl From for AppContractPB { fn from(value: AppContract) -> Self { Self { uuid: value.uuid, - admin_pubkey: value.admin_pubkey, + owner_wallet: value.owner_wallet, node_pubkey: value.node_pubkey, package_url: value.package_url, exposed_ports: value @@ -797,17 +797,17 @@ impl BrainData { contracts.iter().find(|c| c.uuid == uuid).cloned() } - pub fn find_app_contracts_by_admin_pubkey(&self, admin_pubkey: &str) -> Vec { - debug!("Searching contracts for admin pubkey {admin_pubkey}"); + pub fn find_app_contracts_by_admin_pubkey(&self, owner_wallet: &str) -> Vec { + debug!("Searching contracts for admin pubkey {owner_wallet}"); let contracts: Vec = self .app_contracts .read() .unwrap() .iter() - .filter(|c| c.admin_pubkey == admin_pubkey) + .filter(|c| c.owner_wallet == owner_wallet) .cloned() .collect(); - debug!("Found {} contracts or {admin_pubkey}.", contracts.len()); + debug!("Found {} contracts or {owner_wallet}.", contracts.len()); contracts } @@ -906,7 +906,7 @@ impl BrainData { if let Err(err) = new_container_req.1.send(new_container_resp.clone()) { log::error!( "CLI RX for {} dropped before receiving confirmation {:?}.\n{:?}", - &new_container_req.0.admin_pubkey, + &new_container_req.0.owner_wallet, new_container_resp, err ); @@ -916,7 +916,7 @@ impl BrainData { uuid: new_container_req.0.uuid, node_pubkey: new_container_req.0.node_pubkey.clone(), package_url: new_container_req.0.package_url, - admin_pubkey: new_container_req.0.admin_pubkey, + owner_wallet: new_container_req.0.owner_wallet, ..Default::default() }; log::info!("Created new app contract: {app_contracts:?}"); diff --git a/src/grpc.rs b/src/grpc.rs index 1f6c355..712de9c 100644 --- a/src/grpc.rs +++ b/src/grpc.rs @@ -368,13 +368,13 @@ impl BrainAppCli for BrainAppCliMock { ) -> Result, Status> { let req = req.into_inner(); log::info!("Creating new container: {req:?}"); - let admin_pubkey = req.admin_pubkey.clone(); + let owner_wallet = req.owner_wallet.clone(); let (oneshot_tx, oneshot_rx) = tokio::sync::oneshot::channel(); self.data.send_new_container_req(req, oneshot_tx).await; match oneshot_rx.await { Ok(response) => { - info!("responding container confirmation to {admin_pubkey}: {response:?}"); + info!("responding container confirmation to {owner_wallet}: {response:?}"); Ok(Response::new(response)) } Err(e) => { @@ -409,7 +409,7 @@ impl BrainAppCli for BrainAppCliMock { let app_contracts = self .data - .find_app_contracts_by_admin_pubkey(&req_data.admin_pubkey); + .find_app_contracts_by_admin_pubkey(&req_data.owner_wallet); let (tx, rx) = mpsc::channel(6); tokio::spawn(async move { -- 2.43.0 From 784878d0a15011d8a8ca40569d40835e0cc897ec Mon Sep 17 00:00:00 2001 From: Noor Date: Tue, 11 Feb 2025 11:37:07 +0000 Subject: [PATCH 12/29] authendicating daemon --- Cargo.lock | 1 + src/grpc.rs | 49 ++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 43 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 94db7b0..17f9b05 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -418,6 +418,7 @@ dependencies = [ [[package]] name = "detee-shared" version = "0.1.0" +source = "git+ssh://git@gitea.detee.cloud/noormohammedb/detee-shared?branch=stable_01#f2bc29149e32df09508519f3f88cdf880728e6dd" dependencies = [ "base64", "prost", diff --git a/src/grpc.rs b/src/grpc.rs index 712de9c..3b8bae8 100644 --- a/src/grpc.rs +++ b/src/grpc.rs @@ -469,12 +469,23 @@ impl BrainAppDaemon for BrainAppDaemonMock { async fn brain_messages( &self, - req: tonic::Request, + req: tonic::Request, ) -> Result, Status> { - let req = req.into_inner(); - info!("Daemon {} connected to receive brain messages", req.pubkey); + let req_data = req.into_inner(); + let pubkey = req_data.pubkey.clone(); + check_sig_from_parts( + &pubkey, + &req_data.timestamp, + &format!("{:?}", req_data.contracts), + &req_data.signature, + )?; + + info!( + "Daemon {} connected to receive brain messages", + req_data.pubkey + ); let (tx, rx) = mpsc::channel(6); - self.data.add_app_daemon_tx(&req.pubkey, tx); + self.data.add_app_daemon_tx(&req_data.pubkey, tx); let output_stream = ReceiverStream::new(rx).map(Ok); Ok(Response::new( Box::pin(output_stream) as Self::BrainMessagesStream @@ -488,11 +499,35 @@ impl BrainAppDaemon for BrainAppDaemonMock { let mut req_stream = req.into_inner(); let mut pubkey = String::new(); + if let Some(Ok(msg)) = req_stream.next().await { + log::debug!( + "demon_messages received the following auth message: {:?}", + msg.msg + ); + if let Some(detee_shared::pb::brain::daemon_message_app::Msg::Auth(auth)) = msg.msg { + pubkey = auth.pubkey.clone(); + check_sig_from_parts( + &pubkey, + &auth.timestamp, + &format!("{:?}", auth.contracts), + &auth.signature, + )?; + } else { + return Err(Status::unauthenticated( + "Could not authenticate the daemon: could not extract auth signature", + )); + } + } else { + return Err(Status::unauthenticated("Could not authenticate the daemon")); + } + while let Some(daemon_message) = req_stream.next().await { match daemon_message { Ok(msg) => match msg.msg { - Some(detee_shared::pb::brain::daemon_message_app::Msg::Pubkey(node_pubkey)) => { - pubkey = node_pubkey; + Some(detee_shared::pb::brain::daemon_message_app::Msg::Auth(daemon_auth)) => { + dbg!(&daemon_auth); + // TODO: wip on authendication + pubkey = daemon_auth.pubkey; } Some(detee_shared::pb::brain::daemon_message_app::Msg::NewAppRes(new_cont)) => { self.data.send_new_container_resp(new_cont).await; @@ -500,7 +535,7 @@ impl BrainAppDaemon for BrainAppDaemonMock { Some(detee_shared::pb::brain::daemon_message_app::Msg::AppNodeResources(_)) => { todo!("AppNodeResources not implemented yet"); } - None => { + _ => { dbg!("None"); } }, -- 2.43.0 From 2cada47d244e928eb28b68047f0a269c6dbf0fbd Mon Sep 17 00:00:00 2001 From: Noor Date: Mon, 17 Feb 2025 11:38:57 +0530 Subject: [PATCH 13/29] refactor: update detee-shared dependency and adjust imports to use sgx module --- Cargo.lock | 2 +- src/data.rs | 22 ++++++++++++---------- src/grpc.rs | 33 ++++++++++++++++++++------------- src/main.rs | 4 ++-- 4 files changed, 35 insertions(+), 26 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 17f9b05..b67418e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -418,7 +418,7 @@ dependencies = [ [[package]] name = "detee-shared" version = "0.1.0" -source = "git+ssh://git@gitea.detee.cloud/noormohammedb/detee-shared?branch=stable_01#f2bc29149e32df09508519f3f88cdf880728e6dd" +source = "git+ssh://git@gitea.detee.cloud/noormohammedb/detee-shared?branch=stable_01#ee592c71d6c760ef05ef4f8b5c88b31fcbaf52aa" dependencies = [ "base64", "prost", diff --git a/src/data.rs b/src/data.rs index 89855ae..5690aa7 100644 --- a/src/data.rs +++ b/src/data.rs @@ -1,18 +1,18 @@ use crate::grpc::snp_proto::{self as grpc}; use chrono::Utc; use dashmap::DashMap; -use detee_shared::pb::brain::DelAppReq; +use detee_shared::sgx::pb::brain::DelAppReq; use log::{debug, info, warn}; use std::str::FromStr; use std::sync::RwLock; use tokio::sync::mpsc::Sender; use tokio::sync::oneshot::Sender as OneshotSender; -use detee_shared::pb::brain::AppContract as AppContractPB; -use detee_shared::pb::brain::BrainMessageApp; -use detee_shared::pb::brain::MappedPort; -use detee_shared::pb::brain::NewAppReq; -use detee_shared::pb::brain::NewAppRes; +use detee_shared::sgx::pb::brain::AppContract as AppContractPB; +use detee_shared::sgx::pb::brain::BrainMessageApp; +use detee_shared::sgx::pb::brain::MappedPort; +use detee_shared::sgx::pb::brain::NewAppReq; +use detee_shared::sgx::pb::brain::NewAppRes; #[derive(thiserror::Error, Debug)] pub enum Error { @@ -833,9 +833,9 @@ impl BrainData { req.node_pubkey, req.uuid ); let msg = BrainMessageApp { - msg: Some(detee_shared::pb::brain::brain_message_app::Msg::NewAppReq( - req.clone(), - )), + msg: Some( + detee_shared::sgx::pb::brain::brain_message_app::Msg::NewAppReq(req.clone()), + ), }; if let Err(e) = app_daemon_tx.send(msg).await { warn!( @@ -869,7 +869,9 @@ impl BrainData { ); let msg = BrainMessageApp { msg: Some( - detee_shared::pb::brain::brain_message_app::Msg::DeleteAppReq(req.clone()), + detee_shared::sgx::pb::brain::brain_message_app::Msg::DeleteAppReq( + req.clone(), + ), ), }; diff --git a/src/grpc.rs b/src/grpc.rs index 3b8bae8..d135ee1 100644 --- a/src/grpc.rs +++ b/src/grpc.rs @@ -16,9 +16,9 @@ use tokio::sync::mpsc; use tokio_stream::{wrappers::ReceiverStream, Stream, StreamExt}; use tonic::{Request, Response, Status, Streaming}; -use detee_shared::pb::brain::brain_app_cli_server::BrainAppCli; -use detee_shared::pb::brain::brain_app_daemon_server::BrainAppDaemon; -use detee_shared::pb::brain::{ +use detee_shared::sgx::pb::brain::brain_app_cli_server::BrainAppCli; +use detee_shared::sgx::pb::brain::brain_app_daemon_server::BrainAppDaemon; +use detee_shared::sgx::pb::brain::{ AppContract, BrainMessageApp, DaemonMessageApp, DelAppReq, ListAppContractsReq, NewAppReq, NewAppRes, RegisterAppNodeReq, }; @@ -389,7 +389,7 @@ impl BrainAppCli for BrainAppCliMock { async fn delete_app( &self, req: tonic::Request, - ) -> Result, Status> { + ) -> Result, Status> { let req = req.into_inner(); log::info!("deleting container: {}", req.uuid.clone()); if let Err(er) = self.data.send_del_container_req(req).await { @@ -397,7 +397,7 @@ impl BrainAppCli for BrainAppCliMock { return Err(Status::not_found("Could not find container")); }; - Ok(Response::new(detee_shared::pb::brain::Empty {})) + Ok(Response::new(detee_shared::sgx::pb::brain::Empty {})) } async fn list_app_contracts( @@ -469,7 +469,7 @@ impl BrainAppDaemon for BrainAppDaemonMock { async fn brain_messages( &self, - req: tonic::Request, + req: tonic::Request, ) -> Result, Status> { let req_data = req.into_inner(); let pubkey = req_data.pubkey.clone(); @@ -495,16 +495,17 @@ impl BrainAppDaemon for BrainAppDaemonMock { async fn daemon_messages( &self, req: tonic::Request>, - ) -> Result, Status> { + ) -> Result, Status> { let mut req_stream = req.into_inner(); - let mut pubkey = String::new(); + let mut pubkey; if let Some(Ok(msg)) = req_stream.next().await { log::debug!( "demon_messages received the following auth message: {:?}", msg.msg ); - if let Some(detee_shared::pb::brain::daemon_message_app::Msg::Auth(auth)) = msg.msg { + if let Some(detee_shared::sgx::pb::brain::daemon_message_app::Msg::Auth(auth)) = msg.msg + { pubkey = auth.pubkey.clone(); check_sig_from_parts( &pubkey, @@ -524,15 +525,21 @@ impl BrainAppDaemon for BrainAppDaemonMock { while let Some(daemon_message) = req_stream.next().await { match daemon_message { Ok(msg) => match msg.msg { - Some(detee_shared::pb::brain::daemon_message_app::Msg::Auth(daemon_auth)) => { + Some(detee_shared::sgx::pb::brain::daemon_message_app::Msg::Auth( + daemon_auth, + )) => { dbg!(&daemon_auth); // TODO: wip on authendication pubkey = daemon_auth.pubkey; } - Some(detee_shared::pb::brain::daemon_message_app::Msg::NewAppRes(new_cont)) => { + Some(detee_shared::sgx::pb::brain::daemon_message_app::Msg::NewAppRes( + new_cont, + )) => { self.data.send_new_container_resp(new_cont).await; } - Some(detee_shared::pb::brain::daemon_message_app::Msg::AppNodeResources(_)) => { + Some( + detee_shared::sgx::pb::brain::daemon_message_app::Msg::AppNodeResources(_), + ) => { todo!("AppNodeResources not implemented yet"); } _ => { @@ -547,7 +554,7 @@ impl BrainAppDaemon for BrainAppDaemonMock { // } - Ok(Response::new(detee_shared::pb::brain::Empty {})) + Ok(Response::new(detee_shared::sgx::pb::brain::Empty {})) } } impl PubkeyGetter for NewVmReq { diff --git a/src/main.rs b/src/main.rs index b4b5e96..37594ef 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,8 +2,8 @@ mod data; mod grpc; use data::BrainData; -use detee_shared::pb::brain::brain_app_cli_server::BrainAppCliServer; -use detee_shared::pb::brain::brain_app_daemon_server::BrainAppDaemonServer; +use detee_shared::sgx::pb::brain::brain_app_cli_server::BrainAppCliServer; +use detee_shared::sgx::pb::brain::brain_app_daemon_server::BrainAppDaemonServer; use grpc::snp_proto::brain_cli_server::BrainCliServer; use grpc::snp_proto::brain_vm_daemon_server::BrainVmDaemonServer; use grpc::BrainAppCliMock; -- 2.43.0 From 675933dd7c9e1dc7a0d85938b8c69a378c0a160f Mon Sep 17 00:00:00 2001 From: Noor Date: Tue, 18 Feb 2025 21:03:23 +0530 Subject: [PATCH 14/29] authendicating cli rename owner_wallet to admin_pubkey in AppContract and related functions --- Cargo.lock | 31 +++++++++++++------------------ src/data.rs | 24 ++++++++++++------------ src/grpc.rs | 48 +++++++++++++++++++++++++++++++----------------- 3 files changed, 56 insertions(+), 47 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b67418e..aed31ee 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -418,12 +418,13 @@ dependencies = [ [[package]] name = "detee-shared" version = "0.1.0" -source = "git+ssh://git@gitea.detee.cloud/noormohammedb/detee-shared?branch=stable_01#ee592c71d6c760ef05ef4f8b5c88b31fcbaf52aa" +source = "git+ssh://git@gitea.detee.cloud/noormohammedb/detee-shared?branch=stable_01#54abe74d42946b238c2ce44bb33f55778490b13d" dependencies = [ "base64", "prost", "serde", - "serde_yml", + "serde_yaml", + "thiserror", "tonic", "tonic-build", ] @@ -1059,16 +1060,6 @@ version = "0.2.169" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" -[[package]] -name = "libyml" -version = "0.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3302702afa434ffa30847a83305f0a69d6abd74293b6554c18ec85c7ef30c980" -dependencies = [ - "anyhow", - "version_check", -] - [[package]] name = "linux-raw-sys" version = "0.4.15" @@ -1687,18 +1678,16 @@ dependencies = [ ] [[package]] -name = "serde_yml" -version = "0.0.12" +name = "serde_yaml" +version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59e2dd588bf1597a252c3b920e0143eb99b0f76e4e082f4c92ce34fbc9e71ddd" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ "indexmap 2.7.1", "itoa", - "libyml", - "memchr", "ryu", "serde", - "version_check", + "unsafe-libyaml", ] [[package]] @@ -2102,6 +2091,12 @@ version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034" +[[package]] +name = "unsafe-libyaml" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" + [[package]] name = "untrusted" version = "0.9.0" diff --git a/src/data.rs b/src/data.rs index 5690aa7..46f9841 100644 --- a/src/data.rs +++ b/src/data.rs @@ -142,7 +142,7 @@ impl Into for VmContract { pub struct AppContract { pub uuid: String, pub package_url: String, - pub owner_wallet: String, + pub admin_pubkey: String, pub node_pubkey: String, pub mapped_ports: Vec<(u16, u16)>, pub host_ipv4: String, @@ -162,7 +162,7 @@ impl From for AppContractPB { fn from(value: AppContract) -> Self { Self { uuid: value.uuid, - owner_wallet: value.owner_wallet, + admin_pubkey: value.admin_pubkey, node_pubkey: value.node_pubkey, package_url: value.package_url, exposed_ports: value @@ -177,8 +177,8 @@ impl From for AppContractPB { #[derive(Eq, Hash, PartialEq, Clone, Debug, Default)] pub struct AppNode { - pub public_key: String, - pub owner_key: String, + pub node_pubkey: String, + pub operator_pubkey: String, pub country: String, pub region: String, pub city: String, @@ -782,9 +782,9 @@ impl BrainData { pub fn insert_app_node(&self, node: AppNode) { let mut nodes = self.app_nodes.write().unwrap(); for n in nodes.iter_mut() { - if n.public_key == node.public_key { + if n.node_pubkey == node.node_pubkey { // TODO: figure what to do in this case. - warn!("Node {} already exists. Updating data.", n.public_key); + warn!("Node {} already exists. Updating data.", n.node_pubkey); *n = node; return; } @@ -797,17 +797,17 @@ impl BrainData { contracts.iter().find(|c| c.uuid == uuid).cloned() } - pub fn find_app_contracts_by_admin_pubkey(&self, owner_wallet: &str) -> Vec { - debug!("Searching contracts for admin pubkey {owner_wallet}"); + pub fn find_app_contracts_by_admin_pubkey(&self, admin_pubkey: &str) -> Vec { + debug!("Searching contracts for admin pubkey {admin_pubkey}"); let contracts: Vec = self .app_contracts .read() .unwrap() .iter() - .filter(|c| c.owner_wallet == owner_wallet) + .filter(|c| c.admin_pubkey == admin_pubkey) .cloned() .collect(); - debug!("Found {} contracts or {owner_wallet}.", contracts.len()); + debug!("Found {} contracts or {admin_pubkey}.", contracts.len()); contracts } @@ -908,7 +908,7 @@ impl BrainData { if let Err(err) = new_container_req.1.send(new_container_resp.clone()) { log::error!( "CLI RX for {} dropped before receiving confirmation {:?}.\n{:?}", - &new_container_req.0.owner_wallet, + &new_container_req.0.admin_pubkey, new_container_resp, err ); @@ -918,7 +918,7 @@ impl BrainData { uuid: new_container_req.0.uuid, node_pubkey: new_container_req.0.node_pubkey.clone(), package_url: new_container_req.0.package_url, - owner_wallet: new_container_req.0.owner_wallet, + admin_pubkey: new_container_req.0.admin_pubkey, ..Default::default() }; log::info!("Created new app contract: {app_contracts:?}"); diff --git a/src/grpc.rs b/src/grpc.rs index d135ee1..f58a9b4 100644 --- a/src/grpc.rs +++ b/src/grpc.rs @@ -366,15 +366,15 @@ impl BrainAppCli for BrainAppCliMock { &self, req: tonic::Request, ) -> Result, Status> { - let req = req.into_inner(); - log::info!("Creating new container: {req:?}"); - let owner_wallet = req.owner_wallet.clone(); + let req_data = check_sig_from_req(req)?; + log::info!("Creating new container: {req_data:?}"); + let admin_pubkey = req_data.admin_pubkey.clone(); let (oneshot_tx, oneshot_rx) = tokio::sync::oneshot::channel(); - self.data.send_new_container_req(req, oneshot_tx).await; + self.data.send_new_container_req(req_data, oneshot_tx).await; match oneshot_rx.await { Ok(response) => { - info!("responding container confirmation to {owner_wallet}: {response:?}"); + info!("responding container confirmation to {admin_pubkey}: {response:?}"); Ok(Response::new(response)) } Err(e) => { @@ -390,9 +390,9 @@ impl BrainAppCli for BrainAppCliMock { &self, req: tonic::Request, ) -> Result, Status> { - let req = req.into_inner(); - log::info!("deleting container: {}", req.uuid.clone()); - if let Err(er) = self.data.send_del_container_req(req).await { + let req_data = check_sig_from_req(req)?; + log::info!("deleting container: {}", req_data.uuid.clone()); + if let Err(er) = self.data.send_del_container_req(req_data).await { info!("Could not delete container: {er}"); return Err(Status::not_found("Could not find container")); }; @@ -404,12 +404,10 @@ impl BrainAppCli for BrainAppCliMock { &self, req: tonic::Request, ) -> Result, Status> { - let req_data = req.into_inner(); - dbg!(&req_data); - + let req_data = check_sig_from_req(req)?; let app_contracts = self .data - .find_app_contracts_by_admin_pubkey(&req_data.owner_wallet); + .find_app_contracts_by_admin_pubkey(&req_data.admin_pubkey); let (tx, rx) = mpsc::channel(6); tokio::spawn(async move { @@ -437,12 +435,12 @@ impl BrainAppDaemon for BrainAppDaemonMock { log::info!( "registering app node_key : {}, owner_key: {}", &req_data.node_pubkey, - &req_data.owner_pubkey + &req_data.operator_pubkey ); let app_node = crate::data::AppNode { - public_key: req_data.node_pubkey.clone(), - owner_key: req_data.owner_pubkey, + node_pubkey: req_data.node_pubkey.clone(), + operator_pubkey: req_data.operator_pubkey, ip: req_data.main_ip, city: req_data.city, region: req_data.region, @@ -528,8 +526,6 @@ impl BrainAppDaemon for BrainAppDaemonMock { Some(detee_shared::sgx::pb::brain::daemon_message_app::Msg::Auth( daemon_auth, )) => { - dbg!(&daemon_auth); - // TODO: wip on authendication pubkey = daemon_auth.pubkey; } Some(detee_shared::sgx::pb::brain::daemon_message_app::Msg::NewAppRes( @@ -617,6 +613,24 @@ impl PubkeyGetter for RegisterAppNodeReq { } } +impl PubkeyGetter for NewAppReq { + fn get_pubkey(&self) -> Option { + Some(self.admin_pubkey.clone()) + } +} + +impl PubkeyGetter for DelAppReq { + fn get_pubkey(&self) -> Option { + Some(self.admin_pubkey.clone()) + } +} + +impl PubkeyGetter for ListAppContractsReq { + fn get_pubkey(&self) -> Option { + Some(self.admin_pubkey.clone()) + } +} + fn check_sig_from_req(req: Request) -> Result { let time = match req.metadata().get("timestamp") { Some(t) => t.clone(), -- 2.43.0 From 467a4f04b97f7ac4fcfb23ae9a72b761ad5cdbce Mon Sep 17 00:00:00 2001 From: Noor Date: Thu, 20 Feb 2025 10:26:52 +0000 Subject: [PATCH 15/29] refactor: update AppContract structure and resource handling in data and grpc modules --- src/data.rs | 52 ++++++++++++++++++++++++++++++++++++++++++---------- src/grpc.rs | 4 ++-- 2 files changed, 44 insertions(+), 12 deletions(-) diff --git a/src/data.rs b/src/data.rs index 46f9841..af5816b 100644 --- a/src/data.rs +++ b/src/data.rs @@ -9,6 +9,7 @@ use tokio::sync::mpsc::Sender; use tokio::sync::oneshot::Sender as OneshotSender; use detee_shared::sgx::pb::brain::AppContract as AppContractPB; +use detee_shared::sgx::pb::brain::AppResource as AppResourcePB; use detee_shared::sgx::pb::brain::BrainMessageApp; use detee_shared::sgx::pb::brain::MappedPort; use detee_shared::sgx::pb::brain::NewAppReq; @@ -146,7 +147,7 @@ pub struct AppContract { pub node_pubkey: String, pub mapped_ports: Vec<(u16, u16)>, pub host_ipv4: String, - pub disk_size_gb: u32, + pub disk_size_mb: u32, pub vcpus: u32, pub memory_mb: u32, pub created_at: chrono::DateTime, @@ -160,17 +161,33 @@ pub struct AppContract { impl From for AppContractPB { fn from(value: AppContract) -> Self { + let exposed_ports = value + .mapped_ports + .clone() + .into_iter() + .map(MappedPort::from) + .collect(); + + let resource = Some(AppResourcePB { + memory_mb: value.memory_mb, + disk_mb: value.disk_size_mb, + vcpu: value.vcpus, + ports: value.mapped_ports.iter().map(|p| p.1 as u32).collect(), + }); Self { uuid: value.uuid, + package_url: value.package_url, admin_pubkey: value.admin_pubkey, node_pubkey: value.node_pubkey, - package_url: value.package_url, - exposed_ports: value - .mapped_ports - .into_iter() - .map(MappedPort::from) - .collect(), - ..Default::default() + exposed_ports, + public_ipv4: value.host_ipv4, + resource, + created_at: value.created_at.to_rfc3339(), + updated_at: value.updated_at.to_rfc3339(), + // TODO: check while implementing pricing + nano_per_minute: value.price_per_unit, + locked_nano: value.locked_nano, + collected_at: value.collected_at.to_rfc3339(), } } } @@ -914,12 +931,27 @@ impl BrainData { ); } + let requested_resource = new_container_req.0.resource.clone().unwrap_or_default(); + let app_contracts = AppContract { uuid: new_container_req.0.uuid, - node_pubkey: new_container_req.0.node_pubkey.clone(), package_url: new_container_req.0.package_url, admin_pubkey: new_container_req.0.admin_pubkey, - ..Default::default() + node_pubkey: new_container_req.0.node_pubkey.clone(), + mapped_ports: new_container_resp + .mapped_ports + .iter() + .map(|p| (p.host_port as u16, p.app_port as u16)) + .collect::>(), + host_ipv4: new_container_resp.ip_address, + disk_size_mb: requested_resource.disk_mb, + vcpus: requested_resource.vcpu, + memory_mb: requested_resource.memory_mb, + created_at: Utc::now(), + updated_at: Utc::now(), + price_per_unit: new_container_req.0.price_per_unit, + locked_nano: new_container_req.0.locked_nano, + collected_at: Utc::now(), }; log::info!("Created new app contract: {app_contracts:?}"); self.app_contracts.write().unwrap().push(app_contracts); diff --git a/src/grpc.rs b/src/grpc.rs index f58a9b4..f0d09ea 100644 --- a/src/grpc.rs +++ b/src/grpc.rs @@ -529,9 +529,9 @@ impl BrainAppDaemon for BrainAppDaemonMock { pubkey = daemon_auth.pubkey; } Some(detee_shared::sgx::pb::brain::daemon_message_app::Msg::NewAppRes( - new_cont, + new_app_res, )) => { - self.data.send_new_container_resp(new_cont).await; + self.data.send_new_container_resp(new_app_res).await; } Some( detee_shared::sgx::pb::brain::daemon_message_app::Msg::AppNodeResources(_), -- 2.43.0 From 127d3996709fa03e3b5ab05771eb923af9de6cee Mon Sep 17 00:00:00 2001 From: Noor Date: Fri, 21 Feb 2025 10:45:29 +0000 Subject: [PATCH 16/29] Node resources enhance AppNode resource handling --- Cargo.lock | 2 +- src/data.rs | 33 +++++++++++++++++++++++++++++---- src/grpc.rs | 18 +++++++----------- 3 files changed, 37 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index aed31ee..98af335 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -418,7 +418,7 @@ dependencies = [ [[package]] name = "detee-shared" version = "0.1.0" -source = "git+ssh://git@gitea.detee.cloud/noormohammedb/detee-shared?branch=stable_01#54abe74d42946b238c2ce44bb33f55778490b13d" +source = "git+ssh://git@gitea.detee.cloud/noormohammedb/detee-shared?branch=stable_01#a6baa4059d2836b88d52cd56216993e92e83ecbc" dependencies = [ "base64", "prost", diff --git a/src/data.rs b/src/data.rs index af5816b..867b2c6 100644 --- a/src/data.rs +++ b/src/data.rs @@ -9,6 +9,7 @@ use tokio::sync::mpsc::Sender; use tokio::sync::oneshot::Sender as OneshotSender; use detee_shared::sgx::pb::brain::AppContract as AppContractPB; +use detee_shared::sgx::pb::brain::AppNodeResources; use detee_shared::sgx::pb::brain::AppResource as AppResourcePB; use detee_shared::sgx::pb::brain::BrainMessageApp; use detee_shared::sgx::pb::brain::MappedPort; @@ -161,7 +162,7 @@ pub struct AppContract { impl From for AppContractPB { fn from(value: AppContract) -> Self { - let exposed_ports = value + let mapped_ports = value .mapped_ports .clone() .into_iter() @@ -179,7 +180,7 @@ impl From for AppContractPB { package_url: value.package_url, admin_pubkey: value.admin_pubkey, node_pubkey: value.node_pubkey, - exposed_ports, + mapped_ports, public_ipv4: value.host_ipv4, resource, created_at: value.created_at.to_rfc3339(), @@ -202,8 +203,8 @@ pub struct AppNode { pub ip: String, pub avail_mem_mb: u32, pub avail_vcpus: u32, - pub avail_storage_gbs: u32, - pub avail_ports: u32, + pub avail_storage_mb: u32, + pub avail_no_of_port: u32, pub max_ports_per_app: u32, // nanotokens per unit per minute pub price: u64, @@ -837,6 +838,30 @@ impl BrainData { .collect() } + pub fn submit_app_node_resources(&self, node_resource: AppNodeResources) { + debug!("{:#?}", &node_resource); + let mut nodes = self.app_nodes.write().unwrap(); + for n in nodes.iter_mut() { + if n.node_pubkey == node_resource.node_pubkey { + debug!( + "Found node {}. Updating resources to {:?}", + n.node_pubkey, node_resource + ); + n.avail_vcpus = node_resource.avail_vcpus; + n.avail_mem_mb = node_resource.avail_memory_mb; + n.avail_storage_mb = node_resource.avail_storage_mb; + n.max_ports_per_app = node_resource.max_ports_per_app; + n.avail_no_of_port = node_resource.avail_no_of_port; + return; + } + } + debug!( + "VM Node {} not found when trying to update resources.", + node_resource.node_pubkey + ); + debug!("VM Node list:\n{:?}", nodes); + } + pub async fn send_new_container_req(&self, mut req: NewAppReq, tx: OneshotSender) { req.uuid = uuid::Uuid::new_v4().to_string(); diff --git a/src/grpc.rs b/src/grpc.rs index f0d09ea..2fb07bd 100644 --- a/src/grpc.rs +++ b/src/grpc.rs @@ -433,7 +433,7 @@ impl BrainAppDaemon for BrainAppDaemonMock { ) -> Result, Status> { let req_data = check_sig_from_req(req)?; log::info!( - "registering app node_key : {}, owner_key: {}", + "registering app node_key : {}, operator_key: {}", &req_data.node_pubkey, &req_data.operator_pubkey ); @@ -525,19 +525,15 @@ impl BrainAppDaemon for BrainAppDaemonMock { Ok(msg) => match msg.msg { Some(detee_shared::sgx::pb::brain::daemon_message_app::Msg::Auth( daemon_auth, - )) => { - pubkey = daemon_auth.pubkey; - } + )) => pubkey = daemon_auth.pubkey, Some(detee_shared::sgx::pb::brain::daemon_message_app::Msg::NewAppRes( new_app_res, - )) => { - self.data.send_new_container_resp(new_app_res).await; - } + )) => self.data.send_new_container_resp(new_app_res).await, Some( - detee_shared::sgx::pb::brain::daemon_message_app::Msg::AppNodeResources(_), - ) => { - todo!("AppNodeResources not implemented yet"); - } + detee_shared::sgx::pb::brain::daemon_message_app::Msg::AppNodeResources( + node_resource, + ), + ) => self.data.submit_app_node_resources(node_resource), _ => { dbg!("None"); } -- 2.43.0 From 70bf3b5e73c113fb95161ae9ddb4a66d26238689 Mon Sep 17 00:00:00 2001 From: ghe0 Date: Thu, 6 Mar 2025 01:30:51 +0200 Subject: [PATCH 17/29] pass update error from daemon to CLI --- src/data.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/data.rs b/src/data.rs index 13e02e0..17add83 100644 --- a/src/data.rs +++ b/src/data.rs @@ -661,6 +661,11 @@ impl BrainData { return; } }; + if let Err(e) = update_vm_req.1.send(update_vm_resp.clone()) { + log::warn!( + "CLI RX dropped before receiving UpdateVMResp {update_vm_resp:?}. Error: {e:?}" + ); + } if update_vm_resp.error != "" { return; } @@ -697,11 +702,6 @@ impl BrainData { update_vm_resp.error = "VM Contract not found.".to_string(); } } - if let Err(e) = update_vm_req.1.send(update_vm_resp.clone()) { - log::warn!( - "CLI RX dropped before receiving UpdateVMResp {update_vm_resp:?}. Error: {e:?}" - ); - } } pub async fn submit_newvm_req( -- 2.43.0 From dcdfe21c57dc7e3ced78e9510d435d7c8f8670d4 Mon Sep 17 00:00:00 2001 From: Noor Date: Thu, 6 Mar 2025 01:10:41 +0000 Subject: [PATCH 18/29] update detee-shared dependency to latest commit in stable_01 branch --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 98af335..e4b151e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -418,7 +418,7 @@ dependencies = [ [[package]] name = "detee-shared" version = "0.1.0" -source = "git+ssh://git@gitea.detee.cloud/noormohammedb/detee-shared?branch=stable_01#a6baa4059d2836b88d52cd56216993e92e83ecbc" +source = "git+ssh://git@gitea.detee.cloud/noormohammedb/detee-shared?branch=stable_01#9ba3bc318617b08d8c761767a23abd77519b7e6d" dependencies = [ "base64", "prost", -- 2.43.0 From a46f6ef800d155c6b8630903711acda50c9719d1 Mon Sep 17 00:00:00 2001 From: Noor Date: Thu, 6 Mar 2025 18:10:47 +0000 Subject: [PATCH 19/29] Store hratls pubkey and mr_enclave of public package in app contract --- Cargo.lock | 2 +- src/data.rs | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index e4b151e..68bb50f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -418,7 +418,7 @@ dependencies = [ [[package]] name = "detee-shared" version = "0.1.0" -source = "git+ssh://git@gitea.detee.cloud/noormohammedb/detee-shared?branch=stable_01#9ba3bc318617b08d8c761767a23abd77519b7e6d" +source = "git+ssh://git@gitea.detee.cloud/noormohammedb/detee-shared?branch=stable_01#b8f37dec1845d29ea0b69035712e6ebb214376f4" dependencies = [ "base64", "prost", diff --git a/src/data.rs b/src/data.rs index 867b2c6..1fe8714 100644 --- a/src/data.rs +++ b/src/data.rs @@ -158,6 +158,8 @@ pub struct AppContract { pub price_per_unit: u64, pub locked_nano: u64, pub collected_at: chrono::DateTime, + pub hratls_pubkey: String, + pub public_package_mr_enclave: Option>, } impl From for AppContractPB { @@ -189,6 +191,8 @@ impl From for AppContractPB { nano_per_minute: value.price_per_unit, locked_nano: value.locked_nano, collected_at: value.collected_at.to_rfc3339(), + hratls_pubkey: value.hratls_pubkey, + public_package_mr_enclave: value.public_package_mr_enclave, } } } @@ -977,6 +981,8 @@ impl BrainData { price_per_unit: new_container_req.0.price_per_unit, locked_nano: new_container_req.0.locked_nano, collected_at: Utc::now(), + hratls_pubkey: new_container_req.0.hratls_pubkey, + public_package_mr_enclave: new_container_req.0.public_package_mr_enclave, }; log::info!("Created new app contract: {app_contracts:?}"); self.app_contracts.write().unwrap().push(app_contracts); -- 2.43.0 From a90a6c0b79f2fa38b1bd88033db9d18a49715203 Mon Sep 17 00:00:00 2001 From: Noor Date: Fri, 7 Mar 2025 14:12:59 +0000 Subject: [PATCH 20/29] fix contract creation on failed deployment --- src/data.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/data.rs b/src/data.rs index 1fe8714..302837a 100644 --- a/src/data.rs +++ b/src/data.rs @@ -892,7 +892,7 @@ impl BrainData { self.del_app_daemon_tx(&req.node_pubkey); self.send_new_container_resp(NewAppRes { uuid: req.uuid, - status: "Failed".to_string(), + status: "failed".to_string(), error: "Daemon is offline.".to_string(), ..Default::default() }) @@ -960,6 +960,10 @@ impl BrainData { ); } + if new_container_resp.error != "" { + return; + } + let requested_resource = new_container_req.0.resource.clone().unwrap_or_default(); let app_contracts = AppContract { -- 2.43.0 From 92a26c5f66ee4b2273b42f3fc92ca54813b4f026 Mon Sep 17 00:00:00 2001 From: Noor Date: Mon, 10 Mar 2025 08:56:22 +0000 Subject: [PATCH 21/29] Integrate operator in app node handling rename operator_pubkey to operator_wallet --- Cargo.lock | 2 +- src/data.rs | 32 ++++++++++++++++++++++---------- src/grpc.rs | 12 ++++-------- 3 files changed, 27 insertions(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 968e92a..4159c3b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -420,7 +420,7 @@ dependencies = [ [[package]] name = "detee-shared" version = "0.1.0" -source = "git+ssh://git@gitea.detee.cloud/noormohammedb/detee-shared?branch=stable_01#b8f37dec1845d29ea0b69035712e6ebb214376f4" +source = "git+ssh://git@gitea.detee.cloud/noormohammedb/detee-shared?branch=stable_01#dc1e2dd0cd70f0915245fe3cae0863eab838fe5a" dependencies = [ "base64", "prost", diff --git a/src/data.rs b/src/data.rs index 3614099..0aeef2f 100644 --- a/src/data.rs +++ b/src/data.rs @@ -56,6 +56,7 @@ pub struct OperatorData { pub email: String, pub banned_users: HashSet, pub vm_nodes: HashSet, + pub app_nodes: HashSet, } impl From for grpc::AccountBalance { @@ -230,7 +231,7 @@ impl From for AppContractPB { #[derive(Eq, Hash, PartialEq, Clone, Debug, Default, Serialize, Deserialize)] pub struct AppNode { pub node_pubkey: String, - pub operator_pubkey: String, + pub operator_wallet: String, pub country: String, pub region: String, pub city: String, @@ -246,13 +247,6 @@ pub struct AppNode { #[derive(Default, Serialize, Deserialize)] pub struct BrainData { - // amount of nanoLP in each account - // accounts: DashMap, - // vm_nodes: RwLock>, - // vm_contracts: RwLock>, - // tmp_newvm_reqs: DashMap)>, - // tmp_updatevm_reqs: DashMap)>, - // daemon_tx: DashMap>, accounts: DashMap, operators: DashMap, vm_nodes: RwLock>, @@ -950,6 +944,7 @@ impl BrainData { email: String::new(), banned_users: HashSet::new(), vm_nodes: HashSet::from([node_pubkey.to_string()]), + app_nodes: HashSet::new(), }); } @@ -1153,7 +1148,9 @@ impl BrainData { self.app_daemon_tx.remove(node_pubkey); } - pub fn insert_app_node(&self, node: AppNode) { + pub fn register_app_node(&self, node: AppNode) { + info!("Registering app node {node:?}"); + self.add_app_node_to_operator(&node.operator_wallet, &node.node_pubkey); let mut nodes = self.app_nodes.write().unwrap(); for n in nodes.iter_mut() { if n.node_pubkey == node.node_pubkey { @@ -1166,6 +1163,21 @@ impl BrainData { nodes.push(node); } + pub fn add_app_node_to_operator(&self, operator_wallet: &str, node_pubkey: &str) { + self.operators + .entry(operator_wallet.to_string()) + .and_modify(|op| { + op.app_nodes.insert(node_pubkey.to_string()); + }) + .or_insert(OperatorData { + escrow: 0, + email: String::new(), + banned_users: HashSet::new(), + vm_nodes: HashSet::new(), + app_nodes: HashSet::from([node_pubkey.to_string()]), + }); + } + pub fn find_app_contract_by_uuid(&self, uuid: &str) -> Option { let contracts = self.app_contracts.read().unwrap(); contracts.iter().find(|c| c.uuid == uuid).cloned() @@ -1185,7 +1197,7 @@ impl BrainData { contracts } - pub fn find_app_contracts_by_node_pubkey(&self, node_pubkey: &str) -> Vec { + pub fn find_app_contracts_by_node(&self, node_pubkey: &str) -> Vec { let app_contracts = self.app_contracts.read().unwrap(); app_contracts .iter() diff --git a/src/grpc.rs b/src/grpc.rs index d8d313a..c7698b9 100644 --- a/src/grpc.rs +++ b/src/grpc.rs @@ -531,26 +531,22 @@ impl BrainAppDaemon for BrainAppDaemonMock { log::info!( "registering app node_key : {}, operator_key: {}", &req_data.node_pubkey, - &req_data.operator_pubkey + &req_data.operator_wallet ); let app_node = crate::data::AppNode { node_pubkey: req_data.node_pubkey.clone(), - operator_pubkey: req_data.operator_pubkey, + operator_wallet: req_data.operator_wallet, ip: req_data.main_ip, city: req_data.city, region: req_data.region, country: req_data.country, ..Default::default() }; + self.data.register_app_node(app_node); - self.data.insert_app_node(app_node); log::info!("Sending existing contracts to {}", &req_data.node_pubkey); - - let app_contracts = self - .data - .find_app_contracts_by_node_pubkey(&req_data.node_pubkey); - + let app_contracts = self.data.find_app_contracts_by_node(&req_data.node_pubkey); let (tx, rx) = mpsc::channel(6); tokio::spawn(async move { for contract in app_contracts { -- 2.43.0 From 928a7d029be115c4b02f8a206365dbfb6e68289d Mon Sep 17 00:00:00 2001 From: Noor Date: Tue, 11 Mar 2025 10:36:20 +0000 Subject: [PATCH 22/29] Implement app contracts pricing methods with cron job --- src/data.rs | 101 ++++++++++++++++++++++++++++++++++++++++++++++++++-- src/main.rs | 1 + 2 files changed, 100 insertions(+), 2 deletions(-) diff --git a/src/data.rs b/src/data.rs index 0aeef2f..fd2039c 100644 --- a/src/data.rs +++ b/src/data.rs @@ -1,7 +1,6 @@ use crate::grpc::snp_proto::{self as grpc}; use chrono::Utc; use dashmap::DashMap; -use detee_shared::sgx::pb::brain::DelAppReq; use log::{debug, info, warn}; use serde::{Deserialize, Serialize}; use std::str::FromStr; @@ -14,10 +13,12 @@ use std::{ use tokio::sync::mpsc::Sender; use tokio::sync::oneshot::Sender as OneshotSender; +use detee_shared::sgx::pb::brain::brain_message_app; use detee_shared::sgx::pb::brain::AppContract as AppContractPB; use detee_shared::sgx::pb::brain::AppNodeResources; use detee_shared::sgx::pb::brain::AppResource as AppResourcePB; use detee_shared::sgx::pb::brain::BrainMessageApp; +use detee_shared::sgx::pb::brain::DelAppReq; use detee_shared::sgx::pb::brain::MappedPort; use detee_shared::sgx::pb::brain::NewAppReq; use detee_shared::sgx::pb::brain::NewAppRes; @@ -39,7 +40,7 @@ pub enum Error { AccessDenied, } -#[derive(Clone, Default, Serialize, Deserialize)] +#[derive(Clone, Default, Serialize, Deserialize, Debug)] pub struct AccountData { pub balance: u64, pub tmp_locked: u64, @@ -193,6 +194,24 @@ pub struct AppContract { pub public_package_mr_enclave: Option>, } +impl AppContract { + fn total_units(&self) -> u64 { + // TODO: Optimize this based on price of hardware. + (self.vcpus as u64 * 10) + + (self.memory_mb as u64 / 200) + + (self.disk_size_mb as u64 / 10000) + } + + pub fn price_estimate(vcpus: u32, memory_mb: u32, disk_size_mb: u32) -> u64 { + (vcpus as u64 * 10) + (memory_mb as u64 / 200) + (disk_size_mb as u64 / 10000) + } + + /// Returns price per minute in nanoLP + fn price_per_minute(&self) -> u64 { + self.total_units() * self.price_per_unit + } +} + impl From for AppContractPB { fn from(value: AppContract) -> Self { let mapped_ports = value @@ -243,6 +262,8 @@ pub struct AppNode { pub max_ports_per_app: u32, // nanotokens per unit per minute pub price: u64, + + pub offline_minutes: u64, } #[derive(Default, Serialize, Deserialize)] @@ -1183,6 +1204,11 @@ impl BrainData { contracts.iter().find(|c| c.uuid == uuid).cloned() } + pub fn find_app_node_by_pubkey(&self, public_key: &str) -> Option { + let nodes = self.app_nodes.read().unwrap(); + nodes.iter().cloned().find(|n| n.node_pubkey == public_key) + } + pub fn find_app_contracts_by_admin_pubkey(&self, admin_pubkey: &str) -> Vec { debug!("Searching contracts for admin pubkey {admin_pubkey}"); let contracts: Vec = self @@ -1206,6 +1232,60 @@ impl BrainData { .collect() } + pub async fn app_contracts_cron(&self) { + let mut deleted_app_contracts: Vec<(String, String)> = Vec::new(); + log::debug!("Running app contracts cron..."); + { + let mut app_contracts = self.app_contracts.write().unwrap(); + app_contracts.retain_mut(|c| { + let node = self.find_app_node_by_pubkey(&c.node_pubkey).unwrap(); + if node.offline_minutes == 0 { + let operator_wallet = node.operator_wallet.clone(); + let minutes_to_collect = (Utc::now() - c.collected_at).num_minutes() as u64; + c.collected_at = Utc::now(); + dbg!(&minutes_to_collect); + dbg!(&c.price_per_minute()); + let mut nanolp_to_collect = + c.price_per_minute().saturating_mul(minutes_to_collect); + if nanolp_to_collect > c.locked_nano { + nanolp_to_collect = c.locked_nano; + } + dbg!(&nanolp_to_collect); + log::debug!("Removing {nanolp_to_collect} nanoLP from {}", c.uuid); + c.locked_nano -= nanolp_to_collect; + let escrow_multiplier = match self.operators.get(&operator_wallet) { + Some(op) if op.escrow > 5000 => match self.operators.get(&c.admin_pubkey) { + Some(user_is_op) if user_is_op.escrow > 5000 => 1, + _ => 5, + }, + _ => 1, + }; + self.add_nano_to_wallet( + &operator_wallet, + nanolp_to_collect * escrow_multiplier, + ); + if c.locked_nano == 0 { + deleted_app_contracts.push((c.uuid.clone(), c.node_pubkey.clone())); + } + } + c.locked_nano > 0 + }); + } + // inform daemons of the deletion of the contracts + for (uuid, node_pubkey) in deleted_app_contracts.iter() { + if let Some(app_daemon_tx) = self.app_daemon_tx.get(&node_pubkey.clone()) { + let msg = BrainMessageApp { + msg: Some(brain_message_app::Msg::DeleteAppReq(DelAppReq { + uuid: uuid.to_string(), + admin_pubkey: String::new(), + })), + }; + let app_daemon_tx = app_daemon_tx.clone(); + let _ = app_daemon_tx.send(msg).await; + } + } + } + pub fn submit_app_node_resources(&self, node_resource: AppNodeResources) { debug!("{:#?}", &node_resource); let mut nodes = self.app_nodes.write().unwrap(); @@ -1231,6 +1311,23 @@ impl BrainData { } pub async fn send_new_container_req(&self, mut req: NewAppReq, tx: OneshotSender) { + // TODO: make sure locked_nano in cli + if req.locked_nano == 0 { + let resource = req.resource.clone().unwrap_or_default(); + req.locked_nano = + AppContract::price_estimate(resource.vcpu, resource.memory_mb, resource.disk_mb) + } + + if let Err(e) = self.lock_nanotockens(&req.admin_pubkey, req.locked_nano) { + let _ = tx.send(NewAppRes { + uuid: String::new(), + error: e.to_string(), + status: "failed".to_string(), + ..Default::default() + }); + return; + } + req.uuid = uuid::Uuid::new_v4().to_string(); info!("Inserting new container request in memory: {req:?}"); diff --git a/src/main.rs b/src/main.rs index ccc93bf..f9b1041 100644 --- a/src/main.rs +++ b/src/main.rs @@ -25,6 +25,7 @@ async fn main() { tokio::time::sleep(tokio::time::Duration::from_secs(60)).await; data_clone.vm_nodes_cron().await; data_clone.vm_contracts_cron().await; + data_clone.app_contracts_cron().await; if let Err(e) = data_clone.save_to_disk() { log::error!("Could not save data to disk due to error: {e}") } -- 2.43.0 From 41352f2c3380d454bf9645719bdd039be47e0bc6 Mon Sep 17 00:00:00 2001 From: Noor Date: Tue, 11 Mar 2025 12:30:01 +0000 Subject: [PATCH 23/29] improved AppContract pricing calculation precision --- Cargo.lock | 2 +- src/data.rs | 24 +++++++----------------- 2 files changed, 8 insertions(+), 18 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4159c3b..6daadc6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -420,7 +420,7 @@ dependencies = [ [[package]] name = "detee-shared" version = "0.1.0" -source = "git+ssh://git@gitea.detee.cloud/noormohammedb/detee-shared?branch=stable_01#dc1e2dd0cd70f0915245fe3cae0863eab838fe5a" +source = "git+ssh://git@gitea.detee.cloud/noormohammedb/detee-shared?branch=stable_01#a734b392b7dba31693337b5a806d30cc9b8cd967" dependencies = [ "base64", "prost", diff --git a/src/data.rs b/src/data.rs index fd2039c..5010a29 100644 --- a/src/data.rs +++ b/src/data.rs @@ -195,20 +195,16 @@ pub struct AppContract { } impl AppContract { - fn total_units(&self) -> u64 { + fn total_units(&self) -> f64 { // TODO: Optimize this based on price of hardware. - (self.vcpus as u64 * 10) - + (self.memory_mb as u64 / 200) - + (self.disk_size_mb as u64 / 10000) - } - - pub fn price_estimate(vcpus: u32, memory_mb: u32, disk_size_mb: u32) -> u64 { - (vcpus as u64 * 10) + (memory_mb as u64 / 200) + (disk_size_mb as u64 / 10000) + (self.vcpus as f64 * 10f64) + + (self.memory_mb as f64 / 200f64) + + (self.disk_size_mb as f64 / 10000f64) } /// Returns price per minute in nanoLP fn price_per_minute(&self) -> u64 { - self.total_units() * self.price_per_unit + (self.total_units() * self.price_per_unit as f64) as u64 } } @@ -221,6 +217,7 @@ impl From for AppContractPB { .map(MappedPort::from) .collect(); + let nano_per_minute = value.price_per_minute(); let resource = Some(AppResourcePB { memory_mb: value.memory_mb, disk_mb: value.disk_size_mb, @@ -238,7 +235,7 @@ impl From for AppContractPB { created_at: value.created_at.to_rfc3339(), updated_at: value.updated_at.to_rfc3339(), // TODO: check while implementing pricing - nano_per_minute: value.price_per_unit, + nano_per_minute, locked_nano: value.locked_nano, collected_at: value.collected_at.to_rfc3339(), hratls_pubkey: value.hratls_pubkey, @@ -1311,13 +1308,6 @@ impl BrainData { } pub async fn send_new_container_req(&self, mut req: NewAppReq, tx: OneshotSender) { - // TODO: make sure locked_nano in cli - if req.locked_nano == 0 { - let resource = req.resource.clone().unwrap_or_default(); - req.locked_nano = - AppContract::price_estimate(resource.vcpu, resource.memory_mb, resource.disk_mb) - } - if let Err(e) = self.lock_nanotockens(&req.admin_pubkey, req.locked_nano) { let _ = tx.send(NewAppRes { uuid: String::new(), -- 2.43.0 From ddbde12f4209df8c72799e56cdbd4f51bf1512e4 Mon Sep 17 00:00:00 2001 From: Noor Date: Tue, 11 Mar 2025 15:55:23 +0000 Subject: [PATCH 24/29] fix: locked fund fix: node offline error on cli revert locked fund when app deploye failed delete app contract returns contract's locked LP into admin --- src/data.rs | 94 ++++++++++++++++++++++++++++++++--------------------- 1 file changed, 57 insertions(+), 37 deletions(-) diff --git a/src/data.rs b/src/data.rs index 5010a29..c6e76b4 100644 --- a/src/data.rs +++ b/src/data.rs @@ -38,6 +38,9 @@ pub enum Error { ImpossibleError, #[error("You don't have the required permissions for this operation.")] AccessDenied, + + #[error("Could not find contract {0}")] + AppContractNotFound(String), } #[derive(Clone, Default, Serialize, Deserialize, Debug)] @@ -1196,9 +1199,13 @@ impl BrainData { }); } - pub fn find_app_contract_by_uuid(&self, uuid: &str) -> Option { + pub fn find_app_contract_by_uuid(&self, uuid: &str) -> Result { let contracts = self.app_contracts.read().unwrap(); - contracts.iter().find(|c| c.uuid == uuid).cloned() + contracts + .iter() + .find(|c| c.uuid == uuid) + .cloned() + .ok_or(Error::AppContractNotFound(uuid.to_string())) } pub fn find_app_node_by_pubkey(&self, public_key: &str) -> Option { @@ -1349,46 +1356,50 @@ impl BrainData { }) .await; } + } else { + self.send_new_container_resp(NewAppRes { + status: "failed".to_string(), + error: "Daemon is offline.".to_string(), + uuid: req.uuid, + ..Default::default() + }) + .await; } - // TODO: implement daemon offline handling } - pub async fn send_del_container_req( - &self, - req: DelAppReq, - ) -> Result<(), Box> { - if let Some(app_contract) = self.find_app_contract_by_uuid(&req.uuid) { - info!("Found app contract {}. Deleting...", &req.uuid); - if let Some(app_daemon_tx) = self.app_daemon_tx.get(&app_contract.node_pubkey) { - debug!( - "TX for daemon {} found. Informing daemon about deletion of {}.", - app_contract.node_pubkey, &req.uuid - ); - let msg = BrainMessageApp { - msg: Some( - detee_shared::sgx::pb::brain::brain_message_app::Msg::DeleteAppReq( - req.clone(), - ), - ), - }; + pub async fn send_del_container_req(&self, req: DelAppReq) -> Result<(), Error> { + log::debug!("Starting deletion of app {}", req.uuid); + let app_contract = self.find_app_contract_by_uuid(&req.uuid)?; - if let Err(e) = app_daemon_tx.send(msg).await { - warn!( - "Failed to send deletion request to {} due to error: {e:?}", - app_contract.node_pubkey - ); - info!("Deleting daemon TX for {}", app_contract.node_pubkey); - self.del_app_daemon_tx(&app_contract.node_pubkey); - } - } - - let mut app_contracts = self.app_contracts.write().unwrap(); - app_contracts.retain(|c| c.uuid != req.uuid); - - Ok(()) - } else { - Err("Contract not found".into()) + if app_contract.admin_pubkey != req.admin_pubkey { + return Err(Error::AccessDenied); } + + info!("Found app contract {}. Deleting...", &req.uuid); + if let Some(app_daemon_tx) = self.app_daemon_tx.get(&app_contract.node_pubkey) { + debug!( + "TX for daemon {} found. Informing daemon about deletion of {}.", + app_contract.node_pubkey, &req.uuid + ); + let msg = BrainMessageApp { + msg: Some(brain_message_app::Msg::DeleteAppReq(req.clone())), + }; + + if let Err(e) = app_daemon_tx.send(msg).await { + warn!( + "Failed to send deletion request to {} due to error: {e:?}", + app_contract.node_pubkey + ); + info!("Deleting daemon TX for {}", app_contract.node_pubkey); + self.del_app_daemon_tx(&app_contract.node_pubkey); + } + } + + self.add_nano_to_wallet(&app_contract.admin_pubkey, app_contract.locked_nano); + let mut app_contracts = self.app_contracts.write().unwrap(); + app_contracts.retain(|c| c.uuid != req.uuid); + + Ok(()) } pub async fn send_new_container_resp(&self, new_container_resp: NewAppRes) { @@ -1412,9 +1423,18 @@ impl BrainData { } if new_container_resp.error != "" { + if let Some(mut admin_wallet) = self.accounts.get_mut(&new_container_req.0.admin_pubkey) + { + admin_wallet.balance += new_container_req.0.locked_nano; + admin_wallet.tmp_locked -= new_container_req.0.locked_nano; + } return; } + if let Some(mut admin_wallet) = self.accounts.get_mut(&new_container_req.0.admin_pubkey) { + admin_wallet.tmp_locked -= new_container_req.0.locked_nano; + } + let requested_resource = new_container_req.0.resource.clone().unwrap_or_default(); let app_contracts = AppContract { -- 2.43.0 From 1fa37a0bb96e84925fa051aa96601490a5bf2b59 Mon Sep 17 00:00:00 2001 From: Noor Date: Wed, 12 Mar 2025 15:39:30 +0000 Subject: [PATCH 25/29] rename create_app BrainAppCli grpc call to deploy_app --- Cargo.lock | 2 +- src/grpc.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6daadc6..655bc74 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -420,7 +420,7 @@ dependencies = [ [[package]] name = "detee-shared" version = "0.1.0" -source = "git+ssh://git@gitea.detee.cloud/noormohammedb/detee-shared?branch=stable_01#a734b392b7dba31693337b5a806d30cc9b8cd967" +source = "git+ssh://git@gitea.detee.cloud/noormohammedb/detee-shared?branch=stable_01#8230e1f831d0f88fe5203646e323bf215a825574" dependencies = [ "base64", "prost", diff --git a/src/grpc.rs b/src/grpc.rs index c7698b9..371ca0d 100644 --- a/src/grpc.rs +++ b/src/grpc.rs @@ -458,7 +458,7 @@ trait PubkeyGetter { impl BrainAppCli for BrainAppCliMock { type ListAppContractsStream = Pin> + Send>>; - async fn create_app( + async fn deploy_app( &self, req: tonic::Request, ) -> Result, Status> { -- 2.43.0 From 952c62c2188f4511c027bbc79023636d9e9b68d8 Mon Sep 17 00:00:00 2001 From: Noor Date: Wed, 12 Mar 2025 20:05:27 +0000 Subject: [PATCH 26/29] feat: add filtering and listing functionality for app nodes --- Cargo.lock | 2 +- src/data.rs | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/grpc.rs | 37 +++++++++++++++++++++++++++++++++++-- 3 files changed, 88 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 655bc74..6aef7a2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -420,7 +420,7 @@ dependencies = [ [[package]] name = "detee-shared" version = "0.1.0" -source = "git+ssh://git@gitea.detee.cloud/noormohammedb/detee-shared?branch=stable_01#8230e1f831d0f88fe5203646e323bf215a825574" +source = "git+ssh://git@gitea.detee.cloud/noormohammedb/detee-shared?branch=stable_01#a9dcfbe9fec5a86cd69aa2853db124aae9b85598" dependencies = [ "base64", "prost", diff --git a/src/data.rs b/src/data.rs index c6e76b4..0d22f82 100644 --- a/src/data.rs +++ b/src/data.rs @@ -15,6 +15,8 @@ use tokio::sync::oneshot::Sender as OneshotSender; use detee_shared::sgx::pb::brain::brain_message_app; use detee_shared::sgx::pb::brain::AppContract as AppContractPB; +use detee_shared::sgx::pb::brain::AppNodeFilters; +use detee_shared::sgx::pb::brain::AppNodeListResp; use detee_shared::sgx::pb::brain::AppNodeResources; use detee_shared::sgx::pb::brain::AppResource as AppResourcePB; use detee_shared::sgx::pb::brain::BrainMessageApp; @@ -266,6 +268,21 @@ pub struct AppNode { pub offline_minutes: u64, } +impl From for AppNodeListResp { + fn from(value: AppNode) -> Self { + Self { + operator: value.operator_wallet, + node_pubkey: value.node_pubkey, + country: value.country, + region: value.region, + city: value.city, + ip: value.ip, + price: value.price, + reports: Vec::new(), + } + } +} + #[derive(Default, Serialize, Deserialize)] pub struct BrainData { accounts: DashMap, @@ -1199,6 +1216,41 @@ impl BrainData { }); } + pub fn find_app_nodes_by_filters(&self, filters: &AppNodeFilters) -> Vec { + let nodes = self.app_nodes.read().unwrap(); + nodes + .iter() + .filter(|n| { + n.avail_vcpus >= filters.vcpus + && n.avail_mem_mb >= filters.memory_mb + && n.avail_storage_mb >= filters.storage_mb + && (filters.country.is_empty() || (n.country == filters.country)) + && (filters.city.is_empty() || (n.city == filters.city)) + && (filters.region.is_empty() || (n.region == filters.region)) + && (filters.ip.is_empty() || (n.ip == filters.ip)) + }) + .cloned() + .collect() + } + + // TODO: sort by rating + pub fn get_one_app_node_by_filters(&self, filters: &AppNodeFilters) -> Option { + let nodes = self.app_nodes.read().unwrap(); + nodes + .iter() + .find(|n| { + n.avail_vcpus >= filters.vcpus + && n.avail_mem_mb >= filters.memory_mb + && n.avail_storage_mb >= filters.storage_mb + && (filters.country.is_empty() || (n.country == filters.country)) + && (filters.city.is_empty() || (n.city == filters.city)) + && (filters.region.is_empty() || (n.region == filters.region)) + && (filters.ip.is_empty() || (n.ip == filters.ip)) + && (filters.node_pubkey.is_empty() || (n.node_pubkey == filters.node_pubkey)) + }) + .cloned() + } + pub fn find_app_contract_by_uuid(&self, uuid: &str) -> Result { let contracts = self.app_contracts.read().unwrap(); contracts diff --git a/src/grpc.rs b/src/grpc.rs index 371ca0d..c4113fb 100644 --- a/src/grpc.rs +++ b/src/grpc.rs @@ -17,8 +17,8 @@ use tonic::{Request, Response, Status, Streaming}; use detee_shared::sgx::pb::brain::brain_app_cli_server::BrainAppCli; use detee_shared::sgx::pb::brain::brain_app_daemon_server::BrainAppDaemon; use detee_shared::sgx::pb::brain::{ - AppContract, BrainMessageApp, DaemonMessageApp, DelAppReq, ListAppContractsReq, NewAppReq, - NewAppRes, RegisterAppNodeReq, + AppContract, AppNodeFilters, AppNodeListResp, BrainMessageApp, DaemonMessageApp, DelAppReq, + ListAppContractsReq, NewAppReq, NewAppRes, RegisterAppNodeReq, }; const ADMIN_ACCOUNTS: &[&str] = &[ "x52w7jARC5erhWWK65VZmjdGXzBK6ZDgfv1A283d8XK", @@ -457,6 +457,7 @@ trait PubkeyGetter { #[tonic::async_trait] impl BrainAppCli for BrainAppCliMock { type ListAppContractsStream = Pin> + Send>>; + type ListAppNodesStream = Pin> + Send>>; async fn deploy_app( &self, @@ -516,6 +517,37 @@ impl BrainAppCli for BrainAppCliMock { Box::pin(output_stream) as Self::ListAppContractsStream )) } + + async fn list_app_nodes( + &self, + req: tonic::Request, + ) -> Result, Status> { + let req = check_sig_from_req(req)?; + info!("CLI requested ListAppNodes: {req:?}"); + let nodes = self.data.find_app_nodes_by_filters(&req); + let (tx, rx) = mpsc::channel(6); + tokio::spawn(async move { + for node in nodes { + let _ = tx.send(Ok(node.into())).await; + } + }); + let output_stream = ReceiverStream::new(rx); + Ok(Response::new(Box::pin(output_stream))) + } + + async fn get_one_app_node( + &self, + req: tonic::Request, + ) -> Result, Status> { + let req = check_sig_from_req(req)?; + info!("CLI requested GetOneAppNode: {req:?}"); + match self.data.get_one_app_node_by_filters(&req) { + Some(node) => Ok(Response::new(node.into())), + None => Err(Status::not_found( + "Could not find any node based on your search criteria", + )), + } + } } #[tonic::async_trait] @@ -681,6 +713,7 @@ impl_pubkey_getter!(DelAppReq, admin_pubkey); impl_pubkey_getter!(ListAppContractsReq, admin_pubkey); impl_pubkey_getter!(RegisterAppNodeReq); +impl_pubkey_getter!(AppNodeFilters); fn check_sig_from_req(req: Request) -> Result { let time = match req.metadata().get("timestamp") { -- 2.43.0 From 0aa484f79ef6537dd2523a2e31b06dacf497c659 Mon Sep 17 00:00:00 2001 From: Noor Date: Wed, 12 Mar 2025 20:12:35 +0000 Subject: [PATCH 27/29] feat: add method to list all app contracts for super admin and update gRPC interface --- Cargo.lock | 2 +- src/data.rs | 5 +++++ src/grpc.rs | 20 ++++++++++++++++++++ 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 6aef7a2..2796f00 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -420,7 +420,7 @@ dependencies = [ [[package]] name = "detee-shared" version = "0.1.0" -source = "git+ssh://git@gitea.detee.cloud/noormohammedb/detee-shared?branch=stable_01#a9dcfbe9fec5a86cd69aa2853db124aae9b85598" +source = "git+ssh://git@gitea.detee.cloud/noormohammedb/detee-shared?branch=stable_01#d6ffc0d4cb5ed5533379f82d04d15ae286fb49b9" dependencies = [ "base64", "prost", diff --git a/src/data.rs b/src/data.rs index 0d22f82..822a37c 100644 --- a/src/data.rs +++ b/src/data.rs @@ -1279,6 +1279,11 @@ impl BrainData { contracts } + pub fn list_all_app_contracts(&self) -> Vec { + let contracts = self.app_contracts.read().unwrap(); + contracts.iter().cloned().collect() + } + pub fn find_app_contracts_by_node(&self, node_pubkey: &str) -> Vec { let app_contracts = self.app_contracts.read().unwrap(); app_contracts diff --git a/src/grpc.rs b/src/grpc.rs index c4113fb..c5a7102 100644 --- a/src/grpc.rs +++ b/src/grpc.rs @@ -458,6 +458,8 @@ trait PubkeyGetter { impl BrainAppCli for BrainAppCliMock { type ListAppContractsStream = Pin> + Send>>; type ListAppNodesStream = Pin> + Send>>; + type ListAllAppContractsStream = + Pin> + Send>>; async fn deploy_app( &self, @@ -548,6 +550,23 @@ impl BrainAppCli for BrainAppCliMock { )), } } + + async fn list_all_app_contracts( + &self, + req: tonic::Request, + ) -> Result, Status> { + check_admin_key(&req)?; + let _ = check_sig_from_req(req)?; + let contracts = self.data.list_all_app_contracts(); + let (tx, rx) = mpsc::channel(6); + tokio::spawn(async move { + for contract in contracts { + let _ = tx.send(Ok(contract.into())).await; + } + }); + let output_stream = ReceiverStream::new(rx); + Ok(Response::new(Box::pin(output_stream))) + } } #[tonic::async_trait] @@ -713,6 +732,7 @@ impl_pubkey_getter!(DelAppReq, admin_pubkey); impl_pubkey_getter!(ListAppContractsReq, admin_pubkey); impl_pubkey_getter!(RegisterAppNodeReq); +impl_pubkey_getter!(detee_shared::sgx::pb::brain::Empty); impl_pubkey_getter!(AppNodeFilters); fn check_sig_from_req(req: Request) -> Result { -- 2.43.0 From 68e03a25e593e381fe27a05201cabd1edc8cfc2d Mon Sep 17 00:00:00 2001 From: Noor Date: Thu, 13 Mar 2025 06:27:38 +0000 Subject: [PATCH 28/29] fix: Reduce CPU pricing for total_units calculation in AppContract --- src/data.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/data.rs b/src/data.rs index 822a37c..6613202 100644 --- a/src/data.rs +++ b/src/data.rs @@ -202,7 +202,7 @@ pub struct AppContract { impl AppContract { fn total_units(&self) -> f64 { // TODO: Optimize this based on price of hardware. - (self.vcpus as f64 * 10f64) + (self.vcpus as f64 * 5f64) + (self.memory_mb as f64 / 200f64) + (self.disk_size_mb as f64 / 10000f64) } -- 2.43.0 From acb11aae0394843e6307ae6aa6eb39850965f3d7 Mon Sep 17 00:00:00 2001 From: Noor Date: Fri, 14 Mar 2025 10:03:41 +0000 Subject: [PATCH 29/29] feat: add app_name to AppContract and update related implementations --- Cargo.lock | 2 +- src/data.rs | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2796f00..17c0d8f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -420,7 +420,7 @@ dependencies = [ [[package]] name = "detee-shared" version = "0.1.0" -source = "git+ssh://git@gitea.detee.cloud/noormohammedb/detee-shared?branch=stable_01#d6ffc0d4cb5ed5533379f82d04d15ae286fb49b9" +source = "git+ssh://git@gitea.detee.cloud/noormohammedb/detee-shared?branch=stable_01#099f0a0488bce8e59c9c9e9a5e9b1f24998f1633" dependencies = [ "base64", "prost", diff --git a/src/data.rs b/src/data.rs index 6613202..92a1ad4 100644 --- a/src/data.rs +++ b/src/data.rs @@ -197,6 +197,7 @@ pub struct AppContract { pub collected_at: chrono::DateTime, pub hratls_pubkey: String, pub public_package_mr_enclave: Option>, + pub app_name: String, } impl AppContract { @@ -239,12 +240,12 @@ impl From for AppContractPB { resource, created_at: value.created_at.to_rfc3339(), updated_at: value.updated_at.to_rfc3339(), - // TODO: check while implementing pricing nano_per_minute, locked_nano: value.locked_nano, collected_at: value.collected_at.to_rfc3339(), hratls_pubkey: value.hratls_pubkey, public_package_mr_enclave: value.public_package_mr_enclave, + app_name: value.app_name, } } } @@ -1515,6 +1516,7 @@ impl BrainData { collected_at: Utc::now(), hratls_pubkey: new_container_req.0.hratls_pubkey, public_package_mr_enclave: new_container_req.0.public_package_mr_enclave, + app_name: new_container_req.0.app_name, }; log::info!("Created new app contract: {app_contracts:?}"); self.app_contracts.write().unwrap().push(app_contracts); -- 2.43.0