Skip to main content

codec_multibase/
base58btc.rs

1// SPDX-FileCopyrightText: Copyright © 2026 ReallyMe LLC. All rights reserved
2//
3// SPDX-License-Identifier: Apache-2.0
4
5use bs58::decode::Error as Bs58DecodeError;
6use thiserror::Error;
7
8/// Maximum accepted base58btc input length.
9///
10/// The underlying base58 conversion is not linear in input size. This cap keeps
11/// untrusted byte and text inputs bounded while leaving headroom above the
12/// largest currently supported multikey encodings.
13pub const MAX_BASE58BTC_INPUT_LEN: usize = 8 * 1024;
14
15/// Error returned when base58btc decoding fails.
16#[derive(Debug, Error)]
17#[non_exhaustive]
18pub enum Base58Error {
19    /// The output buffer was too small to hold the decoded bytes.
20    #[error("base58btc output buffer too small")]
21    BufferTooSmall,
22    /// Decoding failed for a reason not covered by the other variants.
23    #[error("base58btc decode failed")]
24    DecodeFailed,
25    /// The input contained a character outside the base58btc alphabet.
26    #[error("invalid base58btc character")]
27    InvalidCharacter,
28    /// The input contained a non-ASCII character.
29    #[error("non-ascii base58btc character")]
30    NonAsciiCharacter,
31    /// The input exceeded the accepted base58btc text length.
32    #[error("base58btc input too large")]
33    InputTooLarge,
34}
35
36impl From<Bs58DecodeError> for Base58Error {
37    fn from(value: Bs58DecodeError) -> Self {
38        match value {
39            Bs58DecodeError::BufferTooSmall => Self::BufferTooSmall,
40            Bs58DecodeError::InvalidCharacter { .. } => Self::InvalidCharacter,
41            Bs58DecodeError::NonAsciiCharacter { .. } => Self::NonAsciiCharacter,
42            _ => Self::DecodeFailed,
43        }
44    }
45}
46
47/// Encodes bytes as a base58btc string.
48pub fn base58btc_encode(bytes: &[u8]) -> Result<String, Base58Error> {
49    if bytes.len() > MAX_BASE58BTC_INPUT_LEN {
50        return Err(Base58Error::InputTooLarge);
51    }
52    Ok(bs58::encode(bytes).into_string())
53}
54
55/// Decodes a base58btc string into bytes.
56///
57/// Fails closed: returns an error on any invalid or non-ASCII character.
58pub fn base58btc_decode(s: &str) -> Result<Vec<u8>, Base58Error> {
59    if s.len() > MAX_BASE58BTC_INPUT_LEN {
60        return Err(Base58Error::InputTooLarge);
61    }
62    bs58::decode(s).into_vec().map_err(Base58Error::from)
63}