use crate::hash::sha256d;
use crate::PrimitivesError;
const _ALPHABET: &[u8] = b"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
pub fn encode(data: &[u8]) -> String {
bs58::encode(data)
.with_alphabet(bs58::Alphabet::BITCOIN)
.into_string()
}
pub fn decode(s: &str) -> Result<Vec<u8>, PrimitivesError> {
bs58::decode(s)
.with_alphabet(bs58::Alphabet::BITCOIN)
.into_vec()
.map_err(|e| PrimitivesError::InvalidBase58(e.to_string()))
}
pub fn check_encode(data: &[u8]) -> String {
let checksum = sha256d(data);
let mut payload = data.to_vec();
payload.extend_from_slice(&checksum[..4]);
encode(&payload)
}
pub fn check_decode(s: &str) -> Result<Vec<u8>, PrimitivesError> {
let decoded = decode(s)?;
if decoded.len() < 4 {
return Err(PrimitivesError::InvalidBase58(
"data too short for checksum".to_string(),
));
}
let (payload, checksum) = decoded.split_at(decoded.len() - 4);
let expected = sha256d(payload);
if checksum != &expected[..4] {
return Err(PrimitivesError::ChecksumMismatch);
}
Ok(payload.to_vec())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_base58_empty_string() {
let input = hex::decode("").unwrap();
assert_eq!(encode(&input), "");
let decoded = decode("").unwrap();
assert_eq!(decoded, input);
}
#[test]
fn test_base58_single_zero_byte() {
let input = hex::decode("00").unwrap();
assert_eq!(encode(&input), "1");
let decoded = decode("1").unwrap();
assert_eq!(decoded, input);
}
#[test]
fn test_base58_decoded_address() {
let input = hex::decode("00010966776006953D5567439E5E39F86A0D273BEED61967F6").unwrap();
let encoded = encode(&input);
assert_eq!(encoded, "16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM");
let decoded = decode("16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM").unwrap();
assert_eq!(decoded, input);
}
#[test]
fn test_base58_decoded_hash() {
let input = hex::decode("0123456789ABCDEF").unwrap();
let encoded = encode(&input);
assert_eq!(encoded, "C3CPq7c8PY");
let decoded = decode("C3CPq7c8PY").unwrap();
assert_eq!(decoded, input);
}
#[test]
fn test_base58_leading_zeros() {
let input = hex::decode("000000287FB4CD").unwrap();
let encoded = encode(&input);
assert_eq!(encoded, "111233QC4");
let decoded = decode("111233QC4").unwrap();
assert_eq!(decoded, input);
}
#[test]
fn test_base58_decode_invalid_character() {
assert!(decode("invalid!@#$%").is_err());
}
#[test]
fn test_base58_decode_mixed_valid_invalid() {
assert!(decode("1234!@#$%").is_err());
}
#[test]
fn test_base58_encode_nil_input() {
assert_eq!(encode(&[]), "");
}
#[test]
fn test_base58_encode_all_zeros() {
assert_eq!(encode(&[0, 0, 0, 0]), "1111");
}
#[test]
fn test_base58_encode_large_number() {
assert_eq!(encode(&[255, 255, 255, 255]), "7YXq9G");
}
#[test]
fn test_base58_check_roundtrip() {
let payload = hex::decode("00f54a5851e9372b87810a8e60cdd2e7cfd80b6e31").unwrap();
let encoded = check_encode(&payload);
let decoded = check_decode(&encoded).unwrap();
assert_eq!(decoded, payload);
}
#[test]
fn test_base58_check_bad_checksum() {
let payload = vec![0x80, 0x01, 0x02, 0x03];
let mut encoded = check_encode(&payload);
let last = encoded.pop().unwrap();
let replacement = if last == '1' { '2' } else { '1' };
encoded.push(replacement);
assert!(check_decode(&encoded).is_err());
}
}