solana-leader 0.4.0

solana leader library
Documentation
use std::fmt;

const ALPHABET: &[u8; 58] = b"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum Base58Error {
    Empty,
    InvalidByte(u8),
    TooLong,
    Overflow,
}

impl fmt::Display for Base58Error {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::Empty => f.write_str("empty base58 string"),
            Self::InvalidByte(byte) => write!(f, "invalid base58 byte 0x{byte:02x}"),
            Self::TooLong => f.write_str("base58 string too long for 32 bytes"),
            Self::Overflow => f.write_str("base58 value does not fit in 32 bytes"),
        }
    }
}

impl std::error::Error for Base58Error {}

#[inline]
pub fn decode_32(value: &str) -> Result<[u8; 32], Base58Error> {
    if value.is_empty() {
        return Err(Base58Error::Empty);
    }
    if value.len() > 44 {
        return Err(Base58Error::TooLong);
    }

    let mut output = [0u8; 32];

    for byte in value.bytes() {
        let digit = decode_byte(byte).ok_or(Base58Error::InvalidByte(byte))? as u32;
        let mut carry = digit;

        for out in output.iter_mut().rev() {
            let value = (*out as u32) * 58 + carry;
            *out = value as u8;
            carry = value >> 8;
        }

        if carry != 0 {
            return Err(Base58Error::Overflow);
        }
    }

    Ok(output)
}

#[inline]
pub fn encode_fixed(bytes: &[u8; 32]) -> String {
    if bytes.iter().all(|byte| *byte == 0) {
        return "11111111111111111111111111111111".to_string();
    }

    let mut value = *bytes;
    let mut encoded = [0u8; 44];
    let mut out_index = encoded.len();
    let mut first_non_zero = 0usize;

    while first_non_zero < value.len() {
        let mut remainder = 0u32;
        for byte in value.iter_mut().skip(first_non_zero) {
            let accumulator = (remainder << 8) | *byte as u32;
            *byte = (accumulator / 58) as u8;
            remainder = accumulator % 58;
        }

        out_index -= 1;
        encoded[out_index] = ALPHABET[remainder as usize];

        while first_non_zero < value.len() && value[first_non_zero] == 0 {
            first_non_zero += 1;
        }
    }

    for byte in bytes.iter().take_while(|byte| **byte == 0) {
        let _ = byte;
        out_index -= 1;
        encoded[out_index] = b'1';
    }

    String::from_utf8(encoded[out_index..].to_vec()).expect("base58 alphabet is valid UTF-8")
}

#[inline]
fn decode_byte(byte: u8) -> Option<u8> {
    match byte {
        b'1'..=b'9' => Some(byte - b'1'),
        b'A'..=b'H' => Some(byte - b'A' + 9),
        b'J'..=b'N' => Some(byte - b'J' + 17),
        b'P'..=b'Z' => Some(byte - b'P' + 22),
        b'a'..=b'k' => Some(byte - b'a' + 33),
        b'm'..=b'z' => Some(byte - b'm' + 44),
        _ => None,
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn decode_zero_pubkey() {
        let decoded = decode_32("11111111111111111111111111111111").unwrap();
        assert_eq!(decoded, [0u8; 32]);
    }

    #[test]
    fn round_trip_fixed_bytes() {
        let bytes = [
            0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
            24, 25, 26, 27, 28, 29, 30, 31,
        ];
        let encoded = encode_fixed(&bytes);
        let decoded = decode_32(&encoded).unwrap();
        assert_eq!(decoded, bytes);
    }
}