use alloy::dyn_abi::Eip712Domain;
use alloy::primitives::{Address, B256, Signature};
use alloy::signers::SignerSync;
use alloy::sol_types::{SolStruct, eip712_domain};
pub const SLASH_JUDGE_DOMAIN_NAME: &str = "deCDN SlashJudge";
pub const SLASH_JUDGE_DOMAIN_VERSION: &str = "1";
mod sol_types {
alloy::sol! {
#[allow(non_snake_case, missing_debug_implementations)]
struct ProbeResponse {
bytes32 hash;
bool hasBlob;
uint64 ratePerMb;
uint64 timestampUs;
}
}
}
use sol_types::ProbeResponse as ProbeResponseSol;
pub const PROBE_RESPONSE_TYPEHASH: B256 =
alloy::primitives::b256!("0x4638cb019b77f8677ac7015556f19e56f0be5a6a0d9392006a7fb5b8acd587e5");
#[must_use]
pub fn slash_judge_domain(chain_id: u64, verifying_contract: Address) -> Eip712Domain {
eip712_domain! {
name: SLASH_JUDGE_DOMAIN_NAME,
version: SLASH_JUDGE_DOMAIN_VERSION,
chain_id: chain_id,
verifying_contract: verifying_contract,
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ProbeSlashData {
pub hash: B256,
pub has_blob: bool,
pub rate_per_mb: u64,
pub timestamp_us: u64,
}
impl ProbeSlashData {
const fn to_sol(self) -> ProbeResponseSol {
ProbeResponseSol {
hash: self.hash,
hasBlob: self.has_blob,
ratePerMb: self.rate_per_mb,
timestampUs: self.timestamp_us,
}
}
#[must_use]
pub fn signing_hash(&self, domain: &Eip712Domain) -> B256 {
(*self).to_sol().eip712_signing_hash(domain)
}
#[must_use]
pub fn struct_hash(&self) -> B256 {
(*self).to_sol().eip712_hash_struct()
}
pub fn sign<S: SignerSync>(
&self,
signer: &S,
domain: &Eip712Domain,
) -> Result<Signature, alloy::signers::Error> {
let hash = self.signing_hash(domain);
signer.sign_hash_sync(&hash)
}
pub fn recover_signer(
&self,
signature: &Signature,
domain: &Eip712Domain,
) -> Result<Address, ProbeSlashError> {
if crate::sig_canon::is_high_s(signature) {
return Err(ProbeSlashError::InvalidSignature);
}
let hash = self.signing_hash(domain);
signature
.recover_address_from_prehash(&hash)
.map_err(|_| ProbeSlashError::InvalidSignature)
}
pub fn verify_signer(
&self,
signature: &Signature,
expected: Address,
domain: &Eip712Domain,
) -> Result<(), ProbeSlashError> {
let recovered = self.recover_signer(signature, domain)?;
if recovered == expected {
Ok(())
} else {
Err(ProbeSlashError::WrongSigner {
expected,
recovered,
})
}
}
}
#[derive(Debug, thiserror::Error, PartialEq, Eq)]
pub enum ProbeSlashError {
#[error("probe slash_sig is malformed")]
InvalidSignature,
#[error("probe slash_sig signed by {recovered}, expected {expected}")]
WrongSigner {
expected: Address,
recovered: Address,
},
}
#[cfg(test)]
#[allow(clippy::similar_names)] mod tests;