From 0c0bf0b6fa1741ea866aadce9f003a46cf8439dd Mon Sep 17 00:00:00 2001 From: Noor Date: Fri, 23 May 2025 18:52:56 +0530 Subject: [PATCH 1/6] Ban user by node operator bindings for is_banned_by_node query tests for ban user mock data for test ban user test key from base58 privatekey --- src/constants.rs | 1 + src/db/general.rs | 62 +++- src/db/mod.rs | 2 + src/grpc/general.rs | 9 +- surql/tables.sql | 4 +- tests/common/prepare_test_env.rs | 9 +- tests/common/test_utils.rs | 11 + tests/grpc_general_test.rs | 68 ++++- tests/mock_data.yaml | 475 +++++++++++++++++++++++++++++++ 9 files changed, 617 insertions(+), 24 deletions(-) create mode 100644 tests/mock_data.yaml diff --git a/src/constants.rs b/src/constants.rs index 33cc61f..0679043 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -24,6 +24,7 @@ pub const OLD_BRAIN_DATA_PATH: &str = "./saved_data.yaml"; pub const ACCOUNT: &str = "account"; pub const KICK: &str = "kick"; +pub const BAN: &str = "ban"; pub const VM_NODE: &str = "vm_node"; pub const ACTIVE_VM: &str = "active_vm"; diff --git a/src/db/general.rs b/src/db/general.rs index 7597e7a..952cfab 100644 --- a/src/db/general.rs +++ b/src/db/general.rs @@ -1,5 +1,5 @@ use super::Error; -use crate::constants::{ACCOUNT, KICK, MIN_ESCROW, TOKEN_DECIMAL}; +use crate::constants::{ACCOUNT, BAN, KICK, MIN_ESCROW, TOKEN_DECIMAL, VM_NODE}; use crate::db::prelude::*; use crate::old_brain; use serde::{Deserialize, Serialize}; @@ -86,20 +86,24 @@ impl Account { let mut query_response = db .query(format!( "(select operator->ban[0] as ban - from vm_node:{node} - where operator->ban->account contains account:{user} + from $vm_node_input + where operator->ban->account contains $user_account_input ).ban;" )) + .bind(("vm_node_input", RecordId::from((VM_NODE, node)))) + .bind(("user_account_input", RecordId::from((ACCOUNT, user)))) .query(format!( "(select operator->ban[0] as ban - from app_node:{node} - where operator->ban->account contains account:{user} + from $app_node_input + where operator->ban->account contains $user_account_input ).ban;" )) + .bind(("app_node_input", RecordId::from((APP_NODE, node)))) + .bind(("user_account_input", RecordId::from((ACCOUNT, user)))) .await?; - let vm_node_ban: Option = query_response.take(0)?; - let app_node_ban: Option = query_response.take(1)?; + let vm_node_ban: Option = query_response.take(0).unwrap(); + let app_node_ban: Option = query_response.take(1).unwrap(); Ok(vm_node_ban.is_some() || app_node_ban.is_some()) } @@ -128,12 +132,48 @@ impl From<&old_brain::BrainData> for Vec { #[derive(Debug, Serialize, Deserialize)] pub struct Ban { - id: RecordId, #[serde(rename = "in")] - from_account: RecordId, + pub from_account: RecordId, #[serde(rename = "out")] - to_account: RecordId, - created_at: Datetime, + pub to_account: RecordId, + pub created_at: Datetime, +} + +impl Ban { + pub async fn get_record_by_wallets( + db: &Surreal, + op_wallet: &str, + user_wallet: &str, + ) -> Result, Error> { + let query = + format!("SELECT * FROM {BAN} WHERE in = $operator_input AND out = $user_input;"); + let mut response = db + .query(query) + .bind(("operator_input", RecordId::from((ACCOUNT, op_wallet)))) + .bind(("user_input", RecordId::from((ACCOUNT, user_wallet)))) + .await?; + let ban_record: Option = response.take(0)?; + Ok(ban_record) + } + + pub async fn ban_a_user( + db: &Surreal, + op_wallet: &str, + user_wallet: &str, + ) -> Result<(), Error> { + if Self::get_record_by_wallets(db, op_wallet, user_wallet).await?.is_some() { + return Err(Error::AlreadyBanned(op_wallet.to_string())); + } + let _: Vec = db + .insert(BAN) + .relation(Self { + from_account: RecordId::from((ACCOUNT, op_wallet)), + to_account: RecordId::from((ACCOUNT, user_wallet)), + created_at: Default::default(), + }) + .await?; + Ok(()) + } } #[derive(Debug, Serialize, Deserialize)] diff --git a/src/db/mod.rs b/src/db/mod.rs index cd06be1..0f66625 100644 --- a/src/db/mod.rs +++ b/src/db/mod.rs @@ -45,6 +45,8 @@ pub enum Error { FailedToDeleteContract(String), #[error("Failed to kick contract {0}")] FailedKickContract(String), + #[error("Already banned {0}")] + AlreadyBanned(String), } pub mod prelude { diff --git a/src/grpc/general.rs b/src/grpc/general.rs index cf3bf2b..fa97751 100644 --- a/src/grpc/general.rs +++ b/src/grpc/general.rs @@ -152,11 +152,10 @@ impl BrainGeneralCli for GeneralCliServer { } } - async fn ban_user(&self, _req: Request) -> Result, Status> { - todo!(); - // let req = check_sig_from_req(req)?; - // self.data.ban_user(&req.operator_wallet, &req.user_wallet); - // Ok(Response::new(Empty {})) + async fn ban_user(&self, req: Request) -> Result, Status> { + let req = check_sig_from_req(req)?; + db::Ban::ban_a_user(&self.db, &req.operator_wallet, &req.user_wallet).await?; + Ok(Response::new(Empty {})) } // admin commands diff --git a/surql/tables.sql b/surql/tables.sql index 0071da4..4ab0a99 100644 --- a/surql/tables.sql +++ b/surql/tables.sql @@ -129,14 +129,14 @@ DEFINE FIELD vcpus ON TABLE deleted_app TYPE int; DEFINE FIELD memory_mb ON TABLE deleted_app TYPE int; DEFINE FIELD disk_size_gb ON TABLE deleted_app TYPE int; DEFINE FIELD created_at ON TABLE deleted_app TYPE datetime; -DEFINE FIELD deleted_at ON TABLE deleted_app TYPE datetime DEFAULT time::now();; +DEFINE FIELD deleted_at ON TABLE deleted_app TYPE datetime DEFAULT time::now(); DEFINE FIELD price_per_unit ON TABLE deleted_app TYPE int; DEFINE FIELD mr_enclave ON TABLE deleted_app TYPE string; DEFINE FIELD package_url ON TABLE deleted_app TYPE string; DEFINE FIELD hratls_pubkey ON TABLE deleted_app TYPE string; DEFINE TABLE ban TYPE RELATION FROM account TO account; -DEFINE FIELD created_at ON TABLE ban TYPE datetime; +DEFINE FIELD created_at ON TABLE ban TYPE datetime DEFAULT time::now();; DEFINE TABLE kick TYPE RELATION FROM account TO account; DEFINE FIELD created_at ON TABLE kick TYPE datetime; diff --git a/tests/common/prepare_test_env.rs b/tests/common/prepare_test_env.rs index 96a8587..42e6e0c 100644 --- a/tests/common/prepare_test_env.rs +++ b/tests/common/prepare_test_env.rs @@ -1,4 +1,4 @@ -use anyhow::{anyhow, Result}; +use anyhow::Result; use detee_shared::general_proto::brain_general_cli_server::BrainGeneralCliServer; use detee_shared::vm_proto::brain_vm_cli_server::BrainVmCliServer; use detee_shared::vm_proto::brain_vm_daemon_server::BrainVmDaemonServer; @@ -31,14 +31,15 @@ pub async fn prepare_test_db() -> Result> { let db = surreal_brain::db::db_connection(&db_url, &db_user, &db_pass, db_ns, db_name).await?; DB_STATE .get_or_init(|| async { - let old_brain_data = surreal_brain::old_brain::BrainData::load_from_disk() - .map_err(|e| anyhow!(e.to_string()))?; + let raw_mock_data = std::fs::read_to_string("tests/mock_data.yaml")?; + let mock_data: surreal_brain::old_brain::BrainData = + serde_yaml::from_str(&raw_mock_data)?; db.query(format!("REMOVE DATABASE {db_name}")).await?; for schema in DB_SCHEMA_FILES.map(std::fs::read_to_string) { db.query(schema?).await?; } - surreal_brain::db::migration0(&db, &old_brain_data).await?; + surreal_brain::db::migration0(&db, &mock_data).await?; Ok::<(), anyhow::Error>(()) }) .await; diff --git a/tests/common/test_utils.rs b/tests/common/test_utils.rs index 5ae3fae..a99efb0 100644 --- a/tests/common/test_utils.rs +++ b/tests/common/test_utils.rs @@ -32,6 +32,17 @@ impl Key { Key { sg_key: sk, pubkey } } + pub fn from(private_key: &str) -> Self { + let signing_key: SigningKey = bs58::decode(private_key) + .into_vec() + .expect("Failed to decode private key") + .as_slice() + .try_into() + .expect("Failed to convert to SigningKey"); + let pubkey = bs58::encode(signing_key.verifying_key().to_bytes()).into_string(); + Key { sg_key: signing_key, pubkey } + } + pub fn sign_request(&self, req: T) -> Result> { let pubkey = self.pubkey.clone(); let timestamp = chrono::Utc::now().to_rfc3339(); diff --git a/tests/grpc_general_test.rs b/tests/grpc_general_test.rs index e57c35b..b0a1092 100644 --- a/tests/grpc_general_test.rs +++ b/tests/grpc_general_test.rs @@ -6,10 +6,11 @@ use common::vm_cli_utils::{airdrop, create_new_vm, report_node}; use common::vm_daemon_utils::{mock_vm_daemon, register_vm_node}; use detee_shared::common_proto::{Empty, Pubkey}; use detee_shared::general_proto::brain_general_cli_client::BrainGeneralCliClient; -use detee_shared::general_proto::AirdropReq; +use detee_shared::general_proto::{AirdropReq, BanUserReq}; use detee_shared::vm_proto::brain_vm_daemon_client::BrainVmDaemonClient; use futures::StreamExt; -use surreal_brain::constants::{TOKEN_DECIMAL, VM_NODE}; +use surreal_brain::constants::{BAN, TOKEN_DECIMAL, VM_NODE}; +use surreal_brain::db::prelude as db; use surreal_brain::db::vm::VmNodeWithReports; mod common; @@ -221,11 +222,13 @@ async fn test_kick_contract() { // 6. refund amount calculation // 7. refund of multiple contract kick in a day for same user + /* env_logger::builder() .filter_level(log::LevelFilter::Info) .filter_module("tungstenite", log::LevelFilter::Debug) .filter_module("tokio_tungstenite", log::LevelFilter::Debug) .init(); + */ let db_conn = prepare_test_db().await.unwrap(); let contract_uuid = "e3d01f252b2a410b80e312f44e474334"; @@ -244,3 +247,64 @@ async fn test_kick_contract() { } } } + +#[tokio::test] +async fn test_ban_user() { + let db_conn = prepare_test_db().await.unwrap(); + + let brain_channel = run_service_for_stream().await.unwrap(); + let mut cli_client = BrainGeneralCliClient::new(brain_channel.clone()); + let op_key = Key::from("6f3WcQuv8sPrxAsSh24TdXkauwVd8vFUyCCfbNqxszvR"); // "4qFJJJdRrSB9hCn8rrvYTXHLJg371ab36PJmZ4uxHjGQ" + let user_key = Key::new(); + let node_pubkey = "7fujZQeTme52RdXTLmQST5jBgAbvzic5iERtH5EWoYjk"; + + /* + base58 = "6f3WcQuv8sPrxAsSh24TdXkauwVd8vFUyCCfbNqxszvR" + pubkey = "4qFJJJdRrSB9hCn8rrvYTXHLJg371ab36PJmZ4uxHjGQ" + base58 = "7fujZQeTme52RdXTLmQST5jBgAbvzic5iERtH5EWoYjk" + pubkey = "G2PetsFM8peG77rf9UTfVD7HEHNPmdjV3cebBpKuR3QT" + */ + + let user_wallet = user_key.pubkey.clone(); + let operator_wallet = op_key.pubkey.clone(); + + let _ = cli_client + .ban_user( + op_key + .sign_request(BanUserReq { + operator_wallet: operator_wallet.clone(), + user_wallet: user_wallet.clone(), + }) + .unwrap(), + ) + .await + .unwrap(); + + let ban_reconrd: Option = db_conn + .query(format!("SELECT * FROM {BAN} WHERE in = account:{operator_wallet} AND out = account:{user_wallet};")) + .await.unwrap().take(0).unwrap(); + + assert!(ban_reconrd.is_some()); + + let already_banned = cli_client + .ban_user( + op_key + .sign_request(BanUserReq { + operator_wallet: operator_wallet.clone(), + user_wallet: user_wallet.clone(), + }) + .unwrap(), + ) + .await + .err() + .unwrap(); + + assert!(already_banned.message().contains("Already banned")); + + airdrop(&brain_channel, &user_wallet, 10).await.unwrap(); + + let operator_banned_you = + create_new_vm(&db_conn, &user_key, node_pubkey, &brain_channel).await.err().unwrap(); + + assert!(operator_banned_you.to_string().contains("This operator banned you")); +} diff --git a/tests/mock_data.yaml b/tests/mock_data.yaml new file mode 100644 index 0000000..f651f11 --- /dev/null +++ b/tests/mock_data.yaml @@ -0,0 +1,475 @@ +accounts: + DXXkYSnhP3ijsHYxkedcuMomEyc122WaAbkDX7SaGuUS: + balance: 20293420000 + tmp_locked: 0 + kicked_for: [] + last_kick: 1970-01-01T00:00:00Z + banned_by: [] + FHuecMbeC1PfjkW2JKyoicJAuiU7khgQT16QUB3Q1XdL: + balance: 25949200000 + tmp_locked: 0 + kicked_for: [] + last_kick: 1970-01-01T00:00:00Z + banned_by: [] + Cnkvn3WuHYfTzh1YK1TAv2VD25sNvstJNnQtxjcdQSL7: + balance: 4794480000 + tmp_locked: 0 + kicked_for: [] + last_kick: 1970-01-01T00:00:00Z + banned_by: [] + BFopWmwcZAMF1h2PFECZNdEucdZfnZZ32p6R9ZaBiVsS: + balance: 4672207240000 + tmp_locked: 0 + kicked_for: [] + last_kick: 1970-01-01T00:00:00Z + banned_by: [] + E3bgXsWvgichXeC6AqULJCZDp7FbEdTxBD67UaYVWf9y: + balance: 21121600000 + tmp_locked: 0 + kicked_for: [] + last_kick: 1970-01-01T00:00:00Z + banned_by: [] + HQyGWpiteHbxjszngZvmiX7ZFZAmF6nFjEraBa1M6bbM: + balance: 979410300000 + tmp_locked: 0 + kicked_for: [] + last_kick: 1970-01-01T00:00:00Z + banned_by: [] + H21Shi4iE7vgfjWEQNvzmpmBMJSaiZ17PYUcdNoAoKNc: + balance: 976000000 + tmp_locked: 0 + kicked_for: [] + last_kick: 1970-01-01T00:00:00Z + banned_by: [] + 45Pyv9hRfub43NyRrYv95MhZs1Wrm8sj3RhBvA3F1Bvr: + balance: 1670441080000 + tmp_locked: 0 + kicked_for: [] + last_kick: 1970-01-01T00:00:00Z + banned_by: [] + 49JBVzmgsQbUURHzAWax2gxo6jmukqbEQzP97YeeNQyu: + balance: 1076960680000 + tmp_locked: 0 + kicked_for: [] + last_kick: 1970-01-01T00:00:00Z + banned_by: [] + 7V3rEuh6j8VuwMVB5PyGqWKLmjJ4fYSv6WtrTL51NZTB: + balance: 3271040000 + tmp_locked: 0 + kicked_for: [] + last_kick: 1970-01-01T00:00:00Z + banned_by: [] + x52w7jARC5erhWWK65VZmjdGXzBK6ZDgfv1A283d8XK: + balance: 554454460000 + tmp_locked: 547200000 + kicked_for: [] + last_kick: 1970-01-01T00:00:00Z + banned_by: [] + 3BNggj8ZTsoSjfAGdPfmcU2Gobm2qcTEBg9iHXEUPe1t: + balance: 9978460000 + tmp_locked: 0 + kicked_for: [] + last_kick: 1970-01-01T00:00:00Z + banned_by: [] + B981xPHmHthfKr15J9uJ64qd9zt2KsdiEuDRR7UUCGWi: + balance: 99980200000 + tmp_locked: 0 + kicked_for: [] + last_kick: 1970-01-01T00:00:00Z + banned_by: [] + CLYyE6id5876DW69LHDynuH6TjJPvWRBTQC5XDZ6jfT1: + balance: 25000000000 + tmp_locked: 0 + kicked_for: [] + last_kick: 1970-01-01T00:00:00Z + banned_by: [] + db5ZB6uDbF1mUUgeggBZ9XKbi3mUfX6WHkBpbwUHJpB: + balance: 25000000000 + tmp_locked: 0 + kicked_for: [] + last_kick: 1970-01-01T00:00:00Z + banned_by: [] + fY3NNjvFTeR1FBh5nXV3ujX7zZqrm3eBUWGEiG75TK1: + balance: 1000000000 + tmp_locked: 0 + kicked_for: [] + last_kick: 1970-01-01T00:00:00Z + banned_by: [] + FBMWVqME3t1i4R6zWyDQGUuiTeruZ1TxLhTmhaEcFypZ: + balance: 181560160000 + tmp_locked: 0 + kicked_for: [] + last_kick: 1970-01-01T00:00:00Z + banned_by: [] + DwfL5iFu32xh2YMCUxg63oEAThLRqehDAumiP9q6zuuX: + balance: 74660380000 + tmp_locked: 0 + kicked_for: [] + last_kick: 1970-01-01T00:00:00Z + banned_by: [] + + 4qFJJJdRrSB9hCn8rrvYTXHLJg371ab36PJmZ4uxHjGQ: + balance: 25949200000 + tmp_locked: 0 + kicked_for: [] + last_kick: 1970-01-01T00:00:00Z + banned_by: [] + +operators: + BFopWmwcZAMF1h2PFECZNdEucdZfnZZ32p6R9ZaBiVsS: + escrow: 5096692000000 + email: first_on_detee@proton.me + banned_users: [] + vm_nodes: + - HiyMp21zaBVbRCjDsD5hEjQnHeHv4e1gpUR6pVfHTKqv + - 3zRxiGRnf46vd3zAEmpaYBJocTV9oJB6yXf5GZFR1Sq4 + - Du3UfPSUUZmA5thQmc9Vrxdy7UimpygcpDsQNnwRQPtu + - 4QbUXDM915RUFnHm3NiysLXFLk1WRGZvABwLNzx4tTEW + - DgkbsrwttkZXvzxY5kDwQQoDd79GLmZ5tc7fYJUFkQQb + app_nodes: [] + x52w7jARC5erhWWK65VZmjdGXzBK6ZDgfv1A283d8XK: + escrow: 5499700480000 + email: gheo@detee.ltd + banned_users: [] + vm_nodes: + - 2Uf5pxhxKTUm6gRMnpbJHYDuyA6BWUfFsdmPyWfbMV1f + - 7Xw3RxbP5pvfjZ8U6yA3HHVSS9YXjKH5Vkas3JRbQYd9 + app_nodes: [] + 7V3rEuh6j8VuwMVB5PyGqWKLmjJ4fYSv6WtrTL51NZTB: + escrow: 888888888899999 + email: "" + banned_users: [] + vm_nodes: [] + app_nodes: + - BiqoPUEoAxYxMRXUmyofoS9H1TBQgQqvLJ6MbWh88AQg + + 4qFJJJdRrSB9hCn8rrvYTXHLJg371ab36PJmZ4uxHjGQ: + escrow: 5499700480000 + email: "test_mock@operator" + banned_users: [] + vm_nodes: + - 7fujZQeTme52RdXTLmQST5jBgAbvzic5iERtH5EWoYjk + app_nodes: [] + +vm_nodes: + - public_key: 7Xw3RxbP5pvfjZ8U6yA3HHVSS9YXjKH5Vkas3JRbQYd9 + operator_wallet: x52w7jARC5erhWWK65VZmjdGXzBK6ZDgfv1A283d8XK + country: GB + region: England + city: London + ip: 173.234.17.2 + avail_mem_mb: 26000 + avail_vcpus: 28 + avail_storage_gbs: 680 + avail_ipv4: 2 + avail_ipv6: 65516 + avail_ports: 19999 + max_ports_per_vm: 5 + price: 20000 + reports: {} + offline_minutes: 0 + - public_key: Du3UfPSUUZmA5thQmc9Vrxdy7UimpygcpDsQNnwRQPtu + operator_wallet: BFopWmwcZAMF1h2PFECZNdEucdZfnZZ32p6R9ZaBiVsS + country: FR + region: Île-de-France + city: Paris + ip: 156.146.63.215 + avail_mem_mb: 123000 + avail_vcpus: 46 + avail_storage_gbs: 440 + avail_ipv4: 2 + avail_ipv6: 0 + avail_ports: 20000 + max_ports_per_vm: 5 + price: 20000 + reports: {} + offline_minutes: 0 + - public_key: 2Uf5pxhxKTUm6gRMnpbJHYDuyA6BWUfFsdmPyWfbMV1f + operator_wallet: x52w7jARC5erhWWK65VZmjdGXzBK6ZDgfv1A283d8XK + country: CA + region: Quebec + city: Montréal + ip: 184.107.169.199 + avail_mem_mb: 30000 + avail_vcpus: 31 + avail_storage_gbs: 700 + avail_ipv4: 0 + avail_ipv6: 0 + avail_ports: 20000 + max_ports_per_vm: 5 + price: 18000 + reports: {} + offline_minutes: 0 + - public_key: DgkbsrwttkZXvzxY5kDwQQoDd79GLmZ5tc7fYJUFkQQb + operator_wallet: BFopWmwcZAMF1h2PFECZNdEucdZfnZZ32p6R9ZaBiVsS + country: CA + region: British Columbia + city: Vancouver + ip: 149.22.95.1 + avail_mem_mb: 109000 + avail_vcpus: 45 + avail_storage_gbs: 400 + avail_ipv4: 25 + avail_ipv6: 0 + avail_ports: 20000 + max_ports_per_vm: 5 + price: 20000 + reports: {} + offline_minutes: 0 + - public_key: 3zRxiGRnf46vd3zAEmpaYBJocTV9oJB6yXf5GZFR1Sq4 + operator_wallet: BFopWmwcZAMF1h2PFECZNdEucdZfnZZ32p6R9ZaBiVsS + country: US + region: California + city: San Jose + ip: 149.36.48.99 + avail_mem_mb: 120000 + avail_vcpus: 41 + avail_storage_gbs: 390 + avail_ipv4: 23 + avail_ipv6: 0 + avail_ports: 19999 + max_ports_per_vm: 5 + price: 20000 + reports: {} + offline_minutes: 0 + - public_key: HiyMp21zaBVbRCjDsD5hEjQnHeHv4e1gpUR6pVfHTKqv + operator_wallet: BFopWmwcZAMF1h2PFECZNdEucdZfnZZ32p6R9ZaBiVsS + country: CA + region: British Columbia + city: Vancouver + ip: 149.22.95.28 + avail_mem_mb: 125000 + avail_vcpus: 46 + avail_storage_gbs: 400 + avail_ipv4: 26 + avail_ipv6: 0 + avail_ports: 20000 + max_ports_per_vm: 5 + price: 20000 + reports: {} + offline_minutes: 0 + + - public_key: 7fujZQeTme52RdXTLmQST5jBgAbvzic5iERtH5EWoYjk + operator_wallet: 4qFJJJdRrSB9hCn8rrvYTXHLJg371ab36PJmZ4uxHjGQ + country: GB + region: England + city: London + ip: 193.234.17.2 + avail_mem_mb: 28000 + avail_vcpus: 24 + avail_storage_gbs: 1680 + avail_ipv4: 1 + avail_ipv6: 0 + avail_ports: 19999 + max_ports_per_vm: 10 + price: 24000 + reports: {} + offline_minutes: 0 + +vm_contracts: + - uuid: 958165e3-dea8-407d-8c42-dd17002ef79c + hostname: detee-landing-fr + admin_pubkey: FHuecMbeC1PfjkW2JKyoicJAuiU7khgQT16QUB3Q1XdL + node_pubkey: Du3UfPSUUZmA5thQmc9Vrxdy7UimpygcpDsQNnwRQPtu + exposed_ports: [] + public_ipv4: 156.146.63.216 + public_ipv6: "" + disk_size_gb: 10 + vcpus: 2 + memory_mb: 3000 + kernel_sha: 3ec4fc5aa5729f515967ec71be4a851622785c0080f7191b1b07717149840151 + dtrfs_sha: 3f6b3e5740f249eedfb2f7248c521a551be8b2676f7fcb040f3f3bc840a5004b + created_at: 2025-02-28T23:19:41.769423466Z + updated_at: 2025-04-12T12:11:58.516768949Z + price_per_unit: 20000 + locked_nano: 14875500000 + collected_at: 2025-04-20T00:34:15.461165181Z + - uuid: e807a2fd-cf90-4a14-bc3a-89ce6dc59033 + hostname: detee-landing-gb + admin_pubkey: FHuecMbeC1PfjkW2JKyoicJAuiU7khgQT16QUB3Q1XdL + node_pubkey: 7Xw3RxbP5pvfjZ8U6yA3HHVSS9YXjKH5Vkas3JRbQYd9 + exposed_ports: [] + public_ipv4: 173.234.136.154 + public_ipv6: "" + disk_size_gb: 10 + vcpus: 2 + memory_mb: 3000 + kernel_sha: 3ec4fc5aa5729f515967ec71be4a851622785c0080f7191b1b07717149840151 + dtrfs_sha: 3f6b3e5740f249eedfb2f7248c521a551be8b2676f7fcb040f3f3bc840a5004b + created_at: 2025-03-06T19:51:39.595163157Z + updated_at: 2025-03-06T19:51:39.595163842Z + price_per_unit: 20000 + locked_nano: 14875500000 + collected_at: 2025-04-20T00:34:15.461181545Z + - uuid: 23094406-2307-4332-a642-acee718d0186 + hostname: heroic-door + admin_pubkey: DwfL5iFu32xh2YMCUxg63oEAThLRqehDAumiP9q6zuuX + node_pubkey: 7Xw3RxbP5pvfjZ8U6yA3HHVSS9YXjKH5Vkas3JRbQYd9 + exposed_ports: + - 38288 + public_ipv4: "" + public_ipv6: "" + disk_size_gb: 10 + vcpus: 1 + memory_mb: 1000 + kernel_sha: 14e225e4aaf84cc2e0b5f64206121186ddebc4b378b886da3b2f7515dfd41692 + dtrfs_sha: 03ce24dbbe917fdd4f6347e61036805ddbdded5044c272bab188ef9333093bee + created_at: 2025-03-12T16:28:24.749161605Z + updated_at: 2025-03-12T16:28:24.749162477Z + price_per_unit: 20000 + locked_nano: 14134140000 + collected_at: 2025-04-20T00:34:15.461191231Z + - uuid: 1f49a71c-f68c-4c64-a82e-f50e0ba0b574 + hostname: astromech-wrench + admin_pubkey: FHuecMbeC1PfjkW2JKyoicJAuiU7khgQT16QUB3Q1XdL + node_pubkey: DgkbsrwttkZXvzxY5kDwQQoDd79GLmZ5tc7fYJUFkQQb + exposed_ports: [] + public_ipv4: 149.22.95.2 + public_ipv6: "" + disk_size_gb: 10 + vcpus: 2 + memory_mb: 3000 + kernel_sha: 3a68709138bed09c16671949cf1f03acee95a08381ba84fc70fb586001fa6767 + dtrfs_sha: 0bb93443f65c9f4379ed469f94794f5c1bf14d8905b0b2c56a125df4a9ebe83e + created_at: 2025-03-20T14:40:25.557753393Z + updated_at: 2025-03-20T14:40:25.557754242Z + price_per_unit: 20000 + locked_nano: 11865620000 + collected_at: 2025-04-20T00:34:15.461201690Z + - uuid: 16577f1c-9867-4a17-80a8-6cf0490f1270 + hostname: sofenty + admin_pubkey: FHuecMbeC1PfjkW2JKyoicJAuiU7khgQT16QUB3Q1XdL + node_pubkey: Du3UfPSUUZmA5thQmc9Vrxdy7UimpygcpDsQNnwRQPtu + exposed_ports: [] + public_ipv4: 156.146.63.217 + public_ipv6: "" + disk_size_gb: 10 + vcpus: 2 + memory_mb: 3000 + kernel_sha: e49c8587287b21df7600c04326fd7393524453918c14d67f73757dc769a13542 + dtrfs_sha: b5f408d00e2b93dc594fed3a7f2466a9878802ff1c7ae502247471cd06728a45 + created_at: 2025-04-07T22:57:57.646151746Z + updated_at: 2025-04-07T22:57:57.646152630Z + price_per_unit: 20000 + locked_nano: 11867500000 + collected_at: 2025-04-20T00:34:15.461211040Z + - uuid: 4b6e25ca-87ac-478b-8f16-aa8f5c44c704 + hostname: cloaked-mailbox + admin_pubkey: DwfL5iFu32xh2YMCUxg63oEAThLRqehDAumiP9q6zuuX + node_pubkey: DgkbsrwttkZXvzxY5kDwQQoDd79GLmZ5tc7fYJUFkQQb + exposed_ports: [] + public_ipv4: 149.22.95.2 + public_ipv6: "" + disk_size_gb: 30 + vcpus: 1 + memory_mb: 1000 + kernel_sha: e49c8587287b21df7600c04326fd7393524453918c14d67f73757dc769a13542 + dtrfs_sha: b5f408d00e2b93dc594fed3a7f2466a9878802ff1c7ae502247471cd06728a45 + created_at: 2025-04-12T13:44:56.957037550Z + updated_at: 2025-04-12T13:44:56.957038546Z + price_per_unit: 20000 + locked_nano: 11177760000 + collected_at: 2025-04-20T00:34:15.461219779Z + - uuid: eb1a13ed-d782-4b71-8860-73540129cb7d + hostname: twenty + admin_pubkey: FHuecMbeC1PfjkW2JKyoicJAuiU7khgQT16QUB3Q1XdL + node_pubkey: 3zRxiGRnf46vd3zAEmpaYBJocTV9oJB6yXf5GZFR1Sq4 + exposed_ports: [] + public_ipv4: 149.36.48.100 + public_ipv6: "" + disk_size_gb: 10 + vcpus: 4 + memory_mb: 4000 + kernel_sha: e49c8587287b21df7600c04326fd7393524453918c14d67f73757dc769a13542 + dtrfs_sha: b5f408d00e2b93dc594fed3a7f2466a9878802ff1c7ae502247471cd06728a45 + created_at: 2025-04-15T00:46:35.622165457Z + updated_at: 2025-04-15T00:46:35.622166372Z + price_per_unit: 20000 + locked_nano: 15570720000 + collected_at: 2025-04-20T00:34:15.461230948Z + - uuid: 1bf36309-3774-4825-b023-b2a0ef0405ed + hostname: shadowy-hobo + admin_pubkey: x52w7jARC5erhWWK65VZmjdGXzBK6ZDgfv1A283d8XK + node_pubkey: 3zRxiGRnf46vd3zAEmpaYBJocTV9oJB6yXf5GZFR1Sq4 + exposed_ports: + - 46393 + public_ipv4: "" + public_ipv6: "" + disk_size_gb: 10 + vcpus: 1 + memory_mb: 1000 + kernel_sha: e765e56166ef321b53399b9638584d1279821dbe3d46191c1f66bbaa075e7919 + dtrfs_sha: d207644ee60d54009b6ecdfb720e2ec251cde31774dd249fcc7435aca0377990 + created_at: 2025-04-16T20:37:57.176592933Z + updated_at: 2025-04-16T20:37:57.176594069Z + price_per_unit: 20000 + locked_nano: 12730960000 + collected_at: 2025-04-20T00:34:15.461240342Z +app_nodes: + - node_pubkey: BiqoPUEoAxYxMRXUmyofoS9H1TBQgQqvLJ6MbWh88AQg + operator_wallet: 7V3rEuh6j8VuwMVB5PyGqWKLmjJ4fYSv6WtrTL51NZTB + country: DE + region: Hesse + city: Frankfurt am Main + ip: 212.95.45.139 + avail_mem_mb: 16000 + avail_vcpus: 16 + avail_storage_mb: 200000 + avail_no_of_port: 20000 + max_ports_per_app: 9 + price: 20000 + offline_minutes: 0 +app_contracts: + - uuid: e3d01f25-2b2a-410b-80e3-12f44e474334 + package_url: https://registry.detee.ltd/sgx/packages/base_package_2025-04-17_11-01-08.tar.gz + admin_pubkey: H21Shi4iE7vgfjWEQNvzmpmBMJSaiZ17PYUcdNoAoKNc + node_pubkey: BiqoPUEoAxYxMRXUmyofoS9H1TBQgQqvLJ6MbWh88AQg + mapped_ports: + - - 27158 + - 34500 + - - 28667 + - 8080 + host_ipv4: 212.95.45.139 + disk_size_mb: 1000 + vcpus: 1 + memory_mb: 1000 + created_at: 2025-04-21T11:27:28.833236909Z + updated_at: 2025-04-21T11:27:28.833237729Z + price_per_unit: 200000 + locked_nano: 121200000 + collected_at: 2025-04-21T11:28:24.905665571Z + hratls_pubkey: 7E0F887AA6BB9104EEC1066F454D4C2D9063D676715F55F919D3FBCEDC63240B + public_package_mr_enclave: + - 52 + - 183 + - 102 + - 210 + - 251 + - 219 + - 218 + - 140 + - 168 + - 118 + - 10 + - 193 + - 98 + - 240 + - 147 + - 124 + - 240 + - 189 + - 46 + - 95 + - 138 + - 172 + - 15 + - 246 + - 227 + - 114 + - 70 + - 159 + - 232 + - 212 + - 9 + - 234 + app_name: diligent-seahorse -- 2.43.0 From ff03ec6085eba8a9d33775b6435cf08e6e7a6ed2 Mon Sep 17 00:00:00 2001 From: Noor Date: Sat, 24 May 2025 17:42:08 +0530 Subject: [PATCH 2/6] Feat slashing escrow improved logging tests for slashing and register operator --- src/db/general.rs | 36 ++++++++++++++++++ src/db/mod.rs | 2 + src/grpc/general.rs | 13 ++++--- tests/common/vm_cli_utils.rs | 11 +++++- tests/grpc_general_test.rs | 74 ++++++++++++++++++++++++++++++++++-- 5 files changed, 126 insertions(+), 10 deletions(-) diff --git a/src/db/general.rs b/src/db/general.rs index 952cfab..b6d8061 100644 --- a/src/db/general.rs +++ b/src/db/general.rs @@ -75,6 +75,38 @@ impl Account { op_account.save(db).await?; Ok(()) } + + pub async fn slash_account( + db: &Surreal, + account: &str, + slash_amount: u64, + ) -> Result<(), Error> { + let tx_query = " + BEGIN TRANSACTION; + LET $account = $account_input; + + UPDATE $account SET escrow -= $slash_amount; + IF $account.escrow < 0 {{ + THROW 'Insufficient escrow.' + }}; + + COMMIT TRANSACTION;"; + + let mut query_resp = db + .query(tx_query) + .bind(("account_input", RecordId::from((ACCOUNT, account)))) + .bind(("slash_amount", slash_amount.saturating_mul(TOKEN_DECIMAL))) + .await?; + + log::trace!("query_resp: {query_resp:?}"); + + let query_error = query_resp.take_errors(); + if !query_error.is_empty() { + log::error!("slash_account query error: {query_error:?}"); + return Err(Error::FailedToSlashOperator(account.to_string())); + } + Ok(()) + } } impl Account { @@ -105,6 +137,9 @@ impl Account { let vm_node_ban: Option = query_response.take(0).unwrap(); let app_node_ban: Option = query_response.take(1).unwrap(); + log::trace!("vm_node_ban: {vm_node_ban:?}"); + log::trace!("app_node_ban: {app_node_ban:?}"); + Ok(vm_node_ban.is_some() || app_node_ban.is_some()) } } @@ -162,6 +197,7 @@ impl Ban { user_wallet: &str, ) -> Result<(), Error> { if Self::get_record_by_wallets(db, op_wallet, user_wallet).await?.is_some() { + log::error!("User {user_wallet} is already banned by {op_wallet}"); return Err(Error::AlreadyBanned(op_wallet.to_string())); } let _: Vec = db diff --git a/src/db/mod.rs b/src/db/mod.rs index 0f66625..c84689a 100644 --- a/src/db/mod.rs +++ b/src/db/mod.rs @@ -47,6 +47,8 @@ pub enum Error { FailedKickContract(String), #[error("Already banned {0}")] AlreadyBanned(String), + #[error("Failed to slash operator {0}")] + FailedToSlashOperator(String), } pub mod prelude { diff --git a/src/grpc/general.rs b/src/grpc/general.rs index fa97751..8c048ed 100644 --- a/src/grpc/general.rs +++ b/src/grpc/general.rs @@ -154,6 +154,7 @@ impl BrainGeneralCli for GeneralCliServer { async fn ban_user(&self, req: Request) -> Result, Status> { let req = check_sig_from_req(req)?; + log::info!("Banning user: {}, by: {}", req.user_wallet, req.operator_wallet); db::Ban::ban_a_user(&self.db, &req.operator_wallet, &req.user_wallet).await?; Ok(Response::new(Empty {})) } @@ -163,16 +164,16 @@ impl BrainGeneralCli for GeneralCliServer { async fn airdrop(&self, req: Request) -> Result, Status> { check_admin_key(&req)?; let req = check_sig_from_req(req)?; + log::info!("Airdropping {} tokens to {}", req.tokens, req.pubkey); db::Account::airdrop(&self.db, &req.pubkey, req.tokens).await?; Ok(Response::new(Empty {})) } - async fn slash(&self, _req: Request) -> Result, Status> { - todo!(); - // check_admin_key(&req)?; - // let req = check_sig_from_req(req)?; - // self.data.slash_account(&req.pubkey, req.tokens); - // Ok(Response::new(Empty {})) + async fn slash(&self, req: Request) -> Result, Status> { + check_admin_key(&req)?; + let req = check_sig_from_req(req)?; + db::Account::slash_account(&self.db, &req.pubkey, req.tokens).await?; + Ok(Response::new(Empty {})) } async fn list_accounts( diff --git a/tests/common/vm_cli_utils.rs b/tests/common/vm_cli_utils.rs index 3a1bb40..e106462 100644 --- a/tests/common/vm_cli_utils.rs +++ b/tests/common/vm_cli_utils.rs @@ -2,7 +2,7 @@ use super::test_utils::{admin_keys, Key}; use anyhow::{anyhow, Result}; use detee_shared::common_proto::Empty; use detee_shared::general_proto::brain_general_cli_client::BrainGeneralCliClient; -use detee_shared::general_proto::{AirdropReq, ReportNodeReq}; +use detee_shared::general_proto::{AirdropReq, RegOperatorReq, ReportNodeReq}; use detee_shared::vm_proto; use detee_shared::vm_proto::brain_vm_cli_client::BrainVmCliClient; use surreal_brain::constants::{ACTIVE_VM, NEW_VM_REQ}; @@ -77,3 +77,12 @@ pub async fn report_node( Ok(client_gen_cli.report_node(key.sign_request(report_req)?).await?) } + +pub async fn register_operator(brain_channel: &Channel, key: &Key, escrow: u64) -> Result<()> { + let mut cli_client = BrainGeneralCliClient::new(brain_channel.clone()); + let reg_req = + RegOperatorReq { pubkey: key.pubkey.clone(), escrow, email: "foo@bar.com".to_string() }; + + cli_client.register_operator(key.sign_request(reg_req.clone()).unwrap()).await?; + Ok(()) +} diff --git a/tests/grpc_general_test.rs b/tests/grpc_general_test.rs index b0a1092..aecc728 100644 --- a/tests/grpc_general_test.rs +++ b/tests/grpc_general_test.rs @@ -2,14 +2,14 @@ use common::prepare_test_env::{ prepare_test_db, run_service_for_stream, run_service_in_background, }; use common::test_utils::{admin_keys, Key}; -use common::vm_cli_utils::{airdrop, create_new_vm, report_node}; +use common::vm_cli_utils::{airdrop, create_new_vm, register_operator, report_node}; use common::vm_daemon_utils::{mock_vm_daemon, register_vm_node}; use detee_shared::common_proto::{Empty, Pubkey}; use detee_shared::general_proto::brain_general_cli_client::BrainGeneralCliClient; -use detee_shared::general_proto::{AirdropReq, BanUserReq}; +use detee_shared::general_proto::{AirdropReq, BanUserReq, SlashReq}; use detee_shared::vm_proto::brain_vm_daemon_client::BrainVmDaemonClient; use futures::StreamExt; -use surreal_brain::constants::{BAN, TOKEN_DECIMAL, VM_NODE}; +use surreal_brain::constants::{ACCOUNT, BAN, TOKEN_DECIMAL, VM_NODE}; use surreal_brain::db::prelude as db; use surreal_brain::db::vm::VmNodeWithReports; @@ -210,6 +210,37 @@ async fn test_inspect_operator() { assert_eq!(&inspect_response.vm_nodes[0].operator, &operator_key.pubkey); } +#[tokio::test] +async fn test_register_operator() { + let db_conn = prepare_test_db().await.unwrap(); + + let brain_channel = run_service_for_stream().await.unwrap(); + let key = Key::new(); + + let min_escrew_error = register_operator(&brain_channel, &key, 10).await.err().unwrap(); + assert!(min_escrew_error.to_string().contains("Minimum escrow amount is 5000")); + + let no_balance = register_operator(&brain_channel, &key, 5000).await.err().unwrap(); + assert!(no_balance.to_string().contains("Insufficient funds, deposit more tokens")); + + airdrop(&brain_channel, &key.pubkey, 1000).await.unwrap(); + + let no_balance = register_operator(&brain_channel, &key, 5000).await.err().unwrap(); + assert!(no_balance.to_string().contains("Insufficient funds, deposit more tokens")); + + airdrop(&brain_channel, &key.pubkey, 7000).await.unwrap(); + + register_operator(&brain_channel, &key, 6000).await.unwrap(); + + let operator_account: Option = + db_conn.select((ACCOUNT, key.pubkey)).await.unwrap(); + assert!(operator_account.is_some()); + let account = operator_account.unwrap(); + assert_eq!(account.escrow, 6000 * TOKEN_DECIMAL); + assert_eq!(account.balance, 2000 * TOKEN_DECIMAL); + assert_eq!(account.tmp_locked, 0); +} + #[tokio::test] async fn test_kick_contract() { // TODO: implement seed data to test @@ -308,3 +339,40 @@ async fn test_ban_user() { assert!(operator_banned_you.to_string().contains("This operator banned you")); } +#[tokio::test] +async fn test_slash_operator() { + let db_conn = prepare_test_db().await.unwrap(); + + let brain_channel = run_service_for_stream().await.unwrap(); + let mut cli_client = BrainGeneralCliClient::new(brain_channel.clone()); + let op_key = Key::new(); + + let admin = admin_keys()[0].clone(); + let escrew = 5500; + let slash_amt = 2500; + + airdrop(&brain_channel, &op_key.pubkey, 10000).await.unwrap(); + register_operator(&brain_channel, &op_key, escrew).await.unwrap(); + + let raw_slash_req = SlashReq { pubkey: op_key.pubkey.clone(), tokens: 2500 }; + + let other_key = Key::new(); + let admin_error = cli_client + .slash(other_key.sign_request(raw_slash_req.clone()).unwrap()) + .await + .err() + .unwrap(); + assert!(admin_error.message().contains("This operation is reserved to admin accounts")); + + let admin_error = + cli_client.slash(op_key.sign_request(raw_slash_req.clone()).unwrap()).await.err().unwrap(); + assert!(admin_error.message().contains("This operation is reserved to admin accounts")); + + cli_client.slash(admin.sign_request(raw_slash_req.clone()).unwrap()).await.unwrap(); + + let operator_account: Option = + db_conn.select((ACCOUNT, op_key.pubkey)).await.unwrap(); + assert!(operator_account.is_some()); + let account = operator_account.unwrap(); + assert_eq!(account.escrow, (escrew - slash_amt) * TOKEN_DECIMAL); +} -- 2.43.0 From e122a4e238f185690b3e5770d60f105ad8974530 Mon Sep 17 00:00:00 2001 From: Noor Date: Sun, 25 May 2025 17:37:18 +0530 Subject: [PATCH 3/6] Feat: list all accounts admin can list all accounts tests for list accounts --- src/db/general.rs | 5 +++++ src/grpc/general.rs | 25 ++++++++++++------------- src/grpc/types.rs | 12 +++++++++++- tests/common/vm_cli_utils.rs | 24 +++++++++++++++++++++++- tests/grpc_general_test.rs | 23 ++++++++++++++++++++++- 5 files changed, 73 insertions(+), 16 deletions(-) diff --git a/src/db/general.rs b/src/db/general.rs index b6d8061..3830803 100644 --- a/src/db/general.rs +++ b/src/db/general.rs @@ -107,6 +107,11 @@ impl Account { } Ok(()) } + + pub async fn list_accounts(db: &Surreal) -> Result, Error> { + let accounts: Vec = db.select(ACCOUNT).await?; + Ok(accounts) + } } impl Account { diff --git a/src/grpc/general.rs b/src/grpc/general.rs index 8c048ed..6f1fe00 100644 --- a/src/grpc/general.rs +++ b/src/grpc/general.rs @@ -178,20 +178,19 @@ impl BrainGeneralCli for GeneralCliServer { async fn list_accounts( &self, - _req: Request, + req: Request, ) -> Result, Status> { - todo!(); - // 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)) + check_admin_key(&req)?; + check_sig_from_req(req)?; + let accounts = db::Account::list_accounts(&self.db).await?; + 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)) } async fn list_all_vm_contracts( diff --git a/src/grpc/types.rs b/src/grpc/types.rs index ee7e716..900df5a 100644 --- a/src/grpc/types.rs +++ b/src/grpc/types.rs @@ -2,7 +2,7 @@ use crate::constants::{ACCOUNT, APP_NODE, ID_ALPHABET, NEW_APP_REQ, NEW_VM_REQ, use crate::db::prelude as db; use detee_shared::app_proto::AppNodeListResp; use detee_shared::common_proto::MappedPort; -use detee_shared::general_proto::{AccountBalance, ListOperatorsResp}; +use detee_shared::general_proto::{Account, AccountBalance, ListOperatorsResp}; use detee_shared::{app_proto::*, vm_proto::*}; use nanoid::nanoid; @@ -14,6 +14,16 @@ impl From for AccountBalance { } } +impl From for Account { + fn from(account: db::Account) -> Self { + Account { + pubkey: account.id.to_string(), + balance: account.balance, + tmp_locked: account.tmp_locked, + } + } +} + impl From for db::NewVmReq { fn from(new_vm_req: NewVmReq) -> Self { Self { diff --git a/tests/common/vm_cli_utils.rs b/tests/common/vm_cli_utils.rs index e106462..68251b6 100644 --- a/tests/common/vm_cli_utils.rs +++ b/tests/common/vm_cli_utils.rs @@ -2,9 +2,10 @@ use super::test_utils::{admin_keys, Key}; use anyhow::{anyhow, Result}; use detee_shared::common_proto::Empty; use detee_shared::general_proto::brain_general_cli_client::BrainGeneralCliClient; -use detee_shared::general_proto::{AirdropReq, RegOperatorReq, ReportNodeReq}; +use detee_shared::general_proto::{Account, AirdropReq, RegOperatorReq, ReportNodeReq}; use detee_shared::vm_proto; use detee_shared::vm_proto::brain_vm_cli_client::BrainVmCliClient; +use futures::StreamExt; use surreal_brain::constants::{ACTIVE_VM, NEW_VM_REQ}; use surreal_brain::db::prelude as db; use surrealdb::engine::remote::ws::Client; @@ -86,3 +87,24 @@ pub async fn register_operator(brain_channel: &Channel, key: &Key, escrow: u64) cli_client.register_operator(key.sign_request(reg_req.clone()).unwrap()).await?; Ok(()) } + +pub async fn list_accounts(brain_channel: &Channel, admin_key: &Key) -> Result> { + let mut cli_client = BrainGeneralCliClient::new(brain_channel.clone()); + let mut stream = + cli_client.list_accounts(admin_key.sign_request(Empty {}).unwrap()).await?.into_inner(); + + let mut accounts = Vec::new(); + + while let Some(stream_data) = stream.next().await { + match stream_data { + Ok(account) => { + accounts.push(account); + } + Err(e) => { + panic!("Error while listing accounts: {e:?}"); + } + } + } + + Ok(accounts) +} diff --git a/tests/grpc_general_test.rs b/tests/grpc_general_test.rs index aecc728..0a65c5b 100644 --- a/tests/grpc_general_test.rs +++ b/tests/grpc_general_test.rs @@ -2,7 +2,7 @@ use common::prepare_test_env::{ prepare_test_db, run_service_for_stream, run_service_in_background, }; use common::test_utils::{admin_keys, Key}; -use common::vm_cli_utils::{airdrop, create_new_vm, register_operator, report_node}; +use common::vm_cli_utils::{airdrop, create_new_vm, list_accounts, register_operator, report_node}; use common::vm_daemon_utils::{mock_vm_daemon, register_vm_node}; use detee_shared::common_proto::{Empty, Pubkey}; use detee_shared::general_proto::brain_general_cli_client::BrainGeneralCliClient; @@ -376,3 +376,24 @@ async fn test_slash_operator() { let account = operator_account.unwrap(); assert_eq!(account.escrow, (escrew - slash_amt) * TOKEN_DECIMAL); } + +#[tokio::test] +async fn test_admin_list_account() { + let db_conn = prepare_test_db().await.unwrap(); + let brain_channel = run_service_for_stream().await.unwrap(); + + let admin_key = admin_keys()[0].clone(); + + let unauthenticated = list_accounts(&brain_channel, &Key::new()).await.err().unwrap(); + assert!(unauthenticated.to_string().contains("reserved to admin accounts")); + + let accounts = list_accounts(&brain_channel, &admin_key).await.unwrap(); + assert_eq!(accounts.len(), 19); + + airdrop(&brain_channel, &Key::new().pubkey, 10).await.unwrap(); + + let acc_in_db = db_conn.select::>(ACCOUNT).await.unwrap(); + + let accounts = list_accounts(&brain_channel, &admin_key).await.unwrap(); + assert_eq!(accounts.len(), acc_in_db.len()); +} -- 2.43.0 From 9d1773f0f7da2617f287252e1b6e335751bc9f13 Mon Sep 17 00:00:00 2001 From: Noor Date: Sun, 25 May 2025 18:07:51 +0530 Subject: [PATCH 4/6] Feat list all VM contracts test for for list all vm contracts --- src/db/vm.rs | 6 ++++++ src/grpc/general.rs | 25 ++++++++++++------------- tests/common/vm_cli_utils.rs | 26 ++++++++++++++++++++++++++ tests/grpc_general_test.rs | 24 ++++++++++++++++++++++-- 4 files changed, 66 insertions(+), 15 deletions(-) diff --git a/src/db/vm.rs b/src/db/vm.rs index c9ddcee..80d14c0 100644 --- a/src/db/vm.rs +++ b/src/db/vm.rs @@ -814,6 +814,12 @@ impl ActiveVmWithNode { pub fn price_per_minute(&self) -> u64 { self.total_units() * self.price_per_unit } + + pub async fn list_all(db: &Surreal) -> Result, Error> { + let mut query_response = db.query(format!("SELECT * FROM {ACTIVE_VM} FETCH out;")).await?; + let active_vms: Vec = query_response.take(0)?; + Ok(active_vms) + } } // TODO: delete all of these From implementation after migration 0 gets executed diff --git a/src/grpc/general.rs b/src/grpc/general.rs index 6f1fe00..3dcb31c 100644 --- a/src/grpc/general.rs +++ b/src/grpc/general.rs @@ -195,20 +195,19 @@ impl BrainGeneralCli for GeneralCliServer { async fn list_all_vm_contracts( &self, - _req: Request, + req: Request, ) -> Result, Status> { - todo!(); - // 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::ListAllVmContractsStream)) + check_admin_key(&req)?; + check_sig_from_req(req)?; + let contracts = db::ActiveVmWithNode::list_all(&self.db).await?; + 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::ListAllVmContractsStream)) } async fn list_all_app_contracts( diff --git a/tests/common/vm_cli_utils.rs b/tests/common/vm_cli_utils.rs index 68251b6..6a6937f 100644 --- a/tests/common/vm_cli_utils.rs +++ b/tests/common/vm_cli_utils.rs @@ -108,3 +108,29 @@ pub async fn list_accounts(brain_channel: &Channel, admin_key: &Key) -> Result Result> { + let mut cli_client = BrainGeneralCliClient::new(brain_channel.clone()); + let mut stream = cli_client + .list_all_vm_contracts(admin_key.sign_request(Empty {}).unwrap()) + .await? + .into_inner(); + + let mut vm_contracts = Vec::new(); + + while let Some(stream_data) = stream.next().await { + match stream_data { + Ok(vm_contract) => { + vm_contracts.push(vm_contract); + } + Err(e) => { + panic!("Error while listing vm_contracts: {e:?}"); + } + } + } + + Ok(vm_contracts) +} diff --git a/tests/grpc_general_test.rs b/tests/grpc_general_test.rs index 0a65c5b..e9b0332 100644 --- a/tests/grpc_general_test.rs +++ b/tests/grpc_general_test.rs @@ -2,14 +2,16 @@ use common::prepare_test_env::{ prepare_test_db, run_service_for_stream, run_service_in_background, }; use common::test_utils::{admin_keys, Key}; -use common::vm_cli_utils::{airdrop, create_new_vm, list_accounts, register_operator, report_node}; +use common::vm_cli_utils::{ + airdrop, create_new_vm, list_accounts, list_all_vm_contracts, register_operator, report_node, +}; use common::vm_daemon_utils::{mock_vm_daemon, register_vm_node}; use detee_shared::common_proto::{Empty, Pubkey}; use detee_shared::general_proto::brain_general_cli_client::BrainGeneralCliClient; use detee_shared::general_proto::{AirdropReq, BanUserReq, SlashReq}; use detee_shared::vm_proto::brain_vm_daemon_client::BrainVmDaemonClient; use futures::StreamExt; -use surreal_brain::constants::{ACCOUNT, BAN, TOKEN_DECIMAL, VM_NODE}; +use surreal_brain::constants::{ACCOUNT, ACTIVE_VM, BAN, TOKEN_DECIMAL, VM_NODE}; use surreal_brain::db::prelude as db; use surreal_brain::db::vm::VmNodeWithReports; @@ -397,3 +399,21 @@ async fn test_admin_list_account() { let accounts = list_accounts(&brain_channel, &admin_key).await.unwrap(); assert_eq!(accounts.len(), acc_in_db.len()); } + +#[tokio::test] +async fn test_admin_list_all_vm_contracts() { + let db_conn = prepare_test_db().await.unwrap(); + let brain_channel = run_service_for_stream().await.unwrap(); + + let admin_key = admin_keys()[0].clone(); + + let unauthenticated = list_all_vm_contracts(&brain_channel, &Key::new()).await.err().unwrap(); + assert!(unauthenticated.to_string().contains("reserved to admin accounts")); + + let vm_in_db = db_conn.select::>(ACTIVE_VM).await.unwrap(); + + let vm_contracts = list_all_vm_contracts(&brain_channel, &admin_key).await.unwrap(); + assert_eq!(vm_contracts.len(), vm_in_db.len()); + + // TODO: mock vm daemon and deploy a new vm, then list again +} -- 2.43.0 From 56eebe5ada5fe672c5336a6c86603e850b0ccca9 Mon Sep 17 00:00:00 2001 From: Noor Date: Sun, 25 May 2025 20:11:30 +0530 Subject: [PATCH 5/6] Feat listing all app contracts tests for list all app contract modified mock data for kick contract --- src/db/app.rs | 6 ++++++ src/grpc/general.rs | 25 ++++++++++++------------- tests/common/vm_cli_utils.rs | 27 +++++++++++++++++++++++++++ tests/grpc_general_test.rs | 34 ++++++++++++++++++++++++++++++---- tests/mock_data.yaml | 19 +++++++++++++++++++ 5 files changed, 94 insertions(+), 17 deletions(-) diff --git a/src/db/app.rs b/src/db/app.rs index 2245313..3abe616 100644 --- a/src/db/app.rs +++ b/src/db/app.rs @@ -404,6 +404,12 @@ impl ActiveAppWithNode { None => Ok(vec![]), } } + + pub async fn list_all(db: &Surreal) -> Result, Error> { + let mut query_response = db.query(format!("SELECT * FROM {ACTIVE_APP} FETCH out;")).await?; + let active_apps: Vec = query_response.take(0)?; + Ok(active_apps) + } } #[derive(Debug, Serialize)] diff --git a/src/grpc/general.rs b/src/grpc/general.rs index 3dcb31c..667908b 100644 --- a/src/grpc/general.rs +++ b/src/grpc/general.rs @@ -212,19 +212,18 @@ impl BrainGeneralCli for GeneralCliServer { async fn list_all_app_contracts( &self, - _req: tonic::Request, + req: tonic::Request, ) -> Result, Status> { - todo!(); - // 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))) + check_admin_key(&req)?; + check_sig_from_req(req)?; + let contracts = db::ActiveAppWithNode::list_all(&self.db).await?; + 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))) } } diff --git a/tests/common/vm_cli_utils.rs b/tests/common/vm_cli_utils.rs index 6a6937f..4e4b31a 100644 --- a/tests/common/vm_cli_utils.rs +++ b/tests/common/vm_cli_utils.rs @@ -1,5 +1,6 @@ use super::test_utils::{admin_keys, Key}; use anyhow::{anyhow, Result}; +use detee_shared::app_proto; use detee_shared::common_proto::Empty; use detee_shared::general_proto::brain_general_cli_client::BrainGeneralCliClient; use detee_shared::general_proto::{Account, AirdropReq, RegOperatorReq, ReportNodeReq}; @@ -134,3 +135,29 @@ pub async fn list_all_vm_contracts( Ok(vm_contracts) } + +pub async fn list_all_app_contracts( + brain_channel: &Channel, + admin_key: &Key, +) -> Result> { + let mut cli_client = BrainGeneralCliClient::new(brain_channel.clone()); + let mut stream = cli_client + .list_all_app_contracts(admin_key.sign_request(Empty {}).unwrap()) + .await? + .into_inner(); + + let mut app_contracts = Vec::new(); + + while let Some(stream_data) = stream.next().await { + match stream_data { + Ok(app_contract) => { + app_contracts.push(app_contract); + } + Err(e) => { + panic!("Error while listing app_contracts: {e:?}"); + } + } + } + + Ok(app_contracts) +} diff --git a/tests/grpc_general_test.rs b/tests/grpc_general_test.rs index e9b0332..0eb9ef1 100644 --- a/tests/grpc_general_test.rs +++ b/tests/grpc_general_test.rs @@ -3,7 +3,8 @@ use common::prepare_test_env::{ }; use common::test_utils::{admin_keys, Key}; use common::vm_cli_utils::{ - airdrop, create_new_vm, list_accounts, list_all_vm_contracts, register_operator, report_node, + airdrop, create_new_vm, list_accounts, list_all_app_contracts, list_all_vm_contracts, + register_operator, report_node, }; use common::vm_daemon_utils::{mock_vm_daemon, register_vm_node}; use detee_shared::common_proto::{Empty, Pubkey}; @@ -11,7 +12,7 @@ use detee_shared::general_proto::brain_general_cli_client::BrainGeneralCliClient use detee_shared::general_proto::{AirdropReq, BanUserReq, SlashReq}; use detee_shared::vm_proto::brain_vm_daemon_client::BrainVmDaemonClient; use futures::StreamExt; -use surreal_brain::constants::{ACCOUNT, ACTIVE_VM, BAN, TOKEN_DECIMAL, VM_NODE}; +use surreal_brain::constants::{ACCOUNT, ACTIVE_APP, ACTIVE_VM, BAN, TOKEN_DECIMAL, VM_NODE}; use surreal_brain::db::prelude as db; use surreal_brain::db::vm::VmNodeWithReports; @@ -264,7 +265,7 @@ async fn test_kick_contract() { */ let db_conn = prepare_test_db().await.unwrap(); - let contract_uuid = "e3d01f252b2a410b80e312f44e474334"; + let contract_uuid = "5af49a714c64a82ef50e574b023b2a0ef0405ed"; let operator_wallet = "7V3rEuh6j8VuwMVB5PyGqWKLmjJ4fYSv6WtrTL51NZTB"; let reason = "'; THROW 'Injected error'; --"; // sql injection query @@ -341,6 +342,7 @@ async fn test_ban_user() { assert!(operator_banned_you.to_string().contains("This operator banned you")); } + #[tokio::test] async fn test_slash_operator() { let db_conn = prepare_test_db().await.unwrap(); @@ -389,8 +391,11 @@ async fn test_admin_list_account() { let unauthenticated = list_accounts(&brain_channel, &Key::new()).await.err().unwrap(); assert!(unauthenticated.to_string().contains("reserved to admin accounts")); + tokio::time::sleep(tokio::time::Duration::from_millis(200)).await; // sync time for other db opeartion + + let acc_in_db = db_conn.select::>(ACCOUNT).await.unwrap(); let accounts = list_accounts(&brain_channel, &admin_key).await.unwrap(); - assert_eq!(accounts.len(), 19); + assert_eq!(accounts.len(), acc_in_db.len()); airdrop(&brain_channel, &Key::new().pubkey, 10).await.unwrap(); @@ -417,3 +422,24 @@ async fn test_admin_list_all_vm_contracts() { // TODO: mock vm daemon and deploy a new vm, then list again } + +#[tokio::test] +async fn test_admin_list_all_app_contracts() { + let db_conn = prepare_test_db().await.unwrap(); + let brain_channel = run_service_for_stream().await.unwrap(); + + let admin_key = admin_keys()[0].clone(); + + let unauthenticated = list_all_app_contracts(&brain_channel, &Key::new()).await.err().unwrap(); + assert!(unauthenticated.to_string().contains("reserved to admin accounts")); + + let app_contracts = list_all_app_contracts(&brain_channel, &admin_key).await.unwrap(); + assert_eq!(app_contracts.len(), 1); + + let app_in_db = db_conn.select::>(ACTIVE_APP).await.unwrap(); + + let app_contracts = list_all_app_contracts(&brain_channel, &admin_key).await.unwrap(); + assert_eq!(app_contracts.len(), app_in_db.len()); + + // TODO: mock app daemon and deploy a new app, then list again +} diff --git a/tests/mock_data.yaml b/tests/mock_data.yaml index f651f11..1bae3f9 100644 --- a/tests/mock_data.yaml +++ b/tests/mock_data.yaml @@ -405,6 +405,25 @@ vm_contracts: price_per_unit: 20000 locked_nano: 12730960000 collected_at: 2025-04-20T00:34:15.461240342Z + + - uuid: 5af49a71-4c64-a82e-f50e574-b023-b2a0ef0405ed + hostname: hallow-hobo + admin_pubkey: 4qFJJJdRrSB9hCn8rrvYTXHLJg371ab36PJmZ4uxHjGQ + node_pubkey: 7fujZQeTme52RdXTLmQST5jBgAbvzic5iERtH5EWoYjk + exposed_ports: + - 46393 + public_ipv4: "" + public_ipv6: "" + disk_size_gb: 10 + vcpus: 1 + memory_mb: 1000 + kernel_sha: e765e56166ef321b53399b9638584d1279821dbe3d46191c1f66bbaa075e7919 + dtrfs_sha: d207644ee60d54009b6ecdfb720e2ec251cde31774dd249fcc7435aca0377990 + created_at: 2025-04-16T20:37:57.176592933Z + updated_at: 2025-04-16T20:37:57.176594069Z + price_per_unit: 20000 + locked_nano: 12730960000 + collected_at: 2025-04-20T00:34:15.461240342Z app_nodes: - node_pubkey: BiqoPUEoAxYxMRXUmyofoS9H1TBQgQqvLJ6MbWh88AQg operator_wallet: 7V3rEuh6j8VuwMVB5PyGqWKLmjJ4fYSv6WtrTL51NZTB -- 2.43.0 From bdf98f3bbdd2157a1e97b27e3165f3a02592b092 Mon Sep 17 00:00:00 2001 From: Noor Date: Mon, 26 May 2025 03:07:39 +0530 Subject: [PATCH 6/6] minor refactor changed function names --- src/db/general.rs | 6 +++--- src/grpc/general.rs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/db/general.rs b/src/db/general.rs index 3830803..2d29afb 100644 --- a/src/db/general.rs +++ b/src/db/general.rs @@ -180,7 +180,7 @@ pub struct Ban { } impl Ban { - pub async fn get_record_by_wallets( + pub async fn get_record( db: &Surreal, op_wallet: &str, user_wallet: &str, @@ -196,12 +196,12 @@ impl Ban { Ok(ban_record) } - pub async fn ban_a_user( + pub async fn create( db: &Surreal, op_wallet: &str, user_wallet: &str, ) -> Result<(), Error> { - if Self::get_record_by_wallets(db, op_wallet, user_wallet).await?.is_some() { + if Self::get_record(db, op_wallet, user_wallet).await?.is_some() { log::error!("User {user_wallet} is already banned by {op_wallet}"); return Err(Error::AlreadyBanned(op_wallet.to_string())); } diff --git a/src/grpc/general.rs b/src/grpc/general.rs index 667908b..38e7bcc 100644 --- a/src/grpc/general.rs +++ b/src/grpc/general.rs @@ -155,7 +155,7 @@ impl BrainGeneralCli for GeneralCliServer { async fn ban_user(&self, req: Request) -> Result, Status> { let req = check_sig_from_req(req)?; log::info!("Banning user: {}, by: {}", req.user_wallet, req.operator_wallet); - db::Ban::ban_a_user(&self.db, &req.operator_wallet, &req.user_wallet).await?; + db::Ban::create(&self.db, &req.operator_wallet, &req.user_wallet).await?; Ok(Response::new(Empty {})) } -- 2.43.0