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<bech32::Error> for Bech32Error {
fn from(_err: bech32::Error) -> Self {
Self::InvalidHex
}
}
impl From<FromHexError> for Bech32Error {
fn from(_err: hex::FromHexError) -> Self {
Self::InvalidHex
}
}
pub fn to_bech32(kind: ToBech32Kind, key: &str) -> Result<String, Bech32Error> {
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,
)?);
}
}
}
}
pub fn from_hb_to_hex(kind: ToBech32Kind, key: &str) -> Result<String, Bech32Error> {
let key = key.to_string();
match kind {
ToBech32Kind::SecretKey => {
if key.starts_with("nsec") {
let data = bech32::decode(&key)?.1;
let decoded = Vec::<u8>::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::<u8>::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::<u8>::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);
}
}
}
}
pub fn auto_bech32_to_hex(key: &str) -> Result<String, Bech32Error> {
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)
}
}