add rustfmt and fmtall.sh

This commit is contained in:
creating2morrow 2023-05-09 17:28:07 -04:00
parent 438b22838e
commit e9ef5778d1
38 changed files with 1158 additions and 585 deletions

6
.rustfmt.toml Normal file
View file

@ -0,0 +1,6 @@
# cargo +nightly fmt
comment_width = 100
format_code_in_doc_comments = true
imports_granularity = "Crate"
imports_layout = "Vertical"
wrap_comments = true

View file

@ -20,11 +20,22 @@ NEVidebla-MESago (invisible message)
* install dependencies * install dependencies
* ubuntu example: `sudo apt update -y && sudo apt upgrade -y` * ubuntu example: `sudo apt update -y && sudo apt upgrade -y`
* `sudo apt install -y libssl-dev build-essential libgpgme-dev` * `sudo apt install -y libssl-dev build-essential libgpgme-dev`
* download and run i2prouter start (optional: setup to run on boot similar tor daemon)
* `git clone https://github/com/creating2morrow/nevmes` * `git clone https://github/com/creating2morrow/nevmes`
* `cd nevmes && ./scripts/build_all_and_run.sh "-- -h"` * `cd nevmes && ./scripts/build_all_and_run.sh "-- -h"`
* gui built with rust [egui](https://docs.rs/egui/latest/egui/) * gui built with rust [egui](https://docs.rs/egui/latest/egui/)
## Installation Mananger
* additional required software can be downloaded from the gui home or `Binaries` links below
* hashes are in core [lib.rs](./nevmes-core/src/lib.rs)
* download and run i2p
* `/i2p/i2prouter start`
* `/i2p/i2prouter install` (optional: setup to run on boot similar to tor daemon)
* download i2p-zero, put the path in the connection manager or cli `--i2p-zero-dir` flag
* download monero, update connection manager or cli
* `--monero-blockchain-dir`, where to put lmdb for monero (e.g. path/to/ssd)
* `--monero-location`, path to monero download
## Contributing and Releasing ## Contributing and Releasing
```bash ```bash
@ -36,7 +47,10 @@ NEVidebla-MESago (invisible message)
``` ```
* code on dev branch * code on dev branch
* run `./scripts/fmtall.sh` before committing
* pull request to dev branch * pull request to dev branch
* todo => `TODO(name): detailed work`
* docs on all `pub fn` and `pub struct`
* merge dev to vX.X.X * merge dev to vX.X.X
* merge vX.X.X to main * merge vX.X.X to main
* tag release v.X.X.X every friday (if stable changes) * tag release v.X.X.X every friday (if stable changes)
@ -63,7 +77,6 @@ NEVidebla-MESago (invisible message)
* nevmes-auth - `internal` auth server * nevmes-auth - `internal` auth server
* nevmes-contact - `internal` add contacts server * nevmes-contact - `internal` add contacts server
* nevmes-core - application core logic
* nevmes-gui - primary user interface * nevmes-gui - primary user interface
* nevmes-message - `internal` message tx/read etc. server * nevmes-message - `internal` message tx/read etc. server
* nevmes - `external` primary server for contact share, payment, message rx etc. * nevmes - `external` primary server for contact share, payment, message rx etc.
@ -74,16 +87,8 @@ NEVidebla-MESago (invisible message)
* [i2p-zero](https://github.com/i2p-zero/i2p-zero/releases/tag/v1.20) - (not included) tunnel creation * [i2p-zero](https://github.com/i2p-zero/i2p-zero/releases/tag/v1.20) - (not included) tunnel creation
* [i2p](https://geti2p.net/en/download) - http proxy (not included, *i2p-zero http proxy not working) * [i2p](https://geti2p.net/en/download) - http proxy (not included, *i2p-zero http proxy not working)
most of the complex logic stays in nevmes-core, exported from [lib.rs](./nevmes-core/src/lib.rs)
## Manual ## Manual
[the manual](./docs/man.md) [the manual](./docs/man.md)
## Known issues
* gui password and screen lock needs fixing up
* timeout out JWP payment approval screen with infinite loading
* prove payment edge where payment succeeds but jwp is empty, currently require new payment
* test framework (in progress)
* docs on all `fn` and `structs`
* i2pd installer on home screen?
* and more daemon info and wallet functionality (multisig)

View file

@ -0,0 +1,6 @@
# cargo +nightly fmt
comment_width = 100
format_code_in_doc_comments = true
imports_granularity = "Crate"
imports_layout = "Vertical"
wrap_comments = true

View file

@ -1,17 +1,20 @@
use rocket::http::Status; use rocket::{
use rocket::response::status::Custom; get,
use rocket::serde::json::Json; http::Status,
use rocket::get; response::status::Custom,
serde::json::Json,
};
use nevmes_core::{auth, models::*}; use nevmes_core::{
auth,
models::*,
};
/// Login with wallet signature /// Login with wallet signature
/// ///
/// Creates user on initial login /// Creates user on initial login
///
#[get("/login/<signature>/<aid>/<uid>")] #[get("/login/<signature>/<aid>/<uid>")]
pub async fn login pub async fn login(aid: String, uid: String, signature: String) -> Custom<Json<Authorization>> {
(aid: String, uid: String,signature: String) -> Custom<Json<Authorization>> {
let m_auth: Authorization = auth::verify_login(aid, uid, signature).await; let m_auth: Authorization = auth::verify_login(aid, uid, signature).await;
Custom(Status::Created, Json(m_auth)) Custom(Status::Created, Json(m_auth))
} }

View file

@ -14,6 +14,5 @@ async fn rocket() -> _ {
}; };
env_logger::init(); env_logger::init();
log::info!("nevmes-auth is online"); log::info!("nevmes-auth is online");
rocket::custom(&config) rocket::custom(&config).mount("/", routes![controller::login])
.mount("/", routes![controller::login])
} }

View file

@ -0,0 +1,6 @@
# cargo +nightly fmt
comment_width = 100
format_code_in_doc_comments = true
imports_granularity = "Crate"
imports_layout = "Vertical"
wrap_comments = true

View file

