add order validations for shipping

This commit is contained in:
creating2morrow 2023-06-05 06:07:01 -04:00
parent 3255b15c9a
commit 60786f23d4
7 changed files with 123 additions and 63 deletions

View file

@ -236,8 +236,8 @@ pub struct Product {
pub image: Vec<u8>, pub image: Vec<u8>,
pub in_stock: bool, pub in_stock: bool,
pub name: String, pub name: String,
pub price: i64, pub price: u64,
pub qty: i64, pub qty: u64,
} }
impl Default for Product { impl Default for Product {
@ -272,11 +272,11 @@ impl Product {
Err(_) => false, Err(_) => false,
}; };
let name = v.remove(0); let name = v.remove(0);
let price = match v.remove(0).parse::<i64>() { let price = match v.remove(0).parse::<u64>() {
Ok(p) => p, Ok(p) => p,
Err(_) => 0, Err(_) => 0,
}; };
let qty = match v.remove(0).parse::<i64>() { let qty = match v.remove(0).parse::<u64>() {
Ok(q) => q, Ok(q) => q,
Err(_) => 0, Err(_) => 0,
}; };

View file

@ -4,7 +4,7 @@ use crate::{
models::*, models::*,
monero, monero,
reqres, reqres,
utils, utils, product, gpg, i2p,
}; };
use log::{ use log::{
debug, debug,
@ -15,16 +15,14 @@ use rocket::serde::json::Json;
/* /*
TODOs(c2m): TODOs(c2m):
- API to validate payment and import multisig info, update to multisig complete
- API to upload gpg encrypted tracking number, update order to shipped
- release tracking (locker code?) when txset is released, update to delivered - release tracking (locker code?) when txset is released, update to delivered
*/ */
enum StatusType { enum StatusType {
_Delivered, _Delivered,
MultisigMissing, MultisigMissing,
_MulitsigComplete, MulitsigComplete,
_Shipped, Shipped,
} }
impl StatusType { impl StatusType {
@ -32,8 +30,8 @@ impl StatusType {
match *self { match *self {
StatusType::_Delivered => String::from("Delivered"), StatusType::_Delivered => String::from("Delivered"),
StatusType::MultisigMissing => String::from("MultisigMissing"), StatusType::MultisigMissing => String::from("MultisigMissing"),
StatusType::_MulitsigComplete => String::from("MulitsigComplete"), StatusType::MulitsigComplete => String::from("MulitsigComplete"),
StatusType::_Shipped => String::from("Shipped"), StatusType::Shipped => String::from("Shipped"),
} }
} }
} }
@ -175,7 +173,7 @@ pub async fn sign_and_submit_multisig(
/// that the mediator can see order id for disputes without being able to access /// that the mediator can see order id for disputes without being able to access
/// ///
/// the details of said order. /// the details of said order.
pub async fn retrieve_order(orid: &String, signature: &String) -> Order { pub async fn secure_retrieval(orid: &String, signature: &String) -> Order {
// 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);
let mut xmr_address: String = String::new(); let mut xmr_address: String = String::new();
@ -183,6 +181,7 @@ pub async fn retrieve_order(orid: &String, signature: &String) -> Order {
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;
break;
} }
} }
// send address, orid and signature to verify() // send address, orid and signature to verify()
@ -195,12 +194,57 @@ pub async fn retrieve_order(orid: &String, signature: &String) -> Order {
m_order m_order
} }
pub async fn validate_order_for_ship() -> bool { /// 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) -> bool {
info!("validating order for shipment"); info!("validating order for shipment");
let mut m_order: Order = find(orid);
let m_product: Product = product::find(&m_order.pid);
let price = m_product.price;
let total = price * m_order.quantity;
// import multisig info // import multisig info
let s = db::Interface::open();
let key = format!("export-{}-{}", orid, &m_order.cid);
let info_str = db::Interface::async_read(&s.env, &s.handle, &key).await;
let info_split = info_str.split(":");
let v_info: Vec<String> = info_split.map(|s| String::from(s)).collect();
let wallet_password = utils::empty_string();
monero::open_wallet(&orid, &wallet_password).await;
let r_import = monero::import_multisig_info(v_info).await;
// check balance and unlock_time // check balance and unlock_time
let r_balance = monero::get_balance().await;
// update the order status to multisig complete // update the order status to multisig complete
return false; let ready_to_ship: bool = r_import.result.n_outputs > 0
&& r_balance.result.balance >= total as u128
&& r_balance.result.blocks_to_unlock < monero::LockTimeLimit::Blocks.value();
if ready_to_ship {
m_order.status = StatusType::MulitsigComplete.value();
db::Interface::async_delete(&s.env, &s.handle, &m_order.orid).await;
db::Interface::async_write(&s.env, &s.handle, &m_order.orid, &Order::to_db(&m_order)).await;
}
ready_to_ship
}
/// Write encrypted delivery info to lmdb. Once the customer releases the signed txset
///
/// they will have access to this information (tracking number, locker code, etc.)
pub async fn upload_delivery_info(orid: &String, delivery_info: &Vec<u8>) {
info!("uploading delivery info");
let name = i2p::get_destination(None);
let e_delivery_info: Vec<u8> = gpg::encrypt(name, &delivery_info)
.unwrap_or(Vec::new());
if e_delivery_info.is_empty() {
error!("unable to encrypt delivery info");
}
// write delivery info {delivery}-{order id}
let s = db::Interface::async_open().await;
let k = format!("delivery-{}", orid);
let data = hex::encode(e_delivery_info);
db::Interface::async_write(&s.env, &s.handle, &k, &data).await;
// update the order
let mut m_order: Order = find(orid);
m_order.status = StatusType::Shipped.value();
db::Interface::async_delete(&s.env, &s.handle, &m_order.orid).await;
db::Interface::async_write(&s.env, &s.handle, &m_order.orid, &Order::to_db(&m_order)).await;
} }

