diff --git a/Cargo.lock b/Cargo.lock index e81ed65..50720bd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3587,7 +3587,7 @@ checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" [[package]] name = "plain_bitnames" -version = "0.5.11" +version = "0.5.12" dependencies = [ "addr", "anyhow", @@ -3626,7 +3626,7 @@ dependencies = [ [[package]] name = "plain_bitnames_app" -version = "0.5.11" +version = "0.5.12" dependencies = [ "anyhow", "async_zmq", diff --git a/Cargo.toml b/Cargo.toml index ef09355..51ba13d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ members = [ [workspace.package] authors = [ "Ash Manning " ] edition = "2021" -version = "0.5.11" +version = "0.5.12" [workspace.dependencies.bip300301] git = "https://github.com/Ash-L2L/bip300301.git" diff --git a/app/gui/activity/bitname_explorer.rs b/app/gui/activity/bitname_explorer.rs deleted file mode 100644 index b0aaf4a..0000000 --- a/app/gui/activity/bitname_explorer.rs +++ /dev/null @@ -1,42 +0,0 @@ -use eframe::egui::{self}; - -use crate::{ - app::App, - gui::util::{InnerResponseExt, UiExt}, -}; - -#[derive(Debug, Default)] -pub struct BitNameExplorer; - -impl BitNameExplorer { - pub fn show(&mut self, app: &mut App, ui: &mut egui::Ui) { - let bitnames = app.node.bitnames(); - egui::CentralPanel::default().show_inside(ui, |ui| match bitnames { - Err(node_err) => { - let resp = - ui.monospace_selectable_multiline(node_err.to_string()); - Some(resp) - } - Ok(bitnames) => bitnames - .into_iter() - .map(|(bitname_id, bitname_data)| { - { - ui.horizontal(|ui| { - ui.monospace_selectable_singleline( - true, - format!( - "BitName ID: {}", - hex::encode(bitname_id.0) - ), - ) | crate::gui::bitname_explorer::show_bitname_data( - ui, - &bitname_data, - ) - }) - } - .join() - }) - .reduce(|resp0, resp1| resp0 | resp1), - }); - } -} diff --git a/app/gui/activity/mempool_explorer.rs b/app/gui/activity/mempool_explorer.rs index 47c6e58..c04744c 100644 --- a/app/gui/activity/mempool_explorer.rs +++ b/app/gui/activity/mempool_explorer.rs @@ -9,11 +9,11 @@ use plain_bitnames::{ use crate::app::App; #[derive(Default)] -pub struct MemPoolExplorer { +pub struct MempoolExplorer { current: usize, } -impl MemPoolExplorer { +impl MempoolExplorer { pub fn show(&mut self, app: &mut App, ui: &mut egui::Ui) { let transactions = app.node.get_all_transactions().unwrap_or_default(); let utxos = app.wallet.get_utxos().unwrap_or_default(); diff --git a/app/gui/activity/mod.rs b/app/gui/activity/mod.rs index 0d34e96..b214e70 100644 --- a/app/gui/activity/mod.rs +++ b/app/gui/activity/mod.rs @@ -1,29 +1,28 @@ use eframe::egui; +use strum::{EnumIter, IntoEnumIterator}; use crate::app::App; -mod bitname_explorer; mod block_explorer; mod mempool_explorer; -use bitname_explorer::BitNameExplorer; use block_explorer::BlockExplorer; -use mempool_explorer::MemPoolExplorer; +use mempool_explorer::MempoolExplorer; #[allow(clippy::enum_variant_names)] -#[derive(Debug, Default, Eq, PartialEq)] +#[derive(Debug, Default, EnumIter, Eq, PartialEq, strum::Display)] enum Tab { - BitNameExplorer, #[default] + #[strum(to_string = "Block Explorer")] BlockExplorer, - MemPoolExplorer, + #[strum(to_string = "Mempool Explorer")] + MempoolExplorer, } pub struct Activity { tab: Tab, - bitname_explorer: BitNameExplorer, block_explorer: BlockExplorer, - mempool_explorer: MemPoolExplorer, + mempool_explorer: MempoolExplorer, } impl Activity { @@ -31,40 +30,25 @@ impl Activity { let height = app.node.get_height().unwrap_or(0); Self { tab: Default::default(), - bitname_explorer: BitNameExplorer, block_explorer: BlockExplorer::new(height), mempool_explorer: Default::default(), } } pub fn show(&mut self, app: &mut App, ui: &mut egui::Ui) { - egui::TopBottomPanel::top("coins_tabs").show(ui.ctx(), |ui| { + egui::TopBottomPanel::top("activity_tabs").show(ui.ctx(), |ui| { ui.horizontal(|ui| { - ui.selectable_value( - &mut self.tab, - Tab::BlockExplorer, - "block explorer", - ); - ui.selectable_value( - &mut self.tab, - Tab::MemPoolExplorer, - "mempool explorer", - ); - ui.selectable_value( - &mut self.tab, - Tab::BitNameExplorer, - "bitname explorer", - ); + Tab::iter().for_each(|tab_variant| { + let tab_name = tab_variant.to_string(); + ui.selectable_value(&mut self.tab, tab_variant, tab_name); + }) }); }); egui::CentralPanel::default().show(ui.ctx(), |ui| match self.tab { - Tab::BitNameExplorer => { - self.bitname_explorer.show(app, ui); - } Tab::BlockExplorer => { self.block_explorer.show(app, ui); } - Tab::MemPoolExplorer => { + Tab::MempoolExplorer => { self.mempool_explorer.show(app, ui); } }); diff --git a/app/gui/bitname_explorer.rs b/app/gui/bitname_explorer.rs deleted file mode 100644 index 152031d..0000000 --- a/app/gui/bitname_explorer.rs +++ /dev/null @@ -1,123 +0,0 @@ -use eframe::egui::{self, Response}; - -use plain_bitnames::{ - node, - types::{hashes::BitName, BitNameData}, -}; - -use super::util::{InnerResponseExt, UiExt}; -use crate::app::App; - -/// result of the last bitname lookup query -#[derive(Debug)] -struct LastQueryResult(Result, node::Error>); - -#[derive(Debug, Default)] -pub struct BitnameExplorer { - plaintext_name: String, - last_query_result: Option, -} - -pub fn show_bitname_data( - ui: &mut egui::Ui, - bitname_data: &BitNameData, -) -> Response { - let BitNameData { - commitment, - ipv4_addr, - ipv6_addr, - encryption_pubkey, - signing_pubkey, - paymail_fee, - } = bitname_data; - let commitment = commitment.map_or("Not set".to_owned(), hex::encode); - let ipv4_addr = ipv4_addr - .map_or("Not set".to_owned(), |ipv4_addr| ipv4_addr.to_string()); - let ipv6_addr = ipv6_addr - .map_or("Not set".to_owned(), |ipv6_addr| ipv6_addr.to_string()); - let encryption_pubkey = encryption_pubkey - .map_or("Not set".to_owned(), |epk| hex::encode(epk.0.as_bytes())); - let signing_pubkey = signing_pubkey - .map_or("Not set".to_owned(), |pk| hex::encode(pk.as_bytes())); - let paymail_fee = paymail_fee - .map_or("Not set".to_owned(), |paymail_fee| paymail_fee.to_string()); - ui.horizontal(|ui| { - ui.monospace_selectable_singleline( - true, - format!("Commitment: {commitment}"), - ) - }) - .join() - | ui.horizontal(|ui| { - ui.monospace_selectable_singleline( - false, - format!("IPv4 Address: {ipv4_addr}"), - ) - }) - .join() - | ui.horizontal(|ui| { - ui.monospace_selectable_singleline( - false, - format!("IPv6 Address: {ipv6_addr}"), - ) - }) - .join() - | ui.horizontal(|ui| { - ui.monospace_selectable_singleline( - true, - format!("Encryption Pubkey: {encryption_pubkey}"), - ) - }) - .join() - | ui.horizontal(|ui| { - ui.monospace_selectable_singleline( - true, - format!("Signing Pubkey: {signing_pubkey}"), - ) - }) - .join() - | ui.horizontal(|ui| { - ui.monospace_selectable_singleline( - false, - format!("Paymail fee: {paymail_fee}"), - ) - }) - .join() -} - -impl BitnameExplorer { - pub fn show(&mut self, app: &mut App, ui: &mut egui::Ui) { - egui::CentralPanel::default().show_inside(ui, |ui| { - ui.heading("BitName Explorer"); - let text_resp = ui.horizontal(|ui| { - ui.monospace("Plaintext BitName: ") - | ui.add(egui::TextEdit::singleline(&mut self.plaintext_name)) - }).join(); - let refresh_button = ui.button("Refresh"); - // resolve bitname if changed or refresh button clicked - if text_resp.changed() || refresh_button.clicked() { - let name_hash = blake3::hash(self.plaintext_name.as_bytes()).into(); - let bitname = BitName(name_hash); - let last_query_result = app.node.try_get_current_bitname_data(&bitname); - self.last_query_result = Some(LastQueryResult(last_query_result)); - } - if let Some(LastQueryResult(last_query_result)) = &self.last_query_result { - match last_query_result { - Err(err) => { - ui.horizontal(|ui| { - ui.monospace(format!("Error encountered when resolving bitname: {err}")) - }); - } - Ok(None) => { - ui.horizontal(|ui| { - ui.monospace("No BitName data found") - }); - } - Ok(Some(bitname_data)) => { - let _resp: Response = show_bitname_data(ui, bitname_data); - } - } - } - }); - } -} diff --git a/app/gui/bitnames/all_bitnames.rs b/app/gui/bitnames/all_bitnames.rs new file mode 100644 index 0000000..b9a57a9 --- /dev/null +++ b/app/gui/bitnames/all_bitnames.rs @@ -0,0 +1,156 @@ +use std::collections::BTreeMap; + +use eframe::egui; +use hex::FromHex; +use plain_bitnames::types::{hashes::BitName, BitNameData}; + +use crate::{ + app::App, + gui::util::{InnerResponseExt, UiExt}, +}; + +#[derive(Debug, Default)] +pub(super) struct AllBitNames { + query: String, +} + +fn show_bitname_data( + ui: &mut egui::Ui, + bitname_data: &BitNameData, +) -> egui::Response { + let BitNameData { + commitment, + ipv4_addr, + ipv6_addr, + encryption_pubkey, + signing_pubkey, + paymail_fee, + } = bitname_data; + let commitment = commitment.map_or("Not set".to_owned(), hex::encode); + let ipv4_addr = ipv4_addr + .map_or("Not set".to_owned(), |ipv4_addr| ipv4_addr.to_string()); + let ipv6_addr = ipv6_addr + .map_or("Not set".to_owned(), |ipv6_addr| ipv6_addr.to_string()); + let encryption_pubkey = encryption_pubkey + .map_or("Not set".to_owned(), |epk| hex::encode(epk.0.as_bytes())); + let signing_pubkey = signing_pubkey + .map_or("Not set".to_owned(), |pk| hex::encode(pk.as_bytes())); + let paymail_fee = paymail_fee + .map_or("Not set".to_owned(), |paymail_fee| paymail_fee.to_string()); + ui.horizontal(|ui| { + ui.monospace_selectable_singleline( + true, + format!("Commitment: {commitment}"), + ) + }) + .join() + | ui.horizontal(|ui| { + ui.monospace_selectable_singleline( + false, + format!("IPv4 Address: {ipv4_addr}"), + ) + }) + .join() + | ui.horizontal(|ui| { + ui.monospace_selectable_singleline( + false, + format!("IPv6 Address: {ipv6_addr}"), + ) + }) + .join() + | ui.horizontal(|ui| { + ui.monospace_selectable_singleline( + true, + format!("Encryption Pubkey: {encryption_pubkey}"), + ) + }) + .join() + | ui.horizontal(|ui| { + ui.monospace_selectable_singleline( + true, + format!("Signing Pubkey: {signing_pubkey}"), + ) + }) + .join() + | ui.horizontal(|ui| { + ui.monospace_selectable_singleline( + false, + format!("Paymail fee: {paymail_fee}"), + ) + }) + .join() +} + +fn show_bitname_with_data( + ui: &mut egui::Ui, + bitname_id: &BitName, + bitname_data: &BitNameData, +) -> egui::Response { + ui.horizontal(|ui| { + ui.monospace_selectable_singleline( + true, + format!("BitName ID: {}", hex::encode(bitname_id.0)), + ) + }) + .join() + | show_bitname_data(ui, bitname_data) +} + +impl AllBitNames { + pub fn show(&mut self, app: &mut App, ui: &mut egui::Ui) { + egui::CentralPanel::default().show_inside(ui, |ui| { + match app.node.bitnames() { + Err(node_err) => { + ui.monospace_selectable_multiline(node_err.to_string()); + } + Ok(bitnames) => { + let bitnames = BTreeMap::from_iter(bitnames); + ui.horizontal(|ui| { + let query_edit = + egui::TextEdit::singleline(&mut self.query) + .hint_text("Search") + .desired_width(150.); + ui.add(query_edit); + }); + if self.query.is_empty() { + bitnames.into_iter().for_each( + |(bitname_id, bitname_data)| { + show_bitname_with_data( + ui, + &bitname_id, + &bitname_data, + ); + }, + ) + } else { + let name_hash = + blake3::hash(self.query.as_bytes()).into(); + let name_hash_pattern = BitName(name_hash); + if let Some(bitname_data) = + bitnames.get(&name_hash_pattern) + { + show_bitname_with_data( + ui, + &name_hash_pattern, + bitname_data, + ); + }; + if let Ok(bitname_id_pattern) = + BitName::from_hex(&self.query) + { + if let Some(bitname_data) = + bitnames.get(&bitname_id_pattern) + { + show_bitname_with_data( + ui, + &bitname_id_pattern, + bitname_data, + ); + } + } + } + } + } + }); + } +} diff --git a/app/gui/bitnames/mod.rs b/app/gui/bitnames/mod.rs new file mode 100644 index 0000000..1a29377 --- /dev/null +++ b/app/gui/bitnames/mod.rs @@ -0,0 +1,47 @@ +use eframe::egui; +use strum::{EnumIter, IntoEnumIterator}; + +use crate::app::App; + +mod all_bitnames; +mod reserve_register; + +use all_bitnames::AllBitNames; +use reserve_register::ReserveRegister; + +#[derive(Default, EnumIter, Eq, PartialEq, strum::Display)] +enum Tab { + #[default] + #[strum(to_string = "All BitNames")] + AllBitNames, + #[strum(to_string = "Reserve & Register")] + ReserveRegister, +} + +#[derive(Default)] +pub struct BitNames { + all_bitnames: AllBitNames, + reserve_register: ReserveRegister, + tab: Tab, +} + +impl BitNames { + pub fn show(&mut self, app: &mut App, ui: &mut egui::Ui) { + egui::TopBottomPanel::top("bitnames_tabs").show(ui.ctx(), |ui| { + ui.horizontal(|ui| { + Tab::iter().for_each(|tab_variant| { + let tab_name = tab_variant.to_string(); + ui.selectable_value(&mut self.tab, tab_variant, tab_name); + }) + }); + }); + egui::CentralPanel::default().show(ui.ctx(), |ui| match self.tab { + Tab::AllBitNames => { + let () = self.all_bitnames.show(app, ui); + } + Tab::ReserveRegister => { + let () = self.reserve_register.show(app, ui); + } + }); + } +} diff --git a/app/gui/bitnames/reserve_register.rs b/app/gui/bitnames/reserve_register.rs new file mode 100644 index 0000000..1964189 --- /dev/null +++ b/app/gui/bitnames/reserve_register.rs @@ -0,0 +1,173 @@ +use std::borrow::Cow; + +use bip300301::bitcoin; +use eframe::egui; +use plain_bitnames::types::BitNameData; + +use crate::{ + app::App, + gui::{coins::tx_creator, util::UiExt}, +}; + +fn reserve_bitname( + app: &App, + plaintext_name: &str, + fee: bitcoin::Amount, +) -> anyhow::Result<()> { + let mut tx = app.wallet.create_regular_transaction(fee.to_sat())?; + let () = app.wallet.reserve_bitname(&mut tx, plaintext_name)?; + app.sign_and_send(tx).map_err(anyhow::Error::from) +} + +fn register_bitname( + app: &App, + plaintext_name: &str, + bitname_data: Cow, + fee: bitcoin::Amount, +) -> anyhow::Result<()> { + let mut tx = app.wallet.create_regular_transaction(fee.to_sat())?; + let () = + app.wallet + .register_bitname(&mut tx, plaintext_name, bitname_data)?; + app.sign_and_send(tx).map_err(anyhow::Error::from) +} + +#[derive(Debug, Default)] +struct Reserve { + plaintext_name: String, + fee: String, +} + +impl Reserve { + pub fn show(&mut self, app: &App, ui: &mut egui::Ui) { + ui.add_sized((250., 10.), |ui: &mut egui::Ui| { + ui.horizontal(|ui| { + let plaintext_name_edit = + egui::TextEdit::singleline(&mut self.plaintext_name) + .hint_text("Plaintext Name") + .desired_width(150.); + ui.add(plaintext_name_edit); + }) + .response + }); + ui.add_sized((110., 10.), |ui: &mut egui::Ui| { + ui.horizontal(|ui| { + let fee_edit = egui::TextEdit::singleline(&mut self.fee) + .hint_text("fee") + .desired_width(80.); + ui.add(fee_edit); + ui.label("BTC"); + }) + .response + }); + let fee = bitcoin::Amount::from_str_in( + &self.fee, + bitcoin::Denomination::Bitcoin, + ); + if ui + .add_enabled( + !self.plaintext_name.is_empty() && fee.is_ok(), + egui::Button::new("Reserve"), + ) + .clicked() + { + if let Err(err) = reserve_bitname( + app, + &self.plaintext_name, + fee.expect("should not happen"), + ) { + tracing::error!("{err:#}"); + } else { + *self = Self::default(); + } + } + } +} + +#[derive(Debug, Default)] +struct Register { + plaintext_name: String, + fee: String, + bitname_data: tx_creator::TrySetBitNameData, +} + +impl Register { + pub fn show(&mut self, app: &App, ui: &mut egui::Ui) { + ui.add_sized((250., 10.), |ui: &mut egui::Ui| { + ui.horizontal(|ui| { + let plaintext_name_edit = + egui::TextEdit::singleline(&mut self.plaintext_name) + .hint_text("Plaintext Name") + .desired_width(150.); + ui.add(plaintext_name_edit); + }) + .response + }); + ui.add_sized((110., 10.), |ui: &mut egui::Ui| { + ui.horizontal(|ui| { + let fee_edit = egui::TextEdit::singleline(&mut self.fee) + .hint_text("fee") + .desired_width(80.); + ui.add(fee_edit); + ui.label("BTC"); + }) + .response + }); + let fee = bitcoin::Amount::from_str_in( + &self.fee, + bitcoin::Denomination::Bitcoin, + ); + tx_creator::TxCreator::show_bitname_options(ui, &mut self.bitname_data); + let bitname_data: Result = + self.bitname_data.clone().try_into(); + if let Err(err) = &bitname_data { + ui.monospace_selectable_multiline(err.clone()); + } + if ui + .add_enabled( + !self.plaintext_name.is_empty() + && fee.is_ok() + && bitname_data.is_ok(), + egui::Button::new("Register"), + ) + .clicked() + { + if let Err(err) = register_bitname( + app, + &self.plaintext_name, + Cow::Borrowed(&bitname_data.expect("should not happen")), + fee.expect("should not happen"), + ) { + tracing::error!("{err:#}"); + } else { + *self = Self::default(); + } + } + } +} + +#[derive(Default)] +pub(super) struct ReserveRegister { + reserve: Reserve, + register: Register, +} + +impl ReserveRegister { + pub fn show(&mut self, app: &App, ui: &mut egui::Ui) { + egui::SidePanel::left("reserve") + .exact_width(ui.available_width() / 2.) + .resizable(false) + .show_inside(ui, |ui| { + ui.vertical_centered(|ui| { + ui.heading("Reserve"); + self.reserve.show(app, ui); + }) + }); + egui::CentralPanel::default().show_inside(ui, |ui| { + ui.vertical_centered(|ui| { + ui.heading("Register"); + self.register.show(app, ui); + }) + }); + } +} diff --git a/app/gui/coins/mod.rs b/app/gui/coins/mod.rs index e25361e..3df031d 100644 --- a/app/gui/coins/mod.rs +++ b/app/gui/coins/mod.rs @@ -6,7 +6,7 @@ use crate::app::App; mod my_bitnames; mod transfer_receive; mod tx_builder; -mod tx_creator; +pub(super) mod tx_creator; mod utxo_creator; mod utxo_selector; diff --git a/app/gui/coins/transfer_receive.rs b/app/gui/coins/transfer_receive.rs index c349a6d..fdc5c76 100644 --- a/app/gui/coins/transfer_receive.rs +++ b/app/gui/coins/transfer_receive.rs @@ -17,7 +17,7 @@ fn create_transfer( amount: bitcoin::Amount, fee: bitcoin::Amount, ) -> anyhow::Result<()> { - let tx = app.wallet.create_regular_transaction( + let tx = app.wallet.create_transfer( dest, amount.to_sat(), fee.to_sat(), diff --git a/app/gui/coins/tx_creator.rs b/app/gui/coins/tx_creator.rs index 6b512a7..598d0e5 100644 --- a/app/gui/coins/tx_creator.rs +++ b/app/gui/coins/tx_creator.rs @@ -207,7 +207,7 @@ impl TxCreator { } } - fn show_bitname_options( + pub(in crate::gui) fn show_bitname_options( ui: &mut egui::Ui, bitname_data: &mut TrySetBitNameData, ) -> Response { diff --git a/app/gui/deposit.rs b/app/gui/deposit.rs deleted file mode 100644 index c2d55b6..0000000 --- a/app/gui/deposit.rs +++ /dev/null @@ -1,60 +0,0 @@ -use eframe::egui; - -use plain_bitnames::bip300301::bitcoin; - -use crate::app::App; - -pub struct Deposit { - amount: String, - fee: String, -} - -impl Default for Deposit { - fn default() -> Self { - Self { - amount: "".into(), - fee: "".into(), - } - } -} - -impl Deposit { - pub fn show(&mut self, app: &mut App, ui: &mut egui::Ui) { - ui.horizontal(|ui| { - let amount_edit = egui::TextEdit::singleline(&mut self.amount) - .hint_text("amount") - .desired_width(80.); - ui.add(amount_edit); - ui.label("BTC"); - }); - ui.horizontal(|ui| { - let fee_edit = egui::TextEdit::singleline(&mut self.fee) - .hint_text("fee") - .desired_width(80.); - ui.add(fee_edit); - ui.label("BTC"); - }); - - let amount = bitcoin::Amount::from_str_in( - &self.amount, - bitcoin::Denomination::Bitcoin, - ); - let fee = bitcoin::Amount::from_str_in( - &self.fee, - bitcoin::Denomination::Bitcoin, - ); - - if ui - .add_enabled( - amount.is_ok() && fee.is_ok(), - egui::Button::new("deposit"), - ) - .clicked() - { - let _result = app.deposit( - amount.expect("should not happen"), - fee.expect("should not happen"), - ); - } - } -} diff --git a/app/gui/mod.rs b/app/gui/mod.rs index 59278ff..0a44b63 100644 --- a/app/gui/mod.rs +++ b/app/gui/mod.rs @@ -4,9 +4,8 @@ use strum::{EnumIter, IntoEnumIterator}; use crate::{app::App, logs::LogsCapture}; mod activity; -mod bitname_explorer; +mod bitnames; mod coins; -mod deposit; mod encrypt_message; mod logs; mod miner; @@ -16,9 +15,8 @@ mod seed; mod util; use activity::Activity; -use bitname_explorer::BitnameExplorer; +use bitnames::BitNames; use coins::Coins; -use deposit::Deposit; use encrypt_message::EncryptMessage; use logs::Logs; use miner::Miner; @@ -29,9 +27,8 @@ use seed::SetSeed; pub struct EguiApp { activity: Activity, app: App, - bitname_explorer: BitnameExplorer, + bitnames: BitNames, coins: Coins, - deposit: Deposit, encrypt_message: EncryptMessage, logs: Logs, miner: Miner, @@ -48,8 +45,8 @@ enum Tab { ParentChain, #[strum(to_string = "Coins")] Coins, - #[strum(to_string = "Lookup")] - BitnameExplorer, + #[strum(to_string = "BitNames")] + BitNames, #[strum(to_string = "My Paymail")] Paymail, #[strum(to_string = "Messaging")] @@ -92,9 +89,8 @@ impl EguiApp { Self { activity, app, - bitname_explorer: BitnameExplorer::default(), + bitnames: BitNames::default(), coins, - deposit: Deposit::default(), encrypt_message: EncryptMessage::new(), logs: Logs::new(logs_capture), miner: Miner::default(), @@ -122,8 +118,6 @@ impl EguiApp { // it up if you have multiple widgets to expand, even with different ratios. let this_target_width = this_init_max_width - last_others_width; - self.deposit.show(&mut self.app, ui); - ui.separator(); ui.add_space(this_target_width); ui.separator(); self.miner.show(&self.app, ui); @@ -163,8 +157,8 @@ impl eframe::App for EguiApp { Tab::Coins => { let () = self.coins.show(&mut self.app, ui).unwrap(); } - Tab::BitnameExplorer => { - self.bitname_explorer.show(&mut self.app, ui); + Tab::BitNames => { + self.bitnames.show(&mut self.app, ui); } Tab::Paymail => self.paymail.show(&mut self.app, ui).unwrap(), Tab::EncryptMessage => { diff --git a/app/rpc_server.rs b/app/rpc_server.rs index 4705244..1901498 100644 --- a/app/rpc_server.rs +++ b/app/rpc_server.rs @@ -182,15 +182,9 @@ impl RpcServer for RpcServerImpl { let tx = self .app .wallet - .create_regular_transaction(dest, value, fee, memo) + .create_transfer(dest, value, fee, memo) .map_err(convert_wallet_err)?; - let authorized_tx = - self.app.wallet.authorize(tx).map_err(convert_wallet_err)?; - self.app - .node - .submit_transaction(&authorized_tx) - .await - .map_err(convert_node_err) + self.app.sign_and_send(tx).map_err(convert_app_err) } async fn withdraw( diff --git a/lib/types/hashes.rs b/lib/types/hashes.rs index 31d7870..72d2b73 100644 --- a/lib/types/hashes.rs +++ b/lib/types/hashes.rs @@ -185,6 +185,14 @@ impl std::fmt::Display for BitName { } } +impl FromHex for BitName { + type Error = ::Error; + + fn from_hex>(hex: T) -> Result { + Hash::from_hex(hex).map(Self) + } +} + pub fn hash(data: &T) -> Hash where T: BorshSerialize, diff --git a/lib/wallet.rs b/lib/wallet.rs index f704e44..7b0f068 100644 --- a/lib/wallet.rs +++ b/lib/wallet.rs @@ -182,6 +182,21 @@ impl Wallet { Ok(self.seed.get(&txn, &0)?.is_some()) } + /// Create a transaction with a fee only. + pub fn create_regular_transaction( + &self, + fee: u64, + ) -> Result { + let (total, coins) = self.select_coins(fee)?; + let change = total - fee; + let inputs = coins.into_keys().collect(); + let outputs = vec![Output::new( + self.get_new_address()?, + OutputContent::Value(change), + )]; + Ok(Transaction::new(inputs, outputs)) + } + pub fn create_withdrawal( &self, main_address: bitcoin::Address, @@ -206,7 +221,7 @@ impl Wallet { Ok(Transaction::new(inputs, outputs)) } - pub fn create_regular_transaction( + pub fn create_transfer( &self, address: Address, value: u64, @@ -385,10 +400,14 @@ impl Wallet { let mut selected = HashMap::new(); let mut total: u64 = 0; for (outpoint, output) in &utxos { - if output.content.is_withdrawal() { + if output.content.is_withdrawal() + || output.is_bitname() + || output.is_reservation() + || output.get_value() == 0 + { continue; } - if total > value { + if total >= value { break; } total += output.get_value();