@ -1,41 +1,53 @@
use rocket::http::Status; use rocket::{
use rocket::response::status::Custom; get,
use rocket::serde::json::Json; http::Status,
use rocket::{get, post}; post,
response::status::Custom,
serde::json::Json,
};
use nevmes_core::{auth, contact, models::*, proof, utils, reqres}; use nevmes_core::{
auth,
contact,
models::*,
proof,
reqres,
utils,
};
/// Add contact /// Add contact
#[post("/", data="<req_contact>")] #[post("/", data = "<req_contact>")]
pub async fn add_contact pub async fn add_contact(
(req_contact: Json<Contact>,_token: auth::BearerToken) -> Custom<Json<Contact>> { req_contact: Json<Contact>,
_token: auth::BearerToken,
) -> Custom<Json<Contact>> {
let res_contact = contact::create(&req_contact).await; let res_contact = contact::create(&req_contact).await;
if res_contact.cid == utils::empty_string() { if res_contact.cid == utils::empty_string() {
return Custom(Status::BadRequest, Json(Default::default())) return Custom(Status::BadRequest, Json(Default::default()));
} }
Custom(Status::Ok, Json(res_contact)) Custom(Status::Ok, Json(res_contact))
} }
/// Return all contacts /// Return all contacts
#[get("/")] #[get("/")]
pub async fn get_contacts pub async fn get_contacts(_token: auth::BearerToken) -> Custom<Json<Vec<Contact>>> {
(_token: auth::BearerToken) -> Custom<Json<Vec<Contact>>> {
let contacts = contact::find_all(); let contacts = contact::find_all();
Custom(Status::Ok, Json(contacts)) Custom(Status::Ok, Json(contacts))
} }
/// trust contact /// trust contact
#[post("/<key>")] #[post("/<key>")]
pub async fn trust_contact pub async fn trust_contact(key: String, _token: auth::BearerToken) -> Status {
(key: String, _token: auth::BearerToken) -> Status {
contact::trust_gpg(key); contact::trust_gpg(key);
Status::Ok Status::Ok
} }
/// prove payment /// prove payment
#[get("/<contact>", data="<proof_req>")] #[get("/<contact>", data = "<proof_req>")]
pub async fn prove_payment pub async fn prove_payment(
(contact: String, proof_req: Json<proof::TxProof>, _token: auth::BearerToken contact: String,
proof_req: Json<proof::TxProof>,
_token: auth::BearerToken,
) -> Custom<Json<reqres::Jwp>> { ) -> Custom<Json<reqres::Jwp>> {
let r_jwp = proof::prove_payment(contact, &proof_req).await; let r_jwp = proof::prove_payment(contact, &proof_req).await;
Custom(Status::Ok, Json(r_jwp.unwrap())) Custom(Status::Ok, Json(r_jwp.unwrap()))

View file

@ -1,13 +1,31 @@
use crate::{args, models::*, db, monero, reqres, user, utils}; use crate::{
args,
db,
models::*,
monero,
reqres,
user,
utils,
};
use clap::Parser; use clap::Parser;
use log::{debug, error, info}; use log::{
debug,
error,
info,
};
use rocket::http::Status; use rocket::{
use rocket::outcome::Outcome; http::Status,
use rocket::request::FromRequest; outcome::Outcome,
use rocket::{request, Request}; request,
request::FromRequest,
Request,
};
use hmac::{Hmac, Mac}; use hmac::{
Hmac,
Mac,
};
use jwt::*; use jwt::*;
use sha2::Sha384; use sha2::Sha384;
use std::collections::BTreeMap; use std::collections::BTreeMap;
@ -70,8 +88,7 @@ fn update_expiration(f_auth: Authorization, address: &String) -> Authorization {
} }
/// Performs the signature verfication against stored auth /// Performs the signature verfication against stored auth
pub async fn verify_login pub async fn verify_login(aid: String, uid: String, signature: String) -> Authorization {
(aid: String, uid: String, signature: String) -> Authorization {
let m_address: reqres::XmrRpcAddressResponse = monero::get_address().await; let m_address: reqres::XmrRpcAddressResponse = monero::get_address().await;
let address = m_address.result.address; let address = m_address.result.address;
let f_auth: Authorization = find(&aid); let f_auth: Authorization = find(&aid);
@ -94,16 +111,23 @@ pub async fn verify_login
let u_auth = Authorization::update_uid(f_auth, String::from(&u.uid)); let u_auth = Authorization::update_uid(f_auth, String::from(&u.uid));
let s = db::Interface::open(); let s = db::Interface::open();
db::Interface::delete(&s.env, &s.handle, &u_auth.aid); db::Interface::delete(&s.env, &s.handle, &u_auth.aid);
db::Interface::write(&s.env, &s.handle, &u_auth.aid, &Authorization::to_db(&u_auth)); db::Interface::write(
return u_auth &s.env,
&s.handle,
&u_auth.aid,
&Authorization::to_db(&u_auth),
);
return u_auth;
} else if f_user.xmr_address != utils::empty_string() { } else if f_user.xmr_address != utils::empty_string() {
info!("returning user"); info!("returning user");
let m_access = verify_access(&address, &signature).await; let m_access = verify_access(&address, &signature).await;
if !m_access { return Default::default() } if !m_access {
return Default::default();
}
return f_auth; return f_auth;
} else { } else {
error!("error creating user"); error!("error creating user");
return Default::default() return Default::default();
} }
} }
@ -172,7 +196,7 @@ impl<'r> FromRequest<'r> for BearerToken {
let env = utils::get_release_env(); let env = utils::get_release_env();
let dev = utils::ReleaseEnvironment::Development; let dev = utils::ReleaseEnvironment::Development;
if env == dev { if env == dev {
return Outcome::Success(BearerToken(utils::empty_string())) return Outcome::Success(BearerToken(utils::empty_string()));
} }
let token = request.headers().get_one("token"); let token = request.headers().get_one("token");
let m_address: reqres::XmrRpcAddressResponse = monero::get_address().await; let m_address: reqres::XmrRpcAddressResponse = monero::get_address().await;

View file

@ -1,7 +1,19 @@
// Contact repo/service layer // Contact repo/service layer
use crate::{db, gpg, models::*, utils, reqres, monero, i2p}; use crate::{
db,
gpg,
i2p,
models::*,
monero,
reqres,
utils,
};
use log::{
debug,
error,
info,
};
use rocket::serde::json::Json; use rocket::serde::json::Json;
use log::{debug, error, info};
use std::error::Error; use std::error::Error;
/// Create a new contact /// Create a new contact
@ -42,7 +54,7 @@ pub fn find(cid: &String) -> Contact {
let r = db::Interface::read(&s.env, &s.handle, &String::from(cid)); let r = db::Interface::read(&s.env, &s.handle, &String::from(cid));
if r == utils::empty_string() { if r == utils::empty_string() {
error!("contact not found"); error!("contact not found");
return Default::default() return Default::default();
} }
Contact::from_db(String::from(cid), r) Contact::from_db(String::from(cid), r)
} }
@ -54,7 +66,7 @@ pub fn find_all() -> Vec<Contact> {
let r = db::Interface::read(&s.env, &s.handle, &String::from(list_key)); let r = db::Interface::read(&s.env, &s.handle, &String::from(list_key));
if r == utils::empty_string() { if r == utils::empty_string() {
error!("contact index not found"); error!("contact index not found");
return Default::default() return Default::default();
} }
let v_cid = r.split(","); let v_cid = r.split(",");
let v: Vec<String> = v_cid.map(|s| String::from(s)).collect(); let v: Vec<String> = v_cid.map(|s| String::from(s)).collect();
@ -84,13 +96,20 @@ pub async fn share() -> Contact {
let gpg_key = gpg::export_key().unwrap_or(Vec::new()); let gpg_key = gpg::export_key().unwrap_or(Vec::new());
let i2p_address = i2p::get_destination(); let i2p_address = i2p::get_destination();
let xmr_address = m_address.result.address; let xmr_address = m_address.result.address;
Contact { cid: utils::empty_string(),gpg_key,i2p_address,xmr_address } Contact {
cid: utils::empty_string(),
gpg_key,
i2p_address,
xmr_address,
}
} }
pub fn exists(from: &String) -> bool { pub fn exists(from: &String) -> bool {
let all = find_all(); let all = find_all();
let mut addresses: Vec<String> = Vec::new(); let mut addresses: Vec<String> = Vec::new();
for c in all { addresses.push(c.i2p_address); } for c in all {
addresses.push(c.i2p_address);
}
return addresses.contains(from); return addresses.contains(from);
} }
@ -103,7 +122,9 @@ pub fn exists(from: &String) -> bool {
/// using the app already have some level of knowledge about each other. /// using the app already have some level of knowledge about each other.
/// ///
/// Without signing the key message encryption and sending is not possible. /// Without signing the key message encryption and sending is not possible.
pub fn trust_gpg(key: String) { gpg::sign_key(&key).unwrap(); } pub fn trust_gpg(key: String) {
gpg::sign_key(&key).unwrap();
}
/// Get invoice for jwp creation /// Get invoice for jwp creation
pub async fn request_invoice(contact: String) -> Result<reqres::Invoice, Box<dyn Error>> { pub async fn request_invoice(contact: String) -> Result<reqres::Invoice, Box<dyn Error>> {
@ -111,14 +132,16 @@ pub async fn request_invoice(contact: String) -> Result<reqres::Invoice, Box<dyn
let host = utils::get_i2p_http_proxy(); let host = utils::get_i2p_http_proxy();
let proxy = reqwest::Proxy::http(&host)?; let proxy = reqwest::Proxy::http(&host)?;
let client = reqwest::Client::builder().proxy(proxy).build(); let client = reqwest::Client::builder().proxy(proxy).build();
match client?.get(format!("http://{}/invoice", contact)).send().await { match client?
.get(format!("http://{}/invoice", contact))
.send()
.await
{
Ok(response) => { Ok(response) => {
let res = response.json::<reqres::Invoice>().await; let res = response.json::<reqres::Invoice>().await;
debug!("invoice request response: {:?}", res); debug!("invoice request response: {:?}", res);
match res { match res {
Ok(r) => { Ok(r) => Ok(r),
Ok(r)
},
_ => Ok(Default::default()), _ => Ok(Default::default()),
} }
} }
@ -135,14 +158,16 @@ pub async fn add_contact_request(contact: String) -> Result<Contact, Box<dyn Err
let host = utils::get_i2p_http_proxy(); let host = utils::get_i2p_http_proxy();
let proxy = reqwest::Proxy::http(&host)?; let proxy = reqwest::Proxy::http(&host)?;
let client = reqwest::Client::builder().proxy(proxy).build(); let client = reqwest::Client::builder().proxy(proxy).build();
match client?.get(format!("http://{}/share", contact)).send().await { match client?
.get(format!("http://{}/share", contact))
.send()
.await
{
Ok(response) => { Ok(response) => {
let res = response.json::<Contact>().await; let res = response.json::<Contact>().await;
debug!("share response: {:?}", res); debug!("share response: {:?}", res);
match res { match res {
Ok(r) => { Ok(r) => Ok(r),
Ok(r)
},
_ => Ok(Default::default()), _ => Ok(Default::default()),
} }
} }

View file

@ -1,8 +1,16 @@
// db created and exported from here // db created and exported from here
extern crate lmdb_rs as lmdb; extern crate lmdb_rs as lmdb;
use log::{debug, error}; use lmdb::{
use lmdb::{EnvBuilder, DbFlags, Environment, DbHandle}; DbFlags,
DbHandle,
EnvBuilder,
Environment,
};
use log::{
debug,
error,
};
use crate::utils; use crate::utils;
@ -14,10 +22,17 @@ pub struct Interface {
impl Interface { impl Interface {
pub fn open() -> Self { pub fn open() -> Self {
let release_env = utils::get_release_env(); let release_env = utils::get_release_env();
let file_path = format!("/home/{}/.nevmes/", std::env::var("USER").unwrap_or(String::from("user"))); let file_path = format!(
"/home/{}/.nevmes/",
std::env::var("USER").unwrap_or(String::from("user"))
);
let mut env_str: &str = "test-lmdb"; let mut env_str: &str = "test-lmdb";
if release_env != utils::ReleaseEnvironment::Development { env_str = "lmdb"; }; if release_env != utils::ReleaseEnvironment::Development {
let env = EnvBuilder::new().open(format!("{}/{}", file_path, env_str), 0o777).unwrap(); env_str = "lmdb";
};
let env = EnvBuilder::new()
.open(format!("{}/{}", file_path, env_str), 0o777)
.unwrap();
let handle = env.get_default_db(DbFlags::empty()).unwrap(); let handle = env.get_default_db(DbFlags::empty()).unwrap();
Interface { env, handle } Interface { env, handle }
} }
@ -26,12 +41,14 @@ impl Interface {
{ {
// get a database bound to this transaction // get a database bound to this transaction
let db = txn.bind(&h); let db = txn.bind(&h);
let pair = vec![(k,v)]; let pair = vec![(k, v)];
for &(key, value) in pair.iter() { db.set(&key, &value).unwrap(); } for &(key, value) in pair.iter() {
db.set(&key, &value).unwrap();
}
} }
match txn.commit() { match txn.commit() {
Err(_) => error!("failed to commit!"), Err(_) => error!("failed to commit!"),
Ok(_) => () Ok(_) => (),
} }
} }
pub fn read(e: &Environment, h: &DbHandle, k: &str) -> String { pub fn read(e: &Environment, h: &DbHandle, k: &str) -> String {
@ -51,11 +68,11 @@ impl Interface {
{ {
// get a database bound to this transaction // get a database bound to this transaction
let db = txn.bind(&h); let db = txn.bind(&h);
db.del::<>(&k).unwrap_or_else(|_| error!("failed to delete")); db.del(&k).unwrap_or_else(|_| error!("failed to delete"));
} }
match txn.commit() { match txn.commit() {
Err(_) => error!("failed to commit!"), Err(_) => error!("failed to commit!"),
Ok(_) => () Ok(_) => (),
} }
} }
} }

View file

@ -1,8 +1,19 @@
use log::{debug, error, info}; use crate::{
use std::process::Command; i2p,
utils,
};
use gpgme::*; use gpgme::*;
use std::{error::Error, fs::File, io::Write}; use log::{
use crate::{utils, i2p}; debug,
error,
info,
};
use std::{
error::Error,
fs::File,
io::Write,
process::Command,
};
/// Searches for key, returns empty string if none exists /// Searches for key, returns empty string if none exists
/// ///
@ -73,7 +84,8 @@ pub fn import_key(cid: String, key: Vec<u8>) -> Result<(), Box<dyn Error>> {
let mut data = Data::from_seekable_stream(input)?; let mut data = Data::from_seekable_stream(input)?;
let mode = None; let mode = None;
mode.map(|m| data.set_encoding(m)); mode.map(|m| data.set_encoding(m));
ctx.import(&mut data).map_err(|e| format!("import failed {:?}", e))?; ctx.import(&mut data)
.map_err(|e| format!("import failed {:?}", e))?;
utils::stage_cleanup(filename); utils::stage_cleanup(filename);
Ok(()) Ok(())
} }
@ -82,19 +94,23 @@ pub fn encrypt(name: String, body: &Vec<u8>) -> Result<Vec<u8>, Box<dyn Error>>
let proto = Protocol::OpenPgp; let proto = Protocol::OpenPgp;
let mut ctx = Context::from_protocol(proto)?; let mut ctx = Context::from_protocol(proto)?;
ctx.set_armor(true); ctx.set_armor(true);
let keys: Vec<Key> = ctx.find_keys([&name])? let keys: Vec<Key> = ctx
.find_keys([&name])?
.filter_map(|x| x.ok()) .filter_map(|x| x.ok())
.filter(|k| k.can_encrypt()) .filter(|k| k.can_encrypt())
.collect(); .collect();
let filename = format!("{}.nevmes", name); let filename = format!("{}.nevmes", name);
let mut f = File::create(&filename)?; let mut f = File::create(&filename)?;
f.write_all(body)?; f.write_all(body)?;
let mut input = File::open(&filename) let mut input =
.map_err(|e| format!("can't open file `{}': {}", filename, e))?; File::open(&filename).map_err(|e| format!("can't open file `{}': {}", filename, e))?;
let mut output = Vec::new(); let mut output = Vec::new();
ctx.encrypt(&keys, &mut input, &mut output) ctx.encrypt(&keys, &mut input, &mut output)
.map_err(|e| format!("encrypting failed: {:?}", e))?; .map_err(|e| format!("encrypting failed: {:?}", e))?;
debug!("encrypted message body: {}", String::from_utf8(output.iter().cloned().collect()).unwrap_or(utils::empty_string())); debug!(
"encrypted message body: {}",
String::from_utf8(output.iter().cloned().collect()).unwrap_or(utils::empty_string())
);
utils::stage_cleanup(filename); utils::stage_cleanup(filename);
Ok(output) Ok(output)
} }
@ -106,8 +122,8 @@ pub fn decrypt(mid: &String, body: &Vec<u8>) -> Result<Vec<u8>, Box<dyn Error>>
let filename = format!("{}.nevmes", mid); let filename = format!("{}.nevmes", mid);
let mut f = File::create(&filename)?; let mut f = File::create(&filename)?;
f.write_all(&body)?; f.write_all(&body)?;
let mut input = File::open(&filename) let mut input =
.map_err(|e| format!("can't open file `{}': {}", filename, e))?; File::open(&filename).map_err(|e| format!("can't open file `{}': {}", filename, e))?;
let mut output = Vec::new(); let mut output = Vec::new();
ctx.decrypt(&mut input, &mut output) ctx.decrypt(&mut input, &mut output)
.map_err(|e| format!("decrypting failed: {:?}", e))?; .map_err(|e| format!("decrypting failed: {:?}", e))?;
@ -125,7 +141,8 @@ pub fn write_gen_batch() -> Result<(), Box<dyn Error>> {
Subkey-Curve: Curve25519 Subkey-Curve: Curve25519
Name-Real: {} Name-Real: {}
Name-Email: {} Name-Email: {}
Expire-Date: 0", name, name Expire-Date: 0",
name, name
); );
let filename = format!("genkey-batch"); let filename = format!("genkey-batch");
let mut f = File::create(&filename)?; let mut f = File::create(&filename)?;
@ -157,11 +174,13 @@ pub fn sign_key(key: &str) -> Result<(), Box<dyn Error>> {
.get_secret_key(app_key) .get_secret_key(app_key)
.map_err(|e| format!("unable to find signing key: {:?}", e))?; .map_err(|e| format!("unable to find signing key: {:?}", e))?;
debug!("app key: {:?}", key.id()); debug!("app key: {:?}", key.id());
k2s_ctx.add_signer(&key) k2s_ctx
.add_signer(&key)
.map_err(|e| format!("add_signer() failed: {:?}", e))?; .map_err(|e| format!("add_signer() failed: {:?}", e))?;
} }
k2s_ctx.sign_key(&key_to_sign, None::<String>, Default::default()) k2s_ctx
.sign_key(&key_to_sign, None::<String>, Default::default())
.map_err(|e| format!("signing failed: {:?}", e))?; .map_err(|e| format!("signing failed: {:?}", e))?;
println!("Signed key for {}", key); println!("Signed key for {}", key);

View file

@ -1,9 +1,24 @@
use std::{fs, env, process::Command}; use crate::{
use log::{debug, error, info, warn}; args,
use serde::{Deserialize, Serialize}; utils,
use crate::{args, utils}; };
use clap::Parser; use clap::Parser;
use std::time::Duration; use log::{
debug,
error,
info,
warn,
};
use serde::{
Deserialize,
Serialize,
};
use std::{
env,
fs,
process::Command,
time::Duration,
};
// TODO(c2m): debug i2p-zero http proxy // TODO(c2m): debug i2p-zero http proxy
@ -24,13 +39,13 @@ impl I2pStatus {
#[derive(Debug, Deserialize, Serialize)] #[derive(Debug, Deserialize, Serialize)]
pub struct HttpProxyStatus { pub struct HttpProxyStatus {
pub open: bool pub open: bool,
} }
#[derive(Debug)] #[derive(Debug)]
pub enum ProxyStatus { pub enum ProxyStatus {
Opening, Opening,
Open Open,
} }
impl ProxyStatus { impl ProxyStatus {
@ -56,7 +71,9 @@ struct Tunnels {
impl Default for Tunnels { impl Default for Tunnels {
fn default() -> Self { fn default() -> Self {
Tunnels { tunnels: Vec::new() } Tunnels {
tunnels: Vec::new(),
}
} }
} }
@ -103,7 +120,11 @@ fn create_tunnel() {
let args = args::Args::parse(); let args = args::Args::parse();
let path = args.i2p_zero_dir; let path = args.i2p_zero_dir;
let output = Command::new(format!("{}/router/bin/tunnel-control.sh", path)) let output = Command::new(format!("{}/router/bin/tunnel-control.sh", path))
.args(["server.create", "127.0.0.1", &format!("{}",utils::get_app_port())]) .args([
"server.create",
"127.0.0.1",
&format!("{}", utils::get_app_port()),
])
.spawn() .spawn()
.expect("i2p-zero failed to create a tunnel"); .expect("i2p-zero failed to create a tunnel");
debug!("{:?}", output.stdout); debug!("{:?}", output.stdout);
@ -125,17 +146,20 @@ pub fn get_destination() -> String {
env::var("USER").unwrap_or(String::from("user")) env::var("USER").unwrap_or(String::from("user"))
); );
// Don't panic if i2p-zero isn't installed // Don't panic if i2p-zero isn't installed
let contents = match fs::read_to_string(file_path) let contents = match fs::read_to_string(file_path) {
{
Ok(file) => file, Ok(file) => file,
_=> utils::empty_string(), _ => utils::empty_string(),
}; };
if contents != utils::empty_string() { if contents != utils::empty_string() {
let input = format!(r#"{contents}"#); let input = format!(r#"{contents}"#);
let mut j: Tunnels = serde_json::from_str(&input).unwrap_or(Default::default()); let mut j: Tunnels = serde_json::from_str(&input).unwrap_or(Default::default());
let destination: String = j.tunnels.remove(0).dest.ok_or(utils::empty_string()) let destination: String = j
.tunnels
.remove(0)
.dest
.ok_or(utils::empty_string())
.unwrap_or(utils::empty_string()); .unwrap_or(utils::empty_string());
return destination return destination;
} }
utils::empty_string() utils::empty_string()
} }

View file

@ -9,19 +9,22 @@ pub mod models; // db structs
pub mod monero; // monero-wallet-rpc interface pub mod monero; // monero-wallet-rpc interface
pub mod proof; // external auth/payment proof module pub mod proof; // external auth/payment proof module
pub mod reqres; // http request/responses pub mod reqres; // http request/responses
pub mod utils; // misc. pub mod user;
pub mod user; // user rep/service layer pub mod utils; // misc. // user rep/service layer
pub const NEVMES_JWP_SECRET_KEY: &str = "NEVMES_JWP_SECRET_KEY"; pub const NEVMES_JWP_SECRET_KEY: &str = "NEVMES_JWP_SECRET_KEY";
pub const NEVMES_JWT_SECRET_KEY: &str = "NEVMES_JWT_SECRET_KEY"; pub const NEVMES_JWT_SECRET_KEY: &str = "NEVMES_JWT_SECRET_KEY";
/// The latest monero release download /// The latest monero release download
pub const MONERO_RELEASE_VERSION: &str = "monero-linux-x64-v0.18.2.2.tar.bz2"; pub const MONERO_RELEASE_VERSION: &str = "monero-linux-x64-v0.18.2.2.tar.bz2";
pub const MONERO_RELEASE_HASH: &str = "186800de18f67cca8475ce392168aabeb5709a8f8058b0f7919d7c693786d56b"; pub const MONERO_RELEASE_HASH: &str =
"186800de18f67cca8475ce392168aabeb5709a8f8058b0f7919d7c693786d56b";
/// The latest i2p-zero release version /// The latest i2p-zero release version
pub const I2P_ZERO_RELEASE_VERSION: &str = "v1.20"; pub const I2P_ZERO_RELEASE_VERSION: &str = "v1.20";
pub const I2P_ZERO_RELEASH_HASH: &str = "7e7216b281624ec464b55217284017576d109eaba7b35f7e4994ae2a78634de7"; pub const I2P_ZERO_RELEASH_HASH: &str =
"7e7216b281624ec464b55217284017576d109eaba7b35f7e4994ae2a78634de7";
/// The latest i2pd release version /// The latest i2pd release version
pub const I2P_RELEASE_VERSION: &str = "2.2.1"; pub const I2P_RELEASE_VERSION: &str = "2.2.1";
pub const I2P_RELEASE_HASH: &str = "c9879b8f69ea13c758672c2fa083dc2e0abb289e0fc9a55af98f9f1795f82659"; pub const I2P_RELEASE_HASH: &str =
"c9879b8f69ea13c758672c2fa083dc2e0abb289e0fc9a55af98f9f1795f82659";
// DO NOT EDIT BELOW THIS LINE // DO NOT EDIT BELOW THIS LINE

View file

@ -1,9 +1,21 @@
// Message repo/service layer // Message repo/service layer
use crate::{contact, db, models::*, utils, reqres, i2p, gpg}; use crate::{
use std::error::Error; contact,
use log::{debug, error, info}; db,
gpg,
i2p,
models::*,
reqres,
utils,
};
use log::{
debug,
error,
info,
};
use reqwest::StatusCode; use reqwest::StatusCode;
use rocket::serde::json::Json; use rocket::serde::json::Json;
use std::error::Error;
/// Create a new message /// Create a new message
pub async fn create(m: Json<Message>, jwp: String) -> Message { pub async fn create(m: Json<Message>, jwp: String) -> Message {
@ -12,8 +24,7 @@ pub async fn create(m: Json<Message>, jwp: String) -> Message {
let created = chrono::offset::Utc::now().timestamp(); let created = chrono::offset::Utc::now().timestamp();
// get contact public gpg key and encrypt the message // get contact public gpg key and encrypt the message
debug!("sending message: {:?}", &m); debug!("sending message: {:?}", &m);
let e_body = gpg::encrypt( let e_body = gpg::encrypt(String::from(&m.to), &m.body).unwrap_or(Vec::new());
String::from(&m.to), &m.body).unwrap_or(Vec::new());
let new_message = Message { let new_message = Message {
mid: String::from(&f_mid), mid: String::from(&f_mid),
uid: String::from(&m.uid), uid: String::from(&m.uid),
@ -45,10 +56,14 @@ pub async fn create(m: Json<Message>, jwp: String) -> Message {
pub async fn rx(m: Json<Message>) { pub async fn rx(m: Json<Message>) {
// make sure the message isn't something strange // make sure the message isn't something strange
let is_valid = validate_message(&m); let is_valid = validate_message(&m);
if !is_valid { return; } if !is_valid {
return;
}
// don't allow messages from outside the contact list // don't allow messages from outside the contact list
let is_in_contact_list = contact::exists(&m.from); let is_in_contact_list = contact::exists(&m.from);
if !is_in_contact_list { return; } if !is_in_contact_list {
return;
}
let f_mid: String = format!("m{}", utils::generate_rnd()); let f_mid: String = format!("m{}", utils::generate_rnd());
let new_message = Message { let new_message = Message {
mid: String::from(&f_mid), mid: String::from(&f_mid),
@ -79,7 +94,7 @@ pub fn find(mid: &String) -> Message {
let r = db::Interface::read(&s.env, &s.handle, &String::from(mid)); let r = db::Interface::read(&s.env, &s.handle, &String::from(mid));
if r == utils::empty_string() { if r == utils::empty_string() {
error!("message not found"); error!("message not found");
return Default::default() return Default::default();
} }
Message::from_db(String::from(mid), r) Message::from_db(String::from(mid), r)
} }
@ -120,22 +135,28 @@ pub fn find_all() -> Vec<Message> {
/// Tx message /// Tx message
async fn send_message(out: &Message, jwp: &str) -> Result<(), Box<dyn Error>> { async fn send_message(out: &Message, jwp: &str) -> Result<(), Box<dyn Error>> {
let host = utils::get_i2p_http_proxy(); let host = utils::get_i2p_http_proxy();
let proxy = reqwest::Proxy::http(&host)?; let proxy = reqwest::Proxy::http(&host)?;
let client = reqwest::Client::builder().proxy(proxy).build(); let client = reqwest::Client::builder().proxy(proxy).build();
// check if the contact is online // check if the contact is online
let is_online: bool = is_contact_online(&out.to, String::from(jwp)).await.unwrap_or(false); let is_online: bool = is_contact_online(&out.to, String::from(jwp))
.await
.unwrap_or(false);
if is_online { if is_online {
return match client?.post(format!("http://{}/message/rx", out.to)) return match client?
.header("proof", jwp).json(&out).send().await { .post(format!("http://{}/message/rx", out.to))
.header("proof", jwp)
.json(&out)
.send()
.await
{
Ok(response) => { Ok(response) => {
let status = response.status(); let status = response.status();
debug!("send response: {:?}", status.as_str()); debug!("send response: {:?}", status.as_str());
if status == StatusCode::OK || status == StatusCode::PAYMENT_REQUIRED { if status == StatusCode::OK || status == StatusCode::PAYMENT_REQUIRED {
remove_from_fts(String::from(&out.mid)); remove_from_fts(String::from(&out.mid));
return Ok(()) return Ok(());
} else { } else {
Ok(()) Ok(())
} }
@ -144,7 +165,7 @@ async fn send_message(out: &Message, jwp: &str) -> Result<(), Box<dyn Error>> {
error!("failed to send message due to: {:?}", e); error!("failed to send message due to: {:?}", e);
Ok(()) Ok(())
} }
} };
} else { } else {
send_to_retry(String::from(&out.mid)).await; send_to_retry(String::from(&out.mid)).await;
Ok(()) Ok(())
@ -170,15 +191,23 @@ async fn is_contact_online(contact: &String, jwp: String) -> Result<bool, Box<dy
let host = utils::get_i2p_http_proxy(); let host = utils::get_i2p_http_proxy();
let proxy = reqwest::Proxy::http(&host)?; let proxy = reqwest::Proxy::http(&host)?;
let client = reqwest::Client::builder().proxy(proxy).build(); let client = reqwest::Client::builder().proxy(proxy).build();
match client?.get(format!("http://{}/xmr/rpc/version", contact)) match client?
.header("proof", jwp).send().await { .get(format!("http://{}/xmr/rpc/version", contact))
.header("proof", jwp)
.send()
.await
{
Ok(response) => { Ok(response) => {
let res = response.json::<reqres::XmrRpcVersionResponse>().await; let res = response.json::<reqres::XmrRpcVersionResponse>().await;
debug!("check is contact online by version response: {:?}", res); debug!("check is contact online by version response: {:?}", res);
match res { match res {
Ok(r) => { Ok(r) => {
if r.result.version != 0 { Ok(true) } else { Ok(false) } if r.result.version != 0 {
}, Ok(true)
} else {
Ok(false)
}
}
_ => Ok(false), _ => Ok(false),
} }
} }
@ -204,7 +233,10 @@ async fn send_to_retry(mid: String) {
if String::from(&r).contains(&String::from(&mid)) { if String::from(&r).contains(&String::from(&mid)) {
msg_list = r; msg_list = r;
} }
debug!("writing fts message index {} for id: {}", msg_list, list_key); debug!(
"writing fts message index {} for id: {}",
msg_list, list_key
);
db::Interface::write(&s.env, &s.handle, &String::from(list_key), &msg_list); db::Interface::write(&s.env, &s.handle, &String::from(list_key), &msg_list);
// restart fts if not empty // restart fts if not empty
let list_key = format!("fts"); let list_key = format!("fts");
@ -230,9 +262,20 @@ fn remove_from_fts(mid: String) {
debug!("fts is empty"); debug!("fts is empty");
} }
let pre_v_fts = r.split(","); let pre_v_fts = r.split(",");
let v: Vec<String> = pre_v_fts.map(|s| if s != &mid { String::from(s)} else { utils::empty_string()} ).collect(); let v: Vec<String> = pre_v_fts
.map(|s| {
if s != &mid {
String::from(s)
} else {
utils::empty_string()
}
})
.collect();
let msg_list = v.join(","); let msg_list = v.join(",");
debug!("writing fts message index {} for id: {}", msg_list, list_key); debug!(
"writing fts message index {} for id: {}",
msg_list, list_key
);
db::Interface::write(&s.env, &s.handle, &String::from(list_key), &msg_list); db::Interface::write(&s.env, &s.handle, &String::from(list_key), &msg_list);
} }
@ -286,13 +329,12 @@ fn validate_message(j: &Json<Message>) -> bool {
j.mid.len() < utils::string_limit() j.mid.len() < utils::string_limit()
&& j.body.len() < utils::message_limit() && j.body.len() < utils::message_limit()
&& j.to == i2p::get_destination() && j.to == i2p::get_destination()
&& j.uid .len() < utils::string_limit() && j.uid.len() < utils::string_limit()
} }
fn is_fts_clear(r: String) -> bool { fn is_fts_clear(r: String) -> bool {
let v_mid = r.split(","); let v_mid = r.split(",");
let v: Vec<String> = v_mid.map(|s| String::from(s)).collect(); let v: Vec<String> = v_mid.map(|s| String::from(s)).collect();
debug!("fts contents: {:#?}", v); debug!("fts contents: {:#?}", v);
v.len() >= 2 && v[v.len()-1] == utils::empty_string() v.len() >= 2 && v[v.len() - 1] == utils::empty_string() && v[0] == utils::empty_string()
&& v[0] == utils::empty_string()
} }

View file

@ -1,5 +1,8 @@
use crate::utils; use crate::utils;
use rocket::serde::{Serialize, Deserialize}; use rocket::serde::{
Deserialize,
Serialize,
};
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
#[serde(crate = "rocket::serde")] #[serde(crate = "rocket::serde")]
@ -161,7 +164,14 @@ impl Message {
}; };
let from = v.remove(0); let from = v.remove(0);
let to = v.remove(0); let to = v.remove(0);
Message { mid: k, uid, body, created, from, to, } Message {
mid: k,
uid,
body,
created,
from,
to,
}
} }
} }

View file

@ -1,7 +1,20 @@
use crate::{args, reqres, utils::{self, get_release_env, ReleaseEnvironment}, proof}; use crate::{
args,
proof,
reqres,
utils::{
self,
get_release_env,
ReleaseEnvironment,
},
};
use clap::Parser; use clap::Parser;
use diqwest::WithDigestAuth; use diqwest::WithDigestAuth;
use log::{debug, error, info}; use log::{
debug,
error,
info,
};
use std::process::Command; use std::process::Command;
/// Current xmr ring size updated here. /// Current xmr ring size updated here.
@ -88,7 +101,9 @@ pub enum LockTimeLimit {
impl LockTimeLimit { impl LockTimeLimit {
pub fn value(&self) -> u64 { pub fn value(&self) -> u64 {
match *self { LockTimeLimit::Blocks => 20, } match *self {
LockTimeLimit::Blocks => 20,
}
} }
} }
@ -125,17 +140,22 @@ pub fn start_rpc() {
let login = get_rpc_creds(); let login = get_rpc_creds();
let daemon_address = get_rpc_daemon(); let daemon_address = get_rpc_daemon();
let rpc_login = format!("{}:{}", &login.username, &login.credential); let rpc_login = format!("{}:{}", &login.username, &login.credential);
let mut wallet_dir = format!("/home/{}/.nevmes/stagenet/wallet/", let mut wallet_dir = format!(
"/home/{}/.nevmes/stagenet/wallet/",
std::env::var("USER").unwrap_or(String::from("user")), std::env::var("USER").unwrap_or(String::from("user")),
); );
let release_env = get_release_env(); let release_env = get_release_env();
if release_env == ReleaseEnvironment::Development { if release_env == ReleaseEnvironment::Development {
let args = [ let args = [
"--rpc-bind-port", &port, "--rpc-bind-port",
"--wallet-dir", &wallet_dir, &port,
"--rpc-login", &rpc_login, "--wallet-dir",
"--daemon-address", &daemon_address, &wallet_dir,
"--stagenet" "--rpc-login",
&rpc_login,
"--daemon-address",
&daemon_address,
"--stagenet",
]; ];
let output = Command::new(format!("{}/monero-wallet-rpc", bin_dir)) let output = Command::new(format!("{}/monero-wallet-rpc", bin_dir))
.args(args) .args(args)
@ -143,11 +163,20 @@ pub fn start_rpc() {
.expect("monero-wallet-rpc failed to start"); .expect("monero-wallet-rpc failed to start");
debug!("{:?}", output.stdout); debug!("{:?}", output.stdout);
} else { } else {
wallet_dir = format!("/home/{}/.nevmes/wallet/", wallet_dir = format!(
"/home/{}/.nevmes/wallet/",
std::env::var("USER").unwrap_or(String::from("user")), std::env::var("USER").unwrap_or(String::from("user")),
); );
let args = ["--rpc-bind-port", &port, "--wallet-dir", &wallet_dir, let args = [
"--rpc-login", &rpc_login, "--daemon-address", &daemon_address]; "--rpc-bind-port",
&port,
"--wallet-dir",
&wallet_dir,
"--rpc-login",
&rpc_login,
"--daemon-address",
&daemon_address,
];
let output = Command::new(format!("{}/monero-wallet-rpc", bin_dir)) let output = Command::new(format!("{}/monero-wallet-rpc", bin_dir))
.args(args) .args(args)
.spawn() .spawn()
@ -190,7 +219,10 @@ fn get_rpc_creds() -> RpcLogin {
let args = args::Args::parse(); let args = args::Args::parse();
let username = String::from(args.monero_rpc_username); let username = String::from(args.monero_rpc_username);
let credential = String::from(args.monero_rpc_cred); let credential = String::from(args.monero_rpc_cred);
RpcLogin { username, credential } RpcLogin {
username,
credential,
}
} }
fn get_rpc_daemon() -> String { fn get_rpc_daemon() -> String {
@ -209,8 +241,12 @@ pub async fn get_version() -> reqres::XmrRpcVersionResponse {
method: RpcFields::GetVersion.value(), method: RpcFields::GetVersion.value(),
}; };
let login: RpcLogin = get_rpc_creds(); let login: RpcLogin = get_rpc_creds();
match client.post(host).json(&req) match client
.send_with_digest_auth(&login.username, &login.credential).await { .post(host)
.json(&req)
.send_with_digest_auth(&login.username, &login.credential)
.await
{
Ok(response) => { Ok(response) => {
let res = response.json::<reqres::XmrRpcVersionResponse>().await; let res = response.json::<reqres::XmrRpcVersionResponse>().await;
debug!("get version response: {:?}", res); debug!("get version response: {:?}", res);
@ -248,8 +284,12 @@ pub async fn verify_signature(address: String, data: String, signature: String)
params, params,
}; };
let login: RpcLogin = get_rpc_creds(); let login: RpcLogin = get_rpc_creds();
match client.post(host).json(&req) match client
.send_with_digest_auth(&login.username, &login.credential).await { .post(host)
.json(&req)
.send_with_digest_auth(&login.username, &login.credential)
.await
{
Ok(response) => { Ok(response) => {
let res = response.json::<reqres::XmrRpcVerifyResponse>().await; let res = response.json::<reqres::XmrRpcVerifyResponse>().await;
debug!("verify response: {:?}", res); debug!("verify response: {:?}", res);
@ -284,8 +324,12 @@ pub async fn create_wallet(filename: String) -> bool {
params, params,
}; };
let login: RpcLogin = get_rpc_creds(); let login: RpcLogin = get_rpc_creds();
match client.post(host).json(&req) match client
.send_with_digest_auth(&login.username, &login.credential).await { .post(host)
.json(&req)
.send_with_digest_auth(&login.username, &login.credential)
.await
{
Ok(response) => { Ok(response) => {
// The result from wallet creation is empty // The result from wallet creation is empty
let res = response.text().await; let res = response.text().await;
@ -296,11 +340,11 @@ pub async fn create_wallet(filename: String) -> bool {
return false; return false;
} }
true true
}, }
_ => false, _ => false,
} }
} }
Err(_) => false Err(_) => false,
} }
} }
@ -309,9 +353,7 @@ pub async fn open_wallet(filename: String) -> bool {
info!("opening wallet for {}", &filename); info!("opening wallet for {}", &filename);
let client = reqwest::Client::new(); let client = reqwest::Client::new();
let host = get_rpc_host(); let host = get_rpc_host();
let params = reqres::XmrRpcOpenWalletParams { let params = reqres::XmrRpcOpenWalletParams { filename };
filename,
};
let req = reqres::XmrRpcOpenRequest { let req = reqres::XmrRpcOpenRequest {
jsonrpc: RpcFields::JsonRpcVersion.value(), jsonrpc: RpcFields::JsonRpcVersion.value(),
id: RpcFields::Id.value(), id: RpcFields::Id.value(),
@ -320,8 +362,12 @@ pub async fn open_wallet(filename: String) -> bool {
}; };
debug!("open request: {:?}", req); debug!("open request: {:?}", req);
let login: RpcLogin = get_rpc_creds(); let login: RpcLogin = get_rpc_creds();
match client.post(host).json(&req) match client
.send_with_digest_auth(&login.username, &login.credential).await { .post(host)
.json(&req)
.send_with_digest_auth(&login.username, &login.credential)
.await
{
Ok(response) => { Ok(response) => {
// The result from wallet operation is empty // The result from wallet operation is empty
let res = response.text().await; let res = response.text().await;
@ -332,11 +378,11 @@ pub async fn open_wallet(filename: String) -> bool {
return false; return false;
} }
return true; return true;
}, }
_ => false, _ => false,
} }
} }
Err(_) => false Err(_) => false,
} }
} }
@ -345,9 +391,7 @@ pub async fn close_wallet(filename: String) -> bool {
info!("closing wallet for {}", &filename); info!("closing wallet for {}", &filename);
let client = reqwest::Client::new(); let client = reqwest::Client::new();
let host = get_rpc_host(); let host = get_rpc_host();
let params = reqres::XmrRpcOpenWalletParams { let params = reqres::XmrRpcOpenWalletParams { filename };
filename,
};
let req = reqres::XmrRpcOpenRequest { let req = reqres::XmrRpcOpenRequest {
jsonrpc: RpcFields::JsonRpcVersion.value(), jsonrpc: RpcFields::JsonRpcVersion.value(),
id: RpcFields::Id.value(), id: RpcFields::Id.value(),
@ -355,8 +399,12 @@ pub async fn close_wallet(filename: String) -> bool {
params, params,
}; };
let login: RpcLogin = get_rpc_creds(); let login: RpcLogin = get_rpc_creds();
match client.post(host).json(&req) match client
.send_with_digest_auth(&login.username, &login.credential).await { .post(host)
.json(&req)
.send_with_digest_auth(&login.username, &login.credential)
.await
{
Ok(response) => { Ok(response) => {
// The result from wallet operation is empty // The result from wallet operation is empty
let res = response.text().await; let res = response.text().await;
@ -366,7 +414,7 @@ pub async fn close_wallet(filename: String) -> bool {
_ => false, _ => false,
} }
} }
Err(_) => false Err(_) => false,
} }
} }
@ -376,7 +424,10 @@ pub async fn get_balance() -> reqres::XmrRpcBalanceResponse {
let client = reqwest::Client::new(); let client = reqwest::Client::new();
let host = get_rpc_host(); let host = get_rpc_host();
let params: reqres::XmrRpcBalanceParams = reqres::XmrRpcBalanceParams { let params: reqres::XmrRpcBalanceParams = reqres::XmrRpcBalanceParams {
account_index: 0, address_indices: vec![0], all_accounts: false, strict: false, account_index: 0,
address_indices: vec![0],
all_accounts: false,
strict: false,
}; };
let req = reqres::XmrRpcBalanceRequest { let req = reqres::XmrRpcBalanceRequest {
jsonrpc: RpcFields::JsonRpcVersion.value(), jsonrpc: RpcFields::JsonRpcVersion.value(),
@ -385,8 +436,12 @@ pub async fn get_balance() -> reqres::XmrRpcBalanceResponse {
params, params,
}; };
let login: RpcLogin = get_rpc_creds(); let login: RpcLogin = get_rpc_creds();
match client.post(host).json(&req) match client
.send_with_digest_auth(&login.username, &login.credential).await { .post(host)
.json(&req)
.send_with_digest_auth(&login.username, &login.credential)
.await
{
Ok(response) => { Ok(response) => {
let res = response.json::<reqres::XmrRpcBalanceResponse>().await; let res = response.json::<reqres::XmrRpcBalanceResponse>().await;
debug!("balance response: {:?}", res); debug!("balance response: {:?}", res);
@ -395,7 +450,7 @@ pub async fn get_balance() -> reqres::XmrRpcBalanceResponse {
_ => Default::default(), _ => Default::default(),
} }
} }
Err(_) => Default::default() Err(_) => Default::default(),
} }
} }
@ -404,9 +459,7 @@ pub async fn get_address() -> reqres::XmrRpcAddressResponse {
info!("fetching wallet address"); info!("fetching wallet address");
let client = reqwest::Client::new(); let client = reqwest::Client::new();
let host = get_rpc_host(); let host = get_rpc_host();
let params: reqres::XmrRpcAddressParams = reqres::XmrRpcAddressParams { let params: reqres::XmrRpcAddressParams = reqres::XmrRpcAddressParams { account_index: 0 };
account_index: 0,
};
let req = reqres::XmrRpcAddressRequest { let req = reqres::XmrRpcAddressRequest {
jsonrpc: RpcFields::JsonRpcVersion.value(), jsonrpc: RpcFields::JsonRpcVersion.value(),
id: RpcFields::Id.value(), id: RpcFields::Id.value(),
@ -414,8 +467,12 @@ pub async fn get_address() -> reqres::XmrRpcAddressResponse {
params, params,
}; };
let login: RpcLogin = get_rpc_creds(); let login: RpcLogin = get_rpc_creds();
match client.post(host).json(&req) match client
.send_with_digest_auth(&login.username, &login.credential).await { .post(host)
.json(&req)
.send_with_digest_auth(&login.username, &login.credential)
.await
{
Ok(response) => { Ok(response) => {
let res = response.json::<reqres::XmrRpcAddressResponse>().await; let res = response.json::<reqres::XmrRpcAddressResponse>().await;
debug!("address response: {:?}", res); debug!("address response: {:?}", res);
@ -424,7 +481,7 @@ pub async fn get_address() -> reqres::XmrRpcAddressResponse {
_ => Default::default(), _ => Default::default(),
} }
} }
Err(_) => Default::default() Err(_) => Default::default(),
} }
} }
@ -434,7 +491,9 @@ pub async fn validate_address(address: &String) -> reqres::XmrRpcValidateAddress
let client = reqwest::Client::new(); let client = reqwest::Client::new();
let host = get_rpc_host(); let host = get_rpc_host();
let params: reqres::XmrRpcValidateAddressParams = reqres::XmrRpcValidateAddressParams { let params: reqres::XmrRpcValidateAddressParams = reqres::XmrRpcValidateAddressParams {
address: String::from(address), any_net_type: false, allow_openalias: true, address: String::from(address),
any_net_type: false,
allow_openalias: true,
}; };
let req = reqres::XmrRpcValidateAddressRequest { let req = reqres::XmrRpcValidateAddressRequest {
jsonrpc: RpcFields::JsonRpcVersion.value(), jsonrpc: RpcFields::JsonRpcVersion.value(),
@ -443,17 +502,23 @@ pub async fn validate_address(address: &String) -> reqres::XmrRpcValidateAddress
params, params,
}; };
let login: RpcLogin = get_rpc_creds(); let login: RpcLogin = get_rpc_creds();
match client.post(host).json(&req) match client
.send_with_digest_auth(&login.username, &login.credential).await { .post(host)
.json(&req)
.send_with_digest_auth(&login.username, &login.credential)
.await
{
Ok(response) => { Ok(response) => {
let res = response.json::<reqres::XmrRpcValidateAddressResponse>().await; let res = response
.json::<reqres::XmrRpcValidateAddressResponse>()
.await;
debug!("validate_address response: {:?}", res); debug!("validate_address response: {:?}", res);
match res { match res {
Ok(res) => res, Ok(res) => res,
_ => Default::default(), _ => Default::default(),
} }
} }
Err(_) => Default::default() Err(_) => Default::default(),
} }
} }
// START Multisig // START Multisig
@ -469,8 +534,12 @@ pub async fn prepare_wallet() -> reqres::XmrRpcPrepareResponse {
method: RpcFields::Prepare.value(), method: RpcFields::Prepare.value(),
}; };
let login: RpcLogin = get_rpc_creds(); let login: RpcLogin = get_rpc_creds();
match client.post(host).json(&req) match client
.send_with_digest_auth(&login.username, &login.credential).await { .post(host)
.json(&req)
.send_with_digest_auth(&login.username, &login.credential)
.await
{
Ok(response) => { Ok(response) => {
let res = response.json::<reqres::XmrRpcPrepareResponse>().await; let res = response.json::<reqres::XmrRpcPrepareResponse>().await;
debug!("prepare response: {:?}", res); debug!("prepare response: {:?}", res);
@ -479,7 +548,7 @@ pub async fn prepare_wallet() -> reqres::XmrRpcPrepareResponse {
_ => Default::default(), _ => Default::default(),
} }
} }
Err(_) => Default::default() Err(_) => Default::default(),
} }
} }
@ -499,8 +568,12 @@ pub async fn make_wallet(info: Vec<String>) -> reqres::XmrRpcMakeResponse {
params, params,
}; };
let login: RpcLogin = get_rpc_creds(); let login: RpcLogin = get_rpc_creds();
match client.post(host).json(&req) match client
.send_with_digest_auth(&login.username, &login.credential).await { .post(host)
.json(&req)
.send_with_digest_auth(&login.username, &login.credential)
.await
{
Ok(response) => { Ok(response) => {
let res = response.json::<reqres::XmrRpcMakeResponse>().await; let res = response.json::<reqres::XmrRpcMakeResponse>().await;
debug!("make response: {:?}", res); debug!("make response: {:?}", res);
@ -509,7 +582,7 @@ pub async fn make_wallet(info: Vec<String>) -> reqres::XmrRpcMakeResponse {
_ => Default::default(), _ => Default::default(),
} }
} }
Err(_) => Default::default() Err(_) => Default::default(),
} }
} }
@ -528,8 +601,12 @@ pub async fn finalize_wallet(info: Vec<String>) -> reqres::XmrRpcFinalizeRespons
params, params,
}; };
let login: RpcLogin = get_rpc_creds(); let login: RpcLogin = get_rpc_creds();
match client.post(host).json(&req) match client
.send_with_digest_auth(&login.username, &login.credential).await { .post(host)
.json(&req)
.send_with_digest_auth(&login.username, &login.credential)
.await
{
Ok(response) => { Ok(response) => {
let res = response.json::<reqres::XmrRpcFinalizeResponse>().await; let res = response.json::<reqres::XmrRpcFinalizeResponse>().await;
debug!("finalize response: {:?}", res); debug!("finalize response: {:?}", res);
@ -538,7 +615,7 @@ pub async fn finalize_wallet(info: Vec<String>) -> reqres::XmrRpcFinalizeRespons
_ => Default::default(), _ => Default::default(),
} }
} }
Err(_) => Default::default() Err(_) => Default::default(),
} }
} }
@ -553,8 +630,12 @@ pub async fn export_multisig_info() -> reqres::XmrRpcExportResponse {
method: RpcFields::Export.value(), method: RpcFields::Export.value(),
}; };
let login: RpcLogin = get_rpc_creds(); let login: RpcLogin = get_rpc_creds();
match client.post(host).json(&req) match client
.send_with_digest_auth(&login.username, &login.credential).await { .post(host)
.json(&req)
.send_with_digest_auth(&login.username, &login.credential)
.await
{
Ok(response) => { Ok(response) => {
let res = response.json::<reqres::XmrRpcExportResponse>().await; let res = response.json::<reqres::XmrRpcExportResponse>().await;
debug!("export msig response: {:?}", res); debug!("export msig response: {:?}", res);
@ -563,7 +644,7 @@ pub async fn export_multisig_info() -> reqres::XmrRpcExportResponse {
_ => Default::default(), _ => Default::default(),
} }
} }
Err(_) => Default::default() Err(_) => Default::default(),
} }
} }
@ -572,9 +653,7 @@ pub async fn import_multisig_info(info: Vec<String>) -> reqres::XmrRpcImportResp
info!("import msig wallet"); info!("import msig wallet");
let client = reqwest::Client::new(); let client = reqwest::Client::new();
let host = get_rpc_host(); let host = get_rpc_host();
let params = reqres::XmrRpcImportParams { let params = reqres::XmrRpcImportParams { info };
info,
};
let req = reqres::XmrRpcImportRequest { let req = reqres::XmrRpcImportRequest {
jsonrpc: RpcFields::JsonRpcVersion.value(), jsonrpc: RpcFields::JsonRpcVersion.value(),
id: RpcFields::Id.value(), id: RpcFields::Id.value(),
@ -582,8 +661,12 @@ pub async fn import_multisig_info(info: Vec<String>) -> reqres::XmrRpcImportResp
params, params,
}; };
let login: RpcLogin = get_rpc_creds(); let login: RpcLogin = get_rpc_creds();
match client.post(host).json(&req) match client
.send_with_digest_auth(&login.username, &login.credential).await { .post(host)
.json(&req)
.send_with_digest_auth(&login.username, &login.credential)
.await
{
Ok(response) => { Ok(response) => {
let res = response.json::<reqres::XmrRpcImportResponse>().await; let res = response.json::<reqres::XmrRpcImportResponse>().await;
debug!("import msig info response: {:?}", res); debug!("import msig info response: {:?}", res);
@ -592,7 +675,7 @@ pub async fn import_multisig_info(info: Vec<String>) -> reqres::XmrRpcImportResp
_ => Default::default(), _ => Default::default(),
} }
} }
Err(_) => Default::default() Err(_) => Default::default(),
} }
} }
@ -601,9 +684,7 @@ pub async fn sign_multisig(tx_data_hex: String) -> reqres::XmrRpcSignMultisigRes
info!("sign msig txset"); info!("sign msig txset");
let client = reqwest::Client::new(); let client = reqwest::Client::new();
let host = get_rpc_host(); let host = get_rpc_host();
let params = reqres::XmrRpcSignMultisigParams { let params = reqres::XmrRpcSignMultisigParams { tx_data_hex };
tx_data_hex,
};
let req = reqres::XmrRpcSignMultisigRequest { let req = reqres::XmrRpcSignMultisigRequest {
jsonrpc: RpcFields::JsonRpcVersion.value(), jsonrpc: RpcFields::JsonRpcVersion.value(),
id: RpcFields::Id.value(), id: RpcFields::Id.value(),
@ -611,8 +692,12 @@ pub async fn sign_multisig(tx_data_hex: String) -> reqres::XmrRpcSignMultisigRes
params, params,
}; };
let login: RpcLogin = get_rpc_creds(); let login: RpcLogin = get_rpc_creds();
match client.post(host).json(&req) match client
.send_with_digest_auth(&login.username, &login.credential).await { .post(host)
.json(&req)
.send_with_digest_auth(&login.username, &login.credential)
.await
{
Ok(response) => { Ok(response) => {
let res = response.json::<reqres::XmrRpcSignMultisigResponse>().await; let res = response.json::<reqres::XmrRpcSignMultisigResponse>().await;
debug!("sign msig txset response: {:?}", res); debug!("sign msig txset response: {:?}", res);
@ -621,7 +706,7 @@ pub async fn sign_multisig(tx_data_hex: String) -> reqres::XmrRpcSignMultisigRes
_ => Default::default(), _ => Default::default(),
} }
} }
Err(_) => Default::default() Err(_) => Default::default(),
} }
} }
// END Multisig // END Multisig
@ -644,8 +729,12 @@ pub async fn check_tx_proof(txp: &proof::TxProof) -> reqres::XmrRpcCheckTxProofR
params, params,
}; };
let login: RpcLogin = get_rpc_creds(); let login: RpcLogin = get_rpc_creds();
match client.post(host).json(&req) match client
.send_with_digest_auth(&login.username, &login.credential).await { .post(host)
.json(&req)
.send_with_digest_auth(&login.username, &login.credential)
.await
{
Ok(response) => { Ok(response) => {
let res = response.json::<reqres::XmrRpcCheckTxProofResponse>().await; let res = response.json::<reqres::XmrRpcCheckTxProofResponse>().await;
debug!("check_tx_proof response: {:?}", res); debug!("check_tx_proof response: {:?}", res);
@ -654,7 +743,7 @@ pub async fn check_tx_proof(txp: &proof::TxProof) -> reqres::XmrRpcCheckTxProofR
_ => Default::default(), _ => Default::default(),
} }
} }
Err(_) => Default::default() Err(_) => Default::default(),
} }
} }
@ -675,8 +764,12 @@ pub async fn get_tx_proof(ptxp: proof::TxProof) -> reqres::XmrRpcGetTxProofRespo
params, params,
}; };
let login: RpcLogin = get_rpc_creds(); let login: RpcLogin = get_rpc_creds();
match client.post(host).json(&req) match client
.send_with_digest_auth(&login.username, &login.credential).await { .post(host)
.json(&req)
.send_with_digest_auth(&login.username, &login.credential)
.await
{
Ok(response) => { Ok(response) => {
let res = response.json::<reqres::XmrRpcGetTxProofResponse>().await; let res = response.json::<reqres::XmrRpcGetTxProofResponse>().await;
debug!("get_tx_proof response: {:?}", res); debug!("get_tx_proof response: {:?}", res);
@ -685,7 +778,7 @@ pub async fn get_tx_proof(ptxp: proof::TxProof) -> reqres::XmrRpcGetTxProofRespo
_ => Default::default(), _ => Default::default(),
} }
} }
Err(_) => Default::default() Err(_) => Default::default(),
} }
} }
@ -695,7 +788,7 @@ pub async fn get_transfer_by_txid(txid: &str) -> reqres::XmrRpcGetTxByIdResponse
let client = reqwest::Client::new(); let client = reqwest::Client::new();
let host = get_rpc_host(); let host = get_rpc_host();
let params: reqres::XmrRpcGetTxByIdParams = reqres::XmrRpcGetTxByIdParams { let params: reqres::XmrRpcGetTxByIdParams = reqres::XmrRpcGetTxByIdParams {
txid: String::from(txid) txid: String::from(txid),
}; };
let req = reqres::XmrRpcGetTxByIdRequest { let req = reqres::XmrRpcGetTxByIdRequest {
jsonrpc: RpcFields::JsonRpcVersion.value(), jsonrpc: RpcFields::JsonRpcVersion.value(),
@ -704,8 +797,12 @@ pub async fn get_transfer_by_txid(txid: &str) -> reqres::XmrRpcGetTxByIdResponse
params, params,
}; };
let login: RpcLogin = get_rpc_creds(); let login: RpcLogin = get_rpc_creds();
match client.post(host).json(&req) match client
.send_with_digest_auth(&login.username, &login.credential).await { .post(host)
.json(&req)
.send_with_digest_auth(&login.username, &login.credential)
.await
{
Ok(response) => { Ok(response) => {
let res = response.json::<reqres::XmrRpcGetTxByIdResponse>().await; let res = response.json::<reqres::XmrRpcGetTxByIdResponse>().await;
debug!("get_transfer_by_txid response: {:?}", res); debug!("get_transfer_by_txid response: {:?}", res);
@ -714,7 +811,7 @@ pub async fn get_transfer_by_txid(txid: &str) -> reqres::XmrRpcGetTxByIdResponse
_ => Default::default(), _ => Default::default(),
} }
} }
Err(_) => Default::default() Err(_) => Default::default(),
} }
} }
@ -740,8 +837,12 @@ pub async fn transfer(d: reqres::Destination) -> reqres::XmrRpcTransferResponse
params, params,
}; };
let login: RpcLogin = get_rpc_creds(); let login: RpcLogin = get_rpc_creds();
match client.post(host).json(&req) match client
.send_with_digest_auth(&login.username, &login.credential).await { .post(host)
.json(&req)
.send_with_digest_auth(&login.username, &login.credential)
.await
{
Ok(response) => { Ok(response) => {
let res = response.json::<reqres::XmrRpcTransferResponse>().await; let res = response.json::<reqres::XmrRpcTransferResponse>().await;
debug!("{} response: {:?}", RpcFields::Transfer.value(), res); debug!("{} response: {:?}", RpcFields::Transfer.value(), res);
@ -750,7 +851,7 @@ pub async fn transfer(d: reqres::Destination) -> reqres::XmrRpcTransferResponse
_ => Default::default(), _ => Default::default(),
} }
} }
Err(_) => Default::default() Err(_) => Default::default(),
} }
} }
@ -767,8 +868,12 @@ pub async fn sweep_all(address: String) -> reqres::XmrRpcSweepAllResponse {
params, params,
}; };
let login: RpcLogin = get_rpc_creds(); let login: RpcLogin = get_rpc_creds();
match client.post(host).json(&req) match client
.send_with_digest_auth(&login.username, &login.credential).await { .post(host)
.json(&req)
.send_with_digest_auth(&login.username, &login.credential)
.await
{
Ok(response) => { Ok(response) => {
let res = response.json::<reqres::XmrRpcSweepAllResponse>().await; let res = response.json::<reqres::XmrRpcSweepAllResponse>().await;
debug!("{} response: {:?}", RpcFields::SweepAll.value(), res); debug!("{} response: {:?}", RpcFields::SweepAll.value(), res);
@ -777,7 +882,7 @@ pub async fn sweep_all(address: String) -> reqres::XmrRpcSweepAllResponse {
_ => Default::default(), _ => Default::default(),
} }
} }
Err(_) => Default::default() Err(_) => Default::default(),
} }
} }
@ -786,7 +891,8 @@ pub async fn create_address() -> reqres::XmrRpcCreateAddressResponse {
info!("creating new subaddress"); info!("creating new subaddress");
let client = reqwest::Client::new(); let client = reqwest::Client::new();
let host = get_rpc_host(); let host = get_rpc_host();
let params: reqres::XmrRpcCreateAddressParams = reqres::XmrRpcCreateAddressParams { account_index: 0 }; let params: reqres::XmrRpcCreateAddressParams =
reqres::XmrRpcCreateAddressParams { account_index: 0 };
let req = reqres::XmrRpcCreateAddressRequest { let req = reqres::XmrRpcCreateAddressRequest {
jsonrpc: RpcFields::JsonRpcVersion.value(), jsonrpc: RpcFields::JsonRpcVersion.value(),
id: RpcFields::Id.value(), id: RpcFields::Id.value(),
@ -794,8 +900,12 @@ pub async fn create_address() -> reqres::XmrRpcCreateAddressResponse {
params, params,
}; };
let login: RpcLogin = get_rpc_creds(); let login: RpcLogin = get_rpc_creds();
match client.post(host).json(&req) match client
.send_with_digest_auth(&login.username, &login.credential).await { .post(host)
.json(&req)
.send_with_digest_auth(&login.username, &login.credential)
.await
{
Ok(response) => { Ok(response) => {
let res = response.json::<reqres::XmrRpcCreateAddressResponse>().await; let res = response.json::<reqres::XmrRpcCreateAddressResponse>().await;
debug!("{} response: {:?}", RpcFields::CreateAddress.value(), res); debug!("{} response: {:?}", RpcFields::CreateAddress.value(), res);
@ -804,7 +914,7 @@ pub async fn create_address() -> reqres::XmrRpcCreateAddressResponse {
_ => Default::default(), _ => Default::default(),
} }
} }
Err(_) => Default::default() Err(_) => Default::default(),
} }
} }
@ -829,6 +939,6 @@ pub async fn get_info() -> reqres::XmrDaemonGetInfoResponse {
_ => Default::default(), _ => Default::default(),
} }
} }
Err(_) => Default::default() Err(_) => Default::default(),
} }
} }

