diff --git a/Cargo.lock b/Cargo.lock index a906639..0be4752 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3288,7 +3288,7 @@ checksum = "2900ede94e305130c13ddd391e0ab7cbaeb783945ae07a279c268cb05109c6cb" [[package]] name = "plain_bitnames" -version = "0.5.5" +version = "0.5.6" dependencies = [ "addr", "anyhow", @@ -3327,7 +3327,7 @@ dependencies = [ [[package]] name = "plain_bitnames_app" -version = "0.5.5" +version = "0.5.6" dependencies = [ "anyhow", "async_zmq", @@ -3335,6 +3335,7 @@ dependencies = [ "bincode", "bip300301", "blake3", + "borsh", "clap", "ctrlc", "dirs", diff --git a/app/Cargo.toml b/app/Cargo.toml index 7cf67c0..903f289 100644 --- a/app/Cargo.toml +++ b/app/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "plain_bitnames_app" -version = "0.5.5" +version = "0.5.6" edition = "2021" authors = [ "Ash Manning " ] @@ -16,6 +16,7 @@ tokio = { version = "1.29.1", features = ["process", "rt-multi-thread"] } anyhow = { version = "1.0.72", features = ["backtrace"] } bincode = "1.3.3" blake3 = "1.4.1" +borsh = "1.3.0" clap = { version = "4.3.19", features = ["derive"] } ctrlc = "3.4.0" dirs = "5.0.1" diff --git a/app/gui/activity/bitname_explorer.rs b/app/gui/activity/bitname_explorer.rs index 3b6e6c0..b0aaf4a 100644 --- a/app/gui/activity/bitname_explorer.rs +++ b/app/gui/activity/bitname_explorer.rs @@ -22,10 +22,13 @@ impl BitNameExplorer { .map(|(bitname_id, bitname_data)| { { ui.horizontal(|ui| { - ui.monospace_selectable_singleline(format!( - "BitName ID: {}", - hex::encode(bitname_id.0) - )) | crate::gui::bitname_explorer::show_bitname_data( + ui.monospace_selectable_singleline( + true, + format!( + "BitName ID: {}", + hex::encode(bitname_id.0) + ), + ) | crate::gui::bitname_explorer::show_bitname_data( ui, &bitname_data, ) diff --git a/app/gui/bitname_explorer.rs b/app/gui/bitname_explorer.rs index b677794..152031d 100644 --- a/app/gui/bitname_explorer.rs +++ b/app/gui/bitname_explorer.rs @@ -42,37 +42,45 @@ pub fn show_bitname_data( let paymail_fee = paymail_fee .map_or("Not set".to_owned(), |paymail_fee| paymail_fee.to_string()); ui.horizontal(|ui| { - ui.monospace_selectable_singleline(format!("Commitment: {commitment}")) + ui.monospace_selectable_singleline( + true, + format!("Commitment: {commitment}"), + ) }) .join() | ui.horizontal(|ui| { - ui.monospace_selectable_singleline(format!( - "IPv4 Address: {ipv4_addr}" - )) + ui.monospace_selectable_singleline( + false, + format!("IPv4 Address: {ipv4_addr}"), + ) }) .join() | ui.horizontal(|ui| { - ui.monospace_selectable_singleline(format!( - "IPv6 Address: {ipv6_addr}" - )) + ui.monospace_selectable_singleline( + false, + format!("IPv6 Address: {ipv6_addr}"), + ) }) .join() | ui.horizontal(|ui| { - ui.monospace_selectable_singleline(format!( - "Encryption Pubkey: {encryption_pubkey}" - )) + ui.monospace_selectable_singleline( + true, + format!("Encryption Pubkey: {encryption_pubkey}"), + ) }) .join() | ui.horizontal(|ui| { - ui.monospace_selectable_singleline(format!( - "Signing Pubkey: {signing_pubkey}" - )) + ui.monospace_selectable_singleline( + true, + format!("Signing Pubkey: {signing_pubkey}"), + ) }) .join() | ui.horizontal(|ui| { - ui.monospace_selectable_singleline(format!( - "Paymail fee: {paymail_fee}" - )) + ui.monospace_selectable_singleline( + false, + format!("Paymail fee: {paymail_fee}"), + ) }) .join() } diff --git a/app/gui/coins/my_bitnames.rs b/app/gui/coins/my_bitnames.rs index 6664e35..abc75b8 100644 --- a/app/gui/coins/my_bitnames.rs +++ b/app/gui/coins/my_bitnames.rs @@ -56,15 +56,18 @@ impl MyBitnames { let txid = hex::encode(txid.0); let commitment = hex::encode(commitment); ui.vertical(|ui| { - ui.monospace_selectable_singleline(format!( - "plaintext name: {plaintext_name}" - )); - ui.monospace_selectable_singleline(format!( - "txid: {txid}" - )); - ui.monospace_selectable_singleline(format!( - "commitment: {commitment}" - )); + ui.monospace_selectable_singleline( + true, + format!("plaintext name: {plaintext_name}"), + ); + ui.monospace_selectable_singleline( + true, + format!("txid: {txid}"), + ); + ui.monospace_selectable_singleline( + true, + format!("commitment: {commitment}"), + ); }); ui.end_row() } @@ -74,12 +77,14 @@ impl MyBitnames { let txid = hex::encode(txid.0); let commitment = hex::encode(commitment); ui.vertical(|ui| { - ui.monospace_selectable_singleline(format!( - "txid: {txid}" - )); - ui.monospace_selectable_singleline(format!( - "commitment: {commitment}" - )); + ui.monospace_selectable_singleline( + true, + format!("txid: {txid}"), + ); + ui.monospace_selectable_singleline( + true, + format!("commitment: {commitment}"), + ); }); ui.end_row() } @@ -122,21 +127,25 @@ impl MyBitnames { .show(ui, |ui| { for (bitname, plaintext_name) in known_name_bitnames { ui.vertical(|ui| { - ui.monospace_selectable_singleline(format!( - "plaintext name: {plaintext_name}" - )); - ui.monospace_selectable_singleline(format!( - "bitname: {}", - hex::encode(bitname.0) - )); + ui.monospace_selectable_singleline( + true, + format!("plaintext name: {plaintext_name}"), + ); + ui.monospace_selectable_singleline( + true, + format!( + "bitname: {}", + hex::encode(bitname.0) + ), + ); }); ui.end_row() } for bitname in unknown_name_bitnames { - ui.monospace_selectable_singleline(format!( - "bitname: {}", - hex::encode(bitname.0) - )); + ui.monospace_selectable_singleline( + true, + format!("bitname: {}", hex::encode(bitname.0)), + ); ui.end_row() } }); diff --git a/app/gui/encrypt_message.rs b/app/gui/encrypt_message.rs index fae5731..2fa1e53 100644 --- a/app/gui/encrypt_message.rs +++ b/app/gui/encrypt_message.rs @@ -3,13 +3,14 @@ use eframe::egui; use libes::key::conversion::PublicKeyFrom; use plain_bitnames::types::EncryptionPubKey; -use super::util::{Ecies, InnerResponseExt, UiExt}; +use super::util::{borsh_deserialize_hex, Ecies, InnerResponseExt, UiExt}; use crate::app::App; #[derive(Debug)] pub struct EncryptMessage { - receiver_pubkey_string: String, - // none if not yet set, otherwise result of parsing receiver pubkey + // pubkey or BitName + receiver_input: String, + // none if not yet set, otherwise result of parsing/resolving receiver pubkey receiver_pubkey: Option>, plaintext: String, // none if not yet computed, otherwise result of attempting to encrypt @@ -19,7 +20,7 @@ pub struct EncryptMessage { impl EncryptMessage { pub fn new() -> Self { Self { - receiver_pubkey_string: String::new(), + receiver_input: String::new(), receiver_pubkey: None, plaintext: String::new(), ciphertext: None, @@ -27,27 +28,45 @@ impl EncryptMessage { } fn show_error(ui: &mut egui::Ui, error: &anyhow::Error) { + ui.monospace_selectable_singleline(false, "Error: "); ui.horizontal_wrapped(|ui| { - ui.monospace("Error: "); - ui.code(format!("{error}")); + ui.monospace_selectable_multiline(format!("{error:#}")); }); } - pub fn show(&mut self, _app: &mut App, ui: &mut egui::Ui) { + pub fn show(&mut self, app: &mut App, ui: &mut egui::Ui) { ui.heading("Encrypt Message"); - let receiver_pubkey_response = ui + let receiver_input_response = ui .horizontal(|ui| { - ui.monospace("Receiver's Encryption Pubkey (Bech32m): ") - | ui.add(egui::TextEdit::singleline( - &mut self.receiver_pubkey_string, - )) + ui.monospace( + "Receiver's BitName or Encryption Pubkey (Bech32m): ", + ) | ui.add(egui::TextEdit::singleline(&mut self.receiver_input)) }) .join(); - if receiver_pubkey_response.changed() { - self.receiver_pubkey = Some( - EncryptionPubKey::bech32m_decode(&self.receiver_pubkey_string) - .map_err(anyhow::Error::new), - ); + if receiver_input_response.changed() { + let receiver_pubkey: anyhow::Result = { + if let Ok(bitname) = borsh_deserialize_hex(&self.receiver_input) + { + app.node + .get_current_bitname_data(&bitname) + .map_err(anyhow::Error::from) + .and_then(|bitname_data| { + bitname_data.encryption_pubkey.ok_or( + anyhow::anyhow!( + "No encryption pubkey exists for this BitName" + ), + ) + }) + } else { + EncryptionPubKey::bech32m_decode(&self.receiver_input) + .map_err(|_| { + anyhow::anyhow!( + "Failed to parse BitName or Encryption Pubkey" + ) + }) + } + }; + self.receiver_pubkey = Some(receiver_pubkey); } let plaintext_response = ui .horizontal_wrapped(|ui| { @@ -67,7 +86,7 @@ impl EncryptMessage { Some(Ok(receiver_pubkey)) => receiver_pubkey, }; // regenerate ciphertext if possible - if receiver_pubkey_response.changed() || plaintext_response.changed() { + if receiver_input_response.changed() || plaintext_response.changed() { let receiver_pubkey = libes::key::X25519::pk_from(receiver_pubkey.0); self.ciphertext = Some( diff --git a/app/gui/util.rs b/app/gui/util.rs index c7f3e9e..c79b7ae 100644 --- a/app/gui/util.rs +++ b/app/gui/util.rs @@ -1,5 +1,6 @@ use std::borrow::Borrow; +use borsh::BorshDeserialize; use eframe::egui::{self, InnerResponse, Response, Ui}; use libes::{auth::HmacSha256, enc::Aes256Gcm, key::X25519}; @@ -13,7 +14,11 @@ pub trait InnerResponseExt { /// extension trait for egui::Ui pub trait UiExt { - fn monospace_selectable_singleline(&mut self, text: Text) -> Response + fn monospace_selectable_singleline( + &mut self, + clip_text: bool, + text: Text, + ) -> Response where Text: Borrow; @@ -38,7 +43,11 @@ impl InnerResponseExt for InnerResponse> { } impl UiExt for Ui { - fn monospace_selectable_singleline(&mut self, text: Text) -> Response + fn monospace_selectable_singleline( + &mut self, + clip_text: bool, + text: Text, + ) -> Response where Text: Borrow, { @@ -46,6 +55,7 @@ impl UiExt for Ui { let mut text: &str = text.borrow(); TextEdit::singleline(&mut text) .font(TextStyle::Monospace) + .clip_text(clip_text) .ui(self) } @@ -60,3 +70,14 @@ impl UiExt for Ui { .ui(self) } } + +pub fn borsh_deserialize_hex(hex: impl AsRef<[u8]>) -> anyhow::Result +where + T: BorshDeserialize, +{ + match hex::decode(hex) { + Ok(bytes) => borsh::BorshDeserialize::try_from_slice(&bytes) + .map_err(anyhow::Error::new), + Err(err) => Err(anyhow::Error::new(err)), + } +} diff --git a/app/main.rs b/app/main.rs index 35f206e..e5fae4b 100644 --- a/app/main.rs +++ b/app/main.rs @@ -30,6 +30,7 @@ fn main() -> anyhow::Result<()> { let config = cli.get_config()?; let () = set_tracing_subscriber(config.log_level); let app = app::App::new(&config)?; + // spawn rpc server app.runtime.spawn({ let app = app.clone(); diff --git a/lib/Cargo.toml b/lib/Cargo.toml index 27c02bf..ad7beab 100644 --- a/lib/Cargo.toml +++ b/lib/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "plain_bitnames" -version = "0.5.5" +version = "0.5.6" edition = "2021" authors = [ "Ash Manning " ] diff --git a/lib/state.rs b/lib/state.rs index b4edc9b..d258832 100644 --- a/lib/state.rs +++ b/lib/state.rs @@ -72,9 +72,9 @@ pub enum Error { AuthorizationError, #[error("bad coinbase output content")] BadCoinbaseOutputContent, - #[error("bitname {name_hash:?} already registered")] + #[error("bitname {name_hash} already registered")] BitNameAlreadyRegistered { name_hash: BitName }, - #[error("bitname {name_hash:?} already registered as an ICANN name")] + #[error("bitname {name_hash} already registered as an ICANN name")] BitNameAlreadyIcann { name_hash: BitName }, #[error("bundle too heavy {weight} > {max_weight}")] BundleTooHeavy { weight: u64, max_weight: u64 }, @@ -86,16 +86,16 @@ pub enum Error { IcannNameInvalid { plain_name: String }, #[error("failed to compute merkle root")] MerkleRoot, - #[error("missing BitName {name_hash:?}")] + #[error("missing BitName {name_hash}")] MissingBitName { name_hash: BitName }, #[error( - "Missing BitName data for {name_hash:?} at block height {block_height}" + "Missing BitName data for {name_hash} at block height {block_height}" )] MissingBitNameData { name_hash: BitName, block_height: u32, }, - #[error("missing BitName input {name_hash:?}")] + #[error("missing BitName input {name_hash}")] MissingBitNameInput { name_hash: BitName }, #[error("missing BitName reservation {txid}")] MissingReservation { txid: Txid }, diff --git a/lib/types/hashes.rs b/lib/types/hashes.rs index 5352712..31d7870 100644 --- a/lib/types/hashes.rs +++ b/lib/types/hashes.rs @@ -1,6 +1,6 @@ use bip300301::bitcoin; use bitcoin::hashes::Hash as _; -use borsh::BorshSerialize; +use borsh::{BorshDeserialize, BorshSerialize}; use hex::FromHex; use serde::{Deserialize, Serialize}; @@ -162,6 +162,7 @@ impl std::fmt::Debug for Txid { /// Identifier for a BitName #[derive( + BorshDeserialize, BorshSerialize, Clone, Copy, @@ -178,6 +179,12 @@ impl std::fmt::Debug for Txid { #[serde(transparent)] pub struct BitName(#[serde(with = "serde_hexstr_human_readable")] pub Hash); +impl std::fmt::Display for BitName { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", hex::encode(self.0)) + } +} + pub fn hash(data: &T) -> Hash where T: BorshSerialize,