From ffa625185a407a5f0adca5ff90bf3162de207cd5 Mon Sep 17 00:00:00 2001 From: creating2morrow Date: Sun, 4 Jun 2023 06:42:03 -0400 Subject: [PATCH] add submit multisig api --- docs/man.md | 6 ++++- neveko-core/src/dispute.rs | 10 ++++---- neveko-core/src/message.rs | 6 ----- neveko-core/src/monero.rs | 33 ++++++++++++++++++++++++ neveko-core/src/order.rs | 36 ++++++++++++++++++++------ neveko-core/src/product.rs | 12 ++++----- neveko-core/src/reqres.rs | 39 +++++++++++++++++++++++++++++ neveko-core/src/utils.rs | 5 ++-- neveko-gui/src/apps/address_book.rs | 3 +-- neveko-gui/src/apps/home.rs | 6 ++--- neveko-gui/src/apps/wallet.rs | 6 ++--- neveko-market/src/controller.rs | 16 +++++++++++- neveko-market/src/main.rs | 5 +++- 13 files changed, 144 insertions(+), 39 deletions(-) diff --git a/docs/man.md b/docs/man.md index 52808d1..1282609 100644 --- a/docs/man.md +++ b/docs/man.md @@ -3,7 +3,7 @@ ## Architecture * gui -* three internal mircoservers (auth, contact and message) +* four internal mircoservers (auth, contact, market and message) * core code module and lmdb * one external i2p hidden service * jwt for internal auth, jwp for external @@ -62,3 +62,7 @@ * If contacts don't come back online before JWP expiration the message must be drafted again * It is primarily meant for handling connectivity issues or the edge case where a contact is is online during the `check status` but goes offline while the message is being drafted + +## Market + +WIP diff --git a/neveko-core/src/dispute.rs b/neveko-core/src/dispute.rs index 2e4f1c1..a302bcf 100644 --- a/neveko-core/src/dispute.rs +++ b/neveko-core/src/dispute.rs @@ -1,13 +1,13 @@ -use log::{ - debug, - error, - info, -}; use crate::{ db, models::*, utils, }; +use log::{ + debug, + error, + info, +}; use rocket::serde::json::Json; /// Create a new dispute diff --git a/neveko-core/src/message.rs b/neveko-core/src/message.rs index a4a951a..85814f1 100644 --- a/neveko-core/src/message.rs +++ b/neveko-core/src/message.rs @@ -35,12 +35,6 @@ struct MultisigMessageData { orid: String, } -/* - TODOs(c2m): - - 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(); diff --git a/neveko-core/src/monero.rs b/neveko-core/src/monero.rs index 74ca128..bc622e0 100644 --- a/neveko-core/src/monero.rs +++ b/neveko-core/src/monero.rs @@ -40,6 +40,7 @@ enum RpcFields { Open, Prepare, SignMultisig, + SubmitMultisig, SweepAll, Transfer, ValidateAddress, @@ -67,6 +68,7 @@ impl RpcFields { RpcFields::Open => String::from("open_wallet"), RpcFields::Prepare => String::from("prepare_multisig"), RpcFields::SignMultisig => String::from("sign_multisig"), + RpcFields::SubmitMultisig => String::from("submit_multisig"), RpcFields::SweepAll => String::from("sweep_all"), RpcFields::Transfer => String::from("transfer"), RpcFields::ValidateAddress => String::from("validate_address"), @@ -687,6 +689,37 @@ pub async fn sign_multisig(tx_data_hex: String) -> reqres::XmrRpcSignMultisigRes } } +/// Performs the xmr rpc 'submit_multisig' method +pub async fn submit_multisig(tx_data_hex: String) -> reqres::XmrRpcSubmitMultisigResponse { + info!("executing {}", RpcFields::SubmitMultisig.value()); + let client = reqwest::Client::new(); + let host = get_rpc_host(); + let params = reqres::XmrRpcSignMultisigParams { tx_data_hex }; + let req = reqres::XmrRpcSignMultisigRequest { + jsonrpc: RpcFields::JsonRpcVersion.value(), + id: RpcFields::Id.value(), + method: RpcFields::SubmitMultisig.value(), + params, + }; + let login: RpcLogin = get_rpc_creds(); + match client + .post(host) + .json(&req) + .send_with_digest_auth(&login.username, &login.credential) + .await + { + Ok(response) => { + let res = response.json::().await; + debug!("{} response: {:?}", RpcFields::SubmitMultisig.value(), res); + match res { + Ok(res) => res, + _ => Default::default(), + } + } + Err(_) => Default::default(), + } +} + /// Performs the xmr rpc 'exchange_multisig_keys' method pub async fn exchange_multisig_keys( force_update_use_with_caution: bool, diff --git a/neveko-core/src/order.rs b/neveko-core/src/order.rs index 90db6d9..79eb78c 100644 --- a/neveko-core/src/order.rs +++ b/neveko-core/src/order.rs @@ -1,8 +1,3 @@ -use log::{ - debug, - error, - info, -}; use crate::{ db, models::*, @@ -10,8 +5,21 @@ use crate::{ reqres, utils, }; +use log::{ + debug, + error, + info, +}; use rocket::serde::json::Json; + +/* + TODOs(c2m): + - API to valid payment and import multisig info + - update order status +*/ + + enum StatusType { _Delivered, MultisigMissing, @@ -35,8 +43,7 @@ pub async fn create(j_order: Json) -> Order { info!("creating order"); let wallet_name = String::from(crate::APP_NAME); let wallet_password = - std::env::var(crate::MONERO_WALLET_PASSWORD) - .unwrap_or(String::from("password")); + std::env::var(crate::MONERO_WALLET_PASSWORD).unwrap_or(String::from("password")); monero::close_wallet(&wallet_name, &wallet_password).await; let ts = chrono::offset::Utc::now().timestamp(); let orid: String = format!("O{}", utils::generate_rnd()); @@ -144,3 +151,18 @@ pub fn modify(o: Json) -> Order { db::Interface::write(&s.env, &s.handle, &u_order.pid, &Order::to_db(&u_order)); return u_order; } + +/// Sign and submit multisig +pub async fn sign_and_submit_multisig( + orid: &String, + tx_data_hex: &String) -> reqres::XmrRpcSubmitMultisigResponse { + info!("signin and submitting multisig"); + let r_sign: reqres::XmrRpcSignMultisigResponse = + monero::sign_multisig(String::from(tx_data_hex)).await; + let r_submit: reqres::XmrRpcSubmitMultisigResponse = + monero::submit_multisig(r_sign.result.tx_data_hex).await; + if r_submit.result.tx_hash_list.len() == 0 { + error!("unable to submit payment for order: {}", orid); + } + r_submit +} diff --git a/neveko-core/src/product.rs b/neveko-core/src/product.rs index 923a77f..1c06484 100644 --- a/neveko-core/src/product.rs +++ b/neveko-core/src/product.rs @@ -1,14 +1,14 @@ // Product repo/service layer -use log::{ - debug, - error, - info, -}; use crate::{ db, models::*, utils, }; +use log::{ + debug, + error, + info, +}; use rocket::serde::json::Json; /// Create a new product @@ -16,7 +16,7 @@ pub fn create(d: Json) -> Product { let pid: String = format!("product{}", utils::generate_rnd()); if !validate_product(&d) { error!("invalid product"); - return Default::default() + return Default::default(); } let new_product = Product { pid: String::from(&pid), diff --git a/neveko-core/src/reqres.rs b/neveko-core/src/reqres.rs index 707eabc..f8885aa 100644 --- a/neveko-core/src/reqres.rs +++ b/neveko-core/src/reqres.rs @@ -343,6 +343,12 @@ pub struct XmrRpcImportResult { #[derive(Deserialize, Debug)] pub struct XmrRpcSignMultisigResult { + pub tx_data_hex: String, + pub tx_hash_list: Vec, +} + +#[derive(Deserialize, Debug)] +pub struct XmrRpcSubmitMultisigResult { pub tx_hash_list: Vec, } @@ -862,6 +868,22 @@ impl Default for XmrRpcSignMultisigResponse { fn default() -> Self { XmrRpcSignMultisigResponse { result: XmrRpcSignMultisigResult { + tx_data_hex: utils::empty_string(), + tx_hash_list: Vec::new(), + }, + } + } +} + +#[derive(Deserialize, Debug)] +pub struct XmrRpcSubmitMultisigResponse { + pub result: XmrRpcSubmitMultisigResult, +} + +impl Default for XmrRpcSubmitMultisigResponse { + fn default() -> Self { + XmrRpcSubmitMultisigResponse { + result: XmrRpcSubmitMultisigResult { tx_hash_list: Vec::new(), }, } @@ -1123,3 +1145,20 @@ impl Default for MultisigInfoRequest { } } } + +/// Request for signing and submitting the unsigned txset +#[derive(Serialize, Deserialize, Debug)] +#[serde(crate = "rocket::serde")] +pub struct SignAndSubmitRequest { + pub orid: String, + pub txset: String, +} + +impl Default for SignAndSubmitRequest { + fn default() -> Self { + SignAndSubmitRequest { + orid: utils::empty_string(), + txset: utils::empty_string(), + } + } +} diff --git a/neveko-core/src/utils.rs b/neveko-core/src/utils.rs index 719cac8..dabed0d 100644 --- a/neveko-core/src/utils.rs +++ b/neveko-core/src/utils.rs @@ -237,9 +237,11 @@ pub fn empty_string() -> String { pub const fn string_limit() -> usize { 512 } + pub const fn gpg_key_limit() -> usize { 4096 } + pub const fn message_limit() -> usize { 9999 } @@ -644,8 +646,7 @@ pub async fn estimate_fee() -> u128 { pub async fn can_transfer(invoice: u128) -> bool { let wallet_name = String::from(crate::APP_NAME); let wallet_password = - std::env::var(crate::MONERO_WALLET_PASSWORD) - .unwrap_or(String::from("password")); + std::env::var(crate::MONERO_WALLET_PASSWORD).unwrap_or(String::from("password")); monero::open_wallet(&wallet_name, &wallet_password).await; let balance = monero::get_balance().await; monero::close_wallet(&wallet_name, &wallet_password).await; diff --git a/neveko-gui/src/apps/address_book.rs b/neveko-gui/src/apps/address_book.rs index de8a56e..5a2f0d3 100644 --- a/neveko-gui/src/apps/address_book.rs +++ b/neveko-gui/src/apps/address_book.rs @@ -692,8 +692,7 @@ fn send_payment_req( log::debug!("sending {} piconero(s) to: {}", &d.amount, &d.address); let wallet_name = String::from(neveko_core::APP_NAME); let wallet_password = - std::env::var(neveko_core::MONERO_WALLET_PASSWORD) - .unwrap_or(String::from("password")); + std::env::var(neveko_core::MONERO_WALLET_PASSWORD).unwrap_or(String::from("password")); monero::open_wallet(&wallet_name, &wallet_password).await; let transfer: reqres::XmrRpcTransferResponse = monero::transfer(d).await; // in order to keep the jwp creation process transparent to the user diff --git a/neveko-gui/src/apps/home.rs b/neveko-gui/src/apps/home.rs index 0f905f2..4dcff13 100644 --- a/neveko-gui/src/apps/home.rs +++ b/neveko-gui/src/apps/home.rs @@ -366,8 +366,7 @@ fn send_address_req(tx: Sender, ctx: egui::Contex tokio::spawn(async move { let wallet_name = String::from(neveko_core::APP_NAME); let wallet_password = - std::env::var(neveko_core::MONERO_WALLET_PASSWORD) - .unwrap_or(String::from("password")); + std::env::var(neveko_core::MONERO_WALLET_PASSWORD).unwrap_or(String::from("password")); monero::open_wallet(&wallet_name, &wallet_password).await; let address: reqres::XmrRpcAddressResponse = monero::get_address().await; monero::close_wallet(&wallet_name, &wallet_password).await; @@ -380,8 +379,7 @@ fn send_balance_req(tx: Sender, ctx: egui::Contex tokio::spawn(async move { let wallet_name = String::from(neveko_core::APP_NAME); let wallet_password = - std::env::var(neveko_core::MONERO_WALLET_PASSWORD) - .unwrap_or(String::from("password")); + std::env::var(neveko_core::MONERO_WALLET_PASSWORD).unwrap_or(String::from("password")); monero::open_wallet(&wallet_name, &wallet_password).await; let balance: reqres::XmrRpcBalanceResponse = monero::get_balance().await; monero::close_wallet(&wallet_name, &wallet_password).await; diff --git a/neveko-gui/src/apps/wallet.rs b/neveko-gui/src/apps/wallet.rs index 73766fb..af3d470 100644 --- a/neveko-gui/src/apps/wallet.rs +++ b/neveko-gui/src/apps/wallet.rs @@ -141,8 +141,7 @@ fn send_address_req(tx: Sender, ctx: egui::Contex tokio::spawn(async move { let wallet_name = String::from(neveko_core::APP_NAME); let wallet_password = - std::env::var(neveko_core::MONERO_WALLET_PASSWORD) - .unwrap_or(String::from("password")); + std::env::var(neveko_core::MONERO_WALLET_PASSWORD).unwrap_or(String::from("password")); monero::open_wallet(&wallet_name, &wallet_password).await; let address: reqres::XmrRpcAddressResponse = monero::get_address().await; monero::close_wallet(&wallet_name, &wallet_password).await; @@ -159,8 +158,7 @@ fn send_sweep_all_req( tokio::spawn(async move { let wallet_name = String::from(neveko_core::APP_NAME); let wallet_password = - std::env::var(neveko_core::MONERO_WALLET_PASSWORD) - .unwrap_or(String::from("password")); + std::env::var(neveko_core::MONERO_WALLET_PASSWORD).unwrap_or(String::from("password")); monero::open_wallet(&wallet_name, &wallet_password).await; let result: reqres::XmrRpcSweepAllResponse = monero::sweep_all(address).await; monero::close_wallet(&wallet_name, &wallet_password).await; diff --git a/neveko-market/src/controller.rs b/neveko-market/src/controller.rs index 2e8b630..d5921ba 100644 --- a/neveko-market/src/controller.rs +++ b/neveko-market/src/controller.rs @@ -79,10 +79,24 @@ pub async fn create_dispute( Custom(Status::Ok, Json(m_dispute)) } -/// Create a dispute +/// Fetch a dispute #[get("/")] pub async fn get_dispute(_token: auth::BearerToken, did: String) -> Custom> { let m_dispute: models::Dispute = dispute::find(&did); Custom(Status::Ok, Json(m_dispute)) } + +/// Create a dispute +#[post("/sign/submit", data = "")] +pub async fn sign_and_submit_multisig( + r_data: Json, + _token: auth::BearerToken, +) -> Custom> { + let result: reqres::XmrRpcSubmitMultisigResponse = + order::sign_and_submit_multisig(&r_data.orid, &r_data.txset).await; + if result.result.tx_hash_list.is_empty() { + return Custom(Status::BadRequest, Json(Default::default())); + } + Custom(Status::Ok, Json(Default::default())) +} // END JSON APIs diff --git a/neveko-market/src/main.rs b/neveko-market/src/main.rs index 7b7e0c7..8074945 100755 --- a/neveko-market/src/main.rs +++ b/neveko-market/src/main.rs @@ -21,7 +21,10 @@ async fn rocket() -> _ { ) .mount( "/order", - routes![controller::get_order, controller::update_order], + routes![ + controller::get_order, + controller::sign_and_submit_multisig, + controller::update_order], ) .mount("/orders", routes![controller::get_orders]) .mount("/products", routes![controller::get_products])