View file

@ -1,14 +1,31 @@
use crate::{db, monero, reqres, utils}; use crate::{
use log::{error, info}; db,
monero,
reqres,
utils,
};
use log::{
error,
info,
};
use rocket::{
http::Status,
outcome::Outcome,
request,
request::FromRequest,
Request,
};
use std::error::Error; use std::error::Error;
use rocket::http::Status;
use rocket::outcome::Outcome;
use rocket::request::FromRequest;
use rocket::{request, Request};
use hmac::{Hmac, Mac}; use hmac::{
Hmac,
Mac,
};
use jwt::*; use jwt::*;
use serde::{Deserialize, Serialize}; use serde::{
Deserialize,
Serialize,
};
use sha2::Sha512; use sha2::Sha512;
use std::collections::BTreeMap; use std::collections::BTreeMap;
@ -43,7 +60,11 @@ pub async fn create_invoice() -> reqres::Invoice {
let address = c_address.result.address; let address = c_address.result.address;
let pay_threshold = utils::get_payment_threshold(); let pay_threshold = utils::get_payment_threshold();
let conf_threshold = utils::get_conf_threshold(); let conf_threshold = utils::get_conf_threshold();
reqres::Invoice { address, conf_threshold, pay_threshold } reqres::Invoice {
address,
conf_threshold,
pay_threshold,
}
} }
/// Technically the same process as creating a JWT /// Technically the same process as creating a JWT
@ -94,7 +115,12 @@ pub async fn prove_payment(contact: String, txp: &TxProof) -> Result<reqres::Jwp
let host = utils::get_i2p_http_proxy(); let host = utils::get_i2p_http_proxy();
let proxy = reqwest::Proxy::http(&host)?; let proxy = reqwest::Proxy::http(&host)?;
let client = reqwest::Client::builder().proxy(proxy).build(); let client = reqwest::Client::builder().proxy(proxy).build();
match client?.post(format!("http://{}/prove", contact)).json(txp).send().await { match client?
.post(format!("http://{}/prove", contact))
.json(txp)
.send()
.await
{
Ok(response) => { Ok(response) => {
let res = response.json::<reqres::Jwp>().await; let res = response.json::<reqres::Jwp>().await;
log::debug!("prove payment response: {:?}", res); log::debug!("prove payment response: {:?}", res);
@ -106,7 +132,7 @@ pub async fn prove_payment(contact: String, txp: &TxProof) -> Result<reqres::Jwp
db::Interface::delete(&s.env, &s.handle, &k); db::Interface::delete(&s.env, &s.handle, &k);
db::Interface::write(&s.env, &s.handle, &k, &r.jwp); db::Interface::write(&s.env, &s.handle, &k, &r.jwp);
Ok(r) Ok(r)
}, }
_ => Ok(Default::default()), _ => Ok(Default::default()),
} }
} }
@ -137,7 +163,11 @@ pub async fn prove_payment(contact: String, txp: &TxProof) -> Result<reqres::Jwp
#[derive(Debug)] #[derive(Debug)]
pub struct PaymentProof(String); pub struct PaymentProof(String);
impl PaymentProof { pub fn get_jwp(self) -> String { self.0 } } impl PaymentProof {
pub fn get_jwp(self) -> String {
self.0
}
}
#[derive(Debug)] #[derive(Debug)]
pub enum PaymentProofError { pub enum PaymentProofError {
@ -205,7 +235,10 @@ impl<'r> FromRequest<'r> for PaymentProof {
} }
Err(e) => { Err(e) => {
error!("jwp error: {:?}", e); error!("jwp error: {:?}", e);
return Outcome::Failure((Status::PaymentRequired, PaymentProofError::Invalid)); return Outcome::Failure((
Status::PaymentRequired,
PaymentProofError::Invalid,
));
} }
}; };
} }
@ -224,17 +257,19 @@ async fn validate_proof(txp: &TxProof) -> TxProof {
let p = monero::check_tx_proof(txp).await; let p = monero::check_tx_proof(txp).await;
let cth = utils::get_conf_threshold(); let cth = utils::get_conf_threshold();
let pth = utils::get_payment_threshold(); let pth = utils::get_payment_threshold();
let lgtm = p.result.good && !p.result.in_pool let lgtm = p.result.good
&& !p.result.in_pool
&& unlock_time < monero::LockTimeLimit::Blocks.value() && unlock_time < monero::LockTimeLimit::Blocks.value()
&& p.result.confirmations < cth && p.result.received >= pth; && p.result.confirmations < cth
&& p.result.received >= pth;
if lgtm { if lgtm {
return TxProof { return TxProof {
subaddress: String::from(&txp.subaddress), subaddress: String::from(&txp.subaddress),
hash: String::from(&txp.hash), hash: String::from(&txp.hash),
confirmations: p.result.confirmations, confirmations: p.result.confirmations,
message: String::from(&txp.message), message: String::from(&txp.message),
signature: String::from(&txp.signature) signature: String::from(&txp.signature),
} };
} }
Default::default() Default::default()
} }
@ -248,6 +283,8 @@ async fn validate_subaddress(subaddress: &String) -> bool {
let m_address = monero::get_address().await; let m_address = monero::get_address().await;
let all_address = m_address.result.addresses; let all_address = m_address.result.addresses;
let mut address_list: Vec<String> = Vec::new(); let mut address_list: Vec<String> = Vec::new();
for s_address in all_address { address_list.push(s_address.address); } for s_address in all_address {
address_list.push(s_address.address);
}
return address_list.contains(&subaddress); return address_list.contains(&subaddress);
} }

