crate tx proxy tunnel on app startup

This commit is contained in:
creating2morrow 2023-06-04 12:30:30 -04:00
parent 29a19bdfbc
commit ffee9de424
9 changed files with 114 additions and 23 deletions

View file

@ -113,7 +113,7 @@ pub async fn share() -> Contact {
let m_address: reqres::XmrRpcAddressResponse = monero::get_address().await; let m_address: reqres::XmrRpcAddressResponse = monero::get_address().await;
monero::close_wallet(&wallet_name, &wallet_password).await; monero::close_wallet(&wallet_name, &wallet_password).await;
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(None);
let xmr_address = m_address.result.address; let xmr_address = m_address.result.address;
Contact { Contact {
cid: utils::empty_string(), cid: utils::empty_string(),

View file

@ -24,7 +24,7 @@ pub fn find_key() -> Result<String, Box<dyn Error>> {
let mode = KeyListMode::LOCAL; let mode = KeyListMode::LOCAL;
let mut ctx = Context::from_protocol(proto)?; let mut ctx = Context::from_protocol(proto)?;
ctx.set_key_list_mode(mode)?; ctx.set_key_list_mode(mode)?;
let name = i2p::get_destination(); let name = i2p::get_destination(None);
let mut keys = ctx.find_keys([&name])?; let mut keys = ctx.find_keys([&name])?;
let mut k: String = utils::empty_string(); let mut k: String = utils::empty_string();
for key in keys.by_ref().filter_map(|x| x.ok()) { for key in keys.by_ref().filter_map(|x| x.ok()) {
@ -56,7 +56,7 @@ pub fn export_key() -> Result<Vec<u8>, Box<dyn Error>> {
info!("exporting public key"); info!("exporting public key");
let mut ctx = Context::from_protocol(Protocol::OpenPgp)?; let mut ctx = Context::from_protocol(Protocol::OpenPgp)?;
ctx.set_armor(true); ctx.set_armor(true);
let name = i2p::get_destination(); let name = i2p::get_destination(None);
let keys = { let keys = {
let mut key_iter = ctx.find_keys([&name])?; let mut key_iter = ctx.find_keys([&name])?;
let keys: Vec<_> = key_iter.by_ref().collect::<Result<_, _>>()?; let keys: Vec<_> = key_iter.by_ref().collect::<Result<_, _>>()?;
@ -132,7 +132,7 @@ pub fn decrypt(mid: &String, body: &Vec<u8>) -> Result<Vec<u8>, Box<dyn Error>>
} }
pub fn write_gen_batch() -> Result<(), Box<dyn Error>> { pub fn write_gen_batch() -> Result<(), Box<dyn Error>> {
let name = i2p::get_destination(); let name = i2p::get_destination(None);
let data = format!( let data = format!(
"%no-protection "%no-protection
Key-Type: RSA Key-Type: RSA
@ -168,7 +168,7 @@ pub fn sign_key(key: &str) -> Result<(), Box<dyn Error>> {
let key_to_sign = k2s_ctx let key_to_sign = k2s_ctx
.get_key(k) .get_key(k)
.map_err(|e| format!("no key matched given key-id: {:?}", e))?; .map_err(|e| format!("no key matched given key-id: {:?}", e))?;
let name = Some(i2p::get_destination()); let name = Some(i2p::get_destination(None));
if let Some(app_key) = name { if let Some(app_key) = name {
let key = k2s_ctx let key = k2s_ctx
.get_secret_key(app_key) .get_secret_key(app_key)

View file

@ -1,5 +1,6 @@
use crate::{ use crate::{
args, args,
monero,
utils, utils,
}; };
use clap::Parser; use clap::Parser;
@ -72,7 +73,9 @@ async fn find_tunnels() {
debug!("i2p tunnels: {}", contents); debug!("i2p tunnels: {}", contents);
let has_app_tunnel = contents.contains(&format!("{}", app_port)); let has_app_tunnel = contents.contains(&format!("{}", app_port));
let proxy_port = get_i2p_proxy_port(); let proxy_port = get_i2p_proxy_port();
let tx_proxy_port = monero::get_daemon_port();
let has_http_tunnel = contents.contains(&proxy_port); let has_http_tunnel = contents.contains(&proxy_port);
let has_tx_proxy_tunnel = contents.contains(&format!("{}", &tx_proxy_port));
if !has_app_tunnel || !has_http_tunnel { if !has_app_tunnel || !has_http_tunnel {
tokio::time::sleep(Duration::new(120, 0)).await; tokio::time::sleep(Duration::new(120, 0)).await;
} }
@ -84,6 +87,9 @@ async fn find_tunnels() {
debug!("creating http tunnel"); debug!("creating http tunnel");
create_http_proxy(); create_http_proxy();
} }
if !has_tx_proxy_tunnel && !utils::is_using_remote_node() {
create_tx_proxy_tunnel();
}
} }
/// Called on application startup for i2p tunnel creation, /// Called on application startup for i2p tunnel creation,
@ -129,6 +135,22 @@ fn create_tunnel() {
debug!("{:?}", output.stdout); debug!("{:?}", output.stdout);
} }
/// Create an i2p tunnel for the monero tx proxy
fn create_tx_proxy_tunnel() {
info!("creating monerod tx proxy tunnel");
let args = args::Args::parse();
let path = args.i2p_zero_dir;
let output = Command::new(format!("{}/router/bin/tunnel-control.sh", path))
.args([
"server.create",
"127.0.0.1",
&format!("{}", monero::get_daemon_port()),
])
.spawn()
.expect("i2p-zero failed to create a tunnel");
debug!("{:?}", output.stdout);
}
/// Extract i2p port from command line arg /// Extract i2p port from command line arg
fn get_i2p_proxy_port() -> String { fn get_i2p_proxy_port() -> String {
let proxy_host = utils::get_i2p_http_proxy(); let proxy_host = utils::get_i2p_http_proxy();
@ -151,10 +173,12 @@ fn create_http_proxy() {
debug!("{:?}", output.stdout); debug!("{:?}", output.stdout);
} }
/// This is the `dest` value of the app i2p tunnel /// This is the `dest` value of the app i2p tunnels
/// ///
/// in `tunnels-config.json`. /// in `tunnels-config.json`.
pub fn get_destination() -> String { ///
/// `port` - the port of the tunnel (e.g. `utils::get_app_port()`)
pub fn get_destination(port: Option<u16>) -> String {
let file_path = format!( let file_path = format!(
"/home/{}/.i2p-zero/config/tunnels.json", "/home/{}/.i2p-zero/config/tunnels.json",
env::var("USER").unwrap_or(String::from("user")) env::var("USER").unwrap_or(String::from("user"))
@ -170,7 +194,7 @@ pub fn get_destination() -> String {
let mut destination: String = utils::empty_string(); let mut destination: String = utils::empty_string();
let tunnels: Vec<Tunnel> = j.tunnels; let tunnels: Vec<Tunnel> = j.tunnels;
for tunnel in tunnels { for tunnel in tunnels {
if tunnel.port == format!("{}", utils::get_app_port()) { if tunnel.port == format!("{}", port.unwrap_or(utils::get_app_port())) {
destination = tunnel.dest.unwrap_or(utils::empty_string()); destination = tunnel.dest.unwrap_or(utils::empty_string());
} }
} }

View file

@ -22,6 +22,8 @@ pub const EXCHANGE_MSIG: &str = "exchange";
pub const EXPORT_MSIG: &str = "export"; pub const EXPORT_MSIG: &str = "export";
pub const MAKE_MSIG: &str = "make"; pub const MAKE_MSIG: &str = "make";
pub const PREPARE_MSIG: &str = "prepare"; pub const PREPARE_MSIG: &str = "prepare";
pub const SIGN_MSIG: &str = "sign";
pub const VALID_MSIG_MSG_LENGTH: usize = 4;
#[derive(PartialEq)] #[derive(PartialEq)]
pub enum MessageType { pub enum MessageType {
@ -35,6 +37,16 @@ struct MultisigMessageData {
orid: String, orid: String,
} }
impl Default for MultisigMessageData {
fn default() -> Self {
MultisigMessageData {
info: utils::empty_string(),
sub_type: utils::empty_string(),
orid: utils::empty_string(),
}
}
}
/// Create a new message /// Create a new message
pub async fn create(m: Json<Message>, jwp: String, m_type: MessageType) -> Message { pub async fn create(m: Json<Message>, jwp: String, m_type: MessageType) -> Message {
let rnd = utils::generate_rnd(); let rnd = utils::generate_rnd();
@ -50,7 +62,7 @@ pub async fn create(m: Json<Message>, jwp: String, m_type: MessageType) -> Messa
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),
from: i2p::get_destination(), from: i2p::get_destination(None),
body: e_body, body: e_body,
created, created,
to: String::from(&m.to), to: String::from(&m.to),
@ -117,9 +129,14 @@ fn parse_multisig_message(mid: String) -> MultisigMessageData {
let decoded = String::from_utf8(bytes).unwrap_or(utils::empty_string()); let decoded = String::from_utf8(bytes).unwrap_or(utils::empty_string());
let values = decoded.split(":"); let values = decoded.split(":");
let mut v: Vec<String> = values.map(|s| String::from(s)).collect(); let mut v: Vec<String> = values.map(|s| String::from(s)).collect();
if v.len() != VALID_MSIG_MSG_LENGTH {
return Default::default();
}
let sub_type: String = v.remove(0); let sub_type: String = v.remove(0);
let orid: String = v.remove(0); let orid: String = v.remove(0);
let info: String = v.remove(0); let customer_info: String = v.remove(0);
let mediator_info: String = v.remove(0);
let info = format!("{}:{}", customer_info, mediator_info);
bytes = Vec::new(); bytes = Vec::new();
debug!("zero decryption bytes: {:?}", bytes); debug!("zero decryption bytes: {:?}", bytes);
MultisigMessageData { MultisigMessageData {
@ -136,7 +153,10 @@ fn parse_multisig_message(mid: String) -> MultisigMessageData {
/// decrypted for convenience sake. The client must determine which /// decrypted for convenience sake. The client must determine which
/// ///
/// .b32.i2p address belongs to the vendor / mediator. /// .b32.i2p address belongs to the vendor / mediator.
/// ///
/// The result should be a string that needs to be decomposed into a
///
/// vector.
/// ### Example /// ### Example
/// ///
/// ```rust /// ```rust
@ -144,7 +164,9 @@ fn parse_multisig_message(mid: String) -> MultisigMessageData {
/// use neveko_core::db; /// use neveko_core::db;
/// let s = db::Interface::open(); /// let s = db::Interface::open();
/// let key = "prepare-o123-test.b32.i2p"; /// let key = "prepare-o123-test.b32.i2p";
/// db::Interface::read(&s.env, &s.handle, &key); /// let info_str = db::Interface::read(&s.env, &s.handle, &key);
/// let info_split = info_str.split(":");
/// let mut v_info: Vec<String> = info_split.map(|s| String::from(s)).collect();
/// ``` /// ```
pub async fn rx_multisig(m: Json<Message>) { pub async fn rx_multisig(m: Json<Message>) {
// make sure the message isn't something strange // make sure the message isn't something strange
@ -157,7 +179,7 @@ pub async fn rx_multisig(m: Json<Message>) {
if !is_in_contact_list { if !is_in_contact_list {
return; return;
} }
let f_mid: String = format!("m{}", utils::generate_rnd()); let f_mid: String = format!("msig{}", utils::generate_rnd());
let new_message = Message { let new_message = Message {
mid: String::from(&f_mid), mid: String::from(&f_mid),
uid: String::from("rx"), uid: String::from("rx"),
@ -176,6 +198,7 @@ pub async fn rx_multisig(m: Json<Message>) {
&data.sub_type, &data.orid &data.sub_type, &data.orid
); );
// lookup msig message data by {type}-{order id}-{contact .b32.i2p address} // lookup msig message data by {type}-{order id}-{contact .b32.i2p address}
// store info as {customer_info}:{mediator_info}
let msig_key = format!("{}-{}-{}", &data.sub_type, &data.orid, &m.from); let msig_key = format!("{}-{}-{}", &data.sub_type, &data.orid, &m.from);
db::Interface::async_write(&s.env, &s.handle, &msig_key, &data.info).await; db::Interface::async_write(&s.env, &s.handle, &msig_key, &data.info).await;
} }
@ -428,7 +451,7 @@ fn validate_message(j: &Json<Message>) -> bool {
info!("validating message: {}", &j.mid); info!("validating message: {}", &j.mid);
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(None)
&& j.uid.len() < utils::string_limit() && j.uid.len() < utils::string_limit()
} }

View file

@ -2,7 +2,7 @@ use crate::{
args, args,
proof, proof,
reqres, reqres,
utils, utils, i2p,
}; };
use clap::Parser; use clap::Parser;
use diqwest::WithDigestAuth; use diqwest::WithDigestAuth;
@ -111,6 +111,8 @@ impl LockTimeLimit {
} }
} }
// TODO(c2m): make inbound connections for i2p tx proxy configurable
/// Start monerod from the -`-monero-location` flag /// Start monerod from the -`-monero-location` flag
/// ///
/// default: /home/$USER/monero-xxx-xxx /// default: /home/$USER/monero-xxx-xxx
@ -119,7 +121,10 @@ pub fn start_daemon() {
let blockchain_dir = get_blockchain_dir(); let blockchain_dir = get_blockchain_dir();
let bin_dir = get_monero_location(); let bin_dir = get_monero_location();
let release_env = utils::get_release_env(); let release_env = utils::get_release_env();
let tx_proxy = utils::get_i2p_http_proxy(); let tx_proxy = format!("i2p,{}", utils::get_i2p_http_proxy());
let port = get_daemon_port();
let destination = i2p::get_destination(Some(port));
let anon_inbound = format!("{}:{},8", destination, port);
if release_env == utils::ReleaseEnvironment::Development { if release_env == utils::ReleaseEnvironment::Development {
let args = ["--data-dir", &blockchain_dir, "--stagenet", "--detach"]; let args = ["--data-dir", &blockchain_dir, "--stagenet", "--detach"];
let output = Command::new(format!("{}/monerod", bin_dir)) let output = Command::new(format!("{}/monerod", bin_dir))
@ -128,7 +133,15 @@ pub fn start_daemon() {
.expect("monerod failed to start"); .expect("monerod failed to start");
debug!("{:?}", output.stdout); debug!("{:?}", output.stdout);
} else { } else {
let args = ["--data-dir", &blockchain_dir, "--detach", "--tx-proxy", &tx_proxy]; let args = ["
--data-dir",
&blockchain_dir,
"--tx-proxy",
&tx_proxy,
"--anonymous-inbound",
&anon_inbound,
"--detach",
];
let output = Command::new(format!("{}/monerod", bin_dir)) let output = Command::new(format!("{}/monerod", bin_dir))
.args(args) .args(args)
.spawn() .spawn()
@ -200,6 +213,19 @@ fn get_rpc_port() -> String {
port port
} }
pub fn get_daemon_port() -> u16 {
let args = args::Args::parse();
let rpc = String::from(args.monero_rpc_daemon);
let values = rpc.split(":");
let mut v: Vec<String> = values.map(|s| String::from(s)).collect();
let port = v.remove(2);
debug!("monerod port: {}", port);
match port.parse::<u16>() {
Ok(p) => p,
Err(_) => 0,
}
}
/// Get monero rpc host from command line argument /// Get monero rpc host from command line argument
fn get_blockchain_dir() -> String { fn get_blockchain_dir() -> String {
let args = args::Args::parse(); let args = args::Args::parse();

View file

@ -15,7 +15,9 @@ use rocket::serde::json::Json;
/* /*
TODOs(c2m): TODOs(c2m):
- API to valid payment and import multisig info - API to validate payment and import multisig info
- API to upload gpg encrypted tracking number
release tracking (locker code?) when txset is released
- update order status - update order status
*/ */
@ -156,12 +158,12 @@ pub fn modify(o: Json<Order>) -> Order {
pub async fn sign_and_submit_multisig( pub async fn sign_and_submit_multisig(
orid: &String, orid: &String,
tx_data_hex: &String) -> reqres::XmrRpcSubmitMultisigResponse { tx_data_hex: &String) -> reqres::XmrRpcSubmitMultisigResponse {
info!("signin and submitting multisig"); info!("signing and submitting multisig");
let r_sign: reqres::XmrRpcSignMultisigResponse = let r_sign: reqres::XmrRpcSignMultisigResponse =
monero::sign_multisig(String::from(tx_data_hex)).await; monero::sign_multisig(String::from(tx_data_hex)).await;
let r_submit: reqres::XmrRpcSubmitMultisigResponse = let r_submit: reqres::XmrRpcSubmitMultisigResponse =
monero::submit_multisig(r_sign.result.tx_data_hex).await; monero::submit_multisig(r_sign.result.tx_data_hex).await;
if r_submit.result.tx_hash_list.len() == 0 { if r_submit.result.tx_hash_list.is_empty() {
error!("unable to submit payment for order: {}", orid); error!("unable to submit payment for order: {}", orid);
} }
r_submit r_submit

View file

@ -781,7 +781,7 @@ fn send_message_req(tx: Sender<bool>, ctx: egui::Context, body: String, to: Stri
mid: utils::empty_string(), mid: utils::empty_string(),
uid: utils::empty_string(), uid: utils::empty_string(),
created: 0, created: 0,
from: i2p::get_destination(), from: i2p::get_destination(None),
}; };
let j_message = utils::message_to_json(&m); let j_message = utils::message_to_json(&m);
tokio::spawn(async move { tokio::spawn(async move {

View file

@ -297,7 +297,7 @@ impl eframe::App for HomeApp {
ui.horizontal(|ui| { ui.horizontal(|ui| {
self.logo_i2p.show(ui); self.logo_i2p.show(ui);
ui.horizontal(|ui| { ui.horizontal(|ui| {
let i2p_address = i2p::get_destination(); let i2p_address = i2p::get_destination(None);
ui.label(format!("- status: {}\n- address: {}", str_i2p_status, i2p_address)); ui.label(format!("- status: {}\n- address: {}", str_i2p_status, i2p_address));
}); });
}); });

View file

@ -79,7 +79,7 @@ pub async fn gen_jwp(proof: Json<proof::TxProof>) -> Custom<Json<reqres::Jwp>> {
/// Get all products /// Get all products
/// ///
/// Protected: false /// Protected: true
#[get("/products")] #[get("/products")]
pub async fn get_products(_jwp: proof::PaymentProof) -> Custom<Json<Vec<models::Product>>> { pub async fn get_products(_jwp: proof::PaymentProof) -> Custom<Json<Vec<models::Product>>> {
let m_products: Vec<models::Product> = product::find_all(); let m_products: Vec<models::Product> = product::find_all();
@ -149,3 +149,19 @@ pub async fn rx_multisig_message(
message::rx_multisig(message).await; message::rx_multisig(message).await;
Custom(Status::Ok, Json(Default::default())) Custom(Status::Ok, Json(Default::default()))
} }
/// Customer can request shipment after the wallet is funded
///
/// with the amount of the order. The vendor will then request export
///
/// multisig info, check balance and sanity check `unlock_time`.
///
/// Protected: true
#[post("/", data = "<message>")]
pub async fn request_shipment(
_jwp: proof::PaymentProof,
message: Json<models::Message>,
) -> Custom<Json<models::Message>> {
//validate_order_for_ship();
Custom(Status::Ok, Json(Default::default()))
}