diff --git a/neveko-core/src/models.rs b/neveko-core/src/models.rs index 99fa7a3..2c86cea 100644 --- a/neveko-core/src/models.rs +++ b/neveko-core/src/models.rs @@ -236,8 +236,8 @@ pub struct Product { pub image: Vec, pub in_stock: bool, pub name: String, - pub price: i64, - pub qty: i64, + pub price: u64, + pub qty: u64, } impl Default for Product { @@ -272,11 +272,11 @@ impl Product { Err(_) => false, }; let name = v.remove(0); - let price = match v.remove(0).parse::() { + let price = match v.remove(0).parse::() { Ok(p) => p, Err(_) => 0, }; - let qty = match v.remove(0).parse::() { + let qty = match v.remove(0).parse::() { Ok(q) => q, Err(_) => 0, }; diff --git a/neveko-core/src/order.rs b/neveko-core/src/order.rs index eb2893a..c181c85 100644 --- a/neveko-core/src/order.rs +++ b/neveko-core/src/order.rs @@ -4,7 +4,7 @@ use crate::{ models::*, monero, reqres, - utils, + utils, product, gpg, i2p, }; use log::{ debug, @@ -15,16 +15,14 @@ use rocket::serde::json::Json; /* 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 */ enum StatusType { _Delivered, MultisigMissing, - _MulitsigComplete, - _Shipped, + MulitsigComplete, + Shipped, } impl StatusType { @@ -32,8 +30,8 @@ impl StatusType { match *self { StatusType::_Delivered => String::from("Delivered"), StatusType::MultisigMissing => String::from("MultisigMissing"), - StatusType::_MulitsigComplete => String::from("MulitsigComplete"), - StatusType::_Shipped => String::from("Shipped"), + StatusType::MulitsigComplete => String::from("MulitsigComplete"), + 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 /// /// 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 let m_order: Order = find(&orid); 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 { if customer.i2p_address == m_order.cid { xmr_address = customer.xmr_address; + break; } } // send address, orid and signature to verify() @@ -195,12 +194,57 @@ pub async fn retrieve_order(orid: &String, signature: &String) -> 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"); + 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 - + 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 = 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 - + let r_balance = monero::get_balance().await; // 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) { + info!("uploading delivery info"); + let name = i2p::get_destination(None); + let e_delivery_info: Vec = 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; } diff --git a/neveko-core/src/reqres.rs b/neveko-core/src/reqres.rs index f8885aa..5bbea6c 100644 --- a/neveko-core/src/reqres.rs +++ b/neveko-core/src/reqres.rs @@ -368,7 +368,7 @@ pub struct SubAddressInfo { pub label: String, pub num_unspent_outputs: u64, pub time_to_unlock: u128, - pub blocks_to_unlock: u128, + pub blocks_to_unlock: u64, } #[derive(Deserialize, Debug)] @@ -391,7 +391,7 @@ pub struct XmrRpcBalanceResult { pub unlocked_balance: u128, pub multisig_import_needed: bool, pub time_to_unlock: u128, - pub blocks_to_unlock: u128, + pub blocks_to_unlock: u64, pub per_subaddress: Vec, } diff --git a/neveko-gui/src/lib.rs b/neveko-gui/src/lib.rs index 6852c45..082709d 100644 --- a/neveko-gui/src/lib.rs +++ b/neveko-gui/src/lib.rs @@ -12,7 +12,7 @@ pub const LOCK_SCREEN_TIMEOUT_SECS: u64 = 60 * 5; /// interval to search for credential on initial gui load pub const CRED_CHECK_INTERVAL: u64 = 5; /// 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 pub const ADD_CONTACT_TIMEOUT_SECS: u64 = 0x5A; /// time to wait before giving up on neveko core diff --git a/neveko-market/src/controller.rs b/neveko-market/src/controller.rs index d5921ba..276b2c2 100644 --- a/neveko-market/src/controller.rs +++ b/neveko-market/src/controller.rs @@ -52,7 +52,7 @@ pub async fn get_order(orid: String, _token: auth::BearerToken) -> Custom Custom>> { let m_orders: Vec = order::find_all(); @@ -86,7 +86,7 @@ pub async fn get_dispute(_token: auth::BearerToken, did: String) -> Custom, diff --git a/src/controller.rs b/src/controller.rs index 1227186..af1d4b1 100644 --- a/src/controller.rs +++ b/src/controller.rs @@ -3,7 +3,7 @@ use rocket::{ http::Status, post, response::status::Custom, - serde::json::Json, + serde::json::Json, catch, }; use neveko_core::*; @@ -109,7 +109,7 @@ pub async fn retrieve_order( signature: String, _jwp: proof::PaymentProof, ) -> Custom> { - 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() { 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`. /// /// Protected: true -#[post("/")] -pub async fn request_shipment(_jwp: proof::PaymentProof) -> Custom> { - order::validate_order_for_ship().await; +#[post("/")] +pub async fn request_shipment(orid: String, _jwp: proof::PaymentProof) -> Custom> { + 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())) } + +/// Create a dispute (customer) +#[post("/create", data = "")] +pub async fn create_dispute( + dispute: Json, + _token: auth::BearerToken, +) -> Custom> { + let m_dispute: models::Dispute = dispute::create(dispute); + Custom(Status::Ok, Json(m_dispute)) +} + + +// Catchers +//---------------------------------------------------------------- + +#[catch(402)] +pub fn payment_required() -> Custom> { + Custom( + Status::PaymentRequired, + Json(reqres::ErrorResponse { + error: String::from("Payment required"), + }), + ) +} + +#[catch(404)] +pub fn not_found() -> Custom> { + Custom( + Status::NotFound, + Json(reqres::ErrorResponse { + error: String::from("Resource does not exist"), + }), + ) +} + +#[catch(500)] +pub fn internal_error() -> Custom> { + Custom( + Status::InternalServerError, + Json(reqres::ErrorResponse { + error: String::from("Internal server error"), + }), + ) +} diff --git a/src/main.rs b/src/main.rs index c2e39ff..6980dfb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,44 +1,9 @@ #[macro_use] extern crate rocket; -use rocket::{ - http::Status, - response::status::Custom, - serde::json::Json, -}; use neveko::*; use neveko_core::*; -#[catch(402)] -fn payment_required() -> Custom> { - Custom( - Status::PaymentRequired, - Json(reqres::ErrorResponse { - error: String::from("Payment required"), - }), - ) -} - -#[catch(404)] -fn not_found() -> Custom> { - Custom( - Status::NotFound, - Json(reqres::ErrorResponse { - error: String::from("Resource does not exist"), - }), - ) -} - -#[catch(500)] -fn internal_error() -> Custom> { - Custom( - Status::InternalServerError, - Json(reqres::ErrorResponse { - error: String::from("Internal server error"), - }), - ) -} - // The only changes below here should be mounting new controller methods #[launch] async fn rocket() -> _ { @@ -51,7 +16,11 @@ async fn rocket() -> _ { env_logger::init(); utils::start_up().await; 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("/invoice", routes![controller::gen_invoice]) .mount("/message/rx", routes![controller::rx_message]) @@ -65,6 +34,6 @@ async fn rocket() -> _ { .mount("/xmr/rpc", routes![controller::get_version]) .mount( "/market", - routes![controller::create_order, controller::get_products], + routes![controller::create_order, controller::get_products, controller::create_dispute], ) }