View file

@ -1,5 +1,8 @@
use crate::utils; use crate::utils;
use serde::{Deserialize, Serialize}; use serde::{
Deserialize,
Serialize,
};
// All http requests and responses are here // All http requests and responses are here
@ -363,10 +366,7 @@ pub struct SubAddressIndex {
impl Default for SubAddressIndex { impl Default for SubAddressIndex {
fn default() -> Self { fn default() -> Self {
SubAddressIndex { SubAddressIndex { major: 0, minor: 0 }
major: 0,
minor: 0,
}
} }
} }
@ -820,13 +820,12 @@ impl Default for XmrRpcCreateAddressResponse {
address_index: 0, address_index: 0,
address_indices: Vec::new(), address_indices: Vec::new(),
addresses: Vec::new(), addresses: Vec::new(),
} },
} }
} }
} }
// END XMR Structs // END XMR Structs
/// Container for the message decryption /// Container for the message decryption
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
#[serde(crate = "rocket::serde")] #[serde(crate = "rocket::serde")]

View file

@ -1,7 +1,15 @@
// User repo/service layer // User repo/service layer
use crate::{db, models::*, utils}; use crate::{
db,
models::*,
utils,
};
use log::{
debug,
error,
info,
};
use rocket::serde::json::Json; use rocket::serde::json::Json;
use log::{debug, error, info};
// This module is only used for remote access // This module is only used for remote access
@ -26,7 +34,7 @@ pub fn find(uid: &String) -> User {
let r = db::Interface::read(&s.env, &s.handle, &String::from(uid)); let r = db::Interface::read(&s.env, &s.handle, &String::from(uid));
if r == utils::empty_string() { if r == utils::empty_string() {
error!("user not found"); error!("user not found");
return Default::default() return Default::default();
} }
User::from_db(String::from(uid), r) User::from_db(String::from(uid), r)
} }

