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,
order,
product,
reqres,
utils,
utils, reqres,
};
use log::{
debug,
@ -421,12 +420,120 @@ pub async fn upload_delivery_info(
/// upon a `vendor_update_success: true` response
pub async fn finalize_order(orid: &String) -> reqres::FinalizeOrderResponse {
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 {
vendor_update_success: true,
..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
pub async fn transmit_order_request(
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 {
// ugh, sorry seems we need to get jwp for vendor from fts cache
// get jwp from db

View file

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

View file

@ -111,8 +111,8 @@ pub struct MarketApp {
_refresh_on_delete_product_rx: Receiver<bool>,
submit_order_tx: Sender<models::Order>,
submit_order_rx: Receiver<models::Order>,
_submit_txset_tx: Sender<bool>,
_submit_txset_rx: Receiver<bool>,
submit_txset_tx: Sender<bool>,
submit_txset_rx: Receiver<bool>,
cancel_request_tx: Sender<models::Order>,
cancel_request_rx: Receiver<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 (order_xmr_address_tx, order_xmr_address_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());
MarketApp {
contact_info_rx,
@ -217,8 +217,8 @@ impl Default for MarketApp {
// ship_request_tx,
submit_order_rx,
submit_order_tx,
_submit_txset_rx,
_submit_txset_tx,
submit_txset_rx,
submit_txset_tx,
vendor_status: Default::default(),
vendors: Vec::new(),
}
@ -332,6 +332,13 @@ impl eframe::App for MarketApp {
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
//-----------------------------------------------------------------------------------
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 {
// idk if manual shipping request will be necessary with the new nasr logic,
// let's see
// idk if manual shipping request will be necessary with the new nasr logic
ui.horizontal(|ui| {
ui.label(
RichText::new("Delivery Pending")
@ -698,19 +704,18 @@ impl eframe::App for MarketApp {
ui.horizontal(|ui| {
ui.label("Release Payment: \t");
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
// do something
release_txset(
utils::empty_string(),
utils::empty_string(),
vendor,
self.m_order.orid.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| {
@ -2296,10 +2301,9 @@ fn validate_msig_step(
}
fn release_txset(
_contact: String,
contact: String,
orid: String,
ctx: egui::Context,
_jwp: String,
tx: Sender<bool>,
) {
tokio::spawn(async move {
@ -2311,12 +2315,9 @@ fn release_txset(
let _ = tx.send(false);
return;
}
// TODO(c2m): we need to build an API that tells the vendor
// to verify the txset was submitted successfully
// return boolean.
let finalize = order::d_trigger_finalize_request(&contact, &orid).await;
// update order to delivered if success
let _ = tx.send(true);
let _ = tx.send(finalize.vendor_update_success);
ctx.request_repaint();
todo!()
});

View file

@ -12,6 +12,7 @@ use rocket::{
use neveko_core::*;
// JSON APIs exposed over i2p
// Take care not to put any admin APIs inside of here
/// Get payment API version
///
@ -236,14 +237,28 @@ pub async fn cancel_order(
if m_order.cid == utils::empty_string() {
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>")]
pub async fn create_dispute(
dispute: Json<models::Dispute>,
_token: auth::BearerToken,
_jwp: proof::PaymentProof,
) -> Custom<Json<models::Dispute>> {
let m_dispute: models::Dispute = dispute::create(dispute);
Custom(Status::Ok, Json(m_dispute))

View file

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