use alloc::string::String;
use alloc::vec::Vec;
use ripemd::Ripemd160;
use sha2::{Digest, Sha256};
#[must_use]
pub fn hash160(data: &[u8]) -> [u8; 20] {
let sha = Sha256::digest(data);
Ripemd160::digest(sha).into()
}
#[must_use]
pub fn double_sha256(data: &[u8]) -> [u8; 32] {
let first = Sha256::digest(data);
Sha256::digest(first).into()
}
#[must_use]
pub fn base58check_encode(payload: &[u8]) -> String {
let checksum = double_sha256(payload);
let mut buf = Vec::with_capacity(payload.len() + 4);
buf.extend_from_slice(payload);
buf.extend_from_slice(&checksum[..4]);
bs58::encode(buf).into_string()
}
#[must_use]
#[allow(
clippy::indexing_slicing,
reason = "fixed-size 21-byte Base58Check body is sized by construction"
)]
pub fn base58check_versioned(version: u8, payload_20: &[u8; 20]) -> String {
let mut body = [0u8; 21];
body[0] = version;
body[1..].copy_from_slice(payload_20);
base58check_encode(&body)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn hash160_empty_known() {
let h = hash160(b"");
assert_eq!(hex::encode(h), "b472a266d0bd89c13706a4132ccfb16f7c3b9fcb");
}
#[test]
fn base58check_p2pkh_mainnet_prefix() {
let payload = [0x11u8; 20];
let encoded = base58check_versioned(0x00, &payload);
assert!(encoded.starts_with('1'));
}
#[test]
fn double_sha256_deterministic() {
let a = double_sha256(b"kobe");
let b = double_sha256(b"kobe");
assert_eq!(a, b);
assert_ne!(a, double_sha256(b"other"));
}
}