View file

@ -1,13 +1,32 @@
use rand_core::RngCore; use crate::{
args,
db,
gpg,
i2p,
message,
models,
monero,
reqres,
utils,
};
use clap::Parser; use clap::Parser;
use log::{
debug,
error,
info,
warn,
};
use rand_core::RngCore;
use rocket::serde::json::Json; 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; use std::time::Duration;
/// Enum for selecting hash validation /// Enum for selecting hash validation
#[derive(PartialEq)] #[derive(PartialEq)]
enum ExternalSoftware { I2P, I2PZero, XMR } enum ExternalSoftware {
I2P,
I2PZero,
XMR,
}
/// Handles the state for the installation manager popup /// Handles the state for the installation manager popup
pub struct Installations { pub struct Installations {
@ -87,14 +106,22 @@ impl ReleaseEnvironment {
pub fn start_core(conn: &Connections) { pub fn start_core(conn: &Connections) {
let env = if !conn.mainnet { "dev" } else { "prod" }; let env = if !conn.mainnet { "dev" } else { "prod" };
let args = [ let args = [
"--monero-location", &conn.monero_location, "--monero-location",
"--monero-blockchain-dir", &conn.blockchain_dir, &conn.monero_location,
"--monero-rpc-host", &conn.rpc_host, "--monero-blockchain-dir",
"--monero-rpc-daemon", &conn.daemon_host, &conn.blockchain_dir,
"--monero-rpc-username", &conn.rpc_username, "--monero-rpc-host",
"--monero-rpc-cred", &conn.rpc_credential, &conn.rpc_host,
"--i2p-zero-dir", &conn.i2p_zero_dir, "--monero-rpc-daemon",
"-r", env, &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 output = std::process::Command::new("./nevmes") let output = std::process::Command::new("./nevmes")
.args(args) .args(args)
@ -107,7 +134,9 @@ pub fn start_core(conn: &Connections) {
pub fn is_using_remote_node() -> bool { pub fn is_using_remote_node() -> bool {
let args = args::Args::parse(); let args = args::Args::parse();
let r = args.remote_node; let r = args.remote_node;
if r { warn!("using a remote node may harm privacy"); } if r {
warn!("using a remote node may harm privacy");
}
r r
} }
@ -196,12 +225,20 @@ pub fn message_to_json(m: &models::Message) -> Json<models::Message> {
} }
/// Instead of putting `String::from("")` /// Instead of putting `String::from("")`
pub fn empty_string() -> String { String::from("") } pub fn empty_string() -> String {
String::from("")
}
// DoS prevention // DoS prevention
pub const fn string_limit() -> usize { 512 } pub const fn string_limit() -> usize {
pub const fn gpg_key_limit() -> usize { 4096 } 512
pub const fn message_limit() -> usize { 9999 } }
pub const fn gpg_key_limit() -> usize {
4096
}
pub const fn message_limit() -> usize {
9999
}
/// Generate application gpg keys at startup if none exist /// Generate application gpg keys at startup if none exist
async fn gen_app_gpg() { async fn gen_app_gpg() {
@ -219,15 +256,19 @@ async fn gen_app_gpg() {
/// Handles panic! for missing wallet directory /// Handles panic! for missing wallet directory
fn create_wallet_dir() { fn create_wallet_dir() {
let file_path = format!("/home/{}/.nevmes", let file_path = format!(
std::env::var("USER").unwrap_or(String::from("user"))); "/home/{}/.nevmes",
std::env::var("USER").unwrap_or(String::from("user"))
);
let s_output = std::process::Command::new("mkdir") let s_output = std::process::Command::new("mkdir")
.args(["-p", &format!("{}/stagenet/wallet", file_path)]) .args(["-p", &format!("{}/stagenet/wallet", file_path)])
.spawn().expect("failed to create dir"); .spawn()
.expect("failed to create dir");
debug!("{:?}", s_output); debug!("{:?}", s_output);
let m_output = std::process::Command::new("mkdir") let m_output = std::process::Command::new("mkdir")
.args(["-p", &format!("{}/wallet", file_path)]) .args(["-p", &format!("{}/wallet", file_path)])
.spawn().expect("failed to create dir"); .spawn()
.expect("failed to create dir");
debug!("{:?}", m_output); debug!("{:?}", m_output);
} }
@ -243,8 +284,7 @@ async fn gen_app_wallet() {
} else { } else {
m_wallet = monero::open_wallet(String::from(filename)).await; m_wallet = monero::open_wallet(String::from(filename)).await;
if m_wallet { if m_wallet {
let m_address: reqres::XmrRpcAddressResponse = let m_address: reqres::XmrRpcAddressResponse = monero::get_address().await;
monero::get_address().await;
info!("app wallet address: {}", m_address.result.address) info!("app wallet address: {}", m_address.result.address)
} }
} }
@ -282,7 +322,7 @@ pub fn get_jwt_secret_key() -> String {
let r = db::Interface::read(&s.env, &s.handle, crate::NEVMES_JWT_SECRET_KEY); let r = db::Interface::read(&s.env, &s.handle, crate::NEVMES_JWT_SECRET_KEY);
if r == utils::empty_string() { if r == utils::empty_string() {
error!("JWT key not found"); error!("JWT key not found");
return Default::default() return Default::default();
} }
r r
} }
@ -292,7 +332,7 @@ pub fn get_jwp_secret_key() -> String {
let r = db::Interface::read(&s.env, &s.handle, crate::NEVMES_JWP_SECRET_KEY); let r = db::Interface::read(&s.env, &s.handle, crate::NEVMES_JWP_SECRET_KEY);
if r == utils::empty_string() { if r == utils::empty_string() {
error!("JWP key not found"); error!("JWP key not found");
return Default::default() return Default::default();
} }
r r
} }
@ -302,21 +342,30 @@ fn start_micro_servers() {
info!("starting auth server"); info!("starting auth server");
let mut auth_path = "nevmes-auth/target/debug/nevmes_auth"; let mut auth_path = "nevmes-auth/target/debug/nevmes_auth";
let env = get_release_env(); let env = get_release_env();
if env == ReleaseEnvironment::Production { auth_path = "nevmes_auth"; } if env == ReleaseEnvironment::Production {
auth_path = "nevmes_auth";
}
let a_output = std::process::Command::new(auth_path) let a_output = std::process::Command::new(auth_path)
.spawn().expect("failed to start auth server"); .spawn()
.expect("failed to start auth server");
debug!("{:?}", a_output.stdout); debug!("{:?}", a_output.stdout);
info!("starting contact server"); info!("starting contact server");
let mut contact_path = "nevmes-contact/target/debug/nevmes_contact"; let mut contact_path = "nevmes-contact/target/debug/nevmes_contact";
if env == ReleaseEnvironment::Production { contact_path = "nevmes_contact"; } if env == ReleaseEnvironment::Production {
contact_path = "nevmes_contact";
}
let c_output = std::process::Command::new(contact_path) let c_output = std::process::Command::new(contact_path)
.spawn().expect("failed to start contact server"); .spawn()
.expect("failed to start contact server");
debug!("{:?}", c_output.stdout); debug!("{:?}", c_output.stdout);
info!("starting message server"); info!("starting message server");
let mut message_path = "nevmes-message/target/debug/nevmes_message"; let mut message_path = "nevmes-message/target/debug/nevmes_message";
if env == ReleaseEnvironment::Production { message_path = "nevmes_message"; } if env == ReleaseEnvironment::Production {
message_path = "nevmes_message";
}
let m_output = std::process::Command::new(message_path) let m_output = std::process::Command::new(message_path)
.spawn().expect("failed to start message server"); .spawn()
.expect("failed to start message server");
debug!("{:?}", m_output.stdout); debug!("{:?}", m_output.stdout);
} }
@ -327,9 +376,12 @@ fn start_gui() {
info!("starting gui"); info!("starting gui");
let mut gui_path = "nevmes-gui/target/debug/nevmes_gui"; let mut gui_path = "nevmes-gui/target/debug/nevmes_gui";
let env = get_release_env(); let env = get_release_env();
if env == ReleaseEnvironment::Production { gui_path = "nevmes-gui"; } if env == ReleaseEnvironment::Production {
gui_path = "nevmes-gui";
}
let g_output = std::process::Command::new(gui_path) let g_output = std::process::Command::new(gui_path)
.spawn().expect("failed to start gui"); .spawn()
.expect("failed to start gui");
debug!("{:?}", g_output.stdout); debug!("{:?}", g_output.stdout);
} }
} }
@ -338,10 +390,16 @@ fn start_gui() {
pub async fn start_up() { pub async fn start_up() {
info!("nevmes is starting up"); info!("nevmes is starting up");
let args = args::Args::parse(); let args = args::Args::parse();
if args.remote_access { start_micro_servers(); } if args.remote_access {
if args.clear_fts { clear_fts(); } start_micro_servers();
}
if args.clear_fts {
clear_fts();
}
gen_signing_keys(); gen_signing_keys();
if !is_using_remote_node() { monero::start_daemon(); } if !is_using_remote_node() {
monero::start_daemon();
}
create_wallet_dir(); create_wallet_dir();
// wait for daemon for a bit // wait for daemon for a bit
tokio::time::sleep(std::time::Duration::new(5, 0)).await; tokio::time::sleep(std::time::Duration::new(5, 0)).await;
@ -354,7 +412,11 @@ pub async fn start_up() {
gen_app_gpg().await; gen_app_gpg().await;
let env: String = get_release_env().value(); let env: String = get_release_env().value();
start_gui(); start_gui();
{ tokio::spawn(async { message::retry_fts().await; }); } {
tokio::spawn(async {
message::retry_fts().await;
});
}
info!("{} - nevmes is online", env); info!("{} - nevmes is online", env);
} }
@ -393,7 +455,9 @@ pub fn kill_child_processes(cm: bool) {
/// We can restart fts from since it gets terminated when empty /// We can restart fts from since it gets terminated when empty
pub fn restart_retry_fts() { pub fn restart_retry_fts() {
tokio::spawn(async move { message::retry_fts().await; }); tokio::spawn(async move {
message::retry_fts().await;
});
} }
/// Called on app startup if `--clear-fts` flag is passed. /// Called on app startup if `--clear-fts` flag is passed.
@ -428,7 +492,10 @@ pub async fn install_software(installations: Installations) -> bool {
info!("installing i2p"); info!("installing i2p");
let i2p_version = crate::I2P_RELEASE_VERSION; let i2p_version = crate::I2P_RELEASE_VERSION;
let i2p_jar = format!("i2pinstall_{}.jar", i2p_version); let i2p_jar = format!("i2pinstall_{}.jar", i2p_version);
let link = format!("https://download.i2p2.no/releases/{}/{}", i2p_version, i2p_jar); let link = format!(
"https://download.i2p2.no/releases/{}/{}",
i2p_version, i2p_jar
);
let curl = std::process::Command::new("curl") let curl = std::process::Command::new("curl")
.args(["-O#", &link]) .args(["-O#", &link])
.status(); .status();
@ -441,8 +508,8 @@ pub async fn install_software(installations: Installations) -> bool {
.spawn() .spawn()
.expect("i2p gui installation failed"); .expect("i2p gui installation failed");
debug!("{:?}", jar_output.stdout); debug!("{:?}", jar_output.stdout);
}, }
_=> error!("i2p download failed") _ => error!("i2p download failed"),
} }
valid_i2p_hash = validate_installation_hash(ExternalSoftware::I2P, &i2p_jar); valid_i2p_hash = validate_installation_hash(ExternalSoftware::I2P, &i2p_jar);
tokio::time::sleep(std::time::Duration::from_secs(1)).await; tokio::time::sleep(std::time::Duration::from_secs(1)).await;
@ -451,8 +518,10 @@ pub async fn install_software(installations: Installations) -> bool {
info!("installing i2p-zero"); info!("installing i2p-zero");
let i2p_version = crate::I2P_ZERO_RELEASE_VERSION; let i2p_version = crate::I2P_ZERO_RELEASE_VERSION;
let i2p_zero_zip = format!("i2p-zero-linux.{}.zip", i2p_version); let i2p_zero_zip = format!("i2p-zero-linux.{}.zip", i2p_version);
let link = format!("https://github.com/i2p-zero/i2p-zero/releases/download/{}/{}", let link = format!(
i2p_version, i2p_zero_zip); "https://github.com/i2p-zero/i2p-zero/releases/download/{}/{}",
i2p_version, i2p_zero_zip
);
let curl = std::process::Command::new("curl") let curl = std::process::Command::new("curl")
.args(["-LO#", &link]) .args(["-LO#", &link])
.status(); .status();
@ -465,15 +534,18 @@ pub async fn install_software(installations: Installations) -> bool {
.spawn() .spawn()
.expect("i2p unzip failed"); .expect("i2p unzip failed");
debug!("{:?}", unzip_output.stdout); debug!("{:?}", unzip_output.stdout);
}, }
_=> error!("i2p-zero download failed") _ => error!("i2p-zero download failed"),
} }
valid_i2p_zero_hash = validate_installation_hash(ExternalSoftware::I2PZero, &i2p_zero_zip); valid_i2p_zero_hash = validate_installation_hash(ExternalSoftware::I2PZero, &i2p_zero_zip);
tokio::time::sleep(std::time::Duration::from_secs(1)).await; tokio::time::sleep(std::time::Duration::from_secs(1)).await;
} }
if installations.xmr { if installations.xmr {
info!("installing monero"); info!("installing monero");
let link = format!("https://downloads.getmonero.org/cli/{}", crate::MONERO_RELEASE_VERSION); let link = format!(
"https://downloads.getmonero.org/cli/{}",
crate::MONERO_RELEASE_VERSION
);
let curl = std::process::Command::new("curl") let curl = std::process::Command::new("curl")
.args(["-O#", &link]) .args(["-O#", &link])
.status(); .status();
@ -486,10 +558,13 @@ pub async fn install_software(installations: Installations) -> bool {
.spawn() .spawn()
.expect("monero tar extraction failed"); .expect("monero tar extraction failed");
debug!("{:?}", tar_output.stdout); debug!("{:?}", tar_output.stdout);
},
_=> error!("monero download failed")
} }
valid_xmr_hash = validate_installation_hash(ExternalSoftware::XMR, &String::from(crate::MONERO_RELEASE_VERSION)); _ => error!("monero download failed"),
}
valid_xmr_hash = validate_installation_hash(
ExternalSoftware::XMR,
&String::from(crate::MONERO_RELEASE_VERSION),
);
tokio::time::sleep(std::time::Duration::from_secs(1)).await; tokio::time::sleep(std::time::Duration::from_secs(1)).await;
} }
valid_i2p_hash && valid_i2p_zero_hash && valid_xmr_hash valid_i2p_hash && valid_i2p_zero_hash && valid_xmr_hash
@ -506,7 +581,9 @@ fn validate_installation_hash(sw: ExternalSoftware, filename: &String) -> bool {
String::from(crate::MONERO_RELEASE_HASH) String::from(crate::MONERO_RELEASE_HASH)
}; };
let sha_output = std::process::Command::new("sha256sum") let sha_output = std::process::Command::new("sha256sum")
.arg(filename).output().expect("hash validation failed"); .arg(filename)
.output()
.expect("hash validation failed");
let str_sha = String::from_utf8(sha_output.stdout).unwrap(); let str_sha = String::from_utf8(sha_output.stdout).unwrap();
let split1 = str_sha.split(" "); let split1 = str_sha.split(" ");
let mut v: Vec<String> = split1.map(|s| String::from(s)).collect(); let mut v: Vec<String> = split1.map(|s| String::from(s)).collect();

