metrics and refactoring

Signed-off-by: Valentyn Faychuk <valy@detee.ltd>
This commit is contained in:
Valentyn Faychuk 2024-12-02 03:39:27 +02:00
parent 3c93b258f5
commit 2a87efacc1
Signed by: valy
GPG Key ID: F1AB995E20FEADC5
10 changed files with 298 additions and 221 deletions

40
Cargo.lock generated

@ -1490,6 +1490,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
dependencies = [
"powerfmt",
"serde",
]
[[package]]
@ -1525,7 +1526,7 @@ dependencies = [
[[package]]
name = "detee-sgx"
version = "0.1.0"
source = "git+ssh://git@gitea.detee.cloud/SGX/detee-sgx#a47753a8e07ef533cca5df41bea4893c9eeb133e"
source = "git+ssh://git@gitea.detee.cloud/SGX/detee-sgx?branch=hacker-challenge#2f032b5c5448be40feda466bb457821ef814f959"
dependencies = [
"aes-gcm",
"base64 0.22.1",
@ -2042,6 +2043,7 @@ dependencies = [
"rustls 0.23.14",
"serde",
"serde_json",
"serde_with 3.11.0",
"solana-client",
"solana-program",
"solana-sdk",
@ -2418,6 +2420,7 @@ checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
dependencies = [
"autocfg",
"hashbrown 0.12.3",
"serde",
]
[[package]]
@ -2428,6 +2431,7 @@ checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da"
dependencies = [
"equivalent",
"hashbrown 0.15.0",
"serde",
]
[[package]]
@ -3893,7 +3897,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07ff71d2c147a7b57362cead5e22f772cd52f6ab31cfcd9edcd7f6aeb2a0afbe"
dependencies = [
"serde",
"serde_with_macros",
"serde_with_macros 2.3.3",
]
[[package]]
name = "serde_with"
version = "3.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e28bdad6db2b8340e449f7108f020b3b092e8583a9e3fb82713e1d4e71fe817"
dependencies = [
"base64 0.22.1",
"chrono",
"hex",
"indexmap 1.9.3",
"indexmap 2.6.0",
"serde",
"serde_derive",
"serde_json",
"serde_with_macros 3.11.0",
"time",
]
[[package]]
@ -3908,6 +3930,18 @@ dependencies = [
"syn 2.0.79",
]
[[package]]
name = "serde_with_macros"
version = "3.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d846214a9854ef724f3da161b426242d8de7c1fc7de2f89bb1efcb154dca79d"
dependencies = [
"darling",
"proc-macro2",
"quote",
"syn 2.0.79",
]
[[package]]
name = "sha1"
version = "0.10.6"
@ -4510,7 +4544,7 @@ dependencies = [
"serde_bytes",
"serde_derive",
"serde_json",
"serde_with",
"serde_with 2.3.3",
"sha2 0.10.8",
"sha3 0.10.8",
"siphasher",

@ -12,6 +12,7 @@ prost = "0.13"
prost-types = "0.13"
rand = "0.8"
serde = { version = "1.0", features = ["derive"] }
serde_with = { version = "3.11", features = ["macros", "base64"] }
serde_json = "1.0"
solana-client = "2.0"
solana-program = "2.0"
@ -36,8 +37,7 @@ hyper-util = "0.1.7"
hyper-rustls = { version = "0.27", features = ["http2"] }
base64 = "0.22"
lazy_static = "1.5"
# TODO: create a feature for testing, make occlum feature optional and added only if not compiling for testing
detee-sgx = { git = "ssh://git@gitea.detee.cloud/SGX/detee-sgx", features = ["tonic", "occlum", "sealing"] }
detee-sgx = { git = "ssh://git@gitea.detee.cloud/SGX/detee-sgx", branch = "hacker-challenge", features = ["tonic", "occlum", "sealing"] }
env_logger = "0.11"

@ -1,8 +1,6 @@
#![allow(dead_code)]
use crate::solana::Client as SolClient;
use crate::persistence::Logfile;
use dashmap::{DashMap, DashSet};
use std::time::{Duration, SystemTime};
use crate::persistence::Logfile;
type IP = String;
pub const LOCALHOST: &str = "localhost";
@ -14,7 +12,7 @@ pub struct NodeInfo {
pub mint_requests: u64,
pub mints: u64,
pub mratls_conns: u64,
pub quote_attacks: u64,
pub net_attacks: u64,
pub public: bool,
pub restarts: u64,
pub disk_attacks: u64,
@ -28,7 +26,8 @@ impl NodeInfo {
}
}
if self.mint_requests > other_node.mint_requests
|| self.quote_attacks > other_node.quote_attacks
|| self.net_attacks > other_node.net_attacks
|| self.disk_attacks > other_node.disk_attacks
|| self.mratls_conns > other_node.mratls_conns
|| self.mints > other_node.mints
|| (self.public && !other_node.public)
@ -45,19 +44,17 @@ impl NodeInfo {
}
}
/// Keypair must already be known when creating a Store
/// This means the first node of the network creates the key
/// Second node will grab the key from the first node
pub struct Store {
sol_client: SolClient,
/// Multithreaded state, designed to be
/// shared everywhere in the code
pub struct State {
nodes: DashMap<IP, NodeInfo>,
conns: DashSet<IP>,
}
impl Store {
pub fn init(sol_client: SolClient) -> Self {
let store = Self { sol_client, nodes: DashMap::new(), conns: DashSet::new() };
store.nodes.insert(
impl State {
pub fn new() -> Self {
let state = Self { nodes: DashMap::new(), conns: DashSet::new() };
state.nodes.insert(
LOCALHOST.to_string(),
NodeInfo {
started_at: SystemTime::now(),
@ -65,25 +62,13 @@ impl Store {
mint_requests: 0,
mints: 0,
mratls_conns: 0,
quote_attacks: 0,
net_attacks: 0,
public: false,
restarts: 0,
disk_attacks: 0,
},
);
store
}
pub fn get_token_address(&self) -> String {
self.sol_client.token_address()
}
pub fn get_pubkey_base58(&self) -> String {
self.sol_client.wallet_address()
}
pub fn get_keypair_bytes(&self) -> Vec<u8> {
self.sol_client.get_keypair_bytes()
state
}
pub fn add_conn(&self, ip: &str) {
@ -122,12 +107,11 @@ impl Store {
}
}
pub fn mint(&self, recipient: &str) -> Result<String, Box<dyn std::error::Error>> {
use std::str::FromStr;
let recipient = solana_sdk::pubkey::Pubkey::from_str(recipient)?;
let sig = self.sol_client.mint(&recipient)?;
self.increase_mints();
Ok(sig)
pub fn increase_net_attacks(&self) {
if let Some(mut localhost_info) = self.nodes.get_mut(LOCALHOST) {
localhost_info.net_attacks += 1;
localhost_info.log(localhost_info.key());
}
}
pub fn get_localhost(&self) -> NodeInfo {

@ -1,10 +1,9 @@
#![allow(dead_code)]
use super::challenge::NodeUpdate;
use super::challenge::{Keys, NodeUpdate};
use crate::{
datastore::{Store, LOCALHOST},
datastore::{State, LOCALHOST},
grpc::challenge::{update_client::UpdateClient, Empty},
};
use solana_sdk::{pubkey::Pubkey, signature::keypair::Keypair};
use detee_sgx::RaTlsConfig;
use std::{str::FromStr, sync::Arc};
use tokio::{
sync::broadcast::Sender,
@ -14,13 +13,14 @@ use tokio_stream::{wrappers::BroadcastStream, StreamExt};
#[derive(Clone)]
pub struct ConnManager {
ds: Arc<Store>,
state: Arc<State>,
tx: Sender<NodeUpdate>,
ratls_config: RaTlsConfig,
}
impl ConnManager {
pub fn init(ds: Arc<Store>, tx: Sender<NodeUpdate>) -> Self {
Self { ds, tx }
pub fn init(state: Arc<State>, ratls_config: RaTlsConfig, tx: Sender<NodeUpdate>) -> Self {
Self { state, ratls_config, tx }
}
pub async fn start_with_node(self, node_ip: String) {
@ -29,7 +29,7 @@ impl ConnManager {
pub async fn start(self) {
loop {
if let Some(node) = self.ds.get_random_node() {
if let Some(node) = self.state.get_random_node() {
if node != LOCALHOST {
self.connect_wrapper(node.clone()).await;
}
@ -39,7 +39,7 @@ impl ConnManager {
}
async fn connect_wrapper(&self, node_ip: String) {
let ds = self.ds.clone();
let ds = self.state.clone();
ds.add_conn(&node_ip);
if let Err(e) = self.connect(node_ip.clone()).await {
println!("Client connection for {node_ip} failed: {e:?}");
@ -48,20 +48,14 @@ impl ConnManager {
}
async fn connect(&self, node_ip: String) -> Result<(), Box<dyn std::error::Error>> {
use detee_sgx::{prelude::*, RaTlsConfigBuilder};
use detee_sgx::RaTlsConfigBuilder;
use hyper::Uri;
use hyper_util::{client::legacy::connect::HttpConnector, rt::TokioExecutor};
use tokio_rustls::rustls::ClientConfig;
println!("Connecting to {node_ip}...");
let mrsigner_hex = "83E8A0C3ED045D9747ADE06C3BFC70FCA661A4A65FF79A800223621162A88B76";
let mrsigner =
crate::sgx::mrsigner_from_hex(mrsigner_hex).expect("mrsigner decoding failed");
let config = RaTlsConfig::new()
.allow_instance_measurement(InstanceMeasurement::new().with_mrsigners(vec![mrsigner]));
let tls = ClientConfig::from_ratls_config(config)
let tls = ClientConfig::from_ratls_config(self.ratls_config.clone())
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, format!("{}", e)))?;
let mut http = HttpConnector::new();
@ -93,10 +87,14 @@ impl ConnManager {
let rx = self.tx.subscribe();
let rx_stream = BroadcastStream::new(rx).filter_map(|n| n.ok());
let response = client.get_updates(rx_stream).await?;
let response = client.get_updates(rx_stream).await.map_err(|e| {
println!("Error connecting to {node_ip}: {e}");
self.state.increase_net_attacks();
e
})?;
let mut resp_stream = response.into_inner();
let _ = self.tx.send((LOCALHOST.to_string(), self.ds.get_localhost()).into());
let _ = self.tx.send((LOCALHOST.to_string(), self.state.get_localhost()).into());
while let Some(mut update) = resp_stream.message().await? {
// "localhost" IPs need to be changed to the real IP of the counterpart
@ -108,7 +106,7 @@ impl ConnManager {
}
// update the entire network in case the information is new
if self.ds.process_node_update(update.clone().into())
if self.state.process_node_update(update.clone().into())
&& self.tx.send(update.clone()).is_err()
{
println!("tokio broadcast receivers had an issue consuming the channel");
@ -119,20 +117,19 @@ impl ConnManager {
}
}
pub async fn key_grabber(node_ip: String) -> Result<(Keypair, Pubkey), Box<dyn std::error::Error>> {
use detee_sgx::{prelude::*, RaTlsConfigBuilder};
pub async fn key_grabber(
state: Arc<State>,
node_ip: String,
ratls_config: RaTlsConfig,
) -> Result<Keys, Box<dyn std::error::Error>> {
use detee_sgx::RaTlsConfigBuilder;
use hyper::Uri;
use hyper_util::{client::legacy::connect::HttpConnector, rt::TokioExecutor};
use tokio_rustls::rustls::ClientConfig;
println!("Getting key from {node_ip}...");
let mrsigner_hex = "83E8A0C3ED045D9747ADE06C3BFC70FCA661A4A65FF79A800223621162A88B76";
let mrsigner = crate::sgx::mrsigner_from_hex(mrsigner_hex).expect("mrsigner decoding failed");
let config = RaTlsConfig::new()
.allow_instance_measurement(InstanceMeasurement::new().with_mrsigners(vec![mrsigner]));
let tls = ClientConfig::from_ratls_config(config)
let tls = ClientConfig::from_ratls_config(ratls_config)
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, format!("{}", e)))?;
let mut http = HttpConnector::new();
@ -160,14 +157,10 @@ pub async fn key_grabber(node_ip: String) -> Result<(Keypair, Pubkey), Box<dyn s
let uri = Uri::from_static("https://example.com");
let mut client = UpdateClient::with_origin(client, uri);
let response = client.get_keys(tonic::Request::new(Empty {})).await?;
let response = &response.into_inner();
let keypair = response.keypair.clone();
let keypair = match Keypair::from_bytes(&keypair) {
Ok(k) => k,
Err(_) => return Err("Could not parse keypair.".into()),
};
let token_address = Pubkey::from_str(&response.token_address)
.map_err(|_| "Could not parse wallet address.".to_string())?;
Ok((keypair, token_address))
let response = client.get_keys(tonic::Request::new(Empty {})).await.map_err(|e| {
println!("Error getting keys from {node_ip}: {e}");
state.increase_net_attacks();
e
})?;
Ok(response.into_inner())
}

@ -16,10 +16,10 @@ impl From<(String, NodeInfo)> for NodeUpdate {
mint_requests: info.mint_requests,
mints: info.mints,
mratls_conns: info.mratls_conns,
quote_attacks: info.quote_attacks,
quote_attacks: info.net_attacks,
public: info.public,
restarts: info.restarts,
disk_attacks: info.disk_attacks
disk_attacks: info.disk_attacks,
}
}
}
@ -47,10 +47,10 @@ impl From<NodeUpdate> for (String, NodeInfo) {
mint_requests: val.mint_requests,
mints: val.mints,
mratls_conns: val.mratls_conns,
quote_attacks: val.quote_attacks,
net_attacks: val.quote_attacks,
public: val.public,
restarts: val.restarts,
disk_attacks: val.disk_attacks
disk_attacks: val.disk_attacks,
};
(ip, self_info)
}

@ -1,23 +1,30 @@
#![allow(dead_code)]
use super::challenge::{update_server::UpdateServer, Empty, Keys, NodeUpdate};
use crate::{datastore::Store, grpc::challenge::update_server::Update};
use crate::{datastore::State, grpc::challenge::update_server::Update};
use detee_sgx::RaTlsConfig;
use std::{pin::Pin, sync::Arc};
use tokio::sync::broadcast::Sender;
use tokio_stream::{Stream, StreamExt};
use tonic::{Request, Response, Status, Streaming};
pub struct MyServer {
ds: Arc<Store>,
state: Arc<State>,
tx: Sender<NodeUpdate>,
ratls_config: RaTlsConfig,
keys: Keys, // For sending secret keys to new nodes ;)
}
impl MyServer {
pub fn init(ds: Arc<Store>, tx: Sender<NodeUpdate>) -> Self {
Self { ds, tx }
pub fn init(
state: Arc<State>,
keys: Keys,
ratls_config: RaTlsConfig,
tx: Sender<NodeUpdate>,
) -> Self {
Self { state, tx, keys, ratls_config }
}
pub async fn start(self) {
use detee_sgx::RaTlsConfigBuilder;
use hyper::server::conn::http2::Builder;
use hyper_util::{
rt::{TokioExecutor, TokioIo},
@ -29,22 +36,12 @@ impl MyServer {
use tonic::{body::boxed, service::Routes};
use tower::{ServiceBuilder, ServiceExt};
use detee_sgx::{prelude::*, RaTlsConfigBuilder};
// TODO: ratls config should be global
// TODO: error handling, shouldn't have expects
let mrsigner_hex = "83E8A0C3ED045D9747ADE06C3BFC70FCA661A4A65FF79A800223621162A88B76";
let mrsigner =
crate::sgx::mrsigner_from_hex(mrsigner_hex).expect("mrsigner decoding failed");
let config = RaTlsConfig::new()
.allow_instance_measurement(InstanceMeasurement::new().with_mrsigners(vec![mrsigner]));
let mut tls = ServerConfig::from_ratls_config(config)
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, format!("{}", e)))
.expect("failed to create server config");
let mut tls = ServerConfig::from_ratls_config(self.ratls_config.clone()).unwrap();
tls.alpn_protocols = vec![b"h2".to_vec()];
let state = self.state.clone();
let svc = Routes::new(UpdateServer::new(self)).prepare();
let http = Builder::new(TokioExecutor::new());
@ -53,10 +50,11 @@ impl MyServer {
let tls_acceptor = TlsAcceptor::from(Arc::new(tls));
loop {
let (conn, addr) = match listener.accept().await {
let (conn, _addr) = match listener.accept().await {
Ok(incoming) => incoming,
Err(e) => {
eprintln!("Error accepting connection: {}", e);
println!("Error accepting connection: {}", e);
state.increase_net_attacks();
continue;
}
};
@ -65,6 +63,7 @@ impl MyServer {
let tls_acceptor = tls_acceptor.clone();
let svc = svc.clone();
let state = state.clone();
tokio::spawn(async move {
let mut certificates = Vec::new();
@ -76,30 +75,29 @@ impl MyServer {
}
}
})
.await
.unwrap();
.await;
#[derive(Debug)]
pub struct ConnInfo {
pub addr: std::net::SocketAddr,
pub certificates: Vec<rustls::pki_types::CertificateDer<'static>>,
}
let conn = if let Err(e) = conn {
println!("Error accepting TLS connection: {}", e);
state.increase_net_attacks();
return;
} else {
conn.unwrap()
};
let extension_layer =
tower_http::add_extension::AddExtensionLayer::new(Arc::new(ConnInfo {
addr,
certificates,
}));
let svc = ServiceBuilder::new().layer(extension_layer).service(svc);
let svc = ServiceBuilder::new().service(svc);
http.serve_connection(
if let Err(e) = http
.serve_connection(
TokioIo::new(conn),
TowerToHyperService::new(
svc.map_request(|req: hyper::Request<_>| req.map(boxed)),
),
)
.await
.unwrap();
{
println!("Error serving connection: {}", e);
}
});
}
}
@ -109,16 +107,20 @@ impl MyServer {
impl Update for MyServer {
type GetUpdatesStream = Pin<Box<dyn Stream<Item = Result<NodeUpdate, Status>> + Send>>;
async fn get_keys(&self, _request: Request<Empty>) -> Result<Response<Keys>, Status> {
Ok(Response::new(self.keys.clone()))
}
async fn get_updates(
&self,
req: Request<Streaming<NodeUpdate>>,
) -> Result<Response<Self::GetUpdatesStream>, Status> {
self.ds.increase_mratls_conns();
self.state.increase_mratls_conns();
let remote_ip = req.remote_addr().unwrap().ip().to_string();
let tx = self.tx.clone();
let mut rx = self.tx.subscribe();
let mut inbound = req.into_inner();
let ds = self.ds.clone();
let ds = self.state.clone();
let stream = async_stream::stream! {
let full_update_list: Vec<NodeUpdate> = ds.get_node_list().into_iter().map(Into::<NodeUpdate>::into).collect();
@ -161,12 +163,4 @@ impl Update for MyServer {
Ok(Response::new(Box::pin(stream) as Self::GetUpdatesStream))
}
async fn get_keys(&self, _request: Request<Empty>) -> Result<Response<Keys>, Status> {
let reply = Keys {
keypair: self.ds.get_keypair_bytes(),
token_address: self.ds.get_token_address(),
};
Ok(Response::new(reply))
}
}

@ -1,5 +1,4 @@
#![allow(dead_code)]
use crate::{datastore, datastore::Store};
use crate::{datastore, datastore::State, solana::SolClient};
use actix_web::{get, post, web, App, HttpResponse, HttpServer, Responder};
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
@ -8,11 +7,11 @@ use std::sync::Arc;
const HOMEPAGE: &str = include_str!("HOMEPAGE.md");
#[get("/")]
async fn homepage(ds: web::Data<Arc<Store>>) -> impl Responder {
async fn homepage(sol_client: web::Data<Arc<SolClient>>) -> impl Responder {
let text = HOMEPAGE
.to_string()
.replace("TOKEN_ADDRESS", &ds.get_token_address())
.replace("MINT_AUTHORITY", &ds.get_pubkey_base58());
.replace("TOKEN_ADDRESS", &sol_client.get_token_address())
.replace("MINT_AUTHORITY", &sol_client.get_wallet_pubkey());
HttpResponse::Ok().body(text)
}
@ -40,7 +39,7 @@ impl From<(String, datastore::NodeInfo)> for NodesResp {
last_keepalive,
mints: node_info.mints,
total_ratls_conns: node_info.mratls_conns,
ratls_attacks: node_info.quote_attacks,
ratls_attacks: node_info.net_attacks,
public: node_info.public,
mint_requests: node_info.mint_requests,
}
@ -48,7 +47,7 @@ impl From<(String, datastore::NodeInfo)> for NodesResp {
}
#[get("/nodes")]
async fn get_nodes(ds: web::Data<Arc<Store>>) -> HttpResponse {
async fn get_nodes(ds: web::Data<Arc<State>>) -> HttpResponse {
HttpResponse::Ok().json(
ds.get_node_list().into_iter().map(Into::<NodesResp>::into).collect::<Vec<NodesResp>>(),
)
@ -60,22 +59,30 @@ struct MintReq {
}
#[post("/mint")]
async fn mint(ds: web::Data<Arc<Store>>, req: web::Json<MintReq>) -> impl Responder {
ds.increase_mint_requests();
async fn mint(
state: web::Data<Arc<State>>,
sol_client: web::Data<Arc<SolClient>>,
req: web::Json<MintReq>,
) -> impl Responder {
let recipient = req.into_inner().wallet;
state.increase_mint_requests();
let result =
web::block(move || ds.mint(&req.into_inner().wallet).map_err(|e| e.to_string()))
.await
.unwrap(); // TODO: check if this can get a BlockingError
web::block(move || sol_client.mint(&recipient).map_err(|e| e.to_string())).await.unwrap(); // TODO: check if this can get a BlockingError
match result {
Ok(s) => HttpResponse::Ok().body(format!(r#"{{" signature": "{s} "}}"#)),
Ok(s) => {
state.increase_mints();
HttpResponse::Ok().body(format!(r#"{{" signature": "{s} "}}"#))
}
Err(e) => HttpResponse::InternalServerError().body(format!(r#"{{ "error": "{e}" }}"#)),
}
}
pub async fn init(ds: Arc<Store>) {
pub async fn init(state: Arc<State>, sol_client: Arc<SolClient>) {
HttpServer::new(move || {
App::new()
.app_data(web::Data::new(ds.clone()))
.app_data(web::Data::new(state.clone()))
.app_data(web::Data::new(sol_client.clone()))
.service(homepage)
.service(get_nodes)
.service(mint)

@ -5,9 +5,11 @@ mod persistence;
mod sgx;
mod solana;
use crate::{datastore::LOCALHOST, grpc::challenge::NodeUpdate, solana::Client as SolClient};
use datastore::Store;
use solana_sdk::signer::Signer;
use crate::{
datastore::LOCALHOST, grpc::challenge::NodeUpdate, persistence::KeysFile, solana::SolClient,
};
use datastore::State;
use detee_sgx::{InstanceMeasurement, RaTlsConfig};
use std::{
fs::File,
io::{BufRead, BufReader},
@ -19,52 +21,61 @@ use tokio::{
time::{sleep, Duration},
};
const INIT_NODES: &str = "/host/detee_challenge_nodes";
const DISK_PERSISTENCE: &str = "/host/main/TRY_TO_HACK_THIS";
const MAINTAINED_CONNECTIONS: usize = 3;
const INIT_NODES_FILE: &str = "/host/detee_challenge_nodes";
const KEYS_FILE: &str = "/host/TRY_TO_HACK_THIS";
const MAX_CONNECTIONS: usize = 3;
pub async fn localhost_cron(ds: Arc<Store>, tx: Sender<NodeUpdate>) {
pub async fn localhost_cron(state: Arc<State>, tx: Sender<NodeUpdate>) {
loop {
sleep(Duration::from_secs(60)).await;
let _ = tx.send((LOCALHOST.to_string(), ds.get_localhost()).into());
ds.remove_inactive_nodes();
let _ = tx.send((LOCALHOST.to_string(), state.get_localhost()).into());
state.remove_inactive_nodes();
}
}
async fn get_sol_client() -> SolClient {
match crate::persistence::Data::read(DISK_PERSISTENCE).await {
Ok(data) => {
let (keypair, token) = data.parse();
println!("Found the following wallet saved to disk: {}", keypair.pubkey());
println!("Loading token mint address {}", token);
return SolClient::from(keypair, token);
async fn get_sol_client(state: Arc<State>, ratls_config: RaTlsConfig) -> SolClient {
match KeysFile::read(KEYS_FILE, &state).await {
Ok(keys_file) => {
let sol_client = SolClient::try_from(keys_file).unwrap();
println!(
"Found the following wallet saved to disk: {}",
sol_client.get_wallet_pubkey()
);
println!("Loading token mint address {}", sol_client.get_token_address());
return sol_client;
}
Err(e) => println!("Did not find old pubkeys saved to disk: {e}"),
Err(e) => println!("Can't initialize using sealed keys: {e}"),
};
let input = match File::open(INIT_NODES) {
Ok(i) => i,
let init_nodes = match File::open(INIT_NODES_FILE) {
Ok(init_nodes) => init_nodes,
Err(_) => {
println!("Could not find remote nodes in the file {INIT_NODES}");
println!("Can't initialize using init nodes from {INIT_NODES_FILE}");
println!("Starting a new network with a new key...");
return SolClient::new().await;
}
};
let buffered = BufReader::new(input);
for line in buffered.lines() {
match grpc::client::key_grabber(line.unwrap()).await {
Ok(bundle) => {
let init_nodes_reader = BufReader::new(init_nodes);
for init_node_ip in init_nodes_reader.lines().map(|l| l.unwrap()) {
match grpc::client::key_grabber(state.clone(), init_node_ip, ratls_config.clone()).await {
Ok(keys) => {
let sol_client = SolClient::try_from(keys.clone())
.map_err(|e| {
println!("Received malformed keys from the network: {e}");
state.increase_net_attacks();
})
.unwrap();
println!(
"Got keypair from the network. Joining the network using wallet {}",
bundle.0.pubkey()
sol_client.get_wallet_pubkey()
);
println!("The address of the Token is {}", bundle.1);
println!("Saving this data to disk in the file {DISK_PERSISTENCE}");
let disk_data = crate::persistence::Data::init_from(&bundle.0, &bundle.1).await;
if let Err(e) = disk_data.write(DISK_PERSISTENCE).await {
println!("Could not save data to disk due to: {e}");
println!("The address of the Token is {}", sol_client.get_token_address());
println!("Saving this data to disk in the file {KEYS_FILE}");
if let Err(e) = sol_client.get_keys_file().write(KEYS_FILE).await {
println!("Could not save data to disk: {e}");
}
return SolClient::from(bundle.0, bundle.1);
return sol_client;
}
Err(e) => {
println!("Could not get keypair: {e:?}");
@ -77,29 +88,42 @@ async fn get_sol_client() -> SolClient {
#[tokio::main]
async fn main() {
env_logger::init_from_env(env_logger::Env::default().default_filter_or("trace"));
let ratls_config = RaTlsConfig::new()
.allow_instance_measurement(InstanceMeasurement::new().with_current_mrenclave().unwrap());
let sol_client = get_sol_client().await;
let ds = Arc::new(Store::init(sol_client));
let state = Arc::new(State::new());
let sol_client = Arc::new(get_sol_client(state.clone(), ratls_config.clone()).await);
let (tx, _) = broadcast::channel(500);
let mut tasks = JoinSet::new();
tasks.spawn(localhost_cron(ds.clone(), tx.clone()));
tasks.spawn(http_server::init(ds.clone()));
tasks.spawn(grpc::server::MyServer::init(ds.clone(), tx.clone()).start());
tasks.spawn(localhost_cron(state.clone(), tx.clone()));
tasks.spawn(http_server::init(state.clone(), sol_client.clone()));
tasks.spawn(
grpc::server::MyServer::init(
state.clone(),
sol_client.get_keys(),
ratls_config.clone(),
tx.clone(),
)
.start(),
);
if let Ok(input) = std::fs::read_to_string(INIT_NODES) {
if let Ok(input) = std::fs::read_to_string(INIT_NODES_FILE) {
for line in input.lines() {
tasks.spawn(
grpc::client::ConnManager::init(ds.clone(), tx.clone())
grpc::client::ConnManager::init(state.clone(), ratls_config.clone(), tx.clone())
.start_with_node(line.to_string()),
);
}
}
for _ in 0..MAINTAINED_CONNECTIONS {
tasks.spawn(grpc::client::ConnManager::init(ds.clone(), tx.clone()).start());
for _ in 0..MAX_CONNECTIONS {
tasks.spawn(
grpc::client::ConnManager::init(state.clone(), ratls_config.clone(), tx.clone())
.start(),
);
}
while let Some(Ok(_)) = tasks.join_next().await {}

@ -1,43 +1,52 @@
use crate::{datastore::State, grpc::challenge::Keys};
use serde::{Deserialize, Serialize};
use solana_sdk::{pubkey::Pubkey, signature::keypair::Keypair};
use std::str::FromStr;
use serde_with::base64::Base64;
#[serde_with::serde_as]
#[derive(Serialize, Deserialize)]
pub struct Data {
pub struct KeysFile {
random: String,
keypair: String,
#[serde_as(as = "Base64")]
keypair: Vec<u8>,
token: String,
}
impl Data {
pub async fn init_from(keypair: &Keypair, token: &Pubkey) -> Self {
impl From<Keys> for KeysFile {
fn from(keys: Keys) -> Self {
use rand::{distributions::Alphanumeric, Rng};
let random_string: String =
let random: String =
rand::thread_rng().sample_iter(&Alphanumeric).take(128).map(char::from).collect();
Self {
random: random_string,
keypair: keypair.to_base58_string(),
token: token.to_string(),
Self { keypair: keys.keypair, token: keys.token_address, random }
}
}
impl Into<Keys> for KeysFile {
fn into(self) -> Keys {
Keys { keypair: self.keypair, token_address: self.token }
}
}
impl KeysFile {
pub async fn write(self, path: &str) -> Result<(), Box<dyn std::error::Error>> {
let serialized = serde_json::to_string(&self)?;
let sealed = detee_sgx::SealingConfig::new()?.seal_data(serialized.into_bytes())?;
tokio::fs::write(path, sealed).await.map_err(Into::into)
}
pub async fn read(path: &str) -> Result<Self, Box<dyn std::error::Error>> {
pub async fn read(path: &str, state: &State) -> Result<Self, Box<dyn std::error::Error>> {
let sealed = tokio::fs::read(path).await?;
let serialized = detee_sgx::SealingConfig::new()?.un_seal_data(sealed)?;
Ok(serde_json::from_str(&String::from_utf8(serialized)?)?)
let serialized = detee_sgx::SealingConfig::new()?.un_seal_data(sealed).map_err(|e| {
match e {
detee_sgx::SgxError::UnSealingError(ref ue) => {
state.increase_disk_attacks();
println!("The disk data is corrupted: {ue}");
}
pub fn parse(self) -> (Keypair, Pubkey) {
let keypair = Keypair::from_base58_string(&self.keypair);
let pubkey = Pubkey::from_str(&self.token).unwrap();
(keypair, pubkey)
_ => println!("Failed to unseal data: {e}"),
};
e
})?;
Ok(serde_json::from_str(&String::from_utf8(serialized)?)?)
}
}
@ -48,9 +57,7 @@ pub struct Logfile {}
impl Logfile {
pub fn append(msg: &str) -> Result<(), Box<dyn std::error::Error>> {
use std::io::Write;
let mut file = std::fs::OpenOptions::new()
.append(true)
.open(LOG_PATH)?;
let mut file = std::fs::OpenOptions::new().append(true).open(LOG_PATH)?;
file.write_all(msg.as_bytes())?;
Ok(())
}

@ -1,4 +1,5 @@
#![allow(dead_code)]
use crate::{grpc::challenge::Keys, persistence::KeysFile};
use solana_client::rpc_client::RpcClient;
use solana_program::program_pack::Pack;
use solana_sdk::{
@ -12,18 +13,17 @@ use spl_token::{
instruction::{initialize_mint, mint_to},
state::Mint,
};
use std::error::Error;
use tokio::time::{sleep, Duration};
const RPC_URL: &str = "https://api.devnet.solana.com";
pub struct Client {
pub struct SolClient {
client: RpcClient,
keypair: Keypair,
token: Pubkey,
}
impl Client {
impl SolClient {
pub async fn new() -> Self {
let client = RpcClient::new(RPC_URL);
let keypair = Keypair::new();
@ -31,12 +31,18 @@ impl Client {
Self { client, keypair, token }
}
pub fn from(keypair: Keypair, token: Pubkey) -> Self {
Self { client: RpcClient::new(RPC_URL), keypair, token }
pub fn get_keys(&self) -> Keys {
Keys { keypair: self.get_keypair_bytes(), token_address: self.get_token_address() }
}
pub fn mint(&self, recipient: &Pubkey) -> Result<String, Box<dyn std::error::Error>> {
let associated_token_address = self.create_token_account(recipient)?;
pub fn get_keys_file(&self) -> KeysFile {
self.get_keys().into()
}
pub fn mint(&self, recipient: &str) -> Result<String, Box<dyn std::error::Error>> {
use std::str::FromStr;
let recipient = solana_sdk::pubkey::Pubkey::from_str(recipient)?;
let associated_token_address = self.create_token_account(&recipient)?;
let mint_to_instruction = mint_to(
&spl_token::id(),
&self.token,
@ -56,7 +62,10 @@ impl Client {
Ok(signature.to_string())
}
fn create_token_account(&self, recipient: &Pubkey) -> Result<Pubkey, Box<dyn Error>> {
fn create_token_account(
&self,
recipient: &Pubkey,
) -> Result<Pubkey, Box<dyn std::error::Error>> {
let address = get_associated_token_address(recipient, &self.token);
if self.client.get_account(&address).is_err() {
let create_token_account_instruction = create_associated_token_account(
@ -77,11 +86,12 @@ impl Client {
Ok(address)
}
pub fn wallet_address(&self) -> String {
pub fn get_wallet_pubkey(&self) -> String {
// Return the base58 string representation of the public key
self.keypair.pubkey().to_string()
}
pub fn token_address(&self) -> String {
pub fn get_token_address(&self) -> String {
self.token.to_string()
}
@ -90,6 +100,30 @@ impl Client {
}
}
impl TryFrom<Keys> for SolClient {
type Error = String;
fn try_from(keys: Keys) -> Result<Self, Self::Error> {
use std::str::FromStr;
let keypair = match Keypair::from_bytes(&keys.keypair) {
Ok(k) => k,
Err(_) => return Err("Could not parse keypair.".into()),
};
let token = Pubkey::from_str(&keys.token_address)
.map_err(|_| "Could not parse wallet address.".to_string())?;
Ok(Self { client: RpcClient::new(RPC_URL), keypair, token })
}
}
impl TryFrom<KeysFile> for SolClient {
type Error = String;
fn try_from(keys_file: KeysFile) -> Result<Self, Self::Error> {
let keys: Keys = keys_file.into();
Self::try_from(keys)
}
}
async fn wait_for_sol(client: &RpcClient, pubkey: &Pubkey) {
println!("Waiting to receive 0.01 SOL in address {pubkey}");
loop {