update order module serialization

This commit is contained in:
kn0sys 2024-09-06 09:23:02 -04:00
parent 84c5044975
commit f8570df9a6
No known key found for this signature in database
GPG key ID: 3BDB674C95F103FA
4 changed files with 216 additions and 165 deletions

View file

@ -5,10 +5,16 @@ use thiserror::Error;
#[derive(Debug, Error)]
#[error("Neveko error. See logs for more info.")]
pub enum NevekoError {
///J4I2PRS(J4RsError),
Auth,
Contact,
Database(MdbError),
Dispute,
I2P,
J4I2PRS,
MoneroRpc,
MoneroDaemon,
Nasr,
Order,
Product,
Unknown,
}

View file

@ -3,17 +3,9 @@
use std::error::Error;
use crate::{
contact,
db,
i2p,
models::*,
monero,
neveko25519,
order,
product,
reqres,
utils,
contact, db, error::NevekoError, i2p, models::*, monero, neveko25519, order, product, reqres, utils
};
use kn0sys_lmdb_rs::MdbError;
use log::{
debug,
error,
@ -42,7 +34,7 @@ impl StatusType {
}
/// Create a intial order
pub async fn create(j_order: Json<reqres::OrderRequest>) -> Order {
pub async fn create(j_order: Json<reqres::OrderRequest>) -> Result<Order, MdbError> {
info!("creating order");
let wallet_name = String::from(crate::APP_NAME);
let wallet_password =
@ -70,95 +62,109 @@ pub async fn create(j_order: Json<reqres::OrderRequest>) -> Order {
if !m_wallet {
error!("error creating msig wallet for order {}", &orid);
monero::close_wallet(&orid, &wallet_password).await;
return Default::default();
return Err(MdbError::NotFound);
}
monero::close_wallet(&orid, &order_wallet_password).await;
debug!("insert order: {:?}", &new_order);
let s = db::DatabaseEnvironment::async_open().await;
let s = db::DatabaseEnvironment::open()?;
// inject adjudicator separately, modifying the order model is mendokusai
let adjudicator_k = format!("{}-{}", crate::ADJUDICATOR_DB_KEY, &orid);
db::DatabaseEnvironment::async_write(&s.env, &s.handle, &adjudicator_k, &j_order.adjudicator).await;
db::write_chunks(&s.env, &s.handle?, adjudicator_k.as_bytes(), j_order.adjudicator.as_bytes());
let k = &new_order.orid;
db::DatabaseEnvironment::async_write(&s.env, &s.handle, k, &Order::to_db(&new_order)).await;
let s = db::DatabaseEnvironment::open()?;
let order = bincode::serialize(&new_order).unwrap_or_default();
db::write_chunks(&s.env, &s.handle?, k.as_bytes(), &order);
// in order to retrieve all orders, write keys to with ol
let list_key = crate::ORDER_LIST_DB_KEY;
let r = db::DatabaseEnvironment::async_read(&s.env, &s.handle, &String::from(list_key)).await;
let s = db::DatabaseEnvironment::open()?;
let r = db::DatabaseEnvironment::read(&s.env, &s.handle?, &list_key.as_bytes().to_vec())?;
if r.is_empty() {
debug!("creating order index");
}
let order_list = [r, String::from(&orid)].join(",");
let old: String = bincode::deserialize(&r[..]).unwrap_or_default();
let order_list = [old, String::from(&orid)].join(",");
debug!("writing order index {} for id: {}", order_list, list_key);
db::DatabaseEnvironment::async_write(&s.env, &s.handle, &String::from(list_key), &order_list).await;
new_order
let s = db::DatabaseEnvironment::open()?;
db::write_chunks(&s.env, &s.handle?, list_key.as_bytes(), &order_list.as_bytes());
Ok(new_order)
}
/// Backup order for customer
pub fn backup(order: &Order) {
pub fn backup(order: &Order) -> Result<(), MdbError> {
info!("creating backup of order: {}", order.orid);
let s = db::DatabaseEnvironment::open();
let s = db::DatabaseEnvironment::open()?;
let k = &order.orid;
db::DatabaseEnvironment::delete(&s.env, &s.handle, k);
db::DatabaseEnvironment::write(&s.env, &s.handle, k, &Order::to_db(order));
db::DatabaseEnvironment::delete(&s.env, &s.handle?, k.as_bytes());
let s = db::DatabaseEnvironment::open()?;
let order_to_db = bincode::serialize(&order).unwrap_or_default();
db::write_chunks(&s.env, &s.handle?, k.as_bytes(), &order_to_db);
// in order to retrieve all orders, write keys to with col
let list_key = crate::CUSTOMER_ORDER_LIST_DB_KEY;
let r = db::DatabaseEnvironment::read(&s.env, &s.handle, &String::from(list_key));
let s = db::DatabaseEnvironment::open()?;
let r = db::DatabaseEnvironment::read(&s.env, &s.handle?, &list_key.as_bytes().to_vec())?;
if r.is_empty() {
debug!("creating customer order index");
}
let mut order_list = [String::from(&r), String::from(&order.orid)].join(",");
let d_r: String = bincode::deserialize(&r[..]).unwrap_or_default();
let mut order_list = [String::from(&d_r), String::from(&order.orid)].join(",");
// don't duplicate order ids when backing up updates from vendor
if String::from(&r).contains(&String::from(&order.orid)) {
order_list = r;
if String::from(&d_r).contains(&String::from(&order.orid)) {
order_list = d_r;
}
debug!("writing order index {} for id: {}", order_list, list_key);
db::DatabaseEnvironment::write(&s.env, &s.handle, &String::from(list_key), &order_list);
let s = db::DatabaseEnvironment::open()?;
db::write_chunks(&s.env, &s.handle?, list_key.as_bytes(), order_list.as_bytes());
Ok(())
}
/// Lookup order
pub fn find(oid: &String) -> Order {
pub fn find(oid: &String) -> Result<Order, MdbError> {
info!("find order: {}", &oid);
let s = db::DatabaseEnvironment::open();
let r = db::DatabaseEnvironment::read(&s.env, &s.handle, &String::from(oid));
let s = db::DatabaseEnvironment::open()?;
let r = db::DatabaseEnvironment::read(&s.env, &s.handle?, &oid.as_bytes().to_vec())?;
if r.is_empty() {
error!("order not found");
return Default::default();
return Err(MdbError::NotFound);
}
Order::from_db(String::from(oid), r)
let result: Order = bincode::deserialize(&r[..]).unwrap_or_default();
Ok(result)
}
/// Lookup all orders from admin server
pub fn find_all() -> Vec<Order> {
let i_s = db::DatabaseEnvironment::open();
pub fn find_all() -> Result<Vec<Order>, MdbError> {
let i_s = db::DatabaseEnvironment::open()?;
let i_list_key = crate::ORDER_LIST_DB_KEY;
let i_r = db::DatabaseEnvironment::read(&i_s.env, &i_s.handle, &String::from(i_list_key));
let i_r = db::DatabaseEnvironment::read(&i_s.env, &i_s.handle?, &i_list_key.as_bytes().to_vec())?;
if i_r.is_empty() {
error!("order index not found");
}
let i_v_oid = i_r.split(",");
let de: String = bincode::deserialize(&i_r[..]).unwrap_or_default();
let i_v_oid = de.split(",");
let i_v: Vec<String> = i_v_oid.map(String::from).collect();
let mut orders: Vec<Order> = Vec::new();
for o in i_v {
let order: Order = find(&o);
let order: Order = find(&o)?;
if !order.orid.is_empty() {
orders.push(order);
}
}
orders
Ok(orders)
}
/// Lookup all orders that customer has saved from gui
pub fn find_all_backup() -> Vec<Order> {
let i_s = db::DatabaseEnvironment::open();
pub fn find_all_backup() -> Result<Vec<Order>, MdbError> {
let i_s = db::DatabaseEnvironment::open()?;
let i_list_key = crate::CUSTOMER_ORDER_LIST_DB_KEY;
let i_r = db::DatabaseEnvironment::read(&i_s.env, &i_s.handle, &String::from(i_list_key));
let i_r = db::DatabaseEnvironment::read(&i_s.env, &i_s.handle?, &i_list_key.as_bytes().to_vec())?;
if i_r.is_empty() {
error!("customer order index not found");
}
let i_v_oid = i_r.split(",");
let de: String = bincode::deserialize(&i_r[..]).unwrap_or_default();
let i_v_oid = de.split(",");
let i_v: Vec<String> = i_v_oid.map(String::from).collect();
let mut orders: Vec<Order> = Vec::new();
for o in i_v {
let order: Order = find(&o);
let order: Order = find(&o)?;
let visible = !order.orid.is_empty()
&& order.status != order::StatusType::Delivered.value()
&& order.status != order::StatusType::Cancelled.value();
@ -166,45 +172,47 @@ pub fn find_all_backup() -> Vec<Order> {
orders.push(order);
}
}
orders
Ok(orders)
}
/// Lookup all orders for customer
pub async fn find_all_customer_orders(cid: String) -> Vec<Order> {
pub async fn find_all_customer_orders(cid: String) -> Result<Vec<Order>, MdbError> {
info!("lookup orders for customer: {}", &cid);
let i_s = db::DatabaseEnvironment::open();
let i_s = db::DatabaseEnvironment::open()?;
let i_list_key = crate::ORDER_LIST_DB_KEY;
let i_r = db::DatabaseEnvironment::read(&i_s.env, &i_s.handle, &String::from(i_list_key));
let i_r = db::DatabaseEnvironment::read(&i_s.env, &i_s.handle?, &i_list_key.as_bytes().to_vec())?;
if i_r.is_empty() {
error!("order index not found");
}
let i_v_oid = i_r.split(",");
let de: String = bincode::deserialize(&i_r[..]).unwrap_or_default();
let i_v_oid = de.split(",");
let i_v: Vec<String> = i_v_oid.map(String::from).collect();
let mut orders: Vec<Order> = Vec::new();
for o in i_v {
let order: Order = find(&o);
let order: Order = find(&o)?;
if !order.orid.is_empty() && order.cid == cid {
orders.push(order);
}
}
orders
Ok(orders)
}
/// Lookup all orders for vendor
pub fn find_all_vendor_orders() -> Vec<Order> {
pub fn find_all_vendor_orders() -> Result<Vec<Order>, MdbError> {
info!("lookup orders for vendor");
let i_s = db::DatabaseEnvironment::open();
let i_s = db::DatabaseEnvironment::open()?;
let i_list_key = crate::ORDER_LIST_DB_KEY;
let i_r = db::DatabaseEnvironment::read(&i_s.env, &i_s.handle, &String::from(i_list_key));
let i_r = db::DatabaseEnvironment::read(&i_s.env, &i_s.handle?, &i_list_key.as_bytes().to_vec())?;
if i_r.is_empty() {
error!("order index not found");
}
let i_v_oid = i_r.split(",");
let de: String = bincode::deserialize(&i_r[..]).unwrap_or_default();
let i_v_oid = de.split(",");
let i_v: Vec<String> = i_v_oid.map(String::from).collect();
let mut orders: Vec<Order> = Vec::new();
let vendor_b32: String = i2p::get_destination(None);
for o in i_v {
let order: Order = find(&o);
let order: Order = find(&o)?;
if !order.orid.is_empty() && order.cid != vendor_b32 {
// TODO(c2m): separate functionality for archived orders
if order.status != order::StatusType::Cancelled.value()
@ -214,22 +222,24 @@ pub fn find_all_vendor_orders() -> Vec<Order> {
}
}
}
orders
Ok(orders)
}
/// Modify order from admin server
pub fn modify(o: Json<Order>) -> Order {
pub fn modify(o: Json<Order>) -> Result<Order, MdbError> {
info!("modify order: {}", &o.orid);
let f_order: Order = find(&o.orid);
let f_order: Order = find(&o.orid)?;
if f_order.orid.is_empty() {
error!("order not found");
return Default::default();
return Err(MdbError::NotFound);
}
let u_order = Order::update(String::from(&f_order.orid), &o);
let s = db::DatabaseEnvironment::open();
db::DatabaseEnvironment::delete(&s.env, &s.handle, &u_order.orid);
db::DatabaseEnvironment::write(&s.env, &s.handle, &u_order.orid, &Order::to_db(&u_order));
u_order
let s = db::DatabaseEnvironment::open()?;
db::DatabaseEnvironment::delete(&s.env, &s.handle?, &u_order.orid.as_bytes());
let v = bincode::serialize(&u_order).unwrap_or_default();
let s = db::DatabaseEnvironment::open()?;
db::write_chunks(&s.env, &s.handle?, &u_order.orid.as_bytes(), &v);
Ok(u_order)
}
/// Sign and submit multisig
@ -259,12 +269,12 @@ pub async fn sign_and_submit_multisig(
/// access
///
/// the details of said order.
pub async fn secure_retrieval(orid: &String, signature: &String) -> Order {
pub async fn secure_retrieval(orid: &String, signature: &String) -> Result<Order, NevekoError> {
info!("secure order retrieval for {}", orid);
// get customer address for NEVEKO NOT order wallet
let m_order: Order = find(orid);
let m_order: Order = find(orid).map_err(|_| NevekoError::Order)?;
let mut xmr_address: String = String::new();
let a_customers: Vec<Contact> = contact::find_all();
let a_customers: Vec<Contact> = contact::find_all().map_err(|_| NevekoError::Contact)?;
for customer in a_customers {
if customer.i2p_address == m_order.cid {
xmr_address = customer.xmr_address;
@ -281,20 +291,20 @@ pub async fn secure_retrieval(orid: &String, signature: &String) -> Order {
let is_valid_signature = monero::verify(xmr_address, id, sig).await;
monero::close_wallet(&wallet_name, &wallet_password).await;
if !is_valid_signature {
return Default::default();
return Err(NevekoError::Order);
}
m_order
Ok(m_order)
}
/// In order for the order (...ha) to only be cancelled by the customer
///
/// they must sign the order id with their NEVEKO wallet instance.
pub async fn cancel_order(orid: &String, signature: &String) -> Order {
pub async fn cancel_order(orid: &String, signature: &String) -> Result<Order, NevekoError> {
info!("cancel order {}", orid);
// get customer address for NEVEKO NOT order wallet
let mut m_order: Order = find(orid);
let mut m_order: Order = find(orid).map_err(|_| NevekoError::Order)?;
let mut xmr_address: String = String::new();
let a_customers: Vec<Contact> = contact::find_all();
let a_customers: Vec<Contact> = contact::find_all().map_err(|_| NevekoError::Contact)?;
for customer in a_customers {
if customer.i2p_address == m_order.cid {
xmr_address = customer.xmr_address;
@ -311,27 +321,29 @@ pub async fn cancel_order(orid: &String, signature: &String) -> Order {
let is_valid_signature = monero::verify(xmr_address, id, sig).await;
monero::close_wallet(&wallet_name, &wallet_password).await;
if !is_valid_signature {
return Default::default();
return Err(NevekoError::Order);
}
// update the order status and send to customer
m_order.status = order::StatusType::Cancelled.value();
order::modify(Json(m_order));
order::find(orid)
order::find(orid).map_err(|_| NevekoError::Order)
}
/// Check for import multisig info, validate block time and that the
///
/// order wallet has been funded properly. Update the order to multisig complete
pub async fn validate_order_for_ship(orid: &String) -> reqres::FinalizeOrderResponse {
pub async fn validate_order_for_ship(orid: &String) -> Result<reqres::FinalizeOrderResponse, NevekoError> {
info!("validating order for shipment");
let m_order: Order = find(orid);
let contact: Contact = contact::find(&m_order.cid);
let m_order: Order = find(orid).map_err(|_| NevekoError::Order)?;
let contact: Contact = contact::find(&m_order.cid).map_err(|_| NevekoError::Contact)?;
let hex_nmpk: String = contact.nmpk;
let s = db::DatabaseEnvironment::async_open().await;
let s = db::DatabaseEnvironment::open().map_err(|_| NevekoError::Database(MdbError::Panic))?;
let k = String::from(crate::DELIVERY_INFO_DB_KEY);
let delivery_info: String = db::DatabaseEnvironment::async_read(&s.env, &s.handle, &k).await;
let mut j_order: Order = find(orid);
let m_product: Product = product::find(&m_order.pid);
let handle = &s.handle.map_err(|_| NevekoError::Database(MdbError::Panic))?;
let delivery_info = db::DatabaseEnvironment::read(&s.env, handle, &k.as_bytes().to_vec())
.map_err(|_| NevekoError::Database(MdbError::Panic))?;
let mut j_order: Order = find(orid).map_err(|_| NevekoError::Order)?;
let m_product: Product = product::find(&m_order.pid).map_err(|_| NevekoError::Product)?;
let price = m_product.price;
let total = price * &m_order.quantity;
let wallet_password = String::new();
@ -346,17 +358,18 @@ pub async fn validate_order_for_ship(orid: &String) -> reqres::FinalizeOrderResp
j_order.status = StatusType::Shipped.value();
order::modify(Json(j_order));
}
let d_info: String = bincode::deserialize(&delivery_info[..]).unwrap_or_default();
let e_delivery_info: String = neveko25519::cipher(
&hex_nmpk,
hex::encode(delivery_info),
hex::encode(d_info),
Some(String::from(neveko25519::ENCIPHER)),
)
.await;
reqres::FinalizeOrderResponse {
Ok(reqres::FinalizeOrderResponse {
orid: String::from(orid),
delivery_info: e_delivery_info,
vendor_update_success: false,
}
})
}
/// NASR (neveko auto-ship request)
@ -405,10 +418,10 @@ pub async fn trigger_nasr(
pub async fn upload_delivery_info(
orid: &String,
delivery_info: &String,
) -> reqres::FinalizeOrderResponse {
) -> Result<reqres::FinalizeOrderResponse, NevekoError> {
info!("uploading delivery info");
let lookup: Order = order::find(orid);
let contact: Contact = contact::find(&lookup.cid);
let lookup: Order = order::find(orid).map_err(|_| NevekoError::Order)?;
let contact: Contact = contact::find(&lookup.cid).map_err(|_| NevekoError::Contact)?;
let hex_nmpk: String = contact.nmpk;
let e_delivery_info: String = neveko25519::cipher(
&hex_nmpk,
@ -428,35 +441,42 @@ pub async fn upload_delivery_info(
monero::close_wallet(orid, &wallet_password).await;
if sweep.result.multisig_txset.is_empty() {
error!("unable to create draft txset");
return Default::default();
return Err(NevekoError::MoneroRpc);
}
// update the order
let mut m_order: Order = find(orid);
let mut m_order: Order = find(orid).map_err(|_| NevekoError::Order)?;
m_order.status = StatusType::Shipped.value();
m_order.ship_date = chrono::offset::Utc::now().timestamp();
m_order.vend_msig_txset = sweep.result.multisig_txset;
// delivery info will be stored enciphered and separate from the rest of the
// order
let s = db::DatabaseEnvironment::async_open().await;
let s = db::DatabaseEnvironment::open().map_err(|_| NevekoError::Database(MdbError::Panic))?;
let k = String::from(crate::DELIVERY_INFO_DB_KEY);
db::DatabaseEnvironment::async_write(&s.env, &s.handle, &k, &hex::encode(delivery_info)).await;
let handle = &s.handle.map_err(|_| NevekoError::Database(MdbError::Panic))?;
db::write_chunks(&s.env, handle, k.as_bytes(), delivery_info.as_bytes());
modify(Json(m_order));
// trigger nasr, this will cause the customer's neveko instance to request the
// txset
let i2p_address = i2p::get_destination(None);
let s = db::DatabaseEnvironment::open();
// get jwp from db
let s = db::DatabaseEnvironment::open().map_err(|_| NevekoError::Database(MdbError::Panic))?;
let handle = &s.handle.map_err(|_| NevekoError::Database(MdbError::Panic))?;
let k = format!("{}-{}", crate::FTS_JWP_DB_KEY, &lookup.cid);
let jwp = db::DatabaseEnvironment::read(&s.env, &s.handle, &k);
let nasr_order = trigger_nasr(&lookup.cid, &i2p_address, &jwp, orid).await;
let jwp = db::DatabaseEnvironment::read(&s.env, handle, &k.as_bytes().to_vec())
.map_err(|_| NevekoError::Database(MdbError::Panic))?;
let str_jwp: String = bincode::deserialize(&jwp[..]).unwrap_or_default();
let nasr_order = trigger_nasr(&lookup.cid, &i2p_address, &str_jwp, orid).await;
if nasr_order.is_err() {
return Default::default();
}
reqres::FinalizeOrderResponse {
delivery_info: e_delivery_info,
orid: String::from(orid),
vendor_update_success: false,
error!("failed to trigger nasr");
return Err(NevekoError::Nasr);
}
Ok(
reqres::FinalizeOrderResponse {
delivery_info: e_delivery_info,
orid: String::from(orid),
vendor_update_success: false,
}
)
}
/// Vendor will very txset submission and then update the order to `Delivered`
@ -464,13 +484,13 @@ pub async fn upload_delivery_info(
/// status type. Then customer will update the status on the neveko instanced
///
/// upon a `vendor_update_success: true` response
pub async fn finalize_order(orid: &String) -> reqres::FinalizeOrderResponse {
pub async fn finalize_order(orid: &String) -> Result< reqres::FinalizeOrderResponse, NevekoError> {
info!("finalizing order: {}", orid);
// verify recipient and unlock time
let mut m_order: Order = order::find(orid);
let mut m_order: Order = order::find(orid).map_err(|_| NevekoError::Order)?;
if m_order.vend_msig_txset.is_empty() {
error!("txset missing");
return Default::default();
return Err(NevekoError::MoneroRpc);
}
// get draft payment txset
let wallet_password = String::new();
@ -489,22 +509,24 @@ pub async fn finalize_order(orid: &String) -> reqres::FinalizeOrderResponse {
if !valid {
monero::close_wallet(orid, &wallet_password).await;
error!("invalid txset");
return Default::default();
return Err(NevekoError::MoneroRpc);
}
// 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();
return Err(NevekoError::MoneroRpc);
}
monero::close_wallet(orid, &wallet_password).await;
m_order.status = order::StatusType::Delivered.value();
order::modify(Json(m_order));
reqres::FinalizeOrderResponse {
vendor_update_success: true,
..Default::default()
}
Ok(
reqres::FinalizeOrderResponse {
vendor_update_success: true,
..Default::default()
}
)
}
/// Executes POST /order/finalize/{orid}
@ -551,41 +573,44 @@ pub async fn trigger_finalize_request(
contact: &String,
jwp: &String,
orid: &String,
) -> reqres::FinalizeOrderResponse {
) -> Result<reqres::FinalizeOrderResponse, NevekoError> {
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();
return Err(NevekoError::Order);
}
let unwrap: reqres::FinalizeOrderResponse = finalize.unwrap();
let mut m_order: Order = order::find(orid);
let mut m_order: Order = order::find(orid).map_err(|_| NevekoError::Order)?;
m_order.status = order::StatusType::Delivered.value();
backup(&m_order);
unwrap
Ok(unwrap)
}
/// Decomposition trigger for `finalize_order()`
pub async fn d_trigger_finalize_request(
contact: &String,
orid: &String,
) -> reqres::FinalizeOrderResponse {
) -> Result<reqres::FinalizeOrderResponse, NevekoError> {
// ugh, sorry seems we need to get jwp for vendor from fts cache
// get jwp from db
let s = db::DatabaseEnvironment::async_open().await;
let s = db::DatabaseEnvironment::open().map_err(|_| NevekoError::Database(MdbError::NotFound))?;
let k = format!("{}-{}", crate::FTS_JWP_DB_KEY, &contact);
let jwp = db::DatabaseEnvironment::async_read(&s.env, &s.handle, &k).await;
let handle = &s.handle.map_err(|_| NevekoError::Database(MdbError::Panic))?;
let jwp = db::DatabaseEnvironment::read(&s.env, handle, &k.as_bytes().to_vec())
.map_err(|_| NevekoError::Database(MdbError::Panic))?;
info!("executing d_trigger_finalize_request");
// request finalize if the order status is shipped
let order: Order = order::find(orid);
let order: Order = order::find(orid).map_err(|_| NevekoError::Order)?;
let str_jwp: String = bincode::deserialize(&jwp[..]).unwrap_or_default();
if order.status != order::StatusType::Shipped.value() {
let trigger = trigger_finalize_request(contact, &jwp, orid).await;
let trigger = trigger_finalize_request(contact, &str_jwp, orid).await?;
if trigger.vendor_update_success {
return trigger;
return Ok(trigger);
}
}
Default::default()
Err(NevekoError::Order)
}
/// Send order request to vendor and start multisig flow
@ -735,27 +760,34 @@ pub async fn trigger_cancel_request(contact: &String, jwp: &String, orid: &Strin
}
/// Decomposition trigger for the shipping request
pub async fn d_trigger_ship_request(contact: &String, orid: &String) -> Order {
pub async fn d_trigger_ship_request(contact: &String, orid: &String) -> Result<Order, NevekoError> {
// ugh, sorry seems we need to get jwp for vendor from fts cache
// get jwp from db
let s = db::DatabaseEnvironment::async_open().await;
let s = db::DatabaseEnvironment::open()
.map_err(|_| NevekoError::Database(MdbError::Panic))?;
let k = format!("{}-{}", crate::FTS_JWP_DB_KEY, &contact);
let jwp = db::DatabaseEnvironment::async_read(&s.env, &s.handle, &k).await;
let handle = &s.handle.map_err(|_| NevekoError::Database(MdbError::Panic))?;
let jwp = db::DatabaseEnvironment::read(&s.env, handle, &k.as_bytes().to_vec())
.map_err(|_| NevekoError::Database(MdbError::Panic))?;
info!("executing d_trigger_ship_request");
// request shipment if the order status is MultisigComplete
let trigger = trigger_ship_request(contact, &jwp, orid).await;
let str_jwp: String = bincode::deserialize(&jwp[..]).unwrap_or_default();
let trigger = trigger_ship_request(contact, &str_jwp, orid).await;
if trigger.status == order::StatusType::MulitsigComplete.value() {
let ship_res = transmit_ship_request(contact, &jwp, orid).await;
let ship_res = transmit_ship_request(contact, &str_jwp, orid).await;
if ship_res.is_err() {
error!("failure to decompose trigger_ship_request");
return Default::default();
return Err(NevekoError::Order);
}
let u_ship_res = ship_res.unwrap_or(Default::default());
let hex_delivery_info: String = hex::encode(u_ship_res.delivery_info);
let key = format!("{}-{}", crate::DELIVERY_INFO_DB_KEY, orid);
db::DatabaseEnvironment::write(&s.env, &s.handle, &key, &hex_delivery_info);
let s = db::DatabaseEnvironment::open()
.map_err(|_| NevekoError::Database(MdbError::Panic))?;
let handle = &s.handle.map_err(|_| NevekoError::Database(MdbError::Panic))?;
db::write_chunks(&s.env, handle, key.as_bytes(), hex_delivery_info.as_bytes());
}
trigger
Ok(trigger)
}
/// Executes POST /order/cancel/orid/signature
@ -800,22 +832,26 @@ pub async fn transmit_cancel_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) -> Result<Order, NevekoError> {
// ugh, sorry seems we need to get jwp for vendor from fts cache
// get jwp from db
let s = db::DatabaseEnvironment::async_open().await;
let s = db::DatabaseEnvironment::open()
.map_err(|_| NevekoError::Database(MdbError::Panic))?;
let k = format!("{}-{}", crate::FTS_JWP_DB_KEY, &contact);
let jwp = db::DatabaseEnvironment::async_read(&s.env, &s.handle, &k).await;
let handle = &s.handle.map_err(|_| NevekoError::Database(MdbError::Panic))?;
let jwp = db::DatabaseEnvironment::read(&s.env, handle, &k.as_bytes().to_vec())
.map_err(|_| NevekoError::Database(MdbError::Panic))?;
info!("executing d_trigger_cancel_request");
// request cancel if the order status is not MultisigComplete
let order: Order = order::find(orid);
let order: Order = order::find(orid).map_err(|_| NevekoError::Order)?;
if order.status != order::StatusType::MulitsigComplete.value() {
let trigger = trigger_cancel_request(contact, &jwp, orid).await;
let str_jwp: String = bincode::deserialize(&jwp[..]).unwrap_or_default();
let trigger = trigger_cancel_request(contact, &str_jwp, orid).await;
if trigger.status == order::StatusType::Cancelled.value() {
return trigger;
return Ok(trigger);
}
}
Default::default()
Err(NevekoError::Order)
}
pub async fn init_adjudicator_wallet(orid: &String) {

View file

@ -5,6 +5,7 @@ use crate::{
models::*,
utils,
};
use kn0sys_lmdb_rs::MdbError;
use log::{
debug,
error,
@ -14,11 +15,11 @@ use rocket::serde::json::Json;
use std::error::Error;
/// Create a new product
pub fn create(d: Json<Product>) -> Product {
pub fn create(d: Json<Product>) -> Result<Product, MdbError> {
let pid: String = format!("{}{}", crate::PRODUCT_DB_KEY, utils::generate_rnd());
if !validate_product(&d) {
error!("invalid product");
return Default::default();
return Err(MdbError::NotFound);
}
let new_product = Product {
pid: String::from(&pid),
@ -30,71 +31,79 @@ pub fn create(d: Json<Product>) -> Product {
qty: d.qty,
};
debug!("insert product: {:?}", &new_product);
let s = db::DatabaseEnvironment::open();
let s = db::DatabaseEnvironment::open()?;
let k = &new_product.pid;
db::DatabaseEnvironment::write(&s.env, &s.handle, k, &Product::to_db(&new_product));
let product = bincode::serialize(&new_product).unwrap_or_default();
db::write_chunks(&s.env, &s.handle?, k.as_bytes(), &product);
// in order to retrieve all products, write keys to with pl
let list_key = crate::PRODUCT_LIST_DB_KEY;
let r = db::DatabaseEnvironment::read(&s.env, &s.handle, &String::from(list_key));
let s = db::DatabaseEnvironment::open()?;
let r = db::DatabaseEnvironment::read(&s.env, &s.handle?, &list_key.as_bytes().to_vec())?;
if r.is_empty() {
debug!("creating product index");
}
let product_list = [r, String::from(&pid)].join(",");
let old: String = bincode::deserialize(&r[..]).unwrap_or_default();
let product_list = [old, String::from(&pid)].join(",");
debug!(
"writing product index {} for id: {}",
product_list, list_key
);
db::DatabaseEnvironment::write(&s.env, &s.handle, &String::from(list_key), &product_list);
new_product
let s = db::DatabaseEnvironment::open()?;
db::write_chunks(&s.env, &s.handle?, list_key.as_bytes(), product_list.as_bytes());
Ok(new_product)
}
/// Single Product lookup
pub fn find(pid: &String) -> Product {
let s = db::DatabaseEnvironment::open();
let r = db::DatabaseEnvironment::read(&s.env, &s.handle, &String::from(pid));
pub fn find(pid: &String) -> Result<Product, MdbError> {
let s = db::DatabaseEnvironment::open()?;
let r = db::DatabaseEnvironment::read(&s.env, &s.handle?, &pid.as_bytes().to_vec())?;
if r.is_empty() {
error!("product not found");
return Default::default();
return Err(MdbError::NotFound);
}
Product::from_db(String::from(pid), r)
let result: Product = bincode::deserialize(&r[..]).unwrap_or_default();
Ok(result)
}
/// Product lookup for all
pub fn find_all() -> Vec<Product> {
let i_s = db::DatabaseEnvironment::open();
pub fn find_all() -> Result<Vec<Product>, MdbError> {
let i_s = db::DatabaseEnvironment::open()?;
let i_list_key = crate::PRODUCT_LIST_DB_KEY;
let i_r = db::DatabaseEnvironment::read(&i_s.env, &i_s.handle, &String::from(i_list_key));
let i_r = db::DatabaseEnvironment::read(&i_s.env, &i_s.handle?, &i_list_key.as_bytes().to_vec())?;
if i_r.is_empty() {
error!("product index not found");
}
let i_v_pid = i_r.split(",");
let str_r: String = bincode::deserialize(&i_r[..]).unwrap_or_default();
let i_v_pid = str_r.split(",");
let i_v: Vec<String> = i_v_pid.map(String::from).collect();
let mut products: Vec<Product> = Vec::new();
for p in i_v {
let mut product: Product = find(&p);
let mut product: Product = find(&p)?;
if !product.pid.is_empty() {
// don't return images
product.image = Vec::new();
products.push(product);
}
}
products
Ok(products)
}
/// Modify product
pub fn modify(p: Json<Product>) -> Product {
pub fn modify(p: Json<Product>) -> Result<Product, MdbError> {
// TODO(c2m): don't allow modification to products with un-delivered orders
info!("modify product: {}", &p.pid);
let f_prod: Product = find(&p.pid);
let f_prod: Product = find(&p.pid)?;
if f_prod.pid.is_empty() {
error!("product not found");
return Default::default();
return Err(MdbError::NotFound);
}
let u_prod = Product::update(f_prod, &p);
let s = db::DatabaseEnvironment::open();
db::DatabaseEnvironment::delete(&s.env, &s.handle, &u_prod.pid);
db::DatabaseEnvironment::write(&s.env, &s.handle, &u_prod.pid, &Product::to_db(&u_prod));
u_prod
let s = db::DatabaseEnvironment::open()?;
db::DatabaseEnvironment::delete(&s.env, &s.handle?, u_prod.pid.as_bytes())?;
let product = bincode::serialize(&u_prod).unwrap_or_default();
let s = db::DatabaseEnvironment::open()?;
db::write_chunks(&s.env, &s.handle?, u_prod.pid.as_bytes(), &product);
Ok(u_prod)
}
/// check product field lengths to prevent db spam

View file

@ -52,7 +52,7 @@ mod tests {
use super::*;
fn cleanup(k: &String) -> Result<(), MdbError> {
let s = db::DatabaseEnvironment::open("test")?;
let s = db::DatabaseEnvironment::open()?;
db::DatabaseEnvironment::delete(&s.env, &s.handle?, k.as_bytes());
Ok(())
}
@ -64,7 +64,7 @@ mod tests {
"73a4nWuvkYoYoksGurDjKZQcZkmaxLaKbbeiKzHnMmqKivrCzq5Q2JtJG1UZNZFqLPbQ3MiXCk2Q5bdwdUNSr7X9QrPubkn"
);
let test_user = create(&address);
let s = db::DatabaseEnvironment::open("test")?;
let s = db::DatabaseEnvironment::open()?;
let r = db::DatabaseEnvironment::read(&s.env, &s.handle?, &test_user.uid);
let id = String::from(&test_user.uid);
let cleanup_id = String::from(&test_user.uid);
@ -83,7 +83,7 @@ mod tests {
xmr_address: address,
..Default::default()
};
let s = db::DatabaseEnvironment::open("test")?;
let s = db::DatabaseEnvironment::open()?;
db::DatabaseEnvironment::write_chunks(&s.env, &s.handle?, k, &User::to_db(&expected_user));
let actual_user: User = find(&String::from(k));
assert_eq!(expected_user.xmr_address, actual_user.xmr_address);