From a8cf515061c5aeeb63758a1e77327b81fb80df1e Mon Sep 17 00:00:00 2001 From: ghe0 Date: Thu, 24 Apr 2025 02:47:52 +0300 Subject: [PATCH] added report and airdrop --- src/db.rs | 64 ++++++++++++++++++++-------------------- src/grpc.rs | 84 ++++++++++++++++++++++++++++++++++++----------------- 2 files changed, 90 insertions(+), 58 deletions(-) diff --git a/src/db.rs b/src/db.rs index 9302c97..341b7c6 100644 --- a/src/db.rs +++ b/src/db.rs @@ -51,36 +51,6 @@ pub async fn migration0(old_data: &old_brain::BrainData) -> surrealdb::Result<() Ok(()) } -pub async fn account(address: &str) -> Result { - let id = (ACCOUNT, address); - let account: Option = DB.select(id).await?; - let account = match account { - Some(account) => account, - None => { - Account { id: id.into(), balance: 0, tmp_locked: 0, escrow: 0, email: String::new() } - } - }; - Ok(account) -} - -// I am not deleting this example cause I might need it later. -// -// async fn get_wallet_contracts() -> surrealdb::Result> { -// let mut result = DB -// .query("select *, ->contract.* from wallet:address1;") -// .await?; -// let wallets: Vec = result.take(0)?; -// Ok(wallets) -// } -// -// #[derive(Debug, Serialize, Deserialize)] -// pub struct Wallet { -// balance: u64, -// id: RecordId, -// #[serde(rename = "->contract", default)] -// contracts: Vec, -// } - #[derive(Debug, Serialize, Deserialize)] pub struct Account { pub id: RecordId, @@ -90,6 +60,28 @@ pub struct Account { pub email: String, } +impl Account { + pub async fn get(address: &str) -> Result { + let id = (ACCOUNT, address); + let account: Option = DB.select(id).await?; + let account = match account { + Some(account) => account, + None => { + Self { id: id.into(), balance: 0, tmp_locked: 0, escrow: 0, email: String::new() } + } + }; + Ok(account) + } + + pub async fn airdrop(account: &str, tokens: u64) -> Result<(), Error> { + let tokens = tokens.saturating_mul(1_000_000_000); + let _ = DB + .query(format!("upsert account:{account} SET balance = (balance || 0) + {tokens};")) + .await?; + Ok(()) + } +} + #[derive(Debug, Serialize, Deserialize)] pub struct VmNode { pub id: RecordId, @@ -291,7 +283,6 @@ pub struct Kick { #[derive(Debug, Serialize, Deserialize)] pub struct Report { - id: RecordId, #[serde(rename = "in")] from_account: RecordId, #[serde(rename = "out")] @@ -300,6 +291,17 @@ pub struct Report { reason: String, } +impl Report { + // TODO: test this functionality and remove this comment + pub async fn create(from_account: RecordId, to_node: RecordId, reason: String) -> Result<(), Error> { + let _: Vec = DB + .insert("report") + .relation(Report { from_account, to_node, created_at: Datetime::default(), reason }) + .await?; + Ok(()) + } +} + #[derive(Debug, Serialize, Deserialize)] pub struct OperatorRelation { #[serde(rename = "in")] diff --git a/src/grpc.rs b/src/grpc.rs index 1580ba4..d7f1cd0 100644 --- a/src/grpc.rs +++ b/src/grpc.rs @@ -95,29 +95,25 @@ impl BrainGeneralCli for BrainGeneralCliMock { async fn get_balance(&self, req: Request) -> Result, Status> { let req = check_sig_from_req(req)?; - Ok(Response::new(db::account(&req.pubkey).await?.into())) + Ok(Response::new(db::Account::get(&req.pubkey).await?.into())) } async fn report_node(&self, req: Request) -> Result, Status> { - let _req = check_sig_from_req(req)?; - todo!(); - // match self.data.find_any_contract_by_uuid(&req.contract) { - // Ok((Some(vm_contract), _)) - // if vm_contract.admin_pubkey == req.admin_pubkey - // && vm_contract.node_pubkey == req.node_pubkey => - // { - // () - // } - // Ok((_, Some(app_contract))) - // if app_contract.admin_pubkey == req.admin_pubkey - // && app_contract.node_pubkey == req.node_pubkey => - // { - // () - // } - // _ => return Err(Status::unauthenticated("No contract found by this ID.")), - // }; - // self.data.report_any_node(req.admin_pubkey, &req.node_pubkey, req.reason); - // Ok(Response::new(Empty {})) + let req = check_sig_from_req(req)?; + let (account, node) = match db::VmContractWithNode::get_by_uuid(&req.contract).await? { + Some(vm_contract) + if vm_contract.admin.key().to_string() == req.admin_pubkey + && vm_contract.vm_node.id.key().to_string() == req.node_pubkey => + { + (vm_contract.admin, vm_contract.vm_node.id) + } + _ => { + // TODO: Hey, Noor! Please add app contract here. + return Err(Status::unauthenticated("No contract found by this ID.")); + } + }; + db::Report::create(account, node, req.reason).await?; + Ok(Response::new(Empty {})) } async fn list_operators( @@ -178,12 +174,11 @@ impl BrainGeneralCli for BrainGeneralCliMock { // admin commands - async fn airdrop(&self, _req: Request) -> Result, Status> { - todo!(); - // check_admin_key(&req)?; - // let req = check_sig_from_req(req)?; - // self.data.give_airdrop(&req.pubkey, req.tokens); - // Ok(Response::new(Empty {})) + async fn airdrop(&self, req: Request) -> Result, Status> { + check_admin_key(&req)?; + let req = check_sig_from_req(req)?; + db::Account::airdrop(&req.pubkey, req.tokens).await?; + Ok(Response::new(Empty {})) } async fn slash(&self, _req: Request) -> Result, Status> { @@ -420,13 +415,26 @@ macro_rules! impl_pubkey_getter { impl_pubkey_getter!(Pubkey, pubkey); impl_pubkey_getter!(NewVmReq, admin_pubkey); impl_pubkey_getter!(DeleteVmReq, admin_pubkey); -impl_pubkey_getter!(ReportNodeReq, admin_pubkey); impl_pubkey_getter!(UpdateVmReq, admin_pubkey); impl_pubkey_getter!(ExtendVmReq, admin_pubkey); +impl_pubkey_getter!(ReportNodeReq, admin_pubkey); impl_pubkey_getter!(ListVmContractsReq, wallet); impl_pubkey_getter!(RegisterVmNodeReq, node_pubkey); +impl_pubkey_getter!(RegOperatorReq, pubkey); +impl_pubkey_getter!(KickReq, operator_wallet); +impl_pubkey_getter!(BanUserReq, operator_wallet); + impl_pubkey_getter!(VmNodeFilters); impl_pubkey_getter!(Empty); +impl_pubkey_getter!(AirdropReq); +impl_pubkey_getter!(SlashReq); + +// impl_pubkey_getter!(NewAppReq, admin_pubkey); +// 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") { @@ -490,3 +498,25 @@ fn check_sig_from_req(req: Request) -> Res } Ok(req) } + +const ADMIN_ACCOUNTS: &[&str] = &[ + "x52w7jARC5erhWWK65VZmjdGXzBK6ZDgfv1A283d8XK", + "FHuecMbeC1PfjkW2JKyoicJAuiU7khgQT16QUB3Q1XdL", + "H21Shi4iE7vgfjWEQNvzmpmBMJSaiZ17PYUcdNoAoKNc", +]; + +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(()) +}