137 lines
4.6 KiB
Rust
137 lines
4.6 KiB
Rust
// 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<Vec<Key>> = OnceLock::new();
|
|
|
|
pub fn admin_keys() -> Vec<Key> {
|
|
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<T: std::fmt::Debug>(&self, req: T) -> Result<Request<T>> {
|
|
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<String> {
|
|
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<String>,
|
|
) -> Result<vm_proto::DaemonStreamAuth> {
|
|
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<String>) -> Result<app_proto::DaemonAuth> {
|
|
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<IPInfo> {
|
|
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::<u32>());
|
|
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;
|
|
}
|
|
}
|
|
}
|