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)] #[derive(Debug, Error)]
#[error("Neveko error. See logs for more info.")] #[error("Neveko error. See logs for more info.")]
pub enum NevekoError { pub enum NevekoError {
///J4I2PRS(J4RsError), Auth,
Contact,
Database(MdbError), Database(MdbError),
Dispute, Dispute,
I2P,
J4I2PRS,
MoneroRpc, MoneroRpc,
MoneroDaemon, MoneroDaemon,
Nasr,
Order,
Product,
Unknown, Unknown,
} }

View file

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

View file

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

View file

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