use std::io::{self, Error, ErrorKind};
use ethereum_types::H256;
use ring::digest::{digest, SHA256};
use ripemd::{Digest, Ripemd160};
use sha3::Keccak256;
pub fn hash_sha256_ripemd160<S>(pub_key_bytes: S) -> io::Result<Vec<u8>>
where
S: AsRef<[u8]>,
{
let digest_sha256 = digest(&SHA256, pub_key_bytes.as_ref());
let sha256_ripemd160 = Ripemd160::digest(&digest_sha256);
if sha256_ripemd160.len() != 20 {
return Err(Error::new(
ErrorKind::InvalidData,
format!(
"ripemd160 of sha256 must be 20-byte, got {}",
sha256_ripemd160.len()
),
));
}
Ok(sha256_ripemd160.to_vec())
}
pub fn keccak256(data: impl AsRef<[u8]>) -> H256 {
H256::from_slice(&Keccak256::digest(data.as_ref()))
}
pub fn eth_checksum(addr: &str) -> String {
let addr_lower_case = prefix_manager::strip_0x(addr).to_lowercase();
let digest_h256 = keccak256(&addr_lower_case.as_bytes());
checksum_eip55(&addr_lower_case, &hex::encode(digest_h256))
}
fn checksum_eip55(addr: &str, addr_hash: &str) -> String {
let mut chksum = String::new();
for (c, hash_char) in addr.chars().zip(addr_hash.chars()) {
if hash_char.to_digit(16) >= Some(8) {
chksum.extend(c.to_uppercase());
} else {
chksum.push(c);
}
}
chksum
}