View file

@ -368,7 +368,7 @@ pub struct SubAddressInfo {
pub label: String, pub label: String,
pub num_unspent_outputs: u64, pub num_unspent_outputs: u64,
pub time_to_unlock: u128, pub time_to_unlock: u128,
pub blocks_to_unlock: u128, pub blocks_to_unlock: u64,
} }
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
@ -391,7 +391,7 @@ pub struct XmrRpcBalanceResult {
pub unlocked_balance: u128, pub unlocked_balance: u128,
pub multisig_import_needed: bool, pub multisig_import_needed: bool,
pub time_to_unlock: u128, pub time_to_unlock: u128,
pub blocks_to_unlock: u128, pub blocks_to_unlock: u64,
pub per_subaddress: Vec<SubAddressInfo>, pub per_subaddress: Vec<SubAddressInfo>,
} }

View file

@ -12,7 +12,7 @@ pub const LOCK_SCREEN_TIMEOUT_SECS: u64 = 60 * 5;
/// interval to search for credential on initial gui load /// interval to search for credential on initial gui load
pub const CRED_CHECK_INTERVAL: u64 = 5; pub const CRED_CHECK_INTERVAL: u64 = 5;
/// monero estimated block time in seconds /// monero estimated block time in seconds
pub const BLOCK_TIME_IN_SECS_EST: u128 = 0x78; pub const BLOCK_TIME_IN_SECS_EST: u64 = 0x78;
/// time to wait before giving up on adding a contact /// time to wait before giving up on adding a contact
pub const ADD_CONTACT_TIMEOUT_SECS: u64 = 0x5A; pub const ADD_CONTACT_TIMEOUT_SECS: u64 = 0x5A;
/// time to wait before giving up on neveko core /// time to wait before giving up on neveko core

View file

