use alloy_primitives::{Address, keccak256};
use crate::crypto::{CryptoError, Result};
pub fn public_key_to_address(public_key_hex: &str) -> Result<Address> {
let public_key_hex = public_key_hex.strip_prefix("0x").unwrap_or(public_key_hex);
let public_key_bytes = hex::decode(public_key_hex)
.map_err(|e| CryptoError::invalid_private_key(format!("Invalid public key hex: {}", e)))?;
if public_key_bytes.is_empty() || public_key_bytes[0] != 0x04 {
return Err(CryptoError::invalid_private_key(
"Public key must start with 0x04 (uncompressed format)",
));
}
if public_key_bytes.len() != 65 {
return Err(CryptoError::invalid_private_key(
"Public key must be 65 bytes (uncompressed format)",
));
}
let hash = keccak256(&public_key_bytes[1..]);
let address_bytes = &hash[12..];
let address = Address::from_slice(address_bytes);
Ok(address)
}
pub fn is_valid_address_format(address: &str) -> bool {
let address = address.strip_prefix("0x").unwrap_or(address);
address.len() == 40 && address.chars().all(|c| c.is_ascii_hexdigit())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_is_valid_address_format() {
assert!(is_valid_address_format("0x742d35Cc6634C0532925a3b8D91D6F4A81B8Cbc0"));
assert!(is_valid_address_format("0x1234567890abcdef1234567890abcdef12345678"));
assert!(is_valid_address_format("742d35Cc6634C0532925a3b8D91D6F4A81B8Cbc0")); assert!(is_valid_address_format("1234567890abcdef1234567890abcdef12345678"));
assert!(is_valid_address_format("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"));
assert!(!is_valid_address_format("invalid")); assert!(!is_valid_address_format("0x123")); assert!(!is_valid_address_format("0x1234567890abcdef1234567890abcdef123456789")); assert!(!is_valid_address_format("0x1234567890abcdef1234567890abcdef1234567g")); assert!(!is_valid_address_format("")); }
#[test]
fn test_public_key_to_address() {
let valid_public_key = format!("04{}", "1".repeat(128)); let result = public_key_to_address(&format!("0x{}", valid_public_key));
assert!(result.is_ok());
let address = result.unwrap();
assert_ne!(address, Address::ZERO);
}
#[test]
fn test_public_key_to_address_error_cases() {
let compressed_key = format!("02{}", "1".repeat(64));
let result = public_key_to_address(&compressed_key);
assert!(result.is_err());
let short_key = "041234";
let result = public_key_to_address(short_key);
assert!(result.is_err());
let invalid_hex = format!("04{}", "z".repeat(128));
let result = public_key_to_address(&invalid_hex);
assert!(result.is_err());
}
}