use crate::common::Hash;
use alloy::primitives::keccak256;
use alloy::signers::k256::ecdsa::{RecoveryId, Signature, SigningKey, signature};
use alloy::signers::local::PrivateKeySigner;
pub fn hash<T: AsRef<[u8]>>(data: T) -> Hash {
keccak256(data.as_ref())
}
#[derive(Debug, thiserror::Error)]
pub enum SignError {
#[error("Failed to parse EVM secret key: {0}")]
InvalidEvmSecretKey(String),
#[error("Failed to sign message: {0}")]
Signature(#[from] signature::Error),
}
pub fn sign_message(evm_secret_key_str: &str, message: &[u8]) -> Result<Vec<u8>, SignError> {
let signer: PrivateKeySigner =
evm_secret_key_str
.parse::<PrivateKeySigner>()
.map_err(|err| {
error!("Error parsing EVM secret key: {err}");
SignError::InvalidEvmSecretKey(err.to_string())
})?;
let message_hash = to_eth_signed_message_hash(message);
let (signature, _) = sign_message_recoverable(&signer.into_credential(), message_hash)?;
debug!("Message signed successfully with {message_hash:?} and {signature:?}");
Ok(signature.to_vec())
}
fn to_eth_signed_message_hash<T: AsRef<[u8]>>(message: T) -> [u8; 32] {
const PREFIX: &str = "\x19Ethereum Signed Message:\n32";
let hashed_message = hash(message);
let mut eth_message = Vec::with_capacity(PREFIX.len() + 32);
eth_message.extend_from_slice(PREFIX.as_bytes());
eth_message.extend_from_slice(hashed_message.as_slice());
hash(eth_message).into()
}
fn sign_message_recoverable<T: AsRef<[u8]>>(
secret_key: &SigningKey,
message: T,
) -> Result<(Signature, RecoveryId), signature::Error> {
let hash = to_eth_signed_message_hash(message);
secret_key.sign_prehash_recoverable(&hash)
}