diff --git a/nevmes-core/src/contact.rs b/nevmes-core/src/contact.rs index add3168..76203ea 100644 --- a/nevmes-core/src/contact.rs +++ b/nevmes-core/src/contact.rs @@ -16,6 +16,9 @@ use log::{ use rocket::serde::json::Json; use std::error::Error; +/// Environment variable for activating vendor functionality +pub const NEVMES_VENDOR_ENABLED: &str = "NEVMES_VENDOR_ENABLED"; + /// Create a new contact pub async fn create(c: &Json) -> Contact { let f_cid: String = format!("c{}", utils::generate_rnd()); @@ -24,6 +27,7 @@ pub async fn create(c: &Json) -> Contact { cid: String::from(&f_cid), gpg_key: c.gpg_key.iter().cloned().collect(), i2p_address: String::from(&c.i2p_address), + is_vendor: false, xmr_address: String::from(&c.xmr_address), }; let is_valid = validate_contact(c).await; @@ -95,6 +99,8 @@ async fn validate_contact(j: &Json) -> bool { /// Send our information pub async fn share() -> Contact { + let vendor_env = std::env::var(NEVMES_VENDOR_ENABLED).unwrap_or(String::from("0")); + let is_vendor = vendor_env == String::from("1"); let m_address: reqres::XmrRpcAddressResponse = monero::get_address().await; let gpg_key = gpg::export_key().unwrap_or(Vec::new()); let i2p_address = i2p::get_destination(); @@ -103,6 +109,7 @@ pub async fn share() -> Contact { cid: utils::empty_string(), gpg_key, i2p_address, + is_vendor, xmr_address, } } diff --git a/nevmes-core/src/message.rs b/nevmes-core/src/message.rs index 93bc1da..216fe96 100644 --- a/nevmes-core/src/message.rs +++ b/nevmes-core/src/message.rs @@ -5,8 +5,9 @@ use crate::{ gpg, i2p, models::*, + monero, reqres, - utils, monero, + utils, }; use log::{ debug, @@ -34,6 +35,13 @@ struct MultisigMessageData { orid: String, } +/* + TODOs(c2m): + - add wallet open before multisig methods + - API to valid payment and import multisig info + - API to sign and submit the signed tx set + */ + /// Create a new message pub async fn create(m: Json, jwp: String, m_type: MessageType) -> Message { let rnd = utils::generate_rnd(); @@ -121,19 +129,23 @@ fn parse_multisig_message(mid: String) -> MultisigMessageData { let info: String = v.remove(0); bytes = Vec::new(); debug!("zero decryption bytes: {:?}", bytes); - MultisigMessageData { info, sub_type, orid } + MultisigMessageData { + info, + sub_type, + orid, + } } /// Rx multisig message -/// +/// /// Upon multisig message receipt the message is automatically -/// +/// /// decrypted for convenience sake. The client must determine which -/// +/// /// .b32.i2p address belongs to the vendor / mediator. -/// +/// /// ### Example -/// +/// /// ```rust /// // lookup prepare info for vendor /// use nevmes_core::db; @@ -166,7 +178,10 @@ pub async fn rx_multisig(m: Json) { let k = &new_message.mid; db::Interface::async_write(&s.env, &s.handle, k, &Message::to_db(&new_message)).await; let data: MultisigMessageData = parse_multisig_message(new_message.mid); - debug!("writing multisig message type {} for order {}", &data.sub_type, &data.orid); + debug!( + "writing multisig message type {} for order {}", + &data.sub_type, &data.orid + ); // lookup msig message data by {type}-{order id}-{contact .b32.i2p address} let msig_key = format!("{}-{}-{}", &data.sub_type, &data.orid, &m.from); db::Interface::async_write(&s.env, &s.handle, &msig_key, &data.info).await; @@ -432,28 +447,29 @@ fn is_fts_clear(r: String) -> bool { } /// Encrypts and sends the output from the monero-rpc -/// +/// /// `prepare_multisig_info` method. pub async fn send_prepare_info(orid: &String, contact: &String) { let s = db::Interface::open(); let prepare_info = monero::prepare_wallet().await; let k = format!("{}-{}", "fts-jwp", contact); let jwp = db::Interface::read(&s.env, &s.handle, &k); - let body_str = format!("{}:{}:{}", PREPARE_MSIG, orid, &prepare_info.result.multisig_info); + let body_str = format!( + "{}:{}:{}", + PREPARE_MSIG, orid, &prepare_info.result.multisig_info + ); let message: Message = Message { - mid: utils::empty_string(), - uid: utils::empty_string(), body: body_str.into_bytes(), created: chrono::Utc::now().timestamp(), - from: utils::empty_string(), to: String::from(contact), + ..Default::default() }; let j_message: Json = utils::message_to_json(&message); create(j_message, jwp, MessageType::Multisig).await; } /// Encrypts and sends the output from the monero-rpc -/// +/// /// `make_multisig_info` method. pub async fn send_make_info(orid: &String, contact: &String, info: Vec) { let s = db::Interface::open(); @@ -462,19 +478,17 @@ pub async fn send_make_info(orid: &String, contact: &String, info: Vec) let jwp = db::Interface::read(&s.env, &s.handle, &k); let body_str = format!("{}:{}:{}", MAKE_MSIG, orid, &make_info.result.multisig_info); let message: Message = Message { - mid: utils::empty_string(), - uid: utils::empty_string(), body: body_str.into_bytes(), created: chrono::Utc::now().timestamp(), - from: utils::empty_string(), to: String::from(contact), + ..Default::default() }; let j_message: Json = utils::message_to_json(&message); create(j_message, jwp, MessageType::Multisig).await; } /// Encrypts and sends the output from the monero-rpc -/// +/// /// `exchange_multisig_keys` method. pub async fn send_exchange_info(orid: &String, contact: &String, info: Vec) { let s = db::Interface::open(); @@ -483,21 +497,22 @@ pub async fn send_exchange_info(orid: &String, contact: &String, info: Vec = utils::message_to_json(&message); create(j_message, jwp, MessageType::Multisig).await; } /// Encrypts and sends the output from the monero-rpc -/// +/// /// `export_multisig_info` method. pub async fn send_export_info(orid: &String, contact: &String) { let s = db::Interface::open(); @@ -506,12 +521,10 @@ pub async fn send_export_info(orid: &String, contact: &String) { let jwp = db::Interface::read(&s.env, &s.handle, &k); let body_str = format!("{}:{}:{}", EXPORT_MSIG, orid, &exchange_info.result.info); let message: Message = Message { - mid: utils::empty_string(), - uid: utils::empty_string(), body: body_str.into_bytes(), created: chrono::Utc::now().timestamp(), - from: utils::empty_string(), to: String::from(contact), + ..Default::default() }; let j_message: Json = utils::message_to_json(&message); create(j_message, jwp, MessageType::Multisig).await; diff --git a/nevmes-core/src/models.rs b/nevmes-core/src/models.rs index ac535be..99fa7a3 100644 --- a/nevmes-core/src/models.rs +++ b/nevmes-core/src/models.rs @@ -1,5 +1,6 @@ use crate::utils; use rocket::serde::{ + json::Json, Deserialize, Serialize, }; @@ -88,6 +89,7 @@ impl Authorization { pub struct Contact { pub cid: String, pub i2p_address: String, + pub is_vendor: bool, pub xmr_address: String, pub gpg_key: Vec, } @@ -98,6 +100,7 @@ impl Default for Contact { cid: utils::empty_string(), gpg_key: Vec::new(), i2p_address: utils::empty_string(), + is_vendor: false, xmr_address: utils::empty_string(), } } @@ -106,18 +109,26 @@ impl Default for Contact { impl Contact { pub fn to_db(c: &Contact) -> String { let gpg = hex::encode(&c.gpg_key); - format!("{}!{}!{}", gpg, c.i2p_address, c.xmr_address) + format!( + "{}!{}!{}!{}", + gpg, c.i2p_address, c.is_vendor, c.xmr_address + ) } pub fn from_db(k: String, v: String) -> Contact { let values = v.split("!"); let mut v: Vec = values.map(|s| String::from(s)).collect(); let gpg_key = hex::decode(v.remove(0)).unwrap_or(Vec::new()); let i2p_address = v.remove(0); + let is_vendor = match v.remove(0).parse::() { + Ok(n) => n, + Err(_e) => false, + }; let xmr_address = v.remove(0); Contact { cid: k, gpg_key, i2p_address, + is_vendor, xmr_address, } } @@ -279,23 +290,15 @@ impl Product { qty, } } - pub fn update( - p: Product, - description: String, - image: Vec, - in_stock: bool, - name: String, - price: i64, - qty: i64, - ) -> Product { + pub fn update(p: Product, jp: &Json) -> Product { Product { pid: p.pid, - description, - image, - in_stock, - name, - price, - qty, + description: String::from(&jp.description), + image: jp.image.iter().cloned().collect(), + in_stock: jp.in_stock, + name: String::from(&jp.name), + price: jp.price, + qty: jp.qty, } } } @@ -485,7 +488,7 @@ impl Order { xmr_address, } } - pub fn update(orid: String, o: Order) -> Order { + pub fn update(orid: String, o: &Json) -> Order { Order { orid, cid: String::from(&o.cid), diff --git a/nevmes-core/src/utils.rs b/nevmes-core/src/utils.rs index b6b9f32..91e1e97 100644 --- a/nevmes-core/src/utils.rs +++ b/nevmes-core/src/utils.rs @@ -208,6 +208,7 @@ pub fn contact_to_json(c: &models::Contact) -> Json { let r_contact: models::Contact = models::Contact { cid: String::from(&c.cid), i2p_address: String::from(&c.i2p_address), + is_vendor: c.is_vendor, xmr_address: String::from(&c.xmr_address), gpg_key: c.gpg_key.iter().cloned().collect(), }; diff --git a/nevmes-gui/src/apps/address_book.rs b/nevmes-gui/src/apps/address_book.rs index 8b32b14..33dd286 100644 --- a/nevmes-gui/src/apps/address_book.rs +++ b/nevmes-gui/src/apps/address_book.rs @@ -407,6 +407,7 @@ impl eframe::App for AddressBookApp { let mut is_added = self.added; let is_loading = self.is_loading; let i2p_address = self.s_contact.i2p_address.clone(); + let is_vendor = self.s_contact.is_vendor; let xmr_address = self.s_contact.xmr_address.clone(); let gpg_key = self.s_contact.gpg_key.iter().cloned().collect(); @@ -453,6 +454,7 @@ impl eframe::App for AddressBookApp { let c_contact: models::Contact = models::Contact { cid: self.s_contact.cid.clone(), i2p_address, + is_vendor, xmr_address, gpg_key: self.s_contact.gpg_key.iter().cloned().collect(), }; diff --git a/nevmes-market/src/controller.rs b/nevmes-market/src/controller.rs index b346520..917cc57 100644 --- a/nevmes-market/src/controller.rs +++ b/nevmes-market/src/controller.rs @@ -11,25 +11,32 @@ use nevmes_core::*; use crate::{ dispute, + order, product, }; // JSON APIs -/// Create a product by passing vendor vid +/// Create a product by passings json product #[post("/create", data = "")] pub async fn create_product( req_product: Json, _token: auth::BearerToken, ) -> Custom> { let m_product: models::Product = product::create(req_product); + Custom(Status::Created, Json(m_product)) +} + +/// Get a product by passing id +#[post("/")] +pub async fn get_product(pid: String, _token: auth::BearerToken) -> Custom> { + let m_product: models::Product = product::find(&pid); Custom(Status::Ok, Json(m_product)) } /// Update product information -#[patch("/<_address>/update", data = "")] +#[patch("/update", data = "")] pub async fn update_product( - _address: String, product: Json, _token: auth::BearerToken, ) -> Custom> { @@ -37,51 +44,36 @@ pub async fn update_product( Custom(Status::Ok, Json(m_product)) } -// /// Initialize order -// #[get("/
/create/")] -// pub async fn initialize_order( -// address: String, -// _token: auth::BearerToken, -// pid: String, -// ) -> Custom> { -// // get the cid from the address after verification -// let m_customer = customer::find(address).await; -// let temp_pid = String::from(&pid); -// let m_order: models::Order = order::create(m_customer.cid, temp_pid).await; -// Custom( -// Status::Ok, -// Json(reqres::GetOrderResponse::build(pid, m_order)), -// ) -// } +/// Return all products +#[patch("/")] +pub async fn get_products(_token: auth::BearerToken) -> Custom>> { + let m_products: Vec = product::find_all(); + Custom(Status::Ok, Json(m_products)) +} -// /// Update order information from vendor -// #[patch("/update////vendor")] -// pub async fn update_order( -// _address: String, -// oid: String, -// pid: String, -// _token: auth::BearerToken, -// data: String, -// ) -> Custom> { -// let temp_pid: String = String::from(&pid); -// let m_order: models::Order = order::modify(oid, pid, data, update_type).await; -// Custom( -// Status::Ok, -// Json(reqres::GetOrderResponse::build(temp_pid, m_order)), -// ) -// } +/// Get a order by passing id +#[post("/")] +pub async fn get_order(orid: String, _token: auth::BearerToken) -> Custom> { + let m_order: models::Order = order::find(&orid); + Custom(Status::Ok, Json(m_order)) +} -// /// Get all orders -// /// by passing auth -// #[get("/
/")] -// pub async fn get_orders( -// address: String, -// corv: String, -// _token: auth::BearerToken, -// ) -> Custom> { -// let m_orders: Vec = order::find_all(address, corv).await; -// Custom(Status::Ok, Json(reqres::GetOrdersResponse::build(m_orders))) -// } +/// Get a order by passing id +#[post("/")] +pub async fn get_orders(_token: auth::BearerToken) -> Custom>> { + let m_orders: Vec = order::find_all(); + Custom(Status::Ok, Json(m_orders)) +} + +/// Update order information +#[patch("/update", data = "")] +pub async fn update_order( + order: Json, + _token: auth::BearerToken, +) -> Custom> { + let m_order: models::Order = order::modify(order); + Custom(Status::Ok, Json(m_order)) +} /// Create a dispute #[post("/create", data = "")] diff --git a/nevmes-market/src/main.rs b/nevmes-market/src/main.rs index e959a5b..bdaaac8 100755 --- a/nevmes-market/src/main.rs +++ b/nevmes-market/src/main.rs @@ -19,8 +19,12 @@ async fn rocket() -> _ { "/dispute", routes![controller::create_dispute, controller::get_dispute], ) - // .mount("/order", routes![controller::initialize_order, controller::update_order]) - // .mount("/orders", routes![controller::get_orders]) + .mount( + "/order", + routes![controller::get_order, controller::update_order], + ) + .mount("/orders", routes![controller::get_orders]) + .mount("/products", routes![controller::get_products]) .mount( "/product", routes![controller::create_product, controller::update_product], diff --git a/nevmes-market/src/order.rs b/nevmes-market/src/order.rs index 3ed5265..44b1759 100644 --- a/nevmes-market/src/order.rs +++ b/nevmes-market/src/order.rs @@ -1,5 +1,15 @@ -use nevmes_core::*; -use log::{debug, error, info}; +use log::{ + debug, + error, + info, +}; +use nevmes_core::{ + db, + models::*, + monero, + reqres, + utils, +}; use rocket::serde::json::Json; enum StatusType { @@ -21,13 +31,13 @@ impl StatusType { } /// Create a intial order -pub async fn create(j_order: Json) -> models::Order { +pub async fn create(j_order: Json) -> Order { info!("creating order"); let ts = chrono::offset::Utc::now().timestamp(); let orid: String = format!("O{}", utils::generate_rnd()); let r_subaddress = monero::create_address().await; let subaddress = r_subaddress.result.address; - let new_order = models::Order { + let new_order = Order { orid: String::from(&orid), cid: String::from(&j_order.cid), pid: String::from(&j_order.pid), @@ -47,7 +57,7 @@ pub async fn create(j_order: Json) -> models::Order { debug!("insert order: {:?}", &new_order); let s = db::Interface::open(); let k = &new_order.orid; - db::Interface::write(&s.env, &s.handle, k, &models::Order::to_db(&new_order)); + db::Interface::write(&s.env, &s.handle, k, &Order::to_db(&new_order)); // in order to retrieve all orders, write keys to with ol let list_key = format!("ol"); let r = db::Interface::read(&s.env, &s.handle, &String::from(&list_key)); @@ -61,19 +71,39 @@ pub async fn create(j_order: Json) -> models::Order { } /// Lookup order -pub fn find(oid: String) -> models::Order { +pub fn find(oid: &String) -> Order { info!("find order: {}", &oid); let s = db::Interface::open(); - let r = db::Interface::read(&s.env, &s.handle, &String::from(&oid)); + let r = db::Interface::read(&s.env, &s.handle, &String::from(oid)); if r == utils::empty_string() { error!("order not found"); return Default::default(); } - models::Order::from_db(String::from(&oid), r) + Order::from_db(String::from(oid), r) +} + +/// Lookup all orders from admin server +pub fn find_all() -> Vec { + let i_s = db::Interface::open(); + let i_list_key = format!("ol"); + let i_r = db::Interface::read(&i_s.env, &i_s.handle, &String::from(i_list_key)); + if i_r == utils::empty_string() { + error!("order index not found"); + } + let i_v_oid = i_r.split(","); + let i_v: Vec = i_v_oid.map(|s| String::from(s)).collect(); + let mut orders: Vec = Vec::new(); + for o in i_v { + let order: Order = find(&o); + if order.orid != utils::empty_string() { + orders.push(order); + } + } + orders } /// Lookup all orders for customer -pub async fn find_all_customer_orders(cid: String) -> Vec { +pub async fn find_all_customer_orders(cid: String) -> Vec { info!("lookup orders for customer: {}", &cid); let i_s = db::Interface::open(); let i_list_key = format!("ol"); @@ -83,12 +113,27 @@ pub async fn find_all_customer_orders(cid: String) -> Vec { } let i_v_oid = i_r.split(","); let i_v: Vec = i_v_oid.map(|s| String::from(s)).collect(); - let mut orders: Vec = Vec::new(); + let mut orders: Vec = Vec::new(); for o in i_v { - let order: models::Order = find(o); + let order: Order = find(&o); if order.orid != utils::empty_string() && order.cid == cid { orders.push(order); } } orders } + +/// Modify order from admin server +pub fn modify(o: Json) -> Order { + info!("modify order: {}", &o.orid); + let f_order: Order = find(&o.orid); + if f_order.orid == utils::empty_string() { + error!("order not found"); + return Default::default(); + } + let u_order = Order::update(String::from(&f_order.orid), &o); + let s = db::Interface::open(); + db::Interface::delete(&s.env, &s.handle, &u_order.pid); + db::Interface::write(&s.env, &s.handle, &u_order.pid, &Order::to_db(&u_order)); + return u_order; +} diff --git a/nevmes-market/src/product.rs b/nevmes-market/src/product.rs index e36cf45..b51f53b 100644 --- a/nevmes-market/src/product.rs +++ b/nevmes-market/src/product.rs @@ -81,15 +81,7 @@ pub fn modify(p: Json) -> Product { error!("product not found"); return Default::default(); } - let u_prod = Product::update( - f_prod, - String::from(&p.description), - p.image.iter().cloned().collect(), - p.in_stock, - String::from(&p.description), - p.price, - p.qty, - ); + let u_prod = Product::update(f_prod, &p); let s = db::Interface::open(); db::Interface::delete(&s.env, &s.handle, &u_prod.pid); db::Interface::write(&s.env, &s.handle, &u_prod.pid, &Product::to_db(&u_prod)); diff --git a/src/controller.rs b/src/controller.rs index ce590c8..c7d5a89 100644 --- a/src/controller.rs +++ b/src/controller.rs @@ -37,7 +37,7 @@ pub async fn get_i2p_status() -> Custom> { } /// Share your contact information -/// +/// /// Protected: false #[get("/")] pub async fn share_contact_info() -> Custom> { @@ -93,13 +93,14 @@ pub async fn get_products(_jwp: proof::PaymentProof) -> Custom, - _jwp: proof::PaymentProof) -> Custom> { + _jwp: proof::PaymentProof, +) -> Custom> { let m_order: models::Order = order::create(r_order).await; Custom(Status::Created, Json(m_order)) } -/// Customer order retreival. Must send `signature` -/// +/// TODO: Customer order retreival. Must send `signature` +/// /// which is the order id signed by the wallet. /// /// Protected: true @@ -107,13 +108,13 @@ pub async fn create_order( pub async fn retrieve_order( orid: String, _signature: String, - _jwp: proof::PaymentProof) -> Custom> { - + _jwp: proof::PaymentProof, +) -> Custom> { // get customer address // send address, orid and signature to verify() - let m_order: models::Order = order::find(orid); + let m_order: models::Order = order::find(&orid); Custom(Status::Created, Json(m_order)) } @@ -123,17 +124,18 @@ pub async fn retrieve_order( #[post("/", data = "")] pub async fn get_multisig_info( r_info: Json, - _jwp: proof::PaymentProof) -> Custom> { - let info: Vec = r_info.info.iter().cloned().collect(); - if r_info.msig_type == String::from(message::PREPARE_MSIG) { - message::send_prepare_info(&r_info.orid, &r_info.contact).await; - } else if r_info.msig_type == String::from(message::MAKE_MSIG) { - message::send_make_info(&r_info.orid, &r_info.contact, info).await; - } else if r_info.msig_type == String::from(message::EXPORT_MSIG) { - message::send_export_info(&r_info.orid, &r_info.contact).await; - } else { - message::send_exchange_info(&r_info.orid, &r_info.contact, info).await; - } + _jwp: proof::PaymentProof, +) -> Custom> { + let info: Vec = r_info.info.iter().cloned().collect(); + if r_info.msig_type == String::from(message::PREPARE_MSIG) { + message::send_prepare_info(&r_info.orid, &r_info.contact).await; + } else if r_info.msig_type == String::from(message::MAKE_MSIG) { + message::send_make_info(&r_info.orid, &r_info.contact, info).await; + } else if r_info.msig_type == String::from(message::EXPORT_MSIG) { + message::send_export_info(&r_info.orid, &r_info.contact).await; + } else { + message::send_exchange_info(&r_info.orid, &r_info.contact, info).await; + } Custom(Status::Ok, Json(Default::default())) } diff --git a/src/main.rs b/src/main.rs index 61becc7..731a92d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -55,10 +55,16 @@ async fn rocket() -> _ { .mount("/multisig/info", routes![controller::get_multisig_info]) .mount("/invoice", routes![controller::gen_invoice]) .mount("/message/rx", routes![controller::rx_message]) - .mount("/message/rx/multisig", routes![controller::rx_multisig_message]) + .mount( + "/message/rx/multisig", + routes![controller::rx_multisig_message], + ) .mount("/prove", routes![controller::gen_jwp]) .mount("/share", routes![controller::share_contact_info]) .mount("/i2p", routes![controller::get_i2p_status]) .mount("/xmr/rpc", routes![controller::get_version]) - .mount("/market", routes![controller::get_products]) + .mount( + "/market", + routes![controller::create_order, controller::get_products], + ) }