6
nevmes-gui/.rustfmt.toml Normal file
View file

@ -0,0 +1,6 @@
# cargo +nightly fmt
comment_width = 100
format_code_in_doc_comments = true
imports_granularity = "Crate"
imports_layout = "Vertical"
wrap_comments = true

View file

@ -1,7 +1,13 @@
use nevmes_core::*; use nevmes_core::*;
use std::sync::mpsc::{Receiver, Sender}; use std::sync::mpsc::{
Receiver,
Sender,
};
use crate::{ADD_CONTACT_TIMEOUT_SECS, BLOCK_TIME_IN_SECS_EST}; use crate::{
ADD_CONTACT_TIMEOUT_SECS,
BLOCK_TIME_IN_SECS_EST,
};
// TODO(c2m): better error handling with and error_tx/error_rx channel // TODO(c2m): better error handling with and error_tx/error_rx channel
// hook into the error thread and show toast messages as required // hook into the error thread and show toast messages as required
@ -454,7 +460,10 @@ impl eframe::App for AddressBookApp {
.labelled_by(find_contact_label.id); .labelled_by(find_contact_label.id);
}); });
ui.label("\n"); ui.label("\n");
use egui_extras::{Column, TableBuilder}; use egui_extras::{
Column,
TableBuilder,
};
let table = TableBuilder::new(ui) let table = TableBuilder::new(ui)
.striped(true) .striped(true)
.resizable(true) .resizable(true)
@ -679,7 +688,10 @@ fn send_payment_req(
if check_txp.result.good && check_txp.result.confirmations > 0 { if check_txp.result.good && check_txp.result.confirmations > 0 {
break; break;
} }
tokio::time::sleep(std::time::Duration::from_secs(BLOCK_TIME_IN_SECS_EST as u64)).await; tokio::time::sleep(std::time::Duration::from_secs(
BLOCK_TIME_IN_SECS_EST as u64,
))
.await;
retry_count += 1; retry_count += 1;
} }
write_gui_db( write_gui_db(

View file

@ -3,8 +3,13 @@
use eframe::egui; use eframe::egui;
use nevmes_core::*; use nevmes_core::*;
use std::sync::mpsc::{Receiver, Sender}; use std::{
use std::time::Duration; sync::mpsc::{
Receiver,
Sender,
},
time::Duration,
};
pub struct HomeApp { pub struct HomeApp {
connections: utils::Connections, connections: utils::Connections,
@ -73,9 +78,11 @@ impl Default for HomeApp {
let s_i2p_status = false; let s_i2p_status = false;
let s_can_refresh = false; let s_can_refresh = false;
let c_xmr_logo = std::fs::read("./assets/xmr.png").unwrap_or(Vec::new()); let c_xmr_logo = std::fs::read("./assets/xmr.png").unwrap_or(Vec::new());
let logo_xmr = egui_extras::RetainedImage::from_image_bytes("./assets/xmr.png", &c_xmr_logo).unwrap(); let logo_xmr =
egui_extras::RetainedImage::from_image_bytes("./assets/xmr.png", &c_xmr_logo).unwrap();
let c_i2p_logo = std::fs::read("./assets/i2p.png").unwrap_or(Vec::new()); let c_i2p_logo = std::fs::read("./assets/i2p.png").unwrap_or(Vec::new());
let logo_i2p = egui_extras::RetainedImage::from_image_bytes("./assets/i2p.png", &c_i2p_logo).unwrap(); let logo_i2p =
egui_extras::RetainedImage::from_image_bytes("./assets/i2p.png", &c_i2p_logo).unwrap();
Self { Self {
connections, connections,
core_timeout_rx, core_timeout_rx,
@ -140,7 +147,9 @@ impl eframe::App for HomeApp {
} }
if let Ok(install) = self.installation_rx.try_recv() { if let Ok(install) = self.installation_rx.try_recv() {
self.is_installing = !install; self.is_installing = !install;
if !install && self.is_loading { self.has_install_failed = true } if !install && self.is_loading {
self.has_install_failed = true
}
self.is_loading = false; self.is_loading = false;
} }
if let Ok(timeout) = self.core_timeout_rx.try_recv() { if let Ok(timeout) = self.core_timeout_rx.try_recv() {
@ -251,7 +260,11 @@ impl eframe::App for HomeApp {
if !self.is_loading { if !self.is_loading {
if ui.button("Install").clicked() { if ui.button("Install").clicked() {
self.is_loading = true; self.is_loading = true;
install_software_req(self.installation_tx.clone(), ctx.clone(), &self.installations); install_software_req(
self.installation_tx.clone(),
ctx.clone(),
&self.installations,
);
} }
} }
} }
@ -389,18 +402,23 @@ fn send_reset_refresh(tx: Sender<bool>, ctx: egui::Context, init: bool) {
}); });
} }
fn start_core_timeout fn start_core_timeout(tx: Sender<bool>, ctx: egui::Context) {
(tx: Sender<bool>, ctx: egui::Context) {
tokio::spawn(async move { tokio::spawn(async move {
tokio::time::sleep(std::time::Duration::from_secs(crate::START_CORE_TIMEOUT_SECS)).await; tokio::time::sleep(std::time::Duration::from_secs(
crate::START_CORE_TIMEOUT_SECS,
))
.await;
log::error!("start nevmes-core timeout"); log::error!("start nevmes-core timeout");
let _ = tx.send(true); let _ = tx.send(true);
ctx.request_repaint(); ctx.request_repaint();
}); });
} }
fn install_software_req fn install_software_req(
(tx: Sender<bool>, ctx: egui::Context, installations: &utils::Installations) { tx: Sender<bool>,
ctx: egui::Context,
installations: &utils::Installations,
) {
let req_install: utils::Installations = utils::Installations { let req_install: utils::Installations = utils::Installations {
i2p: installations.i2p, i2p: installations.i2p,
i2p_zero: installations.i2p_zero, i2p_zero: installations.i2p_zero,

View file

@ -1,6 +1,9 @@
use crate::CREDENTIAL_KEY; use crate::CREDENTIAL_KEY;
use nevmes_core::*; use nevmes_core::*;
use sha2::{Digest, Sha512}; use sha2::{
Digest,
Sha512,
};
/// TODO(c2m): Create a more secure locking mechanism /// TODO(c2m): Create a more secure locking mechanism
/// ///

View file

@ -1,5 +1,8 @@
use nevmes_core::*; use nevmes_core::*;
use std::sync::mpsc::{Sender, Receiver}; use std::sync::mpsc::{
Receiver,
Sender,
};
pub struct MailBoxApp { pub struct MailBoxApp {
decrypted_message: String, decrypted_message: String,
@ -26,11 +29,12 @@ impl Default for MailBoxApp {
impl eframe::App for MailBoxApp { impl eframe::App for MailBoxApp {
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
// Hook into async channel threads // Hook into async channel threads
//----------------------------------------------------------------------------------- //-----------------------------------------------------------------------------------
if let Ok(refresh) = self.refresh_on_delete_rx.try_recv() { if let Ok(refresh) = self.refresh_on_delete_rx.try_recv() {
if refresh { self.message_init = false; } if refresh {
self.message_init = false;
}
} }
// initial message load // initial message load
@ -59,7 +63,10 @@ impl eframe::App for MailBoxApp {
self.messages = message::find_all(); self.messages = message::find_all();
} }
ui.label("\n"); ui.label("\n");
use egui_extras::{Column, TableBuilder}; use egui_extras::{
Column,
TableBuilder,
};
let table = TableBuilder::new(ui) let table = TableBuilder::new(ui)
.striped(true) .striped(true)
@ -90,12 +97,14 @@ impl eframe::App for MailBoxApp {
ui.strong(""); ui.strong("");
}); });
}) })
.body(|mut body| .body(|mut body| {
for m in &self.messages { for m in &self.messages {
let row_height = 200.0; let row_height = 200.0;
body.row(row_height, |mut row| { body.row(row_height, |mut row| {
row.col(|ui| { row.col(|ui| {
let h = chrono::NaiveDateTime::from_timestamp_opt(m.created, 0).unwrap().to_string(); let h = chrono::NaiveDateTime::from_timestamp_opt(m.created, 0)
.unwrap()
.to_string();
ui.label(format!("{}", h)); ui.label(format!("{}", h));
}); });
row.col(|ui| { row.col(|ui| {
@ -105,8 +114,10 @@ impl eframe::App for MailBoxApp {
ui.label(format!("{}", m.to)); ui.label(format!("{}", m.to));
}); });
row.col(|ui| { row.col(|ui| {
ui.label(format!("{}", ui.label(format!(
String::from_utf8(m.body.iter().cloned().collect()).unwrap())); "{}",
String::from_utf8(m.body.iter().cloned().collect()).unwrap()
));
}); });
row.col(|ui| { row.col(|ui| {
ui.style_mut().wrap = Some(false); ui.style_mut().wrap = Some(false);
@ -121,23 +132,30 @@ impl eframe::App for MailBoxApp {
self.is_showing_decryption = true; self.is_showing_decryption = true;
d = Default::default(); d = Default::default();
bytes = Vec::new(); bytes = Vec::new();
log::debug!("cleared decryption bytes: {:?} string: {}", bytes, d.body); log::debug!(
"cleared decryption bytes: {:?} string: {}",
bytes,
d.body
);
} }
} }
if ui.button("Delete").clicked() { if ui.button("Delete").clicked() {
message::delete(&m.mid); message::delete(&m.mid);
refresh_on_delete_req(self.refresh_on_delete_tx.clone(), ctx.clone()) refresh_on_delete_req(
self.refresh_on_delete_tx.clone(),
ctx.clone(),
)
} }
}); });
}); });
}); });
}
}); });
}); });
} }
} }
fn refresh_on_delete_req fn refresh_on_delete_req(tx: Sender<bool>, ctx: egui::Context) {
(tx: Sender<bool>, ctx: egui::Context) {
tokio::spawn(async move { tokio::spawn(async move {
tokio::time::sleep(std::time::Duration::from_secs(1)).await; tokio::time::sleep(std::time::Duration::from_secs(1)).await;
log::error!("refreshing messages...."); log::error!("refreshing messages....");

View file

@ -1,14 +1,13 @@
mod home;
mod address_book; mod address_book;
mod home;
mod lock_screen; mod lock_screen;
mod mailbox; mod mailbox;
mod settings; mod settings;
mod wallet; mod wallet;
pub use address_book::AddressBookApp; pub use address_book::AddressBookApp;
pub use lock_screen::LockScreenApp;
pub use home::HomeApp; pub use home::HomeApp;
pub use lock_screen::LockScreenApp;
pub use mailbox::MailBoxApp; pub use mailbox::MailBoxApp;
pub use settings::SettingsApp; pub use settings::SettingsApp;
pub use wallet::WalletApp; pub use wallet::WalletApp;

View file

@ -1,5 +1,8 @@
use nevmes_core::*; use nevmes_core::*;
use sha2::{Digest, Sha512}; use sha2::{
Digest,
Sha512,
};
use crate::CREDENTIAL_KEY; use crate::CREDENTIAL_KEY;

View file

@ -1,7 +1,10 @@
use nevmes_core::*;
use image::Luma; use image::Luma;
use nevmes_core::*;
use qrcode::QrCode; use qrcode::QrCode;
use std::sync::mpsc::{Receiver, Sender}; use std::sync::mpsc::{
Receiver,
Sender,
};
pub struct WalletApp { pub struct WalletApp {
pub init: bool, pub init: bool,
@ -90,8 +93,8 @@ impl eframe::App for WalletApp {
self.init = true; self.init = true;
self.is_qr_set = true; self.is_qr_set = true;
let contents = std::fs::read(&file_path).unwrap_or(Vec::new()); let contents = std::fs::read(&file_path).unwrap_or(Vec::new());
self.qr = egui_extras::RetainedImage::from_image_bytes( self.qr =
"qr.png", &contents,).unwrap(); egui_extras::RetainedImage::from_image_bytes("qr.png", &contents).unwrap();
ctx.request_repaint(); ctx.request_repaint();
} }
self.qr.show(ui); self.qr.show(ui);
@ -120,7 +123,11 @@ impl eframe::App for WalletApp {
ui.text_edit_singleline(&mut self.sweep_address) ui.text_edit_singleline(&mut self.sweep_address)
.labelled_by(sweep_label.id); .labelled_by(sweep_label.id);
if ui.button("Sweep").clicked() { if ui.button("Sweep").clicked() {
send_sweep_all_req(self.xmr_sweep_all_tx.clone(), ctx.clone(), self.sweep_address.clone()); send_sweep_all_req(
self.xmr_sweep_all_tx.clone(),
ctx.clone(),
self.sweep_address.clone(),
);
self.sweep_address = utils::empty_string(); self.sweep_address = utils::empty_string();
self.is_showing_sweep_result = true; self.is_showing_sweep_result = true;
self.is_loading = true; self.is_loading = true;
@ -138,7 +145,11 @@ fn send_address_req(tx: Sender<reqres::XmrRpcAddressResponse>, ctx: egui::Contex
}); });
} }
fn send_sweep_all_req(tx: Sender<reqres::XmrRpcSweepAllResponse>, ctx: egui::Context, address: String) { fn send_sweep_all_req(
tx: Sender<reqres::XmrRpcSweepAllResponse>,
ctx: egui::Context,
address: String,
) {
tokio::spawn(async move { tokio::spawn(async move {
let result: reqres::XmrRpcSweepAllResponse = monero::sweep_all(address).await; let result: reqres::XmrRpcSweepAllResponse = monero::sweep_all(address).await;
let _ = tx.send(result); let _ = tx.send(result);

View file

@ -8,7 +8,7 @@ mod wrap_app;
/// key for fetching the login credential hash /// key for fetching the login credential hash
pub const CREDENTIAL_KEY: &str = "NEVMES_GUI_KEY"; pub const CREDENTIAL_KEY: &str = "NEVMES_GUI_KEY";
/// TODO(c2m): configurable lock screen timeout /// TODO(c2m): configurable lock screen timeout
pub const LOCK_SCREEN_TIMEOUT_SECS: u64 = 60*5; pub const LOCK_SCREEN_TIMEOUT_SECS: u64 = 60 * 5;
/// interval to search for credential on initial gui load /// interval to search for credential on initial gui load
pub const CRED_CHECK_INTERVAL: u64 = 5; pub const CRED_CHECK_INTERVAL: u64 = 5;
/// monero estimated block time in seconds /// monero estimated block time in seconds

View file

@ -1,6 +1,9 @@
use nevmes_core::*;
use sha2::{Sha512, Digest};
use crate::CREDENTIAL_KEY; use crate::CREDENTIAL_KEY;
use nevmes_core::*;
use sha2::{
Digest,
Sha512,
};
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct LoginApp { pub struct LoginApp {
@ -12,7 +15,10 @@ impl Default for LoginApp {
fn default() -> Self { fn default() -> Self {
let credential = utils::empty_string(); let credential = utils::empty_string();
let is_cred_generated = false; let is_cred_generated = false;
LoginApp { credential, is_cred_generated } LoginApp {
credential,
is_cred_generated,
}
} }
} }
@ -44,4 +50,3 @@ impl eframe::App for LoginApp {
}); });
} }
} }

View file

@ -32,7 +32,15 @@ fn main() -> Result<(), eframe::Error> {
// Execute the runtime in its own thread. // Execute the runtime in its own thread.
// The future doesn't have to do anything. In this example, it just sleeps forever. // The future doesn't have to do anything. In this example, it just sleeps forever.
std::thread::spawn(move || { std::thread::spawn(move || {
rt.block_on(async { loop { tokio::time::sleep(Duration::from_secs(3600)).await; } }) rt.block_on(async {
loop {
tokio::time::sleep(Duration::from_secs(3600)).await;
}
})
}); });
eframe::run_native("nevmes-gui-v0.3.0-alpha", options, Box::new(|cc| Box::new(nevmes_gui::WrapApp::new(cc))),) eframe::run_native(
"nevmes-gui-v0.3.0-alpha",
options,
Box::new(|cc| Box::new(nevmes_gui::WrapApp::new(cc))),
)
} }

View file

@ -2,9 +2,10 @@
use eframe::glow; use eframe::glow;
use nevmes_core::*; use nevmes_core::*;
use std::sync::mpsc::{Receiver, Sender}; use std::sync::mpsc::{
Receiver,
Sender,
};
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@ -65,7 +66,6 @@ pub struct State {
lock_timer_tx: Sender<bool>, lock_timer_tx: Sender<bool>,
lock_timer_rx: Receiver<bool>, lock_timer_rx: Receiver<bool>,
// end async notifications // end async notifications
} }
impl Default for State { impl Default for State {
@ -169,7 +169,9 @@ impl eframe::App for WrapApp {
} }
} }
if let Ok(lock_timer) = self.state.lock_timer_rx.try_recv() { if let Ok(lock_timer) = self.state.lock_timer_rx.try_recv() {
if lock_timer { self.state.lock_timer += 1 } if lock_timer {
self.state.lock_timer += 1
}
} }
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
@ -195,7 +197,11 @@ impl eframe::App for WrapApp {
send_inc_lock_timer_req(self.state.lock_timer_tx.clone(), ctx.clone()); send_inc_lock_timer_req(self.state.lock_timer_tx.clone(), ctx.clone());
} }
if (!self.state.is_screen_locking && self.state.is_cred_set) || app_initializing { if (!self.state.is_screen_locking && self.state.is_cred_set) || app_initializing {
self.send_lock_refresh(self.state.is_screen_locked_tx.clone(), ctx.clone(), app_initializing); self.send_lock_refresh(
self.state.is_screen_locked_tx.clone(),
ctx.clone(),
app_initializing,
);
self.state.is_screen_locking = true; self.state.is_screen_locking = true;
} }
self.show_selected_app(ctx, frame); self.show_selected_app(ctx, frame);
@ -210,7 +216,6 @@ impl eframe::App for WrapApp {
fn on_exit(&mut self, _gl: Option<&glow::Context>) { fn on_exit(&mut self, _gl: Option<&glow::Context>) {
utils::kill_child_processes(false); utils::kill_child_processes(false);
} }
} }
impl WrapApp { impl WrapApp {
@ -275,9 +280,12 @@ impl WrapApp {
tokio::spawn(async move { tokio::spawn(async move {
log::debug!("locking screen"); log::debug!("locking screen");
if !init { if !init {
tokio::time::sleep(std::time::Duration::from_secs(crate::LOCK_SCREEN_TIMEOUT_SECS)).await; tokio::time::sleep(std::time::Duration::from_secs(
crate::LOCK_SCREEN_TIMEOUT_SECS,
))
.await;
} }
let _= tx.send(true); let _ = tx.send(true);
ctx.request_repaint(); ctx.request_repaint();
}); });
} }
@ -303,10 +311,10 @@ impl WrapApp {
let r = db::Interface::read(&s.env, &s.handle, crate::CREDENTIAL_KEY); let r = db::Interface::read(&s.env, &s.handle, crate::CREDENTIAL_KEY);
if r == utils::empty_string() { if r == utils::empty_string() {
log::debug!("credential not found"); log::debug!("credential not found");
let _= tx.send(false); let _ = tx.send(false);
ctx.request_repaint(); ctx.request_repaint();
} else { } else {
let _= tx.send(true); let _ = tx.send(true);
ctx.request_repaint(); ctx.request_repaint();
break; break;
} }
@ -327,7 +335,7 @@ fn send_inc_lock_timer_req(tx: Sender<bool>, ctx: egui::Context) {
log::debug!("starting the lock screen timer"); log::debug!("starting the lock screen timer");
loop { loop {
tokio::time::sleep(std::time::Duration::from_secs(1)).await; tokio::time::sleep(std::time::Duration::from_secs(1)).await;
let _= tx.send(true); let _ = tx.send(true);
ctx.request_repaint(); ctx.request_repaint();
} }
}); });

