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;
monero::close_wallet(&wallet_name, &wallet_password).await;
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;
Contact {
cid: utils::empty_string(),

View file

@ -24,7 +24,7 @@ pub fn find_key() -> Result<String, Box<dyn Error>> {
let mode = KeyListMode::LOCAL;
let mut ctx = Context::from_protocol(proto)?;
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 k: String = utils::empty_string();
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");
let mut ctx = Context::from_protocol(Protocol::OpenPgp)?;
ctx.set_armor(true);
let name = i2p::get_destination();
let name = i2p::get_destination(None);
let keys = {
let mut key_iter = ctx.find_keys([&name])?;
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>> {
let name = i2p::get_destination();
let name = i2p::get_destination(None);
let data = format!(
"%no-protection
Key-Type: RSA
@ -168,7 +168,7 @@ pub fn sign_key(key: &str) -> Result<(), Box<dyn Error>> {
let key_to_sign = k2s_ctx
.get_key(k)
.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 {
let key = k2s_ctx
.get_secret_key(app_key)

View file

@ -1,5 +1,6 @@
use crate::{
args,
monero,
utils,
};
use clap::Parser;
@ -72,7 +73,9 @@ async fn find_tunnels() {
debug!("i2p tunnels: {}", contents);
let has_app_tunnel = contents.contains(&format!("{}", app_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_tx_proxy_tunnel = contents.contains(&format!("{}", &tx_proxy_port));
if !has_app_tunnel || !has_http_tunnel {
tokio::time::sleep(Duration::new(120, 0)).await;
}
@ -84,6 +87,9 @@ async fn find_tunnels() {
debug!("creating http tunnel");
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,
@ -129,6 +135,22 @@ fn create_tunnel() {
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
fn get_i2p_proxy_port() -> String {
let proxy_host = utils::get_i2p_http_proxy();
@ -151,10 +173,12 @@ fn create_http_proxy() {
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`.
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!(
"/home/{}/.i2p-zero/config/tunnels.json",
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 tunnels: Vec<Tunnel> = j.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());
}
}

View file

@ -22,6 +22,8 @@ pub const EXCHANGE_MSIG: &str = "exchange";
pub const EXPORT_MSIG: &str = "export";
pub const MAKE_MSIG: &str = "make";
pub const PREPARE_MSIG: &str = "prepare";
pub const SIGN_MSIG: &str = "sign";
pub const VALID_MSIG_MSG_LENGTH: usize = 4;
#[derive(PartialEq)]
pub enum MessageType {
@ -35,6 +37,16 @@ struct MultisigMessageData {
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
pub async fn create(m: Json<Message>, jwp: String, m_type: MessageType) -> Message {
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 {
mid: String::from(&f_mid),
uid: String::from(&m.uid),
from: i2p::get_destination(),
from: i2p::get_destination(None),
body: e_body,
created,
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 values = decoded.split(":");
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 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();
debug!("zero decryption bytes: {:?}", bytes);
MultisigMessageData {
@ -136,7 +153,10 @@ fn parse_multisig_message(mid: String) -> MultisigMessageData {
/// decrypted for convenience sake. The client must determine which
///
/// .b32.i2p address belongs to the vendor / mediator.
///
///
/// The result should be a string that needs to be decomposed into a
///
/// vector.
/// ### Example
///
/// ```rust
@ -144,7 +164,9 @@ fn parse_multisig_message(mid: String) -> MultisigMessageData {
/// use neveko_core::db;
/// let s = db::Interface::open();
/// 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>) {
// 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 {
return;
}
let f_mid: String = format!("m{}", utils::generate_rnd());
let f_mid: String = format!("msig{}", utils::generate_rnd());
let new_message = Message {
mid: String::from(&f_mid),
uid: String::from("rx"),
@ -176,6 +198,7 @@ pub async fn rx_multisig(m: Json<Message>) {
&data.sub_type, &data.orid
);
// 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);
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);
j.mid.len() < utils::string_limit()
&& j.body.len() < utils::message_limit()
&& j.to == i2p::get_destination()
&& j.to == i2p::get_destination(None)
&& j.uid.len() < utils::string_limit()
}

View file

@ -2,7 +2,7 @@ use crate::{
args,
proof,
reqres,
utils,
utils, i2p,
};
use clap::Parser;
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
///
/// default: /home/$USER/monero-xxx-xxx
@ -119,7 +121,10 @@ pub fn start_daemon() {
let blockchain_dir = get_blockchain_dir();
let bin_dir = get_monero_location();
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 {
let args = ["--data-dir", &blockchain_dir, "--stagenet", "--detach"];
let output = Command::new(format!("{}/monerod", bin_dir))
@ -128,7 +133,15 @@ pub fn start_daemon() {
.expect("monerod failed to start");
debug!("{:?}", output.stdout);
} 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))
.args(args)
.spawn()
@ -200,6 +213,19 @@ fn get_rpc_port() -> String {
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
fn get_blockchain_dir() -> String {
let args = args::Args::parse();

View file

@ -15,7 +15,9 @@ use rocket::serde::json::Json;
/*
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
*/
@ -156,12 +158,12 @@ pub fn modify(o: Json<Order>) -> Order {
pub async fn sign_and_submit_multisig(
orid: &String,
tx_data_hex: &String) -> reqres::XmrRpcSubmitMultisigResponse {
info!("signin and submitting multisig");
info!("signing and submitting multisig");
let r_sign: reqres::XmrRpcSignMultisigResponse =
monero::sign_multisig(String::from(tx_data_hex)).await;
let r_submit: reqres::XmrRpcSubmitMultisigResponse =
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);
}
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(),
uid: utils::empty_string(),
created: 0,
from: i2p::get_destination(),
from: i2p::get_destination(None),
};
let j_message = utils::message_to_json(&m);
tokio::spawn(async move {

View file

@ -297,7 +297,7 @@ impl eframe::App for HomeApp {
ui.horizontal(|ui| {
self.logo_i2p.show(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));
});
});

View file

@ -79,7 +79,7 @@ pub async fn gen_jwp(proof: Json<proof::TxProof>) -> Custom<Json<reqres::Jwp>> {
/// Get all products
///
/// Protected: false
/// Protected: true
#[get("/products")]
pub async fn get_products(_jwp: proof::PaymentProof) -> Custom<Json<Vec<models::Product>>> {
let m_products: Vec<models::Product> = product::find_all();
@ -149,3 +149,19 @@ pub async fn rx_multisig_message(
message::rx_multisig(message).await;
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()))
}