submit txset and finalize order - untested

This commit is contained in:
creating2morrow 2023-12-15 19:56:33 -05:00
parent bb492fce8c
commit 6340119435
5 changed files with 154 additions and 30 deletions

View file

@ -9,8 +9,7 @@ use crate::{
monero, monero,
order, order,
product, product,
reqres, utils, reqres,
utils,
}; };
use log::{ use log::{
debug, debug,
@ -421,12 +420,120 @@ pub async fn upload_delivery_info(
/// upon a `vendor_update_success: true` response /// upon a `vendor_update_success: true` response
pub async fn finalize_order(orid: &String) -> reqres::FinalizeOrderResponse { pub async fn finalize_order(orid: &String) -> reqres::FinalizeOrderResponse {
info!("finalizing order: {}", orid); info!("finalizing order: {}", orid);
// verify recipient and unlock time
let mut m_order: Order = order::find(orid);
if m_order.vend_msig_txset == utils::empty_string() {
error!("txset missing");
return Default::default();
}
// get draft payment txset
let wallet_password = utils::empty_string();
monero::open_wallet(orid, &wallet_password).await;
monero::refresh().await;
let address: String = String::from(&m_order.subaddress);
let m_describe = monero::describe_transfer(&m_order.vend_msig_txset).await;
let check_destination: reqres::Destination = reqres::Destination {
address,
..Default::default()
};
let valid = m_describe.result.desc[0].recepients.contains(&check_destination)
&& m_describe.result.desc[0].unlock_time < monero::LockTimeLimit::Blocks.value();
if !valid {
monero::close_wallet(orid, &wallet_password).await;
error!("invalid txset");
return Default::default();
}
// verify order wallet has been swept clean
let balance = monero::get_balance().await;
if balance.result.unlocked_balance != 0 {
monero::close_wallet(orid, &wallet_password).await;
error!("order wallet not swept");
return Default::default();
}
m_order.status = order::StatusType::Delivered.value();
order::modify(Json(m_order));
reqres::FinalizeOrderResponse { reqres::FinalizeOrderResponse {
vendor_update_success: true,
..Default::default() ..Default::default()
} }
} }
/// Executes POST /order/finalize/{orid}
///
/// finalizing the order on the vendor side.
///
/// Customer needs to verify the response and update their lmdb.
///
/// see `finalize_order`
pub async fn transmit_finalize_request(
contact: &String,
jwp: &String,
orid: &String,
) -> Result<reqres::FinalizeOrderResponse, Box<dyn Error>> {
info!("executing transmit_cancel_request");
let host = utils::get_i2p_http_proxy();
let proxy = reqwest::Proxy::http(&host)?;
let client = reqwest::Client::builder().proxy(proxy).build();
match client?
.post(format!(
"http://{}/market/order/finalize/{}", contact, orid
))
.header("proof", jwp)
.send()
.await
{
Ok(response) => {
let res = response.json::<reqres::FinalizeOrderResponse>().await;
debug!("finalize order response: {:?}", res);
match res {
Ok(r) => Ok(r),
_ => Ok(Default::default()),
}
}
Err(e) => {
error!("failed to finalize order due to: {:?}", e);
Ok(Default::default())
}
}
}
/// A post-decomposition trigger for the finalize request so that the logic
///
/// can be executed from the gui.
pub async fn trigger_finalize_request(contact: &String, jwp: &String, orid: &String) -> reqres::FinalizeOrderResponse {
info!("executing trigger_finalize_request");
let finalize = transmit_finalize_request(contact, jwp, orid).await;
// cache finalize order request to db
if finalize.is_err() {
log::error!("failed to trigger cancel request");
return Default::default();
}
let unwrap: reqres::FinalizeOrderResponse = finalize.unwrap();
let mut m_order: Order = order::find(&orid);
m_order.status = order::StatusType::Delivered.value();
backup(&m_order);
unwrap
}
/// Decomposition trigger for `finalize_order()`
pub async fn d_trigger_finalize_request(contact: &String, orid: &String) -> reqres::FinalizeOrderResponse {
// ugh, sorry seems we need to get jwp for vendor from fts cache
// get jwp from db
let s = db::Interface::async_open().await;
let k = format!("{}-{}", crate::FTS_JWP_DB_KEY, &contact);
let jwp = db::Interface::async_read(&s.env, &s.handle, &k).await;
info!("executing d_trigger_finalize_request");
// request finalize if the order status is shipped
let order: Order = order::find(&orid);
if order.status != order::StatusType::Shipped.value() {
let trigger = trigger_finalize_request(contact, &jwp, orid).await;
if trigger.vendor_update_success {
return trigger;
}
}
Default::default()
}
/// Send order request to vendor and start multisig flow /// Send order request to vendor and start multisig flow
pub async fn transmit_order_request( pub async fn transmit_order_request(
contact: String, contact: String,
@ -638,7 +745,7 @@ pub async fn transmit_cancel_request(
} }
} }
/// Decomposition trigger for the shipping request /// Decomposition trigger for the cancel request
pub async fn d_trigger_cancel_request(contact: &String, orid: &String) -> Order { pub async fn d_trigger_cancel_request(contact: &String, orid: &String) -> Order {
// ugh, sorry seems we need to get jwp for vendor from fts cache // ugh, sorry seems we need to get jwp for vendor from fts cache
// get jwp from db // get jwp from db

View file

@ -106,7 +106,7 @@ pub struct XmrRpcGetTxByIdParams {
pub txid: String, pub txid: String,
} }
#[derive(Deserialize, Serialize, Debug)] #[derive(Deserialize, Serialize, Debug, PartialEq)]
pub struct Destination { pub struct Destination {
pub address: String, pub address: String,
pub amount: u128, pub amount: u128,

View file

@ -111,8 +111,8 @@ pub struct MarketApp {
_refresh_on_delete_product_rx: Receiver<bool>, _refresh_on_delete_product_rx: Receiver<bool>,
submit_order_tx: Sender<models::Order>, submit_order_tx: Sender<models::Order>,
submit_order_rx: Receiver<models::Order>, submit_order_rx: Receiver<models::Order>,
_submit_txset_tx: Sender<bool>, submit_txset_tx: Sender<bool>,
_submit_txset_rx: Receiver<bool>, submit_txset_rx: Receiver<bool>,
cancel_request_tx: Sender<models::Order>, cancel_request_tx: Sender<models::Order>,
cancel_request_rx: Receiver<models::Order>, cancel_request_rx: Receiver<models::Order>,
// ship_request_tx: Sender<models::Order>, // ship_request_tx: Sender<models::Order>,
@ -144,7 +144,7 @@ impl Default for MarketApp {
let (our_make_info_tx, our_make_info_rx) = std::sync::mpsc::channel(); let (our_make_info_tx, our_make_info_rx) = std::sync::mpsc::channel();
let (order_xmr_address_tx, order_xmr_address_rx) = std::sync::mpsc::channel(); let (order_xmr_address_tx, order_xmr_address_rx) = std::sync::mpsc::channel();
let (order_funded_tx, order_funded_rx) = std::sync::mpsc::channel(); let (order_funded_tx, order_funded_rx) = std::sync::mpsc::channel();
let (_submit_txset_tx, _submit_txset_rx) = std::sync::mpsc::channel(); let (submit_txset_tx, submit_txset_rx) = std::sync::mpsc::channel();
let contents = std::fs::read("./assets/qr.png").unwrap_or(Vec::new()); let contents = std::fs::read("./assets/qr.png").unwrap_or(Vec::new());
MarketApp { MarketApp {
contact_info_rx, contact_info_rx,
@ -217,8 +217,8 @@ impl Default for MarketApp {
// ship_request_tx, // ship_request_tx,
submit_order_rx, submit_order_rx,
submit_order_tx, submit_order_tx,
_submit_txset_rx, submit_txset_rx,
_submit_txset_tx, submit_txset_tx,
vendor_status: Default::default(), vendor_status: Default::default(),
vendors: Vec::new(), vendors: Vec::new(),
} }
@ -332,6 +332,13 @@ impl eframe::App for MarketApp {
self.is_loading = false; self.is_loading = false;
} }
if let Ok(finalized) = self.submit_txset_rx.try_recv() {
if !finalized {
log::error!("failure to finalize shipment please contact vendor")
}
self.is_loading = false;
}
// Vendor status window // Vendor status window
//----------------------------------------------------------------------------------- //-----------------------------------------------------------------------------------
let mut is_showing_vendor_status = self.is_showing_vendor_status; let mut is_showing_vendor_status = self.is_showing_vendor_status;
@ -649,8 +656,7 @@ impl eframe::App for MarketApp {
}); });
} }
if self.msig.completed_export && !self.msig.completed_shipping_request { if self.msig.completed_export && !self.msig.completed_shipping_request {
// idk if manual shipping request will be necessary with the new nasr logic, // idk if manual shipping request will be necessary with the new nasr logic
// let's see
ui.horizontal(|ui| { ui.horizontal(|ui| {
ui.label( ui.label(
RichText::new("Delivery Pending") RichText::new("Delivery Pending")
@ -698,19 +704,18 @@ impl eframe::App for MarketApp {
ui.horizontal(|ui| { ui.horizontal(|ui| {
ui.label("Release Payment: \t"); ui.label("Release Payment: \t");
if ui.button("Submit Txset").clicked() { if ui.button("Submit Txset").clicked() {
self.is_loading = true;
let vendor_prefix = String::from(crate::GUI_OVL_DB_KEY);
let vendor =
utils::search_gui_db(vendor_prefix, self.m_order.orid.clone());
// async trigger for signing and submitted the txset // async trigger for signing and submitted the txset
// do something
release_txset( release_txset(
utils::empty_string(), vendor,
utils::empty_string(), self.m_order.orid.clone(),
ctx.clone(), ctx.clone(),
utils::empty_string(), self.submit_txset_tx.clone(),
self._submit_txset_tx.clone(),
); );
} }
if ui.button("Check").clicked() {
// the multisig wallet should have a zero balance if the sweep succeeded
}
}); });
} }
// ui.horizontal(|ui| { // ui.horizontal(|ui| {
@ -2296,10 +2301,9 @@ fn validate_msig_step(
} }
fn release_txset( fn release_txset(
_contact: String, contact: String,
orid: String, orid: String,
ctx: egui::Context, ctx: egui::Context,
_jwp: String,
tx: Sender<bool>, tx: Sender<bool>,
) { ) {
tokio::spawn(async move { tokio::spawn(async move {
@ -2311,12 +2315,9 @@ fn release_txset(
let _ = tx.send(false); let _ = tx.send(false);
return; return;
} }
// TODO(c2m): we need to build an API that tells the vendor let finalize = order::d_trigger_finalize_request(&contact, &orid).await;
// to verify the txset was submitted successfully
// return boolean.
// update order to delivered if success // update order to delivered if success
let _ = tx.send(true); let _ = tx.send(finalize.vendor_update_success);
ctx.request_repaint(); ctx.request_repaint();
todo!() todo!()
}); });

View file

@ -12,6 +12,7 @@ use rocket::{
use neveko_core::*; use neveko_core::*;
// JSON APIs exposed over i2p // JSON APIs exposed over i2p
// Take care not to put any admin APIs inside of here
/// Get payment API version /// Get payment API version
/// ///
@ -236,14 +237,28 @@ pub async fn cancel_order(
if m_order.cid == utils::empty_string() { if m_order.cid == utils::empty_string() {
return Custom(Status::BadRequest, Json(Default::default())); return Custom(Status::BadRequest, Json(Default::default()));
} }
Custom(Status::Created, Json(m_order)) Custom(Status::Ok, Json(m_order))
} }
/// Create a dispute (customer) /// Customer finalize order logic. Vendor updates order
///
/// to `Delivered` status.
///
/// Protected: true
#[post("/order/finalize/<orid>")]
pub async fn finalize_order(orid: String, _jwp: proof::PaymentProof) -> Custom<Json<reqres::FinalizeOrderResponse>> {
let finalize = order::finalize_order(&orid).await;
if !finalize.vendor_update_success {
return Custom(Status::BadRequest, Json(Default::default()));
}
Custom(Status::Ok, Json(finalize))
}
/// Create a dispute
#[post("/create", data = "<dispute>")] #[post("/create", data = "<dispute>")]
pub async fn create_dispute( pub async fn create_dispute(
dispute: Json<models::Dispute>, dispute: Json<models::Dispute>,
_token: auth::BearerToken, _jwp: proof::PaymentProof,
) -> Custom<Json<models::Dispute>> { ) -> Custom<Json<models::Dispute>> {
let m_dispute: models::Dispute = dispute::create(dispute); let m_dispute: models::Dispute = dispute::create(dispute);
Custom(Status::Ok, Json(m_dispute)) Custom(Status::Ok, Json(m_dispute))

View file

@ -50,6 +50,7 @@ async fn rocket() -> _ {
controller::request_shipment, controller::request_shipment,
controller::retrieve_order, controller::retrieve_order,
controller::trigger_nasr, controller::trigger_nasr,
controller::finalize_order,
], ],
) )
} }