From dcacd7b6b0ddddb2d1d5a3b32e26f367c5380c66 Mon Sep 17 00:00:00 2001 From: Noor Date: Fri, 20 Jun 2025 15:49:46 +0530 Subject: [PATCH 1/2] App contract payment implement online/offline status updates add timer to charge app contract modified app_node in schema --- scripts/deploy.sh | 2 +- src/db/app.rs | 18 ++++++++++++++++-- src/grpc/app.rs | 7 +++++-- surql/tables.sql | 3 ++- surql/timer.sql | 30 +++++++++++++++++++++++++++++- tests/grpc_app_cli_test.rs | 1 + 6 files changed, 54 insertions(+), 7 deletions(-) diff --git a/scripts/deploy.sh b/scripts/deploy.sh index 210fcd8..f4eb716 100755 --- a/scripts/deploy.sh +++ b/scripts/deploy.sh @@ -13,7 +13,7 @@ server="$1" } [[ "$server" == "testnet" ]] && server="root@prod-brain-1" -[[ "$server" == "staging" ]] && server="brain-staging" +[[ "$server" == "staging" ]] && server="root@brain-staging" cargo build --release --bin brain ssh $server systemctl stop detee-brain.service diff --git a/src/db/app.rs b/src/db/app.rs index f224c03..178a196 100644 --- a/src/db/app.rs +++ b/src/db/app.rs @@ -31,7 +31,8 @@ pub struct AppNode { pub avail_ports: u32, pub max_ports_per_app: u32, pub price: u64, - pub offline_minutes: u64, + pub connected_at: Datetime, + pub disconnected_at: Datetime, } impl AppNode { @@ -41,6 +42,18 @@ impl AppNode { let app_node: Option = db.upsert(app_node_id.clone()).content(self).await?; app_node.ok_or(Error::FailedToCreateDBEntry(format!("{APP_NODE}:{app_node_id}"))) } + + pub async fn set_online(db: &Surreal, app_node_id: &str) -> Result<(), Error> { + db.query(format!("UPDATE {APP_NODE}:{app_node_id} SET connected_at = time::now();")) + .await?; + Ok(()) + } + + pub async fn set_offline(db: &Surreal, app_node_id: &str) -> Result<(), Error> { + db.query(format!("UPDATE {APP_NODE}:{app_node_id} SET disconnected_at = time::now();")) + .await?; + Ok(()) + } } pub enum AppDaemonMsg { @@ -634,7 +647,8 @@ impl From<&old_brain::BrainData> for Vec { avail_ports: old_node.avail_no_of_port, max_ports_per_app: old_node.max_ports_per_app, price: old_node.price, - offline_minutes: old_node.offline_minutes, + disconnected_at: Datetime::default(), + connected_at: Datetime::default(), }); } nodes diff --git a/src/grpc/app.rs b/src/grpc/app.rs index bbd22ac..5bce79d 100644 --- a/src/grpc/app.rs +++ b/src/grpc/app.rs @@ -57,7 +57,8 @@ impl BrainAppDaemon for AppDaemonServer { avail_storage_gbs: 0, avail_ports: 0, max_ports_per_app: 0, - offline_minutes: 0, + disconnected_at: surrealdb::sql::Datetime::default(), + connected_at: surrealdb::sql::Datetime::default(), }; app_node.register(&self.db).await?; @@ -89,6 +90,7 @@ impl BrainAppDaemon for AppDaemonServer { )?; info!("App Daemon {} connected to receive brain messages", pubkey); + let _ = db::AppNode::set_online(&self.db, &pubkey).await; let (tx, rx) = mpsc::channel(6); { @@ -162,7 +164,8 @@ impl BrainAppDaemon for AppDaemonServer { }, Err(e) => { - log::warn!("App Daemon Disconnected: {e:?}") + log::warn!("App Daemon Disconnected: {e:?}"); + let _ = db::AppNode::set_offline(&self.db, &pubkey).await; } } } diff --git a/surql/tables.sql b/surql/tables.sql index 3a4f288..d3e0f71 100644 --- a/surql/tables.sql +++ b/surql/tables.sql @@ -94,7 +94,8 @@ DEFINE FIELD avail_storage_gbs ON TABLE app_node TYPE int; DEFINE FIELD avail_ports ON TABLE app_node TYPE int; DEFINE FIELD max_ports_per_app ON TABLE app_node TYPE int; DEFINE FIELD price ON TABLE app_node TYPE int; -DEFINE FIELD offline_minutes ON TABLE app_node TYPE int; +DEFINE FIELD connected_at ON TABLE app_node TYPE datetime; +DEFINE FIELD disconnected_at ON TABLE app_node TYPE datetime; DEFINE TABLE new_app_req Type RELATION FROM account to app_node SCHEMAFULL; DEFINE FIELD app_name ON TABLE new_app_req TYPE string; diff --git a/surql/timer.sql b/surql/timer.sql index 309b3d0..fda6835 100644 --- a/surql/timer.sql +++ b/surql/timer.sql @@ -30,4 +30,32 @@ FOR $contract IN (select * from active_vm fetch out) { }; }; --- TODO: implement for active_app \ No newline at end of file +FOR $app_contract IN (select * from active_app fetch out) { + LET $operator = (select * from $app_contract.out.operator)[0]; + LET $node_is_online = $app_contract.out.collected_at > $app_contract.out.disconnected_at; + LET $price_per_minute = fn::app_price_per_minute($app_contract.id); + LET $amount_due = (time::now() - $app_contract.collected_at).mins() * $price_per_minute; + LET $amount_paid = IF $amount_due > $app_contract.locked_nano { + $app_contract.locked_nano + } ELSE { + $amount_due + }; + LET $escrow_multiplier = IF $operator.escrow < 5_000_000_000_000 { 1 } ELSE { 5 }; + IF $node_is_online { + UPDATE $operator.id SET balance += $amount_paid * $escrow_multiplier; + UPDTE $app_contract.id SET + locked_nano -= $amount_paid, + collected_at = time::now(); + } ELSE { + LET $compensation = IF $amount_due > $operator.escrow { + $operator.escrow + } ELSE { + $amount_due + }; + UPDATE $operator.id SET escrow -= $compensation; + UPDATE $app_contract.in SET balance += $compensation; + }; + IF $amount_paid >= $app_contract.locked_nano { + fn::delete_app($app_contract.id); + } +} \ No newline at end of file diff --git a/tests/grpc_app_cli_test.rs b/tests/grpc_app_cli_test.rs index ef2acd7..5410447 100644 --- a/tests/grpc_app_cli_test.rs +++ b/tests/grpc_app_cli_test.rs @@ -93,6 +93,7 @@ async fn test_app_creation() { db.select::>((ACTIVE_APP, new_app_resp.uuid)).await.unwrap(); assert!(active_app.is_some()); + tokio::time::sleep(std::time::Duration::from_secs(1)).await; let acc_db: db::Account = db.select((ACCOUNT, key.pubkey.clone())).await.unwrap().unwrap(); assert_eq!(acc_db.balance, airdrop_amount * TOKEN_DECIMAL - (locking_nano + 100)); assert_eq!(acc_db.tmp_locked, 0); From 41b6b9b9d039494c89ecc9a40957b07f2e500a0b Mon Sep 17 00:00:00 2001 From: Noor Date: Fri, 20 Jun 2025 16:20:12 +0530 Subject: [PATCH 2/2] Minor fix on app node fields Remove offline_minutes from AppNodeWithReports and update query condition for active app node filter --- src/db/app.rs | 3 ++- surql/timer.sql | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/db/app.rs b/src/db/app.rs index 178a196..23544cc 100644 --- a/src/db/app.rs +++ b/src/db/app.rs @@ -231,7 +231,6 @@ pub struct AppNodeWithReports { pub avail_ports: u32, pub max_ports_per_app: u32, pub price: u64, - pub offline_minutes: u64, pub reports: Vec, } @@ -269,6 +268,8 @@ impl AppNodeWithReports { filter_query += &format!("&& ip = '{}' ", filters.ip); } + filter_query += " && connected_at > disconnected_at "; + if limit_one { filter_query += "limit 1"; } diff --git a/surql/timer.sql b/surql/timer.sql index fda6835..e8402b4 100644 --- a/surql/timer.sql +++ b/surql/timer.sql @@ -32,7 +32,7 @@ FOR $contract IN (select * from active_vm fetch out) { FOR $app_contract IN (select * from active_app fetch out) { LET $operator = (select * from $app_contract.out.operator)[0]; - LET $node_is_online = $app_contract.out.collected_at > $app_contract.out.disconnected_at; + LET $node_is_online = $app_contract.out.connected_at > $app_contract.out.disconnected_at; LET $price_per_minute = fn::app_price_per_minute($app_contract.id); LET $amount_due = (time::now() - $app_contract.collected_at).mins() * $price_per_minute; LET $amount_paid = IF $amount_due > $app_contract.locked_nano { @@ -43,7 +43,7 @@ FOR $app_contract IN (select * from active_app fetch out) { LET $escrow_multiplier = IF $operator.escrow < 5_000_000_000_000 { 1 } ELSE { 5 }; IF $node_is_online { UPDATE $operator.id SET balance += $amount_paid * $escrow_multiplier; - UPDTE $app_contract.id SET + UPDATE $app_contract.id SET locked_nano -= $amount_paid, collected_at = time::now(); } ELSE {