From cce5430c608124b8bf840b01f0a1b8e56f580bc7 Mon Sep 17 00:00:00 2001 From: Yash Patil <40046473+ypatil12@users.noreply.github.com> Date: Tue, 12 Dec 2023 11:21:29 -0500 Subject: [PATCH] Feat: Add AVS/Operator Registration Support in RegistryCoordinator (#99) --- src/RegistryCoordinator.sol | 74 ++++---- src/interfaces/IRegistryCoordinator.sol | 2 +- test/harnesses/RegistryCoordinatorHarness.sol | 3 +- test/integration/CoreRegistration.t.sol | 175 ++++++++++++++++++ test/unit/RegistryCoordinatorUnit.t.sol | 104 +++++------ test/unit/StakeRegistryUnit.t.sol | 1 + test/utils/MockAVSDeployer.sol | 42 +++-- 7 files changed, 283 insertions(+), 118 deletions(-) create mode 100644 test/integration/CoreRegistration.t.sol diff --git a/src/RegistryCoordinator.sol b/src/RegistryCoordinator.sol index af5de04b..714285bb 100644 --- a/src/RegistryCoordinator.sol +++ b/src/RegistryCoordinator.sol @@ -9,6 +9,7 @@ import {EIP1271SignatureUtils} from "eigenlayer-contracts/src/contracts/librarie import {IPauserRegistry} from "eigenlayer-contracts/src/contracts/interfaces/IPauserRegistry.sol"; import {Pausable} from "eigenlayer-contracts/src/contracts/permissions/Pausable.sol"; import {ISlasher} from "eigenlayer-contracts/src/contracts/interfaces/ISlasher.sol"; +import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; import {IRegistryCoordinator} from "src/interfaces/IRegistryCoordinator.sol"; import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; @@ -18,7 +19,6 @@ import {IStakeRegistry} from "src/interfaces/IStakeRegistry.sol"; import {IIndexRegistry} from "src/interfaces/IIndexRegistry.sol"; import {BitmapUtils} from "src/libraries/BitmapUtils.sol"; -import {BN254} from "src/libraries/BN254.sol"; /** * @title A `RegistryCoordinator` that has three registries: @@ -38,13 +38,10 @@ contract RegistryCoordinator is ISignatureUtils { using BitmapUtils for *; - using BN254 for BN254.G1Point; /// @notice The EIP-712 typehash for the `DelegationApproval` struct used by the contract bytes32 public constant OPERATOR_CHURN_APPROVAL_TYPEHASH = keccak256("OperatorChurnApproval(bytes32 registeringOperatorId,OperatorKickParam[] operatorKickParams)OperatorKickParam(address operator,bytes32[] operatorIdsToSwap)"); - /// @notice The EIP-712 typehash used for registering BLS public keys - bytes32 public constant PUBKEY_REGISTRATION_TYPEHASH = keccak256("BN254PubkeyRegistration(address operator)"); /// @notice The maximum value of a quorum bitmap uint256 internal constant MAX_QUORUM_BITMAP = type(uint192).max; /// @notice The basis point denominator @@ -66,6 +63,8 @@ contract RegistryCoordinator is IStakeRegistry public immutable stakeRegistry; /// @notice the Index Registry contract that will keep track of operators' indexes IIndexRegistry public immutable indexRegistry; + /// @notice The Delegation Manager contract to record operator avs relationships + IDelegationManager public immutable delegationManager; /// @notice the current number of quorums supported by the registry coordinator uint8 public quorumCount; @@ -102,11 +101,13 @@ contract RegistryCoordinator is } constructor( + IDelegationManager _delegationManager, ISlasher _slasher, IStakeRegistry _stakeRegistry, IBLSApkRegistry _blsApkRegistry, IIndexRegistry _indexRegistry ) EIP712("AVSRegistryCoordinator", "v0.0.1") { + delegationManager = _delegationManager; slasher = _slasher; stakeRegistry = _stakeRegistry; blsApkRegistry = _blsApkRegistry; @@ -155,29 +156,23 @@ contract RegistryCoordinator is * @notice Registers msg.sender as an operator for one or more quorums. If any quorum reaches its maximum * operator capacity, this method will fail. * @param quorumNumbers is an ordered byte array containing the quorum numbers being registered for - * @param params contains the G1 & G2 public keys of the operator, and a signature proving their ownership - * @dev the `params` input param is ignored if the caller has previously registered a public key + * @param socket is the socket of the operator + * @param operatorSignature is the signature of the operator used by the AVS to register the operator in the delegation manager */ function registerOperator( bytes calldata quorumNumbers, string calldata socket, - IBLSApkRegistry.PubkeyRegistrationParams calldata params + SignatureWithSaltAndExpiry memory operatorSignature ) external onlyWhenNotPaused(PAUSED_REGISTER_OPERATOR) { - /** - * IF the operator has never registered a pubkey before, THEN register their pubkey - * OTHERWISE, simply ignore the provided `params` - */ bytes32 operatorId = blsApkRegistry.getOperatorId(msg.sender); - if (operatorId == 0) { - operatorId = blsApkRegistry.registerBLSPublicKey(msg.sender, params, pubkeyRegistrationMessageHash(msg.sender)); - } // Register the operator in each of the registry contracts RegisterResults memory results = _registerOperator({ operator: msg.sender, operatorId: operatorId, quorumNumbers: quorumNumbers, - socket: socket + socket: socket, + operatorSignature: operatorSignature }); for (uint256 i = 0; i < quorumNumbers.length; i++) { @@ -200,29 +195,21 @@ contract RegistryCoordinator is * @notice Registers msg.sender as an operator for one or more quorums. If any quorum reaches its maximum operator * capacity, `operatorKickParams` is used to replace an old operator with the new one. * @param quorumNumbers is an ordered byte array containing the quorum numbers being registered for - * @param params contains the G1 & G2 public keys of the operator, and a signature proving their ownership * @param operatorKickParams are used to determine which operator is removed to maintain quorum capacity as the * operator registers for quorums. * @param churnApproverSignature is the signature of the churnApprover on the operator kick params - * @dev the `params` input param is ignored if the caller has previously registered a public key + * @param operatorSignature is the signature of the operator used by the AVS to register the operator in the delegation manager */ function registerOperatorWithChurn( bytes calldata quorumNumbers, string calldata socket, - IBLSApkRegistry.PubkeyRegistrationParams calldata params, OperatorKickParam[] calldata operatorKickParams, - SignatureWithSaltAndExpiry memory churnApproverSignature + SignatureWithSaltAndExpiry memory churnApproverSignature, + SignatureWithSaltAndExpiry memory operatorSignature ) external onlyWhenNotPaused(PAUSED_REGISTER_OPERATOR) { require(operatorKickParams.length == quorumNumbers.length, "RegistryCoordinator.registerOperatorWithChurn: input length mismatch"); - /** - * IF the operator has never registered a pubkey before, THEN register their pubkey - * OTHERWISE, simply ignore the `pubkeyRegistrationSignature`, `pubkeyG1`, and `pubkeyG2` inputs - */ bytes32 operatorId = blsApkRegistry.getOperatorId(msg.sender); - if (operatorId == 0) { - operatorId = blsApkRegistry.registerBLSPublicKey(msg.sender, params, pubkeyRegistrationMessageHash(msg.sender)); - } // Verify the churn approver's signature for the registering operator and kick params _verifyChurnApproverSignature({ @@ -236,11 +223,13 @@ contract RegistryCoordinator is operator: msg.sender, operatorId: operatorId, quorumNumbers: quorumNumbers, - socket: socket + socket: socket, + operatorSignature: operatorSignature }); for (uint256 i = 0; i < quorumNumbers.length; i++) { uint8 quorumNumber = uint8(quorumNumbers[i]); + OperatorSetParam memory operatorSetParams = _quorumParams[quorumNumber]; /** @@ -425,6 +414,15 @@ contract RegistryCoordinator is _setEjector(_ejector); } + /** + * @notice Sets the metadata URI for the AVS + * @param _metadataURI is the metadata URI for the AVS + * @dev only callable by the service manager owner + */ + function setMetadataURI(string memory _metadataURI) external onlyOwner { + delegationManager.updateAVSMetadataURI(_metadataURI); + } + /******************************************************************************* INTERNAL FUNCTIONS *******************************************************************************/ @@ -443,7 +441,8 @@ contract RegistryCoordinator is address operator, bytes32 operatorId, bytes calldata quorumNumbers, - string memory socket + string memory socket, + SignatureWithSaltAndExpiry memory operatorSignature ) internal virtual returns (RegisterResults memory) { /** * Get bitmap of quorums to register for and operator's current bitmap. Validate that: @@ -475,6 +474,9 @@ contract RegistryCoordinator is status: OperatorStatus.REGISTERED }); + // Register the operator with the delegation manager + delegationManager.registerOperatorToAVS(operator, operatorSignature); + emit OperatorRegistered(operator, operatorId); } @@ -554,9 +556,10 @@ contract RegistryCoordinator is newBitmap: newBitmap }); - // If the operator is no longer registered for any quorums, update their status + // If the operator is no longer registered for any quorums, update their status and deregister from delegationManager if (newBitmap.isEmpty()) { operatorInfo.status = OperatorStatus.DEREGISTERED; + delegationManager.deregisterOperatorFromAVS(operator); emit OperatorDeregistered(operator, operatorId); } @@ -835,18 +838,6 @@ contract RegistryCoordinator is return _hashTypedDataV4(keccak256(abi.encode(OPERATOR_CHURN_APPROVAL_TYPEHASH, registeringOperatorId, operatorKickParams, salt, expiry))); } -<<<<<<< HEAD - /** - * @notice Returns the message hash that an operator must sign to register their BLS public key. - * @param operator is the address of the operator registering their BLS public key - */ - function pubkeyRegistrationMessageHash(address operator) public view returns (BN254.G1Point memory) { - return BN254.hashToG1( - _hashTypedDataV4( - keccak256(abi.encode(PUBKEY_REGISTRATION_TYPEHASH, operator)) - ) - ); -======= /// @dev need to override function here since its defined in both these contracts function owner() public @@ -855,6 +846,5 @@ contract RegistryCoordinator is returns (address) { return OwnableUpgradeable.owner(); ->>>>>>> ecf7849 (chore: remove ServiceManagerBase and add RegistryCoordinator owner (#98)) } } diff --git a/src/interfaces/IRegistryCoordinator.sol b/src/interfaces/IRegistryCoordinator.sol index ccbd8a86..02b72a5e 100644 --- a/src/interfaces/IRegistryCoordinator.sol +++ b/src/interfaces/IRegistryCoordinator.sol @@ -26,7 +26,7 @@ interface IRegistryCoordinator { /// @notice emitted when all the operators for a quorum are updated at once event QuorumBlockNumberUpdated(uint8 indexed quorumNumber, uint256 blocknumber); - + // DATA STRUCTURES enum OperatorStatus { diff --git a/test/harnesses/RegistryCoordinatorHarness.sol b/test/harnesses/RegistryCoordinatorHarness.sol index eff9bead..246ea705 100644 --- a/test/harnesses/RegistryCoordinatorHarness.sol +++ b/test/harnesses/RegistryCoordinatorHarness.sol @@ -6,11 +6,12 @@ import "src/RegistryCoordinator.sol"; // wrapper around the RegistryCoordinator contract that exposes the internal functions for unit testing. contract RegistryCoordinatorHarness is RegistryCoordinator { constructor( + IDelegationManager _delegationManager, ISlasher _slasher, IStakeRegistry _stakeRegistry, IBLSApkRegistry _blsApkRegistry, IIndexRegistry _indexRegistry - ) RegistryCoordinator(_slasher, _stakeRegistry, _blsApkRegistry, _indexRegistry) { + ) RegistryCoordinator(_delegationManager, _slasher, _stakeRegistry, _blsApkRegistry, _indexRegistry) { _transferOwnership(msg.sender); } diff --git a/test/integration/CoreRegistration.t.sol b/test/integration/CoreRegistration.t.sol new file mode 100644 index 00000000..1df04dfc --- /dev/null +++ b/test/integration/CoreRegistration.t.sol @@ -0,0 +1,175 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity =0.8.12; + +import "test/utils/MockAVSDeployer.sol"; +import { DelegationManager } from "eigenlayer-contracts/src/contracts/core/DelegationManager.sol"; +import { IDelegationManager } from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; + +contract Test_CoreRegistration is MockAVSDeployer { + // Contracts + DelegationManager public delegationManager; + + // Operator info + uint256 operatorPrivateKey = 420; + address operator; + + // Dummy vals used across tests + bytes32 emptySalt; + uint256 maxExpiry = type(uint256).max; + string emptyStringForMetadataURI; + + function setUp() public { + _deployMockEigenLayerAndAVS(); + + // Deploy New DelegationManager + DelegationManager delegationManagerImplementation = new DelegationManager(strategyManagerMock, slasher, eigenPodManagerMock); + delegationManager = DelegationManager( + address( + new TransparentUpgradeableProxy( + address(delegationManagerImplementation), + address(proxyAdmin), + abi.encodeWithSelector( + DelegationManager.initialize.selector, + address(this), + pauserRegistry, + 0, // 0 is initialPausedStatus + 50400 // Initial withdrawal delay blocks + ) + ) + ) + ); + + // Deploy New RegistryCoordinator + registryCoordinatorImplementation = new RegistryCoordinatorHarness( + delegationManager, + slasher, + stakeRegistry, + blsApkRegistry, + indexRegistry + ); + + // Upgrade Registry Coordinator + cheats.prank(proxyAdminOwner); + proxyAdmin.upgrade( + TransparentUpgradeableProxy(payable(address(registryCoordinator))), + address(registryCoordinatorImplementation) + ); + + // Set operator address + operator = cheats.addr(operatorPrivateKey); + pubkeyCompendium.setBLSPublicKey(operator, defaultPubKey); + + // Register operator to EigenLayer + cheats.prank(operator); + delegationManager.registerAsOperator( + IDelegationManager.OperatorDetails({ + earningsReceiver: operator, + delegationApprover: address(0), + stakerOptOutWindowBlocks: 0 + }), + emptyStringForMetadataURI + ); + + // Set operator weight in single quorum + bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(MAX_QUORUM_BITMAP); + for (uint i = 0; i < quorumNumbers.length; i++) { + stakeRegistry.setOperatorWeight(uint8(quorumNumbers[i]), operator, defaultStake); + } + } + + function test_registerOperator_coreStateChanges() public { + bytes memory quorumNumbers = new bytes(1); + + // Get operator signature + ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature = _getOperatorSignature( + operatorPrivateKey, + operator, + address(registryCoordinator), + emptySalt, + maxExpiry + ); + + // Register operator + cheats.prank(operator); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, operatorSignature); + + // Check operator is registered + IDelegationManager.OperatorAVSRegistrationStatus operatorStatus = delegationManager.avsOperatorStatus(address(registryCoordinator), operator); + assertEq(uint8(operatorStatus), uint8(IDelegationManager.OperatorAVSRegistrationStatus.REGISTERED)); + } + + function test_deregisterOperator_coreStateChanges() public { + // Register operator + bytes memory quorumNumbers = new bytes(1); + _registerOperator(quorumNumbers); + + // Deregister Operator + cheats.prank(operator); + registryCoordinator.deregisterOperator(quorumNumbers); + + // Check operator is deregistered + IDelegationManager.OperatorAVSRegistrationStatus operatorStatus = delegationManager.avsOperatorStatus(address(registryCoordinator), operator); + assertEq(uint8(operatorStatus), uint8(IDelegationManager.OperatorAVSRegistrationStatus.UNREGISTERED)); + } + + function test_deregisterOperator_notGloballyDeregistered() public { + // Register operator with all quorums + bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(MAX_QUORUM_BITMAP); + emit log_named_bytes("quorumNumbers", quorumNumbers); + _registerOperator(quorumNumbers); + + // Deregister Operator with single quorum + quorumNumbers = new bytes(1); + cheats.prank(operator); + registryCoordinator.deregisterOperator(quorumNumbers); + + // Check operator is still registered + IDelegationManager.OperatorAVSRegistrationStatus operatorStatus = delegationManager.avsOperatorStatus(address(registryCoordinator), operator); + assertEq(uint8(operatorStatus), uint8(IDelegationManager.OperatorAVSRegistrationStatus.REGISTERED)); + } + + function test_setMetadataURI_fail_notServiceManagerOwner() public { + cheats.prank(operator); + cheats.expectRevert("Ownable: caller is not the owner"); + registryCoordinator.setMetadataURI("Test MetadataURI"); + } + + function test_setMetadataURI() public { + cheats.prank(registryCoordinatorOwner); + registryCoordinator.setMetadataURI("Test MetadataURI"); + } + + // Utils + function _registerOperator(bytes memory quorumNumbers) internal { + // Get operator signature + ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature = _getOperatorSignature( + operatorPrivateKey, + operator, + address(registryCoordinator), + emptySalt, + maxExpiry + ); + + // Register operator + cheats.prank(operator); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, operatorSignature); + } + + function _getOperatorSignature( + uint256 _operatorPrivateKey, + address operatorToSign, + address avs, + bytes32 salt, + uint256 expiry + ) internal view returns (ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature) { + operatorSignature.salt = salt; + operatorSignature.expiry = expiry; + { + bytes32 digestHash = delegationManager.calculateOperatorAVSRegistrationDigestHash(operatorToSign, avs, salt, expiry); + (uint8 v, bytes32 r, bytes32 s) = cheats.sign(_operatorPrivateKey, digestHash); + operatorSignature.signature = abi.encodePacked(r, s, v); + } + return operatorSignature; + } + +} \ No newline at end of file diff --git a/test/unit/RegistryCoordinatorUnit.t.sol b/test/unit/RegistryCoordinatorUnit.t.sol index bc398ff5..3f20e23d 100644 --- a/test/unit/RegistryCoordinatorUnit.t.sol +++ b/test/unit/RegistryCoordinatorUnit.t.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; -import "../utils/MockAVSDeployer.sol"; +import "test/utils/MockAVSDeployer.sol"; contract RegistryCoordinatorUnit is MockAVSDeployer { using BN254 for BN254.G1Point; @@ -116,6 +116,7 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { function testRegisterOperatorWithCoordinator_WhenPaused_Reverts() public { bytes memory emptyQuorumNumbers = new bytes(0); + ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; // pause registerOperator cheats.prank(pauser); @@ -123,38 +124,42 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { cheats.startPrank(defaultOperator); cheats.expectRevert(bytes("Pausable: index is paused")); - registryCoordinator.registerOperator(emptyQuorumNumbers, defaultSocket, pubkeyRegistrationParams); + registryCoordinator.registerOperator(emptyQuorumNumbers, defaultSocket, emptySig); } function testRegisterOperatorWithCoordinator_EmptyQuorumNumbers_Reverts() public { bytes memory emptyQuorumNumbers = new bytes(0); + ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; cheats.expectRevert("RegistryCoordinator._registerOperator: bitmap cannot be 0"); cheats.prank(defaultOperator); - registryCoordinator.registerOperator(emptyQuorumNumbers, defaultSocket, pubkeyRegistrationParams); + registryCoordinator.registerOperator(emptyQuorumNumbers, defaultSocket, emptySig); } function testRegisterOperatorWithCoordinator_QuorumNumbersTooLarge_Reverts() public { bytes memory quorumNumbersTooLarge = new bytes(1); - quorumNumbersTooLarge[0] = 0xC0; + ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; + quorumNumbersTooLarge[0] = 0xC0; cheats.expectRevert("BitmapUtils.orderedBytesArrayToBitmap: bitmap exceeds max value"); cheats.prank(defaultOperator); - registryCoordinator.registerOperator(quorumNumbersTooLarge, defaultSocket, pubkeyRegistrationParams); + registryCoordinator.registerOperator(quorumNumbersTooLarge, defaultSocket, emptySig); } function testRegisterOperatorWithCoordinator_QuorumNotCreated_Reverts() public { _deployMockEigenLayerAndAVS(10); bytes memory quorumNumbersNotCreated = new bytes(1); - quorumNumbersNotCreated[0] = 0x0B; + ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; + quorumNumbersNotCreated[0] = 0x0B; cheats.prank(defaultOperator); cheats.expectRevert("BitmapUtils.orderedBytesArrayToBitmap: bitmap exceeds max value"); - registryCoordinator.registerOperator(quorumNumbersNotCreated, defaultSocket, pubkeyRegistrationParams); + registryCoordinator.registerOperator(quorumNumbersNotCreated, defaultSocket, emptySig); } function testRegisterOperatorWithCoordinatorForSingleQuorum_Valid() public { bytes memory quorumNumbers = new bytes(1); + ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; quorumNumbers[0] = bytes1(defaultQuorumNumber); stakeRegistry.setOperatorWeight(uint8(quorumNumbers[0]), defaultOperator, defaultStake); @@ -170,7 +175,7 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { uint256 gasBefore = gasleft(); cheats.prank(defaultOperator); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, emptySig); uint256 gasAfter = gasleft(); emit log_named_uint("gasUsed", gasBefore - gasAfter); @@ -197,6 +202,7 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { function testRegisterOperatorWithCoordinatorForFuzzedQuorums_Valid(uint256 quorumBitmap) public { quorumBitmap = quorumBitmap & MAX_QUORUM_BITMAP; + ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; cheats.assume(quorumBitmap != 0); bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); @@ -222,7 +228,7 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { uint256 gasBefore = gasleft(); cheats.prank(defaultOperator); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, emptySig); uint256 gasAfter = gasleft(); emit log_named_uint("gasUsed", gasBefore - gasAfter); emit log_named_uint("numQuorums", quorumNumbers.length); @@ -249,6 +255,7 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { function testRegisterOperatorWithCoordinator_RegisteredOperatorForNewQuorums_Valid() public { uint256 registrationBlockNumber = block.number + 100; uint256 nextRegistrationBlockNumber = registrationBlockNumber + 100; + ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; bytes memory quorumNumbers = new bytes(1); quorumNumbers[0] = bytes1(defaultQuorumNumber); @@ -256,7 +263,7 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { stakeRegistry.setOperatorWeight(uint8(quorumNumbers[0]), defaultOperator, defaultStake); cheats.prank(defaultOperator); cheats.roll(registrationBlockNumber); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, emptySig); bytes memory newQuorumNumbers = new bytes(1); newQuorumNumbers[0] = bytes1(defaultQuorumNumber+1); @@ -272,7 +279,7 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { emit QuorumIndexUpdate(defaultOperatorId, uint8(newQuorumNumbers[0]), 0); cheats.roll(nextRegistrationBlockNumber); cheats.prank(defaultOperator); - registryCoordinator.registerOperator(newQuorumNumbers, defaultSocket, pubkeyRegistrationParams); + registryCoordinator.registerOperator(newQuorumNumbers, defaultSocket, emptySig); uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers) | BitmapUtils.orderedBytesArrayToBitmap(newQuorumNumbers); @@ -306,6 +313,7 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { function testRegisterOperatorWithCoordinator_OverFilledQuorum_Reverts(uint256 pseudoRandomNumber) public { uint32 numOperators = defaultMaxOperatorCount; uint32 registrationBlockNumber = 200; + ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; bytes memory quorumNumbers = new bytes(1); quorumNumbers[0] = bytes1(defaultQuorumNumber); @@ -324,18 +332,19 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { address operatorToRegister = _incrementAddress(defaultOperator, numOperators); BN254.G1Point memory operatorToRegisterPubKey = BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, numOperators))); - blsApkRegistry.setBLSPublicKey(operatorToRegister, operatorToRegisterPubKey); + pubkeyCompendium.setBLSPublicKey(operatorToRegister, operatorToRegisterPubKey); stakeRegistry.setOperatorWeight(defaultQuorumNumber, operatorToRegister, defaultStake); cheats.prank(operatorToRegister); cheats.expectRevert("RegistryCoordinator.registerOperator: operator count exceeds maximum"); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, emptySig); } function testRegisterOperatorWithCoordinator_RegisteredOperatorForSameQuorums_Reverts() public { uint256 registrationBlockNumber = block.number + 100; uint256 nextRegistrationBlockNumber = registrationBlockNumber + 100; + ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; bytes memory quorumNumbers = new bytes(1); quorumNumbers[0] = bytes1(defaultQuorumNumber); @@ -343,12 +352,12 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { stakeRegistry.setOperatorWeight(uint8(quorumNumbers[0]), defaultOperator, defaultStake); cheats.prank(defaultOperator); cheats.roll(registrationBlockNumber); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, emptySig); cheats.prank(defaultOperator); cheats.roll(nextRegistrationBlockNumber); cheats.expectRevert("RegistryCoordinator._registerOperator: operator already registered for some quorums being registered for"); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, emptySig); } function testDeregisterOperatorWithCoordinator_WhenPaused_Reverts() public { @@ -393,6 +402,7 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { } function testDeregisterOperatorWithCoordinatorForSingleQuorumAndSingleOperator_Valid() public { + ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; uint32 registrationBlockNumber = 100; uint32 deregistrationBlockNumber = 200; @@ -405,7 +415,7 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { cheats.roll(registrationBlockNumber); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, emptySig); uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers); @@ -440,6 +450,7 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { } function testDeregisterOperatorWithCoordinatorForFuzzedQuorumAndSingleOperator_Valid(uint256 quorumBitmap) public { + ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; uint32 registrationBlockNumber = 100; uint32 deregistrationBlockNumber = 200; @@ -455,7 +466,7 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { cheats.roll(registrationBlockNumber); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, emptySig); cheats.expectEmit(true, true, true, true, address(blsApkRegistry)); emit OperatorRemovedFromQuorums(defaultOperator, quorumNumbers); @@ -568,6 +579,7 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { function testReregisterOperatorWithCoordinator_Valid() public { testDeregisterOperatorWithCoordinatorForSingleQuorumAndSingleOperator_Valid(); + ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; uint32 reregistrationBlockNumber = 201; bytes memory quorumNumbers = new bytes(1); @@ -582,7 +594,7 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0); // re-register the operator - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, emptySig); // check success of registration uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers); @@ -659,7 +671,7 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { }); } - blsApkRegistry.setBLSPublicKey(operatorToRegister, operatorToRegisterPubKey); + pubkeyCompendium.setBLSPublicKey(operatorToRegister, operatorToRegisterPubKey); uint96 registeringStake = defaultKickBIPsOfOperatorStake * defaultStake; stakeRegistry.setOperatorWeight(defaultQuorumNumber, operatorToRegister, registeringStake); @@ -680,15 +692,16 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { emit QuorumIndexUpdate(operatorToRegisterId, defaultQuorumNumber, numOperators - 1); { + ISignatureUtils.SignatureWithSaltAndExpiry memory emptyAVSRegSig; ISignatureUtils.SignatureWithSaltAndExpiry memory signatureWithExpiry = _signOperatorChurnApproval(operatorToRegisterId, operatorKickParams, defaultSalt, block.timestamp + 10); cheats.prank(operatorToRegister); uint256 gasBefore = gasleft(); registryCoordinator.registerOperatorWithChurn( quorumNumbers, - defaultSocket, - pubkeyRegistrationParams, + defaultSocket, operatorKickParams, - signatureWithExpiry + signatureWithExpiry, + emptyAVSRegSig ); uint256 gasAfter = gasleft(); emit log_named_uint("gasUsed", gasBefore - gasAfter); @@ -721,6 +734,7 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { function testRegisterOperatorWithCoordinatorWithKicks_LessThanKickBIPsOfOperatorStake_Reverts(uint256 pseudoRandomNumber) public { bytes memory quorumNumbers = new bytes(1); quorumNumbers[0] = bytes1(defaultQuorumNumber); + ISignatureUtils.SignatureWithSaltAndExpiry memory emptyAVSRegSig; ( address operatorToRegister, @@ -735,18 +749,13 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { ISignatureUtils.SignatureWithSaltAndExpiry memory signatureWithExpiry = _signOperatorChurnApproval(operatorToRegisterId, operatorKickParams, defaultSalt, block.timestamp + 10); cheats.prank(operatorToRegister); cheats.expectRevert("RegistryCoordinator._validateChurn: incoming operator has insufficient stake for churn"); - registryCoordinator.registerOperatorWithChurn( - quorumNumbers, - defaultSocket, - pubkeyRegistrationParams, - operatorKickParams, - signatureWithExpiry - ); + registryCoordinator.registerOperatorWithChurn(quorumNumbers, defaultSocket, operatorKickParams, signatureWithExpiry, emptyAVSRegSig); } function testRegisterOperatorWithCoordinatorWithKicks_LessThanKickBIPsOfTotalStake_Reverts(uint256 pseudoRandomNumber) public { bytes memory quorumNumbers = new bytes(1); quorumNumbers[0] = bytes1(defaultQuorumNumber); + ISignatureUtils.SignatureWithSaltAndExpiry memory emptyAVSRegSig; uint96 operatorToKickStake = defaultMaxOperatorCount * defaultStake; ( @@ -764,18 +773,13 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { ISignatureUtils.SignatureWithSaltAndExpiry memory signatureWithExpiry = _signOperatorChurnApproval(operatorToRegisterId, operatorKickParams, defaultSalt, block.timestamp + 10); cheats.prank(operatorToRegister); cheats.expectRevert("RegistryCoordinator._validateChurn: cannot kick operator with more than kickBIPsOfTotalStake"); - registryCoordinator.registerOperatorWithChurn( - quorumNumbers, - defaultSocket, - pubkeyRegistrationParams, - operatorKickParams, - signatureWithExpiry - ); + registryCoordinator.registerOperatorWithChurn(quorumNumbers, defaultSocket, operatorKickParams, signatureWithExpiry, emptyAVSRegSig); } function testRegisterOperatorWithCoordinatorWithKicks_InvalidSignatures_Reverts(uint256 pseudoRandomNumber) public { bytes memory quorumNumbers = new bytes(1); quorumNumbers[0] = bytes1(defaultQuorumNumber); + ISignatureUtils.SignatureWithSaltAndExpiry memory emptyAVSRegSig; ( address operatorToRegister, @@ -793,18 +797,13 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { signatureWithSaltAndExpiry.salt = defaultSalt; cheats.prank(operatorToRegister); cheats.expectRevert("ECDSA: invalid signature"); - registryCoordinator.registerOperatorWithChurn( - quorumNumbers, - defaultSocket, - pubkeyRegistrationParams, - operatorKickParams, - signatureWithSaltAndExpiry - ); + registryCoordinator.registerOperatorWithChurn(quorumNumbers, defaultSocket, operatorKickParams, signatureWithSaltAndExpiry, emptyAVSRegSig); } function testRegisterOperatorWithCoordinatorWithKicks_ExpiredSignatures_Reverts(uint256 pseudoRandomNumber) public { bytes memory quorumNumbers = new bytes(1); quorumNumbers[0] = bytes1(defaultQuorumNumber); + ISignatureUtils.SignatureWithSaltAndExpiry memory emptyAVSRegSig; ( address operatorToRegister, @@ -820,24 +819,19 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { ISignatureUtils.SignatureWithSaltAndExpiry memory signatureWithSaltAndExpiry = _signOperatorChurnApproval(operatorToRegisterId, operatorKickParams, defaultSalt, block.timestamp - 1); cheats.prank(operatorToRegister); cheats.expectRevert("RegistryCoordinator._verifyChurnApproverSignature: churnApprover signature expired"); - registryCoordinator.registerOperatorWithChurn( - quorumNumbers, - defaultSocket, - pubkeyRegistrationParams, - operatorKickParams, - signatureWithSaltAndExpiry - ); + registryCoordinator.registerOperatorWithChurn(quorumNumbers, defaultSocket, operatorKickParams, signatureWithSaltAndExpiry, emptyAVSRegSig); } function testEjectOperatorFromCoordinator_AllQuorums_Valid() public { // register operator with default stake with default quorum number bytes memory quorumNumbers = new bytes(1); quorumNumbers[0] = bytes1(defaultQuorumNumber); + ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; stakeRegistry.setOperatorWeight(uint8(quorumNumbers[0]), defaultOperator, defaultStake); cheats.prank(defaultOperator); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, emptySig); cheats.expectEmit(true, true, true, true, address(blsApkRegistry)); emit OperatorRemovedFromQuorums(defaultOperator, quorumNumbers); @@ -866,13 +860,14 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { bytes memory quorumNumbers = new bytes(2); quorumNumbers[0] = bytes1(defaultQuorumNumber); quorumNumbers[1] = bytes1(defaultQuorumNumber + 1); + ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; for (uint i = 0; i < quorumNumbers.length; i++) { stakeRegistry.setOperatorWeight(uint8(quorumNumbers[i]), defaultOperator, defaultStake); } cheats.prank(defaultOperator); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, emptySig); // eject from only first quorum bytes memory quorumNumbersToEject = new bytes(1); @@ -905,11 +900,12 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { function testEjectOperatorFromCoordinator_NotEjector_Reverts() public { bytes memory quorumNumbers = new bytes(1); quorumNumbers[0] = bytes1(defaultQuorumNumber); + ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; stakeRegistry.setOperatorWeight(uint8(quorumNumbers[0]), defaultOperator, defaultStake); cheats.prank(defaultOperator); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, emptySig); cheats.expectRevert("RegistryCoordinator.onlyEjector: caller is not the ejector"); cheats.prank(defaultOperator); @@ -976,6 +972,6 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { }); } - blsApkRegistry.setBLSPublicKey(operatorToRegister, operatorToRegisterPubKey); + pubkeyCompendium.setBLSPublicKey(operatorToRegister, operatorToRegisterPubKey); } } diff --git a/test/unit/StakeRegistryUnit.t.sol b/test/unit/StakeRegistryUnit.t.sol index e6bd3c6a..7ab78b1e 100644 --- a/test/unit/StakeRegistryUnit.t.sol +++ b/test/unit/StakeRegistryUnit.t.sol @@ -103,6 +103,7 @@ contract StakeRegistryUnitTests is Test { cheats.startPrank(registryCoordinatorOwner); registryCoordinator = new RegistryCoordinatorHarness( + delegationMock, slasher, stakeRegistry, IBLSApkRegistry(apkRegistry), diff --git a/test/utils/MockAVSDeployer.sol b/test/utils/MockAVSDeployer.sol index 5e6b9cb5..45ea8c4c 100644 --- a/test/utils/MockAVSDeployer.sol +++ b/test/utils/MockAVSDeployer.sol @@ -7,11 +7,13 @@ import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.so import {Slasher} from "eigenlayer-contracts/src/contracts/core/Slasher.sol"; import {ISlasher} from "eigenlayer-contracts/src/contracts/interfaces/ISlasher.sol"; import {PauserRegistry} from "eigenlayer-contracts/src/contracts/permissions/PauserRegistry.sol"; +import {IStrategyManager} from "eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol"; import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; import {BitmapUtils} from "src/libraries/BitmapUtils.sol"; import {BN254} from "src/libraries/BN254.sol"; +import {BLSPublicKeyCompendium} from "src/BLSPublicKeyCompendium.sol"; import {OperatorStateRetriever} from "src/OperatorStateRetriever.sol"; import {RegistryCoordinator} from "src/RegistryCoordinator.sol"; import {RegistryCoordinatorHarness} from "test/harnesses/RegistryCoordinatorHarness.sol"; @@ -26,13 +28,10 @@ import {IRegistryCoordinator} from "src/interfaces/IRegistryCoordinator.sol"; import {StrategyManagerMock} from "eigenlayer-contracts/src/test/mocks/StrategyManagerMock.sol"; import {EigenPodManagerMock} from "eigenlayer-contracts/src/test/mocks/EigenPodManagerMock.sol"; -<<<<<<< HEAD -import {ServiceManagerMock} from "test/mocks/ServiceManagerMock.sol"; -======= import {OwnableMock} from "eigenlayer-contracts/src/test/mocks/OwnableMock.sol"; ->>>>>>> ecf7849 (chore: remove ServiceManagerBase and add RegistryCoordinator owner (#98)) import {DelegationManagerMock} from "eigenlayer-contracts/src/test/mocks/DelegationManagerMock.sol"; -import {BLSApkRegistryHarness} from "test/harnesses/BLSApkRegistryHarness.sol"; +import {SlasherMock} from "eigenlayer-contracts/src/test/mocks/SlasherMock.sol"; +import {BLSPublicKeyCompendiumMock} from "test/mocks/BLSPublicKeyCompendiumMock.sol"; import {EmptyContract} from "eigenlayer-contracts/src/test/mocks/EmptyContract.sol"; import {StakeRegistryHarness} from "test/harnesses/StakeRegistryHarness.sol"; @@ -51,6 +50,7 @@ contract MockAVSDeployer is Test { Slasher public slasherImplementation; EmptyContract public emptyContract; + BLSPublicKeyCompendiumMock public pubkeyCompendium; RegistryCoordinatorHarness public registryCoordinatorImplementation; StakeRegistryHarness public stakeRegistryImplementation; @@ -60,7 +60,7 @@ contract MockAVSDeployer is Test { OperatorStateRetriever public operatorStateRetriever; RegistryCoordinatorHarness public registryCoordinator; StakeRegistryHarness public stakeRegistry; - BLSApkRegistryHarness public blsApkRegistry; + IBLSApkRegistry public blsApkRegistry; IIndexRegistry public indexRegistry; StrategyManagerMock public strategyManagerMock; @@ -77,7 +77,7 @@ contract MockAVSDeployer is Test { bytes32 defaultSalt = bytes32(uint256(keccak256("defaultSalt"))); address ejector = address(uint160(uint256(keccak256("ejector")))); - + address defaultOperator = address(uint160(uint256(keccak256("defaultOperator")))); bytes32 defaultOperatorId; BN254.G1Point internal defaultPubKey = BN254.G1Point(18260007818883133054078754218619977578772505796600400998181738095793040006897,3432351341799135763167709827653955074218841517684851694584291831827675065899); @@ -97,8 +97,6 @@ contract MockAVSDeployer is Test { uint32 registrationBlockNumber = 100; uint32 blocksBetweenRegistrations = 10; - IBLSApkRegistry.PubkeyRegistrationParams pubkeyRegistrationParams; - struct OperatorMetadata { uint256 quorumBitmap; address operator; @@ -144,6 +142,10 @@ contract MockAVSDeployer is Test { eigenPodManagerMock, slasher ); + + pubkeyCompendium = new BLSPublicKeyCompendiumMock(); + pubkeyCompendium.setBLSPublicKey(defaultOperator, defaultPubKey); + cheats.stopPrank(); cheats.startPrank(registryCoordinatorOwner); @@ -175,7 +177,7 @@ contract MockAVSDeployer is Test { ) ); - blsApkRegistry = BLSApkRegistryHarness( + blsApkRegistry = BLSApkRegistry( address( new TransparentUpgradeableProxy( address(emptyContract), @@ -186,7 +188,6 @@ contract MockAVSDeployer is Test { ); cheats.stopPrank(); - cheats.startPrank(proxyAdminOwner); stakeRegistryImplementation = new StakeRegistryHarness( @@ -199,8 +200,9 @@ contract MockAVSDeployer is Test { address(stakeRegistryImplementation) ); - blsApkRegistryImplementation = new BLSApkRegistryHarness( - registryCoordinator + blsApkRegistryImplementation = new BLSApkRegistry( + registryCoordinator, + BLSPublicKeyCompendium(address(pubkeyCompendium)) ); proxyAdmin.upgrade( @@ -217,9 +219,6 @@ contract MockAVSDeployer is Test { address(indexRegistryImplementation) ); - // set the public key for an operator, using harnessed function to bypass checks - blsApkRegistry.setBLSPublicKey(defaultOperator, defaultPubKey); - // setup the dummy minimum stake for quorum uint96[] memory minimumStakeForQuorum = new uint96[](numQuorumsToAdd); for (uint256 i = 0; i < minimumStakeForQuorum.length; i++) { @@ -238,6 +237,7 @@ contract MockAVSDeployer is Test { } registryCoordinatorImplementation = new RegistryCoordinatorHarness( + delegationMock, slasher, stakeRegistry, blsApkRegistry, @@ -290,15 +290,16 @@ contract MockAVSDeployer is Test { // quorumBitmap can only have 192 least significant bits quorumBitmap &= MAX_QUORUM_BITMAP; - blsApkRegistry.setBLSPublicKey(operator, pubKey); + pubkeyCompendium.setBLSPublicKey(operator, pubKey); bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); for (uint i = 0; i < quorumNumbers.length; i++) { stakeRegistry.setOperatorWeight(uint8(quorumNumbers[i]), operator, stake); } + ISignatureUtils.SignatureWithSaltAndExpiry memory emptySignatureAndExpiry; cheats.prank(operator); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, emptySignatureAndExpiry); } /** @@ -308,15 +309,16 @@ contract MockAVSDeployer is Test { // quorumBitmap can only have 192 least significant bits quorumBitmap &= MAX_QUORUM_BITMAP; - blsApkRegistry.setBLSPublicKey(operator, pubKey); + pubkeyCompendium.setBLSPublicKey(operator, pubKey); bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); for (uint i = 0; i < quorumNumbers.length; i++) { stakeRegistry.setOperatorWeight(uint8(quorumNumbers[i]), operator, stakes[uint8(quorumNumbers[i])]); } + ISignatureUtils.SignatureWithSaltAndExpiry memory emptySignatureAndExpiry; cheats.prank(operator); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, emptySignatureAndExpiry); } function _registerRandomOperators(uint256 pseudoRandomNumber) internal returns(OperatorMetadata[] memory, uint256[][] memory) {