diff --git a/base58/Cargo.toml b/base58/Cargo.toml index 97618bf1..f27b76e4 100644 --- a/base58/Cargo.toml +++ b/base58/Cargo.toml @@ -14,7 +14,7 @@ exclude = ["tests", "contrib"] [features] default = ["std"] -std = ["hashes/std"] +std = ["hashes/std", "internals/std"] [package.metadata.docs.rs] all-features = true diff --git a/base58/src/error.rs b/base58/src/error.rs index 8a35656d..3e214a76 100644 --- a/base58/src/error.rs +++ b/base58/src/error.rs @@ -4,12 +4,14 @@ use core::fmt; +use internals::write_err; + /// An error that might occur during base58 decoding. #[derive(Debug, Clone, PartialEq, Eq)] #[non_exhaustive] pub enum Error { - /// Invalid character encountered. - BadByte(u8), + /// Invalid character while decoding. + Decode(InvalidCharacterError), /// Checksum was not correct (expected, actual). BadChecksum(u32, u32), /// The length (in bytes) of the object was not correct. @@ -32,7 +34,7 @@ impl fmt::Display for Error { use Error::*; match *self { - BadByte(b) => write!(f, "invalid base58 character {:#x}", b), + Decode(ref e) => write_err!(f, "decode"; e), BadChecksum(exp, actual) => write!(f, "base58ck checksum {:#x} does not match expected {:#x}", actual, exp), InvalidLength(ell) => write!(f, "length {} invalid for this base58 type", ell), @@ -51,8 +53,8 @@ impl std::error::Error for Error { use Error::*; match self { - BadByte(_) - | BadChecksum(_, _) + Decode(ref e) => Some(e), + BadChecksum(_, _) | InvalidLength(_) | InvalidExtendedKeyVersion(_) | InvalidAddressVersion(_) @@ -60,3 +62,28 @@ impl std::error::Error for Error { } } } + +impl From for Error { + #[inline] + fn from(e: InvalidCharacterError) -> Self { Self::Decode(e) } +} + +/// Found a invalid ASCII byte while decoding base58 string. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct InvalidCharacterError { + pub(super) invalid: u8, +} + +impl InvalidCharacterError { + /// Returns the ASCII byte that is not a valid base58 character. + pub fn invalid_base58_character(&self) -> u8 { self.invalid } +} + +impl fmt::Display for InvalidCharacterError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "invalid base58 character {:#x}", self.invalid) + } +} + +#[cfg(feature = "std")] +impl std::error::Error for InvalidCharacterError {} diff --git a/base58/src/lib.rs b/base58/src/lib.rs index 42fc127d..32ef8276 100644 --- a/base58/src/lib.rs +++ b/base58/src/lib.rs @@ -36,7 +36,7 @@ use hashes::{sha256d, Hash}; #[rustfmt::skip] // Keep public re-exports separate. #[doc(inline)] -pub use self::error::Error; +pub use self::error::{Error, InvalidCharacterError}; #[rustfmt::skip] static BASE58_DIGITS: [Option; 128] = [ @@ -59,19 +59,19 @@ static BASE58_DIGITS: [Option; 128] = [ ]; /// Decodes a base58-encoded string into a byte vector. -pub fn decode(data: &str) -> Result, Error> { +pub fn decode(data: &str) -> Result, InvalidCharacterError> { // 11/15 is just over log_256(58) let mut scratch = vec![0u8; 1 + data.len() * 11 / 15]; // Build in base 256 for d58 in data.bytes() { // Compute "X = X * 58 + next_digit" in base 256 if d58 as usize >= BASE58_DIGITS.len() { - return Err(Error::BadByte(d58)); + return Err(InvalidCharacterError { invalid: d58 }); } let mut carry = match BASE58_DIGITS[d58 as usize] { Some(d58) => d58 as u32, None => { - return Err(Error::BadByte(d58)); + return Err(InvalidCharacterError { invalid: d58 }); } }; for d256 in scratch.iter_mut().rev() { @@ -266,7 +266,10 @@ mod tests { Some(hex!("00f8917303bfa8ef24f292e8fa1419b20460ba064d")) ); // Non Base58 char. - assert_eq!(decode("¢").unwrap_err(), Error::BadByte(194)); + assert_eq!( + decode("¢").unwrap_err(), + InvalidCharacterError { invalid: 194 } + ); } #[test] diff --git a/units/src/amount.rs b/units/src/amount.rs index e96238ba..457e20d5 100644 --- a/units/src/amount.rs +++ b/units/src/amount.rs @@ -2500,7 +2500,7 @@ mod tests { use super::ParseAmountError as E; - assert_eq!(Amount::from_str("x BTC"), Err(E::from(InvalidCharacterError { invalid_char: 'x', position: 0 }).into())); + assert_eq!(Amount::from_str("x BTC"), Err(InvalidCharacterError { invalid_char: 'x', position: 0 }.into())); assert_eq!( Amount::from_str("xBTC"), Err(Unknown(UnknownDenominationError("xBTC".into())).into()),