diff options
| author | DanConwayDev <DanConwayDev@protonmail.com> | 2023-05-21 11:18:29 +0000 |
|---|---|---|
| committer | DanConwayDev <DanConwayDev@protonmail.com> | 2023-05-21 11:18:29 +0000 |
| commit | fda0fdd81caab1ca92eb7ed601058e6c2fdc59f5 (patch) | |
| tree | dd91fc1a7b41d02aead655ea2dc07463b3487d5d /src/utils.rs | |
| parent | 0067804cc00e94ce2b7043e67f9ff50968525479 (diff) | |
helpers and utilities
Diffstat (limited to 'src/utils.rs')
| -rw-r--r-- | src/utils.rs | 212 |
1 files changed, 212 insertions, 0 deletions
diff --git a/src/utils.rs b/src/utils.rs new file mode 100644 index 0000000..48aafa9 --- /dev/null +++ b/src/utils.rs | |||
| @@ -0,0 +1,212 @@ | |||
| 1 | use std::fs::File; | ||
| 2 | use std::io::{Read, Write}; | ||
| 3 | use std::path::{Path}; | ||
| 4 | use std::time::Duration; | ||
| 5 | |||
| 6 | use dialoguer::{Select, Input}; | ||
| 7 | use dialoguer::theme::ColorfulTheme; | ||
| 8 | use nostr_sdk::blocking::Client; | ||
| 9 | use nostr_sdk::prelude::*; | ||
| 10 | |||
| 11 | use crate::config::{MyConfig, save_conifg}; | ||
| 12 | |||
| 13 | pub fn handle_keys(private_key: Option<String>, hex: bool) -> Result<Keys> { | ||
| 14 | // Parse and validate private key | ||
| 15 | let keys = match private_key { | ||
| 16 | Some(pk) => { | ||
| 17 | // create a new identity using the provided private key | ||
| 18 | Keys::from_sk_str(pk.as_str())? | ||
| 19 | } | ||
| 20 | None => { | ||
| 21 | // create a new identity with a new keypair | ||
| 22 | println!("No private key provided, creating new identity"); | ||
| 23 | Keys::generate() | ||
| 24 | } | ||
| 25 | }; | ||
| 26 | |||
| 27 | if !hex { | ||
| 28 | println!("Private key: {}", keys.secret_key()?.to_bech32()?); | ||
| 29 | println!("Public key: {}", keys.public_key().to_bech32()?); | ||
| 30 | } else { | ||
| 31 | println!("Private key: {}", keys.secret_key()?.display_secret()); | ||
| 32 | println!("Public key: {}", keys.public_key()); | ||
| 33 | } | ||
| 34 | Ok(keys) | ||
| 35 | } | ||
| 36 | |||
| 37 | // Creates the websocket client that is used for communicating with relays | ||
| 38 | pub fn create_client(keys: &Keys, relays: Vec<String>) -> Result<Client> { | ||
| 39 | let opts = Options::new() | ||
| 40 | .wait_for_send(true) | ||
| 41 | .timeout(Some(Duration::from_secs(7))); | ||
| 42 | let client = Client::with_opts(keys, opts); | ||
| 43 | let relays = relays.iter().map(|url| (url, None)).collect(); | ||
| 44 | client.add_relays(relays)?; | ||
| 45 | client.connect(); | ||
| 46 | Ok(client) | ||
| 47 | } | ||
| 48 | |||
| 49 | // Accepts both hex and bech32 keys and returns the hex encoded key | ||
| 50 | pub fn parse_key(key: String) -> Result<String> { | ||
| 51 | // Check if the key is a bech32 encoded key | ||
| 52 | let parsed_key = if key.starts_with("npub") { | ||
| 53 | XOnlyPublicKey::from_bech32(key)?.to_string() | ||
| 54 | } else if key.starts_with("nsec") { | ||
| 55 | SecretKey::from_bech32(key)?.display_secret().to_string() | ||
| 56 | } else if key.starts_with("note") { | ||
| 57 | EventId::from_bech32(key)?.to_hex() | ||
| 58 | } else if key.starts_with("nchannel") { | ||
| 59 | ChannelId::from_bech32(key)?.to_hex() | ||
| 60 | } else { | ||
| 61 | // If the key is not bech32 encoded, return it as is | ||
| 62 | key | ||
| 63 | }; | ||
| 64 | Ok(parsed_key) | ||
| 65 | } | ||
| 66 | |||
| 67 | pub fn get_stored_keys(cfg:&mut MyConfig) -> Option<Keys> { | ||
| 68 | match &cfg.private_key { | ||
| 69 | None => None, | ||
| 70 | Some(k) => Some(Keys::new(*k)), | ||
| 71 | } | ||
| 72 | } | ||
| 73 | |||
| 74 | pub fn get_or_generate_keys(cfg:&mut MyConfig) -> Keys { | ||
| 75 | match cfg.private_key { | ||
| 76 | None => { | ||
| 77 | let selection = Select::with_theme(&ColorfulTheme::default()) | ||
| 78 | .items(&vec!["enter existing private key", "generate new keys"]) | ||
| 79 | .default(0) | ||
| 80 | .with_prompt("no keys are stored") | ||
| 81 | .interact().unwrap(); | ||
| 82 | let key = match selection { | ||
| 83 | 0 => { | ||
| 84 | let mut prompt = "secret key (nsec, hex, etc)"; | ||
| 85 | loop { | ||
| 86 | let pk: String = Input::with_theme(&ColorfulTheme::default()) | ||
| 87 | .with_prompt(prompt) | ||
| 88 | .interact_text() | ||
| 89 | .unwrap(); | ||
| 90 | match Keys::from_sk_str(&pk) { | ||
| 91 | Ok(key) => { break key; }, | ||
| 92 | Err(_e) => { prompt = "error interpeting secret key. try again with nsec, hex, etc"; }, | ||
| 93 | } | ||
| 94 | } | ||
| 95 | |||
| 96 | } | ||
| 97 | _ => Keys::generate(), | ||
| 98 | }; | ||
| 99 | cfg.private_key = Some(key.secret_key().unwrap()); | ||
| 100 | save_conifg(&cfg); | ||
| 101 | key | ||
| 102 | } | ||
| 103 | Some(k) => Keys::new(k), | ||
| 104 | } | ||
| 105 | } | ||
| 106 | |||
| 107 | #[derive(clap::ValueEnum, Clone, Debug)] | ||
| 108 | pub enum Prefix { | ||
| 109 | Npub, | ||
| 110 | Nsec, | ||
| 111 | Note, | ||
| 112 | Nchannel, | ||
| 113 | } | ||
| 114 | |||
| 115 | |||
| 116 | /// [`LoadFile`] error | ||
| 117 | #[derive(Debug, thiserror::Error)] | ||
| 118 | pub enum Error { | ||
| 119 | /// Error loading event file | ||
| 120 | #[error("cannot load event file.")] | ||
| 121 | // LoadFile(#[from] init::Error), | ||
| 122 | LoadFile(), | ||
| 123 | } | ||
| 124 | |||
| 125 | pub fn load_file<P: AsRef<Path>>(path: P) -> Result<String,Error> { | ||
| 126 | let mut buf = vec![]; | ||
| 127 | match File::open(path) { | ||
| 128 | Ok(mut f) => { | ||
| 129 | f.read_to_end(&mut buf) | ||
| 130 | .expect("read_to_end not to error on file"); | ||
| 131 | Ok( | ||
| 132 | std::str::from_utf8(&buf[..]) | ||
| 133 | .expect("file contents u8 to convert to str") | ||
| 134 | .to_string(), | ||
| 135 | ) | ||
| 136 | }, | ||
| 137 | Err(_e) => { Err(Error::LoadFile()) }, | ||
| 138 | } | ||
| 139 | |||
| 140 | } | ||
| 141 | |||
| 142 | pub fn load_event<P: AsRef<Path>>(path: P) -> Result<Event,Error> { | ||
| 143 | if let Ok(mut file) = File::open(path) { | ||
| 144 | let mut buf = vec![]; | ||
| 145 | if file.read_to_end(&mut buf).is_ok() { | ||
| 146 | if let Ok(event) = Event::from_json(std::str::from_utf8(&buf[..]).unwrap()) { | ||
| 147 | return Ok(event) | ||
| 148 | } | ||
| 149 | } | ||
| 150 | } | ||
| 151 | Err(Error::LoadFile()) | ||
| 152 | } | ||
| 153 | |||
| 154 | pub fn save_event<P: AsRef<Path>>(path: P, event: &Event) -> Result<()> { | ||
| 155 | let mut f = File::create(path)?; | ||
| 156 | f.write_all(&event.as_json().as_bytes())?; | ||
| 157 | Ok(()) | ||
| 158 | } | ||
| 159 | |||
| 160 | #[cfg(test)] | ||
| 161 | mod tests { | ||
| 162 | use super::*; | ||
| 163 | |||
| 164 | #[test] | ||
| 165 | fn test_parse_key_hex_input() { | ||
| 166 | let hex_key = | ||
| 167 | String::from("f4deaad98b61fa24d86ef315f1d5d57c1a6a533e1e87e777e5d0b48dcd332cdb"); | ||
| 168 | let result = parse_key(hex_key.clone()); | ||
| 169 | |||
| 170 | assert!(result.is_ok()); | ||
| 171 | assert_eq!(result.unwrap(), hex_key); | ||
| 172 | } | ||
| 173 | |||
| 174 | #[test] | ||
| 175 | fn test_parse_key_bech32_note_input() { | ||
| 176 | let bech32_note_id = | ||
| 177 | String::from("note1h445ule4je70k7kvddate8kpsh2fd6n77esevww5hmgda2qwssjsw957wk"); | ||
| 178 | let result = parse_key(bech32_note_id); | ||
| 179 | |||
| 180 | assert!(result.is_ok()); | ||
| 181 | assert_eq!( | ||
| 182 | result.unwrap(), | ||
| 183 | String::from("bd6b4e7f35967cfb7acc6b7abc9ec185d496ea7ef6619639d4bed0dea80e8425") | ||
| 184 | ); | ||
| 185 | } | ||
| 186 | |||
| 187 | #[test] | ||
| 188 | fn test_parse_bech32_public_key_input() { | ||
| 189 | let bech32_encoded_key = | ||
| 190 | String::from("npub1ktt8phjnkfmfrsxrgqpztdjuxk3x6psf80xyray0l3c7pyrln49qhkyhz0"); | ||
| 191 | let result = parse_key(bech32_encoded_key); | ||
| 192 | |||
| 193 | assert!(result.is_ok()); | ||
| 194 | assert_eq!( | ||
| 195 | result.unwrap(), | ||
| 196 | String::from("b2d670de53b27691c0c3400225b65c35a26d06093bcc41f48ffc71e0907f9d4a") | ||
| 197 | ); | ||
| 198 | } | ||
| 199 | |||
| 200 | #[test] | ||
| 201 | fn test_parse_bech32_private_key() { | ||
| 202 | let bech32_encoded_key = | ||
| 203 | String::from("nsec1hdeqm0y8vgzuucqv4840h7rlpy4qfu928ulxh3dzj6s2nqupdtzqagtew3"); | ||
| 204 | let result = parse_key(bech32_encoded_key); | ||
| 205 | |||
| 206 | assert!(result.is_ok()); | ||
| 207 | assert_eq!( | ||
| 208 | result.unwrap(), | ||
| 209 | String::from("bb720dbc876205ce600ca9eafbf87f092a04f0aa3f3e6bc5a296a0a983816ac4") | ||
| 210 | ); | ||
| 211 | } | ||
| 212 | } | ||