// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Unlicense use anyhow::Result; use detee_shared::general_proto::brain_general_cli_client::BrainGeneralCliClient; use detee_shared::general_proto::AirdropReq; use detee_shared::{app_proto, vm_proto}; use ed25519_dalek::{Signer, SigningKey}; use itertools::Itertools; use rand::Rng; use std::net::Ipv4Addr; use std::sync::OnceLock; use surreal_brain::constants::TOKEN_DECIMAL; use tonic::metadata::AsciiMetadataValue; use tonic::transport::Channel; use tonic::Request; pub static ADMIN_KEYS: OnceLock> = OnceLock::new(); pub fn admin_keys() -> Vec { let admin_keys = ADMIN_KEYS.get_or_init(|| { let admin_keys = vec![Key::new(), Key::new(), Key::new()]; let admin_pub_keys = admin_keys.iter().map(|k| k.pubkey.clone()).join(", "); std::env::set_var("ADMIN_PUB_KEYS", admin_pub_keys); admin_keys.clone() }); admin_keys.clone() } #[derive(Debug, Clone)] pub struct Key { pub sg_key: SigningKey, pub pubkey: String, } impl Key { pub fn new() -> Self { let sk = SigningKey::generate(&mut rand::rngs::OsRng); let pubkey = bs58::encode(sk.verifying_key().to_bytes()).into_string(); 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(); let signature = self.try_sign_message(&format!("{timestamp}{req:?}"))?; let timestamp: AsciiMetadataValue = timestamp.parse()?; let pubkey: AsciiMetadataValue = pubkey.parse()?; let signature: AsciiMetadataValue = signature.parse()?; let mut req = Request::new(req); req.metadata_mut().insert("timestamp", timestamp); req.metadata_mut().insert("pubkey", pubkey); req.metadata_mut().insert("request-signature", signature); Ok(req) } pub fn try_sign_message(&self, message: &str) -> Result { let key = self.sg_key.clone(); Ok(bs58::encode(key.sign(message.as_bytes()).to_bytes()).into_string()) } pub fn sign_stream_auth_vm( &self, contracts: Vec, ) -> Result { let pubkey = self.pubkey.clone(); let timestamp = chrono::Utc::now().to_rfc3339(); let signature = self.try_sign_message(&(timestamp.to_string() + &format!("{contracts:?}")))?; Ok(vm_proto::DaemonStreamAuth { timestamp, pubkey, contracts, signature }) } pub fn sign_stream_auth_app(&self, contracts: Vec) -> Result { let pubkey = self.pubkey.clone(); let timestamp = chrono::Utc::now().to_rfc3339(); let signature = self.try_sign_message(&(timestamp.to_string() + &format!("{contracts:?}")))?; Ok(app_proto::DaemonAuth { timestamp, pubkey, contracts, signature }) } } pub async fn airdrop(brain_channel: &Channel, wallet: &str, amount: u64) -> Result<()> { let mut client = BrainGeneralCliClient::new(brain_channel.clone()); let airdrop_req = AirdropReq { pubkey: wallet.to_string(), tokens: amount * TOKEN_DECIMAL }; let admin_key = admin_keys()[0].clone(); client.airdrop(admin_key.sign_request(airdrop_req.clone())?).await?; Ok(()) } pub async fn get_ip_info(ip: &str) -> anyhow::Result { let body = reqwest::get(format!("https://ipinfo.io/{ip}")).await?.text().await?; log::info!("Got the following data from ipinfo.io: {body}"); Ok(serde_json::de::from_str(&body)?) } #[derive(serde::Deserialize, Clone, Debug)] pub struct IPInfo { pub country: String, pub region: String, pub city: String, pub ip: String, } pub fn generate_random_public_ip() -> Ipv4Addr { let mut rng = rand::thread_rng(); loop { let ip = Ipv4Addr::from(rng.gen::()); if !ip.is_private() && !ip.is_loopback() && !ip.is_link_local() && !ip.is_broadcast() && !ip.is_documentation() && !ip.is_unspecified() && !ip.is_multicast() && ip.octets()[0] < 240 { return ip; } } }