diff --git a/Makefile b/Makefile index 03b15fb..4128da2 100644 --- a/Makefile +++ b/Makefile @@ -4,12 +4,20 @@ help: @grep -E '^[a-zA-Z0-9_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' +# RPC URLs HOLESKY_RPC_URL=https://eth-holesky.g.alchemy.com/v2/8lbq3evplhjE7rP48rxeMXcpDNTGz0Hf ANVIL_RPC_URL=http://localhost:8545 + +# CONTRACTS ADDRESSES +GIZA_AVS_ADDRESS=0x68d2Ecd85bDEbfFd075Fb6D87fFD829AD025DD5C +TASK_REGISTRY_ADDRESS=0x6Da3D07a6BF01F02fB41c02984a49B5d9Aa6ea92 +CLIENT_APP_REGISTRY_ADDRESS=0xa8d297D643a11cE83b432e87eEBce6bee0fd2bAb +AVS_DIRECTORY_ADDRESS=0x055733000064333CaDDbC92763c58BF0192fFeBf +OPERATOR_UJI_ADDRESS=0x37893031A8484066232AcBE6bFe7E2a7A4411a7d + +#UTILS DEPLOYER_PK=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 -GIZA_AVS_ADDRESS=0x6Da3D07a6BF01F02fB41c02984a49B5d9Aa6ea92 -TASK_REGISTRY_ADDRESS=0xa8d297D643a11cE83b432e87eEBce6bee0fd2bAb -CLIENT_APP_REGISTRY_ADDRESS=0xb4e9A5BC64DC07f890367F72941403EEd7faDCbB +OPERATOR_UJI_PK=0x2a7f875389f0ce57b6d3200fb88e9a95e864a2ff589e8b1b11e56faff32a1fc5 TASK_ID=0xc86aab04e8ef18a63006f43fa41a2a0150bae3dbe276d581fa8b5cde0ccbc966 -----------------------------: ## @@ -17,7 +25,7 @@ TASK_ID=0xc86aab04e8ef18a63006f43fa41a2a0150bae3dbe276d581fa8b5cde0ccbc966 ___CONTRACTS___: ## anvil: ## starts anvil - anvil --ipc --fork-url $(HOLESKY_RPC_URL) + anvil --ipc --fork-url $(HOLESKY_RPC_URL) --fork-block-number 2577255 build-contracts: ## builds all contracts cd contracts && forge build @@ -30,4 +38,5 @@ deploy-contracts: ## deploy contracts (you need to run anvil first in a separat __TASKS__: ## create-task: ## create a task (you need to run anvil first in a separate terminal and the contract deployed) - cast send $(TASK_REGISTRY_ADDRESS) "createTask(bytes32)" $(TASK_ID) --private-key $(DEPLOYER_PK) --rpc-url $(ANVIL_RPC_URL) \ No newline at end of file + cast send $(TASK_REGISTRY_ADDRESS) "createTask(bytes32)" $(TASK_ID) --private-key $(DEPLOYER_PK) --rpc-url $(ANVIL_RPC_URL) + diff --git a/contract-bindings/src/lib.rs b/contract-bindings/src/lib.rs index 9f180d7..15141fa 100644 --- a/contract-bindings/src/lib.rs +++ b/contract-bindings/src/lib.rs @@ -14,11 +14,12 @@ use alloy::sol; use alloy_primitives::{address, Address}; -pub const TASK_REGISTRY_ADDRESS: Address = address!("e7f1725E7734CE288F8367e1Bb143E90bb3F0512"); +pub const TASK_REGISTRY_ADDRESS: Address = address!("6Da3D07a6BF01F02fB41c02984a49B5d9Aa6ea92"); pub const CLIENT_APP_REGISTRY_ADDRESS: Address = - address!("5FbDB2315678afecb367f032d93F642f64180aa3"); - -// TODO: For now we provide the path to the compiled contract, but once the contract is "freeze" we can provide static ABI + address!("a8d297D643a11cE83b432e87eEBce6bee0fd2bAb"); +pub const AVS_DIRECTORY_ADDRESS: Address = address!("055733000064333CaDDbC92763c58BF0192fFeBf"); +pub const GIZA_AVS_ADDRESS: Address = address!("68d2Ecd85bDEbfFd075Fb6D87fFD829AD025DD5C"); +pub const OPERATOR_UJI_ADDRESS: Address = address!("37893031A8484066232AcBE6bFe7E2a7A4411a7d"); sol!( #[sol(rpc)] @@ -58,6 +59,18 @@ sol!( "../contracts/out/ClientAppRegistry.sol/ClientAppRegistry.json" ); +sol!( + #[sol(rpc)] + GizaAVS, + "../contracts/out/GizaAVS.sol/GizaAVS.json" +); + +sol! { + #[sol(rpc)] + interface AVSDirectory { + function calculateOperatorAVSRegistrationDigestHash(address operator, address avs, bytes32 salt, uint256 expiry) external view returns (bytes32); +}} + #[cfg(test)] mod tests { use super::*; diff --git a/operator/src/lib.rs b/operator/src/lib.rs index c29fa0b..e8fa678 100644 --- a/operator/src/lib.rs +++ b/operator/src/lib.rs @@ -5,17 +5,17 @@ use alloy::{ BlobGasFiller, ChainIdFiller, FillProvider, GasFiller, JoinFill, NonceFiller, WalletFiller, }, - Identity, IpcConnect, Provider, ProviderBuilder, RootProvider, + Identity, IpcConnect, ProviderBuilder, RootProvider, }, pubsub::PubSubFrontend, signers::{local::PrivateKeySigner, Signer}, transports::http::{Client, Http}, }; -use alloy_primitives::{Address, Bytes, FixedBytes, U256}; +use alloy_primitives::{Address, FixedBytes, U256}; use contract_bindings::{ - AVSDirectory::{self, AVSDirectoryInstance}, - ClientAppRegistry::{self, ClientAppMetadata, ClientAppRegistryInstance}, - GizaAVS::{self, GizaAVSInstance}, + AVSDirectory::AVSDirectoryInstance, + ClientAppRegistry::{ClientAppMetadata, ClientAppRegistryInstance}, + GizaAVS::GizaAVSInstance, ISignatureUtils::SignatureWithSaltAndExpiry, TaskRegistry::{self, TaskRegistryInstance}, AVS_DIRECTORY_ADDRESS, CLIENT_APP_REGISTRY_ADDRESS, GIZA_AVS_ADDRESS, TASK_REGISTRY_ADDRESS, @@ -30,21 +30,6 @@ use tokio::{ }; use tracing::{error, info, warn}; -pub type PubSubProviderWithSigner = Arc< - FillProvider< - JoinFill< - JoinFill< - Identity, - JoinFill>>, - >, - WalletFiller, - >, - RootProvider, - PubSubFrontend, - Ethereum, - >, ->; - pub type HttpProviderWithSigner = Arc< FillProvider< JoinFill< @@ -66,7 +51,7 @@ const QUEUE_CAPACITY: usize = 100; #[derive(Clone)] pub struct Operator { operator_address: Address, - pubsub_provider: PubSubProviderWithSigner, + pubsub_provider: Arc>, http_provider: HttpProviderWithSigner, signer: PrivateKeySigner, } @@ -84,10 +69,8 @@ impl Operator { //Create PubSubProvider let ipc_path = "/tmp/anvil.ipc"; let ipc = IpcConnect::new(ipc_path.to_string()); - let pubsub_provider = Arc::new( + let pubsub_provider: Arc> = Arc::new( ProviderBuilder::new() - .with_recommended_fillers() - .wallet(wallet.clone()) .on_ipc(ipc) .await .wrap_err("Failed to create provider")?, @@ -115,7 +98,7 @@ impl Operator { pub async fn run(&self) -> Result<()> { info!("Starting operator..."); - // self.register_operator().await?; + self.register_operator_in_avs().await?; self.fetch_client_app().await?; @@ -136,6 +119,115 @@ impl Operator { Ok(()) } + async fn register_operator_in_avs(&self) -> Result<()> { + let giza_avs = GizaAVSInstance::new(GIZA_AVS_ADDRESS, self.http_provider.clone()); + let avs_directory = + AVSDirectoryInstance::new(AVS_DIRECTORY_ADDRESS, self.http_provider.clone()); + + // Register operator in GizaAVS + let is_operator_registered = giza_avs + .isOperatorRegistered(self.operator_address) + .call() + .await? + .isRegistered; + + if is_operator_registered { + info!("Operator already registered"); + return Ok(()); + } + + let salt = FixedBytes::<32>::from_str( + "0x2ef06b8bbad022ca2dd29795902ceb588d06d1cfd10cb6e687db0dbb837865e9", + ) + .unwrap(); + let expiry = U256::from(1779248899); + + // Eigenlayer provide a view function to calculate the digest hash that needs to be signed + let digest_hash = avs_directory + .calculateOperatorAVSRegistrationDigestHash( + self.operator_address, + GIZA_AVS_ADDRESS, + salt, + expiry, + ) + .call() + .await? + ._0; + + // We signed the hash + let signed_digest = self.signer.sign_hash(&digest_hash).await?; + + // Broadcast tx to register in EL contracts and GizaAVS contracts + let tx = giza_avs + .registerOperatorToAVS( + self.operator_address, + SignatureWithSaltAndExpiry { + signature: signed_digest.as_bytes().into(), + salt, + expiry, + }, + ) + .send() + .await? + .watch() + .await?; + info!("GizaAVS registration submitted {:?}", tx); + + // Check if the operator is registered + let is_operator_registered = giza_avs + .isOperatorRegistered(self.operator_address) + .call() + .await? + .isRegistered; + + match is_operator_registered { + true => info!("Successfully registered operator in GizaAVS"), + false => { + return Err(eyre::eyre!("Operator registration failed")); + } + } + + // Register client app in GizaAVS + let client_app_id: FixedBytes<32> = FixedBytes::<32>::from_str( + "0xc86aab04e8ef18a63006f43fa41a2a0150bae3dbe276d581fa8b5cde0ccbc966", + ) + .unwrap(); + + let is_client_app_registered = giza_avs + .operatorClientAppIdRegistrationStatus(self.operator_address, client_app_id.clone()) + .call() + .await? + .isRegistered; + + if is_client_app_registered { + info!("Client app already registered"); + return Ok(()); + } + + let tx = giza_avs + .optInClientAppId(client_app_id) + .send() + .await? + .watch() + .await?; + info!("Client app registration submitted {:?}", tx); + + let is_client_app_registered = giza_avs + .operatorClientAppIdRegistrationStatus(self.operator_address, client_app_id.clone()) + .call() + .await? + .isRegistered; + + match is_client_app_registered { + true => info!("Successfully registered client app in GizaAVS"), + false => { + return Err(eyre::eyre!("Client app registration failed")); + } + } + + Ok(()) + } + async fn fetch_client_app(&self) -> Result<()> { let client_app_registry = ClientAppRegistryInstance::new(CLIENT_APP_REGISTRY_ADDRESS, self.http_provider.clone());