v0.1.0-beta

This commit is contained in:
creating2morrow 2023-12-18 08:30:04 -05:00
parent b94802711c
commit 57e2be6384
30 changed files with 399 additions and 38 deletions

14
Cargo.lock generated
View file

@ -1932,7 +1932,7 @@ dependencies = [
[[package]] [[package]]
name = "neveko" name = "neveko"
version = "0.4.8-alpha" version = "0.1.0-beta"
dependencies = [ dependencies = [
"env_logger", "env_logger",
"neveko_auth", "neveko_auth",
@ -1946,7 +1946,7 @@ dependencies = [
[[package]] [[package]]
name = "neveko_auth" name = "neveko_auth"
version = "0.4.8-alpha" version = "0.1.0-beta"
dependencies = [ dependencies = [
"env_logger", "env_logger",
"log 0.4.19", "log 0.4.19",
@ -1956,7 +1956,7 @@ dependencies = [
[[package]] [[package]]
name = "neveko_contact" name = "neveko_contact"
version = "0.4.8-alpha" version = "0.1.0-beta"
dependencies = [ dependencies = [
"env_logger", "env_logger",
"log 0.4.19", "log 0.4.19",
@ -1966,7 +1966,7 @@ dependencies = [
[[package]] [[package]]
name = "neveko_core" name = "neveko_core"
version = "0.4.8-alpha" version = "0.1.0-beta"
dependencies = [ dependencies = [
"chrono", "chrono",
"clap", "clap",
@ -1993,7 +1993,7 @@ dependencies = [
[[package]] [[package]]
name = "neveko_gui" name = "neveko_gui"
version = "0.4.8-alpha" version = "0.1.0-beta"
dependencies = [ dependencies = [
"chrono", "chrono",
"console_error_panic_hook", "console_error_panic_hook",
@ -2017,7 +2017,7 @@ dependencies = [
[[package]] [[package]]
name = "neveko_market" name = "neveko_market"
version = "0.4.8-alpha" version = "0.1.0-beta"
dependencies = [ dependencies = [
"chrono", "chrono",
"env_logger", "env_logger",
@ -2028,7 +2028,7 @@ dependencies = [
[[package]] [[package]]
name = "neveko_message" name = "neveko_message"
version = "0.4.8-alpha" version = "0.1.0-beta"
dependencies = [ dependencies = [
"env_logger", "env_logger",
"log 0.4.19", "log 0.4.19",

View file

@ -1,6 +1,6 @@
[package] [package]
name = "neveko" name = "neveko"
version = "0.4.8-alpha" version = "0.1.0-beta"
edition = "2021" edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View file

@ -41,6 +41,7 @@ NEVidebla-EKOnomia (invisible economy)
* just remember to put cli password in the original window, not the log file window * just remember to put cli password in the original window, not the log file window
* https://stackoverflow.com/questions/6674327/redirect-all-output-to-file-in-bash * https://stackoverflow.com/questions/6674327/redirect-all-output-to-file-in-bash
* gui built with rust [egui](https://docs.rs/egui/latest/egui/) * gui built with rust [egui](https://docs.rs/egui/latest/egui/)
* darknet release server links are located at: http://c2m66oddrzozztxyzjegbdwtgbeiibq5vz2tpchmqamrzcahcfoq.b32.i2p/index.txt
## Installation Mananger ## Installation Mananger
@ -111,5 +112,8 @@ most of the complex logic stays in neveko-core, exported from [lib.rs](./neveko-
## Donations ## Donations
This is just a hobby project but if anything here is useful donations are much appreciated! This is a research project as of v0.1.0-beta but if anything here is useful donations are much appreciated!
No need to send xmr, donating time is fine. Just test code if you can and open issues. Thanks! Features and bug fixes aren't guaranteed by donations but they will supply coffee for devs on
sleepless nights!
87TzQS4g6mN4oAcEhcnEHGCxw9bFwMXR8WHJEEZoCd7tPHgcH3NsiCF5FSWSkKYVa7EYJjuosPZBiNAh9LqHaRSiBUhsAcC

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

BIN
assets/msig_management.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

BIN
assets/view_vendors.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

View file

@ -67,4 +67,30 @@
## Market ## Market
WIP ![market main view](../assets/neveko-market_main.png)
* neveko market allows 3 i2p users to create an order
* first you need to have a contact in vendor `enabled` mode
* vendors must also have products in stock
![vendor order management](../assets/vendor_manage_orders.png)
* here vendors can upload delivery info
* all other functionality including payment is automated
* funds will need to manually swept from neveko for now
![view vendors](../assets/view_vendors.png)
* don't forget to `check status` often
* expired jwp will cause errors when orchestrated multisig operations
![customer order management](../assets/customer_manage_orders.png)
* customers are responsible for orchestrated multisig information exchange with `MSIG`
* orders can also be disputed or cancelled from here
![create_jwp](../assets/msig_management.png)
* burden of multisig is on the customer
* click the msig step and use `check` to cycle to the next step
* upon delivery don't forget to `release txset`

View file

@ -1195,7 +1195,7 @@ dependencies = [
[[package]] [[package]]
name = "neveko_auth" name = "neveko_auth"
version = "0.4.8-alpha" version = "0.1.0-beta"
dependencies = [ dependencies = [
"env_logger", "env_logger",
"log 0.4.17", "log 0.4.17",
@ -1205,7 +1205,7 @@ dependencies = [
[[package]] [[package]]
name = "neveko_core" name = "neveko_core"
version = "0.4.8-alpha" version = "0.1.0-beta"
dependencies = [ dependencies = [
"chrono", "chrono",
"clap", "clap",

View file

@ -1,6 +1,6 @@
[package] [package]
name = "neveko_auth" name = "neveko_auth"
version = "0.4.8-alpha" version = "0.1.0-beta"
edition = "2021" edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View file

@ -1195,7 +1195,7 @@ dependencies = [
[[package]] [[package]]
name = "neveko_contact" name = "neveko_contact"
version = "0.4.8-alpha" version = "0.1.0-beta"
dependencies = [ dependencies = [
"env_logger", "env_logger",
"log 0.4.17", "log 0.4.17",
@ -1205,7 +1205,7 @@ dependencies = [
[[package]] [[package]]
name = "neveko_core" name = "neveko_core"
version = "0.4.8-alpha" version = "0.1.0-beta"
dependencies = [ dependencies = [
"chrono", "chrono",
"clap", "clap",

View file

@ -1,6 +1,6 @@
[package] [package]
name = "neveko_contact" name = "neveko_contact"
version = "0.4.8-alpha" version = "0.1.0-beta"
edition = "2021" edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View file

@ -1195,7 +1195,7 @@ dependencies = [
[[package]] [[package]]
name = "neveko_core" name = "neveko_core"
version = "0.4.8-alpha" version = "0.1.0-beta"
dependencies = [ dependencies = [
"chrono", "chrono",
"clap", "clap",

View file

@ -1,6 +1,6 @@
[package] [package]
name = "neveko_core" name = "neveko_core"
version = "0.4.8-alpha" version = "0.1.0-beta"
edition = "2021" edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View file

@ -148,6 +148,13 @@ pub struct Args {
default_value = "false" default_value = "false"
)] )]
pub clear_fts: bool, pub clear_fts: bool,
/// Remove all disputes from db on app startup
#[arg(
long,
help = "this will clear disputes from the database",
default_value = "false"
)]
pub clear_disputes: bool,
/// Manually configure i2p /// Manually configure i2p
#[arg( #[arg(
long, long,

View file

@ -1,6 +1,9 @@
use std::error::Error;
use crate::{ use crate::{
db, db,
models::*, models::*,
monero,
utils, utils,
}; };
use log::{ use log::{
@ -24,6 +27,24 @@ pub fn create(d: Json<Dispute>) -> Dispute {
let s = db::Interface::open(); let s = db::Interface::open();
let k = &d.did; let k = &d.did;
db::Interface::write(&s.env, &s.handle, k, &Dispute::to_db(&new_dispute)); db::Interface::write(&s.env, &s.handle, k, &Dispute::to_db(&new_dispute));
// in order to retrieve all orders, write keys to with dl
let list_key = crate::DISPUTE_LIST_DB_KEY;
let r = db::Interface::read(&s.env, &s.handle, &String::from(list_key));
if r == utils::empty_string() {
debug!("creating dispute index");
}
let dispute_list = [String::from(&r), String::from(&f_did)].join(",");
debug!(
"writing dispute index {} for id: {}",
dispute_list, list_key
);
db::Interface::write(&s.env, &s.handle, &String::from(list_key), &dispute_list);
// restart the dispute aut-settle thread
let cleared = is_dispute_clear(r);
if !cleared {
debug!("restarting dispute auto-settle");
utils::restart_dispute_auto_settle();
}
new_dispute new_dispute
} }
@ -37,3 +58,183 @@ pub fn find(did: &String) -> Dispute {
} }
Dispute::from_db(String::from(did), r) Dispute::from_db(String::from(did), r)
} }
/// Lookup all disputes
pub fn find_all() -> Vec<Dispute> {
let d_s = db::Interface::open();
let d_list_key = crate::DISPUTE_LIST_DB_KEY;
let d_r = db::Interface::read(&d_s.env, &d_s.handle, &String::from(d_list_key));
if d_r == utils::empty_string() {
error!("dispute index not found");
}
let d_v_did = d_r.split(",");
let d_v: Vec<String> = d_v_did.map(|s| String::from(s)).collect();
let mut disputes: Vec<Dispute> = Vec::new();
for o in d_v {
let dispute: Dispute = find(&o);
if dispute.did != utils::empty_string() {
disputes.push(dispute);
}
}
disputes
}
/// Dispute deletion
pub fn delete(did: &String) {
let s = db::Interface::open();
let r = db::Interface::read(&s.env, &s.handle, &String::from(did));
if r == utils::empty_string() {
error!("dispute not found");
return Default::default();
}
db::Interface::delete(&s.env, &s.handle, &String::from(did))
}
/// Triggered on DISPUTE_LAST_CHECK_DB_KEY.
///
/// If the current UNIX timestamp is less than the
///
/// creation date of the dispute plus the one week
///
/// grace period then the dispute is auto-settled.
pub async fn settle_dispute() {
let tick: std::sync::mpsc::Receiver<()> =
schedule_recv::periodic_ms(crate::DISPUTE_CHECK_INTERVAL);
loop {
debug!("running dispute auto-settle thread");
tick.recv().unwrap();
let s = db::Interface::open();
let list_key = crate::DISPUTE_LIST_DB_KEY;
let r = db::Interface::read(&s.env, &s.handle, &String::from(list_key));
if r == utils::empty_string() {
info!("dispute index not found");
}
let v_mid = r.split(",");
let d_vec: Vec<String> = v_mid.map(|s| String::from(s)).collect();
debug!("dispute contents: {:#?}", d_vec);
let cleared = is_dispute_clear(r);
if cleared {
// index was created but cleared
info!("terminating dispute auto-settle thread");
db::Interface::delete(&s.env, &s.handle, list_key);
return;
}
for d in d_vec {
let dispute: Dispute = find(&d);
if dispute.did != utils::empty_string() {
let now = chrono::offset::Utc::now().timestamp();
let settle_date = dispute.created + crate::DISPUTE_AUTO_SETTLE as i64;
if settle_date > now {
let wallet_name = String::from(dispute.orid);
let wallet_password = utils::empty_string();
monero::open_wallet(&wallet_name, &wallet_password).await;
let signed = monero::sign_multisig(dispute.tx_set).await;
let submit = monero::submit_multisig(signed.result.tx_data_hex).await;
monero::close_wallet(&wallet_name, &wallet_password).await;
if submit.result.tx_hash_list.is_empty() {
error!("could not broadcast txset for dispute: {}", &dispute.did);
return;
}
// remove the dispute from the db
remove_from_auto_settle(dispute.did);
}
}
}
}
}
fn is_dispute_clear(r: String) -> bool {
let v_mid = r.split(",");
let v: Vec<String> = v_mid.map(|s| String::from(s)).collect();
debug!("dispute index contents: {:#?}", v);
let limit = v.len() <= 1;
if !limit {
return v.len() >= 2
&& v[v.len() - 1] == utils::empty_string()
&& v[0] == utils::empty_string();
} else {
return limit;
}
}
/// clear dispute from index
fn remove_from_auto_settle(did: String) {
info!("removing id {} from disputes", &did);
let s = db::Interface::open();
let list_key = crate::DISPUTE_LIST_DB_KEY;
let r = db::Interface::read(&s.env, &s.handle, &String::from(list_key));
if r == utils::empty_string() {
debug!("dispute list index is empty");
}
let pre_v_fts = r.split(",");
let v: Vec<String> = pre_v_fts
.map(|s| {
if s != &did {
String::from(s)
} else {
utils::empty_string()
}
})
.collect();
let dispute_list = v.join(",");
debug!(
"writing dipsute index {} for id: {}",
dispute_list, list_key
);
db::Interface::write(&s.env, &s.handle, &String::from(list_key), &dispute_list);
}
/// Executes POST /market/dispute/create
///
/// cancelling the order on the vendor side.
///
/// Customer needs to verify the response and update their lmdb.
///
/// see `cancel_order`
async fn transmit_dispute_request(
contact: &String,
jwp: &String,
request: &Dispute,
) -> Result<Dispute, Box<dyn Error>> {
info!("executing transmit_dispute_request");
let host = utils::get_i2p_http_proxy();
let proxy = reqwest::Proxy::http(&host)?;
let client = reqwest::Client::builder().proxy(proxy).build();
match client?
.post(format!("http://{}/market/dispute/create", contact))
.header("proof", jwp)
.json(&request)
.send()
.await
{
Ok(response) => {
let res = response.json::<Dispute>().await;
debug!("dispute response: {:?}", res);
match res {
Ok(r) => Ok(r),
_ => Ok(Default::default()),
}
}
Err(e) => {
error!("failed to create a dispute due to: {:?}", e);
Ok(Default::default())
}
}
}
/// A decomposition trigger for the dispute request so that the logic
///
/// can be executed from the gui.
pub async fn trigger_dispute_request(contact: &String, dispute: &Dispute) -> Dispute {
info!("executing trigger_dispute_request");
let s = db::Interface::async_open().await;
let k = format!("{}-{}", crate::FTS_JWP_DB_KEY, &contact);
let jwp = db::Interface::async_read(&s.env, &s.handle, &k).await;
let dispute = transmit_dispute_request(contact, &jwp, dispute).await;
// handle a failure to create dispute
if dispute.is_err() {
error!("failed to create dispute");
return Default::default();
}
dispute.unwrap_or(Default::default())
}

View file

@ -28,10 +28,12 @@ pub const ORDER_DB_KEY: &str = "o";
pub const PRODUCT_DB_KEY: &str = "p"; pub const PRODUCT_DB_KEY: &str = "p";
pub const USER_DB_KEY: &str = "u"; pub const USER_DB_KEY: &str = "u";
pub const CONTACT_LIST_DB_KEY: &str = "cl"; pub const CONTACT_LIST_DB_KEY: &str = "cl";
pub const DISPUTE_LIST_DB_KEY: &str = "dl";
pub const MESSAGE_LIST_DB_KEY: &str = "ml"; pub const MESSAGE_LIST_DB_KEY: &str = "ml";
pub const ORDER_LIST_DB_KEY: &str = "ol"; pub const ORDER_LIST_DB_KEY: &str = "ol";
pub const PRODUCT_LIST_DB_KEY: &str = "pl"; pub const PRODUCT_LIST_DB_KEY: &str = "pl";
pub const RX_MESSAGE_DB_KEY: &str = "rx"; pub const RX_MESSAGE_DB_KEY: &str = "rx";
pub const DISPUTE_LAST_CHECK_DB_KEY: &str = "dlc";
pub const FTS_DB_KEY: &str = "fts"; pub const FTS_DB_KEY: &str = "fts";
pub const CUSTOMER_ORDER_LIST_DB_KEY: &str = "olc"; pub const CUSTOMER_ORDER_LIST_DB_KEY: &str = "olc";
pub const MEDIATOR_DB_KEY: &str = "med8"; pub const MEDIATOR_DB_KEY: &str = "med8";
@ -68,4 +70,9 @@ pub const I2P_ZERO_RELEASH_HASH: &str =
pub const LMDB_MAPSIZE: u64 = 1 * 1024 * 1024 * 1024; pub const LMDB_MAPSIZE: u64 = 1 * 1024 * 1024 * 1024;
pub const I2P_CONNECTIVITY_CHECK_INTERVAL: u32 = 600000; pub const I2P_CONNECTIVITY_CHECK_INTERVAL: u32 = 600000;
pub const FTS_RETRY_INTERVAL: u32 = 60000;
/// There is a one week grace period for manual intervention of disputes
pub const DISPUTE_AUTO_SETTLE: u32 = 1000 * 60 * 60 * 24 * 7;
/// Daily dispute auto-settle check interval
pub const DISPUTE_CHECK_INTERVAL: u32 = 1000 * 60 * 60 * 24;
// DO NOT EDIT BELOW THIS LINE // DO NOT EDIT BELOW THIS LINE

View file

@ -422,7 +422,7 @@ fn remove_from_fts(mid: String) {
/// ///
/// failed-to-send message. /// failed-to-send message.
pub async fn retry_fts() { pub async fn retry_fts() {
let tick: std::sync::mpsc::Receiver<()> = schedule_recv::periodic_ms(60000); let tick: std::sync::mpsc::Receiver<()> = schedule_recv::periodic_ms(crate::FTS_RETRY_INTERVAL);
loop { loop {
debug!("running retry failed-to-send thread"); debug!("running retry failed-to-send thread");
tick.recv().unwrap(); tick.recv().unwrap();

View file

@ -238,10 +238,13 @@ pub async fn sign_and_submit_multisig(
tx_data_hex: &String, tx_data_hex: &String,
) -> reqres::XmrRpcSubmitMultisigResponse { ) -> reqres::XmrRpcSubmitMultisigResponse {
info!("signing and submitting multisig"); info!("signing and submitting multisig");
let wallet_password = utils::empty_string();
monero::open_wallet(&orid, &wallet_password).await;
let r_sign: reqres::XmrRpcSignMultisigResponse = let r_sign: reqres::XmrRpcSignMultisigResponse =
monero::sign_multisig(String::from(tx_data_hex)).await; monero::sign_multisig(String::from(tx_data_hex)).await;
let r_submit: reqres::XmrRpcSubmitMultisigResponse = let r_submit: reqres::XmrRpcSubmitMultisigResponse =
monero::submit_multisig(r_sign.result.tx_data_hex).await; monero::submit_multisig(r_sign.result.tx_data_hex).await;
monero::close_wallet(&orid, &wallet_password).await;
if r_submit.result.tx_hash_list.is_empty() { if r_submit.result.tx_hash_list.is_empty() {
error!("unable to submit payment for order: {}", orid); error!("unable to submit payment for order: {}", orid);
} }

View file

@ -2,6 +2,7 @@ use crate::{
args, args,
contact, contact,
db, db,
dispute,
gpg, gpg,
i2p, i2p,
message, message,
@ -348,6 +349,16 @@ pub fn order_to_json(o: &reqres::OrderRequest) -> Json<reqres::OrderRequest> {
Json(r_order) Json(r_order)
} }
pub fn dispute_to_json(d: &models::Dispute) -> Json<models::Dispute> {
let dispute: models::Dispute = models::Dispute {
created: d.created,
did: String::from(&d.did),
orid: String::from(&d.orid),
tx_set: String::from(&d.tx_set),
};
Json(dispute)
}
/// Instead of putting `String::from("")` /// Instead of putting `String::from("")`
pub fn empty_string() -> String { pub fn empty_string() -> String {
String::from("") String::from("")
@ -537,6 +548,9 @@ pub async fn start_up() {
if args.clear_fts { if args.clear_fts {
clear_fts(); clear_fts();
} }
if args.clear_disputes {
clear_disputes();
}
gen_signing_keys(); gen_signing_keys();
if !is_using_remote_node() { if !is_using_remote_node() {
monero::start_daemon(); monero::start_daemon();
@ -565,9 +579,11 @@ pub async fn start_up() {
gen_app_gpg().await; gen_app_gpg().await;
gen_app_wallet(&wallet_password).await; gen_app_wallet(&wallet_password).await;
start_gui(); start_gui();
// start async background tasks here
{ {
tokio::spawn(async { tokio::spawn(async {
message::retry_fts().await; message::retry_fts().await;
dispute::settle_dispute().await;
}); });
} }
info!("{} - neveko is online", env); info!("{} - neveko is online", env);
@ -608,11 +624,25 @@ pub fn restart_retry_fts() {
}); });
} }
/// We can restart dispute auto-settle from since it gets terminated when empty
pub fn restart_dispute_auto_settle() {
tokio::spawn(async move {
dispute::settle_dispute().await;
});
}
/// Called on app startup if `--clear-fts` flag is passed. /// Called on app startup if `--clear-fts` flag is passed.
fn clear_fts() { fn clear_fts() {
info!("clear fts"); info!("clear fts");
let s = db::Interface::open(); let s = db::Interface::open();
db::Interface::delete(&s.env, &s.handle, "fts"); db::Interface::delete(&s.env, &s.handle, crate::FTS_DB_KEY);
}
/// Called on app startup if `--clear-dispute` flag is passed.
fn clear_disputes() {
info!("clear_disputes");
let s = db::Interface::open();
db::Interface::delete(&s.env, &s.handle, crate::DISPUTE_LIST_DB_KEY);
} }
/// Destroy temp files /// Destroy temp files

4
neveko-gui/Cargo.lock generated
View file

@ -2415,7 +2415,7 @@ dependencies = [
[[package]] [[package]]
name = "neveko_core" name = "neveko_core"
version = "0.4.8-alpha" version = "0.1.0-beta"
dependencies = [ dependencies = [
"chrono", "chrono",
"clap", "clap",
@ -2442,7 +2442,7 @@ dependencies = [
[[package]] [[package]]
name = "neveko_gui" name = "neveko_gui"
version = "0.4.8-alpha" version = "0.1.0-beta"
dependencies = [ dependencies = [
"bytemuck", "bytemuck",
"chrono", "chrono",

View file

@ -1,6 +1,6 @@
[package] [package]
name = "neveko_gui" name = "neveko_gui"
version = "0.4.8-alpha" version = "0.1.0-beta"
authors = ["emilk", "creating2morrow"] authors = ["emilk", "creating2morrow"]
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
edition = "2021" edition = "2021"

View file

@ -116,6 +116,8 @@ pub struct MarketApp {
submit_txset_rx: Receiver<bool>, submit_txset_rx: Receiver<bool>,
cancel_request_tx: Sender<models::Order>, cancel_request_tx: Sender<models::Order>,
cancel_request_rx: Receiver<models::Order>, cancel_request_rx: Receiver<models::Order>,
dispute_request_tx: Sender<models::Dispute>,
dispute_request_rx: Receiver<models::Dispute>,
// ship_request_tx: Sender<models::Order>, // ship_request_tx: Sender<models::Order>,
// ship_request_rx: Receiver<models::Order>, // ship_request_rx: Receiver<models::Order>,
upload_dinfo_tx: Sender<bool>, upload_dinfo_tx: Sender<bool>,
@ -144,6 +146,7 @@ impl Default for MarketApp {
let (submit_order_tx, submit_order_rx) = std::sync::mpsc::channel(); let (submit_order_tx, submit_order_rx) = std::sync::mpsc::channel();
// let (ship_request_tx, ship_request_rx) = std::sync::mpsc::channel(); // let (ship_request_tx, ship_request_rx) = std::sync::mpsc::channel();
let (cancel_request_tx, cancel_request_rx) = std::sync::mpsc::channel(); let (cancel_request_tx, cancel_request_rx) = std::sync::mpsc::channel();
let (dispute_request_tx, dispute_request_rx) = std::sync::mpsc::channel();
let (our_prepare_info_tx, our_prepare_info_rx) = std::sync::mpsc::channel(); let (our_prepare_info_tx, our_prepare_info_rx) = std::sync::mpsc::channel();
let (our_make_info_tx, our_make_info_rx) = std::sync::mpsc::channel(); let (our_make_info_tx, our_make_info_rx) = std::sync::mpsc::channel();
let (order_xmr_address_tx, order_xmr_address_rx) = std::sync::mpsc::channel(); let (order_xmr_address_tx, order_xmr_address_rx) = std::sync::mpsc::channel();
@ -219,6 +222,8 @@ impl Default for MarketApp {
s_order: Default::default(), s_order: Default::default(),
cancel_request_rx, cancel_request_rx,
cancel_request_tx, cancel_request_tx,
dispute_request_rx,
dispute_request_tx,
// ship_request_rx, // ship_request_rx,
// ship_request_tx, // ship_request_tx,
submit_order_rx, submit_order_rx,
@ -341,6 +346,14 @@ impl eframe::App for MarketApp {
self.is_loading = false; self.is_loading = false;
} }
if let Ok(disputed) = self.dispute_request_rx.try_recv() {
if disputed.tx_set != utils::empty_string() {
log::info!("dispute in progress");
}
self.is_loading = false;
self.is_customer_viewing_orders = false;
}
if let Ok(finalized) = self.submit_txset_rx.try_recv() { if let Ok(finalized) = self.submit_txset_rx.try_recv() {
if !finalized { if !finalized {
log::error!("failure to finalize shipment please contact vendor") log::error!("failure to finalize shipment please contact vendor")
@ -734,10 +747,6 @@ impl eframe::App for MarketApp {
} }
}); });
} }
// ui.horizontal(|ui| {
// ui.label("Create Dispute: \t\t");
// if ui.button("Dispute").clicked() {}
// });
ui.label("\n"); ui.label("\n");
if ui.button("Exit").clicked() { if ui.button("Exit").clicked() {
self.is_managing_multisig = false; self.is_managing_multisig = false;
@ -816,6 +825,7 @@ impl eframe::App for MarketApp {
.column(Column::auto()) .column(Column::auto())
.column(Column::auto()) .column(Column::auto())
.column(Column::auto()) .column(Column::auto())
.column(Column::auto())
.min_scrolled_height(0.0); .min_scrolled_height(0.0);
table table
@ -838,6 +848,9 @@ impl eframe::App for MarketApp {
header.col(|ui| { header.col(|ui| {
ui.strong(""); ui.strong("");
}); });
header.col(|ui| {
ui.strong("");
});
}) })
.body(|mut body| { .body(|mut body| {
for o in &self.customer_orders { for o in &self.customer_orders {
@ -898,6 +911,25 @@ impl eframe::App for MarketApp {
} }
}); });
}); });
row.col(|ui| {
ui.style_mut().wrap = Some(false);
ui.horizontal(|ui| {
if ui.button("Dispute").clicked() {
let vendor_prefix = String::from(crate::GUI_OVL_DB_KEY);
let vendor = utils::search_gui_db(
vendor_prefix,
self.m_order.orid.clone(),
);
self.is_loading = true;
create_dispute_req(
&self.m_order.orid.clone(),
ctx.clone(),
self.dispute_request_tx.clone(),
&vendor,
)
}
});
});
}); });
} }
}); });
@ -1003,7 +1035,8 @@ impl eframe::App for MarketApp {
.labelled_by(delivery_info.id); .labelled_by(delivery_info.id);
}); });
if self.new_order.orid != utils::empty_string() if self.new_order.orid != utils::empty_string()
&& self.upload_dinfo_str != utils::empty_string() { && self.upload_dinfo_str != utils::empty_string()
{
if ui.button("Trigger NASR").clicked() { if ui.button("Trigger NASR").clicked() {
// upload delivery info // upload delivery info
let dinfo_str = String::from(&self.upload_dinfo_str); let dinfo_str = String::from(&self.upload_dinfo_str);
@ -2402,3 +2435,53 @@ fn upload_dinfo_req(dinfo: Vec<u8>, orid: String, ctx: egui::Context, tx: Sender
ctx.request_repaint(); ctx.request_repaint();
}); });
} }
fn create_dispute_req(
orid: &String,
ctx: egui::Context,
tx: Sender<models::Dispute>,
contact: &String,
) {
let d_orid: String = String::from(orid);
let a_contact: String = String::from(contact);
tokio::spawn(async move {
log::info!("async create_dispute_req");
// generate address for refund
let wallet_password =
std::env::var(neveko_core::MONERO_WALLET_PASSWORD).unwrap_or(String::from("password"));
let wallet_name = String::from(neveko_core::APP_NAME);
monero::open_wallet(&wallet_name, &wallet_password).await;
let address_res = monero::get_address().await;
monero::close_wallet(&wallet_name, &wallet_password).await;
// generate a txset for the mediator
let wallet_password = utils::empty_string();
monero::open_wallet(&d_orid, &wallet_password).await;
let transfer = monero::sweep_all(String::from(address_res.result.address)).await;
monero::close_wallet(&d_orid, &wallet_password).await;
if transfer.result.multisig_txset.is_empty() {
log::error!("could not create txset");
let _ = tx.send(Default::default());
ctx.request_repaint();
return;
}
let dispute: models::Dispute = models::Dispute {
orid: String::from(&d_orid),
tx_set: transfer.result.multisig_txset,
..Default::default()
};
let res = dispute::trigger_dispute_request(&a_contact, &dispute).await;
if res.created != 0 {
// cancel the order and write the dispute to the db
let wallet_password = std::env::var(neveko_core::MONERO_WALLET_PASSWORD)
.unwrap_or(String::from("password"));
monero::open_wallet(&String::from(neveko_core::APP_NAME), &wallet_password).await;
let pre_sign = monero::sign(String::from(&d_orid)).await;
monero::close_wallet(&String::from(neveko_core::APP_NAME), &wallet_password).await;
order::cancel_order(&d_orid, &pre_sign.result.signature).await;
let j_dispute = utils::dispute_to_json(&res);
dispute::create(j_dispute);
let _ = tx.send(res);
ctx.request_repaint();
}
});
}

View file

@ -39,7 +39,7 @@ fn main() -> Result<(), eframe::Error> {
}) })
}); });
eframe::run_native( eframe::run_native(
"neveko-gui-v0.4.8-alpha", "neveko-gui-v0.1.0-beta",
options, options,
Box::new(|cc| Box::new(neveko_gui::WrapApp::new(cc))), Box::new(|cc| Box::new(neveko_gui::WrapApp::new(cc))),
) )

View file

@ -1244,7 +1244,7 @@ dependencies = [
[[package]] [[package]]
name = "neveko_core" name = "neveko_core"
version = "0.4.8-alpha" version = "0.1.0-beta"
dependencies = [ dependencies = [
"chrono", "chrono",
"clap", "clap",
@ -1271,7 +1271,7 @@ dependencies = [
[[package]] [[package]]
name = "neveko_market" name = "neveko_market"
version = "0.4.8-alpha" version = "0.1.0-beta"
dependencies = [ dependencies = [
"chrono", "chrono",
"env_logger", "env_logger",

View file

@ -1,6 +1,6 @@
[package] [package]
name = "neveko_market" name = "neveko_market"
version = "0.4.8-alpha" version = "0.1.0-beta"
edition = "2021" edition = "2021"
publish = false publish = false

View file

@ -1195,7 +1195,7 @@ dependencies = [
[[package]] [[package]]
name = "neveko_core" name = "neveko_core"
version = "0.4.8-alpha" version = "0.1.0-beta"
dependencies = [ dependencies = [
"chrono", "chrono",
"clap", "clap",
@ -1222,7 +1222,7 @@ dependencies = [
[[package]] [[package]]
name = "neveko_message" name = "neveko_message"
version = "0.4.8-alpha" version = "0.1.0-beta"
dependencies = [ dependencies = [
"env_logger", "env_logger",
"log 0.4.17", "log 0.4.17",

View file

@ -1,6 +1,6 @@
[package] [package]
name = "neveko_message" name = "neveko_message"
version = "0.4.8-alpha" version = "0.1.0-beta"
edition = "2021" edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View file

@ -258,7 +258,7 @@ pub async fn finalize_order(
} }
/// Create a dispute /// Create a dispute
#[post("/create", data = "<dispute>")] #[post("/dispute/create", data = "<dispute>")]
pub async fn create_dispute( pub async fn create_dispute(
dispute: Json<models::Dispute>, dispute: Json<models::Dispute>,
_jwp: proof::PaymentProof, _jwp: proof::PaymentProof,