use alloy::{
primitives::{keccak256, Address, Bytes, Signature, B256, U256},
providers::Provider,
sol_types::SolStruct,
};
use newton_core::{
newton_prover_task_manager::INewtonProverTaskManager::Task,
operator_registry::OperatorRegistry::OperatorRegistryInstance,
};
use thiserror::Error;
#[derive(Debug, Error)]
pub enum TaskEip712Error {
#[error("Invalid signature format: {0}")]
InvalidSignature(String),
#[error("Failed to recover signer: {0}")]
SignerRecoveryFailed(String),
#[error("Signer {0} is not an authorized task generator")]
NotAuthorizedTaskGenerator(Address),
#[error("Failed to check task generator: {0}")]
TaskGeneratorCheckFailed(String),
}
pub const TASK_DOMAIN_NAME: &str = "Newton Prover Task Manager";
pub const TASK_DOMAIN_VERSION: &str = "1";
#[derive(Debug)]
pub struct TaskEip712Domain {
pub name: String,
pub version: String,
pub chain_id: u64,
pub verifying_contract: Address,
}
impl Default for TaskEip712Domain {
fn default() -> Self {
Self {
name: TASK_DOMAIN_NAME.to_string(),
version: TASK_DOMAIN_VERSION.to_string(),
chain_id: 0, verifying_contract: Address::ZERO, }
}
}
impl TaskEip712Domain {
pub fn new(chain_id: u64, verifying_contract: Address) -> Self {
Self {
name: TASK_DOMAIN_NAME.to_string(),
version: TASK_DOMAIN_VERSION.to_string(),
chain_id,
verifying_contract,
}
}
}
pub fn compute_task_eip712_hash(task: &Task, domain: &TaskEip712Domain) -> B256 {
let domain_type_hash =
keccak256(b"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
let name_hash = keccak256(domain.name.as_bytes());
let version_hash = keccak256(domain.version.as_bytes());
let chain_id_u256 = U256::from(domain.chain_id);
let verifying_contract = domain.verifying_contract;
let mut domain_data = Vec::new();
domain_data.extend_from_slice(&domain_type_hash[..]);
domain_data.extend_from_slice(&name_hash[..]);
domain_data.extend_from_slice(&version_hash[..]);
domain_data.extend_from_slice(&chain_id_u256.to_be_bytes::<32>());
domain_data.extend_from_slice(&verifying_contract.into_array());
let salt_hash = B256::ZERO;
domain_data.extend_from_slice(&salt_hash[..]);
let domain_separator = keccak256(&domain_data);
let struct_hash = task.eip712_hash_struct();
let mut message_data = Vec::new();
message_data.push(0x19u8);
message_data.push(0x01u8);
message_data.extend_from_slice(&domain_separator[..]);
message_data.extend_from_slice(&struct_hash[..]);
keccak256(&message_data)
}
pub fn compute_commit_auth_hash(task: &Task, domain: &TaskEip712Domain, consensus_data_hash: B256) -> B256 {
let task_hash = compute_task_eip712_hash(task, domain);
let mut buf = Vec::with_capacity(64);
buf.extend_from_slice(&task_hash[..]);
buf.extend_from_slice(&consensus_data_hash[..]);
keccak256(&buf)
}
pub fn recover_task_signer(
task: &Task,
domain: &TaskEip712Domain,
signature_bytes: &Bytes,
) -> Result<Address, TaskEip712Error> {
let eip712_hash = compute_task_eip712_hash(task, domain);
let signature =
Signature::try_from(signature_bytes.as_ref()).map_err(|e| TaskEip712Error::InvalidSignature(e.to_string()))?;
signature
.recover_address_from_prehash(&eip712_hash)
.map_err(|e| TaskEip712Error::SignerRecoveryFailed(e.to_string()))
}
pub async fn verify_task_generator_signature<P, N>(
task: &Task,
domain: &TaskEip712Domain,
signature_bytes: &Bytes,
operator_registry: &OperatorRegistryInstance<P, N>,
) -> Result<Address, TaskEip712Error>
where
P: Provider<N>,
N: alloy::providers::Network,
{
let recovered_signer = recover_task_signer(task, domain, signature_bytes)?;
let is_task_generator = operator_registry
.isTaskGenerator(recovered_signer)
.call()
.await
.map_err(|e| TaskEip712Error::TaskGeneratorCheckFailed(e.to_string()))?;
if !is_task_generator {
return Err(TaskEip712Error::NotAuthorizedTaskGenerator(recovered_signer));
}
Ok(recovered_signer)
}