View file

@ -0,0 +1,6 @@
# cargo +nightly fmt
comment_width = 100
format_code_in_doc_comments = true
imports_granularity = "Crate"
imports_layout = "Vertical"
wrap_comments = true

View file

@ -1,30 +1,41 @@
use rocket::http::Status; use rocket::{
use rocket::response::status::Custom; get,
use rocket::serde::json::Json; http::Status,
use rocket::{get, post}; post,
response::status::Custom,
serde::json::Json,
};
use nevmes_core::{auth, message, models::*, proof, reqres}; use nevmes_core::{
auth,
message,
models::*,
proof,
reqres,
};
/// Send message /// Send message
#[post("/", data="<m_req>")] #[post("/", data = "<m_req>")]
pub async fn send_message pub async fn send_message(
(m_req: Json<Message>, token: proof::PaymentProof) -> Custom<Json<Message>> { m_req: Json<Message>,
token: proof::PaymentProof,
) -> Custom<Json<Message>> {
let res: Message = message::create(m_req, token.get_jwp()).await; let res: Message = message::create(m_req, token.get_jwp()).await;
Custom(Status::Ok, Json(res)) Custom(Status::Ok, Json(res))
} }
/// Return all messages /// Return all messages
#[get("/")] #[get("/")]
pub async fn get_messages pub async fn get_messages(_token: auth::BearerToken) -> Custom<Json<Vec<Message>>> {
(_token: auth::BearerToken) -> Custom<Json<Vec<Message>>> {
let messages = message::find_all(); let messages = message::find_all();
Custom(Status::Ok, Json(messages)) Custom(Status::Ok, Json(messages))
} }
/// decrypt a message body /// decrypt a message body
#[get("/<mid>")] #[get("/<mid>")]
pub async fn decrypt pub async fn decrypt(
(mid: String, _token: auth::BearerToken mid: String,
_token: auth::BearerToken,
) -> Custom<Json<reqres::DecryptedMessageBody>> { ) -> Custom<Json<reqres::DecryptedMessageBody>> {
let d_message = message::decrypt_body(mid); let d_message = message::decrypt_body(mid);
Custom(Status::Ok, Json(d_message)) Custom(Status::Ok, Json(d_message))

View file

@ -1,8 +1,8 @@
#[macro_use] #[macro_use]
extern crate rocket; extern crate rocket;
use nevmes_message::*;
use nevmes_core::*; use nevmes_core::*;
use nevmes_message::*;
// The only changes in here should be mounting new controller methods // The only changes in here should be mounting new controller methods

8
scripts/fmtall.sh Executable file
View file

@ -0,0 +1,8 @@
#!/bin/bash
# Run from the nevmes root
cd nevmes-auth && cargo +nightly fmt
cd ../nevmes-contact && cargo +nightly fmt
cd ../nevmes-core && cargo +nightly fmt
cd ../nevmes-gui && cargo +nightly fmt
cd ../nevmes-message && cargo +nightly fmt
cd ../ && cargo +nightly fmt

View file

@ -1,9 +1,20 @@
use rocket::http::Status; use rocket::{
use rocket::response::status::Custom; get,
use rocket::serde::json::Json; http::Status,
use rocket::{get, post}; post,
response::status::Custom,
serde::json::Json,
};
use nevmes_core::{contact, i2p, message, models, monero, proof, reqres}; use nevmes_core::{
contact,
i2p,
message,
models,
monero,
proof,
reqres,
};
// JSON APIs exposed over i2p // JSON APIs exposed over i2p

View file

@ -1,28 +1,42 @@
#[macro_use] #[macro_use]
extern crate rocket; extern crate rocket;
use rocket::http::Status; use rocket::{
use rocket::serde::json::Json; http::Status,
use rocket::response::status::Custom; response::status::Custom,
serde::json::Json,
};
use nevmes::*; use nevmes::*;
use nevmes_core::*; use nevmes_core::*;
#[catch(402)] #[catch(402)]
fn payment_required() -> Custom<Json<reqres::ErrorResponse>> { fn payment_required() -> Custom<Json<reqres::ErrorResponse>> {
Custom(Status::PaymentRequired, Custom(
Json(reqres::ErrorResponse { error: String::from("Payment required") })) Status::PaymentRequired,
Json(reqres::ErrorResponse {
error: String::from("Payment required"),
}),
)
} }
#[catch(404)] #[catch(404)]
fn not_found() -> Custom<Json<reqres::ErrorResponse>> { fn not_found() -> Custom<Json<reqres::ErrorResponse>> {
Custom(Status::NotFound, Custom(
Json(reqres::ErrorResponse { error: String::from("Resource does not exist") })) Status::NotFound,
Json(reqres::ErrorResponse {
error: String::from("Resource does not exist"),
}),
)
} }
#[catch(500)] #[catch(500)]
fn internal_error() -> Custom<Json<reqres::ErrorResponse>> { fn internal_error() -> Custom<Json<reqres::ErrorResponse>> {
Custom(Status::InternalServerError, Custom(
Json(reqres::ErrorResponse { error: String::from("Internal server error") })) Status::InternalServerError,
Json(reqres::ErrorResponse {
error: String::from("Internal server error"),
}),
)
} }
// The only changes below here should be mounting new controller methods // The only changes below here should be mounting new controller methods