codec_multibase/
base58btc.rs1use bs58::decode::Error as Bs58DecodeError;
6use thiserror::Error;
7use zeroize::Zeroizing;
8
9pub const MAX_BASE58BTC_INPUT_LEN: usize = 5_600;
15
16pub const MAX_BASE58BTC_DECODED_LEN: usize = 4_098;
19
20#[derive(Debug, Error)]
22#[non_exhaustive]
23pub enum Base58Error {
24 #[error("base58btc output buffer too small")]
26 BufferTooSmall,
27 #[error("base58btc decode failed")]
29 DecodeFailed,
30 #[error("invalid base58btc character")]
32 InvalidCharacter,
33 #[error("non-ascii base58btc character")]
35 NonAsciiCharacter,
36 #[error("base58btc input too large")]
38 InputTooLarge,
39}
40
41impl From<Bs58DecodeError> for Base58Error {
42 fn from(value: Bs58DecodeError) -> Self {
43 match value {
44 Bs58DecodeError::BufferTooSmall => Self::BufferTooSmall,
45 Bs58DecodeError::InvalidCharacter { .. } => Self::InvalidCharacter,
46 Bs58DecodeError::NonAsciiCharacter { .. } => Self::NonAsciiCharacter,
47 _ => Self::DecodeFailed,
48 }
49 }
50}
51
52pub fn base58btc_encode(bytes: &[u8]) -> Result<String, Base58Error> {
54 if bytes.len() > MAX_BASE58BTC_DECODED_LEN {
55 return Err(Base58Error::InputTooLarge);
56 }
57 let mut output = Zeroizing::new(Vec::new());
58 bs58::encode(bytes)
59 .onto(&mut *output)
60 .map_err(|_| Base58Error::BufferTooSmall)?;
61 match String::from_utf8(core::mem::take(&mut *output)) {
62 Ok(encoded) => Ok(encoded),
63 Err(error) => {
64 let _bytes = Zeroizing::new(error.into_bytes());
65 Err(Base58Error::DecodeFailed)
66 }
67 }
68}
69
70pub fn base58btc_decode(s: &str) -> Result<Vec<u8>, Base58Error> {
74 if s.len() > MAX_BASE58BTC_INPUT_LEN {
75 return Err(Base58Error::InputTooLarge);
76 }
77 let mut output = Zeroizing::new(vec![0_u8; s.len()]);
80 let length = bs58::decode(s)
81 .onto(output.as_mut_slice())
82 .map_err(Base58Error::from)?;
83 output.truncate(length);
84 if length > MAX_BASE58BTC_DECODED_LEN {
85 return Err(Base58Error::InputTooLarge);
86 }
87 Ok(core::mem::take(&mut *output))
88}