@ -52,7 +52,7 @@ pub async fn get_order(orid: String, _token: auth::BearerToken) -> Custom<Json<m
Custom(Status::Ok, Json(m_order)) Custom(Status::Ok, Json(m_order))
} }
/// Get a order by passing id /// Get all orders
#[get("/")] #[get("/")]
pub async fn get_orders(_token: auth::BearerToken) -> Custom<Json<Vec<models::Order>>> { pub async fn get_orders(_token: auth::BearerToken) -> Custom<Json<Vec<models::Order>>> {
let m_orders: Vec<models::Order> = order::find_all(); let m_orders: Vec<models::Order> = order::find_all();
@ -86,7 +86,7 @@ pub async fn get_dispute(_token: auth::BearerToken, did: String) -> Custom<Json<
Custom(Status::Ok, Json(m_dispute)) Custom(Status::Ok, Json(m_dispute))
} }
/// Create a dispute /// Sign and submit multisig
#[post("/sign/submit", data = "<r_data>")] #[post("/sign/submit", data = "<r_data>")]
pub async fn sign_and_submit_multisig( pub async fn sign_and_submit_multisig(
r_data: Json<reqres::SignAndSubmitRequest>, r_data: Json<reqres::SignAndSubmitRequest>,

View file

@ -3,7 +3,7 @@ use rocket::{
http::Status, http::Status,
post, post,
response::status::Custom, response::status::Custom,
serde::json::Json, serde::json::Json, catch,
}; };
use neveko_core::*; use neveko_core::*;
@ -109,7 +109,7 @@ pub async fn retrieve_order(
signature: String, signature: String,
_jwp: proof::PaymentProof, _jwp: proof::PaymentProof,
) -> Custom<Json<models::Order>> { ) -> Custom<Json<models::Order>> {
let m_order = order::retrieve_order(&orid, &signature).await; let m_order = order::secure_retrieval(&orid, &signature).await;
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()));
} }
@ -156,8 +156,55 @@ pub async fn rx_multisig_message(
/// multisig info, check balance and sanity check `unlock_time`. /// multisig info, check balance and sanity check `unlock_time`.
/// ///
/// Protected: true /// Protected: true
#[post("/")] #[post("/<orid>")]
pub async fn request_shipment(_jwp: proof::PaymentProof) -> Custom<Json<models::Message>> { pub async fn request_shipment(orid: String, _jwp: proof::PaymentProof) -> Custom<Json<models::Message>> {
order::validate_order_for_ship().await; let is_ready: bool = order::validate_order_for_ship(&orid).await;
if !is_ready {
return Custom(Status::BadRequest, Json(Default::default()));
}
Custom(Status::Ok, Json(Default::default())) Custom(Status::Ok, Json(Default::default()))
} }
/// Create a dispute (customer)
#[post("/create", data = "<dispute>")]
pub async fn create_dispute(
dispute: Json<models::Dispute>,
_token: auth::BearerToken,
) -> Custom<Json<models::Dispute>> {
let m_dispute: models::Dispute = dispute::create(dispute);
Custom(Status::Ok, Json(m_dispute))
}
// Catchers
//----------------------------------------------------------------
#[catch(402)]
pub fn payment_required() -> Custom<Json<reqres::ErrorResponse>> {
Custom(
Status::PaymentRequired,
Json(reqres::ErrorResponse {
error: String::from("Payment required"),
}),
)
}
#[catch(404)]
pub fn not_found() -> Custom<Json<reqres::ErrorResponse>> {
Custom(
Status::NotFound,
Json(reqres::ErrorResponse {
error: String::from("Resource does not exist"),
}),
)
}
#[catch(500)]
pub fn internal_error() -> Custom<Json<reqres::ErrorResponse>> {
Custom(
Status::InternalServerError,
Json(reqres::ErrorResponse {
error: String::from("Internal server error"),
}),
)
}

View file

@ -1,44 +1,9 @@
#[macro_use] #[macro_use]
extern crate rocket; extern crate rocket;
use rocket::{
http::Status,
response::status::Custom,
serde::json::Json,
};
use neveko::*; use neveko::*;
use neveko_core::*; use neveko_core::*;
#[catch(402)]
fn payment_required() -> Custom<Json<reqres::ErrorResponse>> {
Custom(
Status::PaymentRequired,
Json(reqres::ErrorResponse {
error: String::from("Payment required"),
}),
)
}
#[catch(404)]
fn not_found() -> Custom<Json<reqres::ErrorResponse>> {
Custom(
Status::NotFound,
Json(reqres::ErrorResponse {
error: String::from("Resource does not exist"),
}),
)
}
#[catch(500)]
fn internal_error() -> Custom<Json<reqres::ErrorResponse>> {
Custom(
Status::InternalServerError,
Json(reqres::ErrorResponse {
error: String::from("Internal server error"),
}),
)
}
// The only changes below here should be mounting new controller methods // The only changes below here should be mounting new controller methods
#[launch] #[launch]
async fn rocket() -> _ { async fn rocket() -> _ {
@ -51,7 +16,11 @@ async fn rocket() -> _ {
env_logger::init(); env_logger::init();
utils::start_up().await; utils::start_up().await;
rocket::custom(&config) rocket::custom(&config)
.register("/", catchers![internal_error, not_found, payment_required]) .register("/", catchers![
controller::internal_error,
controller::not_found,
controller::payment_required
])
.mount("/multisig/info", routes![controller::get_multisig_info]) .mount("/multisig/info", routes![controller::get_multisig_info])
.mount("/invoice", routes![controller::gen_invoice]) .mount("/invoice", routes![controller::gen_invoice])
.mount("/message/rx", routes![controller::rx_message]) .mount("/message/rx", routes![controller::rx_message])
@ -65,6 +34,6 @@ async fn rocket() -> _ {
.mount("/xmr/rpc", routes![controller::get_version]) .mount("/xmr/rpc", routes![controller::get_version])
.mount( .mount(
"/market", "/market",
routes![controller::create_order, controller::get_products], routes![controller::create_order, controller::get_products, controller::create_dispute],
) )
} }