use bech32::{FromBase32, ToBase32}; use hex::FromHexError; use thiserror::Error; pub enum ToBech32Kind { SecretKey, PublicKey, Note, } #[derive(Error, Debug, Eq, PartialEq)] pub enum Bech32Error { #[error("Invalid hex string")] InvalidHex, #[error("Bech32 given key is not a {0}")] InvalidKey(String), } impl From for Bech32Error { fn from(_err: bech32::Error) -> Self { Self::InvalidHex } } impl From for Bech32Error { fn from(_err: hex::FromHexError) -> Self { Self::InvalidHex } } /// Transform a string (bech32 or hex) into an bech32 string /// /// # Example /// ```rust /// use gnostr_rs::bech32::{ToBech32Kind, to_bech32}; /// let bech32 = to_bech32(ToBech32Kind::PublicKey, "3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d"); /// assert_eq!(bech32.unwrap(), "npub180cvv07tjdrrgpa0j7j7tmnyl2yr6yr7l8j4s3evf6u64th6gkwsyjh6w6"); /// ``` pub fn to_bech32(kind: ToBech32Kind, key: &str) -> Result { let key = key.to_string(); match kind { ToBech32Kind::SecretKey => { if key.starts_with("nsec") { Ok(key) } else if key.starts_with("npub") || key.starts_with("note") { return Err(Bech32Error::InvalidKey("npub or note".to_string())); } else { return Ok(bech32::encode( "nsec", hex::decode(key)?.to_base32(), bech32::Variant::Bech32, )?); } } ToBech32Kind::PublicKey => { if key.starts_with("npub") { Ok(key) } else if key.starts_with("nsec") || key.starts_with("note") { return Err(Bech32Error::InvalidKey("nsec or note".to_string())); } else { return Ok(bech32::encode( "npub", hex::decode(key)?.to_base32(), bech32::Variant::Bech32, )?); } } ToBech32Kind::Note => { if key.starts_with("note") { Ok(key) } else if key.starts_with("nsec") || key.starts_with("npub") { return Err(Bech32Error::InvalidKey("nsec or npub".to_string())); } else { return Ok(bech32::encode( "note", hex::decode(key)?.to_base32(), bech32::Variant::Bech32, )?); } } } } /// (hb for hex and bech32) Transform a string (bech32 or hex) into an hex string /// # Example /// ```rust /// use gnostr_rs::bech32::{ToBech32Kind, from_hb_to_hex}; /// let hex = from_hb_to_hex(ToBech32Kind::PublicKey, "npub180cvv07tjdrrgpa0j7j7tmnyl2yr6yr7l8j4s3evf6u64th6gkwsyjh6w6"); /// assert_eq!(hex.unwrap(), "3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d"); /// ``` pub fn from_hb_to_hex(kind: ToBech32Kind, key: &str) -> Result { let key = key.to_string(); match kind { ToBech32Kind::SecretKey => { if key.starts_with("nsec") { let data = bech32::decode(&key)?.1; let decoded = Vec::::from_base32(&data)?; let hex_str = hex::encode(decoded); Ok(hex_str) } else if key.starts_with("npub") { return Err(Bech32Error::InvalidKey("npub".to_string())); } else { return Ok(key); } } ToBech32Kind::PublicKey => { if key.starts_with("npub") { let data = bech32::decode(&key)?.1; let decoded = Vec::::from_base32(&data)?; let hex_str = hex::encode(decoded); Ok(hex_str) } else if key.starts_with("nsec") { return Err(Bech32Error::InvalidKey("nsec".to_string())); } else { return Ok(key); } } ToBech32Kind::Note => { if key.starts_with("note") { let data = bech32::decode(&key)?.1; let decoded = Vec::::from_base32(&data)?; let hex_str = hex::encode(decoded); Ok(hex_str) } else if key.starts_with("nsec") || key.starts_with("npub") { return Err(Bech32Error::InvalidKey("nsec or npub".to_string())); } else { return Ok(key); } } } } /// Transform a string (bech32 or hex) into an hex string pub fn auto_bech32_to_hex(key: &str) -> Result { let key = key.to_string(); if key.starts_with("nsec") { from_hb_to_hex(ToBech32Kind::SecretKey, &key) } else if key.starts_with("npub") { from_hb_to_hex(ToBech32Kind::PublicKey, &key) } else if key.starts_with("note") { from_hb_to_hex(ToBech32Kind::Note, &key) } else { Ok(key) } }