fix prove payment zero conf retry logic

This commit is contained in:
creating2morrow 2023-06-12 11:37:39 -04:00
parent c75ff4e4f1
commit 39827212f6
9 changed files with 178 additions and 87 deletions

View file

@ -165,7 +165,6 @@ pub fn trust_gpg(key: String) {
/// Get invoice for jwp creation /// Get invoice for jwp creation
pub async fn request_invoice(contact: String) -> Result<reqres::Invoice, Box<dyn Error>> { pub async fn request_invoice(contact: String) -> Result<reqres::Invoice, Box<dyn Error>> {
// TODO(c2m): Error handling for http 402 status
let host = utils::get_i2p_http_proxy(); let host = utils::get_i2p_http_proxy();
let proxy = reqwest::Proxy::http(&host)?; let proxy = reqwest::Proxy::http(&host)?;
let client = reqwest::Client::builder().proxy(proxy).build(); let client = reqwest::Client::builder().proxy(proxy).build();
@ -193,7 +192,6 @@ pub async fn request_invoice(contact: String) -> Result<reqres::Invoice, Box<dyn
/// ///
/// for gpg key removal. /// for gpg key removal.
pub async fn add_contact_request(contact: String, prune: u32) -> Result<Contact, Box<dyn Error>> { pub async fn add_contact_request(contact: String, prune: u32) -> Result<Contact, Box<dyn Error>> {
// TODO(c2m): Error handling for http 402 status
let host = utils::get_i2p_http_proxy(); let host = utils::get_i2p_http_proxy();
let proxy = reqwest::Proxy::http(&host)?; let proxy = reqwest::Proxy::http(&host)?;
let client = reqwest::Client::builder().proxy(proxy).build(); let client = reqwest::Client::builder().proxy(proxy).build();

View file

@ -19,7 +19,6 @@ pub const APP_NAME: &str = "neveko";
pub const NEVEKO_JWP_SECRET_KEY: &str = "NEVEKO_JWP_SECRET_KEY"; pub const NEVEKO_JWP_SECRET_KEY: &str = "NEVEKO_JWP_SECRET_KEY";
pub const NEVEKO_JWT_SECRET_KEY: &str = "NEVEKO_JWT_SECRET_KEY"; pub const NEVEKO_JWT_SECRET_KEY: &str = "NEVEKO_JWT_SECRET_KEY";
// TODO(c2m): better handling of setting initial wallet password
/// Environment variable for injecting wallet password /// Environment variable for injecting wallet password
pub const MONERO_WALLET_PASSWORD: &str = "MONERO_WALLET_PASSWORD"; pub const MONERO_WALLET_PASSWORD: &str = "MONERO_WALLET_PASSWORD";

View file

@ -45,10 +45,10 @@ impl TransactionType {
pub fn value(&self) -> String { pub fn value(&self) -> String {
match *self { match *self {
Self::Failed => String::from("failed"), Self::Failed => String::from("failed"),
Self::In => String::from("In"), Self::In => String::from("in"),
Self::Out => String::from("Out"), Self::Out => String::from("out"),
Self::Pending => String::from("Pending"), Self::Pending => String::from("pending"),
Self::Pool => String::from("Pool"), Self::Pool => String::from("pool"),
} }
} }
pub fn propogated(tx_type: String) -> bool { pub fn propogated(tx_type: String) -> bool {

View file

@ -117,7 +117,6 @@ pub async fn create_jwp(proof: &TxProof) -> String {
/// Send transaction proof to contact for JWP generation /// Send transaction proof to contact for JWP generation
pub async fn prove_payment(contact: String, txp: &TxProof) -> Result<reqres::Jwp, Box<dyn Error>> { pub async fn prove_payment(contact: String, txp: &TxProof) -> Result<reqres::Jwp, Box<dyn Error>> {
// TODO(c2m): Error handling for http 402 status
let host = utils::get_i2p_http_proxy(); let host = utils::get_i2p_http_proxy();
let proxy = reqwest::Proxy::http(&host)?; let proxy = reqwest::Proxy::http(&host)?;
let client = reqwest::Client::builder().proxy(proxy).build(); let client = reqwest::Client::builder().proxy(proxy).build();

View file

@ -112,6 +112,15 @@ pub struct Destination {
pub amount: u128, pub amount: u128,
} }
impl Default for Destination {
fn default() -> Self {
Destination {
address: utils::empty_string(),
amount: 0,
}
}
}
#[derive(Deserialize, Serialize, Debug)] #[derive(Deserialize, Serialize, Debug)]
pub struct XmrRpcTransferParams { pub struct XmrRpcTransferParams {
pub destinations: Vec<Destination>, pub destinations: Vec<Destination>,
@ -438,7 +447,8 @@ pub struct Transfer {
pub address: String, pub address: String,
pub amount: u128, pub amount: u128,
pub amounts: Vec<u128>, pub amounts: Vec<u128>,
pub confirmations: u64, /// On zero conf this field is missing
pub confirmations: Option<u64>,
pub double_spend_seen: bool, pub double_spend_seen: bool,
pub fee: u128, pub fee: u128,
pub height: u64, pub height: u64,
@ -984,7 +994,7 @@ impl Default for XmrRpcGetTxByIdResponse {
address: utils::empty_string(), address: utils::empty_string(),
amount: 0, amount: 0,
amounts: Vec::new(), amounts: Vec::new(),
confirmations: 0, confirmations: None,
double_spend_seen: false, double_spend_seen: false,
fee: 0, fee: 0,
height: 0, height: 0,

View file

@ -716,6 +716,7 @@ pub async fn can_transfer(invoice: u128) -> bool {
/// Gui toggle for vendor mode /// Gui toggle for vendor mode
pub fn toggle_vendor_enabled() -> bool { pub fn toggle_vendor_enabled() -> bool {
// TODO(c2m): Dont toggle vendors with orders status != Delivered
let s = db::Interface::open(); let s = db::Interface::open();
let r = db::Interface::read(&s.env, &s.handle, contact::NEVEKO_VENDOR_ENABLED); let r = db::Interface::read(&s.env, &s.handle, contact::NEVEKO_VENDOR_ENABLED);
if r != contact::NEVEKO_VENDOR_MODE_ON { if r != contact::NEVEKO_VENDOR_MODE_ON {

View file

@ -61,6 +61,7 @@ pub struct AddressBookApp {
invoice_rx: Receiver<reqres::Invoice>, invoice_rx: Receiver<reqres::Invoice>,
is_adding: bool, is_adding: bool,
is_composing: bool, is_composing: bool,
is_approving_jwp: bool,
is_estimating_fee: bool, is_estimating_fee: bool,
is_pinging: bool, is_pinging: bool,
is_loading: bool, is_loading: bool,
@ -110,6 +111,7 @@ impl Default for AddressBookApp {
invoice_rx, invoice_rx,
is_adding: false, is_adding: false,
is_composing: false, is_composing: false,
is_approving_jwp: false,
is_estimating_fee: false, is_estimating_fee: false,
is_loading: false, is_loading: false,
is_message_sent: false, is_message_sent: false,
@ -275,8 +277,10 @@ impl eframe::App for AddressBookApp {
d, d,
self.status.i2p.clone(), self.status.i2p.clone(),
expire, expire,
false,
); );
self.is_loading = true; self.is_loading = true;
self.is_approving_jwp = false;
} }
} }
if ui.button("Exit").clicked() { if ui.button("Exit").clicked() {
@ -295,9 +299,14 @@ impl eframe::App for AddressBookApp {
.title_bar(false) .title_bar(false)
.id(egui::Id::new(self.status.i2p.clone())) .id(egui::Id::new(self.status.i2p.clone()))
.show(&ctx, |ui| { .show(&ctx, |ui| {
if self.is_pinging { if self.is_pinging || self.is_loading {
let spinner_text = if self.is_loading {
"retrying payment proof... "
} else {
"pinging..."
};
ui.add(egui::Spinner::new()); ui.add(egui::Spinner::new());
ui.label("pinging..."); ui.label(spinner_text);
} }
let status = if self.s_contact.xmr_address != utils::empty_string() { let status = if self.s_contact.xmr_address != utils::empty_string() {
"online" "online"
@ -323,6 +332,7 @@ impl eframe::App for AddressBookApp {
); );
self.approve_payment = true; self.approve_payment = true;
self.showing_status = false; self.showing_status = false;
self.is_approving_jwp = true;
} }
} }
if !self.status.signed_key { if !self.status.signed_key {
@ -346,6 +356,21 @@ impl eframe::App for AddressBookApp {
self.showing_status = false; self.showing_status = false;
} }
} }
if self.status.txp != utils::empty_string()
&& self.status.jwp == utils::empty_string()
&& status == "online" {
if ui.button("Prove Retry").clicked() {
send_payment_req(
self.payment_tx.clone(),
ctx.clone(),
Default::default(),
self.status.i2p.clone(),
expire as u64,
true,
);
self.is_loading = true;
}
}
ui.horizontal(|ui| { ui.horizontal(|ui| {
let nick_label = ui.label("nick: "); let nick_label = ui.label("nick: ");
ui.text_edit_singleline(&mut self.add_nick) ui.text_edit_singleline(&mut self.add_nick)
@ -357,12 +382,16 @@ impl eframe::App for AddressBookApp {
} }
if ui.button("Exit").clicked() { if ui.button("Exit").clicked() {
self.showing_status = false; self.showing_status = false;
self.is_loading = false;
} }
}); });
// Main panel for adding contacts // Main panel for adding contacts
//----------------------------------------------------------------------------------- //-----------------------------------------------------------------------------------
egui::CentralPanel::default().show(ctx, |ui| { egui::CentralPanel::default().show(ctx, |ui| {
if self.is_approving_jwp {
ui.add(egui::Spinner::new());
}
ui.heading("Add Contact"); ui.heading("Add Contact");
ui.label( ui.label(
"____________________________________________________________________________\n", "____________________________________________________________________________\n",
@ -643,13 +672,15 @@ fn send_payment_req(
d: reqres::Destination, d: reqres::Destination,
contact: String, contact: String,
expire: u64, expire: u64,
retry: bool,
) { ) {
log::debug!("async send_payment_req"); log::debug!("async send_payment_req");
log::debug!("cleaning stale jwp values"); log::debug!("cleaning stale jwp values");
tokio::spawn(async move {
if !retry {
utils::clear_gui_db(String::from("gui-txp"), String::from(&contact)); utils::clear_gui_db(String::from("gui-txp"), String::from(&contact));
utils::clear_gui_db(String::from("gui-jwp"), String::from(&contact)); utils::clear_gui_db(String::from("gui-jwp"), String::from(&contact));
utils::clear_gui_db(String::from("gui-exp"), String::from(&contact)); utils::clear_gui_db(String::from("gui-exp"), String::from(&contact));
tokio::spawn(async move {
let ptxp_address = String::from(&d.address); let ptxp_address = String::from(&d.address);
let ftxp_address = String::from(&d.address); let ftxp_address = String::from(&d.address);
log::debug!("sending {} piconero(s) to: {}", &d.amount, &d.address); log::debug!("sending {} piconero(s) to: {}", &d.amount, &d.address);
@ -686,18 +717,26 @@ fn send_payment_req(
String::from(&contact), String::from(&contact),
String::from(&ftxp.signature), String::from(&ftxp.signature),
); );
utils::write_gui_db(
String::from("gui-txp-hash"),
String::from(&contact),
String::from(&ftxp.hash),
);
utils::write_gui_db(
String::from("gui-txp-sig"),
String::from(&contact),
String::from(&ftxp.signature),
);
utils::write_gui_db(
String::from("gui-txp-subaddress"),
String::from(&contact),
String::from(&ftxp.subaddress),
);
log::debug!( log::debug!(
"proving payment to {} for: {}", "proving payment to {} for: {}",
String::from(&contact), String::from(&contact),
&ftxp.hash &ftxp.hash
); );
monero::close_wallet(&wallet_name, &wallet_password).await;
// if we made it this far we can now request a JWP from our friend
// wait a bit for the tx to propogate
tokio::time::sleep(std::time::Duration::from_secs(
crate::BLOCK_TIME_IN_SECS_EST,
))
.await;
match proof::prove_payment(String::from(&contact), &ftxp).await { match proof::prove_payment(String::from(&contact), &ftxp).await {
Ok(result) => { Ok(result) => {
utils::write_gui_db( utils::write_gui_db(
@ -721,6 +760,55 @@ fn send_payment_req(
} }
_ => log::error!("failed to obtain jwp"), _ => log::error!("failed to obtain jwp"),
} }
monero::close_wallet(&wallet_name, &wallet_password).await;
// if we made it this far we can now request a JWP from our friend
// wait a bit for the tx to propogate
tokio::time::sleep(std::time::Duration::from_secs(
crate::BLOCK_TIME_IN_SECS_EST,
))
.await;
}
if retry {
let k_hash = String::from("gui-txp-hash");
let k_sig = String::from("gui-txp-sig");
let k_subaddress = String::from("gui-txp-subaddress");
let hash = utils::search_gui_db(k_hash, String::from(&contact));
let signature = utils::search_gui_db(k_sig, String::from(&contact));
let subaddress = utils::search_gui_db(k_subaddress, String::from(&contact));
let ftxp: proof::TxProof = proof::TxProof {
subaddress,
confirmations: 0,
hash: String::from(&hash),
message: utils::empty_string(),
signature,
};
log::debug!(
"proving payment to {} for: {}",
String::from(&contact),
&ftxp.hash
);
match proof::prove_payment(String::from(&contact), &ftxp).await {
Ok(result) => {
utils::write_gui_db(
String::from("gui-jwp"),
String::from(&contact),
String::from(&result.jwp),
);
// this is just an estimate expiration but should suffice
let seconds: i64 = expire as i64 * 2 * 60;
// subtract 120 seconds since we had to wait for one confirmation
let grace: i64 = seconds - BLOCK_TIME_IN_SECS_EST as i64;
let unix: i64 = chrono::offset::Utc::now().timestamp() + grace;
utils::write_gui_db(
String::from("gui-exp"),
String::from(&contact),
format!("{}", unix),
);
ctx.request_repaint();
}
_ => log::error!("failed to obtain jwp"),
}
}
let _ = tx.send(true); let _ = tx.send(true);
ctx.request_repaint(); ctx.request_repaint();
}); });

View file

@ -365,7 +365,6 @@ impl eframe::App for HomeApp {
self.s_xmr_rpc_ver.result.version, address, unlocked_balance, locked_balance, self.s_xmr_rpc_ver.result.version, address, unlocked_balance, locked_balance,
unlock_time, xmrd_info.nettype, xmrd_info.top_block_hash, xmrd_info.height, xmrd_info.synchronized, unlock_time, xmrd_info.nettype, xmrd_info.top_block_hash, xmrd_info.height, xmrd_info.synchronized,
db_size, free_space, xmrd_info.version)); db_size, free_space, xmrd_info.version));
// TODO(c2m): pull in more xmr blockchain information?
}); });
ui.label("____________________________________________________________________\n"); ui.label("____________________________________________________________________\n");
ui.label("\n"); ui.label("\n");

View file

@ -5,9 +5,6 @@ use sha2::{
Sha512, Sha512,
}; };
/// TODO(c2m): Create a more secure locking mechanism
///
/// is there a way to trigger system screen lock on the machine???
#[derive(PartialEq)] #[derive(PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "serde", serde(default))] #[cfg_attr(feature = "serde", serde(default))]