From 74811ee2e683fdd92c01d92805caf5ee67e42aeb Mon Sep 17 00:00:00 2001 From: creating2morrow Date: Fri, 17 Nov 2023 03:54:35 -0500 Subject: [PATCH] update mediator order injection and import multisig info --- README.md | 1 + neveko-core/src/lib.rs | 1 + neveko-core/src/message.rs | 25 +++++++++- neveko-core/src/models.rs | 1 - neveko-core/src/order.rs | 13 +++-- neveko-core/src/reqres.rs | 2 + neveko-core/src/utils.rs | 1 + neveko-gui/src/apps/market.rs | 91 ++++++++++++++++++----------------- src/controller.rs | 4 +- 9 files changed, 85 insertions(+), 54 deletions(-) diff --git a/README.md b/README.md index 0117a94..f12c1d4 100644 --- a/README.md +++ b/README.md @@ -100,6 +100,7 @@ NEVidebla-EKOnomia (invisible economy) * can be overriden with remote node * use the `--remote-node` flag * [monero-wallet-rpc](https://www.getmonero.org/downloads/#cli) - (not included) interface for xmr wallet ops +* [monero-wallet-cli](https://www.getmonero.org/downloads/#cli) - enable experimental multisig * [i2p-zero](https://github.com/creating2morrow/i2p-zero/releases/tag/v1.21-neveko) - (not included) tunnel creation and http proxy most of the complex logic stays in neveko-core, exported from [lib.rs](./neveko-core/src/lib.rs) diff --git a/neveko-core/src/lib.rs b/neveko-core/src/lib.rs index 971d821..1b54687 100644 --- a/neveko-core/src/lib.rs +++ b/neveko-core/src/lib.rs @@ -34,6 +34,7 @@ pub const PRODUCT_LIST_DB_KEY: &str = "pl"; pub const RX_MESSAGE_DB_KEY: &str = "rx"; pub const FTS_DB_KEY: &str = "fts"; pub const CUSTOMER_ORDER_LIST_DB_KEY: &str = "olc"; +pub const MEDIATOR_DB_KEY: &str = "med8"; pub const MSIG_MESSAGE_DB_KEY: &str = "msig"; pub const MSIG_MESSAGE_LIST_DB_KEY: &str = "msigl"; pub const FTS_JWP_DB_KEY: &str = "fts-jwp"; diff --git a/neveko-core/src/message.rs b/neveko-core/src/message.rs index 7063d22..006066e 100644 --- a/neveko-core/src/message.rs +++ b/neveko-core/src/message.rs @@ -7,7 +7,7 @@ use crate::{ models::*, monero, reqres, - utils, + utils, order, }; use log::{ debug, @@ -596,7 +596,28 @@ pub async fn send_export_info(orid: &String, contact: &String) { create(j_message, jwp, MessageType::Multisig).await; } -// TODO: import multisig_info +/// The customer or vendor (dispute only) needs to export +/// +/// multisig info after funding. Once the info is imported +/// +/// successfully the order needs to be updated to `MultisigComplete`. +pub async fn send_import_info(orid: &String, info: &Vec) { + let wallet_name = String::from(orid); + let wallet_password = utils::empty_string(); + monero::open_wallet(&wallet_name, &wallet_password).await; + let pre_import = monero::import_multisig_info(info.to_vec()).await; + monero::close_wallet(&orid, &wallet_password).await; + if pre_import.result.n_outputs == 0 { + error!("unable to import multisig info for order: {}", orid); + return; + } + let mut old_order = order::find(orid); + let status = order::StatusType::MulitsigComplete.value(); + old_order.status = String::from(&status); + let j_old_order = Json(old_order); + order::modify(j_old_order); + debug!("order: {} updated to: {}", orid, status); +} /// Customer begins multisig orchestration by requesting the prepare info /// diff --git a/neveko-core/src/models.rs b/neveko-core/src/models.rs index 3f93307..4425346 100644 --- a/neveko-core/src/models.rs +++ b/neveko-core/src/models.rs @@ -492,7 +492,6 @@ impl Order { Order { orid, cid: String::from(&o.cid), - // fml, the mediator .b32 isn't getting sent to vendor on order creation TODO(c2m): fix it pid: String::from(&o.pid), cust_kex_1: String::from(&o.cust_kex_1), cust_kex_2: String::from(&o.cust_kex_2), diff --git a/neveko-core/src/order.rs b/neveko-core/src/order.rs index ec75096..e4ddf86 100644 --- a/neveko-core/src/order.rs +++ b/neveko-core/src/order.rs @@ -19,7 +19,7 @@ use log::{ }; use rocket::serde::json::Json; -enum StatusType { +pub enum StatusType { _Cancelled, Delivered, MultisigMissing, @@ -74,18 +74,21 @@ pub async fn create(j_order: Json) -> Order { monero::close_wallet(&orid, &order_wallet_password).await; monero::enable_experimental_multisig(&orid); debug!("insert order: {:?}", &new_order); - let s = db::Interface::open(); + let s = db::Interface::async_open().await; + // inject mediator separately, modifying the order model is mendokusai + let mediator_k = format!("{}-{}", crate::MEDIATOR_DB_KEY, &orid); + db::Interface::async_write(&s.env, &s.handle, &mediator_k, &j_order.mediator).await; let k = &new_order.orid; - db::Interface::write(&s.env, &s.handle, k, &Order::to_db(&new_order)); + db::Interface::async_write(&s.env, &s.handle, k, &Order::to_db(&new_order)).await; // in order to retrieve all orders, write keys to with ol let list_key = crate::ORDER_LIST_DB_KEY; - let r = db::Interface::read(&s.env, &s.handle, &String::from(list_key)); + let r = db::Interface::async_read(&s.env, &s.handle, &String::from(list_key)).await; if r == utils::empty_string() { debug!("creating order index"); } let order_list = [r, String::from(&orid)].join(","); debug!("writing order index {} for id: {}", order_list, list_key); - db::Interface::write(&s.env, &s.handle, &String::from(list_key), &order_list); + db::Interface::async_write(&s.env, &s.handle, &String::from(list_key), &order_list).await; new_order } diff --git a/neveko-core/src/reqres.rs b/neveko-core/src/reqres.rs index 5e8d138..ba94c38 100644 --- a/neveko-core/src/reqres.rs +++ b/neveko-core/src/reqres.rs @@ -1179,6 +1179,7 @@ impl Default for ErrorResponse { #[serde(crate = "rocket::serde")] pub struct OrderRequest { pub cid: String, + pub mediator: String, pub pid: String, pub ship_address: Vec, pub quantity: u128, @@ -1188,6 +1189,7 @@ impl Default for OrderRequest { fn default() -> Self { OrderRequest { cid: utils::empty_string(), + mediator: utils::empty_string(), pid: utils::empty_string(), ship_address: Vec::new(), quantity: 0, diff --git a/neveko-core/src/utils.rs b/neveko-core/src/utils.rs index 7bb646a..0061d38 100644 --- a/neveko-core/src/utils.rs +++ b/neveko-core/src/utils.rs @@ -340,6 +340,7 @@ pub fn product_to_json(m: &models::Product) -> Json { pub fn order_to_json(o: &reqres::OrderRequest) -> Json { let r_order: reqres::OrderRequest = reqres::OrderRequest { cid: String::from(&o.cid), + mediator: String::from(&o.mediator), pid: String::from(&o.pid), ship_address: o.ship_address.iter().cloned().collect(), quantity: o.quantity, diff --git a/neveko-gui/src/apps/market.rs b/neveko-gui/src/apps/market.rs index 99650cc..a8efc0d 100644 --- a/neveko-gui/src/apps/market.rs +++ b/neveko-gui/src/apps/market.rs @@ -345,7 +345,7 @@ impl eframe::App for MarketApp { } }); - // Multisig Management window + // Customer Multisig Management window //----------------------------------------------------------------------------------- let mut is_managing_multisig = self.is_managing_multisig; egui::Window::new("msig") @@ -575,43 +575,42 @@ impl eframe::App for MarketApp { } }); } - // if self.msig.completed_funding && !self.msig.completed_export { - // ui.horizontal(|ui| { - // ui.label("Export Info: \t\t\t\t"); - // if ui.button("Export").clicked() { - // self.is_loading = true; - // let mediator_prefix = String::from(crate::GUI_MSIG_MEDIATOR_DB_KEY); - // let vendor_prefix = String::from(crate::GUI_OVL_DB_KEY); - // let mediator = - // utils::search_gui_db(mediator_prefix, self.m_order.orid.clone()); - // let vendor = - // utils::search_gui_db(vendor_prefix, self.m_order.orid.clone()); - // // not much orchestration here afaik, just send the output to the other participants - // // TODO(c2m): 'idk remember why this tx.clone() is being reused' but not nothing breaks for now... - // send_export_info_req( - // self.our_make_info_tx.clone(), - // ctx.clone(), - // mediator, - // &self.m_order.orid.clone(), - // vendor, - // ) - // } - // if ui.button("Check").clicked() {} - // }); - // } - // TODO(c2m): there is no API that orchestrates importing the info after customer collects it - // ui.horizontal(|ui| { - // ui.label("Import Info: \t"); - // if ui.button("Update").clicked() {} - // }); - // ui.horizontal(|ui| { - // ui.label("Release Payment: \t"); - // if ui.button("Sign Txset").clicked() {} - // }); - // ui.horizontal(|ui| { - // ui.label("Create Dispute: \t\t"); - // if ui.button("Dispute").clicked() {} - // }); + if self.msig.completed_funding && !self.msig.completed_export { + ui.horizontal(|ui| { + ui.label("Export Info: \t\t\t\t"); + if ui.button("Export").clicked() { + self.is_loading = true; + let mediator_prefix = String::from(crate::GUI_MSIG_MEDIATOR_DB_KEY); + let vendor_prefix = String::from(crate::GUI_OVL_DB_KEY); + let mediator = + utils::search_gui_db(mediator_prefix, self.m_order.orid.clone()); + let vendor = + utils::search_gui_db(vendor_prefix, self.m_order.orid.clone()); + // not much orchestration here afaik, just send the output to the other participants + // TODO(c2m): 'idk remember why this tx.clone() is being reused' but not nothing breaks for now... + send_import_info_req( + self.our_make_info_tx.clone(), + ctx.clone(), + mediator, + &self.m_order.orid.clone(), + vendor, + ) + } + if ui.button("Check").clicked() {} + }); + } + ui.horizontal(|ui| { + ui.label("Import Info: \t"); + if ui.button("Update").clicked() {} + }); + ui.horizontal(|ui| { + ui.label("Release Payment: \t"); + if ui.button("Sign Txset").clicked() {} + }); + ui.horizontal(|ui| { + ui.label("Create Dispute: \t\t"); + if ui.button("Dispute").clicked() {} + }); ui.label("\n"); if ui.button("Exit").clicked() { self.is_managing_multisig = false; @@ -748,8 +747,12 @@ impl eframe::App for MarketApp { ui.add(egui::Spinner::new()); ui.label("loading..."); } - ui.label(format!("cid: {}", self.new_order.cid)); - ui.label(format!("pid: {}", self.new_order.pid)); + let mediator_prefix = String::from(crate::GUI_MSIG_MEDIATOR_DB_KEY); + let mediator = + utils::search_gui_db(mediator_prefix, self.m_order.orid.clone()); + ui.label(format!("customer id: {}", self.new_order.cid)); + ui.label(format!("mediator id: {}", mediator)); + ui.label(format!("product id: {}", self.new_order.pid)); ui.horizontal(|ui| { let shipping_name = ui.label("shipping address: "); ui.text_edit_singleline(&mut self.new_order_shipping_address) @@ -779,6 +782,8 @@ impl eframe::App for MarketApp { gpg::encrypt(self.vendor_status.i2p.clone(), &address_bytes); let new_order = reqres::OrderRequest { cid: String::from(&self.new_order.cid), + // TODO: inject mediator for vendor dispute handling + mediator: String::from(&mediator), pid: String::from(&self.new_order.pid), ship_address: encrypted_shipping_address.unwrap_or(Vec::new()), quantity: qty, @@ -2030,7 +2035,7 @@ fn verify_order_wallet_funded(contact: &String, orid: &String, tx: Sender, }); } -fn send_export_info_req( +fn send_import_info_req( tx: Sender, ctx: egui::Context, mediator: String, @@ -2046,7 +2051,6 @@ fn send_export_info_req( let v_jwp: String = utils::search_gui_db(String::from(crate::GUI_JWP_DB_KEY), String::from(&vendor)); let wallet_password = utils::empty_string(); - monero::create_wallet(&w_orid, &wallet_password).await; let m_wallet = monero::open_wallet(&w_orid, &wallet_password).await; if !m_wallet { log::error!("failed to open wallet"); @@ -2054,7 +2058,6 @@ fn send_export_info_req( let _ = tx.send(utils::empty_string()); return; } - let export_info = monero::export_multisig_info().await; let ref_export_info: &String = &export_info.result.info; utils::write_gui_db( @@ -2089,7 +2092,7 @@ fn send_export_info_req( info: Vec::new(), init_mediator: false, kex_init: false, - msig_type: String::from(message::EXPORT_MSIG), + msig_type: String::from(message::IMPORT_MSIG), orid: String::from(v_orid), }; let _v_result = message::d_trigger_msig_info(&vendor, &v_jwp, &v_msig_request).await; diff --git a/src/controller.rs b/src/controller.rs index 9de3334..421d0cf 100644 --- a/src/controller.rs +++ b/src/controller.rs @@ -136,8 +136,6 @@ pub async fn retrieve_order( } /// Send multisig info for contact's order -/// -/// TODO: import info too /// /// Protected: true #[post("/", data = "")] @@ -156,6 +154,8 @@ pub async fn get_multisig_info( message::send_make_info(&r_info.orid, &r_info.contact, info).await; } else if r_info.msig_type == String::from(message::EXPORT_MSIG) { message::send_export_info(&r_info.orid, &r_info.contact).await; + } else if r_info.msig_type == String::from(message::EXPORT_MSIG) { + message::send_import_info(&r_info.orid, &r_info.info).await; } else { message::send_exchange_info(&r_info.orid, &r_info.contact, info, r_info.kex_init).await; }