add submit multisig api

This commit is contained in:
creating2morrow 2023-06-04 06:42:03 -04:00
parent 84a7656616
commit ffa625185a
13 changed files with 144 additions and 39 deletions

View file

@ -3,7 +3,7 @@
## Architecture ## Architecture
* gui * gui
* three internal mircoservers (auth, contact and message) * four internal mircoservers (auth, contact, market and message)
* core code module and lmdb * core code module and lmdb
* one external i2p hidden service * one external i2p hidden service
* jwt for internal auth, jwp for external * 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 * 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 * 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 is online during the `check status` but goes offline while the message is being drafted
## Market
WIP

View file

@ -1,13 +1,13 @@
use log::{
debug,
error,
info,
};
use crate::{ use crate::{
db, db,
models::*, models::*,
utils, utils,
}; };
use log::{
debug,
error,
info,
};
use rocket::serde::json::Json; use rocket::serde::json::Json;
/// Create a new dispute /// Create a new dispute

View file

@ -35,12 +35,6 @@ struct MultisigMessageData {
orid: String, 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 /// Create a new message
pub async fn create(m: Json<Message>, jwp: String, m_type: MessageType) -> Message { pub async fn create(m: Json<Message>, jwp: String, m_type: MessageType) -> Message {
let rnd = utils::generate_rnd(); let rnd = utils::generate_rnd();

View file

@ -40,6 +40,7 @@ enum RpcFields {
Open, Open,
Prepare, Prepare,
SignMultisig, SignMultisig,
SubmitMultisig,
SweepAll, SweepAll,
Transfer, Transfer,
ValidateAddress, ValidateAddress,
@ -67,6 +68,7 @@ impl RpcFields {
RpcFields::Open => String::from("open_wallet"), RpcFields::Open => String::from("open_wallet"),
RpcFields::Prepare => String::from("prepare_multisig"), RpcFields::Prepare => String::from("prepare_multisig"),
RpcFields::SignMultisig => String::from("sign_multisig"), RpcFields::SignMultisig => String::from("sign_multisig"),
RpcFields::SubmitMultisig => String::from("submit_multisig"),
RpcFields::SweepAll => String::from("sweep_all"), RpcFields::SweepAll => String::from("sweep_all"),
RpcFields::Transfer => String::from("transfer"), RpcFields::Transfer => String::from("transfer"),
RpcFields::ValidateAddress => String::from("validate_address"), 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::<reqres::XmrRpcSubmitMultisigResponse>().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 /// Performs the xmr rpc 'exchange_multisig_keys' method
pub async fn exchange_multisig_keys( pub async fn exchange_multisig_keys(
force_update_use_with_caution: bool, force_update_use_with_caution: bool,

View file

@ -1,8 +1,3 @@
use log::{
debug,
error,
info,
};
use crate::{ use crate::{
db, db,
models::*, models::*,
@ -10,8 +5,21 @@ use crate::{
reqres, reqres,
utils, utils,
}; };
use log::{
debug,
error,
info,
};
use rocket::serde::json::Json; use rocket::serde::json::Json;
/*
TODOs(c2m):
- API to valid payment and import multisig info
- update order status
*/
enum StatusType { enum StatusType {
_Delivered, _Delivered,
MultisigMissing, MultisigMissing,
@ -35,8 +43,7 @@ pub async fn create(j_order: Json<reqres::OrderRequest>) -> Order {
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 =
std::env::var(crate::MONERO_WALLET_PASSWORD) std::env::var(crate::MONERO_WALLET_PASSWORD).unwrap_or(String::from("password"));
.unwrap_or(String::from("password"));
monero::close_wallet(&wallet_name, &wallet_password).await; monero::close_wallet(&wallet_name, &wallet_password).await;
let ts = chrono::offset::Utc::now().timestamp(); let ts = chrono::offset::Utc::now().timestamp();
let orid: String = format!("O{}", utils::generate_rnd()); let orid: String = format!("O{}", utils::generate_rnd());
@ -144,3 +151,18 @@ pub fn modify(o: Json<Order>) -> Order {
db::Interface::write(&s.env, &s.handle, &u_order.pid, &Order::to_db(&u_order)); db::Interface::write(&s.env, &s.handle, &u_order.pid, &Order::to_db(&u_order));
return 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
}

View file

@ -1,14 +1,14 @@
// Product repo/service layer // Product repo/service layer
use log::{
debug,
error,
info,
};
use crate::{ use crate::{
db, db,
models::*, models::*,
utils, utils,
}; };
use log::{
debug,
error,
info,
};
use rocket::serde::json::Json; use rocket::serde::json::Json;
/// Create a new product /// Create a new product
@ -16,7 +16,7 @@ pub fn create(d: Json<Product>) -> Product {
let pid: String = format!("product{}", utils::generate_rnd()); let pid: String = format!("product{}", utils::generate_rnd());
if !validate_product(&d) { if !validate_product(&d) {
error!("invalid product"); error!("invalid product");
return Default::default() return Default::default();
} }
let new_product = Product { let new_product = Product {
pid: String::from(&pid), pid: String::from(&pid),

View file

@ -343,6 +343,12 @@ pub struct XmrRpcImportResult {
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
pub struct XmrRpcSignMultisigResult { pub struct XmrRpcSignMultisigResult {
pub tx_data_hex: String,
pub tx_hash_list: Vec<String>,
}
#[derive(Deserialize, Debug)]
pub struct XmrRpcSubmitMultisigResult {
pub tx_hash_list: Vec<String>, pub tx_hash_list: Vec<String>,
} }
@ -862,6 +868,22 @@ impl Default for XmrRpcSignMultisigResponse {
fn default() -> Self { fn default() -> Self {
XmrRpcSignMultisigResponse { XmrRpcSignMultisigResponse {
result: XmrRpcSignMultisigResult { 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(), 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(),
}
}
}

View file

@ -237,9 +237,11 @@ pub fn empty_string() -> String {
pub const fn string_limit() -> usize { pub const fn string_limit() -> usize {
512 512
} }
pub const fn gpg_key_limit() -> usize { pub const fn gpg_key_limit() -> usize {
4096 4096
} }
pub const fn message_limit() -> usize { pub const fn message_limit() -> usize {
9999 9999
} }
@ -644,8 +646,7 @@ pub async fn estimate_fee() -> u128 {
pub async fn can_transfer(invoice: u128) -> bool { pub async fn can_transfer(invoice: u128) -> bool {
let wallet_name = String::from(crate::APP_NAME); let wallet_name = String::from(crate::APP_NAME);
let wallet_password = let wallet_password =
std::env::var(crate::MONERO_WALLET_PASSWORD) std::env::var(crate::MONERO_WALLET_PASSWORD).unwrap_or(String::from("password"));
.unwrap_or(String::from("password"));
monero::open_wallet(&wallet_name, &wallet_password).await; monero::open_wallet(&wallet_name, &wallet_password).await;
let balance = monero::get_balance().await; let balance = monero::get_balance().await;
monero::close_wallet(&wallet_name, &wallet_password).await; monero::close_wallet(&wallet_name, &wallet_password).await;

View file

@ -692,8 +692,7 @@ fn send_payment_req(
log::debug!("sending {} piconero(s) to: {}", &d.amount, &d.address); log::debug!("sending {} piconero(s) to: {}", &d.amount, &d.address);
let wallet_name = String::from(neveko_core::APP_NAME); let wallet_name = String::from(neveko_core::APP_NAME);
let wallet_password = let wallet_password =
std::env::var(neveko_core::MONERO_WALLET_PASSWORD) std::env::var(neveko_core::MONERO_WALLET_PASSWORD).unwrap_or(String::from("password"));
.unwrap_or(String::from("password"));
monero::open_wallet(&wallet_name, &wallet_password).await; monero::open_wallet(&wallet_name, &wallet_password).await;
let transfer: reqres::XmrRpcTransferResponse = monero::transfer(d).await; let transfer: reqres::XmrRpcTransferResponse = monero::transfer(d).await;
// in order to keep the jwp creation process transparent to the user // in order to keep the jwp creation process transparent to the user

View file

@ -366,8 +366,7 @@ fn send_address_req(tx: Sender<reqres::XmrRpcAddressResponse>, ctx: egui::Contex
tokio::spawn(async move { tokio::spawn(async move {
let wallet_name = String::from(neveko_core::APP_NAME); let wallet_name = String::from(neveko_core::APP_NAME);
let wallet_password = let wallet_password =
std::env::var(neveko_core::MONERO_WALLET_PASSWORD) std::env::var(neveko_core::MONERO_WALLET_PASSWORD).unwrap_or(String::from("password"));
.unwrap_or(String::from("password"));
monero::open_wallet(&wallet_name, &wallet_password).await; monero::open_wallet(&wallet_name, &wallet_password).await;
let address: reqres::XmrRpcAddressResponse = monero::get_address().await; let address: reqres::XmrRpcAddressResponse = monero::get_address().await;
monero::close_wallet(&wallet_name, &wallet_password).await; monero::close_wallet(&wallet_name, &wallet_password).await;
@ -380,8 +379,7 @@ fn send_balance_req(tx: Sender<reqres::XmrRpcBalanceResponse>, ctx: egui::Contex
tokio::spawn(async move { tokio::spawn(async move {
let wallet_name = String::from(neveko_core::APP_NAME); let wallet_name = String::from(neveko_core::APP_NAME);
let wallet_password = let wallet_password =
std::env::var(neveko_core::MONERO_WALLET_PASSWORD) std::env::var(neveko_core::MONERO_WALLET_PASSWORD).unwrap_or(String::from("password"));
.unwrap_or(String::from("password"));
monero::open_wallet(&wallet_name, &wallet_password).await; monero::open_wallet(&wallet_name, &wallet_password).await;
let balance: reqres::XmrRpcBalanceResponse = monero::get_balance().await; let balance: reqres::XmrRpcBalanceResponse = monero::get_balance().await;
monero::close_wallet(&wallet_name, &wallet_password).await; monero::close_wallet(&wallet_name, &wallet_password).await;

View file

@ -141,8 +141,7 @@ fn send_address_req(tx: Sender<reqres::XmrRpcAddressResponse>, ctx: egui::Contex
tokio::spawn(async move { tokio::spawn(async move {
let wallet_name = String::from(neveko_core::APP_NAME); let wallet_name = String::from(neveko_core::APP_NAME);
let wallet_password = let wallet_password =
std::env::var(neveko_core::MONERO_WALLET_PASSWORD) std::env::var(neveko_core::MONERO_WALLET_PASSWORD).unwrap_or(String::from("password"));
.unwrap_or(String::from("password"));
monero::open_wallet(&wallet_name, &wallet_password).await; monero::open_wallet(&wallet_name, &wallet_password).await;
let address: reqres::XmrRpcAddressResponse = monero::get_address().await; let address: reqres::XmrRpcAddressResponse = monero::get_address().await;
monero::close_wallet(&wallet_name, &wallet_password).await; monero::close_wallet(&wallet_name, &wallet_password).await;
@ -159,8 +158,7 @@ fn send_sweep_all_req(
tokio::spawn(async move { tokio::spawn(async move {
let wallet_name = String::from(neveko_core::APP_NAME); let wallet_name = String::from(neveko_core::APP_NAME);
let wallet_password = let wallet_password =
std::env::var(neveko_core::MONERO_WALLET_PASSWORD) std::env::var(neveko_core::MONERO_WALLET_PASSWORD).unwrap_or(String::from("password"));
.unwrap_or(String::from("password"));
monero::open_wallet(&wallet_name, &wallet_password).await; monero::open_wallet(&wallet_name, &wallet_password).await;
let result: reqres::XmrRpcSweepAllResponse = monero::sweep_all(address).await; let result: reqres::XmrRpcSweepAllResponse = monero::sweep_all(address).await;
monero::close_wallet(&wallet_name, &wallet_password).await; monero::close_wallet(&wallet_name, &wallet_password).await;

View file

@ -79,10 +79,24 @@ pub async fn create_dispute(
Custom(Status::Ok, Json(m_dispute)) Custom(Status::Ok, Json(m_dispute))
} }
/// Create a dispute /// Fetch a dispute
#[get("/<did>")] #[get("/<did>")]
pub async fn get_dispute(_token: auth::BearerToken, did: String) -> Custom<Json<models::Dispute>> { pub async fn get_dispute(_token: auth::BearerToken, did: String) -> Custom<Json<models::Dispute>> {
let m_dispute: models::Dispute = dispute::find(&did); let m_dispute: models::Dispute = dispute::find(&did);
Custom(Status::Ok, Json(m_dispute)) Custom(Status::Ok, Json(m_dispute))
} }
/// Create a dispute
#[post("/sign/submit", data = "<r_data>")]
pub async fn sign_and_submit_multisig(
r_data: Json<reqres::SignAndSubmitRequest>,
_token: auth::BearerToken,
) -> Custom<Json<reqres::SignAndSubmitRequest>> {
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 // END JSON APIs

View file

@ -21,7 +21,10 @@ async fn rocket() -> _ {
) )
.mount( .mount(
"/order", "/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("/orders", routes![controller::get_orders])
.mount("/products", routes![controller::get_products]) .mount("/products", routes![controller::get_products])