use k256::ecdsa::{RecoveryId, Signature, VerifyingKey};
use tiny_keccak::{Hasher, Keccak};
use crate::error::VerifyError;
pub fn recover_address(contract_hash_hex: &str, sig_hex: &str) -> Result<String, VerifyError> {
let digest = hex32(contract_hash_hex)?;
let sig_bytes = decode_hex(sig_hex)?;
if sig_bytes.len() != 65 {
return Err(VerifyError::SchemaViolation(
format!("expected 65-byte signature, got {}", sig_bytes.len())));
}
let mut v = sig_bytes[64];
if v >= 27 { v -= 27; }
let recid = RecoveryId::try_from(v).map_err(|e|
VerifyError::SchemaViolation(format!("bad recovery id: {e}")))?;
let sig = Signature::from_slice(&sig_bytes[..64]).map_err(|e|
VerifyError::SchemaViolation(format!("bad signature: {e}")))?;
let recovered = VerifyingKey::recover_from_prehash(&digest, &sig, recid)
.map_err(|_| VerifyError::PrincipalSignatureInvalid("principal"))?;
let encoded = recovered.to_encoded_point(false);
let pubkey_bytes = encoded.as_bytes();
if pubkey_bytes.len() != 65 || pubkey_bytes[0] != 0x04 {
return Err(VerifyError::PrincipalSignatureInvalid("principal"));
}
let mut hasher = Keccak::v256();
let mut hash = [0u8; 32];
hasher.update(&pubkey_bytes[1..]);
hasher.finalize(&mut hash);
Ok(format!("0x{}", hex::encode(&hash[12..])))
}
pub fn address_from_did_ethr(did: &str) -> String {
let prefix = "did:ethr:";
if let Some(rest) = did.strip_prefix(prefix) {
return rest.to_string();
}
did.to_string()
}
fn hex32(s: &str) -> Result<[u8; 32], VerifyError> {
let stripped = s.trim_start_matches("0x");
let bytes = hex::decode(stripped).map_err(|_|
VerifyError::SchemaViolation(format!("not hex: {s:?}")))?;
if bytes.len() != 32 {
return Err(VerifyError::SchemaViolation(
format!("expected 32 bytes, got {}", bytes.len())));
}
let mut arr = [0u8; 32];
arr.copy_from_slice(&bytes);
Ok(arr)
}
fn decode_hex(s: &str) -> Result<Vec<u8>, VerifyError> {
let stripped = s.trim_start_matches("0x");
hex::decode(stripped).map_err(|_|
VerifyError::SchemaViolation(format!("not hex: {s:?}")))
}