#![doc = include_str!("../README.md")]
use thiserror::Error;
use log::{debug, error, info};
pub const STACKS_ADDRESS_PREFIX: char = 'S';
#[derive(Debug, Error)]
pub enum AddressError {
#[error("Invalid hash160 length: expected 20 bytes, got {0}")]
InvalidLength(usize),
#[error("Invalid address format: {0}")]
InvalidFormat(String),
#[error("Invalid version: {0}")]
InvalidVersion(u8),
#[error("Base58 decode error: {0}")]
Base58Error(String),
}
pub fn encode_address(version: u8, hash160: &[u8]) -> Result<String, AddressError> {
debug!("Encoding address with version {} and hash160 length {}", version, hash160.len());
if version >= 32 {
error!("Invalid version {}: must be less than 32", version);
return Err(AddressError::InvalidVersion(version));
}
if hash160.len() != 20 {
error!("Invalid hash160 length {}: must be 20 bytes", hash160.len());
return Err(AddressError::InvalidLength(hash160.len()));
}
let c32string_result = c32::encode_check(hash160, version);
match c32string_result {
Ok(c32string) => {
let address = format!("{}{}", STACKS_ADDRESS_PREFIX, c32string);
info!("Successfully encoded address: {}", address);
Ok(address)
},
Err(e) => {
error!("Failed to encode c32check string: {}", e);
Err(AddressError::InvalidFormat(e.to_string()))
}
}
}
pub fn decode_address(address: &str) -> Result<(u8, Vec<u8>), AddressError> {
debug!("Decoding address: {}", address);
if address.len() < 5 {
error!("Address too short: length {}", address.len());
return Err(AddressError::InvalidFormat(
"Address too short".to_string()
));
}
if !address.starts_with(STACKS_ADDRESS_PREFIX) {
error!("Invalid address prefix: must start with '{}'", STACKS_ADDRESS_PREFIX);
return Err(AddressError::InvalidFormat(
format!("Address must start with '{}'", STACKS_ADDRESS_PREFIX)
));
}
match c32::decode_check(&address[1..]) {
Ok((version, hash)) => {
info!("Successfully decoded address {} to version {} and hash160", address, version);
Ok((version, hash))
},
Err(e) => {
error!("Failed to decode c32check string: {}", e);
Err(AddressError::InvalidFormat(e.to_string()))
}
}
}