mirror of
https://github.com/creating2morrow/neveko.git
synced 2025-03-21 06:38:49 +00:00
366 lines
12 KiB
Rust
366 lines
12 KiB
Rust
use rand_core::RngCore;
|
|
use clap::Parser;
|
|
use rocket::serde::json::Json;
|
|
use crate::{args, db, i2p, message, models, monero, gpg, utils, reqres};
|
|
use log::{info, debug, error, warn};
|
|
use std::time::Duration;
|
|
|
|
/// Handles the state for the connection manager popup
|
|
pub struct Connections {
|
|
pub blockchain_dir: String,
|
|
pub daemon_host: String,
|
|
pub i2p_zero_dir: String,
|
|
pub mainnet: bool,
|
|
pub monero_location: String,
|
|
pub rpc_credential: String,
|
|
pub rpc_username: String,
|
|
pub rpc_host: String,
|
|
}
|
|
|
|
impl Default for Connections {
|
|
fn default() -> Self {
|
|
Connections {
|
|
blockchain_dir: String::from("/home/user/.bitmonero"),
|
|
daemon_host: String::from("http://localhost:38081"),
|
|
i2p_zero_dir: String::from("/home/user/i2p-zero-linux.v1.20"),
|
|
mainnet: false,
|
|
monero_location: String::from("/home/user/monero-x86_64-linux-gnu-v0.18.2.2"),
|
|
rpc_credential: String::from("pass"),
|
|
rpc_username: String::from("user"),
|
|
rpc_host: String::from("http://localhost:38083"),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub enum ApplicationErrors {
|
|
LoginError,
|
|
UnknownError,
|
|
}
|
|
|
|
impl ApplicationErrors {
|
|
pub fn value(&self) -> String {
|
|
match *self {
|
|
ApplicationErrors::LoginError => String::from("LoginError"),
|
|
ApplicationErrors::UnknownError => String::from("UnknownError"),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, PartialEq)]
|
|
pub enum ReleaseEnvironment {
|
|
Development,
|
|
Production,
|
|
}
|
|
|
|
impl ReleaseEnvironment {
|
|
pub fn value(&self) -> String {
|
|
match *self {
|
|
ReleaseEnvironment::Development => String::from("development"),
|
|
ReleaseEnvironment::Production => String::from("production"),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// start core module from gui
|
|
pub fn start_core(conn: &Connections) {
|
|
let env = if !conn.mainnet { "dev" } else { "prod" };
|
|
let args = [
|
|
"--monero-location", &conn.monero_location,
|
|
"--monero-blockchain-dir", &conn.blockchain_dir,
|
|
"--monero-rpc-host", &conn.rpc_host,
|
|
"--monero-rpc-daemon", &conn.daemon_host,
|
|
"--monero-rpc-username", &conn.rpc_username,
|
|
"--monero-rpc-cred", &conn.rpc_credential,
|
|
"--i2p-zero-dir", &conn.i2p_zero_dir,
|
|
"-r", env,
|
|
];
|
|
let path = if conn.mainnet { "nevmes" } else { "../target/debug/nevmes" };
|
|
let output = std::process::Command::new(path)
|
|
.args(args)
|
|
.spawn()
|
|
.expect("core module failed to start");
|
|
debug!("{:?}", output.stdout);
|
|
}
|
|
|
|
/// Using remote node?
|
|
pub fn is_using_remote_node() -> bool {
|
|
let args = args::Args::parse();
|
|
let r = args.remote_node;
|
|
if r { warn!("using a remote node may harm privacy"); }
|
|
r
|
|
}
|
|
|
|
/// Random data generation for auth / primary keys
|
|
pub fn generate_rnd() -> String {
|
|
let mut data = [0u8; 32];
|
|
rand::thread_rng().fill_bytes(&mut data);
|
|
hex::encode(data)
|
|
}
|
|
|
|
/// Helper for separation of dev and prod concerns
|
|
pub fn get_release_env() -> ReleaseEnvironment {
|
|
let args = args::Args::parse();
|
|
let env = String::from(args.release_env);
|
|
if env == "prod" {
|
|
return ReleaseEnvironment::Production;
|
|
} else {
|
|
return ReleaseEnvironment::Development;
|
|
}
|
|
}
|
|
|
|
/// app port
|
|
pub fn get_app_port() -> u16 {
|
|
let args = args::Args::parse();
|
|
args.port
|
|
}
|
|
|
|
/// i2p http proxy
|
|
pub fn get_i2p_http_proxy() -> String {
|
|
let args = args::Args::parse();
|
|
args.i2p_proxy_host
|
|
}
|
|
|
|
/// app auth port
|
|
pub fn get_app_auth_port() -> u16 {
|
|
let args = args::Args::parse();
|
|
args.auth_port
|
|
}
|
|
|
|
/// app contact port
|
|
pub fn get_app_contact_port() -> u16 {
|
|
let args = args::Args::parse();
|
|
args.contact_port
|
|
}
|
|
|
|
/// app message port
|
|
pub fn get_app_message_port() -> u16 {
|
|
let args = args::Args::parse();
|
|
args.message_port
|
|
}
|
|
|
|
/// jwp confirmation limit
|
|
pub fn get_conf_threshold() -> u64 {
|
|
let args = args::Args::parse();
|
|
args.confirmation_threshold
|
|
}
|
|
|
|
/// jwp confirmation limit
|
|
pub fn get_payment_threshold() -> u128 {
|
|
let args = args::Args::parse();
|
|
args.payment_threshold
|
|
}
|
|
|
|
/// convert contact to json so only core module does the work
|
|
pub fn contact_to_json(c: &models::Contact) -> Json<models::Contact> {
|
|
let r_contact: models::Contact = models::Contact {
|
|
cid: String::from(&c.cid),
|
|
i2p_address: String::from(&c.i2p_address),
|
|
xmr_address: String::from(&c.xmr_address),
|
|
gpg_key: c.gpg_key.iter().cloned().collect(),
|
|
};
|
|
Json(r_contact)
|
|
}
|
|
|
|
/// convert message to json so only core module does the work
|
|
pub fn message_to_json(m: &models::Message) -> Json<models::Message> {
|
|
let r_message: models::Message = models::Message {
|
|
body: m.body.iter().cloned().collect(),
|
|
mid: String::from(&m.mid),
|
|
uid: utils::empty_string(),
|
|
created: m.created,
|
|
from: String::from(&m.from),
|
|
to: String::from(&m.to),
|
|
};
|
|
Json(r_message)
|
|
}
|
|
|
|
/// Instead of putting `String::from("")`
|
|
pub fn empty_string() -> String { String::from("") }
|
|
|
|
// DoS prevention
|
|
pub const fn string_limit() -> usize { 512 }
|
|
pub const fn gpg_key_limit() -> usize { 4096 }
|
|
pub const fn message_limit() -> usize { 9999 }
|
|
|
|
/// Generate application gpg keys at startup if none exist
|
|
async fn gen_app_gpg() {
|
|
let mut gpg_key = gpg::find_key().unwrap_or(utils::empty_string());
|
|
if gpg_key == utils::empty_string() {
|
|
info!("no gpg key found for nevmes, creating it...");
|
|
// wait for key gen
|
|
gpg::write_gen_batch().unwrap();
|
|
gpg::gen_key();
|
|
tokio::time::sleep(Duration::new(9, 0)).await;
|
|
gpg_key = gpg::find_key().unwrap_or(utils::empty_string());
|
|
}
|
|
debug!("gpg key: {}", gpg_key);
|
|
}
|
|
|
|
/// Generate application wallet at startup if none exist
|
|
async fn gen_app_wallet() {
|
|
info!("fetching application wallet");
|
|
let filename = "nevmes";
|
|
let mut m_wallet = monero::open_wallet(String::from(filename)).await;
|
|
if !m_wallet {
|
|
m_wallet = monero::create_wallet(String::from(filename)).await;
|
|
if !m_wallet {
|
|
error!("failed to create wallet")
|
|
} else {
|
|
m_wallet = monero::open_wallet(String::from(filename)).await;
|
|
if m_wallet {
|
|
let m_address: reqres::XmrRpcAddressResponse =
|
|
monero::get_address().await;
|
|
info!("app wallet address: {}", m_address.result.address)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Secret keys for signing internal/external auth tokens
|
|
fn gen_signing_keys() {
|
|
info!("generating signing keys");
|
|
let jwp = get_jwp_secret_key();
|
|
let jwt = get_jwt_secret_key();
|
|
// send to db
|
|
let s = db::Interface::open();
|
|
if jwp == utils::empty_string() {
|
|
let rnd_jwp = generate_rnd();
|
|
db::Interface::write(&s.env, &s.handle, crate::NEVMES_JWP_SECRET_KEY, &rnd_jwp);
|
|
}
|
|
if jwt == utils::empty_string() {
|
|
let rnd_jwt = generate_rnd();
|
|
db::Interface::write(&s.env, &s.handle, crate::NEVMES_JWT_SECRET_KEY, &rnd_jwt);
|
|
}
|
|
}
|
|
|
|
/// TODO(c2m): add a button to gui to call this
|
|
///
|
|
/// dont' forget to generate new keys as well
|
|
pub fn revoke_signing_keys() {
|
|
let s = db::Interface::open();
|
|
db::Interface::delete(&s.env, &s.handle, crate::NEVMES_JWT_SECRET_KEY);
|
|
db::Interface::delete(&s.env, &s.handle, crate::NEVMES_JWP_SECRET_KEY);
|
|
}
|
|
|
|
pub fn get_jwt_secret_key() -> String {
|
|
let s = db::Interface::open();
|
|
let r = db::Interface::read(&s.env, &s.handle, crate::NEVMES_JWT_SECRET_KEY);
|
|
if r == utils::empty_string() {
|
|
error!("JWT key not found");
|
|
return Default::default()
|
|
}
|
|
r
|
|
}
|
|
|
|
pub fn get_jwp_secret_key() -> String {
|
|
let s = db::Interface::open();
|
|
let r = db::Interface::read(&s.env, &s.handle, crate::NEVMES_JWP_SECRET_KEY);
|
|
if r == utils::empty_string() {
|
|
error!("JWP key not found");
|
|
return Default::default()
|
|
}
|
|
r
|
|
}
|
|
|
|
/// Start the remote access microservers `--remote-access` flag
|
|
fn start_micro_servers() {
|
|
info!("starting auth server");
|
|
let mut auth_path = "nevmes-auth/target/debug/nevmes_auth";
|
|
let env = get_release_env();
|
|
if env == ReleaseEnvironment::Production { auth_path = "nevmes_auth"; }
|
|
let a_output = std::process::Command::new(auth_path)
|
|
.spawn().expect("failed to start auth server");
|
|
debug!("{:?}", a_output.stdout);
|
|
info!("starting contact server");
|
|
let mut contact_path = "nevmes-contact/target/debug/nevmes_contact";
|
|
if env == ReleaseEnvironment::Production { contact_path = "nevmes_contact"; }
|
|
let c_output = std::process::Command::new(contact_path)
|
|
.spawn().expect("failed to start contact server");
|
|
debug!("{:?}", c_output.stdout);
|
|
info!("starting message server");
|
|
let mut message_path = "nevmes-message/target/debug/nevmes_message";
|
|
if env == ReleaseEnvironment::Production { message_path = "nevmes_message"; }
|
|
let m_output = std::process::Command::new(message_path)
|
|
.spawn().expect("failed to start message server");
|
|
debug!("{:?}", m_output.stdout);
|
|
}
|
|
|
|
/// open gui from i2m core launch
|
|
fn start_gui() {
|
|
let args = args::Args::parse();
|
|
if args.gui {
|
|
info!("starting gui");
|
|
let mut gui_path = "nevmes-gui/target/debug/nevmes_gui";
|
|
let env = get_release_env();
|
|
if env == ReleaseEnvironment::Production { gui_path = "nevmes-gui"; }
|
|
let g_output = std::process::Command::new(gui_path)
|
|
.spawn().expect("failed to start gui");
|
|
debug!("{:?}", g_output.stdout);
|
|
}
|
|
}
|
|
|
|
/// Put all app pre-checks here
|
|
pub async fn start_up() {
|
|
info!("nevmes is starting up");
|
|
let args = args::Args::parse();
|
|
if args.remote_access { start_micro_servers(); }
|
|
gen_signing_keys();
|
|
if !is_using_remote_node() { monero::start_daemon(); }
|
|
// wait for daemon for a bit
|
|
tokio::time::sleep(std::time::Duration::new(5, 0)).await;
|
|
monero::start_rpc();
|
|
// wait for rpc server for a bit
|
|
tokio::time::sleep(std::time::Duration::new(5, 0)).await;
|
|
monero::check_rpc_connection().await;
|
|
gen_app_wallet().await;
|
|
i2p::start().await;
|
|
gen_app_gpg().await;
|
|
let env: String = get_release_env().value();
|
|
start_gui();
|
|
{ tokio::spawn(async { message::retry_fts().await; }); }
|
|
info!("{} - nevmes is online", env);
|
|
}
|
|
|
|
/// Called by gui for cleaning up monerod, rpc, etc.
|
|
///
|
|
/// pass true from gui connection manager so not to kill nevmes
|
|
pub fn kill_child_processes(cm: bool) {
|
|
info!("stopping child processes");
|
|
// TODO(c2m): prompt on gui letting user determine what background
|
|
// services to keep running
|
|
if cm {
|
|
let xmrd_output = std::process::Command::new("pkill")
|
|
.arg("monerod")
|
|
.spawn()
|
|
.expect("monerod failed to stop");
|
|
debug!("{:?}", xmrd_output.stdout);
|
|
let rpc_output = std::process::Command::new("killall")
|
|
.arg("monero-wallet-rpc")
|
|
.spawn()
|
|
.expect("monero-wallet-rpc failed to stop");
|
|
debug!("{:?}", rpc_output.stdout);
|
|
}
|
|
if !cm {
|
|
let nevmes_output = std::process::Command::new("pkill")
|
|
.arg("nevmes")
|
|
.spawn()
|
|
.expect("nevmes failed to stop");
|
|
debug!("{:?}", nevmes_output.stdout);
|
|
}
|
|
let i2pz_output = std::process::Command::new("pkill")
|
|
.arg("i2p-zero")
|
|
.spawn()
|
|
.expect("i2p-zero failed to stop");
|
|
debug!("{:?}", i2pz_output.stdout);
|
|
}
|
|
|
|
/// Move temp files to /tmp
|
|
pub fn stage_cleanup(f: String) {
|
|
info!("staging {} for cleanup", &f);
|
|
let output = std::process::Command::new("mv")
|
|
.args([&f, "/tmp"])
|
|
.spawn()
|
|
.expect("cleanup staging failed");
|
|
debug!("{:?}", output.stdout);
|
|
}
|