use bs58::decode::Error as Bs58DecodeError;
use thiserror::Error;
use zeroize::Zeroizing;
pub const MAX_BASE58BTC_INPUT_LEN: usize = 8 * 1024;
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum Base58Error {
#[error("base58btc output buffer too small")]
BufferTooSmall,
#[error("base58btc decode failed")]
DecodeFailed,
#[error("invalid base58btc character")]
InvalidCharacter,
#[error("non-ascii base58btc character")]
NonAsciiCharacter,
#[error("base58btc input too large")]
InputTooLarge,
}
impl From<Bs58DecodeError> for Base58Error {
fn from(value: Bs58DecodeError) -> Self {
match value {
Bs58DecodeError::BufferTooSmall => Self::BufferTooSmall,
Bs58DecodeError::InvalidCharacter { .. } => Self::InvalidCharacter,
Bs58DecodeError::NonAsciiCharacter { .. } => Self::NonAsciiCharacter,
_ => Self::DecodeFailed,
}
}
}
pub fn base58btc_encode(bytes: &[u8]) -> Result<String, Base58Error> {
if bytes.len() > MAX_BASE58BTC_INPUT_LEN {
return Err(Base58Error::InputTooLarge);
}
let mut output = Zeroizing::new(Vec::new());
bs58::encode(bytes)
.onto(&mut *output)
.map_err(|_| Base58Error::BufferTooSmall)?;
match String::from_utf8(core::mem::take(&mut *output)) {
Ok(encoded) => Ok(encoded),
Err(error) => {
let _bytes = Zeroizing::new(error.into_bytes());
Err(Base58Error::DecodeFailed)
}
}
}
pub fn base58btc_decode(s: &str) -> Result<Vec<u8>, Base58Error> {
if s.len() > MAX_BASE58BTC_INPUT_LEN {
return Err(Base58Error::InputTooLarge);
}
let mut output = Zeroizing::new(vec![0_u8; s.len()]);
let length = bs58::decode(s)
.onto(output.as_mut_slice())
.map_err(Base58Error::from)?;
output.truncate(length);
Ok(core::mem::take(&mut *output))
}