From 7d49b5181b09198ed275783453aa082bb3766990 Mon Sep 17 00:00:00 2001 From: Gautham Anant <32277907+gpsanant@users.noreply.github.com> Date: Wed, 17 Jul 2024 08:42:54 -0700 Subject: [PATCH 01/27] Update LICENSE (#285) --- LICENSE | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/LICENSE b/LICENSE index 91921f64..b65b84a3 100644 --- a/LICENSE +++ b/LICENSE @@ -12,9 +12,35 @@ Licensor: Layr Labs, Inc. Licensed Work: EigenLayer Middleware Contracts The Licensed Work is (c) 2023 Layr Labs, Inc. -Additional Use Grant: None. - -Change Date: 2025-10-16 (October 16th, 2025) +Additional Use Grant: + +You may additionally use any of the software included in the following repositories +[here](https://docs.google.com/spreadsheets/d/1PlJRow5C0GMqXZlIxRm5CEnkhH-gMV1wIdq1pCfbZco/edit?usp=sharing) +(“Additional Use Grant Software”) for production commercial uses, but only if such +uses are (i) built on or using the EigenLayer Protocol or EigenDA, and (ii) not +Competing Uses. + +“Competing Use” means any use of the Additional Use Grant Software in any product, +protocol, application or service that is made available to third parties and that +(i) substitutes for use of EigenLayer Protocol or EigenDA, (ii) offers the same or +substantially similar functionality as the EigenLayer Protocol or EigenDA or +(iii) is built on or using a protocol with substantially similar functionality as +the EigenLayer Protocol. + +EigenLayer Protocol means the restaking protocol as further described in the +documentation [here](https://docs.eigenlayer.xyz/), as updated from time to time. + +EigenDA means the data availability protocol built on top of the EigenLayer +Protocol as further described in the documentation +[here](https://docs.eigenlayer.xyz/eigenda/overview), as updated from time to time. + +Change Dates: + +- All commits at or prior to commit a23de118e7d16081d350c7f83c24261d1421b0ba +(i.e. committed to this repository on or before May 19, 2024, the date of) have a +change date of 2025-10-16 (October 16th, 2025) +- All commits after a23de118e7d16081d350c7f83c24261d1421b0ba (i.e. committed to this +repository after May 19, 2024) have a change date of 2027-05-01 (May 1st, 2027) Change License: MIT From 163f4c334042accc12baacd284b78218e0a1c56e Mon Sep 17 00:00:00 2001 From: steven Date: Thu, 25 Jul 2024 10:53:02 -0400 Subject: [PATCH 02/27] chore: upgrade core to target operator set release --- lib/eigenlayer-contracts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/eigenlayer-contracts b/lib/eigenlayer-contracts index bd1c9b2d..f88912de 160000 --- a/lib/eigenlayer-contracts +++ b/lib/eigenlayer-contracts @@ -1 +1 @@ -Subproject commit bd1c9b2d34e93ba5d0be224bc15d2c211848dad6 +Subproject commit f88912dec4ce2854b0f8824fb293e32eaa6c83d5 From b7f49030171fac63116337f78f1afcfa486f0f55 Mon Sep 17 00:00:00 2001 From: Madhur Shrimal Date: Mon, 5 Aug 2024 16:21:33 -0700 Subject: [PATCH 03/27] chore: update mock contracts with latest interfaces (#293) * chore: update mock contracts with latest interfaces --- lib/eigenlayer-contracts | 2 +- test/mocks/AVSDirectoryMock.sol | 123 ++++++++++++++++++++++++++ test/mocks/RewardsCoordinatorMock.sol | 32 +++++++ 3 files changed, 156 insertions(+), 1 deletion(-) diff --git a/lib/eigenlayer-contracts b/lib/eigenlayer-contracts index f88912de..4716a9c2 160000 --- a/lib/eigenlayer-contracts +++ b/lib/eigenlayer-contracts @@ -1 +1 @@ -Subproject commit f88912dec4ce2854b0f8824fb293e32eaa6c83d5 +Subproject commit 4716a9c2f32c3fcae6c32814be5d6f69b176bdf6 diff --git a/test/mocks/AVSDirectoryMock.sol b/test/mocks/AVSDirectoryMock.sol index 813e913a..581fe8b6 100644 --- a/test/mocks/AVSDirectoryMock.sol +++ b/test/mocks/AVSDirectoryMock.sol @@ -4,6 +4,83 @@ pragma solidity ^0.8.12; import {IAVSDirectory, ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; contract AVSDirectoryMock is IAVSDirectory { + /** + * @notice Called by an AVS to create a list of new operatorSets. + * + * @param operatorSetIds The IDs of the operator set to initialize. + * + * @dev msg.sender must be the AVS. + * @dev The AVS may create operator sets before it becomes an operator set AVS. + */ + function createOperatorSets(uint32[] calldata operatorSetIds) external {} + + /** + * @notice Sets the AVS as an operator set AVS, preventing legacy M2 operator registrations. + * + * @dev msg.sender must be the AVS. + */ + function becomeOperatorSetAVS() external {} + + /** + * @notice Called by an AVS to migrate operators that have a legacy M2 registration to operator sets. + * + * @param operators The list of operators to migrate + * @param operatorSetIds The list of operatorSets to migrate the operators to + * + * @dev The msg.sender used is the AVS + * @dev The operator can only be migrated at most once per AVS + * @dev The AVS can no longer register operators via the legacy M2 registration path once it begins migration + * @dev The operator is deregistered from the M2 legacy AVS once migrated + */ + function migrateOperatorsToOperatorSets( + address[] calldata operators, + uint32[][] calldata operatorSetIds + ) external {} + + /** + * @notice Called by AVSs to add an operator to list of operatorSets. + * + * @param operator The address of the operator to be added to the operator set. + * @param operatorSetIds The IDs of the operator sets. + * @param operatorSignature The signature of the operator on their intent to register. + * + * @dev msg.sender is used as the AVS. + * @dev The operator must not have a pending deregistration from the operator set. + */ + function registerOperatorToOperatorSets( + address operator, + uint32[] calldata operatorSetIds, + ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature + ) external {} + + /** + * @notice Called by AVSs to remove an operator from an operator set. + * + * @param operator The address of the operator to be removed from the operator set. + * @param operatorSetIds The IDs of the operator sets. + * + * @dev msg.sender is used as the AVS. + */ + function deregisterOperatorFromOperatorSets(address operator, uint32[] calldata operatorSetIds) external {} + + /** + * @notice Called by an operator to deregister from an operator set + * + * @param operator The operator to deregister from the operatorSets. + * @param avs The address of the AVS to deregister the operator from. + * @param operatorSetIds The IDs of the operator sets. + * @param operatorSignature the signature of the operator on their intent to deregister or empty if the operator itself is calling + * + * @dev if the operatorSignature is empty, the caller must be the operator + * @dev this will likely only be called in case the AVS contracts are in a state that prevents operators from deregistering + */ + function forceDeregisterFromOperatorSets( + address operator, + address avs, + uint32[] calldata operatorSetIds, + ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature + ) external {} + /** * @notice Called by an avs to register an operator with the avs. * @param operator The address of the operator to register. @@ -27,12 +104,21 @@ contract AVSDirectoryMock is IAVSDirectory { */ function updateAVSMetadataURI(string calldata metadataURI) external {} + /** + * @notice Called by an operator to cancel a salt that has been used to register with an AVS. + * + * @param salt A unique and single use value associated with the approver signature. + */ + function cancelSalt(bytes32 salt) external{} + /** * @notice Returns whether or not the salt has already been used by the operator. * @dev Salts is used in the `registerOperatorToAVS` function. */ function operatorSaltIsSpent(address operator, bytes32 salt) external view returns (bool) {} + function isMember(address avs, address operator, uint32 operatorSetId) external view returns (bool){} + /** * @notice Calculates the digest hash to be signed by an operator to register with an AVS * @param operator The account registering as an operator @@ -47,6 +133,43 @@ contract AVSDirectoryMock is IAVSDirectory { uint256 expiry ) external view returns (bytes32) {} + /** + * @notice Calculates the digest hash to be signed by an operator to register with an operator set. + * + * @param avs The AVS that operator is registering to operator sets for. + * @param operatorSetIds An array of operator set IDs the operator is registering to. + * @param salt A unique and single use value associated with the approver signature. + * @param expiry Time after which the approver's signature becomes invalid. + */ + function calculateOperatorSetRegistrationDigestHash( + address avs, + uint32[] calldata operatorSetIds, + bytes32 salt, + uint256 expiry + ) external view returns (bytes32) {} + + /** + * @notice Calculates the digest hash to be signed by an operator to force deregister from an operator set. + * + * @param avs The AVS that operator is deregistering from. + * @param operatorSetIds An array of operator set IDs the operator is deregistering from. + * @param salt A unique and single use value associated with the approver signature. + * @param expiry Time after which the approver's signature becomes invalid. + */ + function calculateOperatorSetForceDeregistrationTypehash( + address avs, + uint32[] calldata operatorSetIds, + bytes32 salt, + uint256 expiry + ) external view returns (bytes32) {} + + /// @notice Getter function for the current EIP-712 domain separator for this contract. + /// @dev The domain separator will change in the event of a fork that changes the ChainID. + function domainSeparator() external view returns (bytes32) {} + /// @notice The EIP-712 typehash for the Registration struct used by the contract function OPERATOR_AVS_REGISTRATION_TYPEHASH() external view returns (bytes32) {} + + /// @notice The EIP-712 typehash for the OperatorSetRegistration struct used by the contract. + function OPERATOR_SET_REGISTRATION_TYPEHASH() external view returns (bytes32) {} } diff --git a/test/mocks/RewardsCoordinatorMock.sol b/test/mocks/RewardsCoordinatorMock.sol index ff32a27a..11a6c94d 100644 --- a/test/mocks/RewardsCoordinatorMock.sol +++ b/test/mocks/RewardsCoordinatorMock.sol @@ -4,6 +4,7 @@ pragma solidity ^0.8.12; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {IRewardsCoordinator} from "eigenlayer-contracts/src/contracts/interfaces/IRewardsCoordinator.sol"; +import "./AVSDirectoryMock.sol"; contract RewardsCoordinatorMock is IRewardsCoordinator { /// @notice The address of the entity that can update the contract with new merkle roots @@ -25,6 +26,21 @@ contract RewardsCoordinatorMock is IRewardsCoordinator { function cumulativeClaimed(address claimer, IERC20 token) external view returns (uint256) {} + /// @notice the commission for a specific operator for a specific avs + /// NOTE: Currently unused and simply returns the globalOperatorCommissionBips value but will be used in future release + function getOperatorCommissionBips( + address operator, + IAVSDirectory.OperatorSet calldata operatorSet, + RewardType rewardType + ) external view returns (uint16) {} + + /// @notice returns the length of the operator commission update history + function getOperatorCommissionUpdateHistoryLength( + address operator, + IAVSDirectory.OperatorSet calldata operatorSet, + RewardType rewardType + ) external view returns (uint256) {} + function globalOperatorCommissionBips() external view returns (uint16) {} function operatorCommissionBips(address operator, address avs) external view returns (uint16) {} @@ -75,4 +91,20 @@ contract RewardsCoordinatorMock is IRewardsCoordinator { * @param _newValue The new value for isPayAllForRangeSubmitter */ function setRewardsForAllSubmitter(address _submitter, bool _newValue) external {} + + /** + * @notice Sets the commission an operator takes in bips for a given reward type and operatorSet + * @param operatorSet The operatorSet to update commission for + * @param rewardType The associated rewardType to update commission for + * @param commissionBips The commission in bips for the operator, must be <= MAX_COMMISSION_BIPS + * @return effectTimestamp The timestamp at which the operator commission update will take effect + * + * @dev The commission can range from 1 to 10000 + * @dev The commission update takes effect after 7 days + */ + function setOperatorCommissionBips( + IAVSDirectory.OperatorSet calldata operatorSet, + RewardType rewardType, + uint16 commissionBips + ) external returns (uint32) {} } \ No newline at end of file From d991827fe3d3f818c9e80b4f1b4c8e7644d19446 Mon Sep 17 00:00:00 2001 From: steven Date: Thu, 8 Aug 2024 07:25:32 -0400 Subject: [PATCH 04/27] chore: bump to latest operator set release commits --- lib/eigenlayer-contracts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/eigenlayer-contracts b/lib/eigenlayer-contracts index 4716a9c2..ed44903c 160000 --- a/lib/eigenlayer-contracts +++ b/lib/eigenlayer-contracts @@ -1 +1 @@ -Subproject commit 4716a9c2f32c3fcae6c32814be5d6f69b176bdf6 +Subproject commit ed44903ca218a52cf259d226b5a60bee8a8320a7 From 496681381572567e1baeb02c4e6fa54e988ca739 Mon Sep 17 00:00:00 2001 From: steven Date: Thu, 8 Aug 2024 07:33:24 -0400 Subject: [PATCH 05/27] chore: fixes for depedency bump --- test/mocks/AVSDirectoryMock.sol | 4 ++++ test/mocks/RewardsCoordinatorMock.sol | 2 ++ test/unit/ServiceManagerBase.t.sol | 8 +++++++- 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/test/mocks/AVSDirectoryMock.sol b/test/mocks/AVSDirectoryMock.sol index 581fe8b6..fdb02613 100644 --- a/test/mocks/AVSDirectoryMock.sol +++ b/test/mocks/AVSDirectoryMock.sol @@ -172,4 +172,8 @@ contract AVSDirectoryMock is IAVSDirectory { /// @notice The EIP-712 typehash for the OperatorSetRegistration struct used by the contract. function OPERATOR_SET_REGISTRATION_TYPEHASH() external view returns (bytes32) {} + + function isOperatorSetAVS(address avs) external view returns (bool) {} + + function isOperatorSet(address avs, uint32 operatorSetId) external view returns (bool){} } diff --git a/test/mocks/RewardsCoordinatorMock.sol b/test/mocks/RewardsCoordinatorMock.sol index 11a6c94d..6e4f6538 100644 --- a/test/mocks/RewardsCoordinatorMock.sol +++ b/test/mocks/RewardsCoordinatorMock.sol @@ -107,4 +107,6 @@ contract RewardsCoordinatorMock is IRewardsCoordinator { RewardType rewardType, uint16 commissionBips ) external returns (uint32) {} + + function rewardOperatorSetForRange(OperatorSetRewardsSubmission[] calldata rewardsSubmissions) external{} } \ No newline at end of file diff --git a/test/unit/ServiceManagerBase.t.sol b/test/unit/ServiceManagerBase.t.sol index 2b04dabd..7673facd 100644 --- a/test/unit/ServiceManagerBase.t.sol +++ b/test/unit/ServiceManagerBase.t.sol @@ -21,6 +21,9 @@ contract ServiceManagerBase_UnitTests is MockAVSDeployer, IServiceManagerBaseEve uint32 MAX_FUTURE_LENGTH = 28 days; uint32 GENESIS_REWARDS_TIMESTAMP = 1_712_188_800; uint256 MAX_REWARDS_AMOUNT = 1e38 - 1; + uint32 OPERATOR_SET_GENESIS_REWARDS_TIMESTAMP = 0; /// TODO: what values should these have + uint32 OPERATOR_SET_MAX_RETROACTIVE_LENGTH = 0; /// TODO: What values these should have + /// @notice Delay in timestamp before a posted root can be claimed against uint32 activationDelay = 7 days; /// @notice the commission for all operators across all avss @@ -51,11 +54,14 @@ contract ServiceManagerBase_UnitTests is MockAVSDeployer, IServiceManagerBaseEve rewardsCoordinatorImplementation = new RewardsCoordinator( delegationMock, strategyManagerMock, + avsDirectoryMock, CALCULATION_INTERVAL_SECONDS, MAX_REWARDS_DURATION, MAX_RETROACTIVE_LENGTH, MAX_FUTURE_LENGTH, - GENESIS_REWARDS_TIMESTAMP + GENESIS_REWARDS_TIMESTAMP, + OPERATOR_SET_GENESIS_REWARDS_TIMESTAMP, + OPERATOR_SET_MAX_RETROACTIVE_LENGTH ); rewardsCoordinator = RewardsCoordinator( From 4fabf8074ad2e827f9d3da25420beaa6ae5e4926 Mon Sep 17 00:00:00 2001 From: steven <12021290+stevennevins@users.noreply.github.com> Date: Mon, 12 Aug 2024 09:46:27 -0400 Subject: [PATCH 06/27] feat: operator set migration-1-migration (#286) * chore: checkout migration branch * feat: implement migration function * chore: update to release branch * feat: operator set creation for each quorum number * feat: migration with merge sorted array of operators and their quorums * feat: operator set migration working * chore: revert change from testing * chore: revert change from testing * chore: remove extra logging and commented out asserts * chore: remove unused file * fix: remove console logs * refactor: to view functions * chore: nit and remove unneeded function * fix: remove duplication of looping for all the operators * chore: remove comment * feat: allow migrating in two transactions * feat: finalization of migration * chore: use string errors and fix migration issues * chore: rename * feat: use library for merge sort * test: fuzz view function --- src/ServiceManagerBase.sol | 74 ++++++ src/ServiceManagerBaseStorage.sol | 2 + src/libraries/LibMergeSort.sol | 61 +++++ test/harnesses/AVSDirectoryHarness.sol | 49 ++++ test/unit/LibMergeSort.t.sol | 188 ++++++++++++++ test/unit/ServiceManagerBase.t.sol | 2 +- test/unit/ServiceManagerMigration.t.sol | 309 ++++++++++++++++++++++++ 7 files changed, 684 insertions(+), 1 deletion(-) create mode 100644 src/libraries/LibMergeSort.sol create mode 100644 test/harnesses/AVSDirectoryHarness.sol create mode 100644 test/unit/LibMergeSort.t.sol create mode 100644 test/unit/ServiceManagerMigration.t.sol diff --git a/src/ServiceManagerBase.sol b/src/ServiceManagerBase.sol index 3560692b..ab1a47d8 100644 --- a/src/ServiceManagerBase.sol +++ b/src/ServiceManagerBase.sol @@ -4,6 +4,7 @@ pragma solidity ^0.8.12; import {Initializable} from "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol"; import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; import {IAVSDirectory} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; +import {AVSDirectory} from "eigenlayer-contracts/src/contracts/core/AVSDirectory.sol"; import {IRewardsCoordinator} from "eigenlayer-contracts/src/contracts/interfaces/IRewardsCoordinator.sol"; import {ServiceManagerBaseStorage} from "./ServiceManagerBaseStorage.sol"; @@ -11,6 +12,8 @@ import {IServiceManager} from "./interfaces/IServiceManager.sol"; import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol"; import {IStakeRegistry} from "./interfaces/IStakeRegistry.sol"; import {BitmapUtils} from "./libraries/BitmapUtils.sol"; +import {LibMergeSort} from "./libraries/LibMergeSort.sol"; +import {console} from "forge-std/Test.sol"; /** * @title Minimal implementation of a ServiceManager-type contract. @@ -135,6 +138,77 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage { _setRewardsInitiator(newRewardsInitiator); } + function migrateAndCreateOperatorSetIds(uint32[] memory operatorSetsToCreate) external onlyOwner{ + _migrateAndCreateOperatorSetIds(operatorSetsToCreate); + } + + function migrateToOperatorSets(uint32[][] memory operatorSetIds, address[] memory operators) external onlyOwner { + require(!migrationFinalized, "SerivceManager: Migration Already Finalized"); + _migrateToOperatorSets(operatorSetIds, operators); + } + + function finalizeMigration() external onlyOwner{ + require(!migrationFinalized, "SerivceManager: Migration Already Finalized"); + migrationFinalized = true; + } + + function _migrateAndCreateOperatorSetIds(uint32[] memory operatorSetIdsToCreate) internal { + _avsDirectory.becomeOperatorSetAVS(); + AVSDirectory(address(_avsDirectory)).createOperatorSets(operatorSetIdsToCreate); + } + + function _migrateToOperatorSets(uint32[][] memory operatorSetIds, address[] memory operators) internal { + AVSDirectory(address(_avsDirectory)).migrateOperatorsToOperatorSets(operators, operatorSetIds); + } + + function getOperatorsToMigrate() public view returns (uint32[] memory operatorSetIdsToCreate, uint32[][] memory operatorSetIds, address[] memory allOperators) { + uint256 quorumCount = _registryCoordinator.quorumCount(); + + allOperators = new address[](0); + operatorSetIdsToCreate = new uint32[](quorumCount); + + // Step 1: Iterate through quorum numbers and get a list of unique operators + for (uint8 quorumNumber = 0; quorumNumber < quorumCount; quorumNumber++) { + // Step 2: Get operator list for quorum at current block + bytes32[] memory operatorIds = _registryCoordinator.indexRegistry().getOperatorListAtBlockNumber(quorumNumber, uint32(block.number)); + + // Step 3: Convert to address list and maintain a sorted array of operators + address[] memory operators = new address[](operatorIds.length); + for (uint256 i = 0; i < operatorIds.length; i++) { + operators[i] = _registryCoordinator.blsApkRegistry().getOperatorFromPubkeyHash(operatorIds[i]); + // Insert into sorted array of all operators + allOperators = LibMergeSort.mergeSortArrays(allOperators, LibMergeSort.sort(operators)); + } + address[] memory filteredOperators = new address[](allOperators.length); + uint256 count = 0; + for (uint256 i = 0; i < allOperators.length; i++) { + if (allOperators[i] != address(0)) { + filteredOperators[count++] = allOperators[i]; + } + } + // Resize array to remove empty slots + assembly { mstore(filteredOperators, count) } + allOperators = filteredOperators; + + operatorSetIdsToCreate[quorumNumber] = uint32(quorumNumber); + } + + operatorSetIds = new uint32[][](allOperators.length); + // Loop through each unique operator to get the quorums they are registered for + for (uint256 i = 0; i < allOperators.length; i++) { + address operator = allOperators[i]; + bytes32 operatorId = _registryCoordinator.getOperatorId(operator); + uint192 quorumsBitmap = _registryCoordinator.getCurrentQuorumBitmap(operatorId); + bytes memory quorumBytesArray = BitmapUtils.bitmapToBytesArray(quorumsBitmap); + uint32[] memory quorums = new uint32[](quorumBytesArray.length); + for (uint256 j = 0; j < quorumBytesArray.length; j++) { + quorums[j] = uint32(uint8(quorumBytesArray[j])); + } + operatorSetIds[i] = quorums; + } + + } + function _setRewardsInitiator(address newRewardsInitiator) internal { emit RewardsInitiatorUpdated(rewardsInitiator, newRewardsInitiator); rewardsInitiator = newRewardsInitiator; diff --git a/src/ServiceManagerBaseStorage.sol b/src/ServiceManagerBaseStorage.sol index e0c1d86a..4d0c1fec 100644 --- a/src/ServiceManagerBaseStorage.sol +++ b/src/ServiceManagerBaseStorage.sol @@ -35,6 +35,8 @@ abstract contract ServiceManagerBaseStorage is IServiceManager, OwnableUpgradeab /// @notice The address of the entity that can initiate rewards address public rewardsInitiator; + bool public migrationFinalized; + /// @notice Sets the (immutable) `_avsDirectory`, `_rewardsCoordinator`, `_registryCoordinator`, and `_stakeRegistry` addresses constructor( IAVSDirectory __avsDirectory, diff --git a/src/libraries/LibMergeSort.sol b/src/libraries/LibMergeSort.sol new file mode 100644 index 00000000..012feed4 --- /dev/null +++ b/src/libraries/LibMergeSort.sol @@ -0,0 +1,61 @@ + +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.12; + +library LibMergeSort { + function sort(address[] memory array) internal pure returns (address[] memory) { + if (array.length <= 1) { + return array; + } + + uint256 mid = array.length / 2; + address[] memory left = new address[](mid); + address[] memory right = new address[](array.length - mid); + + for (uint256 i = 0; i < mid; i++) { + left[i] = array[i]; + } + for (uint256 i = mid; i < array.length; i++) { + right[i - mid] = array[i]; + } + + return mergeSortArrays(sort(left), sort(right)); + } + function mergeSortArrays(address[] memory left, address[] memory right) internal pure returns (address[] memory) { + uint256 leftLength = left.length; + uint256 rightLength = right.length; + address[] memory merged = new address[](leftLength + rightLength); + + uint256 i = 0; // Index for left array + uint256 j = 0; // Index for right array + uint256 k = 0; // Index for merged array + + // Merge the two arrays into the merged array + while (i < leftLength && j < rightLength) { + if (left[i] < right[j]) { + merged[k++] = left[i++]; + } else if (left[i] > right[j]) { + merged[k++] = right[j++]; + } else { + merged[k++] = left[i++]; + j++; + } + } + + // Copy remaining elements of left, if any + while (i < leftLength) { + merged[k++] = left[i++]; + } + + // Copy remaining elements of right, if any + while (j < rightLength) { + merged[k++] = right[j++]; + } + + // Resize the merged array to remove unused space + assembly { mstore(merged, k) } + + return merged; + } + +} \ No newline at end of file diff --git a/test/harnesses/AVSDirectoryHarness.sol b/test/harnesses/AVSDirectoryHarness.sol new file mode 100644 index 00000000..4f20a668 --- /dev/null +++ b/test/harnesses/AVSDirectoryHarness.sol @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.12; + +import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; +import {AVSDirectory} from "eigenlayer-contracts/src/contracts/core/AVSDirectory.sol"; + +// wrapper around the AVSDirectory contract that exposes internal functionality, for unit testing +contract AVSDirectoryHarness is AVSDirectory { + + constructor(IDelegationManager _delegation) AVSDirectory(_delegation) {} + + function setOperatorSaltIsSpent(address operator, bytes32 salt, bool isSpent) external { + operatorSaltIsSpent[operator][salt] = isSpent; + } + + function setAvsOperatorStatus(address avs, address operator, OperatorAVSRegistrationStatus status) external { + avsOperatorStatus[avs][operator] = status; + } + + function setIsOperatorSetAVS(address avs, bool isOperatorSet) external { + isOperatorSetAVS[avs] = isOperatorSet; + } + + function setIsOperatorSet(address avs, uint32 operatorSetId, bool isSet) external { + isOperatorSet[avs][operatorSetId] = isSet; + } + + function setIsMember(address avs, address operator, uint32 operatorSetId, bool membershipStatus) external { + isMember[avs][operator][operatorSetId] = membershipStatus; + } + + function _registerToOperatorSetsExternal(address avs, address operator, uint32[] calldata operatorSetIds) external { + _registerToOperatorSets(avs, operator, operatorSetIds); + } + + function _deregisterFromOperatorSetsExternal(address avs, address operator, uint32[] calldata operatorSetIds) external { + _deregisterFromOperatorSets(avs, operator, operatorSetIds); + } + + function _calculateDigestHashExternal(bytes32 structHash) external view returns (bytes32) { + return _calculateDigestHash(structHash); + } + + function _calculateDomainSeparatorExternal() external view returns (bytes32) { + return _calculateDomainSeparator(); + } +} + + diff --git a/test/unit/LibMergeSort.t.sol b/test/unit/LibMergeSort.t.sol new file mode 100644 index 00000000..f3014d3f --- /dev/null +++ b/test/unit/LibMergeSort.t.sol @@ -0,0 +1,188 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.12; + +import "forge-std/Test.sol"; +import "../../src/libraries/LibMergeSort.sol"; + +contract LibMergeSortTest is Test { + using LibMergeSort for address[]; + + function testMergeSortArrays() public { + address[] memory left = new address[](3); + address[] memory right = new address[](3); + + left[0] = address(0x1); + left[1] = address(0x3); + left[2] = address(0x5); + + right[0] = address(0x2); + right[1] = address(0x4); + right[2] = address(0x6); + + address[] memory expected = new address[](6); + expected[0] = address(0x1); + expected[1] = address(0x2); + expected[2] = address(0x3); + expected[3] = address(0x4); + expected[4] = address(0x5); + expected[5] = address(0x6); + + address[] memory result = left.mergeSortArrays(right); + + for (uint256 i = 0; i < expected.length; i++) { + assertEq(result[i], expected[i], "Array elements are not sorted correctly"); + } + } + + function testMergeSortArraysWithDuplicates() public { + address[] memory left = new address[](3); + address[] memory right = new address[](3); + + left[0] = address(0x1); + left[1] = address(0x3); + left[2] = address(0x5); + + right[0] = address(0x1); + right[1] = address(0x3); + right[2] = address(0x5); + + address[] memory expected = new address[](3); + expected[0] = address(0x1); + expected[1] = address(0x3); + expected[2] = address(0x5); + + address[] memory result = left.mergeSortArrays(right); + assertEq(expected, result, "Not sorted"); + } + + function testMergeSortArraysWithEmptyLeft() public { + address[] memory left = new address[](0); + address[] memory right = new address[](3); + + right[0] = address(0x2); + right[1] = address(0x4); + right[2] = address(0x6); + + address[] memory expected = new address[](3); + expected[0] = address(0x2); + expected[1] = address(0x4); + expected[2] = address(0x6); + + address[] memory result = left.mergeSortArrays(right); + + for (uint256 i = 0; i < expected.length; i++) { + assertEq(result[i], expected[i], "Array elements are not sorted correctly"); + } + } + + function testMergeSortArraysWithEmptyRight() public { + address[] memory left = new address[](3); + address[] memory right = new address[](0); + + left[0] = address(0x1); + left[1] = address(0x3); + left[2] = address(0x5); + + address[] memory expected = new address[](3); + expected[0] = address(0x1); + expected[1] = address(0x3); + expected[2] = address(0x5); + + address[] memory result = left.mergeSortArrays(right); + + for (uint256 i = 0; i < expected.length; i++) { + assertEq(result[i], expected[i], "Array elements are not sorted correctly"); + } + } + +function testMergeSortArrays_Sort() public { + address[] memory left = new address[](3); + address[] memory right = new address[](3); + + left[0] = address(0x3); + left[1] = address(0x1); + left[2] = address(0x2); + + right[0] = address(0x6); + right[1] = address(0x4); + right[2] = address(0x5); + + left = left.sort(); + right = right.sort(); + + address[] memory expected = new address[](6); + expected[0] = address(0x1); + expected[1] = address(0x2); + expected[2] = address(0x3); + expected[3] = address(0x4); + expected[4] = address(0x5); + expected[5] = address(0x6); + + address[] memory result = left.mergeSortArrays(right); + + for (uint256 i = 0; i < expected.length; i++) { + assertEq(result[i], expected[i], "Array elements are not sorted correctly"); + } +} + +/// NOTE: we're assuming the input arrays themselves are unique. +/// Demonstrating behavior of library +function testMergeSortArraysWithDuplicateInLeft() public { + address[] memory left = new address[](4); + address[] memory right = new address[](3); + + left[0] = address(0x1); + left[1] = address(0x3); + left[2] = address(0x3); // Duplicate + left[3] = address(0x5); + + right[0] = address(0x2); + right[1] = address(0x4); + right[2] = address(0x6); + + address[] memory expected = new address[](7); + expected[0] = address(0x1); + expected[1] = address(0x2); + expected[2] = address(0x3); + expected[3] = address(0x3); + expected[4] = address(0x4); + expected[5] = address(0x5); + expected[6] = address(0x6); + + address[] memory result = left.mergeSortArrays(right); + + for (uint256 i = 0; i < expected.length; i++) { + assertEq(result[i], expected[i], "Array elements are not sorted correctly"); + } +} +function testMergeSortArraysWithDuplicateInRight() public { + address[] memory left = new address[](3); + address[] memory right = new address[](4); + + left[0] = address(0x1); + left[1] = address(0x3); + left[2] = address(0x5); + + right[0] = address(0x2); + right[1] = address(0x4); + right[2] = address(0x4); // Duplicate + right[3] = address(0x6); + + address[] memory expected = new address[](7); + expected[0] = address(0x1); + expected[1] = address(0x2); + expected[2] = address(0x3); + expected[3] = address(0x4); + expected[4] = address(0x4); + expected[5] = address(0x5); + expected[6] = address(0x6); + + address[] memory result = left.mergeSortArrays(right); + + for (uint256 i = 0; i < expected.length; i++) { + assertEq(result[i], expected[i], "Array elements are not sorted correctly"); + } +} + + +} diff --git a/test/unit/ServiceManagerBase.t.sol b/test/unit/ServiceManagerBase.t.sol index 7673facd..eba791c3 100644 --- a/test/unit/ServiceManagerBase.t.sol +++ b/test/unit/ServiceManagerBase.t.sol @@ -530,4 +530,4 @@ contract ServiceManagerBase_UnitTests is MockAVSDeployer, IServiceManagerBaseEve cheats.prank(caller); serviceManager.setRewardsInitiator(newRewardsInitiator); } -} +} \ No newline at end of file diff --git a/test/unit/ServiceManagerMigration.t.sol b/test/unit/ServiceManagerMigration.t.sol new file mode 100644 index 00000000..91451458 --- /dev/null +++ b/test/unit/ServiceManagerMigration.t.sol @@ -0,0 +1,309 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.12; + +import "@openzeppelin/contracts/token/ERC20/presets/ERC20PresetFixedSupply.sol"; +import { + RewardsCoordinator, + IRewardsCoordinator, + IERC20 +} from "eigenlayer-contracts/src/contracts/core/RewardsCoordinator.sol"; +import {StrategyBase} from "eigenlayer-contracts/src/contracts/strategies/StrategyBase.sol"; +import {IServiceManagerBaseEvents} from "../events/IServiceManagerBaseEvents.sol"; +import {AVSDirectoryHarness} from "../harnesses/AVSDirectoryHarness.sol"; + +import "../utils/MockAVSDeployer.sol"; + +contract ServiceManagerMigration_UnitTests is MockAVSDeployer, IServiceManagerBaseEvents { + // RewardsCoordinator config + address rewardsUpdater = address(uint160(uint256(keccak256("rewardsUpdater")))); + uint32 CALCULATION_INTERVAL_SECONDS = 7 days; + uint32 MAX_REWARDS_DURATION = 70 days; + uint32 MAX_RETROACTIVE_LENGTH = 84 days; + uint32 MAX_FUTURE_LENGTH = 28 days; + uint32 GENESIS_REWARDS_TIMESTAMP = 1_712_188_800; + uint256 MAX_REWARDS_AMOUNT = 1e38 - 1; + uint32 OPERATOR_SET_GENESIS_REWARDS_TIMESTAMP = 0; /// TODO: what values should these have + uint32 OPERATOR_SET_MAX_RETROACTIVE_LENGTH = 0; /// TODO: What values these should have + /// @notice Delay in timestamp before a posted root can be claimed against + uint32 activationDelay = 7 days; + /// @notice the commission for all operators across all avss + uint16 globalCommissionBips = 1000; + + // Testing Config and Mocks + address serviceManagerOwner; + address rewardsInitiator = address(uint160(uint256(keccak256("rewardsInitiator")))); + IERC20[] rewardTokens; + uint256 mockTokenInitialSupply = 10e50; + IStrategy strategyMock1; + IStrategy strategyMock2; + IStrategy strategyMock3; + StrategyBase strategyImplementation; + IRewardsCoordinator.StrategyAndMultiplier[] defaultStrategyAndMultipliers; + AVSDirectoryHarness avsDirectoryHarness; + + // mapping to setting fuzzed inputs + mapping(address => bool) public addressIsExcludedFromFuzzedInputs; + + modifier filterFuzzedAddressInputs(address fuzzedAddress) { + cheats.assume(!addressIsExcludedFromFuzzedInputs[fuzzedAddress]); + _; + } + + function setUp() public virtual { + numQuorums = maxQuorumsToRegisterFor; + _deployMockEigenLayerAndAVS(); + + avsDirectoryHarness = new AVSDirectoryHarness(delegationMock); + // Deploy rewards coordinator + rewardsCoordinatorImplementation = new RewardsCoordinator( + delegationMock, + strategyManagerMock, + avsDirectoryMock, + CALCULATION_INTERVAL_SECONDS, + MAX_REWARDS_DURATION, + MAX_RETROACTIVE_LENGTH, + MAX_FUTURE_LENGTH, + GENESIS_REWARDS_TIMESTAMP, + OPERATOR_SET_GENESIS_REWARDS_TIMESTAMP, + OPERATOR_SET_MAX_RETROACTIVE_LENGTH + ); + + rewardsCoordinator = RewardsCoordinator( + address( + new TransparentUpgradeableProxy( + address(rewardsCoordinatorImplementation), + address(proxyAdmin), + abi.encodeWithSelector( + RewardsCoordinator.initialize.selector, + msg.sender, + pauserRegistry, + 0, /*initialPausedStatus*/ + rewardsUpdater, + activationDelay, + globalCommissionBips + ) + ) + ) + ); + // Deploy ServiceManager + serviceManagerImplementation = new ServiceManagerMock( + avsDirectory, + rewardsCoordinator, + registryCoordinator, + stakeRegistry + ); + + serviceManager = ServiceManagerMock( + address( + new TransparentUpgradeableProxy( + address(serviceManagerImplementation), + address(proxyAdmin), + abi.encodeWithSelector( + ServiceManagerMock.initialize.selector, msg.sender, msg.sender + ) + ) + ) + ); + + + serviceManagerOwner = serviceManager.owner(); + cheats.prank(serviceManagerOwner); + serviceManager.setRewardsInitiator(rewardsInitiator); + + _setUpDefaultStrategiesAndMultipliers(); + + cheats.warp(GENESIS_REWARDS_TIMESTAMP + 2 weeks); + + addressIsExcludedFromFuzzedInputs[address(pauserRegistry)] = true; + addressIsExcludedFromFuzzedInputs[address(proxyAdmin)] = true; + } + + function _setUpDefaultStrategiesAndMultipliers() internal virtual { + // Deploy Mock Strategies + IERC20 token1 = new ERC20PresetFixedSupply( + "dog wif hat", "MOCK1", mockTokenInitialSupply, address(this) + ); + IERC20 token2 = + new ERC20PresetFixedSupply("jeo boden", "MOCK2", mockTokenInitialSupply, address(this)); + IERC20 token3 = new ERC20PresetFixedSupply( + "pepe wif avs", "MOCK3", mockTokenInitialSupply, address(this) + ); + strategyImplementation = new StrategyBase(strategyManagerMock); + strategyMock1 = StrategyBase( + address( + new TransparentUpgradeableProxy( + address(strategyImplementation), + address(proxyAdmin), + abi.encodeWithSelector(StrategyBase.initialize.selector, token1, pauserRegistry) + ) + ) + ); + strategyMock2 = StrategyBase( + address( + new TransparentUpgradeableProxy( + address(strategyImplementation), + address(proxyAdmin), + abi.encodeWithSelector(StrategyBase.initialize.selector, token2, pauserRegistry) + ) + ) + ); + strategyMock3 = StrategyBase( + address( + new TransparentUpgradeableProxy( + address(strategyImplementation), + address(proxyAdmin), + abi.encodeWithSelector(StrategyBase.initialize.selector, token3, pauserRegistry) + ) + ) + ); + IStrategy[] memory strategies = new IStrategy[](3); + strategies[0] = strategyMock1; + strategies[1] = strategyMock2; + strategies[2] = strategyMock3; + strategies = _sortArrayAsc(strategies); + + strategyManagerMock.setStrategyWhitelist(strategies[0], true); + strategyManagerMock.setStrategyWhitelist(strategies[1], true); + strategyManagerMock.setStrategyWhitelist(strategies[2], true); + + defaultStrategyAndMultipliers.push( + IRewardsCoordinator.StrategyAndMultiplier(IStrategy(address(strategies[0])), 1e18) + ); + defaultStrategyAndMultipliers.push( + IRewardsCoordinator.StrategyAndMultiplier(IStrategy(address(strategies[1])), 2e18) + ); + defaultStrategyAndMultipliers.push( + IRewardsCoordinator.StrategyAndMultiplier(IStrategy(address(strategies[2])), 3e18) + ); + } + + /// @dev Sort to ensure that the array is in ascending order for strategies + function _sortArrayAsc(IStrategy[] memory arr) internal pure returns (IStrategy[] memory) { + uint256 l = arr.length; + for (uint256 i = 0; i < l; i++) { + for (uint256 j = i + 1; j < l; j++) { + if (address(arr[i]) > address(arr[j])) { + IStrategy temp = arr[i]; + arr[i] = arr[j]; + arr[j] = temp; + } + } + } + return arr; + } + + function test_viewFunction(uint256 randomValue) public { + _registerRandomOperators(randomValue); + (uint32[] memory operatorSetsToCreate, uint32[][] memory operatorSetIdsToMigrate, address[] memory operators) = serviceManager.getOperatorsToMigrate(); + + // Assert that all operators are in quorum 0 invariant of _registerRandomOperators + for (uint256 i = 0; i < operators.length; i++) { + bytes32 operatorId = registryCoordinator.getOperatorId(operators[i]); + uint192 operatorBitmap = registryCoordinator.getCurrentQuorumBitmap(operatorId); + assertTrue(operatorId != bytes32(0), "Operator was registered"); + assertTrue(operatorBitmap & 1 == 1, "Operator is not registered in quorum 0"); + } + + // Assert we are migrating all the quorums that existed + uint256 quorumCount = registryCoordinator.quorumCount(); + assertEq(quorumCount, operatorSetsToCreate.length, "Operator sets to create incorrect"); + } + + function test_migrateToOperatorSets() public { + (uint32[] memory operatorSetsToCreate, uint32[][] memory operatorSetIdsToMigrate, address[] memory operators) = serviceManager.getOperatorsToMigrate(); + cheats.startPrank(serviceManagerOwner); + serviceManager.migrateAndCreateOperatorSetIds(operatorSetsToCreate); + serviceManager.migrateToOperatorSets(operatorSetIdsToMigrate, operators); + cheats.stopPrank(); + + assertTrue(avsDirectory.isOperatorSetAVS(address(serviceManager)), "AVS is not an operator set AVS"); + } + + function test_migrateTwoTransactions() public { + (uint32[] memory operatorSetsToCreate, uint32[][] memory operatorSetIdsToMigrate, address[] memory operators) = serviceManager.getOperatorsToMigrate(); + // Split the operatorSetIdsToMigrate and operators into two separate sets + uint256 halfLength = operatorSetIdsToMigrate.length / 2; + + uint32[][] memory firstHalfOperatorSetIds = new uint32[][](halfLength); + uint32[][] memory secondHalfOperatorSetIds = new uint32[][](operatorSetIdsToMigrate.length - halfLength); + address[] memory firstHalfOperators = new address[](halfLength); + address[] memory secondHalfOperators = new address[](operators.length - halfLength); + + for (uint256 i = 0; i < halfLength; i++) { + firstHalfOperatorSetIds[i] = operatorSetIdsToMigrate[i]; + firstHalfOperators[i] = operators[i]; + } + + for (uint256 i = halfLength; i < operatorSetIdsToMigrate.length; i++) { + secondHalfOperatorSetIds[i - halfLength] = operatorSetIdsToMigrate[i]; + secondHalfOperators[i - halfLength] = operators[i]; + } + + // Migrate the first half + cheats.startPrank(serviceManagerOwner); + serviceManager.migrateAndCreateOperatorSetIds(operatorSetsToCreate); + serviceManager.migrateToOperatorSets(firstHalfOperatorSetIds, firstHalfOperators); + serviceManager.migrateToOperatorSets(secondHalfOperatorSetIds, secondHalfOperators); + cheats.stopPrank(); + + assertTrue(avsDirectory.isOperatorSetAVS(address(serviceManager)), "AVS is not an operator set AVS"); + } + + function test_migrateToOperatorSets_revert_alreadyMigrated() public { + (uint32[] memory operatorSetsToCreate, uint32[][] memory operatorSetIdsToMigrate, address[] memory operators) = serviceManager.getOperatorsToMigrate(); + cheats.startPrank(serviceManagerOwner); + serviceManager.migrateAndCreateOperatorSetIds(operatorSetsToCreate); + serviceManager.migrateToOperatorSets(operatorSetIdsToMigrate, operators); + serviceManager.finalizeMigration(); + + vm.expectRevert(); /// TODO: Now that it's not 1 step, we should have a way to signal completion + serviceManager.migrateToOperatorSets(operatorSetIdsToMigrate, operators); + + cheats.stopPrank(); + } + + function test_migrateToOperatorSets_revert_notOwner() public { + (uint32[] memory operatorSetsToCreate, uint32[][] memory operatorSetIdsToMigrate, address[] memory operators) = serviceManager.getOperatorsToMigrate(); + cheats.startPrank(serviceManagerOwner); + serviceManager.migrateAndCreateOperatorSetIds(operatorSetsToCreate); + cheats.stopPrank(); + address caller = address(uint160(uint256(keccak256("caller")))); + cheats.expectRevert("Ownable: caller is not the owner"); + cheats.prank(caller); + serviceManager.migrateToOperatorSets(operatorSetIdsToMigrate, operators); + } + + function test_migrateToOperatorSets_verify() public { + uint256 pseudoRandomNumber = uint256(keccak256("pseudoRandomNumber")); + _registerRandomOperators(pseudoRandomNumber); + + vm.prank(proxyAdmin.owner()); + proxyAdmin.upgrade( + TransparentUpgradeableProxy(payable(address(avsDirectory))), + address(avsDirectoryHarness) + ); + + uint256 quorumCount = registryCoordinator.quorumCount(); + for (uint256 i = 0; i < quorumCount; i++) { + uint256 operatorCount = indexRegistry.totalOperatorsForQuorum(uint8(i)); + bytes32[] memory operatorIds = indexRegistry.getOperatorListAtBlockNumber(uint8(i), uint32(block.number)); + assertEq(operatorCount, operatorIds.length, "Operator Id length mismatch");// sanity check + for (uint256 j = 0; j < operatorCount; j++) { + address operatorAddress = registryCoordinator.blsApkRegistry().getOperatorFromPubkeyHash(operatorIds[j]); + AVSDirectoryHarness(address(avsDirectory)).setAvsOperatorStatus(address(serviceManager), operatorAddress, IAVSDirectory.OperatorAVSRegistrationStatus.REGISTERED); + } + } + + (uint32[] memory operatorSetsToCreate, uint32[][] memory operatorSetIdsToMigrate, address[] memory operators) = serviceManager.getOperatorsToMigrate(); + cheats.startPrank(serviceManagerOwner); + serviceManager.migrateAndCreateOperatorSetIds(operatorSetsToCreate); + serviceManager.migrateToOperatorSets(operatorSetIdsToMigrate, operators); + cheats.stopPrank(); + + /// quick check, this operator is in operator set 3 + assertTrue( + avsDirectory.isMember(address(serviceManager), 0x73e2Ce949f15Be901f76b54F5a4554A6C8DCf539, uint32(3)), + "Operator not migrated to operator set" + ); + } +} From fe93ac6c8bb7b1186c3d84d6db3eb96915fc67f1 Mon Sep 17 00:00:00 2001 From: steven <12021290+stevennevins@users.noreply.github.com> Date: Mon, 12 Aug 2024 09:57:22 -0400 Subject: [PATCH 07/27] feat: operator set migration-2-create quorum (#287) * chore: checkout migration branch * feat: implement migration function * chore: update to release branch * feat: operator set creation for each quorum number * feat: migration with merge sorted array of operators and their quorums * feat: operator set migration working * chore: revert change from testing * chore: revert change from testing * chore: remove extra logging and commented out asserts * chore: remove unused file * fix: remove console logs * feat: create quorum post operator set migration * test(wip): create quorum test adds new operator set * test: migration create quorum * refactor: to view functions * chore: nit and remove unneeded function * fix: remove duplication of looping for all the operators * chore: remove comment * feat: allow migrating in two transactions * feat: finalization of migration * chore: use string errors and fix migration issues * chore: rename * feat: use library for merge sort * test: fuzz view function * fix: updates from merge * chore: use interface --- src/RegistryCoordinator.sol | 9 + src/ServiceManagerBase.sol | 8 +- src/interfaces/IServiceManager.sol | 2 + test/mocks/ECDSAServiceManagerMock.sol | 9 +- test/unit/RegistryCoordinatorMigration.t.sol | 200 +++++++++++++++++++ 5 files changed, 224 insertions(+), 4 deletions(-) create mode 100644 test/unit/RegistryCoordinatorMigration.t.sol diff --git a/src/RegistryCoordinator.sol b/src/RegistryCoordinator.sol index 6d2ff3f4..86266f5d 100644 --- a/src/RegistryCoordinator.sol +++ b/src/RegistryCoordinator.sol @@ -3,6 +3,7 @@ pragma solidity ^0.8.12; import {IPauserRegistry} from "eigenlayer-contracts/src/contracts/interfaces/IPauserRegistry.sol"; import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; +import {AVSDirectory} from "eigenlayer-contracts/src/contracts/core/AVSDirectory.sol"; import {ISocketUpdater} from "./interfaces/ISocketUpdater.sol"; import {IBLSApkRegistry} from "./interfaces/IBLSApkRegistry.sol"; import {IStakeRegistry} from "./interfaces/IStakeRegistry.sol"; @@ -732,6 +733,14 @@ contract RegistryCoordinator is stakeRegistry.initializeQuorum(quorumNumber, minimumStake, strategyParams); indexRegistry.initializeQuorum(quorumNumber); blsApkRegistry.initializeQuorum(quorumNumber); + // Check if the AVS has migrated to operator sets + AVSDirectory avsDirectory = AVSDirectory(serviceManager.avsDirectory()); + if (avsDirectory.isOperatorSetAVS(address(serviceManager))){ + // Create an operator set for the new quorum + uint32[] memory operatorSetIds = new uint32[](1); + operatorSetIds[0] = uint32(quorumNumber); + serviceManager.createOperatorSets(operatorSetIds); + } } /** diff --git a/src/ServiceManagerBase.sol b/src/ServiceManagerBase.sol index ab1a47d8..ac12619c 100644 --- a/src/ServiceManagerBase.sol +++ b/src/ServiceManagerBase.sol @@ -4,7 +4,6 @@ pragma solidity ^0.8.12; import {Initializable} from "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol"; import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; import {IAVSDirectory} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; -import {AVSDirectory} from "eigenlayer-contracts/src/contracts/core/AVSDirectory.sol"; import {IRewardsCoordinator} from "eigenlayer-contracts/src/contracts/interfaces/IRewardsCoordinator.sol"; import {ServiceManagerBaseStorage} from "./ServiceManagerBaseStorage.sol"; @@ -108,6 +107,9 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage { _rewardsCoordinator.createAVSRewardsSubmission(rewardsSubmissions); } + function createOperatorSets(uint32[] memory operatorSetIds) external onlyRegistryCoordinator{ + _avsDirectory.createOperatorSets(operatorSetIds); + } /** * @notice Forwards a call to EigenLayer's AVSDirectory contract to confirm operator registration with the AVS @@ -154,11 +156,11 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage { function _migrateAndCreateOperatorSetIds(uint32[] memory operatorSetIdsToCreate) internal { _avsDirectory.becomeOperatorSetAVS(); - AVSDirectory(address(_avsDirectory)).createOperatorSets(operatorSetIdsToCreate); + IAVSDirectory(address(_avsDirectory)).createOperatorSets(operatorSetIdsToCreate); } function _migrateToOperatorSets(uint32[][] memory operatorSetIds, address[] memory operators) internal { - AVSDirectory(address(_avsDirectory)).migrateOperatorsToOperatorSets(operators, operatorSetIds); + IAVSDirectory(address(_avsDirectory)).migrateOperatorsToOperatorSets(operators, operatorSetIds); } function getOperatorsToMigrate() public view returns (uint32[] memory operatorSetIdsToCreate, uint32[][] memory operatorSetIds, address[] memory allOperators) { diff --git a/src/interfaces/IServiceManager.sol b/src/interfaces/IServiceManager.sol index ad953ec0..26f2c71b 100644 --- a/src/interfaces/IServiceManager.sol +++ b/src/interfaces/IServiceManager.sol @@ -22,6 +22,8 @@ interface IServiceManager is IServiceManagerUI { */ function createAVSRewardsSubmission(IRewardsCoordinator.RewardsSubmission[] calldata rewardsSubmissions) external; + function createOperatorSets(uint32[] memory operatorSetIds) external ; + // EVENTS event RewardsInitiatorUpdated(address prevRewardsInitiator, address newRewardsInitiator); } diff --git a/test/mocks/ECDSAServiceManagerMock.sol b/test/mocks/ECDSAServiceManagerMock.sol index 528270ae..e38e0395 100644 --- a/test/mocks/ECDSAServiceManagerMock.sol +++ b/test/mocks/ECDSAServiceManagerMock.sol @@ -10,7 +10,12 @@ contract ECDSAServiceManagerMock is ECDSAServiceManagerBase { address _rewardsCoordinator, address _delegationManager ) - ECDSAServiceManagerBase(_avsDirectory, _stakeRegistry, _rewardsCoordinator, _delegationManager) + ECDSAServiceManagerBase( + _avsDirectory, + _stakeRegistry, + _rewardsCoordinator, + _delegationManager + ) {} function initialize( @@ -19,4 +24,6 @@ contract ECDSAServiceManagerMock is ECDSAServiceManagerBase { ) public virtual initializer { __ServiceManagerBase_init(initialOwner, rewardsInitiator); } + + function createOperatorSets(uint32[] memory) external {} } diff --git a/test/unit/RegistryCoordinatorMigration.t.sol b/test/unit/RegistryCoordinatorMigration.t.sol new file mode 100644 index 00000000..0eb80273 --- /dev/null +++ b/test/unit/RegistryCoordinatorMigration.t.sol @@ -0,0 +1,200 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.12; + +import "@openzeppelin/contracts/token/ERC20/presets/ERC20PresetFixedSupply.sol"; +import { + RewardsCoordinator, + IRewardsCoordinator, + IERC20 +} from "eigenlayer-contracts/src/contracts/core/RewardsCoordinator.sol"; +import {StrategyBase} from "eigenlayer-contracts/src/contracts/strategies/StrategyBase.sol"; +import {IServiceManagerBaseEvents} from "../events/IServiceManagerBaseEvents.sol"; +import {AVSDirectoryHarness} from "../harnesses/AVSDirectoryHarness.sol"; + +import "../utils/MockAVSDeployer.sol"; + +contract RegistryCoordinatorMigrationUnit is MockAVSDeployer, IServiceManagerBaseEvents { + // RewardsCoordinator config + address rewardsUpdater = address(uint160(uint256(keccak256("rewardsUpdater")))); + uint32 CALCULATION_INTERVAL_SECONDS = 7 days; + uint32 MAX_REWARDS_DURATION = 70 days; + uint32 MAX_RETROACTIVE_LENGTH = 84 days; + uint32 MAX_FUTURE_LENGTH = 28 days; + uint32 GENESIS_REWARDS_TIMESTAMP = 1_712_188_800; + uint256 MAX_REWARDS_AMOUNT = 1e38 - 1; + /// @notice Delay in timestamp before a posted root can be claimed against + uint32 activationDelay = 7 days; + /// @notice the commission for all operators across all avss + uint16 globalCommissionBips = 1000; + + // Testing Config and Mocks + address serviceManagerOwner; + IERC20[] rewardTokens; + uint256 mockTokenInitialSupply = 10e50; + IStrategy strategyMock1; + IStrategy strategyMock2; + IStrategy strategyMock3; + StrategyBase strategyImplementation; + IRewardsCoordinator.StrategyAndMultiplier[] defaultStrategyAndMultipliers; + AVSDirectoryHarness avsDirectoryHarness; + + // mapping to setting fuzzed inputs + mapping(address => bool) public addressIsExcludedFromFuzzedInputs; + + modifier filterFuzzedAddressInputs(address fuzzedAddress) { + cheats.assume(!addressIsExcludedFromFuzzedInputs[fuzzedAddress]); + _; + } + + function setUp() public virtual { + numQuorums = maxQuorumsToRegisterFor; + _deployMockEigenLayerAndAVS(); + + serviceManagerImplementation = new ServiceManagerMock( + avsDirectory, + IRewardsCoordinator(address(rewardsCoordinatorMock)), + registryCoordinator, + stakeRegistry + ); + avsDirectoryHarness = new AVSDirectoryHarness(delegationMock); + + serviceManagerImplementation = new ServiceManagerMock( + avsDirectory, + rewardsCoordinatorMock, + registryCoordinator, + stakeRegistry + ); + /// Needed to upgrade to a service manager that points to an AVS Directory that can track state + vm.prank(proxyAdmin.owner()); + proxyAdmin.upgrade( + TransparentUpgradeableProxy(payable(address(serviceManager))), + address(serviceManagerImplementation) + ); + + serviceManagerOwner = serviceManager.owner(); + + _setUpDefaultStrategiesAndMultipliers(); + + addressIsExcludedFromFuzzedInputs[address(pauserRegistry)] = true; + addressIsExcludedFromFuzzedInputs[address(proxyAdmin)] = true; + } + + function _setUpDefaultStrategiesAndMultipliers() internal virtual { + // Deploy Mock Strategies + IERC20 token1 = new ERC20PresetFixedSupply( + "dog wif hat", "MOCK1", mockTokenInitialSupply, address(this) + ); + IERC20 token2 = + new ERC20PresetFixedSupply("jeo boden", "MOCK2", mockTokenInitialSupply, address(this)); + IERC20 token3 = new ERC20PresetFixedSupply( + "pepe wif avs", "MOCK3", mockTokenInitialSupply, address(this) + ); + strategyImplementation = new StrategyBase(strategyManagerMock); + strategyMock1 = StrategyBase( + address( + new TransparentUpgradeableProxy( + address(strategyImplementation), + address(proxyAdmin), + abi.encodeWithSelector(StrategyBase.initialize.selector, token1, pauserRegistry) + ) + ) + ); + strategyMock2 = StrategyBase( + address( + new TransparentUpgradeableProxy( + address(strategyImplementation), + address(proxyAdmin), + abi.encodeWithSelector(StrategyBase.initialize.selector, token2, pauserRegistry) + ) + ) + ); + strategyMock3 = StrategyBase( + address( + new TransparentUpgradeableProxy( + address(strategyImplementation), + address(proxyAdmin), + abi.encodeWithSelector(StrategyBase.initialize.selector, token3, pauserRegistry) + ) + ) + ); + IStrategy[] memory strategies = new IStrategy[](3); + strategies[0] = strategyMock1; + strategies[1] = strategyMock2; + strategies[2] = strategyMock3; + strategies = _sortArrayAsc(strategies); + + strategyManagerMock.setStrategyWhitelist(strategies[0], true); + strategyManagerMock.setStrategyWhitelist(strategies[1], true); + strategyManagerMock.setStrategyWhitelist(strategies[2], true); + + defaultStrategyAndMultipliers.push( + IRewardsCoordinator.StrategyAndMultiplier(IStrategy(address(strategies[0])), 1e18) + ); + defaultStrategyAndMultipliers.push( + IRewardsCoordinator.StrategyAndMultiplier(IStrategy(address(strategies[1])), 2e18) + ); + defaultStrategyAndMultipliers.push( + IRewardsCoordinator.StrategyAndMultiplier(IStrategy(address(strategies[2])), 3e18) + ); + } + + /// @dev Sort to ensure that the array is in ascending order for strategies + function _sortArrayAsc(IStrategy[] memory arr) internal pure returns (IStrategy[] memory) { + uint256 l = arr.length; + for (uint256 i = 0; i < l; i++) { + for (uint256 j = i + 1; j < l; j++) { + if (address(arr[i]) > address(arr[j])) { + IStrategy temp = arr[i]; + arr[i] = arr[j]; + arr[j] = temp; + } + } + } + return arr; + } + + function test_migrateToOperatorSets() public { + (uint32[] memory operatorSetsToCreate, uint32[][] memory operatorSetIdsToMigrate, address[] memory operators) = serviceManager.getOperatorsToMigrate(); + cheats.startPrank(serviceManagerOwner); + serviceManager.migrateAndCreateOperatorSetIds(operatorSetsToCreate); + serviceManager.migrateToOperatorSets(operatorSetIdsToMigrate, operators); + cheats.stopPrank(); + + assertTrue(avsDirectory.isOperatorSetAVS(address(serviceManager)), "AVS is not an operator set AVS"); + } + + + + function test_createQuorum() public { + (uint32[] memory operatorSetsToCreate, uint32[][] memory operatorSetIdsToMigrate, address[] memory operators) = serviceManager.getOperatorsToMigrate(); + cheats.startPrank(serviceManagerOwner); + serviceManager.migrateAndCreateOperatorSetIds(operatorSetsToCreate); + serviceManager.migrateToOperatorSets(operatorSetIdsToMigrate, operators); + cheats.stopPrank(); + + assertTrue(avsDirectory.isOperatorSetAVS(address(serviceManager)), "AVS is not an operator set AVS"); + + uint8 quorumNumber = registryCoordinator.quorumCount(); + uint96 minimumStake = 1000; + IRegistryCoordinator.OperatorSetParam memory operatorSetParams = IRegistryCoordinator.OperatorSetParam({ + maxOperatorCount: 10, + kickBIPsOfOperatorStake: 50, + kickBIPsOfTotalStake: 2 + }); + IStakeRegistry.StrategyParams[] memory strategyParams = new IStakeRegistry.StrategyParams[](1); + strategyParams[0] = + IStakeRegistry.StrategyParams({ + strategy: IStrategy(address(1000)), + multiplier: 1e16 + }); + + assertFalse(avsDirectory.isOperatorSet(address(serviceManager), quorumNumber), "Operator set already existed"); + assertTrue(avsDirectory.isOperatorSet(address(serviceManager), quorumNumber-1), "Operator set doesn't already existed"); + + vm.prank(registryCoordinator.owner()); + registryCoordinator.createQuorum(operatorSetParams, minimumStake, strategyParams); + + assertTrue(avsDirectory.isOperatorSet(address(serviceManager), quorumNumber), "Operator set was not created for the quorum"); + + } +} From e5b26885f8f388dd20132beb06af66bc88ce21a9 Mon Sep 17 00:00:00 2001 From: steven Date: Thu, 15 Aug 2024 08:45:52 -0400 Subject: [PATCH 08/27] chore: bump operator set release dependency in core --- lib/eigenlayer-contracts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/eigenlayer-contracts b/lib/eigenlayer-contracts index ed44903c..f2a7515a 160000 --- a/lib/eigenlayer-contracts +++ b/lib/eigenlayer-contracts @@ -1 +1 @@ -Subproject commit ed44903ca218a52cf259d226b5a60bee8a8320a7 +Subproject commit f2a7515a43162adb5e1b778308349043f60db6a8 From ec2fdf31b2653556c3375c2e915d652aa256b421 Mon Sep 17 00:00:00 2001 From: steven Date: Thu, 15 Aug 2024 08:46:02 -0400 Subject: [PATCH 09/27] chore: updates from dependency bump --- test/harnesses/AVSDirectoryHarness.sol | 37 ++++++-- test/integration/IntegrationDeployer.t.sol | 44 +++++----- .../mocks/BeaconChainOracleMock.t.sol | 7 +- test/mocks/AVSDirectoryMock.sol | 27 ++++-- test/unit/ServiceManagerMigration.t.sol | 84 ++++++++++++++----- test/utils/MockAVSDeployer.sol | 7 +- 6 files changed, 140 insertions(+), 66 deletions(-) diff --git a/test/harnesses/AVSDirectoryHarness.sol b/test/harnesses/AVSDirectoryHarness.sol index 4f20a668..99efedec 100644 --- a/test/harnesses/AVSDirectoryHarness.sol +++ b/test/harnesses/AVSDirectoryHarness.sol @@ -1,19 +1,23 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.12; -import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; +import {IDelegationManager} from + "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; import {AVSDirectory} from "eigenlayer-contracts/src/contracts/core/AVSDirectory.sol"; // wrapper around the AVSDirectory contract that exposes internal functionality, for unit testing contract AVSDirectoryHarness is AVSDirectory { - constructor(IDelegationManager _delegation) AVSDirectory(_delegation) {} function setOperatorSaltIsSpent(address operator, bytes32 salt, bool isSpent) external { operatorSaltIsSpent[operator][salt] = isSpent; } - function setAvsOperatorStatus(address avs, address operator, OperatorAVSRegistrationStatus status) external { + function setAvsOperatorStatus( + address avs, + address operator, + OperatorAVSRegistrationStatus status + ) external { avsOperatorStatus[avs][operator] = status; } @@ -25,15 +29,32 @@ contract AVSDirectoryHarness is AVSDirectory { isOperatorSet[avs][operatorSetId] = isSet; } - function setIsMember(address avs, address operator, uint32 operatorSetId, bool membershipStatus) external { - isMember[avs][operator][operatorSetId] = membershipStatus; + function setIsMember( + address avs, + address operator, + uint32[] calldata operatorSetIds, + bool membershipStatus + ) external { + if (membershipStatus) { + _registerToOperatorSets(avs, operator, operatorSetIds); + } else { + _deregisterFromOperatorSets(avs, operator, operatorSetIds); + } } - function _registerToOperatorSetsExternal(address avs, address operator, uint32[] calldata operatorSetIds) external { + function _registerToOperatorSetsExternal( + address avs, + address operator, + uint32[] calldata operatorSetIds + ) external { _registerToOperatorSets(avs, operator, operatorSetIds); } - function _deregisterFromOperatorSetsExternal(address avs, address operator, uint32[] calldata operatorSetIds) external { + function _deregisterFromOperatorSetsExternal( + address avs, + address operator, + uint32[] calldata operatorSetIds + ) external { _deregisterFromOperatorSets(avs, operator, operatorSetIds); } @@ -45,5 +66,3 @@ contract AVSDirectoryHarness is AVSDirectory { return _calculateDomainSeparator(); } } - - diff --git a/test/integration/IntegrationDeployer.t.sol b/test/integration/IntegrationDeployer.t.sol index ec9075e9..51525a8c 100644 --- a/test/integration/IntegrationDeployer.t.sol +++ b/test/integration/IntegrationDeployer.t.sol @@ -20,7 +20,7 @@ import "eigenlayer-contracts/src/contracts/core/RewardsCoordinator.sol"; import "eigenlayer-contracts/src/contracts/strategies/StrategyBase.sol"; import "eigenlayer-contracts/src/contracts/pods/EigenPodManager.sol"; import "eigenlayer-contracts/src/contracts/pods/EigenPod.sol"; -import "eigenlayer-contracts/src/contracts/pods/DelayedWithdrawalRouter.sol"; +// import "eigenlayer-contracts/src/contracts/pods/DelayedWithdrawalRouter.sol"; import "eigenlayer-contracts/src/contracts/permissions/PauserRegistry.sol"; import "eigenlayer-contracts/src/test/mocks/ETHDepositMock.sol"; // import "eigenlayer-contracts/src/test/integration/mocks/BeaconChainOracleMock.t.sol"; @@ -57,7 +57,7 @@ abstract contract IntegrationDeployer is Test, IUserDeployer { Slasher slasher; IBeacon eigenPodBeacon; EigenPod pod; - DelayedWithdrawalRouter delayedWithdrawalRouter; + // DelayedWithdrawalRouter delayedWithdrawalRouter; ETHPOSDepositMock ethPOSDeposit; BeaconChainOracleMock beaconChainOracle; @@ -147,11 +147,11 @@ abstract contract IntegrationDeployer is Test, IUserDeployer { new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") ) ); - delayedWithdrawalRouter = DelayedWithdrawalRouter( - address( - new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") - ) - ); + // delayedWithdrawalRouter = DelayedWithdrawalRouter( + // address( + // new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") + // ) + // ); avsDirectory = AVSDirectory( address( new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") @@ -164,9 +164,9 @@ abstract contract IntegrationDeployer is Test, IUserDeployer { // Deploy EigenPod Contracts pod = new EigenPod( ethPOSDeposit, - delayedWithdrawalRouter, + // delayedWithdrawalRouter, eigenPodManager, - MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR, + // MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR, 0 ); @@ -181,8 +181,8 @@ abstract contract IntegrationDeployer is Test, IUserDeployer { EigenPodManager eigenPodManagerImplementation = new EigenPodManager( ethPOSDeposit, eigenPodBeacon, strategyManager, slasher, delegationManager ); - DelayedWithdrawalRouter delayedWithdrawalRouterImplementation = - new DelayedWithdrawalRouter(eigenPodManager); + // DelayedWithdrawalRouter delayedWithdrawalRouterImplementation = + // new DelayedWithdrawalRouter(eigenPodManager); AVSDirectory avsDirectoryImplemntation = new AVSDirectory(delegationManager); // RewardsCoordinator rewardsCoordinatorImplementation = new RewardsCoordinator( // delegationManager, @@ -247,17 +247,17 @@ abstract contract IntegrationDeployer is Test, IUserDeployer { ) ); // Delayed Withdrawal Router - proxyAdmin.upgradeAndCall( - TransparentUpgradeableProxy(payable(address(delayedWithdrawalRouter))), - address(delayedWithdrawalRouterImplementation), - abi.encodeWithSelector( - DelayedWithdrawalRouter.initialize.selector, - eigenLayerReputedMultisig, // initialOwner - pauserRegistry, - 0, // initialPausedStatus - minWithdrawalDelayBlocks - ) - ); + // proxyAdmin.upgradeAndCall( + // TransparentUpgradeableProxy(payable(address(delayedWithdrawalRouter))), + // address(delayedWithdrawalRouterImplementation), + // abi.encodeWithSelector( + // DelayedWithdrawalRouter.initialize.selector, + // eigenLayerReputedMultisig, // initialOwner + // pauserRegistry, + // 0, // initialPausedStatus + // minWithdrawalDelayBlocks + // ) + // ); // AVSDirectory proxyAdmin.upgradeAndCall( TransparentUpgradeableProxy(payable(address(avsDirectory))), diff --git a/test/integration/mocks/BeaconChainOracleMock.t.sol b/test/integration/mocks/BeaconChainOracleMock.t.sol index dabd6b6a..6dd1756a 100644 --- a/test/integration/mocks/BeaconChainOracleMock.t.sol +++ b/test/integration/mocks/BeaconChainOracleMock.t.sol @@ -1,16 +1,17 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.12; -import "eigenlayer-contracts/src/contracts/interfaces/IBeaconChainOracle.sol"; +// import "eigenlayer-contracts/src/contracts/interfaces/IBeaconChainOracle.sol"; // NOTE: There's a copy of this file in the core repo, but importing that was causing // the compiler to complain for an unfathomable reason. Apparently reimplementing it // here fixes the issue. -contract BeaconChainOracleMock is IBeaconChainOracle { +contract BeaconChainOracleMock { + // contract BeaconChainOracleMock is IBeaconChainOracle { mapping(uint64 => bytes32) blockRoots; - function timestampToBlockRoot(uint timestamp) public view returns (bytes32) { + function timestampToBlockRoot(uint256 timestamp) public view returns (bytes32) { return blockRoots[uint64(timestamp)]; } diff --git a/test/mocks/AVSDirectoryMock.sol b/test/mocks/AVSDirectoryMock.sol index fdb02613..46bfb5db 100644 --- a/test/mocks/AVSDirectoryMock.sol +++ b/test/mocks/AVSDirectoryMock.sol @@ -1,7 +1,10 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.12; -import {IAVSDirectory, ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; +import { + IAVSDirectory, + ISignatureUtils +} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; contract AVSDirectoryMock is IAVSDirectory { /** @@ -61,7 +64,10 @@ contract AVSDirectoryMock is IAVSDirectory { * * @dev msg.sender is used as the AVS. */ - function deregisterOperatorFromOperatorSets(address operator, uint32[] calldata operatorSetIds) external {} + function deregisterOperatorFromOperatorSets( + address operator, + uint32[] calldata operatorSetIds + ) external {} /** * @notice Called by an operator to deregister from an operator set @@ -109,7 +115,7 @@ contract AVSDirectoryMock is IAVSDirectory { * * @param salt A unique and single use value associated with the approver signature. */ - function cancelSalt(bytes32 salt) external{} + function cancelSalt(bytes32 salt) external {} /** * @notice Returns whether or not the salt has already been used by the operator. @@ -117,7 +123,11 @@ contract AVSDirectoryMock is IAVSDirectory { */ function operatorSaltIsSpent(address operator, bytes32 salt) external view returns (bool) {} - function isMember(address avs, address operator, uint32 operatorSetId) external view returns (bool){} + function isMember( + address avs, + address operator, + uint32 operatorSetId + ) external view returns (bool) {} /** * @notice Calculates the digest hash to be signed by an operator to register with an AVS @@ -175,5 +185,10 @@ contract AVSDirectoryMock is IAVSDirectory { function isOperatorSetAVS(address avs) external view returns (bool) {} - function isOperatorSet(address avs, uint32 operatorSetId) external view returns (bool){} -} + function isOperatorSet(address avs, uint32 operatorSetId) external view returns (bool) {} + + function isMember( + address operator, + OperatorSet memory operatorSet + ) external view returns (bool) {} +} diff --git a/test/unit/ServiceManagerMigration.t.sol b/test/unit/ServiceManagerMigration.t.sol index 91451458..69cd4c7c 100644 --- a/test/unit/ServiceManagerMigration.t.sol +++ b/test/unit/ServiceManagerMigration.t.sol @@ -22,8 +22,10 @@ contract ServiceManagerMigration_UnitTests is MockAVSDeployer, IServiceManagerBa uint32 MAX_FUTURE_LENGTH = 28 days; uint32 GENESIS_REWARDS_TIMESTAMP = 1_712_188_800; uint256 MAX_REWARDS_AMOUNT = 1e38 - 1; - uint32 OPERATOR_SET_GENESIS_REWARDS_TIMESTAMP = 0; /// TODO: what values should these have - uint32 OPERATOR_SET_MAX_RETROACTIVE_LENGTH = 0; /// TODO: What values these should have + uint32 OPERATOR_SET_GENESIS_REWARDS_TIMESTAMP = 0; + /// TODO: what values should these have + uint32 OPERATOR_SET_MAX_RETROACTIVE_LENGTH = 0; + /// TODO: What values these should have /// @notice Delay in timestamp before a posted root can be claimed against uint32 activationDelay = 7 days; /// @notice the commission for all operators across all avss @@ -87,10 +89,7 @@ contract ServiceManagerMigration_UnitTests is MockAVSDeployer, IServiceManagerBa ); // Deploy ServiceManager serviceManagerImplementation = new ServiceManagerMock( - avsDirectory, - rewardsCoordinator, - registryCoordinator, - stakeRegistry + avsDirectory, rewardsCoordinator, registryCoordinator, stakeRegistry ); serviceManager = ServiceManagerMock( @@ -105,7 +104,6 @@ contract ServiceManagerMigration_UnitTests is MockAVSDeployer, IServiceManagerBa ) ); - serviceManagerOwner = serviceManager.owner(); cheats.prank(serviceManagerOwner); serviceManager.setRewardsInitiator(rewardsInitiator); @@ -194,7 +192,11 @@ contract ServiceManagerMigration_UnitTests is MockAVSDeployer, IServiceManagerBa function test_viewFunction(uint256 randomValue) public { _registerRandomOperators(randomValue); - (uint32[] memory operatorSetsToCreate, uint32[][] memory operatorSetIdsToMigrate, address[] memory operators) = serviceManager.getOperatorsToMigrate(); + ( + uint32[] memory operatorSetsToCreate, + uint32[][] memory operatorSetIdsToMigrate, + address[] memory operators + ) = serviceManager.getOperatorsToMigrate(); // Assert that all operators are in quorum 0 invariant of _registerRandomOperators for (uint256 i = 0; i < operators.length; i++) { @@ -210,22 +212,33 @@ contract ServiceManagerMigration_UnitTests is MockAVSDeployer, IServiceManagerBa } function test_migrateToOperatorSets() public { - (uint32[] memory operatorSetsToCreate, uint32[][] memory operatorSetIdsToMigrate, address[] memory operators) = serviceManager.getOperatorsToMigrate(); + ( + uint32[] memory operatorSetsToCreate, + uint32[][] memory operatorSetIdsToMigrate, + address[] memory operators + ) = serviceManager.getOperatorsToMigrate(); cheats.startPrank(serviceManagerOwner); serviceManager.migrateAndCreateOperatorSetIds(operatorSetsToCreate); serviceManager.migrateToOperatorSets(operatorSetIdsToMigrate, operators); cheats.stopPrank(); - assertTrue(avsDirectory.isOperatorSetAVS(address(serviceManager)), "AVS is not an operator set AVS"); + assertTrue( + avsDirectory.isOperatorSetAVS(address(serviceManager)), "AVS is not an operator set AVS" + ); } function test_migrateTwoTransactions() public { - (uint32[] memory operatorSetsToCreate, uint32[][] memory operatorSetIdsToMigrate, address[] memory operators) = serviceManager.getOperatorsToMigrate(); + ( + uint32[] memory operatorSetsToCreate, + uint32[][] memory operatorSetIdsToMigrate, + address[] memory operators + ) = serviceManager.getOperatorsToMigrate(); // Split the operatorSetIdsToMigrate and operators into two separate sets uint256 halfLength = operatorSetIdsToMigrate.length / 2; uint32[][] memory firstHalfOperatorSetIds = new uint32[][](halfLength); - uint32[][] memory secondHalfOperatorSetIds = new uint32[][](operatorSetIdsToMigrate.length - halfLength); + uint32[][] memory secondHalfOperatorSetIds = + new uint32[][](operatorSetIdsToMigrate.length - halfLength); address[] memory firstHalfOperators = new address[](halfLength); address[] memory secondHalfOperators = new address[](operators.length - halfLength); @@ -246,24 +259,36 @@ contract ServiceManagerMigration_UnitTests is MockAVSDeployer, IServiceManagerBa serviceManager.migrateToOperatorSets(secondHalfOperatorSetIds, secondHalfOperators); cheats.stopPrank(); - assertTrue(avsDirectory.isOperatorSetAVS(address(serviceManager)), "AVS is not an operator set AVS"); + assertTrue( + avsDirectory.isOperatorSetAVS(address(serviceManager)), "AVS is not an operator set AVS" + ); } function test_migrateToOperatorSets_revert_alreadyMigrated() public { - (uint32[] memory operatorSetsToCreate, uint32[][] memory operatorSetIdsToMigrate, address[] memory operators) = serviceManager.getOperatorsToMigrate(); + ( + uint32[] memory operatorSetsToCreate, + uint32[][] memory operatorSetIdsToMigrate, + address[] memory operators + ) = serviceManager.getOperatorsToMigrate(); cheats.startPrank(serviceManagerOwner); serviceManager.migrateAndCreateOperatorSetIds(operatorSetsToCreate); serviceManager.migrateToOperatorSets(operatorSetIdsToMigrate, operators); serviceManager.finalizeMigration(); - vm.expectRevert(); /// TODO: Now that it's not 1 step, we should have a way to signal completion + vm.expectRevert(); + + /// TODO: Now that it's not 1 step, we should have a way to signal completion serviceManager.migrateToOperatorSets(operatorSetIdsToMigrate, operators); cheats.stopPrank(); } function test_migrateToOperatorSets_revert_notOwner() public { - (uint32[] memory operatorSetsToCreate, uint32[][] memory operatorSetIdsToMigrate, address[] memory operators) = serviceManager.getOperatorsToMigrate(); + ( + uint32[] memory operatorSetsToCreate, + uint32[][] memory operatorSetIdsToMigrate, + address[] memory operators + ) = serviceManager.getOperatorsToMigrate(); cheats.startPrank(serviceManagerOwner); serviceManager.migrateAndCreateOperatorSetIds(operatorSetsToCreate); cheats.stopPrank(); @@ -286,15 +311,25 @@ contract ServiceManagerMigration_UnitTests is MockAVSDeployer, IServiceManagerBa uint256 quorumCount = registryCoordinator.quorumCount(); for (uint256 i = 0; i < quorumCount; i++) { uint256 operatorCount = indexRegistry.totalOperatorsForQuorum(uint8(i)); - bytes32[] memory operatorIds = indexRegistry.getOperatorListAtBlockNumber(uint8(i), uint32(block.number)); - assertEq(operatorCount, operatorIds.length, "Operator Id length mismatch");// sanity check + bytes32[] memory operatorIds = + indexRegistry.getOperatorListAtBlockNumber(uint8(i), uint32(block.number)); + assertEq(operatorCount, operatorIds.length, "Operator Id length mismatch"); // sanity check for (uint256 j = 0; j < operatorCount; j++) { - address operatorAddress = registryCoordinator.blsApkRegistry().getOperatorFromPubkeyHash(operatorIds[j]); - AVSDirectoryHarness(address(avsDirectory)).setAvsOperatorStatus(address(serviceManager), operatorAddress, IAVSDirectory.OperatorAVSRegistrationStatus.REGISTERED); + address operatorAddress = + registryCoordinator.blsApkRegistry().getOperatorFromPubkeyHash(operatorIds[j]); + AVSDirectoryHarness(address(avsDirectory)).setAvsOperatorStatus( + address(serviceManager), + operatorAddress, + IAVSDirectory.OperatorAVSRegistrationStatus.REGISTERED + ); } } - (uint32[] memory operatorSetsToCreate, uint32[][] memory operatorSetIdsToMigrate, address[] memory operators) = serviceManager.getOperatorsToMigrate(); + ( + uint32[] memory operatorSetsToCreate, + uint32[][] memory operatorSetIdsToMigrate, + address[] memory operators + ) = serviceManager.getOperatorsToMigrate(); cheats.startPrank(serviceManagerOwner); serviceManager.migrateAndCreateOperatorSetIds(operatorSetsToCreate); serviceManager.migrateToOperatorSets(operatorSetIdsToMigrate, operators); @@ -302,8 +337,11 @@ contract ServiceManagerMigration_UnitTests is MockAVSDeployer, IServiceManagerBa /// quick check, this operator is in operator set 3 assertTrue( - avsDirectory.isMember(address(serviceManager), 0x73e2Ce949f15Be901f76b54F5a4554A6C8DCf539, uint32(3)), + avsDirectory.isMember( + 0x73e2Ce949f15Be901f76b54F5a4554A6C8DCf539, + IAVSDirectory.OperatorSet(address(serviceManager), uint32(3)) + ), "Operator not migrated to operator set" - ); + ); } } diff --git a/test/utils/MockAVSDeployer.sol b/test/utils/MockAVSDeployer.sol index 23875760..b3eebf1a 100644 --- a/test/utils/MockAVSDeployer.sol +++ b/test/utils/MockAVSDeployer.sol @@ -34,8 +34,9 @@ import {IAVSDirectory} from "eigenlayer-contracts/src/contracts/interfaces/IAVSD import {RewardsCoordinatorMock} from "../mocks/RewardsCoordinatorMock.sol"; -import { RewardsCoordinator } from "eigenlayer-contracts/src/contracts/core/RewardsCoordinator.sol"; -import { IRewardsCoordinator } from "eigenlayer-contracts/src/contracts/interfaces/IRewardsCoordinator.sol"; +import {RewardsCoordinator} from "eigenlayer-contracts/src/contracts/core/RewardsCoordinator.sol"; +import {IRewardsCoordinator} from + "eigenlayer-contracts/src/contracts/interfaces/IRewardsCoordinator.sol"; import {BLSApkRegistryHarness} from "../harnesses/BLSApkRegistryHarness.sol"; import {EmptyContract} from "eigenlayer-contracts/src/test/mocks/EmptyContract.sol"; @@ -147,7 +148,7 @@ contract MockAVSDeployer is Test { delegationMock = new DelegationMock(); avsDirectoryMock = new AVSDirectoryMock(); - eigenPodManagerMock = new EigenPodManagerMock(); + eigenPodManagerMock = new EigenPodManagerMock(pauserRegistry); strategyManagerMock = new StrategyManagerMock(); slasherImplementation = new Slasher(strategyManagerMock, delegationMock); slasher = Slasher( From 12eda5dd36ba793b03136516d2342554b173151a Mon Sep 17 00:00:00 2001 From: steven Date: Thu, 15 Aug 2024 09:02:11 -0400 Subject: [PATCH 10/27] feat: add natspec --- src/ServiceManagerBase.sol | 100 ++++++++++++++++++++++++++++--------- 1 file changed, 77 insertions(+), 23 deletions(-) diff --git a/src/ServiceManagerBase.sol b/src/ServiceManagerBase.sol index ac12619c..469addd1 100644 --- a/src/ServiceManagerBase.sol +++ b/src/ServiceManagerBase.sol @@ -4,7 +4,8 @@ pragma solidity ^0.8.12; import {Initializable} from "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol"; import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; import {IAVSDirectory} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; -import {IRewardsCoordinator} from "eigenlayer-contracts/src/contracts/interfaces/IRewardsCoordinator.sol"; +import {IRewardsCoordinator} from + "eigenlayer-contracts/src/contracts/interfaces/IRewardsCoordinator.sol"; import {ServiceManagerBaseStorage} from "./ServiceManagerBaseStorage.sol"; import {IServiceManager} from "./interfaces/IServiceManager.sol"; @@ -89,15 +90,15 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage { * @dev This function will revert if the `rewardsSubmission` is malformed, * e.g. if the `strategies` and `weights` arrays are of non-equal lengths */ - function createAVSRewardsSubmission(IRewardsCoordinator.RewardsSubmission[] calldata rewardsSubmissions) - public - virtual - onlyRewardsInitiator - { + function createAVSRewardsSubmission( + IRewardsCoordinator.RewardsSubmission[] calldata rewardsSubmissions + ) public virtual onlyRewardsInitiator { for (uint256 i = 0; i < rewardsSubmissions.length; ++i) { // transfer token to ServiceManager and approve RewardsCoordinator to transfer again // in createAVSRewardsSubmission() call - rewardsSubmissions[i].token.transferFrom(msg.sender, address(this), rewardsSubmissions[i].amount); + rewardsSubmissions[i].token.transferFrom( + msg.sender, address(this), rewardsSubmissions[i].amount + ); uint256 allowance = rewardsSubmissions[i].token.allowance(address(this), address(_rewardsCoordinator)); rewardsSubmissions[i].token.approve( @@ -107,7 +108,8 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage { _rewardsCoordinator.createAVSRewardsSubmission(rewardsSubmissions); } - function createOperatorSets(uint32[] memory operatorSetIds) external onlyRegistryCoordinator{ + + function createOperatorSets(uint32[] memory operatorSetIds) external onlyRegistryCoordinator { _avsDirectory.createOperatorSets(operatorSetIds); } @@ -140,30 +142,78 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage { _setRewardsInitiator(newRewardsInitiator); } - function migrateAndCreateOperatorSetIds(uint32[] memory operatorSetsToCreate) external onlyOwner{ + /** + * @notice Migrates the AVS to use operator sets and creates new operator set IDs. + * @param operatorSetsToCreate An array of operator set IDs to create. + * @dev This function can only be called by the contract owner. + */ + function migrateAndCreateOperatorSetIds(uint32[] memory operatorSetsToCreate) + external + onlyOwner + { _migrateAndCreateOperatorSetIds(operatorSetsToCreate); } - function migrateToOperatorSets(uint32[][] memory operatorSetIds, address[] memory operators) external onlyOwner { - require(!migrationFinalized, "SerivceManager: Migration Already Finalized"); + /** + * @notice Migrates operators to their respective operator sets. + * @param operatorSetIds A 2D array where each sub-array contains the operator set IDs for a specific operator. + * @param operators An array of operator addresses to migrate. + * @dev This function can only be called by the contract owner. + * @dev Reverts if the migration has already been finalized. + */ + function migrateToOperatorSets( + uint32[][] memory operatorSetIds, + address[] memory operators + ) external onlyOwner { + require(!migrationFinalized, "ServiceManager: Migration Already Finalized"); _migrateToOperatorSets(operatorSetIds, operators); } - function finalizeMigration() external onlyOwner{ - require(!migrationFinalized, "SerivceManager: Migration Already Finalized"); + /** + * @notice Finalizes the migration process, preventing further migrations. + * @dev This function can only be called by the contract owner. + * @dev Reverts if the migration has already been finalized. + */ + function finalizeMigration() external onlyOwner { + require(!migrationFinalized, "ServiceManager: Migration Already Finalized"); migrationFinalized = true; } - function _migrateAndCreateOperatorSetIds(uint32[] memory operatorSetIdsToCreate) internal { + /** + * @notice Migrates the AVS to use operator sets and create new operator set IDs. + * @param operatorSetIdsToCreate An array of operator set IDs to create. + */ + function _migrateAndCreateOperatorSetIds(uint32[] memory operatorSetIdsToCreate) { _avsDirectory.becomeOperatorSetAVS(); IAVSDirectory(address(_avsDirectory)).createOperatorSets(operatorSetIdsToCreate); } - function _migrateToOperatorSets(uint32[][] memory operatorSetIds, address[] memory operators) internal { - IAVSDirectory(address(_avsDirectory)).migrateOperatorsToOperatorSets(operators, operatorSetIds); + /** + * @notice Migrates operators to their respective operator sets. + * @param operatorSetIds A 2D array where each sub-array contains the operator set IDs for a specific operator. + * @param operators An array of operator addresses to migrate. + */ + function _migrateToOperatorSets(uint32[][] memory operatorSetIds, address[] memory operators) { + IAVSDirectory(address(_avsDirectory)).migrateOperatorsToOperatorSets( + operators, operatorSetIds + ); } - function getOperatorsToMigrate() public view returns (uint32[] memory operatorSetIdsToCreate, uint32[][] memory operatorSetIds, address[] memory allOperators) { + /** + * @notice Retrieves the operators to migrate along with their respective operator set IDs. + * @return operatorSetIdsToCreate An array of operator set IDs to create. + * @return operatorSetIds A 2D array where each sub-array contains the operator set IDs for a specific operator. + * @return allOperators An array of all unique operator addresses. + */ + function getOperatorsToMigrate() + public + view + returns ( + uint32[] memory operatorSetIdsToCreate, + uint32[][] memory operatorSetIds, + address[] memory allOperators + ) + { uint256 quorumCount = _registryCoordinator.quorumCount(); allOperators = new address[](0); @@ -172,14 +222,17 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage { // Step 1: Iterate through quorum numbers and get a list of unique operators for (uint8 quorumNumber = 0; quorumNumber < quorumCount; quorumNumber++) { // Step 2: Get operator list for quorum at current block - bytes32[] memory operatorIds = _registryCoordinator.indexRegistry().getOperatorListAtBlockNumber(quorumNumber, uint32(block.number)); - + bytes32[] memory operatorIds = _registryCoordinator.indexRegistry() + .getOperatorListAtBlockNumber(quorumNumber, uint32(block.number)); + // Step 3: Convert to address list and maintain a sorted array of operators address[] memory operators = new address[](operatorIds.length); for (uint256 i = 0; i < operatorIds.length; i++) { - operators[i] = _registryCoordinator.blsApkRegistry().getOperatorFromPubkeyHash(operatorIds[i]); + operators[i] = + _registryCoordinator.blsApkRegistry().getOperatorFromPubkeyHash(operatorIds[i]); // Insert into sorted array of all operators - allOperators = LibMergeSort.mergeSortArrays(allOperators, LibMergeSort.sort(operators)); + allOperators = + LibMergeSort.mergeSortArrays(allOperators, LibMergeSort.sort(operators)); } address[] memory filteredOperators = new address[](allOperators.length); uint256 count = 0; @@ -189,7 +242,9 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage { } } // Resize array to remove empty slots - assembly { mstore(filteredOperators, count) } + assembly { + mstore(filteredOperators, count) + } allOperators = filteredOperators; operatorSetIdsToCreate[quorumNumber] = uint32(quorumNumber); @@ -208,7 +263,6 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage { } operatorSetIds[i] = quorums; } - } function _setRewardsInitiator(address newRewardsInitiator) internal { From 788cd0946ba37dcffee3369a612713e0c0b3f9c3 Mon Sep 17 00:00:00 2001 From: steven Date: Thu, 15 Aug 2024 09:03:08 -0400 Subject: [PATCH 11/27] docs: add natspec --- src/ServiceManagerBase.sol | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/ServiceManagerBase.sol b/src/ServiceManagerBase.sol index 469addd1..213e8ee2 100644 --- a/src/ServiceManagerBase.sol +++ b/src/ServiceManagerBase.sol @@ -183,7 +183,7 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage { * @notice Migrates the AVS to use operator sets and create new operator set IDs. * @param operatorSetIdsToCreate An array of operator set IDs to create. */ - function _migrateAndCreateOperatorSetIds(uint32[] memory operatorSetIdsToCreate) { + function _migrateAndCreateOperatorSetIds(uint32[] memory operatorSetIdsToCreate) internal { _avsDirectory.becomeOperatorSetAVS(); IAVSDirectory(address(_avsDirectory)).createOperatorSets(operatorSetIdsToCreate); } @@ -193,7 +193,10 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage { * @param operatorSetIds A 2D array where each sub-array contains the operator set IDs for a specific operator. * @param operators An array of operator addresses to migrate. */ - function _migrateToOperatorSets(uint32[][] memory operatorSetIds, address[] memory operators) { + function _migrateToOperatorSets( + uint32[][] memory operatorSetIds, + address[] memory operators + ) internal { IAVSDirectory(address(_avsDirectory)).migrateOperatorsToOperatorSets( operators, operatorSetIds ); From d64f4af465201d66e2c581e42f28b32764a82381 Mon Sep 17 00:00:00 2001 From: steven Date: Thu, 15 Aug 2024 09:54:59 -0400 Subject: [PATCH 12/27] feat: add checks operator was registered for quorums when migrating --- src/ServiceManagerBase.sol | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/ServiceManagerBase.sol b/src/ServiceManagerBase.sol index 213e8ee2..a3374a4d 100644 --- a/src/ServiceManagerBase.sol +++ b/src/ServiceManagerBase.sol @@ -197,11 +197,37 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage { uint32[][] memory operatorSetIds, address[] memory operators ) internal { + require( + operators.length == operatorSetIds.length, "ServiceManager: Input array length mismatch" + ); + for (uint256 i; i < operators.length; i++) { + _isOperatorRegisteredForQuorums(operators[i], operatorSetIds[i]); + } IAVSDirectory(address(_avsDirectory)).migrateOperatorsToOperatorSets( operators, operatorSetIds ); } + /** + * @notice Checks if an operator is registered for a specific quorum + * @param operator The address of the operator to check + * @param quorumNumbers The quorum number to check the registration for + * @return bool Returns true if the operator is registered for the specified quorum, false otherwise + */ + function _isOperatorRegisteredForQuorums( + address operator, + uint32[] memory quorumNumbers + ) internal view returns (bool) { + bytes32 operatorId = _registryCoordinator.getOperatorId(operator); + uint192 operatorBitmap = _registryCoordinator.getCurrentQuorumBitmap(operatorId); + for (uint256 i; i < quorumNumbers.length; i++) { + require( + BitmapUtils.isSet(operatorBitmap, uint8(quorumNumbers[i])), + "ServiceManager: Operator not in quorum" + ); + } + } + /** * @notice Retrieves the operators to migrate along with their respective operator set IDs. * @return operatorSetIdsToCreate An array of operator set IDs to create. From a92d51acc2f09197a363761b4ede024219269cb3 Mon Sep 17 00:00:00 2001 From: steven Date: Thu, 15 Aug 2024 10:23:51 -0400 Subject: [PATCH 13/27] fix: resolve code size issue in RegistryCoordinator --- src/RegistryCoordinator.sol | 381 ++++++++++++++++---------- src/libraries/SignatureCheckerLib.sol | 27 ++ 2 files changed, 256 insertions(+), 152 deletions(-) create mode 100644 src/libraries/SignatureCheckerLib.sol diff --git a/src/RegistryCoordinator.sol b/src/RegistryCoordinator.sol index 86266f5d..738213f3 100644 --- a/src/RegistryCoordinator.sol +++ b/src/RegistryCoordinator.sol @@ -11,9 +11,9 @@ import {IIndexRegistry} from "./interfaces/IIndexRegistry.sol"; import {IServiceManager} from "./interfaces/IServiceManager.sol"; import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol"; -import {EIP1271SignatureUtils} from "eigenlayer-contracts/src/contracts/libraries/EIP1271SignatureUtils.sol"; import {BitmapUtils} from "./libraries/BitmapUtils.sol"; import {BN254} from "./libraries/BN254.sol"; +import {SignatureCheckerLib} from "./libraries/SignatureCheckerLib.sol"; import {OwnableUpgradeable} from "@openzeppelin-upgrades/contracts/access/OwnableUpgradeable.sol"; import {Initializable} from "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol"; @@ -27,22 +27,22 @@ import {RegistryCoordinatorStorage} from "./RegistryCoordinatorStorage.sol"; * 1) a `StakeRegistry` that keeps track of operators' stakes * 2) a `BLSApkRegistry` that keeps track of operators' BLS public keys and aggregate BLS public keys for each quorum * 3) an `IndexRegistry` that keeps track of an ordered list of operators for each quorum - * + * * @author Layr Labs, Inc. */ -contract RegistryCoordinator is - EIP712, - Initializable, +contract RegistryCoordinator is + EIP712, + Initializable, Pausable, OwnableUpgradeable, - RegistryCoordinatorStorage, - ISocketUpdater, + RegistryCoordinatorStorage, + ISocketUpdater, ISignatureUtils { using BitmapUtils for *; using BN254 for BN254.G1Point; - modifier onlyEjector { + modifier onlyEjector() { _checkEjector(); _; } @@ -59,9 +59,9 @@ contract RegistryCoordinator is IStakeRegistry _stakeRegistry, IBLSApkRegistry _blsApkRegistry, IIndexRegistry _indexRegistry - ) + ) RegistryCoordinatorStorage(_serviceManager, _stakeRegistry, _blsApkRegistry, _indexRegistry) - EIP712("AVSRegistryCoordinator", "v0.0.1") + EIP712("AVSRegistryCoordinator", "v0.0.1") { _disableInitializers(); } @@ -88,10 +88,11 @@ contract RegistryCoordinator is IStakeRegistry.StrategyParams[][] memory _strategyParams ) external initializer { require( - _operatorSetParams.length == _minimumStakes.length && _minimumStakes.length == _strategyParams.length, + _operatorSetParams.length == _minimumStakes.length + && _minimumStakes.length == _strategyParams.length, "RegistryCoordinator.initialize: input length mismatch" ); - + // Initialize roles _transferOwnership(_initialOwner); _initializePauser(_pauserRegistry, _initialPausedStatus); @@ -109,9 +110,11 @@ contract RegistryCoordinator is } } - /******************************************************************************* - EXTERNAL FUNCTIONS - *******************************************************************************/ + /** + * + * EXTERNAL FUNCTIONS + * + */ /** * @notice Registers msg.sender as an operator for one or more quorums. If any quorum exceeds its maximum @@ -141,9 +144,9 @@ contract RegistryCoordinator is // Register the operator in each of the registry contracts and update the operator's // quorum bitmap and registration status uint32[] memory numOperatorsPerQuorum = _registerOperator({ - operator: msg.sender, + operator: msg.sender, operatorId: operatorId, - quorumNumbers: quorumNumbers, + quorumNumbers: quorumNumbers, socket: socket, operatorSignature: operatorSignature }).numOperatorsPerQuorum; @@ -173,15 +176,18 @@ contract RegistryCoordinator is * @dev `operatorSignature` is ignored if the operator's status is already REGISTERED */ function registerOperatorWithChurn( - bytes calldata quorumNumbers, + bytes calldata quorumNumbers, string calldata socket, IBLSApkRegistry.PubkeyRegistrationParams calldata params, OperatorKickParam[] calldata operatorKickParams, SignatureWithSaltAndExpiry memory churnApproverSignature, SignatureWithSaltAndExpiry memory operatorSignature ) external onlyWhenNotPaused(PAUSED_REGISTER_OPERATOR) { - require(operatorKickParams.length == quorumNumbers.length, "RegistryCoordinator.registerOperatorWithChurn: input length mismatch"); - + require( + operatorKickParams.length == quorumNumbers.length, + "RegistryCoordinator.registerOperatorWithChurn: input length mismatch" + ); + /** * If the operator has NEVER registered a pubkey before, use `params` to register * their pubkey in blsApkRegistry @@ -213,7 +219,7 @@ contract RegistryCoordinator is // is exceeded, use `operatorKickParams` to deregister an existing operator to make space for (uint256 i = 0; i < quorumNumbers.length; i++) { OperatorSetParam memory operatorSetParams = _quorumParams[uint8(quorumNumbers[i])]; - + /** * If the new operator count for any quorum exceeds the maximum, validate * that churn can be performed, then deregister the specified operator @@ -228,7 +234,7 @@ contract RegistryCoordinator is setParams: operatorSetParams }); - _deregisterOperator(operatorKickParams[i].operator, quorumNumbers[i:i+1]); + _deregisterOperator(operatorKickParams[i].operator, quorumNumbers[i:i + 1]); } } } @@ -237,13 +243,11 @@ contract RegistryCoordinator is * @notice Deregisters the caller from one or more quorums * @param quorumNumbers is an ordered byte array containing the quorum numbers being deregistered from */ - function deregisterOperator( - bytes calldata quorumNumbers - ) external onlyWhenNotPaused(PAUSED_DEREGISTER_OPERATOR) { - _deregisterOperator({ - operator: msg.sender, - quorumNumbers: quorumNumbers - }); + function deregisterOperator(bytes calldata quorumNumbers) + external + onlyWhenNotPaused(PAUSED_DEREGISTER_OPERATOR) + { + _deregisterOperator({operator: msg.sender, quorumNumbers: quorumNumbers}); } /** @@ -252,7 +256,10 @@ contract RegistryCoordinator is * @dev stakes are queried from the Eigenlayer core DelegationManager contract * @param operators a list of operator addresses to update */ - function updateOperators(address[] calldata operators) external onlyWhenNotPaused(PAUSED_UPDATE_OPERATOR) { + function updateOperators(address[] calldata operators) + external + onlyWhenNotPaused(PAUSED_UPDATE_OPERATOR) + { for (uint256 i = 0; i < operators.length; i++) { address operator = operators[i]; OperatorInfo memory operatorInfo = _operatorInfo[operator]; @@ -276,18 +283,19 @@ contract RegistryCoordinator is * @param quorumNumbers is an ordered byte array containing the quorum numbers being updated * @dev invariant: Each list of `operatorsPerQuorum` MUST be a sorted version of `IndexRegistry.getOperatorListAtBlockNumber` * for the corresponding quorum. - * @dev note on race condition: if an operator registers/deregisters for any quorum in `quorumNumbers` after a txn to + * @dev note on race condition: if an operator registers/deregisters for any quorum in `quorumNumbers` after a txn to * this method is broadcast (but before it is executed), the method will fail */ function updateOperatorsForQuorum( address[][] calldata operatorsPerQuorum, bytes calldata quorumNumbers ) external onlyWhenNotPaused(PAUSED_UPDATE_OPERATOR) { - // Input validation + // Input validation // - all quorums should exist (checked against `quorumCount` in orderedBytesArrayToBitmap) // - there should be no duplicates in `quorumNumbers` // - there should be one list of operators per quorum - uint192 quorumBitmap = uint192(BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers, quorumCount)); + uint192 quorumBitmap = + uint192(BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers, quorumCount)); require( operatorsPerQuorum.length == quorumNumbers.length, "RegistryCoordinator.updateOperatorsForQuorum: input length mismatch" @@ -311,10 +319,10 @@ contract RegistryCoordinator is // ... then, update their stakes for (uint256 j = 0; j < currQuorumOperators.length; ++j) { address operator = currQuorumOperators[j]; - + OperatorInfo memory operatorInfo = _operatorInfo[operator]; bytes32 operatorId = operatorInfo.operatorId; - + { uint192 currentBitmap = _currentOperatorBitmap(operatorId); // Check that the operator is registered @@ -328,9 +336,9 @@ contract RegistryCoordinator is "RegistryCoordinator.updateOperatorsForQuorum: operators array must be sorted in ascending address order" ); } - + // Update the operator - _updateOperator(operator, operatorInfo, quorumNumbers[i:i+1]); + _updateOperator(operator, operatorInfo, quorumNumbers[i:i + 1]); prevOperatorAddress = operator; } @@ -345,13 +353,18 @@ contract RegistryCoordinator is * @param socket is the new socket of the operator */ function updateSocket(string memory socket) external { - require(_operatorInfo[msg.sender].status == OperatorStatus.REGISTERED, "RegistryCoordinator.updateSocket: operator is not registered"); + require( + _operatorInfo[msg.sender].status == OperatorStatus.REGISTERED, + "RegistryCoordinator.updateSocket: operator is not registered" + ); emit OperatorSocketUpdate(_operatorInfo[msg.sender].operatorId, socket); } - /******************************************************************************* - EXTERNAL FUNCTIONS - EJECTOR - *******************************************************************************/ + /** + * + * EXTERNAL FUNCTIONS - EJECTOR + * + */ /** * @notice Forcibly deregisters an operator from one or more quorums @@ -359,31 +372,27 @@ contract RegistryCoordinator is * @param quorumNumbers the quorum numbers to eject the operator from * @dev possible race condition if prior to being ejected for a set of quorums the operator self deregisters from a subset */ - function ejectOperator( - address operator, - bytes calldata quorumNumbers - ) external onlyEjector { + function ejectOperator(address operator, bytes calldata quorumNumbers) external onlyEjector { lastEjectionTimestamp[operator] = block.timestamp; OperatorInfo storage operatorInfo = _operatorInfo[operator]; bytes32 operatorId = operatorInfo.operatorId; - uint192 quorumsToRemove = uint192(BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers, quorumCount)); + uint192 quorumsToRemove = + uint192(BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers, quorumCount)); uint192 currentBitmap = _currentOperatorBitmap(operatorId); - if( - operatorInfo.status == OperatorStatus.REGISTERED && - !quorumsToRemove.isEmpty() && - quorumsToRemove.isSubsetOf(currentBitmap) - ){ - _deregisterOperator({ - operator: operator, - quorumNumbers: quorumNumbers - }); + if ( + operatorInfo.status == OperatorStatus.REGISTERED && !quorumsToRemove.isEmpty() + && quorumsToRemove.isSubsetOf(currentBitmap) + ) { + _deregisterOperator({operator: operator, quorumNumbers: quorumNumbers}); } } - /******************************************************************************* - EXTERNAL FUNCTIONS - OWNER - *******************************************************************************/ + /** + * + * EXTERNAL FUNCTIONS - OWNER + * + */ /** * @notice Creates a quorum and initializes it in each registry contract @@ -409,7 +418,7 @@ contract RegistryCoordinator is * @dev only callable by the owner */ function setOperatorSetParams( - uint8 quorumNumber, + uint8 quorumNumber, OperatorSetParam memory operatorSetParams ) external onlyOwner quorumExists(quorumNumber) { _setOperatorSetParams(quorumNumber, operatorSetParams); @@ -435,7 +444,7 @@ contract RegistryCoordinator is } /** - * @notice Sets the ejection cooldown, which is the time an operator must wait in + * @notice Sets the ejection cooldown, which is the time an operator must wait in * seconds afer ejection before registering for any quorum * @param _ejectionCooldown the new ejection cooldown in seconds * @dev only callable by the owner @@ -444,22 +453,23 @@ contract RegistryCoordinator is ejectionCooldown = _ejectionCooldown; } - /******************************************************************************* - INTERNAL FUNCTIONS - *******************************************************************************/ - + /** + * + * INTERNAL FUNCTIONS + * + */ struct RegisterResults { uint32[] numOperatorsPerQuorum; uint96[] operatorStakes; uint96[] totalStakes; } - /** + /** * @notice Register the operator for one or more quorums. This method updates the * operator's quorum bitmap, socket, and status, then registers them with each registry. */ function _registerOperator( - address operator, + address operator, bytes32 operatorId, bytes calldata quorumNumbers, string memory socket, @@ -472,33 +482,37 @@ contract RegistryCoordinator is * - the operator is not currently registered for any quorums we're registering for * Then, calculate the operator's new bitmap after registration */ - uint192 quorumsToAdd = uint192(BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers, quorumCount)); + uint192 quorumsToAdd = + uint192(BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers, quorumCount)); uint192 currentBitmap = _currentOperatorBitmap(operatorId); - require(!quorumsToAdd.isEmpty(), "RegistryCoordinator._registerOperator: bitmap cannot be 0"); - require(quorumsToAdd.noBitsInCommon(currentBitmap), "RegistryCoordinator._registerOperator: operator already registered for some quorums being registered for"); + require( + !quorumsToAdd.isEmpty(), "RegistryCoordinator._registerOperator: bitmap cannot be 0" + ); + require( + quorumsToAdd.noBitsInCommon(currentBitmap), + "RegistryCoordinator._registerOperator: operator already registered for some quorums being registered for" + ); uint192 newBitmap = uint192(currentBitmap.plus(quorumsToAdd)); // Check that the operator can reregister if ejected - require(lastEjectionTimestamp[operator] + ejectionCooldown < block.timestamp, "RegistryCoordinator._registerOperator: operator cannot reregister yet"); + require( + lastEjectionTimestamp[operator] + ejectionCooldown < block.timestamp, + "RegistryCoordinator._registerOperator: operator cannot reregister yet" + ); /** * Update operator's bitmap, socket, and status. Only update operatorInfo if needed: * if we're `REGISTERED`, the operatorId and status are already correct. */ - _updateOperatorBitmap({ - operatorId: operatorId, - newBitmap: newBitmap - }); + _updateOperatorBitmap({operatorId: operatorId, newBitmap: newBitmap}); emit OperatorSocketUpdate(operatorId, socket); // If the operator wasn't registered for any quorums, update their status // and register them with this AVS in EigenLayer core (DelegationManager) if (_operatorInfo[operator].status != OperatorStatus.REGISTERED) { - _operatorInfo[operator] = OperatorInfo({ - operatorId: operatorId, - status: OperatorStatus.REGISTERED - }); + _operatorInfo[operator] = + OperatorInfo({operatorId: operatorId, status: OperatorStatus.REGISTERED}); // Register the operator with the EigenLayer core contracts via this AVS's ServiceManager serviceManager.registerOperatorToAVS(operator, operatorSignature); @@ -508,7 +522,7 @@ contract RegistryCoordinator is // Register the operator with the BLSApkRegistry, StakeRegistry, and IndexRegistry blsApkRegistry.registerOperator(operator, quorumNumbers); - (results.operatorStakes, results.totalStakes) = + (results.operatorStakes, results.totalStakes) = stakeRegistry.registerOperator(operator, operatorId, quorumNumbers); results.numOperatorsPerQuorum = indexRegistry.registerOperator(operatorId, quorumNumbers); @@ -530,8 +544,7 @@ contract RegistryCoordinator is */ function _checkQuorumExists(uint8 quorumNumber) internal view { require( - quorumNumber < quorumCount, - "RegistryCoordinator.quorumExists: quorum does not exist" + quorumNumber < quorumCount, "RegistryCoordinator.quorumExists: quorum does not exist" ); } @@ -549,7 +562,9 @@ contract RegistryCoordinator is ) internal returns (bytes32 operatorId) { operatorId = blsApkRegistry.getOperatorId(operator); if (operatorId == 0) { - operatorId = blsApkRegistry.registerBLSPublicKey(operator, params, pubkeyRegistrationMessageHash(operator)); + operatorId = blsApkRegistry.registerBLSPublicKey( + operator, params, pubkeyRegistrationMessageHash(operator) + ); } return operatorId; } @@ -573,17 +588,22 @@ contract RegistryCoordinator is * mentioned above */ function _validateChurn( - uint8 quorumNumber, + uint8 quorumNumber, uint96 totalQuorumStake, - address newOperator, + address newOperator, uint96 newOperatorStake, - OperatorKickParam memory kickParams, + OperatorKickParam memory kickParams, OperatorSetParam memory setParams ) internal view { address operatorToKick = kickParams.operator; bytes32 idToKick = _operatorInfo[operatorToKick].operatorId; - require(newOperator != operatorToKick, "RegistryCoordinator._validateChurn: cannot churn self"); - require(kickParams.quorumNumber == quorumNumber, "RegistryCoordinator._validateChurn: quorumNumber not the same as signed"); + require( + newOperator != operatorToKick, "RegistryCoordinator._validateChurn: cannot churn self" + ); + require( + kickParams.quorumNumber == quorumNumber, + "RegistryCoordinator._validateChurn: quorumNumber not the same as signed" + ); // Get the target operator's stake and check that it is below the kick thresholds uint96 operatorToKickStake = stakeRegistry.getCurrentStake(idToKick, quorumNumber); @@ -602,15 +622,15 @@ contract RegistryCoordinator is * This method updates the operator's quorum bitmap and status, then deregisters * the operator with the BLSApkRegistry, IndexRegistry, and StakeRegistry */ - function _deregisterOperator( - address operator, - bytes memory quorumNumbers - ) internal virtual { + function _deregisterOperator(address operator, bytes memory quorumNumbers) internal virtual { // Fetch the operator's info and ensure they are registered OperatorInfo storage operatorInfo = _operatorInfo[operator]; bytes32 operatorId = operatorInfo.operatorId; - require(operatorInfo.status == OperatorStatus.REGISTERED, "RegistryCoordinator._deregisterOperator: operator is not registered"); - + require( + operatorInfo.status == OperatorStatus.REGISTERED, + "RegistryCoordinator._deregisterOperator: operator is not registered" + ); + /** * Get bitmap of quorums to deregister from and operator's current bitmap. Validate that: * - we're trying to deregister from at least 1 quorum @@ -618,19 +638,23 @@ contract RegistryCoordinator is * - the operator is currently registered for any quorums we're trying to deregister from * Then, calculate the operator's new bitmap after deregistration */ - uint192 quorumsToRemove = uint192(BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers, quorumCount)); + uint192 quorumsToRemove = + uint192(BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers, quorumCount)); uint192 currentBitmap = _currentOperatorBitmap(operatorId); - require(!quorumsToRemove.isEmpty(), "RegistryCoordinator._deregisterOperator: bitmap cannot be 0"); - require(quorumsToRemove.isSubsetOf(currentBitmap), "RegistryCoordinator._deregisterOperator: operator is not registered for specified quorums"); + require( + !quorumsToRemove.isEmpty(), + "RegistryCoordinator._deregisterOperator: bitmap cannot be 0" + ); + require( + quorumsToRemove.isSubsetOf(currentBitmap), + "RegistryCoordinator._deregisterOperator: operator is not registered for specified quorums" + ); uint192 newBitmap = uint192(currentBitmap.minus(quorumsToRemove)); // Update operator's bitmap and status - _updateOperatorBitmap({ - operatorId: operatorId, - newBitmap: newBitmap - }); + _updateOperatorBitmap({operatorId: operatorId, newBitmap: newBitmap}); - // If the operator is no longer registered for any quorums, update their status and deregister + // If the operator is no longer registered for any quorums, update their status and deregister // them from the AVS via the EigenLayer core contracts if (newBitmap.isEmpty()) { operatorInfo.status = OperatorStatus.DEREGISTERED; @@ -659,13 +683,14 @@ contract RegistryCoordinator is return; } bytes32 operatorId = operatorInfo.operatorId; - uint192 quorumsToRemove = stakeRegistry.updateOperatorStake(operator, operatorId, quorumsToUpdate); + uint192 quorumsToRemove = + stakeRegistry.updateOperatorStake(operator, operatorId, quorumsToUpdate); if (!quorumsToRemove.isEmpty()) { _deregisterOperator({ operator: operator, quorumNumbers: BitmapUtils.bitmapToBytesArray(quorumsToRemove) - }); + }); } } @@ -673,7 +698,10 @@ contract RegistryCoordinator is * @notice Returns the stake threshold required for an incoming operator to replace an existing operator * The incoming operator must have more stake than the return value. */ - function _individualKickThreshold(uint96 operatorStake, OperatorSetParam memory setParams) internal pure returns (uint96) { + function _individualKickThreshold( + uint96 operatorStake, + OperatorSetParam memory setParams + ) internal pure returns (uint96) { return operatorStake * setParams.kickBIPsOfOperatorStake / BIPS_DENOMINATOR; } @@ -681,28 +709,43 @@ contract RegistryCoordinator is * @notice Returns the total stake threshold required for an operator to remain in a quorum. * The operator must have at least the returned stake amount to keep their position. */ - function _totalKickThreshold(uint96 totalStake, OperatorSetParam memory setParams) internal pure returns (uint96) { + function _totalKickThreshold( + uint96 totalStake, + OperatorSetParam memory setParams + ) internal pure returns (uint96) { return totalStake * setParams.kickBIPsOfTotalStake / BIPS_DENOMINATOR; } /// @notice verifies churnApprover's signature on operator churn approval and increments the churnApprover nonce function _verifyChurnApproverSignature( address registeringOperator, - bytes32 registeringOperatorId, - OperatorKickParam[] memory operatorKickParams, + bytes32 registeringOperatorId, + OperatorKickParam[] memory operatorKickParams, SignatureWithSaltAndExpiry memory churnApproverSignature ) internal { // make sure the salt hasn't been used already - require(!isChurnApproverSaltUsed[churnApproverSignature.salt], "RegistryCoordinator._verifyChurnApproverSignature: churnApprover salt already used"); - require(churnApproverSignature.expiry >= block.timestamp, "RegistryCoordinator._verifyChurnApproverSignature: churnApprover signature expired"); + require( + !isChurnApproverSaltUsed[churnApproverSignature.salt], + "RegistryCoordinator._verifyChurnApproverSignature: churnApprover salt already used" + ); + require( + churnApproverSignature.expiry >= block.timestamp, + "RegistryCoordinator._verifyChurnApproverSignature: churnApprover signature expired" + ); // set salt used to true - isChurnApproverSaltUsed[churnApproverSignature.salt] = true; - - // check the churnApprover's signature - EIP1271SignatureUtils.checkSignature_EIP1271( - churnApprover, - calculateOperatorChurnApprovalDigestHash(registeringOperator, registeringOperatorId, operatorKickParams, churnApproverSignature.salt, churnApproverSignature.expiry), + isChurnApproverSaltUsed[churnApproverSignature.salt] = true; + + // check the churnApprover's signature + SignatureCheckerLib.isValidSignature( + churnApprover, + calculateOperatorChurnApprovalDigestHash( + registeringOperator, + registeringOperatorId, + operatorKickParams, + churnApproverSignature.salt, + churnApproverSignature.expiry + ), churnApproverSignature.signature ); } @@ -722,9 +765,12 @@ contract RegistryCoordinator is ) internal { // Increment the total quorum count. Fails if we're already at the max uint8 prevQuorumCount = quorumCount; - require(prevQuorumCount < MAX_QUORUM_COUNT, "RegistryCoordinator.createQuorum: max quorums reached"); + require( + prevQuorumCount < MAX_QUORUM_COUNT, + "RegistryCoordinator.createQuorum: max quorums reached" + ); quorumCount = prevQuorumCount + 1; - + // The previous count is the new quorum's number uint8 quorumNumber = prevQuorumCount; @@ -735,7 +781,7 @@ contract RegistryCoordinator is blsApkRegistry.initializeQuorum(quorumNumber); // Check if the AVS has migrated to operator sets AVSDirectory avsDirectory = AVSDirectory(serviceManager.avsDirectory()); - if (avsDirectory.isOperatorSetAVS(address(serviceManager))){ + if (avsDirectory.isOperatorSetAVS(address(serviceManager))) { // Create an operator set for the new quorum uint32[] memory operatorSetIds = new uint32[](1); operatorSetIds[0] = uint32(quorumNumber); @@ -748,19 +794,21 @@ contract RegistryCoordinator is * @param newBitmap is the most up-to-date set of bitmaps the operator is registered for */ function _updateOperatorBitmap(bytes32 operatorId, uint192 newBitmap) internal { - uint256 historyLength = _operatorBitmapHistory[operatorId].length; if (historyLength == 0) { // No prior bitmap history - push our first entry - _operatorBitmapHistory[operatorId].push(QuorumBitmapUpdate({ - updateBlockNumber: uint32(block.number), - nextUpdateBlockNumber: 0, - quorumBitmap: newBitmap - })); + _operatorBitmapHistory[operatorId].push( + QuorumBitmapUpdate({ + updateBlockNumber: uint32(block.number), + nextUpdateBlockNumber: 0, + quorumBitmap: newBitmap + }) + ); } else { // We have prior history - fetch our last-recorded update - QuorumBitmapUpdate storage lastUpdate = _operatorBitmapHistory[operatorId][historyLength - 1]; + QuorumBitmapUpdate storage lastUpdate = + _operatorBitmapHistory[operatorId][historyLength - 1]; /** * If the last update was made in the current block, update the entry. @@ -770,11 +818,13 @@ contract RegistryCoordinator is lastUpdate.quorumBitmap = newBitmap; } else { lastUpdate.nextUpdateBlockNumber = uint32(block.number); - _operatorBitmapHistory[operatorId].push(QuorumBitmapUpdate({ - updateBlockNumber: uint32(block.number), - nextUpdateBlockNumber: 0, - quorumBitmap: newBitmap - })); + _operatorBitmapHistory[operatorId].push( + QuorumBitmapUpdate({ + updateBlockNumber: uint32(block.number), + nextUpdateBlockNumber: 0, + quorumBitmap: newBitmap + }) + ); } } } @@ -796,7 +846,7 @@ contract RegistryCoordinator is * @dev This function is designed to find proper inputs to the `getQuorumBitmapAtBlockNumberByIndex` function */ function _getQuorumBitmapIndexAtBlockNumber( - uint32 blockNumber, + uint32 blockNumber, bytes32 operatorId ) internal view returns (uint32 index) { uint256 length = _operatorBitmapHistory[operatorId].length; @@ -816,11 +866,14 @@ contract RegistryCoordinator is ); } - function _setOperatorSetParams(uint8 quorumNumber, OperatorSetParam memory operatorSetParams) internal { + function _setOperatorSetParams( + uint8 quorumNumber, + OperatorSetParam memory operatorSetParams + ) internal { _quorumParams[quorumNumber] = operatorSetParams; emit OperatorSetParamsUpdated(quorumNumber, operatorSetParams); } - + function _setChurnApprover(address newChurnApprover) internal { emit ChurnApproverUpdated(churnApprover, newChurnApprover); churnApprover = newChurnApprover; @@ -831,12 +884,18 @@ contract RegistryCoordinator is ejector = newEjector; } - /******************************************************************************* - VIEW FUNCTIONS - *******************************************************************************/ + /** + * + * VIEW FUNCTIONS + * + */ /// @notice Returns the operator set params for the given `quorumNumber` - function getOperatorSetParams(uint8 quorumNumber) external view returns (OperatorSetParam memory) { + function getOperatorSetParams(uint8 quorumNumber) + external + view + returns (OperatorSetParam memory) + { return _quorumParams[quorumNumber]; } @@ -856,7 +915,11 @@ contract RegistryCoordinator is } /// @notice Returns the status for the given `operator` - function getOperatorStatus(address operator) external view returns (IRegistryCoordinator.OperatorStatus) { + function getOperatorStatus(address operator) + external + view + returns (IRegistryCoordinator.OperatorStatus) + { return _operatorInfo[operator].status; } @@ -866,7 +929,7 @@ contract RegistryCoordinator is * @dev This function is designed to find proper inputs to the `getQuorumBitmapAtBlockNumberByIndex` function */ function getQuorumBitmapIndicesAtBlockNumber( - uint32 blockNumber, + uint32 blockNumber, bytes32[] memory operatorIds ) external view returns (uint32[] memory) { uint32[] memory indices = new uint32[](operatorIds.length); @@ -881,25 +944,26 @@ contract RegistryCoordinator is * reverting if `index` is incorrect * @dev This function is meant to be used in concert with `getQuorumBitmapIndicesAtBlockNumber`, which * helps off-chain processes to fetch the correct `index` input - */ + */ function getQuorumBitmapAtBlockNumberByIndex( - bytes32 operatorId, - uint32 blockNumber, + bytes32 operatorId, + uint32 blockNumber, uint256 index ) external view returns (uint192) { QuorumBitmapUpdate memory quorumBitmapUpdate = _operatorBitmapHistory[operatorId][index]; - + /** * Validate that the update is valid for the given blockNumber: * - blockNumber should be >= the update block number * - the next update block number should be either 0 or strictly greater than blockNumber */ require( - blockNumber >= quorumBitmapUpdate.updateBlockNumber, + blockNumber >= quorumBitmapUpdate.updateBlockNumber, "RegistryCoordinator.getQuorumBitmapAtBlockNumberByIndex: quorumBitmapUpdate is from after blockNumber" ); require( - quorumBitmapUpdate.nextUpdateBlockNumber == 0 || blockNumber < quorumBitmapUpdate.nextUpdateBlockNumber, + quorumBitmapUpdate.nextUpdateBlockNumber == 0 + || blockNumber < quorumBitmapUpdate.nextUpdateBlockNumber, "RegistryCoordinator.getQuorumBitmapAtBlockNumberByIndex: quorumBitmapUpdate is from before blockNumber" ); @@ -908,7 +972,7 @@ contract RegistryCoordinator is /// @notice Returns the `index`th entry in the operator with `operatorId`'s bitmap history function getQuorumBitmapUpdateByIndex( - bytes32 operatorId, + bytes32 operatorId, uint256 index ) external view returns (QuorumBitmapUpdate memory) { return _operatorBitmapHistory[operatorId][index]; @@ -931,7 +995,7 @@ contract RegistryCoordinator is /** * @notice Public function for the the churnApprover signature hash calculation when operators are being kicked from quorums - * @param registeringOperatorId The id of the registering operator + * @param registeringOperatorId The id of the registering operator * @param operatorKickParams The parameters needed to kick the operator from the quorums that have reached their caps * @param salt The salt to use for the churnApprover's signature * @param expiry The desired expiry time of the churnApprover's signature @@ -944,18 +1008,31 @@ contract RegistryCoordinator is uint256 expiry ) public view returns (bytes32) { // calculate the digest hash - return _hashTypedDataV4(keccak256(abi.encode(OPERATOR_CHURN_APPROVAL_TYPEHASH, registeringOperator, registeringOperatorId, operatorKickParams, salt, expiry))); + return _hashTypedDataV4( + keccak256( + abi.encode( + OPERATOR_CHURN_APPROVAL_TYPEHASH, + registeringOperator, + registeringOperatorId, + operatorKickParams, + salt, + expiry + ) + ) + ); } /** * @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) { + function pubkeyRegistrationMessageHash(address operator) + public + view + returns (BN254.G1Point memory) + { return BN254.hashToG1( - _hashTypedDataV4( - keccak256(abi.encode(PUBKEY_REGISTRATION_TYPEHASH, operator)) - ) + _hashTypedDataV4(keccak256(abi.encode(PUBKEY_REGISTRATION_TYPEHASH, operator))) ); } diff --git a/src/libraries/SignatureCheckerLib.sol b/src/libraries/SignatureCheckerLib.sol new file mode 100644 index 00000000..fa2fb137 --- /dev/null +++ b/src/libraries/SignatureCheckerLib.sol @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.12; + +import {EIP1271SignatureUtils} from + "eigenlayer-contracts/src/contracts/libraries/EIP1271SignatureUtils.sol"; + +/** + * @title SignatureCheckerLib + * @dev This library wraps the EIP1271SignatureUtils library to provide an external function for signature validation. + * This approach helps in reducing the code size of the RegistryCoordinator contract by offloading the signature + * validation logic to this external library. + */ +library SignatureCheckerLib { + /** + * @notice Validates a signature using EIP-1271 standard. + * @param signer The address of the signer. + * @param digestHash The hash of the data that was signed. + * @param signature The signature to be validated. + */ + function isValidSignature( + address signer, + bytes32 digestHash, + bytes memory signature + ) external view { + EIP1271SignatureUtils.checkSignature_EIP1271(signer, digestHash, signature); + } +} From 32148dee470cf8addfff85cbb34d73693bf1c04d Mon Sep 17 00:00:00 2001 From: Madhur Shrimal Date: Thu, 15 Aug 2024 13:46:22 -0700 Subject: [PATCH 14/27] chore: update to latest core (#299) * chore: update to latest eigenlayer-contracts feat/operator-set-release * chore: add method to mock --- lib/eigenlayer-contracts | 2 +- test/mocks/RewardsCoordinatorMock.sol | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/eigenlayer-contracts b/lib/eigenlayer-contracts index f2a7515a..d8a8341c 160000 --- a/lib/eigenlayer-contracts +++ b/lib/eigenlayer-contracts @@ -1 +1 @@ -Subproject commit f2a7515a43162adb5e1b778308349043f60db6a8 +Subproject commit d8a8341c5d5c960e6da7c08a845f2584da579cf7 diff --git a/test/mocks/RewardsCoordinatorMock.sol b/test/mocks/RewardsCoordinatorMock.sol index 6e4f6538..a31df78c 100644 --- a/test/mocks/RewardsCoordinatorMock.sol +++ b/test/mocks/RewardsCoordinatorMock.sol @@ -63,6 +63,8 @@ contract RewardsCoordinatorMock is IRewardsCoordinator { function getDistributionRootAtIndex(uint256 index) external view returns (DistributionRoot memory) {} + function getCurrentClaimableDistributionRoot() external view returns (DistributionRoot memory) {} + function getCurrentDistributionRoot() external view returns (DistributionRoot memory) {} function createAVSRewardsSubmission(RewardsSubmission[] calldata rewardsSubmissions) external {} From 86f0928dd6030eefe971c6de65b1a1241117a149 Mon Sep 17 00:00:00 2001 From: steven <12021290+stevennevins@users.noreply.github.com> Date: Wed, 21 Aug 2024 14:27:00 -0400 Subject: [PATCH 15/27] feat(op sets): update stakes when forceUnregister (#300) * feat: update stakes handle direct deregistration on AVSDirectory * test: update stake for quorum if operator directly unregistered from the AVSDirectory * chore: simplify setup * chore: simplify setup * chore: make service manager immutable on stakeRegistry --- src/StakeRegistry.sol | 28 ++++++- src/StakeRegistryStorage.sol | 14 +++- src/interfaces/IRegistryCoordinator.sol | 3 + test/harnesses/StakeRegistryHarness.sol | 6 +- test/integration/IntegrationDeployer.t.sol | 2 +- test/mocks/RegistryCoordinatorMock.sol | 4 +- test/unit/RegistryCoordinatorMigration.t.sol | 85 ++++++++++++++++++++ test/unit/StakeRegistryUnit.t.sol | 2 +- test/utils/MockAVSDeployer.sol | 2 +- 9 files changed, 135 insertions(+), 11 deletions(-) diff --git a/src/StakeRegistry.sol b/src/StakeRegistry.sol index 929b67d9..ca4b55fb 100644 --- a/src/StakeRegistry.sol +++ b/src/StakeRegistry.sol @@ -2,6 +2,8 @@ pragma solidity ^0.8.12; import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; +import {IAVSDirectory} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; +import {IServiceManager} from "./interfaces/IServiceManager.sol"; import {StakeRegistryStorage, IStrategy} from "./StakeRegistryStorage.sol"; @@ -40,8 +42,10 @@ contract StakeRegistry is StakeRegistryStorage { constructor( IRegistryCoordinator _registryCoordinator, - IDelegationManager _delegationManager - ) StakeRegistryStorage(_registryCoordinator, _delegationManager) {} + IDelegationManager _delegationManager, + IAVSDirectory _avsDirectory, + IServiceManager _serviceManager + ) StakeRegistryStorage(_registryCoordinator, _delegationManager, _avsDirectory, _serviceManager) {} /******************************************************************************* EXTERNAL FUNCTIONS - REGISTRY COORDINATOR @@ -148,6 +152,10 @@ contract StakeRegistry is StakeRegistryStorage { ) external onlyRegistryCoordinator returns (uint192) { uint192 quorumsToRemove; + bool isOperatorSetAVS = avsDirectory.isOperatorSetAVS( + address(serviceManager) + ); + /** * For each quorum, update the operator's stake and record the delta * in the quorum's total stake. @@ -163,9 +171,21 @@ contract StakeRegistry is StakeRegistryStorage { // Fetch the operator's current stake, applying weighting parameters and checking // against the minimum stake requirements for the quorum. (uint96 stakeWeight, bool hasMinimumStake) = _weightOfOperatorForQuorum(quorumNumber, operator); - // If the operator no longer meets the minimum stake, set their stake to zero and mark them for removal - if (!hasMinimumStake) { + /// also handle setting the operator's stake to 0 and remove them from the quorum + /// if they directly unregistered from the AVSDirectory bubbles up info via registry coordinator to deregister them + bool operatorRegistered; + // Convert quorumNumber to operatorSetId + uint32 operatorSetId = uint32(quorumNumber); + + // Get the AVSDirectory address from the RegistryCoordinator + // Query the AVSDirectory to check if the operator is directly unregistered + operatorRegistered = avsDirectory.isMember( + operator, + IAVSDirectory.OperatorSet(address(serviceManager), operatorSetId) + ); + + if (!hasMinimumStake || (isOperatorSetAVS && !operatorRegistered)) { stakeWeight = 0; quorumsToRemove = uint192(quorumsToRemove.setBit(quorumNumber)); } diff --git a/src/StakeRegistryStorage.sol b/src/StakeRegistryStorage.sol index 6ef74e3e..f76608d2 100644 --- a/src/StakeRegistryStorage.sol +++ b/src/StakeRegistryStorage.sol @@ -2,6 +2,8 @@ pragma solidity ^0.8.12; import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; +import {IAVSDirectory} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; +import {IServiceManager} from "./interfaces/IServiceManager.sol"; import {IStrategyManager, IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol"; import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol"; @@ -24,6 +26,12 @@ abstract contract StakeRegistryStorage is IStakeRegistry { /// @notice The address of the Delegation contract for EigenLayer. IDelegationManager public immutable delegation; + /// @notice The address of the Delegation contract for EigenLayer. + IAVSDirectory public immutable avsDirectory; + + /// @notice the address of the ServiceManager associtated with the stake registries + IServiceManager public immutable serviceManager; + /// @notice the coordinator contract that this registry is associated with address public immutable registryCoordinator; @@ -47,10 +55,14 @@ abstract contract StakeRegistryStorage is IStakeRegistry { constructor( IRegistryCoordinator _registryCoordinator, - IDelegationManager _delegationManager + IDelegationManager _delegationManager, + IAVSDirectory _avsDirectory, + IServiceManager _serviceManager ) { registryCoordinator = address(_registryCoordinator); delegation = _delegationManager; + avsDirectory = _avsDirectory; + serviceManager = _serviceManager; } // storage gap for upgradeability diff --git a/src/interfaces/IRegistryCoordinator.sol b/src/interfaces/IRegistryCoordinator.sol index 43fa7e0a..3a56baa2 100644 --- a/src/interfaces/IRegistryCoordinator.sol +++ b/src/interfaces/IRegistryCoordinator.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.12; +import {IServiceManager} from "./IServiceManager.sol"; import {IBLSApkRegistry} from "./IBLSApkRegistry.sol"; import {IStakeRegistry} from "./IStakeRegistry.sol"; import {IIndexRegistry} from "./IIndexRegistry.sol"; @@ -150,4 +151,6 @@ interface IRegistryCoordinator { /// @notice The owner of the registry coordinator function owner() external view returns (address); + + function serviceManager() external view returns (IServiceManager); } diff --git a/test/harnesses/StakeRegistryHarness.sol b/test/harnesses/StakeRegistryHarness.sol index 386508d9..ca3cd2af 100644 --- a/test/harnesses/StakeRegistryHarness.sol +++ b/test/harnesses/StakeRegistryHarness.sol @@ -7,8 +7,10 @@ import "../../src/StakeRegistry.sol"; contract StakeRegistryHarness is StakeRegistry { constructor( IRegistryCoordinator _registryCoordinator, - IDelegationManager _delegationManager - ) StakeRegistry(_registryCoordinator, _delegationManager) { + IDelegationManager _delegationManager, + IAVSDirectory _avsDirectory, + IServiceManager _serviceManager + ) StakeRegistry(_registryCoordinator, _delegationManager, _avsDirectory, _serviceManager) { } function recordOperatorStakeUpdate(bytes32 operatorId, uint8 quorumNumber, uint96 newStake) external returns(int256) { diff --git a/test/integration/IntegrationDeployer.t.sol b/test/integration/IntegrationDeployer.t.sol index 51525a8c..6d36a8be 100644 --- a/test/integration/IntegrationDeployer.t.sol +++ b/test/integration/IntegrationDeployer.t.sol @@ -330,7 +330,7 @@ abstract contract IntegrationDeployer is Test, IUserDeployer { cheats.stopPrank(); StakeRegistry stakeRegistryImplementation = new StakeRegistry( - IRegistryCoordinator(registryCoordinator), IDelegationManager(delegationManager) + IRegistryCoordinator(registryCoordinator), IDelegationManager(delegationManager), IAVSDirectory(avsDirectory), IServiceManager(serviceManager) ); BLSApkRegistry blsApkRegistryImplementation = new BLSApkRegistry(IRegistryCoordinator(registryCoordinator)); diff --git a/test/mocks/RegistryCoordinatorMock.sol b/test/mocks/RegistryCoordinatorMock.sol index abee1a6a..73308f14 100644 --- a/test/mocks/RegistryCoordinatorMock.sol +++ b/test/mocks/RegistryCoordinatorMock.sol @@ -68,4 +68,6 @@ contract RegistryCoordinatorMock is IRegistryCoordinator { function quorumUpdateBlockNumber(uint8 quorumNumber) external view returns (uint256) {} function owner() external view returns (address) {} -} + + function serviceManager() external view returns (IServiceManager){} +} \ No newline at end of file diff --git a/test/unit/RegistryCoordinatorMigration.t.sol b/test/unit/RegistryCoordinatorMigration.t.sol index 0eb80273..357af6d2 100644 --- a/test/unit/RegistryCoordinatorMigration.t.sol +++ b/test/unit/RegistryCoordinatorMigration.t.sol @@ -197,4 +197,89 @@ contract RegistryCoordinatorMigrationUnit is MockAVSDeployer, IServiceManagerBas assertTrue(avsDirectory.isOperatorSet(address(serviceManager), quorumNumber), "Operator set was not created for the quorum"); } + + function test_updateOperatorsForQuorumsAfterDirectUnregister() public { + vm.prank(proxyAdmin.owner()); + proxyAdmin.upgrade( + TransparentUpgradeableProxy(payable(address(avsDirectory))), + address(avsDirectoryMock) + ); + uint256 pseudoRandomNumber = uint256(keccak256("pseudoRandomNumber")); + _registerRandomOperators(pseudoRandomNumber); + + vm.prank(proxyAdmin.owner()); + proxyAdmin.upgrade( + TransparentUpgradeableProxy(payable(address(avsDirectory))), + address(avsDirectoryHarness) + ); + + uint256 quorumCount = registryCoordinator.quorumCount(); + for (uint256 i = 0; i < quorumCount; i++) { + uint256 operatorCount = indexRegistry.totalOperatorsForQuorum(uint8(i)); + bytes32[] memory operatorIds = + indexRegistry.getOperatorListAtBlockNumber(uint8(i), uint32(block.number)); + assertEq(operatorCount, operatorIds.length, "Operator Id length mismatch"); // sanity check + for (uint256 j = 0; j < operatorCount; j++) { + address operatorAddress = + registryCoordinator.blsApkRegistry().getOperatorFromPubkeyHash(operatorIds[j]); + AVSDirectoryHarness(address(avsDirectory)).setAvsOperatorStatus( + address(serviceManager), + operatorAddress, + IAVSDirectory.OperatorAVSRegistrationStatus.REGISTERED + ); + } + } + + ( + uint32[] memory operatorSetsToCreate, + uint32[][] memory operatorSetIdsToMigrate, + address[] memory operators + ) = serviceManager.getOperatorsToMigrate(); + cheats.startPrank(serviceManagerOwner); + serviceManager.migrateAndCreateOperatorSetIds(operatorSetsToCreate); + serviceManager.migrateToOperatorSets(operatorSetIdsToMigrate, operators); + cheats.stopPrank(); + + bytes32[] memory registeredOperators = indexRegistry.getOperatorListAtBlockNumber(defaultQuorumNumber, uint32(block.number)); + uint256 preNumOperators = registeredOperators.length; + address[] memory registeredOperatorAddresses = new address[](registeredOperators.length); + for (uint256 i = 0; i < registeredOperators.length; i++) { + registeredOperatorAddresses[i] = registryCoordinator.blsApkRegistry().pubkeyHashToOperator(registeredOperators[i]); + } + + uint32[] memory operatorSetsToUnregister = new uint32[](1); + operatorSetsToUnregister[0] = defaultQuorumNumber; + + vm.prank(operators[0]); + avsDirectory.forceDeregisterFromOperatorSets( + operators[0], + address(serviceManager), + operatorSetsToUnregister, + ISignatureUtils.SignatureWithSaltAndExpiry({ + signature: new bytes(0), + salt: bytes32(0), + expiry: 0 + }) + ); + // sanity check if the operator was unregistered from the intended operator set + bool operatorIsUnRegistered = !avsDirectory.isMember(operators[0], IAVSDirectory.OperatorSet({ + avs: address(serviceManager), + operatorSetId: defaultQuorumNumber + })); + bool isOperatorSetAVS = avsDirectory.isOperatorSetAVS(address(serviceManager)); + assertTrue(isOperatorSetAVS, "ServiceManager is not an operator set AVS"); + assertTrue(operatorIsUnRegistered, "Operator wasnt unregistered from op set"); + + address[][] memory registeredOperatorAddresses2D = new address[][](1); + registeredOperatorAddresses2D[0] = registeredOperatorAddresses; + bytes memory quorumNumbers = new bytes(1); + quorumNumbers[0] = bytes1(defaultQuorumNumber); + registryCoordinator.updateOperatorsForQuorum(registeredOperatorAddresses2D, quorumNumbers); + + registeredOperators = indexRegistry.getOperatorListAtBlockNumber(defaultQuorumNumber, uint32(block.number)); + uint256 postRegisteredOperators = registeredOperators.length; + + assertEq(preNumOperators-1, postRegisteredOperators, ""); + + } } diff --git a/test/unit/StakeRegistryUnit.t.sol b/test/unit/StakeRegistryUnit.t.sol index ed53225c..56f244cf 100644 --- a/test/unit/StakeRegistryUnit.t.sol +++ b/test/unit/StakeRegistryUnit.t.sol @@ -52,7 +52,7 @@ contract StakeRegistryUnitTests is MockAVSDeployer, IStakeRegistryEvents { ); stakeRegistryImplementation = new StakeRegistryHarness( - IRegistryCoordinator(address(registryCoordinator)), delegationMock + IRegistryCoordinator(address(registryCoordinator)), delegationMock, avsDirectoryMock, serviceManager ); stakeRegistry = StakeRegistryHarness( diff --git a/test/utils/MockAVSDeployer.sol b/test/utils/MockAVSDeployer.sol index b3eebf1a..7ce4d5c4 100644 --- a/test/utils/MockAVSDeployer.sol +++ b/test/utils/MockAVSDeployer.sol @@ -222,7 +222,7 @@ contract MockAVSDeployer is Test { cheats.startPrank(proxyAdminOwner); stakeRegistryImplementation = - new StakeRegistryHarness(IRegistryCoordinator(registryCoordinator), delegationMock); + new StakeRegistryHarness(IRegistryCoordinator(registryCoordinator), delegationMock, avsDirectory, serviceManager); proxyAdmin.upgrade( TransparentUpgradeableProxy(payable(address(stakeRegistry))), From f6ad20ecedf810bb13b11aba18691195227e4d2f Mon Sep 17 00:00:00 2001 From: steven <12021290+stevennevins@users.noreply.github.com> Date: Thu, 22 Aug 2024 10:54:54 -0400 Subject: [PATCH 16/27] feat(op sets): register and deregister (#301) * feat: register and deregister to operator sets * fix: bytecode wrangling * chore: shorter errors to reduce bytecode * test: register after migration * chore: remove stale todos * chore: add back commented line * chore: remain consistent with m2 events * fix: side effect of merge from _deregister function * fix: bytecode massaging * chore: rename for clarity * chore: remove comments and whitespace * docs: add natspec to the library --- src/RegistryCoordinator.sol | 171 +++++++---------- src/RegistryCoordinatorStorage.sol | 7 +- src/ServiceManagerBase.sol | 26 +++ src/interfaces/IServiceManager.sol | 20 ++ src/libraries/BN254.sol | 18 +- src/libraries/QuorumBitmapHistoryLib.sol | 145 ++++++++++++++ .../RegistryCoordinatorHarness.t.sol | 5 +- test/integration/CoreRegistration.t.sol | 3 +- test/integration/IntegrationDeployer.t.sol | 2 +- test/mocks/ECDSAServiceManagerMock.sol | 8 + test/unit/OperatorStateRetrieverUnit.t.sol | 8 +- test/unit/RegistryCoordinatorMigration.t.sol | 179 ++++++++++++++++++ test/unit/RegistryCoordinatorUnit.t.sol | 30 +-- test/unit/StakeRegistryUnit.t.sol | 3 +- test/utils/MockAVSDeployer.sol | 2 +- 15 files changed, 494 insertions(+), 133 deletions(-) create mode 100644 src/libraries/QuorumBitmapHistoryLib.sol diff --git a/src/RegistryCoordinator.sol b/src/RegistryCoordinator.sol index 738213f3..818f0541 100644 --- a/src/RegistryCoordinator.sol +++ b/src/RegistryCoordinator.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.12; import {IPauserRegistry} from "eigenlayer-contracts/src/contracts/interfaces/IPauserRegistry.sol"; import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; -import {AVSDirectory} from "eigenlayer-contracts/src/contracts/core/AVSDirectory.sol"; +import {IAVSDirectory} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; import {ISocketUpdater} from "./interfaces/ISocketUpdater.sol"; import {IBLSApkRegistry} from "./interfaces/IBLSApkRegistry.sol"; import {IStakeRegistry} from "./interfaces/IStakeRegistry.sol"; @@ -14,6 +14,7 @@ import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol"; import {BitmapUtils} from "./libraries/BitmapUtils.sol"; import {BN254} from "./libraries/BN254.sol"; import {SignatureCheckerLib} from "./libraries/SignatureCheckerLib.sol"; +import {QuorumBitmapHistoryLib} from "./libraries/QuorumBitmapHistoryLib.sol"; import {OwnableUpgradeable} from "@openzeppelin-upgrades/contracts/access/OwnableUpgradeable.sol"; import {Initializable} from "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol"; @@ -58,9 +59,10 @@ contract RegistryCoordinator is IServiceManager _serviceManager, IStakeRegistry _stakeRegistry, IBLSApkRegistry _blsApkRegistry, - IIndexRegistry _indexRegistry + IIndexRegistry _indexRegistry, + IAVSDirectory _avsDirectory ) - RegistryCoordinatorStorage(_serviceManager, _stakeRegistry, _blsApkRegistry, _indexRegistry) + RegistryCoordinatorStorage(_serviceManager, _stakeRegistry, _blsApkRegistry, _indexRegistry, _avsDirectory) EIP712("AVSRegistryCoordinator", "v0.0.1") { _disableInitializers(); @@ -158,7 +160,7 @@ contract RegistryCoordinator is require( numOperatorsPerQuorum[i] <= _quorumParams[quorumNumber].maxOperatorCount, - "RegistryCoordinator.registerOperator: operator count exceeds maximum" + "RegistryCoordinator.registerOperator: operator exceeds max" ); } } @@ -333,7 +335,7 @@ contract RegistryCoordinator is // Prevent duplicate operators require( operator > prevOperatorAddress, - "RegistryCoordinator.updateOperatorsForQuorum: operators array must be sorted in ascending address order" + "RegistryCoordinator.updateOperatorsForQuorum: operators must be sorted" ); } @@ -355,7 +357,7 @@ contract RegistryCoordinator is function updateSocket(string memory socket) external { require( _operatorInfo[msg.sender].status == OperatorStatus.REGISTERED, - "RegistryCoordinator.updateSocket: operator is not registered" + "RegistryCoordinator.updateSocket: not registered" ); emit OperatorSocketUpdate(_operatorInfo[msg.sender].operatorId, socket); } @@ -486,7 +488,7 @@ contract RegistryCoordinator is uint192(BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers, quorumCount)); uint192 currentBitmap = _currentOperatorBitmap(operatorId); require( - !quorumsToAdd.isEmpty(), "RegistryCoordinator._registerOperator: bitmap cannot be 0" + !quorumsToAdd.isEmpty(), "RegistryCoordinator._registerOperator: bitmap empty" ); require( quorumsToAdd.noBitsInCommon(currentBitmap), @@ -515,9 +517,20 @@ contract RegistryCoordinator is OperatorInfo({operatorId: operatorId, status: OperatorStatus.REGISTERED}); // Register the operator with the EigenLayer core contracts via this AVS's ServiceManager - serviceManager.registerOperatorToAVS(operator, operatorSignature); + bool operatorSetAVS = avsDirectory.isOperatorSetAVS(address(serviceManager)); + if (operatorSetAVS){ + bytes memory quorumBytes = BitmapUtils.bitmapToBytesArray(quorumsToAdd); + uint32[] memory operatorSetIds = new uint32[](quorumBytes.length); + for (uint256 i = 0; i < quorumBytes.length; i++) { + operatorSetIds[i] = uint8(quorumBytes[i]); + } + serviceManager.registerOperatorToOperatorSets(operator, operatorSetIds, operatorSignature); + + } else { + serviceManager.registerOperatorToAVS(operator, operatorSignature); + emit OperatorRegistered(operator, operatorId); + } - emit OperatorRegistered(operator, operatorId); } // Register the operator with the BLSApkRegistry, StakeRegistry, and IndexRegistry @@ -534,7 +547,7 @@ contract RegistryCoordinator is * @dev Reverts if the caller is not the ejector */ function _checkEjector() internal view { - require(msg.sender == ejector, "RegistryCoordinator.onlyEjector: caller is not the ejector"); + require(msg.sender == ejector, "RegistryCoordinator.onlyEjector: not ejector"); } /** @@ -628,7 +641,7 @@ contract RegistryCoordinator is bytes32 operatorId = operatorInfo.operatorId; require( operatorInfo.status == OperatorStatus.REGISTERED, - "RegistryCoordinator._deregisterOperator: operator is not registered" + "RegistryCoordinator._deregisterOperator: not registered" ); /** @@ -647,19 +660,54 @@ contract RegistryCoordinator is ); require( quorumsToRemove.isSubsetOf(currentBitmap), - "RegistryCoordinator._deregisterOperator: operator is not registered for specified quorums" + "RegistryCoordinator._deregisterOperator: not registered for quorum" ); uint192 newBitmap = uint192(currentBitmap.minus(quorumsToRemove)); // Update operator's bitmap and status _updateOperatorBitmap({operatorId: operatorId, newBitmap: newBitmap}); - // If the operator is no longer registered for any quorums, update their status and deregister - // them from the AVS via the EigenLayer core contracts - if (newBitmap.isEmpty()) { - operatorInfo.status = OperatorStatus.DEREGISTERED; - serviceManager.deregisterOperatorFromAVS(operator); - emit OperatorDeregistered(operator, operatorId); + + bool operatorSetAVS = IAVSDirectory(serviceManager.avsDirectory()).isOperatorSetAVS(address(serviceManager)); + if (operatorSetAVS){ + bytes memory quorumBytes = BitmapUtils.bitmapToBytesArray(quorumsToRemove); + uint32[] memory operatorSetIds = new uint32[](quorumBytes.length); + uint256 forceDeregistrationCount; + for (uint256 i = 0; i < quorumBytes.length; i++) { + /// We need to track forceDeregistrations so we don't pass an id that was already deregistered on the AVSDirectory + /// but hasnt yet been recorded in the middleware contracts + if (!avsDirectory.isMember(operator, IAVSDirectory.OperatorSet(address(serviceManager), uint8(quorumBytes[i])))){ + forceDeregistrationCount++; + } + operatorSetIds[i] = uint8(quorumBytes[i]); + } + + /// Filter out forceDeregistration operator set Ids + if (forceDeregistrationCount > 0 ){ + uint32[] memory filteredOperatorSetIds = new uint32[](operatorSetIds.length - forceDeregistrationCount); + uint256 offset; + for (uint256 i; i < operatorSetIds.length; i++){ + if (avsDirectory.isMember(operator, IAVSDirectory.OperatorSet(address(serviceManager), operatorSetIds[i]))){ + filteredOperatorSetIds[i] = operatorSetIds[i+offset]; + } else { + offset++; + } + } + serviceManager.deregisterOperatorFromOperatorSets(operator, filteredOperatorSetIds); + } else { + serviceManager.deregisterOperatorFromOperatorSets(operator, operatorSetIds); + + } + + + } else { + // If the operator is no longer registered for any quorums, update their status and deregister + // them from the AVS via the EigenLayer core contracts + if (newBitmap.isEmpty()) { + operatorInfo.status = OperatorStatus.DEREGISTERED; + serviceManager.deregisterOperatorFromAVS(operator); + emit OperatorDeregistered(operator, operatorId); + } } // Deregister operator with each of the registry contracts @@ -726,11 +774,11 @@ contract RegistryCoordinator is // make sure the salt hasn't been used already require( !isChurnApproverSaltUsed[churnApproverSignature.salt], - "RegistryCoordinator._verifyChurnApproverSignature: churnApprover salt already used" + "RegistryCoordinator._verifyChurnApproverSignature: salt spent" ); require( churnApproverSignature.expiry >= block.timestamp, - "RegistryCoordinator._verifyChurnApproverSignature: churnApprover signature expired" + "RegistryCoordinator._verifyChurnApproverSignature: signature expired" ); // set salt used to true @@ -780,7 +828,6 @@ contract RegistryCoordinator is indexRegistry.initializeQuorum(quorumNumber); blsApkRegistry.initializeQuorum(quorumNumber); // Check if the AVS has migrated to operator sets - AVSDirectory avsDirectory = AVSDirectory(serviceManager.avsDirectory()); if (avsDirectory.isOperatorSetAVS(address(serviceManager))) { // Create an operator set for the new quorum uint32[] memory operatorSetIds = new uint32[](1); @@ -794,50 +841,13 @@ contract RegistryCoordinator is * @param newBitmap is the most up-to-date set of bitmaps the operator is registered for */ function _updateOperatorBitmap(bytes32 operatorId, uint192 newBitmap) internal { - uint256 historyLength = _operatorBitmapHistory[operatorId].length; - - if (historyLength == 0) { - // No prior bitmap history - push our first entry - _operatorBitmapHistory[operatorId].push( - QuorumBitmapUpdate({ - updateBlockNumber: uint32(block.number), - nextUpdateBlockNumber: 0, - quorumBitmap: newBitmap - }) - ); - } else { - // We have prior history - fetch our last-recorded update - QuorumBitmapUpdate storage lastUpdate = - _operatorBitmapHistory[operatorId][historyLength - 1]; - - /** - * If the last update was made in the current block, update the entry. - * Otherwise, push a new entry and update the previous entry's "next" field - */ - if (lastUpdate.updateBlockNumber == uint32(block.number)) { - lastUpdate.quorumBitmap = newBitmap; - } else { - lastUpdate.nextUpdateBlockNumber = uint32(block.number); - _operatorBitmapHistory[operatorId].push( - QuorumBitmapUpdate({ - updateBlockNumber: uint32(block.number), - nextUpdateBlockNumber: 0, - quorumBitmap: newBitmap - }) - ); - } - } + QuorumBitmapHistoryLib.updateOperatorBitmap(_operatorBitmapHistory, operatorId, newBitmap); } /// @notice Get the most recent bitmap for the operator, returning an empty bitmap if /// the operator is not registered. function _currentOperatorBitmap(bytes32 operatorId) internal view returns (uint192) { - uint256 historyLength = _operatorBitmapHistory[operatorId].length; - if (historyLength == 0) { - return 0; - } else { - return _operatorBitmapHistory[operatorId][historyLength - 1].quorumBitmap; - } + return QuorumBitmapHistoryLib.currentOperatorBitmap(_operatorBitmapHistory, operatorId); } /** @@ -849,21 +859,7 @@ contract RegistryCoordinator is uint32 blockNumber, bytes32 operatorId ) internal view returns (uint32 index) { - uint256 length = _operatorBitmapHistory[operatorId].length; - - // Traverse the operator's bitmap history in reverse, returning the first index - // corresponding to an update made before or at `blockNumber` - for (uint256 i = 0; i < length; i++) { - index = uint32(length - i - 1); - - if (_operatorBitmapHistory[operatorId][index].updateBlockNumber <= blockNumber) { - return index; - } - } - - revert( - "RegistryCoordinator.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operatorId at block number" - ); + return QuorumBitmapHistoryLib.getQuorumBitmapIndexAtBlockNumber(_operatorBitmapHistory,blockNumber, operatorId); } function _setOperatorSetParams( @@ -932,11 +928,7 @@ contract RegistryCoordinator is uint32 blockNumber, bytes32[] memory operatorIds ) external view returns (uint32[] memory) { - uint32[] memory indices = new uint32[](operatorIds.length); - for (uint256 i = 0; i < operatorIds.length; i++) { - indices[i] = _getQuorumBitmapIndexAtBlockNumber(blockNumber, operatorIds[i]); - } - return indices; + return QuorumBitmapHistoryLib.getQuorumBitmapIndicesAtBlockNumber(_operatorBitmapHistory, blockNumber, operatorIds); } /** @@ -950,24 +942,7 @@ contract RegistryCoordinator is uint32 blockNumber, uint256 index ) external view returns (uint192) { - QuorumBitmapUpdate memory quorumBitmapUpdate = _operatorBitmapHistory[operatorId][index]; - - /** - * Validate that the update is valid for the given blockNumber: - * - blockNumber should be >= the update block number - * - the next update block number should be either 0 or strictly greater than blockNumber - */ - require( - blockNumber >= quorumBitmapUpdate.updateBlockNumber, - "RegistryCoordinator.getQuorumBitmapAtBlockNumberByIndex: quorumBitmapUpdate is from after blockNumber" - ); - require( - quorumBitmapUpdate.nextUpdateBlockNumber == 0 - || blockNumber < quorumBitmapUpdate.nextUpdateBlockNumber, - "RegistryCoordinator.getQuorumBitmapAtBlockNumberByIndex: quorumBitmapUpdate is from before blockNumber" - ); - - return quorumBitmapUpdate.quorumBitmap; + return QuorumBitmapHistoryLib.getQuorumBitmapAtBlockNumberByIndex(_operatorBitmapHistory, operatorId, blockNumber, index); } /// @notice Returns the `index`th entry in the operator with `operatorId`'s bitmap history diff --git a/src/RegistryCoordinatorStorage.sol b/src/RegistryCoordinatorStorage.sol index 5451efb3..64486e93 100644 --- a/src/RegistryCoordinatorStorage.sol +++ b/src/RegistryCoordinatorStorage.sol @@ -5,6 +5,7 @@ import {IBLSApkRegistry} from "./interfaces/IBLSApkRegistry.sol"; import {IStakeRegistry} from "./interfaces/IStakeRegistry.sol"; import {IIndexRegistry} from "./interfaces/IIndexRegistry.sol"; import {IServiceManager} from "./interfaces/IServiceManager.sol"; +import {IAVSDirectory} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol"; abstract contract RegistryCoordinatorStorage is IRegistryCoordinator { @@ -39,6 +40,8 @@ abstract contract RegistryCoordinatorStorage is IRegistryCoordinator { IStakeRegistry public immutable stakeRegistry; /// @notice the Index Registry contract that will keep track of operators' indexes IIndexRegistry public immutable indexRegistry; + /// @notice the AVS Directory that tracks operator registrations to AVS and operator sets + IAVSDirectory public immutable avsDirectory; /******************************************************************************* STATE @@ -73,12 +76,14 @@ abstract contract RegistryCoordinatorStorage is IRegistryCoordinator { IServiceManager _serviceManager, IStakeRegistry _stakeRegistry, IBLSApkRegistry _blsApkRegistry, - IIndexRegistry _indexRegistry + IIndexRegistry _indexRegistry, + IAVSDirectory _avsDirectory ) { serviceManager = _serviceManager; stakeRegistry = _stakeRegistry; blsApkRegistry = _blsApkRegistry; indexRegistry = _indexRegistry; + avsDirectory = _avsDirectory; } // storage gap for upgradeability diff --git a/src/ServiceManagerBase.sol b/src/ServiceManagerBase.sol index f7f4a957..2fbacea2 100644 --- a/src/ServiceManagerBase.sol +++ b/src/ServiceManagerBase.sol @@ -133,6 +133,32 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage { _avsDirectory.deregisterOperatorFromAVS(operator); } + /** + * @notice Forwards a call to EigenLayer's AVSDirectory contract to register an operator to operator sets + * @param operator The address of the operator to register. + * @param operatorSetIds The IDs of the operator sets. + * @param operatorSignature The signature, salt, and expiry of the operator's signature. + */ + function registerOperatorToOperatorSets( + address operator, + uint32[] calldata operatorSetIds, + ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature + ) public virtual onlyRegistryCoordinator { + _avsDirectory.registerOperatorToOperatorSets(operator, operatorSetIds, operatorSignature); + } + + /** + * @notice Forwards a call to EigenLayer's AVSDirectory contract to deregister an operator from operator sets + * @param operator The address of the operator to deregister. + * @param operatorSetIds The IDs of the operator sets. + */ + function deregisterOperatorFromOperatorSets( + address operator, + uint32[] calldata operatorSetIds + ) public virtual onlyRegistryCoordinator { + _avsDirectory.deregisterOperatorFromOperatorSets(operator, operatorSetIds); + } + /** * @notice Sets the rewards initiator address * @param newRewardsInitiator The new rewards initiator address diff --git a/src/interfaces/IServiceManager.sol b/src/interfaces/IServiceManager.sol index 26f2c71b..8807e9df 100644 --- a/src/interfaces/IServiceManager.sol +++ b/src/interfaces/IServiceManager.sol @@ -3,6 +3,7 @@ pragma solidity >=0.5.0; import {IRewardsCoordinator} from "eigenlayer-contracts/src/contracts/interfaces/IRewardsCoordinator.sol"; import {IServiceManagerUI} from "./IServiceManagerUI.sol"; +import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; /** * @title Minimal interface for a ServiceManager-type contract that forms the single point for an AVS to push updates to EigenLayer @@ -24,6 +25,25 @@ interface IServiceManager is IServiceManagerUI { function createOperatorSets(uint32[] memory operatorSetIds) external ; + /** + * @notice Forwards a call to EigenLayer's AVSDirectory contract to register an operator to operator sets + * @param operator The address of the operator to register. + * @param operatorSetIds The IDs of the operator sets. + * @param operatorSignature The signature, salt, and expiry of the operator's signature. + */ + function registerOperatorToOperatorSets( + address operator, + uint32[] calldata operatorSetIds, + ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature + ) external; + + /** + * @notice Forwards a call to EigenLayer's AVSDirectory contract to deregister an operator from operator sets + * @param operator The address of the operator to deregister. + * @param operatorSetIds The IDs of the operator sets. + */ + function deregisterOperatorFromOperatorSets(address operator, uint32[] calldata operatorSetIds) external; + // EVENTS event RewardsInitiatorUpdated(address prevRewardsInitiator, address newRewardsInitiator); } diff --git a/src/libraries/BN254.sol b/src/libraries/BN254.sol index 0d8adb92..4d0edf70 100644 --- a/src/libraries/BN254.sol +++ b/src/libraries/BN254.sol @@ -46,7 +46,7 @@ library BN254 { uint256[2] Y; } - function generatorG1() internal pure returns (G1Point memory) { + function generatorG1() external pure returns (G1Point memory) { return G1Point(1, 2); } @@ -62,7 +62,7 @@ library BN254 { /// this is because of the (unknown to us) convention used in the bn254 pairing precompile contract /// "Elements a * i + b of F_p^2 are encoded as two elements of F_p, (a, b)." /// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-197.md#encoding - function generatorG2() internal pure returns (G2Point memory) { + function generatorG2() external pure returns (G2Point memory) { return G2Point([G2x1, G2x0], [G2y1, G2y0]); } @@ -73,7 +73,7 @@ library BN254 { uint256 internal constant nG2y1 = 17805874995975841540914202342111839520379459829704422454583296818431106115052; uint256 internal constant nG2y0 = 13392588948715843804641432497768002650278120570034223513918757245338268106653; - function negGeneratorG2() internal pure returns (G2Point memory) { + function negGeneratorG2() external pure returns (G2Point memory) { return G2Point([nG2x1, nG2x0], [nG2y1, nG2y0]); } @@ -84,7 +84,7 @@ library BN254 { * @param p Some point in G1. * @return The negation of `p`, i.e. p.plus(p.negate()) should be zero. */ - function negate(G1Point memory p) internal pure returns (G1Point memory) { + function negate(G1Point memory p) external pure returns (G1Point memory) { // The prime q in the base field F_q for G1 if (p.X == 0 && p.Y == 0) { return G1Point(0, 0); @@ -194,7 +194,7 @@ library BN254 { G2Point memory a2, G1Point memory b1, G2Point memory b2 - ) internal view returns (bool) { + ) external view returns (bool) { G1Point[2] memory p1 = [a1, b1]; G2Point[2] memory p2 = [a2, b2]; @@ -238,7 +238,7 @@ library BN254 { G1Point memory b1, G2Point memory b2, uint256 pairingGas - ) internal view returns (bool, bool) { + ) external view returns (bool, bool) { G1Point[2] memory p1 = [a1, b1]; G2Point[2] memory p2 = [a2, b2]; @@ -270,7 +270,7 @@ library BN254 { /// @return hashedG1 the keccak256 hash of the G1 Point /// @dev used for BLS signatures - function hashG1Point(BN254.G1Point memory pk) internal pure returns (bytes32 hashedG1) { + function hashG1Point(BN254.G1Point memory pk) external pure returns (bytes32 hashedG1) { assembly { mstore(0, mload(pk)) mstore(0x20, mload(add(0x20, pk))) @@ -282,14 +282,14 @@ library BN254 { /// @dev used for BLS signatures function hashG2Point( BN254.G2Point memory pk - ) internal pure returns (bytes32) { + ) external pure returns (bytes32) { return keccak256(abi.encodePacked(pk.X[0], pk.X[1], pk.Y[0], pk.Y[1])); } /** * @notice adapted from https://github.com/HarryR/solcrypto/blob/master/contracts/altbn128.sol */ - function hashToG1(bytes32 _x) internal view returns (G1Point memory) { + function hashToG1(bytes32 _x) external view returns (G1Point memory) { uint256 beta = 0; uint256 y = 0; diff --git a/src/libraries/QuorumBitmapHistoryLib.sol b/src/libraries/QuorumBitmapHistoryLib.sol new file mode 100644 index 00000000..4a3e72fd --- /dev/null +++ b/src/libraries/QuorumBitmapHistoryLib.sol @@ -0,0 +1,145 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.12; + +import {IRegistryCoordinator} from "../interfaces/IRegistryCoordinator.sol"; + +/// @title QuorumBitmapHistoryLib +/// @notice This library operates on the _operatorBitmapHistory in the RegistryCoordinator +library QuorumBitmapHistoryLib { + + /// @notice Retrieves the index of the quorum bitmap update at or before the specified block number + /// @param self The mapping of operator IDs to their quorum bitmap update history + /// @param blockNumber The block number to search for + /// @param operatorId The ID of the operator + /// @return index The index of the quorum bitmap update + function getQuorumBitmapIndexAtBlockNumber( + mapping(bytes32 => IRegistryCoordinator.QuorumBitmapUpdate[]) storage self, + uint32 blockNumber, + bytes32 operatorId + ) internal view returns (uint32 index) { + uint256 length = self[operatorId].length; + + // Traverse the operator's bitmap history in reverse, returning the first index + // corresponding to an update made before or at `blockNumber` + for (uint256 i = 0; i < length; i++) { + index = uint32(length - i - 1); + + if (self[operatorId][index].updateBlockNumber <= blockNumber) { + return index; + } + } + + revert( + "RegistryCoordinator.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operatorId" + ); + } + + /// @notice Retrieves the current quorum bitmap for the given operator ID + /// @param self The mapping of operator IDs to their quorum bitmap update history + /// @param operatorId The ID of the operator + /// @return The current quorum bitmap + function currentOperatorBitmap( + mapping(bytes32 => IRegistryCoordinator.QuorumBitmapUpdate[]) storage self, + bytes32 operatorId + ) external view returns (uint192) { + uint256 historyLength = self[operatorId].length; + if (historyLength == 0) { + return 0; + } else { + return self[operatorId][historyLength - 1].quorumBitmap; + } + } + + /// @notice Retrieves the indices of the quorum bitmap updates for the given operator IDs at the specified block number + /// @param self The mapping of operator IDs to their quorum bitmap update history + /// @param blockNumber The block number to search for + /// @param operatorIds The array of operator IDs + /// @return An array of indices corresponding to the quorum bitmap updates + function getQuorumBitmapIndicesAtBlockNumber( + mapping(bytes32 => IRegistryCoordinator.QuorumBitmapUpdate[]) storage self, + uint32 blockNumber, + bytes32[] memory operatorIds + ) external view returns (uint32[] memory) { + uint32[] memory indices = new uint32[](operatorIds.length); + for (uint256 i = 0; i < operatorIds.length; i++) { + indices[i] = getQuorumBitmapIndexAtBlockNumber(self, blockNumber, operatorIds[i]); + } + return indices; + } + + /// @notice Retrieves the quorum bitmap for the given operator ID at the specified block number and index + /// @param self The mapping of operator IDs to their quorum bitmap update history + /// @param operatorId The ID of the operator + /// @param blockNumber The block number to validate against + /// @param index The index of the quorum bitmap update + /// @return The quorum bitmap at the specified index + function getQuorumBitmapAtBlockNumberByIndex( + mapping(bytes32 => IRegistryCoordinator.QuorumBitmapUpdate[]) storage self, + bytes32 operatorId, + uint32 blockNumber, + uint256 index + ) external view returns (uint192) { + IRegistryCoordinator.QuorumBitmapUpdate memory quorumBitmapUpdate = self[operatorId][index]; + + /** + * Validate that the update is valid for the given blockNumber: + * - blockNumber should be >= the update block number + * - the next update block number should be either 0 or strictly greater than blockNumber + */ + require( + blockNumber >= quorumBitmapUpdate.updateBlockNumber, + "RegistryCoordinator.getQuorumBitmapAtBlockNumberByIndex: quorumBitmapUpdate is from after blockNumber" + ); + require( + quorumBitmapUpdate.nextUpdateBlockNumber == 0 + || blockNumber < quorumBitmapUpdate.nextUpdateBlockNumber, + "RegistryCoordinator.getQuorumBitmapAtBlockNumberByIndex: quorumBitmapUpdate is from before blockNumber" + ); + + return quorumBitmapUpdate.quorumBitmap; + } + + /// @notice Updates the quorum bitmap for the given operator ID + /// @param self The mapping of operator IDs to their quorum bitmap update history + /// @param operatorId The ID of the operator + /// @param newBitmap The new quorum bitmap to set + function updateOperatorBitmap( + mapping(bytes32 => IRegistryCoordinator.QuorumBitmapUpdate[]) storage self, + bytes32 operatorId, + uint192 newBitmap + ) external { + uint256 historyLength = self[operatorId].length; + + if (historyLength == 0) { + // No prior bitmap history - push our first entry + self[operatorId].push( + IRegistryCoordinator.QuorumBitmapUpdate({ + updateBlockNumber: uint32(block.number), + nextUpdateBlockNumber: 0, + quorumBitmap: newBitmap + }) + ); + } else { + // We have prior history - fetch our last-recorded update + IRegistryCoordinator.QuorumBitmapUpdate storage lastUpdate = + self[operatorId][historyLength - 1]; + + /** + * If the last update was made in the current block, update the entry. + * Otherwise, push a new entry and update the previous entry's "next" field + */ + if (lastUpdate.updateBlockNumber == uint32(block.number)) { + lastUpdate.quorumBitmap = newBitmap; + } else { + lastUpdate.nextUpdateBlockNumber = uint32(block.number); + self[operatorId].push( + IRegistryCoordinator.QuorumBitmapUpdate({ + updateBlockNumber: uint32(block.number), + nextUpdateBlockNumber: 0, + quorumBitmap: newBitmap + }) + ); + } + } + } +} diff --git a/test/harnesses/RegistryCoordinatorHarness.t.sol b/test/harnesses/RegistryCoordinatorHarness.t.sol index d7ae81ae..4644c276 100644 --- a/test/harnesses/RegistryCoordinatorHarness.t.sol +++ b/test/harnesses/RegistryCoordinatorHarness.t.sol @@ -11,8 +11,9 @@ contract RegistryCoordinatorHarness is RegistryCoordinator, Test { IServiceManager _serviceManager, IStakeRegistry _stakeRegistry, IBLSApkRegistry _blsApkRegistry, - IIndexRegistry _indexRegistry - ) RegistryCoordinator(_serviceManager, _stakeRegistry, _blsApkRegistry, _indexRegistry) { + IIndexRegistry _indexRegistry, + IAVSDirectory _avsDirectory + ) RegistryCoordinator(_serviceManager, _stakeRegistry, _blsApkRegistry, _indexRegistry, _avsDirectory) { _transferOwnership(msg.sender); } diff --git a/test/integration/CoreRegistration.t.sol b/test/integration/CoreRegistration.t.sol index 5998025c..f6cbf571 100644 --- a/test/integration/CoreRegistration.t.sol +++ b/test/integration/CoreRegistration.t.sol @@ -79,7 +79,8 @@ contract Test_CoreRegistration is MockAVSDeployer { serviceManager, stakeRegistry, blsApkRegistry, - indexRegistry + indexRegistry, + avsDirectory ); // Upgrade Registry Coordinator & ServiceManager diff --git a/test/integration/IntegrationDeployer.t.sol b/test/integration/IntegrationDeployer.t.sol index 4f6bd719..ee3722a7 100644 --- a/test/integration/IntegrationDeployer.t.sol +++ b/test/integration/IntegrationDeployer.t.sol @@ -341,7 +341,7 @@ abstract contract IntegrationDeployer is Test, IUserDeployer { }); RegistryCoordinator registryCoordinatorImplementation = - new RegistryCoordinator(serviceManager, stakeRegistry, blsApkRegistry, indexRegistry); + new RegistryCoordinator(serviceManager, stakeRegistry, blsApkRegistry, indexRegistry, avsDirectory); proxyAdmin.upgradeAndCall( TransparentUpgradeableProxy(payable(address(registryCoordinator))), address(registryCoordinatorImplementation), diff --git a/test/mocks/ECDSAServiceManagerMock.sol b/test/mocks/ECDSAServiceManagerMock.sol index e38e0395..88e3ac6f 100644 --- a/test/mocks/ECDSAServiceManagerMock.sol +++ b/test/mocks/ECDSAServiceManagerMock.sol @@ -26,4 +26,12 @@ contract ECDSAServiceManagerMock is ECDSAServiceManagerBase { } function createOperatorSets(uint32[] memory) external {} + + function registerOperatorToOperatorSets( + address operator, + uint32[] calldata operatorSetIds, + ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature + ) external {} + + function deregisterOperatorFromOperatorSets(address operator, uint32[] calldata operatorSetIds) external{} } diff --git a/test/unit/OperatorStateRetrieverUnit.t.sol b/test/unit/OperatorStateRetrieverUnit.t.sol index ba4fe75e..78b634d7 100644 --- a/test/unit/OperatorStateRetrieverUnit.t.sol +++ b/test/unit/OperatorStateRetrieverUnit.t.sol @@ -13,7 +13,7 @@ contract OperatorStateRetrieverUnitTests is MockAVSDeployer { function test_getOperatorState_revert_neverRegistered() public { cheats.expectRevert( - "RegistryCoordinator.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operatorId at block number" + "RegistryCoordinator.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operatorId" ); operatorStateRetriever.getOperatorState( registryCoordinator, defaultOperatorId, uint32(block.number) @@ -26,7 +26,7 @@ contract OperatorStateRetrieverUnitTests is MockAVSDeployer { // should revert because the operator was registered for the first time after the reference block number cheats.expectRevert( - "RegistryCoordinator.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operatorId at block number" + "RegistryCoordinator.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operatorId" ); operatorStateRetriever.getOperatorState( registryCoordinator, defaultOperatorId, registrationBlockNumber - 1 @@ -143,7 +143,7 @@ contract OperatorStateRetrieverUnitTests is MockAVSDeployer { nonSignerOperatorIds[0] = defaultOperatorId; cheats.expectRevert( - "RegistryCoordinator.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operatorId at block number" + "RegistryCoordinator.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operatorId" ); operatorStateRetriever.getCheckSignaturesIndices( registryCoordinator, @@ -164,7 +164,7 @@ contract OperatorStateRetrieverUnitTests is MockAVSDeployer { // should revert because the operator was registered for the first time after the reference block number cheats.expectRevert( - "RegistryCoordinator.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operatorId at block number" + "RegistryCoordinator.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operatorId" ); operatorStateRetriever.getCheckSignaturesIndices( registryCoordinator, diff --git a/test/unit/RegistryCoordinatorMigration.t.sol b/test/unit/RegistryCoordinatorMigration.t.sol index 357af6d2..99fd38f4 100644 --- a/test/unit/RegistryCoordinatorMigration.t.sol +++ b/test/unit/RegistryCoordinatorMigration.t.sol @@ -7,6 +7,7 @@ import { IRewardsCoordinator, IERC20 } from "eigenlayer-contracts/src/contracts/core/RewardsCoordinator.sol"; +import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; import {StrategyBase} from "eigenlayer-contracts/src/contracts/strategies/StrategyBase.sol"; import {IServiceManagerBaseEvents} from "../events/IServiceManagerBaseEvents.sol"; import {AVSDirectoryHarness} from "../harnesses/AVSDirectoryHarness.sol"; @@ -282,4 +283,182 @@ contract RegistryCoordinatorMigrationUnit is MockAVSDeployer, IServiceManagerBas assertEq(preNumOperators-1, postRegisteredOperators, ""); } + + function test_deregister_afterMigration() public { + vm.prank(proxyAdmin.owner()); + proxyAdmin.upgrade( + TransparentUpgradeableProxy(payable(address(avsDirectory))), + address(avsDirectoryMock) + ); + uint256 pseudoRandomNumber = uint256(keccak256("pseudoRandomNumber")); + _registerRandomOperators(pseudoRandomNumber); + + vm.prank(proxyAdmin.owner()); + proxyAdmin.upgrade( + TransparentUpgradeableProxy(payable(address(avsDirectory))), + address(avsDirectoryHarness) + ); + + uint256 quorumCount = registryCoordinator.quorumCount(); + for (uint256 i = 0; i < quorumCount; i++) { + uint256 operatorCount = indexRegistry.totalOperatorsForQuorum(uint8(i)); + bytes32[] memory operatorIds = + indexRegistry.getOperatorListAtBlockNumber(uint8(i), uint32(block.number)); + assertEq(operatorCount, operatorIds.length, "Operator Id length mismatch"); // sanity check + for (uint256 j = 0; j < operatorCount; j++) { + address operatorAddress = + registryCoordinator.blsApkRegistry().getOperatorFromPubkeyHash(operatorIds[j]); + AVSDirectoryHarness(address(avsDirectory)).setAvsOperatorStatus( + address(serviceManager), + operatorAddress, + IAVSDirectory.OperatorAVSRegistrationStatus.REGISTERED + ); + } + } + + ( + uint32[] memory operatorSetsToCreate, + uint32[][] memory operatorSetIdsToMigrate, + address[] memory operators + ) = serviceManager.getOperatorsToMigrate(); + cheats.startPrank(serviceManagerOwner); + serviceManager.migrateAndCreateOperatorSetIds(operatorSetsToCreate); + serviceManager.migrateToOperatorSets(operatorSetIdsToMigrate, operators); + cheats.stopPrank(); + + bytes32[] memory registeredOperators = indexRegistry.getOperatorListAtBlockNumber(defaultQuorumNumber, uint32(block.number)); + address[] memory registeredOperatorAddresses = new address[](registeredOperators.length); + for (uint256 i = 0; i < registeredOperators.length; i++) { + registeredOperatorAddresses[i] = registryCoordinator.blsApkRegistry().pubkeyHashToOperator(registeredOperators[i]); + } + + uint32[] memory operatorSetsToUnregister = new uint32[](1); + operatorSetsToUnregister[0] = defaultQuorumNumber; + + address operatorToDeregister = operators[0]; + + bool isOperatorRegistered = avsDirectory.isMember(operatorToDeregister, IAVSDirectory.OperatorSet({ + avs: address(serviceManager), + operatorSetId: defaultQuorumNumber + })); + bool isOperatorSetAVS = avsDirectory.isOperatorSetAVS(address(serviceManager)); + // sanity check if the operator was registered from the intended operator set + assertTrue(isOperatorSetAVS, "ServiceManager is not an operator set AVS"); + assertTrue(isOperatorRegistered, "Operator wasnt unregistered from op set"); + + bytes memory quorumNumbers = new bytes(1); + quorumNumbers[0] = bytes1(defaultQuorumNumber); + cheats.startPrank(operatorToDeregister); + registryCoordinator.deregisterOperator(quorumNumbers); + cheats.stopPrank(); + + isOperatorRegistered = avsDirectory.isMember(operatorToDeregister, IAVSDirectory.OperatorSet({ + avs: address(serviceManager), + operatorSetId: defaultQuorumNumber + })); + assertFalse(isOperatorRegistered, "Operator wasn't deregistered from operator set"); + } + + function test_register_afterMigration() public { + vm.prank(proxyAdmin.owner()); + proxyAdmin.upgrade( + TransparentUpgradeableProxy(payable(address(avsDirectory))), + address(avsDirectoryMock) + ); + uint256 pseudoRandomNumber = uint256(keccak256("pseudoRandomNumber")); + _registerRandomOperators(pseudoRandomNumber); + + vm.prank(proxyAdmin.owner()); + proxyAdmin.upgrade( + TransparentUpgradeableProxy(payable(address(avsDirectory))), + address(avsDirectoryHarness) + ); + + uint256 quorumCount = registryCoordinator.quorumCount(); + for (uint256 i = 0; i < quorumCount; i++) { + uint256 operatorCount = indexRegistry.totalOperatorsForQuorum(uint8(i)); + bytes32[] memory operatorIds = + indexRegistry.getOperatorListAtBlockNumber(uint8(i), uint32(block.number)); + assertEq(operatorCount, operatorIds.length, "Operator Id length mismatch"); // sanity check + for (uint256 j = 0; j < operatorCount; j++) { + address operatorAddress = + registryCoordinator.blsApkRegistry().getOperatorFromPubkeyHash(operatorIds[j]); + AVSDirectoryHarness(address(avsDirectory)).setAvsOperatorStatus( + address(serviceManager), + operatorAddress, + IAVSDirectory.OperatorAVSRegistrationStatus.REGISTERED + ); + } + } + + ( + uint32[] memory operatorSetsToCreate, + uint32[][] memory operatorSetIdsToMigrate, + address[] memory operators + ) = serviceManager.getOperatorsToMigrate(); + cheats.startPrank(serviceManagerOwner); + serviceManager.migrateAndCreateOperatorSetIds(operatorSetsToCreate); + serviceManager.migrateToOperatorSets(operatorSetIdsToMigrate, operators); + cheats.stopPrank(); + + bytes32[] memory registeredOperators = indexRegistry.getOperatorListAtBlockNumber(defaultQuorumNumber, uint32(block.number)); + address[] memory registeredOperatorAddresses = new address[](registeredOperators.length); + for (uint256 i = 0; i < registeredOperators.length; i++) { + registeredOperatorAddresses[i] = registryCoordinator.blsApkRegistry().pubkeyHashToOperator(registeredOperators[i]); + } + + uint32[] memory operatorSetsToRegisterFor = new uint32[](1); + operatorSetsToRegisterFor[0] = defaultQuorumNumber; + + uint256 operatorPk = uint256(keccak256("operator to register")); + address operatorToRegister = vm.addr(operatorPk) ; + + bool isOperatorRegistered = avsDirectory.isMember(operatorToRegister, IAVSDirectory.OperatorSet({ + avs: address(serviceManager), + operatorSetId: defaultQuorumNumber + })); + bool isOperatorSetAVS = avsDirectory.isOperatorSetAVS(address(serviceManager)); + // sanity check if the operator was registered from the intended operator set + assertTrue(isOperatorSetAVS, "ServiceManager is not an operator set AVS"); + assertTrue(!isOperatorRegistered, "Operator wasnt unregistered from op set"); + + IDelegationManager.OperatorDetails memory details; + + cheats.startPrank(operatorToRegister); + delegationMock.registerAsOperator(details, "your_metadata_URI_here"); + cheats.stopPrank(); + + delegationMock.setIsOperator(operatorToRegister, true); + + bytes memory quorumNumbers = new bytes(1); + IBLSApkRegistry.PubkeyRegistrationParams memory params; + ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature; + quorumNumbers[0] = bytes1(defaultQuorumNumber); + bytes32 typeHash = avsDirectory.calculateOperatorSetRegistrationDigestHash( + address(serviceManager), + operatorSetsToRegisterFor, + keccak256(abi.encodePacked("operator registration salt")), + block.timestamp + 1 days + ); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(operatorPk, typeHash); + operatorSignature = ISignatureUtils.SignatureWithSaltAndExpiry({ + signature: abi.encodePacked(r, s, v), + salt: keccak256(abi.encodePacked("operator registration salt")), + expiry: block.timestamp + 1 days + }); + + blsApkRegistry.setBLSPublicKey(operatorToRegister, defaultPubKey); + delegationMock.setOperatorShares(operatorToRegister, IStrategy(address(0)), 100 ether); + cheats.startPrank(operatorToRegister); + registryCoordinator.registerOperator(quorumNumbers, "", params, operatorSignature); + cheats.stopPrank(); + + isOperatorRegistered = avsDirectory.isMember(operatorToRegister, IAVSDirectory.OperatorSet({ + avs: address(serviceManager), + operatorSetId: defaultQuorumNumber + })); + assertTrue(isOperatorRegistered, "Operator wasn't deregistered from operator set"); + } + + } diff --git a/test/unit/RegistryCoordinatorUnit.t.sol b/test/unit/RegistryCoordinatorUnit.t.sol index c262b0da..6ae55d59 100644 --- a/test/unit/RegistryCoordinatorUnit.t.sol +++ b/test/unit/RegistryCoordinatorUnit.t.sol @@ -197,7 +197,7 @@ contract RegistryCoordinatorUnitTests_Initialization_Setters is RegistryCoordina function test_updateSocket_revert_notRegistered() public { cheats.prank(defaultOperator); - cheats.expectRevert("RegistryCoordinator.updateSocket: operator is not registered"); + cheats.expectRevert("RegistryCoordinator.updateSocket: not registered"); registryCoordinator.updateSocket("localhost:32004"); } @@ -268,7 +268,7 @@ contract RegistryCoordinatorUnitTests_RegisterOperator is RegistryCoordinatorUni bytes memory emptyQuorumNumbers = new bytes(0); ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; - cheats.expectRevert("RegistryCoordinator._registerOperator: bitmap cannot be 0"); + cheats.expectRevert("RegistryCoordinator._registerOperator: bitmap empty"); cheats.prank(defaultOperator); registryCoordinator.registerOperator(emptyQuorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); } @@ -482,7 +482,7 @@ contract RegistryCoordinatorUnitTests_RegisterOperator is RegistryCoordinatorUni _setOperatorWeight(operatorToRegister, defaultQuorumNumber, defaultStake); cheats.prank(operatorToRegister); - cheats.expectRevert("RegistryCoordinator.registerOperator: operator count exceeds maximum"); + cheats.expectRevert("RegistryCoordinator.registerOperator: operator exceeds max"); registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); } @@ -512,7 +512,7 @@ contract RegistryCoordinatorUnitTests_RegisterOperator is RegistryCoordinatorUni bytes memory emptyQuorumNumbers = new bytes(0); ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; - cheats.expectRevert("RegistryCoordinator._registerOperator: bitmap cannot be 0"); + cheats.expectRevert("RegistryCoordinator._registerOperator: bitmap empty"); registryCoordinator._registerOperatorExternal(defaultOperator, defaultOperatorId, emptyQuorumNumbers, defaultSocket, emptySig); } @@ -600,7 +600,7 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist bytes memory quorumNumbers = new bytes(1); quorumNumbers[0] = bytes1(defaultQuorumNumber); - cheats.expectRevert("RegistryCoordinator._deregisterOperator: operator is not registered"); + cheats.expectRevert("RegistryCoordinator._deregisterOperator: not registered"); cheats.prank(defaultOperator); registryCoordinator.deregisterOperator(quorumNumbers); } @@ -616,7 +616,7 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist quorumNumbers[0] = bytes1(defaultQuorumNumber + 1); quorumNumbers[1] = bytes1(defaultQuorumNumber + 2); - cheats.expectRevert("RegistryCoordinator._deregisterOperator: operator is not registered for specified quorums"); + cheats.expectRevert("RegistryCoordinator._deregisterOperator: not registered for quorum"); cheats.prank(defaultOperator); registryCoordinator.deregisterOperator(quorumNumbers); } @@ -962,7 +962,7 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist function test_deregisterOperatorExternal_revert_notRegistered() public { bytes memory emptyQuorumNumbers = new bytes(0); - cheats.expectRevert("RegistryCoordinator._deregisterOperator: operator is not registered"); + cheats.expectRevert("RegistryCoordinator._deregisterOperator: not registered"); registryCoordinator._deregisterOperatorExternal(defaultOperator, emptyQuorumNumbers); } @@ -984,7 +984,7 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist incorrectQuorum[0] = bytes1(defaultQuorumNumber + 1); cheats.roll(deregistrationBlockNumber); - cheats.expectRevert("RegistryCoordinator._deregisterOperator: operator is not registered for specified quorums"); + cheats.expectRevert("RegistryCoordinator._deregisterOperator: not registered for quorum"); registryCoordinator._deregisterOperatorExternal(defaultOperator, incorrectQuorum); } @@ -1213,7 +1213,7 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist cheats.prank(defaultOperator); registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); - cheats.expectRevert("RegistryCoordinator.onlyEjector: caller is not the ejector"); + cheats.expectRevert("RegistryCoordinator.onlyEjector: not ejector"); cheats.prank(defaultOperator); registryCoordinator.ejectOperator(defaultOperator, quorumNumbers); } @@ -1221,7 +1221,7 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist function test_getQuorumBitmapIndicesAtBlockNumber_revert_notRegistered() public { uint32 blockNumber; bytes32[] memory operatorIds = new bytes32[](1); - cheats.expectRevert("RegistryCoordinator.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operatorId at block number"); + cheats.expectRevert("RegistryCoordinator.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operatorId"); registryCoordinator.getQuorumBitmapIndicesAtBlockNumber(blockNumber, operatorIds); } @@ -1242,7 +1242,7 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist operatorIds[0] = defaultOperatorId; uint32[] memory returnArray; - cheats.expectRevert("RegistryCoordinator.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operatorId at block number"); + cheats.expectRevert("RegistryCoordinator.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operatorId"); registryCoordinator.getQuorumBitmapIndicesAtBlockNumber(blockNumber, operatorIds); blockNumber = registrationBlockNumber; @@ -1264,7 +1264,7 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist operatorIds[0] = defaultOperatorId; uint32[] memory returnArray; - cheats.expectRevert("RegistryCoordinator.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operatorId at block number"); + cheats.expectRevert("RegistryCoordinator.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operatorId"); registryCoordinator.getQuorumBitmapIndicesAtBlockNumber(blockNumber, operatorIds); blockNumber = registrationBlockNumber; @@ -1556,7 +1556,7 @@ contract RegistryCoordinatorUnitTests_RegisterOperatorWithChurn is RegistryCoord ISignatureUtils.SignatureWithSaltAndExpiry memory signatureWithSaltAndExpiry = _signOperatorChurnApproval(operatorToRegister, operatorToRegisterId, operatorKickParams, defaultSalt, block.timestamp - 1); cheats.prank(operatorToRegister); - cheats.expectRevert("RegistryCoordinator._verifyChurnApproverSignature: churnApprover signature expired"); + cheats.expectRevert("RegistryCoordinator._verifyChurnApproverSignature: signature expired"); registryCoordinator.registerOperatorWithChurn( quorumNumbers, defaultSocket, @@ -1738,7 +1738,7 @@ contract RegistryCoordinatorUnitTests_UpdateOperators is RegistryCoordinatorUnit operatorsToUpdate[0] = operatorArray; // note: there is not an explicit check for duplicates, as checking for explicit ordering covers this - cheats.expectRevert(bytes("RegistryCoordinator.updateOperatorsForQuorum: operators array must be sorted in ascending address order")); + cheats.expectRevert(bytes("RegistryCoordinator.updateOperatorsForQuorum: operators must be sorted")); registryCoordinator.updateOperatorsForQuorum(operatorsToUpdate, quorumNumbers); } @@ -1764,7 +1764,7 @@ contract RegistryCoordinatorUnitTests_UpdateOperators is RegistryCoordinatorUnit operatorArray[1] = defaultOperator; operatorsToUpdate[0] = operatorArray; - cheats.expectRevert(bytes("RegistryCoordinator.updateOperatorsForQuorum: operators array must be sorted in ascending address order")); + cheats.expectRevert(bytes("RegistryCoordinator.updateOperatorsForQuorum: operators must be sorted")); registryCoordinator.updateOperatorsForQuorum(operatorsToUpdate, quorumNumbers); } diff --git a/test/unit/StakeRegistryUnit.t.sol b/test/unit/StakeRegistryUnit.t.sol index 56f244cf..71348e1b 100644 --- a/test/unit/StakeRegistryUnit.t.sol +++ b/test/unit/StakeRegistryUnit.t.sol @@ -48,7 +48,8 @@ contract StakeRegistryUnitTests is MockAVSDeployer, IStakeRegistryEvents { serviceManager, stakeRegistry, IBLSApkRegistry(blsApkRegistry), - IIndexRegistry(indexRegistry) + IIndexRegistry(indexRegistry), + IAVSDirectory(avsDirectory) ); stakeRegistryImplementation = new StakeRegistryHarness( diff --git a/test/utils/MockAVSDeployer.sol b/test/utils/MockAVSDeployer.sol index 7ce4d5c4..b06364a8 100644 --- a/test/utils/MockAVSDeployer.sol +++ b/test/utils/MockAVSDeployer.sol @@ -280,7 +280,7 @@ contract MockAVSDeployer is Test { } registryCoordinatorImplementation = new RegistryCoordinatorHarness( - serviceManager, stakeRegistry, blsApkRegistry, indexRegistry + serviceManager, stakeRegistry, blsApkRegistry, indexRegistry, avsDirectory ); { delete operatorSetParams; From e0a79f11076ae2e2beb0866030cc916ca4ff066a Mon Sep 17 00:00:00 2001 From: steven <12021290+stevennevins@users.noreply.github.com> Date: Thu, 22 Aug 2024 11:05:38 -0400 Subject: [PATCH 17/27] feat(op sets): upgrade and migrate script (#303) * feat: upgrade and test pre prod upgrade and migration * fix: add param introduced in merge --- foundry.toml | 4 + script/OperatorSetUpgrade.s.sol | 255 ++++++++++++++++++ script/utils/UpgradeLib.sol | 40 +++ .../utils/testdata/17000/core_testdata.json | 23 ++ .../testdata/17000/middlware_testdata.json | 18 ++ 5 files changed, 340 insertions(+) create mode 100644 script/OperatorSetUpgrade.s.sol create mode 100644 script/utils/UpgradeLib.sol create mode 100644 script/utils/testdata/17000/core_testdata.json create mode 100644 script/utils/testdata/17000/middlware_testdata.json diff --git a/foundry.toml b/foundry.toml index 4e13d2e8..fd0aac0f 100644 --- a/foundry.toml +++ b/foundry.toml @@ -17,6 +17,10 @@ via_ir = false # Override the Solidity version (this overrides `auto_detect_solc`) solc_version = '0.8.12' +[etherscan] +mainnet = { key = "${ETHERSCAN_API_KEY}" } +holesky = { key = "${ETHERSCAN_API_KEY}" } + [fmt] bracket_spacing = false int_types = "long" diff --git a/script/OperatorSetUpgrade.s.sol b/script/OperatorSetUpgrade.s.sol new file mode 100644 index 00000000..39158e0a --- /dev/null +++ b/script/OperatorSetUpgrade.s.sol @@ -0,0 +1,255 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import {Script, console2} from "forge-std/Script.sol"; +import {OperatorSetUpgradeLib} from "./utils/UpgradeLib.sol"; +import {stdJson} from "forge-std/StdJson.sol"; +import {ServiceManagerMock, IServiceManager} from "../test/mocks/ServiceManagerMock.sol"; +import {StakeRegistry, IStakeRegistry} from "../src/StakeRegistry.sol"; +import {RegistryCoordinator, IRegistryCoordinator} from "../src/RegistryCoordinator.sol"; +import {AVSDirectory} from "eigenlayer-contracts/src/contracts/core/AVSDirectory.sol"; +import {IBLSApkRegistry} from "../src/interfaces/IBLSApkRegistry.sol"; +import {IIndexRegistry} from "../src/interfaces/IIndexRegistry.sol"; +import {IRewardsCoordinator} from "eigenlayer-contracts/src/contracts/interfaces/IRewardsCoordinator.sol"; +import {IAVSDirectory} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; +import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; +interface IServiceManagerMigration { + function getOperatorsToMigrate() + external + view + returns ( + uint32[] memory operatorSetIdsToCreate, + uint32[][] memory operatorSetIds, + address[] memory allOperators + ); + function migrateAndCreateOperatorSetIds(uint32[] memory operatorSetsToCreate) external; + function migrateToOperatorSets(uint32[][] memory operatorSetIds, address[] memory operators) external; + function finalizeMigration() external; + function migrationFinalized() external returns (bool); +} + + +import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; + +contract OperatorSetUpgradeScript is Script { + using stdJson for string; + + address private constant DEFAULT_FORGE_SENDER = 0x1804c8AB1F12E6bbf3894d4083f33e07309d1f38; + + address public proxyAdminOwner; + address public serviceManagerOwner; + address public serviceManager; + address public stakeRegistry; + address public registryCoordinator; + address public avsDirectory; + address public rewardsCoordinator; + address public delegationManager; + address public blsApkRegistry; + address public indexRegistry; + + function setUp() public { + vm.label(DEFAULT_FORGE_SENDER, "DEFAULT FORGE SENDER"); + + // Note: Ensure that the following environment variables are set before running the script: + // - PROXY_ADMIN_OWNER: The private key of the proxy admin owner. + // - SERVICE_MANAGER_OWNER: The private key of the service manager owner. + // These environment variables are crucial for the proper execution of the upgrade and migration processes. + /// TODO: improve DEVX of gnosis safe. Would like to do an tx service integration for SafeAPI + proxyAdminOwner = vm.rememberKey(vm.envUint("PROXY_ADMIN_OWNER")); + serviceManagerOwner = vm.rememberKey(vm.envUint("PROXY_ADMIN_OWNER")); + + string memory middlewareJson = vm.readFile(vm.envString("MIDDLEWARE_JSON_PATH")); + string memory coreJson = vm.readFile(vm.envString("CORE_JSON_PATH")); + + /* + * Note: Ensure that the structure of the configuration JSON files matches the structure + * of `core_testdata.json`. If you rename any of the files, you will need to update the + * corresponding key values in the code. + */ + loadAddressesSetup(middlewareJson, coreJson); + labelAndLogAddressesSetup(); + } + + function run() public { + vm.startBroadcast(proxyAdminOwner); + + _upgrade(); + + vm.stopBroadcast(); + + vm.startBroadcast(serviceManagerOwner); + + _migrateToOperatorSets(); + + vm.stopBroadcast(); + } + // forge script script/OperatorSetUpgrade.s.sol --sig "simulateUpgrade()" -vvv + function simulateUpgrade() public { + + address proxyAdmin = OperatorSetUpgradeLib.getAdmin(serviceManager); + proxyAdminOwner = Ownable(proxyAdmin).owner(); + vm.startPrank(proxyAdminOwner); + + _upgrade(); + + vm.stopPrank(); + + } + + // forge script script/OperatorSetUpgrade.s.sol --sig "simulateMigrate()" -vvv + function simulateMigrate() public { + _upgradeAvsDirectory(); /// Workaround since this isn't on pre-prod yet + + serviceManagerOwner = Ownable(serviceManager).owner(); + vm.startPrank(serviceManagerOwner); + + _migrateToOperatorSets(); + + vm.stopPrank(); + } + + // forge script script/OperatorSetUpgrade.s.sol --sig "simulateUpgradeAndMigrate()" -vvv + function simulateUpgradeAndMigrate() public { + _upgradeAvsDirectory(); /// Workaround since this isn't on pre-prod yet + + address proxyAdmin = OperatorSetUpgradeLib.getAdmin(serviceManager); + proxyAdminOwner = Ownable(proxyAdmin).owner(); + + console2.log(proxyAdminOwner, "Pranker"); + vm.startPrank(proxyAdminOwner); + + _upgrade(); + + vm.stopPrank(); + + serviceManagerOwner = Ownable(serviceManager).owner(); + vm.startPrank(serviceManagerOwner); + + _migrateToOperatorSets(); + + vm.stopPrank(); + + // Assert that serviceManager is an operatorSetAVS + require( + IAVSDirectory(avsDirectory).isOperatorSetAVS(serviceManager), + "simulateUpgradeAndMigrate: serviceManager is not an operatorSetAVS" + ); + + // Assert that the migration is finalized + require( + IServiceManagerMigration(serviceManager).migrationFinalized(), + "simulateUpgradeAndMigrate: Migration is not finalized" + ); + } + + function _upgradeAvsDirectory() internal { + address proxyAdmin = OperatorSetUpgradeLib.getAdmin(avsDirectory); + address avsDirectoryOwner = Ownable(proxyAdmin).owner(); + AVSDirectory avsDirectoryImpl = new AVSDirectory(IDelegationManager(delegationManager)); + + vm.startPrank(avsDirectoryOwner); + OperatorSetUpgradeLib.upgrade(avsDirectory, address(avsDirectoryImpl)); + vm.stopPrank(); + } + + function labelAndLogAddressesSetup() internal virtual { + vm.label(proxyAdminOwner, "Proxy Admin Owner Account"); + vm.label(serviceManagerOwner, "Service Manager Owner Account"); + vm.label(serviceManager, "Service Manager Proxy"); + vm.label(stakeRegistry, "Stake Registry Proxy"); + vm.label(registryCoordinator, "Registry Coordinator Proxy"); + vm.label(indexRegistry, "Index Registry Proxy"); + vm.label(blsApkRegistry, "BLS APK Registry Proxy"); + vm.label(avsDirectory, "AVS Directory Proxy"); + vm.label(delegationManager, "Delegation Manager Proxy"); + vm.label(rewardsCoordinator, "Rewards Coordinator Proxy"); + + console2.log("Proxy Admin Owner Account", proxyAdminOwner); + console2.log("ServiceManager Owner Account", serviceManagerOwner); + console2.log("Service Manager:", serviceManager); + console2.log("Stake Registry:", stakeRegistry); + console2.log("Registry Coordinator:", registryCoordinator); + console2.log("Index Registry:", indexRegistry); + console2.log("BLS APK Registry:", blsApkRegistry); + console2.log("AVS Directory:", avsDirectory); + console2.log("Delegation Manager:", delegationManager); + console2.log("Rewards Coordinator:", rewardsCoordinator); + + address oldServiceManagerImpl = OperatorSetUpgradeLib.getImplementation(serviceManager); + address oldStakeRegistryImpl = OperatorSetUpgradeLib.getImplementation(stakeRegistry); + address oldRegistryCoordinatorImpl = OperatorSetUpgradeLib.getImplementation(registryCoordinator); + address oldAvsDirectoryImpl = OperatorSetUpgradeLib.getImplementation(avsDirectory); + address oldDelegationManagerImpl = OperatorSetUpgradeLib.getImplementation(delegationManager); + + vm.label(oldServiceManagerImpl, "Old Service Manager Implementation"); + vm.label(oldStakeRegistryImpl, "Old Stake Registry Implementation"); + vm.label(oldRegistryCoordinatorImpl, "Old Registry Coordinator Implementation"); + vm.label(oldAvsDirectoryImpl, "Old AVS Directory Implementation"); + vm.label(oldDelegationManagerImpl, "Old Delegation Manager Implementation"); + + console2.log("Old Service Manager Implementation:", oldServiceManagerImpl); + console2.log("Old Stake Registry Implementation:", oldStakeRegistryImpl); + console2.log("Old Registry Coordinator Implementation:", oldRegistryCoordinatorImpl); + console2.log("Old AVS Directory Implementation:", oldAvsDirectoryImpl); + console2.log("Old Delegation Manager Implementation:", oldDelegationManagerImpl); + } + + function loadAddressesSetup(string memory middlewareJson, string memory coreJson) internal virtual { + serviceManager = middlewareJson.readAddress(".addresses.eigenDAServiceManager"); + stakeRegistry = middlewareJson.readAddress(".addresses.stakeRegistry"); + registryCoordinator = middlewareJson.readAddress(".addresses.registryCoordinator"); + blsApkRegistry = middlewareJson.readAddress(".addresses.blsApkRegistry"); + indexRegistry = middlewareJson.readAddress(".addresses.indexRegistry"); + + avsDirectory = coreJson.readAddress(".addresses.avsDirectory"); + delegationManager = coreJson.readAddress(".addresses.delegationManager"); + rewardsCoordinator = coreJson.readAddress(".addresses.rewardsCoordinator"); + } + + function _upgrade() internal virtual { + address newServiceManagerImpl = address(new ServiceManagerMock( + IAVSDirectory(avsDirectory), + IRewardsCoordinator(rewardsCoordinator), + IRegistryCoordinator(registryCoordinator), + IStakeRegistry(stakeRegistry) + )); + address newRegistryCoordinatorImpl = address(new RegistryCoordinator( + IServiceManager(serviceManager), + IStakeRegistry(stakeRegistry), + IBLSApkRegistry(blsApkRegistry), + IIndexRegistry(indexRegistry), + IAVSDirectory(avsDirectory) + )); + address newStakeRegistryImpl = address(new StakeRegistry( + IRegistryCoordinator(registryCoordinator), + IDelegationManager(delegationManager), + IAVSDirectory(avsDirectory), + IServiceManager(serviceManager) + )); + + console2.log("New Service Manager Implementation:", newServiceManagerImpl); + console2.log("New Registry Coordinator Implementation:", newRegistryCoordinatorImpl); + console2.log("New Stake Registry Implementation:", newStakeRegistryImpl); + + vm.label(newServiceManagerImpl, "New Service Manager Implementation"); + vm.label(newRegistryCoordinatorImpl, "New Registry Coordinator Implementation"); + vm.label(newStakeRegistryImpl, "New Stake Registry Implementation"); + + OperatorSetUpgradeLib.upgrade(serviceManager, newServiceManagerImpl); + OperatorSetUpgradeLib.upgrade(registryCoordinator, newRegistryCoordinatorImpl); + OperatorSetUpgradeLib.upgrade(stakeRegistry, newStakeRegistryImpl); + } + + function _migrateToOperatorSets() internal virtual { + IServiceManagerMigration serviceManager = IServiceManagerMigration(serviceManager); + ( + uint32[] memory operatorSetsToCreate, + uint32[][] memory operatorSetIdsToMigrate, + address[] memory operators + ) = serviceManager.getOperatorsToMigrate(); + + serviceManager.migrateAndCreateOperatorSetIds(operatorSetsToCreate); + serviceManager.migrateToOperatorSets(operatorSetIdsToMigrate, operators); + serviceManager.finalizeMigration(); + } +} \ No newline at end of file diff --git a/script/utils/UpgradeLib.sol b/script/utils/UpgradeLib.sol new file mode 100644 index 00000000..0b136c8e --- /dev/null +++ b/script/utils/UpgradeLib.sol @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; +// Deploy L2AVS proxy + +import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; + +import {Vm} from "forge-std/Vm.sol"; +import {stdJson} from "forge-std/StdJson.sol"; + +library OperatorSetUpgradeLib { + using stdJson for string; + Vm private constant vm = Vm(address(uint160(uint256(keccak256("hevm cheat code"))))); + bytes32 internal constant IMPLEMENTATION_SLOT = + 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; + + bytes32 internal constant ADMIN_SLOT = + 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; + + + function upgrade(address proxy, address implementation, bytes memory data) internal { + ProxyAdmin admin = ProxyAdmin(getAdmin(proxy)); + admin.upgradeAndCall(TransparentUpgradeableProxy(payable(proxy)), implementation, data); + } + + function upgrade(address proxy, address implementation) internal { + ProxyAdmin admin = ProxyAdmin(getAdmin(proxy)); + admin.upgrade(TransparentUpgradeableProxy(payable(proxy)), implementation); + } + + function getAdmin(address proxy) internal view returns (address){ + bytes32 value = vm.load(proxy, ADMIN_SLOT); + return address(uint160(uint256(value))); + } + + function getImplementation(address proxy) internal view returns (address) { + bytes32 value = vm.load(proxy, IMPLEMENTATION_SLOT); + return address(uint160(uint256(value))); + } +} \ No newline at end of file diff --git a/script/utils/testdata/17000/core_testdata.json b/script/utils/testdata/17000/core_testdata.json new file mode 100644 index 00000000..2ebdc8fb --- /dev/null +++ b/script/utils/testdata/17000/core_testdata.json @@ -0,0 +1,23 @@ +{ + "addresses":{ + "avsDirectory": "0x141d6995556135D4997b2ff72EB443Be300353bC", + "avsDirectoryImplementation": "0x357978adC03375BD6a3605DE055fABb84695d79A", + "baseStrategyImplementation": "0x62450517EfA1CE60d79801daf8f95973865e8D40", + "beaconOracle": "0x4C116BB629bff7A8373c2378bBd919f8349B8f25", + "delayedWithdrawalRouter": "0xC4BC46a87A67a531eCF7f74338E1FA79533334Fa", + "delayedWithdrawalRouterImplementation": "0x0011FA2c512063C495f77296Af8d195F33A8Dd38", + "delegationManager": "0x75dfE5B44C2E530568001400D3f704bC8AE350CC", + "delegationManagerImplementation": "0x56E88cb4f0136fC27D95499dE4BE2acf47946Fa1", + "eigenLayerPauserReg": "0x9Ab2FEAf0465f0eD51Fc2b663eF228B418c9Dad1", + "eigenLayerProxyAdmin": "0x1BEF05C7303d44e0E2FCD2A19d993eDEd4c51b5B", + "eigenPodBeacon": "0x92Cc4a800A1513E85C481dDDf3A06C6921211eaC", + "eigenPodImplementation": "0x2D6c7f9862BD80Cf0d9d93FC6b513D69E7Db7869", + "eigenPodManager": "0xB8d8952f572e67B11e43bC21250967772fa883Ff", + "eigenPodManagerImplementation": "0xc5B857A92245f64e9D90cCc5b096Db82eB77eB5c", + "emptyContract": "0x9690d52B1Ce155DB2ec5eCbF5a262ccCc7B3A6D2", + "rewardsCoordinator": "0xb22Ef643e1E067c994019A4C19e403253C05c2B0", + "rewardsCoordinatorImplementation": "0x76d4D84c90a2AFf213F7D859d2a288685A1a2Ede", + "slasher": "0x12699471dF8dca329C76D72823B1b79d55709384", + "slasherImplementation": "0x9460fCe11E1e0365419fa860599903B4E5097cf0" + } +} \ No newline at end of file diff --git a/script/utils/testdata/17000/middlware_testdata.json b/script/utils/testdata/17000/middlware_testdata.json new file mode 100644 index 00000000..6c25eeee --- /dev/null +++ b/script/utils/testdata/17000/middlware_testdata.json @@ -0,0 +1,18 @@ +{ + "addresses":{ + "blsApkRegistry": "0xAd7f9e558170a149Ca8E90f41Ab2444A5d3bd6aD", + "blsApkRegistryImplementation": "0x482a96D5879e32347d8df125f038D7eC8Ab358dd", + "eigenDAProxyAdmin": "0x9Fd7E279f5bD692Dc04792151E14Ad814FC60eC1", + "eigenDAServiceManager": "0x54A03db2784E3D0aCC08344D05385d0b62d4F432", + "eigenDAServiceManagerImplementation": "0xEB11a0f320E39d3371Fec4Bf5C76944DfBA8ee10", + "indexRegistry": "0x8cE5F2a53cBd29710eb94A04e40C07A4DdF15d10", + "indexRegistryImplementation": "0x1D4d6054BD11A5711ad7c5d3E376C987a603e17C", + "mockRollup": "0x0433646AdCeE95fbF89b3BFDb8157e75c19b6C2e", + "operatorStateRetriever": "0x17cA8C41a59466710443143b2ECF08CaA35d80ad", + "registryCoordinator": "0x2c61EA360D6500b58E7f481541A36B443Bc858c6", + "registryCoordinatorImplementation": "0x6f21A84E7f185cCBA248B436e3b583E609d1dE1D", + "serviceManagerRouter": "0xDb028E067fe81e9f406C2DE382Ba82e9cD7cBD03", + "stakeRegistry": "0x53668EBf2e28180e38B122c641BC51Ca81088871", + "stakeRegistryImplementation": "0x854dc9e5d011B060bf77B1a492302C349f2f00b5" + } +} \ No newline at end of file From eb0d6ad8f6ea25894fcdf8dd25230afe4cfcd117 Mon Sep 17 00:00:00 2001 From: steven <12021290+stevennevins@users.noreply.github.com> Date: Thu, 17 Oct 2024 17:05:56 -0400 Subject: [PATCH 18/27] chore: bump slashing core dependency (#312) * chore: bump to slashing branch * chore: bump compiler version * fix: dep interface changes * fix: compiler errors from interface changes and type changes * fix: compiler errors * chore: bump dependencies * chore: bump core dependency and resolve issues * chore: bump core dependency and fix compiler errors * feat: integrate AllocationManager * feat: add a slashing permission to the service manager * chore: remove unneeded casting * feat: implement a slasher permission and forward call to AllocationManager * feat: add simiple slasher starting point * chore: bump slashing magnitudes * chore: bump core slashing-magnitudes branch --- foundry.toml | 2 +- lib/eigenlayer-contracts | 2 +- script/OperatorSetUpgrade.s.sol | 7 +- src/BLSSignatureChecker.sol | 8 +- src/RegistryCoordinator.sol | 6 +- src/ServiceManagerBase.sol | 53 +- src/ServiceManagerBaseStorage.sol | 13 +- src/StakeRegistry.sol | 7 +- src/interfaces/IServiceManager.sol | 6 +- src/libraries/SignatureCheckerLib.sol | 9 +- src/slashers/SimpleSlasher.sol | 33 ++ src/slashers/SlasherStorage.sol | 8 + src/unaudited/ECDSAServiceManagerBase.sol | 6 +- src/unaudited/ECDSAStakeRegistry.sol | 10 +- test/harnesses/AVSDirectoryHarness.sol | 4 +- test/integration/CoreRegistration.t.sol | 30 +- test/integration/IntegrationBase.t.sol | 10 +- test/integration/IntegrationChecks.t.sol | 2 +- test/integration/IntegrationDeployer.t.sol | 39 +- test/integration/User.t.sol | 17 +- ...ll_Register_CoreBalanceChange_Update.t.sol | 4 +- test/mocks/AVSDirectoryMock.sol | 357 ++++++------ test/mocks/AllocationManagerMock.sol | 83 +++ test/mocks/DelegationMock.sol | 506 +++++++++++------- test/mocks/ECDSAServiceManagerMock.sol | 5 + test/mocks/EigenPodManagerMock.sol | 80 +++ test/mocks/RewardsCoordinatorMock.sol | 165 +++--- test/mocks/ServiceManagerMock.sol | 16 +- test/unit/ECDSAServiceManager.t.sol | 17 +- test/unit/RegistryCoordinatorMigration.t.sol | 34 +- test/unit/ServiceManagerBase.t.sol | 36 +- test/unit/ServiceManagerMigration.t.sol | 25 +- test/unit/ServiceManagerRouter.t.sol | 3 +- test/utils/MockAVSDeployer.sol | 51 +- 34 files changed, 1032 insertions(+), 622 deletions(-) create mode 100644 src/slashers/SimpleSlasher.sol create mode 100644 src/slashers/SlasherStorage.sol create mode 100644 test/mocks/AllocationManagerMock.sol create mode 100644 test/mocks/EigenPodManagerMock.sol diff --git a/foundry.toml b/foundry.toml index fd0aac0f..a4b33112 100644 --- a/foundry.toml +++ b/foundry.toml @@ -15,7 +15,7 @@ optimizer_runs = 200 # Whether or not to use the Yul intermediate representation compilation pipeline via_ir = false # Override the Solidity version (this overrides `auto_detect_solc`) -solc_version = '0.8.12' +solc_version = '0.8.27' [etherscan] mainnet = { key = "${ETHERSCAN_API_KEY}" } diff --git a/lib/eigenlayer-contracts b/lib/eigenlayer-contracts index d8a8341c..d98c5a7d 160000 --- a/lib/eigenlayer-contracts +++ b/lib/eigenlayer-contracts @@ -1 +1 @@ -Subproject commit d8a8341c5d5c960e6da7c08a845f2584da579cf7 +Subproject commit d98c5a7df7634e25073b9a508be1a6606d7caf0c diff --git a/script/OperatorSetUpgrade.s.sol b/script/OperatorSetUpgrade.s.sol index 39158e0a..32cd80fe 100644 --- a/script/OperatorSetUpgrade.s.sol +++ b/script/OperatorSetUpgrade.s.sol @@ -12,6 +12,7 @@ import {IBLSApkRegistry} from "../src/interfaces/IBLSApkRegistry.sol"; import {IIndexRegistry} from "../src/interfaces/IIndexRegistry.sol"; import {IRewardsCoordinator} from "eigenlayer-contracts/src/contracts/interfaces/IRewardsCoordinator.sol"; import {IAVSDirectory} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; +import {IAllocationManager} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; interface IServiceManagerMigration { function getOperatorsToMigrate() @@ -46,6 +47,7 @@ contract OperatorSetUpgradeScript is Script { address public delegationManager; address public blsApkRegistry; address public indexRegistry; + address public allocationManager; function setUp() public { vm.label(DEFAULT_FORGE_SENDER, "DEFAULT FORGE SENDER"); @@ -145,7 +147,7 @@ contract OperatorSetUpgradeScript is Script { function _upgradeAvsDirectory() internal { address proxyAdmin = OperatorSetUpgradeLib.getAdmin(avsDirectory); address avsDirectoryOwner = Ownable(proxyAdmin).owner(); - AVSDirectory avsDirectoryImpl = new AVSDirectory(IDelegationManager(delegationManager)); + AVSDirectory avsDirectoryImpl = new AVSDirectory(IDelegationManager(delegationManager), 0); // TODO: config vm.startPrank(avsDirectoryOwner); OperatorSetUpgradeLib.upgrade(avsDirectory, address(avsDirectoryImpl)); @@ -211,7 +213,8 @@ contract OperatorSetUpgradeScript is Script { IAVSDirectory(avsDirectory), IRewardsCoordinator(rewardsCoordinator), IRegistryCoordinator(registryCoordinator), - IStakeRegistry(stakeRegistry) + IStakeRegistry(stakeRegistry), + IAllocationManager(allocationManager) )); address newRegistryCoordinatorImpl = address(new RegistryCoordinator( IServiceManager(serviceManager), diff --git a/src/BLSSignatureChecker.sol b/src/BLSSignatureChecker.sol index 5392289c..b3a66ae8 100644 --- a/src/BLSSignatureChecker.sol +++ b/src/BLSSignatureChecker.sol @@ -193,9 +193,11 @@ contract BLSSignatureChecker is IBLSSignatureChecker { */ { bool _staleStakesForbidden = staleStakesForbidden; - uint256 withdrawalDelayBlocks = _staleStakesForbidden - ? delegation.minWithdrawalDelayBlocks() - : 0; + /// TODO: FIX + uint256 withdrawalDelayBlocks = 0; + // uint256 withdrawalDelayBlocks = _staleStakesForbidden + // ? delegation.minWithdrawalDelayBlocks() + // : 0; for (uint256 i = 0; i < quorumNumbers.length; i++) { // If we're disallowing stale stake updates, check that each quorum's last update block diff --git a/src/RegistryCoordinator.sol b/src/RegistryCoordinator.sol index 818f0541..c71e7278 100644 --- a/src/RegistryCoordinator.sol +++ b/src/RegistryCoordinator.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.12; import {IPauserRegistry} from "eigenlayer-contracts/src/contracts/interfaces/IPauserRegistry.sol"; import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; -import {IAVSDirectory} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; +import {IAVSDirectory, OperatorSet} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; import {ISocketUpdater} from "./interfaces/ISocketUpdater.sol"; import {IBLSApkRegistry} from "./interfaces/IBLSApkRegistry.sol"; import {IStakeRegistry} from "./interfaces/IStakeRegistry.sol"; @@ -676,7 +676,7 @@ contract RegistryCoordinator is for (uint256 i = 0; i < quorumBytes.length; i++) { /// We need to track forceDeregistrations so we don't pass an id that was already deregistered on the AVSDirectory /// but hasnt yet been recorded in the middleware contracts - if (!avsDirectory.isMember(operator, IAVSDirectory.OperatorSet(address(serviceManager), uint8(quorumBytes[i])))){ + if (!avsDirectory.isMember(operator, OperatorSet(address(serviceManager), uint8(quorumBytes[i])))){ forceDeregistrationCount++; } operatorSetIds[i] = uint8(quorumBytes[i]); @@ -687,7 +687,7 @@ contract RegistryCoordinator is uint32[] memory filteredOperatorSetIds = new uint32[](operatorSetIds.length - forceDeregistrationCount); uint256 offset; for (uint256 i; i < operatorSetIds.length; i++){ - if (avsDirectory.isMember(operator, IAVSDirectory.OperatorSet(address(serviceManager), operatorSetIds[i]))){ + if (avsDirectory.isMember(operator, OperatorSet(address(serviceManager), operatorSetIds[i]))){ filteredOperatorSetIds[i] = operatorSetIds[i+offset]; } else { offset++; diff --git a/src/ServiceManagerBase.sol b/src/ServiceManagerBase.sol index 2fbacea2..47ac07f1 100644 --- a/src/ServiceManagerBase.sol +++ b/src/ServiceManagerBase.sol @@ -6,6 +6,7 @@ import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISi import {IAVSDirectory} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; import {IRewardsCoordinator} from "eigenlayer-contracts/src/contracts/interfaces/IRewardsCoordinator.sol"; +import {IAllocationManager} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; import {ServiceManagerBaseStorage} from "./ServiceManagerBaseStorage.sol"; import {IServiceManager} from "./interfaces/IServiceManager.sol"; @@ -38,11 +39,10 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage { _; } - function _checkRewardsInitiator() internal view { - require( - msg.sender == rewardsInitiator, - "ServiceManagerBase.onlyRewardsInitiator: caller is not the rewards initiator" - ); + /// @notice only slasher can call functions with this modifier + modifier onlySlasher() { + _checkSlasher(); + _; } /// @notice Sets the (immutable) `_registryCoordinator` address @@ -50,13 +50,15 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage { IAVSDirectory __avsDirectory, IRewardsCoordinator __rewardsCoordinator, IRegistryCoordinator __registryCoordinator, - IStakeRegistry __stakeRegistry + IStakeRegistry __stakeRegistry, + IAllocationManager __allocationManager ) ServiceManagerBaseStorage( __avsDirectory, __rewardsCoordinator, __registryCoordinator, - __stakeRegistry + __stakeRegistry, + __allocationManager ) { _disableInitializers(); @@ -64,10 +66,12 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage { function __ServiceManagerBase_init( address initialOwner, - address _rewardsInitiator + address _rewardsInitiator, + address _slasher ) internal virtual onlyInitializing { _transferOwnership(initialOwner); _setRewardsInitiator(_rewardsInitiator); + _setSlasher(_slasher); } /** @@ -79,6 +83,10 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage { _avsDirectory.updateAVSMetadataURI(_metadataURI); } + function slashOperator(IAllocationManager.SlashingParams memory params) external onlySlasher { + _allocationManager.slashOperator(params); + } + /** * @notice Creates a new rewards submission to the EigenLayer RewardsCoordinator contract, to be split amongst the * set of stakers delegated to operators who are registered to this `avs` @@ -168,6 +176,15 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage { _setRewardsInitiator(newRewardsInitiator); } + /** + * @notice Sets the slasher address + * @param newSlasher The new slasher address + * @dev only callable by the owner + */ + function setSlasher(address newSlasher) external onlyOwner { + _setSlasher(newSlasher); + } + /** * @notice Migrates the AVS to use operator sets and creates new operator set IDs. * @param operatorSetsToCreate An array of operator set IDs to create. @@ -325,6 +342,11 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage { rewardsInitiator = newRewardsInitiator; } + function _setSlasher(address newSlasher) internal { + emit SlasherUpdated(slasher, newSlasher); + slasher = newSlasher; + } + /** * @notice Returns the list of strategies that the AVS supports for restaking * @dev This function is intended to be called off-chain @@ -402,4 +424,19 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage { function avsDirectory() external view override returns (address) { return address(_avsDirectory); } + + function _checkRewardsInitiator() internal view { + require( + msg.sender == rewardsInitiator, + "ServiceManagerBase.onlyRewardsInitiator: caller is not the rewards initiator" + ); + } + + + function _checkSlasher() internal view { + require( + msg.sender == slasher, + "ServiceManagerBase.onlySlasher: caller is not the slasher" + ); + } } diff --git a/src/ServiceManagerBaseStorage.sol b/src/ServiceManagerBaseStorage.sol index 4d0c1fec..71d54d98 100644 --- a/src/ServiceManagerBaseStorage.sol +++ b/src/ServiceManagerBaseStorage.sol @@ -9,6 +9,7 @@ import {IStakeRegistry} from "./interfaces/IStakeRegistry.sol"; import {IAVSDirectory} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; import {IRewardsCoordinator} from "eigenlayer-contracts/src/contracts/interfaces/IRewardsCoordinator.sol"; +import {IAllocationManager} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; /** * @title Storage variables for the `ServiceManagerBase` contract. @@ -25,6 +26,7 @@ abstract contract ServiceManagerBaseStorage is IServiceManager, OwnableUpgradeab IRewardsCoordinator internal immutable _rewardsCoordinator; IRegistryCoordinator internal immutable _registryCoordinator; IStakeRegistry internal immutable _stakeRegistry; + IAllocationManager internal immutable _allocationManager; /** * @@ -35,21 +37,26 @@ abstract contract ServiceManagerBaseStorage is IServiceManager, OwnableUpgradeab /// @notice The address of the entity that can initiate rewards address public rewardsInitiator; + /// @notice The address of the slasher account + address public slasher; + bool public migrationFinalized; - /// @notice Sets the (immutable) `_avsDirectory`, `_rewardsCoordinator`, `_registryCoordinator`, and `_stakeRegistry` addresses + /// @notice Sets the (immutable) `_avsDirectory`, `_rewardsCoordinator`, `_registryCoordinator`, `_stakeRegistry`, and `_allocationManager` addresses constructor( IAVSDirectory __avsDirectory, IRewardsCoordinator __rewardsCoordinator, IRegistryCoordinator __registryCoordinator, - IStakeRegistry __stakeRegistry + IStakeRegistry __stakeRegistry, + IAllocationManager __allocationManager ) { _avsDirectory = __avsDirectory; _rewardsCoordinator = __rewardsCoordinator; _registryCoordinator = __registryCoordinator; _stakeRegistry = __stakeRegistry; + _allocationManager = __allocationManager; } // storage gap for upgradeability - uint256[49] private __GAP; + uint256[48] private __GAP; } diff --git a/src/StakeRegistry.sol b/src/StakeRegistry.sol index ca4b55fb..9d4da097 100644 --- a/src/StakeRegistry.sol +++ b/src/StakeRegistry.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.12; import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; -import {IAVSDirectory} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; +import {IAVSDirectory, OperatorSet} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; import {IServiceManager} from "./interfaces/IServiceManager.sol"; import {StakeRegistryStorage, IStrategy} from "./StakeRegistryStorage.sol"; @@ -182,7 +182,7 @@ contract StakeRegistry is StakeRegistryStorage { // Query the AVSDirectory to check if the operator is directly unregistered operatorRegistered = avsDirectory.isMember( operator, - IAVSDirectory.OperatorSet(address(serviceManager), operatorSetId) + OperatorSet(address(serviceManager), operatorSetId) ); if (!hasMinimumStake || (isOperatorSetAVS && !operatorRegistered)) { @@ -491,7 +491,8 @@ contract StakeRegistry is StakeRegistryStorage { uint256 stratsLength = strategyParamsLength(quorumNumber); StrategyParams memory strategyAndMultiplier; - uint256[] memory strategyShares = delegation.getOperatorShares(operator, strategiesPerQuorum[quorumNumber]); + uint256[] memory strategyShares; + // = delegation.getDelegatableShares(operator, strategiesPerQuorum[quorumNumber]); for (uint256 i = 0; i < stratsLength; i++) { // accessing i^th StrategyParams struct for the quorumNumber strategyAndMultiplier = strategyParams[quorumNumber][i]; diff --git a/src/interfaces/IServiceManager.sol b/src/interfaces/IServiceManager.sol index 8807e9df..5057525e 100644 --- a/src/interfaces/IServiceManager.sol +++ b/src/interfaces/IServiceManager.sol @@ -4,6 +4,7 @@ pragma solidity >=0.5.0; import {IRewardsCoordinator} from "eigenlayer-contracts/src/contracts/interfaces/IRewardsCoordinator.sol"; import {IServiceManagerUI} from "./IServiceManagerUI.sol"; import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; +import {IAllocationManagerTypes} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; /** * @title Minimal interface for a ServiceManager-type contract that forms the single point for an AVS to push updates to EigenLayer @@ -23,7 +24,7 @@ interface IServiceManager is IServiceManagerUI { */ function createAVSRewardsSubmission(IRewardsCoordinator.RewardsSubmission[] calldata rewardsSubmissions) external; - function createOperatorSets(uint32[] memory operatorSetIds) external ; + function createOperatorSets(uint32[] memory operatorSetIds) external; /** * @notice Forwards a call to EigenLayer's AVSDirectory contract to register an operator to operator sets @@ -44,6 +45,9 @@ interface IServiceManager is IServiceManagerUI { */ function deregisterOperatorFromOperatorSets(address operator, uint32[] calldata operatorSetIds) external; + function slashOperator(IAllocationManagerTypes.SlashingParams memory params) external; + // EVENTS event RewardsInitiatorUpdated(address prevRewardsInitiator, address newRewardsInitiator); + event SlasherUpdated(address prevSlasher, address newSlasher); } diff --git a/src/libraries/SignatureCheckerLib.sol b/src/libraries/SignatureCheckerLib.sol index fa2fb137..c01fd3a7 100644 --- a/src/libraries/SignatureCheckerLib.sol +++ b/src/libraries/SignatureCheckerLib.sol @@ -1,8 +1,7 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.12; -import {EIP1271SignatureUtils} from - "eigenlayer-contracts/src/contracts/libraries/EIP1271SignatureUtils.sol"; +import "@openzeppelin-upgrades/contracts/utils/cryptography/SignatureCheckerUpgradeable.sol"; /** * @title SignatureCheckerLib @@ -11,6 +10,8 @@ import {EIP1271SignatureUtils} from * validation logic to this external library. */ library SignatureCheckerLib { + error InvalidSignature(); + /** * @notice Validates a signature using EIP-1271 standard. * @param signer The address of the signer. @@ -22,6 +23,8 @@ library SignatureCheckerLib { bytes32 digestHash, bytes memory signature ) external view { - EIP1271SignatureUtils.checkSignature_EIP1271(signer, digestHash, signature); + if (!SignatureCheckerUpgradeable.isValidSignatureNow(signer, digestHash, signature)) { + revert InvalidSignature(); + } } } diff --git a/src/slashers/SimpleSlasher.sol b/src/slashers/SimpleSlasher.sol new file mode 100644 index 00000000..faa4f49b --- /dev/null +++ b/src/slashers/SimpleSlasher.sol @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.12; + +import {Initializable} from "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol"; +import {IServiceManager} from "../interfaces/IServiceManager.sol"; +import {SlasherStorage} from "./SlasherStorage.sol"; +import {IAllocationManagerTypes} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; +import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; + +contract SimpleSlasher is Initializable, SlasherStorage { + function initialize(address _serviceManager) public initializer { + serviceManager = _serviceManager; + } + + function slashOperator( + address operator, + uint32 operatorSetId, + IStrategy[] memory strategies, + uint256 wadToSlash, + string memory description + ) external { + + IAllocationManagerTypes.SlashingParams memory params = IAllocationManagerTypes.SlashingParams({ + operator: operator, + operatorSetId: operatorSetId, + strategies: strategies, + wadToSlash: wadToSlash, + description: description + }); + + IServiceManager(serviceManager).slashOperator(params); + } +} diff --git a/src/slashers/SlasherStorage.sol b/src/slashers/SlasherStorage.sol new file mode 100644 index 00000000..1b3d61de --- /dev/null +++ b/src/slashers/SlasherStorage.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.12; + +contract SlasherStorage { + address public serviceManager; + + uint256[49] private __gap; +} \ No newline at end of file diff --git a/src/unaudited/ECDSAServiceManagerBase.sol b/src/unaudited/ECDSAServiceManagerBase.sol index 57326bea..3d30f451 100644 --- a/src/unaudited/ECDSAServiceManagerBase.sol +++ b/src/unaudited/ECDSAServiceManagerBase.sol @@ -238,8 +238,10 @@ abstract contract ECDSAServiceManagerBase is for (uint256 i; i < count; i++) { strategies[i] = quorum.strategies[i].strategy; } - uint256[] memory shares = IDelegationManager(delegationManager) - .getOperatorShares(_operator, strategies); + uint256[] memory shares; + // TODO: Fix + // = IDelegationManager(delegationManager) + // .getOperatorShares(_operator, strategies); uint256 activeCount; for (uint256 i; i < count; i++) { diff --git a/src/unaudited/ECDSAStakeRegistry.sol b/src/unaudited/ECDSAStakeRegistry.sol index ab4bdbeb..a8dff79a 100644 --- a/src/unaudited/ECDSAStakeRegistry.sol +++ b/src/unaudited/ECDSAStakeRegistry.sol @@ -248,10 +248,12 @@ contract ECDSAStakeRegistry is for (uint256 i; i < strategyParams.length; i++) { strategies[i] = strategyParams[i].strategy; } - uint256[] memory shares = DELEGATION_MANAGER.getOperatorShares( - _operator, - strategies - ); + uint256[] memory shares; + /// TODO: FIX + // = DELEGATION_MANAGER.getOperatorShares( + // _operator, + // strategies + // ); for (uint256 i; i < strategyParams.length; i++) { weight += shares[i] * strategyParams[i].multiplier; } diff --git a/test/harnesses/AVSDirectoryHarness.sol b/test/harnesses/AVSDirectoryHarness.sol index 99efedec..94598058 100644 --- a/test/harnesses/AVSDirectoryHarness.sol +++ b/test/harnesses/AVSDirectoryHarness.sol @@ -7,7 +7,7 @@ import {AVSDirectory} from "eigenlayer-contracts/src/contracts/core/AVSDirectory // wrapper around the AVSDirectory contract that exposes internal functionality, for unit testing contract AVSDirectoryHarness is AVSDirectory { - constructor(IDelegationManager _delegation) AVSDirectory(_delegation) {} + constructor(IDelegationManager _delegation) AVSDirectory(_delegation, 0) {} // TODO: config update function setOperatorSaltIsSpent(address operator, bytes32 salt, bool isSpent) external { operatorSaltIsSpent[operator][salt] = isSpent; @@ -59,7 +59,7 @@ contract AVSDirectoryHarness is AVSDirectory { } function _calculateDigestHashExternal(bytes32 structHash) external view returns (bytes32) { - return _calculateDigestHash(structHash); + // return calculateOperatorSetRegistrationDigestHash(structHash); // TODO: Fix } function _calculateDomainSeparatorExternal() external view returns (bytes32) { diff --git a/test/integration/CoreRegistration.t.sol b/test/integration/CoreRegistration.t.sol index f6cbf571..b9784dc6 100644 --- a/test/integration/CoreRegistration.t.sol +++ b/test/integration/CoreRegistration.t.sol @@ -3,9 +3,10 @@ pragma solidity ^0.8.12; import "../utils/MockAVSDeployer.sol"; import { AVSDirectory } from "eigenlayer-contracts/src/contracts/core/AVSDirectory.sol"; -import { IAVSDirectory } from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; +import { IAVSDirectory, IAVSDirectoryTypes} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; +import { IStrategyManager } from "eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol"; import { DelegationManager } from "eigenlayer-contracts/src/contracts/core/DelegationManager.sol"; -import { IDelegationManager } from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; +import { IDelegationManager, IDelegationManagerTypes } from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; import { RewardsCoordinator } from "eigenlayer-contracts/src/contracts/core/RewardsCoordinator.sol"; import { IRewardsCoordinator } from "eigenlayer-contracts/src/contracts/interfaces/IRewardsCoordinator.sol"; @@ -26,7 +27,7 @@ contract Test_CoreRegistration is MockAVSDeployer { _deployMockEigenLayerAndAVS(); // Deploy New DelegationManager - DelegationManager delegationManagerImplementation = new DelegationManager(strategyManagerMock, slasher, eigenPodManagerMock); + DelegationManager delegationManagerImplementation = new DelegationManager(avsDirectoryMock, IStrategyManager(address(strategyManagerMock)), eigenPodManagerMock, allocationManagerMock, 0); IStrategy[] memory initializeStrategiesToSetDelayBlocks = new IStrategy[](0); uint256[] memory initializeWithdrawalDelayBlocks = new uint256[](0); delegationManager = DelegationManager( @@ -48,7 +49,7 @@ contract Test_CoreRegistration is MockAVSDeployer { ); // Deploy New AVS Directory - AVSDirectory avsDirectoryImplementation = new AVSDirectory(delegationManager); + AVSDirectory avsDirectoryImplementation = new AVSDirectory(delegationManager, 0); // TODO: Fix Config avsDirectory = AVSDirectory( address( new TransparentUpgradeableProxy( @@ -72,7 +73,8 @@ contract Test_CoreRegistration is MockAVSDeployer { avsDirectory, rewardsCoordinatorMock, registryCoordinator, - stakeRegistry + stakeRegistry, + allocationManager ); registryCoordinatorImplementation = new RegistryCoordinatorHarness( @@ -102,11 +104,13 @@ contract Test_CoreRegistration is MockAVSDeployer { // Register operator to EigenLayer cheats.prank(operator); delegationManager.registerAsOperator( - IDelegationManager.OperatorDetails({ + IDelegationManagerTypes.OperatorDetails({ __deprecated_earningsReceiver: operator, delegationApprover: address(0), - stakerOptOutWindowBlocks: 0 + __deprecated_stakerOptOutWindowBlocks: 0 }), + // TODO: fix or parameterize + 0, emptyStringForMetadataURI ); @@ -137,8 +141,8 @@ contract Test_CoreRegistration is MockAVSDeployer { registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, operatorSignature); // Check operator is registered - IAVSDirectory.OperatorAVSRegistrationStatus operatorStatus = avsDirectory.avsOperatorStatus(address(serviceManager), operator); - assertEq(uint8(operatorStatus), uint8(IAVSDirectory.OperatorAVSRegistrationStatus.REGISTERED)); + IAVSDirectoryTypes.OperatorAVSRegistrationStatus operatorStatus = avsDirectory.avsOperatorStatus(address(serviceManager), operator); + assertEq(uint8(operatorStatus), uint8(IAVSDirectoryTypes.OperatorAVSRegistrationStatus.REGISTERED)); } function test_deregisterOperator_coreStateChanges() public { @@ -151,8 +155,8 @@ contract Test_CoreRegistration is MockAVSDeployer { registryCoordinator.deregisterOperator(quorumNumbers); // Check operator is deregistered - IAVSDirectory.OperatorAVSRegistrationStatus operatorStatus = avsDirectory.avsOperatorStatus(address(serviceManager), operator); - assertEq(uint8(operatorStatus), uint8(IAVSDirectory.OperatorAVSRegistrationStatus.UNREGISTERED)); + IAVSDirectoryTypes.OperatorAVSRegistrationStatus operatorStatus = avsDirectory.avsOperatorStatus(address(serviceManager), operator); + assertEq(uint8(operatorStatus), uint8(IAVSDirectoryTypes.OperatorAVSRegistrationStatus.UNREGISTERED)); } function test_deregisterOperator_notGloballyDeregistered() public { @@ -167,8 +171,8 @@ contract Test_CoreRegistration is MockAVSDeployer { registryCoordinator.deregisterOperator(quorumNumbers); // Check operator is still registered - IAVSDirectory.OperatorAVSRegistrationStatus operatorStatus = avsDirectory.avsOperatorStatus(address(serviceManager), operator); - assertEq(uint8(operatorStatus), uint8(IAVSDirectory.OperatorAVSRegistrationStatus.REGISTERED)); + IAVSDirectoryTypes.OperatorAVSRegistrationStatus operatorStatus = avsDirectory.avsOperatorStatus(address(serviceManager), operator); + assertEq(uint8(operatorStatus), uint8(IAVSDirectoryTypes.OperatorAVSRegistrationStatus.REGISTERED)); } function test_setMetadataURI_fail_notServiceManagerOwner() public { diff --git a/test/integration/IntegrationBase.t.sol b/test/integration/IntegrationBase.t.sol index c9695d70..7a0265f9 100644 --- a/test/integration/IntegrationBase.t.sol +++ b/test/integration/IntegrationBase.t.sol @@ -166,15 +166,15 @@ abstract contract IntegrationBase is IntegrationConfig { /// AVSDirectory: function assert_NotRegisteredToAVS(User operator, string memory err) internal { - IAVSDirectory.OperatorAVSRegistrationStatus status = avsDirectory.avsOperatorStatus(address(serviceManager), address(operator)); + IAVSDirectoryTypes.OperatorAVSRegistrationStatus status = avsDirectory.avsOperatorStatus(address(serviceManager), address(operator)); - assertTrue(status == IAVSDirectory.OperatorAVSRegistrationStatus.UNREGISTERED, err); + assertTrue(status == IAVSDirectoryTypes.OperatorAVSRegistrationStatus.UNREGISTERED, err); } function assert_IsRegisteredToAVS(User operator, string memory err) internal { IAVSDirectory.OperatorAVSRegistrationStatus status = avsDirectory.avsOperatorStatus(address(serviceManager), address(operator)); - assertTrue(status == IAVSDirectory.OperatorAVSRegistrationStatus.REGISTERED, err); + assertTrue(status == IAVSDirectoryTypes.OperatorAVSRegistrationStatus.REGISTERED, err); } /******************************************************************************* @@ -591,7 +591,7 @@ abstract contract IntegrationBase is IntegrationConfig { function assert_Snap_Removed_OperatorShares( User operator, IStrategy[] memory strategies, - uint[] memory removedShares, + uint256[] memory removedShares, string memory err ) internal { uint[] memory curShares = _getOperatorShares(operator, strategies); @@ -773,7 +773,7 @@ abstract contract IntegrationBase is IntegrationConfig { for (uint i = 0; i < strategies.length; i++) { IStrategy strat = strategies[i]; - curShares[i] = strategyManager.stakerStrategyShares(address(staker), strat); + curShares[i] = strategyManager.stakerDepositShares(address(staker), strat); } return curShares; diff --git a/test/integration/IntegrationChecks.t.sol b/test/integration/IntegrationChecks.t.sol index b54f8568..67ee38f5 100644 --- a/test/integration/IntegrationChecks.t.sol +++ b/test/integration/IntegrationChecks.t.sol @@ -245,7 +245,7 @@ contract IntegrationChecks is IntegrationBase { User operator, bytes memory quorums, IStrategy[] memory strategies, - uint[] memory shares + uint256[] memory shares ) internal { _log("check_Withdraw_State", operator); diff --git a/test/integration/IntegrationDeployer.t.sol b/test/integration/IntegrationDeployer.t.sol index ee3722a7..7b9705f9 100644 --- a/test/integration/IntegrationDeployer.t.sol +++ b/test/integration/IntegrationDeployer.t.sol @@ -14,9 +14,9 @@ import "@openzeppelin/contracts/utils/Strings.sol"; // Core contracts import "eigenlayer-contracts/src/contracts/core/DelegationManager.sol"; import "eigenlayer-contracts/src/contracts/core/StrategyManager.sol"; -import "eigenlayer-contracts/src/contracts/core/Slasher.sol"; import "eigenlayer-contracts/src/contracts/core/AVSDirectory.sol"; import "eigenlayer-contracts/src/contracts/core/RewardsCoordinator.sol"; +import "eigenlayer-contracts/src/contracts/core/AllocationManager.sol"; import "eigenlayer-contracts/src/contracts/strategies/StrategyBase.sol"; import "eigenlayer-contracts/src/contracts/pods/EigenPodManager.sol"; import "eigenlayer-contracts/src/contracts/pods/EigenPod.sol"; @@ -51,10 +51,10 @@ abstract contract IntegrationDeployer is Test, IUserDeployer { EigenPodManager eigenPodManager; RewardsCoordinator rewardsCoordinator; PauserRegistry pauserRegistry; - Slasher slasher; IBeacon eigenPodBeacon; EigenPod pod; ETHPOSDepositMock ethPOSDeposit; + AllocationManager allocationManager; // Base strategy implementation in case we want to create more strategies later StrategyBase baseStrategyImplementation; @@ -131,17 +131,18 @@ abstract contract IntegrationDeployer is Test, IUserDeployer { new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") ) ); - slasher = Slasher( + eigenPodManager = EigenPodManager( address( new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") ) ); - eigenPodManager = EigenPodManager( + avsDirectory = AVSDirectory( address( new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") ) ); - avsDirectory = AVSDirectory( + + allocationManager = AllocationManager( address( new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") ) @@ -161,14 +162,13 @@ abstract contract IntegrationDeployer is Test, IUserDeployer { // Second, deploy the *implementation* contracts, using the *proxy contracts* as inputs DelegationManager delegationImplementation = - new DelegationManager(strategyManager, slasher, eigenPodManager); + new DelegationManager(avsDirectory, strategyManager, eigenPodManager, allocationManager, 0); StrategyManager strategyManagerImplementation = - new StrategyManager(delegationManager, eigenPodManager, slasher); - Slasher slasherImplementation = new Slasher(strategyManager, delegationManager); + new StrategyManager(delegationManager); EigenPodManager eigenPodManagerImplementation = new EigenPodManager( - ethPOSDeposit, eigenPodBeacon, strategyManager, slasher, delegationManager + ethPOSDeposit, eigenPodBeacon, strategyManager, delegationManager ); - AVSDirectory avsDirectoryImplemntation = new AVSDirectory(delegationManager); + AVSDirectory avsDirectoryImplemntation = new AVSDirectory(delegationManager, 0); // TODO: fix config // RewardsCoordinator rewardsCoordinatorImplementation = new RewardsCoordinator( // delegationManager, // IStrategyManager(address(strategyManager)), @@ -208,17 +208,6 @@ abstract contract IntegrationDeployer is Test, IUserDeployer { 0 // initialPausedStatus ) ); - // Slasher - proxyAdmin.upgradeAndCall( - TransparentUpgradeableProxy(payable(address(slasher))), - address(slasherImplementation), - abi.encodeWithSelector( - Slasher.initialize.selector, - eigenLayerReputedMultisig, - pauserRegistry, - 0 // initialPausedStatus - ) - ); // EigenPodManager proxyAdmin.upgradeAndCall( TransparentUpgradeableProxy(payable(address(eigenPodManager))), @@ -312,7 +301,8 @@ abstract contract IntegrationDeployer is Test, IUserDeployer { IAVSDirectory(avsDirectory), rewardsCoordinator, IRegistryCoordinator(registryCoordinator), - stakeRegistry + stakeRegistry, + allocationManager ); proxyAdmin.upgrade( @@ -337,7 +327,8 @@ abstract contract IntegrationDeployer is Test, IUserDeployer { serviceManager.initialize({ initialOwner: registryCoordinatorOwner, - rewardsInitiator: address(msg.sender) + rewardsInitiator: address(msg.sender), + slasher: address(msg.sender) }); RegistryCoordinator registryCoordinatorImplementation = @@ -389,7 +380,7 @@ abstract contract IntegrationDeployer is Test, IUserDeployer { strategies[0] = strategy; cheats.prank(strategyManager.strategyWhitelister()); strategyManager.addStrategiesToDepositWhitelist( - strategies, thirdPartyTransfersForbiddenValues + strategies ); // Add to allStrats diff --git a/test/integration/User.t.sol b/test/integration/User.t.sol index 9e5e5990..ff31a7ed 100644 --- a/test/integration/User.t.sol +++ b/test/integration/User.t.sol @@ -11,6 +11,7 @@ import "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; // Core import "eigenlayer-contracts/src/contracts/core/DelegationManager.sol"; +import "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; import "eigenlayer-contracts/src/contracts/core/StrategyManager.sol"; import "eigenlayer-contracts/src/contracts/core/AVSDirectory.sol"; @@ -243,13 +244,13 @@ contract User is Test { function registerAsOperator() public createSnapshot virtual { _log("registerAsOperator (core)"); - IDelegationManager.OperatorDetails memory details = IDelegationManager.OperatorDetails({ + IDelegationManagerTypes.OperatorDetails memory details = IDelegationManagerTypes.OperatorDetails({ __deprecated_earningsReceiver: address(this), delegationApprover: address(0), - stakerOptOutWindowBlocks: 0 + __deprecated_stakerOptOutWindowBlocks: 0 }); - delegationManager.registerAsOperator(details, NAME); + delegationManager.registerAsOperator(details,0, NAME); } // Deposit LSTs into the StrategyManager. This setup does not use the EPMgr or native ETH. @@ -266,13 +267,15 @@ contract User is Test { } } - function exitEigenlayer() public createSnapshot virtual returns (IStrategy[] memory, uint[] memory) { + function exitEigenlayer() public createSnapshot virtual returns (IStrategy[] memory, uint256[] memory) { _log("exitEigenlayer (core)"); - (IStrategy[] memory strategies, uint[] memory shares) = delegationManager.getDelegatableShares(address(this)); + IStrategy[] memory strategies; + uint256[] memory shares; + // = delegationManager.getDelegatableShares(address(this)); // TODO: Fix - IDelegationManager.QueuedWithdrawalParams[] memory params = new IDelegationManager.QueuedWithdrawalParams[](1); - params[0] = IDelegationManager.QueuedWithdrawalParams({ + IDelegationManagerTypes.QueuedWithdrawalParams[] memory params = new IDelegationManager.QueuedWithdrawalParams[](1); + params[0] = IDelegationManagerTypes.QueuedWithdrawalParams({ strategies: strategies, shares: shares, withdrawer: address(this) diff --git a/test/integration/tests/NonFull_Register_CoreBalanceChange_Update.t.sol b/test/integration/tests/NonFull_Register_CoreBalanceChange_Update.t.sol index 43daf518..a4ffe3e6 100644 --- a/test/integration/tests/NonFull_Register_CoreBalanceChange_Update.t.sol +++ b/test/integration/tests/NonFull_Register_CoreBalanceChange_Update.t.sol @@ -118,7 +118,7 @@ contract Integration_NonFull_Register_CoreBalanceChange_Update is IntegrationChe check_Register_State(operator, quorums); // 2. (core) queue full withdrawal - (IStrategy[] memory strategies, uint[] memory shares) = operator.exitEigenlayer(); + (IStrategy[] memory strategies, uint256[] memory shares) = operator.exitEigenlayer(); check_Withdraw_State(operator, quorums, strategies, shares); // 3. Update stakes @@ -151,7 +151,7 @@ contract Integration_NonFull_Register_CoreBalanceChange_Update is IntegrationChe check_Register_State(operator, quorums); // 2. (core) queue full withdrawal - (IStrategy[] memory strategies, uint[] memory shares) = operator.exitEigenlayer(); + (IStrategy[] memory strategies, uint256[] memory shares) = operator.exitEigenlayer(); check_Withdraw_State(operator, quorums, strategies, shares); // 3. Deregister from all quorums diff --git a/test/mocks/AVSDirectoryMock.sol b/test/mocks/AVSDirectoryMock.sol index 46bfb5db..a6bf2a3c 100644 --- a/test/mocks/AVSDirectoryMock.sol +++ b/test/mocks/AVSDirectoryMock.sol @@ -1,194 +1,173 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.12; -import { - IAVSDirectory, - ISignatureUtils -} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; +import {IAVSDirectory, OperatorSet} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; +import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; +import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; +import {IPauserRegistry} from "eigenlayer-contracts/src/contracts/interfaces/IPauserRegistry.sol"; contract AVSDirectoryMock is IAVSDirectory { - /** - * @notice Called by an AVS to create a list of new operatorSets. - * - * @param operatorSetIds The IDs of the operator set to initialize. - * - * @dev msg.sender must be the AVS. - * @dev The AVS may create operator sets before it becomes an operator set AVS. - */ - function createOperatorSets(uint32[] calldata operatorSetIds) external {} - - /** - * @notice Sets the AVS as an operator set AVS, preventing legacy M2 operator registrations. - * - * @dev msg.sender must be the AVS. - */ - function becomeOperatorSetAVS() external {} - - /** - * @notice Called by an AVS to migrate operators that have a legacy M2 registration to operator sets. - * - * @param operators The list of operators to migrate - * @param operatorSetIds The list of operatorSets to migrate the operators to - * - * @dev The msg.sender used is the AVS - * @dev The operator can only be migrated at most once per AVS - * @dev The AVS can no longer register operators via the legacy M2 registration path once it begins migration - * @dev The operator is deregistered from the M2 legacy AVS once migrated - */ - function migrateOperatorsToOperatorSets( - address[] calldata operators, - uint32[][] calldata operatorSetIds - ) external {} - - /** - * @notice Called by AVSs to add an operator to list of operatorSets. - * - * @param operator The address of the operator to be added to the operator set. - * @param operatorSetIds The IDs of the operator sets. - * @param operatorSignature The signature of the operator on their intent to register. - * - * @dev msg.sender is used as the AVS. - * @dev The operator must not have a pending deregistration from the operator set. - */ - function registerOperatorToOperatorSets( - address operator, - uint32[] calldata operatorSetIds, - ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature - ) external {} - - /** - * @notice Called by AVSs to remove an operator from an operator set. - * - * @param operator The address of the operator to be removed from the operator set. - * @param operatorSetIds The IDs of the operator sets. - * - * @dev msg.sender is used as the AVS. - */ - function deregisterOperatorFromOperatorSets( - address operator, - uint32[] calldata operatorSetIds - ) external {} - - /** - * @notice Called by an operator to deregister from an operator set - * - * @param operator The operator to deregister from the operatorSets. - * @param avs The address of the AVS to deregister the operator from. - * @param operatorSetIds The IDs of the operator sets. - * @param operatorSignature the signature of the operator on their intent to deregister or empty if the operator itself is calling - * - * @dev if the operatorSignature is empty, the caller must be the operator - * @dev this will likely only be called in case the AVS contracts are in a state that prevents operators from deregistering - */ - function forceDeregisterFromOperatorSets( - address operator, - address avs, - uint32[] calldata operatorSetIds, - ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature - ) external {} - - /** - * @notice Called by an avs to register an operator with the avs. - * @param operator The address of the operator to register. - * @param operatorSignature The signature, salt, and expiry of the operator's signature. - */ - function registerOperatorToAVS( - address operator, - ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature - ) external {} - - /** - * @notice Called by an avs to deregister an operator with the avs. - * @param operator The address of the operator to deregister. - */ - function deregisterOperatorFromAVS(address operator) external {} - - /** - * @notice Called by an AVS to emit an `AVSMetadataURIUpdated` event indicating the information has updated. - * @param metadataURI The URI for metadata associated with an AVS - * @dev Note that the `metadataURI` is *never stored * and is only emitted in the `AVSMetadataURIUpdated` event - */ - function updateAVSMetadataURI(string calldata metadataURI) external {} - - /** - * @notice Called by an operator to cancel a salt that has been used to register with an AVS. - * - * @param salt A unique and single use value associated with the approver signature. - */ - function cancelSalt(bytes32 salt) external {} - - /** - * @notice Returns whether or not the salt has already been used by the operator. - * @dev Salts is used in the `registerOperatorToAVS` function. - */ - function operatorSaltIsSpent(address operator, bytes32 salt) external view returns (bool) {} - - function isMember( - address avs, - address operator, - uint32 operatorSetId - ) external view returns (bool) {} - - /** - * @notice Calculates the digest hash to be signed by an operator to register with an AVS - * @param operator The account registering as an operator - * @param avs The AVS the operator is registering to - * @param salt A unique and single use value associated with the approver signature. - * @param expiry Time after which the approver's signature becomes invalid - */ - function calculateOperatorAVSRegistrationDigestHash( - address operator, - address avs, - bytes32 salt, - uint256 expiry - ) external view returns (bytes32) {} - - /** - * @notice Calculates the digest hash to be signed by an operator to register with an operator set. - * - * @param avs The AVS that operator is registering to operator sets for. - * @param operatorSetIds An array of operator set IDs the operator is registering to. - * @param salt A unique and single use value associated with the approver signature. - * @param expiry Time after which the approver's signature becomes invalid. - */ - function calculateOperatorSetRegistrationDigestHash( - address avs, - uint32[] calldata operatorSetIds, - bytes32 salt, - uint256 expiry - ) external view returns (bytes32) {} - - /** - * @notice Calculates the digest hash to be signed by an operator to force deregister from an operator set. - * - * @param avs The AVS that operator is deregistering from. - * @param operatorSetIds An array of operator set IDs the operator is deregistering from. - * @param salt A unique and single use value associated with the approver signature. - * @param expiry Time after which the approver's signature becomes invalid. - */ - function calculateOperatorSetForceDeregistrationTypehash( - address avs, - uint32[] calldata operatorSetIds, - bytes32 salt, - uint256 expiry - ) external view returns (bytes32) {} - - /// @notice Getter function for the current EIP-712 domain separator for this contract. - /// @dev The domain separator will change in the event of a fork that changes the ChainID. - function domainSeparator() external view returns (bytes32) {} - - /// @notice The EIP-712 typehash for the Registration struct used by the contract - function OPERATOR_AVS_REGISTRATION_TYPEHASH() external view returns (bytes32) {} - - /// @notice The EIP-712 typehash for the OperatorSetRegistration struct used by the contract. - function OPERATOR_SET_REGISTRATION_TYPEHASH() external view returns (bytes32) {} - - function isOperatorSetAVS(address avs) external view returns (bool) {} - - function isOperatorSet(address avs, uint32 operatorSetId) external view returns (bool) {} - - function isMember( - address operator, - OperatorSet memory operatorSet - ) external view returns (bool) {} -} + function createOperatorSets( + uint32[] calldata operatorSetIds + ) external {} + + function becomeOperatorSetAVS() external {} + + function migrateOperatorsToOperatorSets( + address[] calldata operators, + uint32[][] calldata operatorSetIds + ) external {} + + function registerOperatorToOperatorSets( + address operator, + uint32[] calldata operatorSetIds, + ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature + ) external {} + + function deregisterOperatorFromOperatorSets( + address operator, + uint32[] calldata operatorSetIds + ) external {} + + function forceDeregisterFromOperatorSets( + address operator, + address avs, + uint32[] calldata operatorSetIds, + ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature + ) external {} + + function registerOperatorToAVS( + address operator, + ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature + ) external {} + + function deregisterOperatorFromAVS(address operator) external {} + + function updateAVSMetadataURI( + string calldata metadataURI + ) external {} + + function cancelSalt(bytes32 salt) external {} + + function operatorSaltIsSpent( + address operator, + bytes32 salt + ) external view returns (bool) {} + + function isMember( + address operator, + OperatorSet memory operatorSet + ) external view returns (bool) {} + + function isOperatorSlashable( + address operator, + OperatorSet memory operatorSet + ) external view returns (bool) {} + + function isOperatorSetAVS( + address avs + ) external view returns (bool) {} + + function isOperatorSet( + address avs, + uint32 operatorSetId + ) external view returns (bool) {} + + function isOperatorSetBatch( + OperatorSet[] calldata operatorSets + ) external view returns (bool) {} + + function operatorSetsMemberOfAtIndex( + address operator, + uint256 index + ) external view returns (OperatorSet memory) {} + + function operatorSetMemberAtIndex( + OperatorSet memory operatorSet, + uint256 index + ) external view returns (address) {} + + function getOperatorSetsOfOperator( + address operator, + uint256 start, + uint256 length + ) external view returns (OperatorSet[] memory operatorSets) {} + + function getOperatorsInOperatorSet( + OperatorSet memory operatorSet, + uint256 start, + uint256 length + ) external view returns (address[] memory operators) {} + + function getNumOperatorsInOperatorSet( + OperatorSet memory operatorSet + ) external view returns (uint256) {} + + function inTotalOperatorSets( + address operator + ) external view returns (uint256) {} + + function calculateOperatorAVSRegistrationDigestHash( + address operator, + address avs, + bytes32 salt, + uint256 expiry + ) external view returns (bytes32) {} + + function calculateOperatorSetRegistrationDigestHash( + address avs, + uint32[] calldata operatorSetIds, + bytes32 salt, + uint256 expiry + ) external view returns (bytes32) {} + + function calculateOperatorSetForceDeregistrationTypehash( + address avs, + uint32[] calldata operatorSetIds, + bytes32 salt, + uint256 expiry + ) external view returns (bytes32) {} + + function OPERATOR_AVS_REGISTRATION_TYPEHASH() + external + view + returns (bytes32) + {} + + function OPERATOR_SET_REGISTRATION_TYPEHASH() + external + view + returns (bytes32) + {} + + function operatorSetStatus( + address avs, + address operator, + uint32 operatorSetId + ) + external + view + returns (bool registered, uint32 lastDeregisteredTimestamp) + {} + + function getNumOperatorSetsOfOperator( + address operator + ) external view returns (uint256) {} + + function getStrategiesInOperatorSet( + OperatorSet memory operatorSet + ) external view returns (IStrategy[] memory) {} + + function initialize( + address initialOwner, + IPauserRegistry _pauserRegistry, + uint256 initialPausedStatus + ) external {} + + function removeStrategiesFromOperatorSet( + uint32 operatorSetId, + IStrategy[] calldata strategies + ) external {} + + function addStrategiesToOperatorSet(uint32 operatorSetId, IStrategy[] calldata strategies) external {} +} \ No newline at end of file diff --git a/test/mocks/AllocationManagerMock.sol b/test/mocks/AllocationManagerMock.sol new file mode 100644 index 00000000..1436882e --- /dev/null +++ b/test/mocks/AllocationManagerMock.sol @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.12; + +import {IAllocationManager} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; +import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; +import {IPauserRegistry} from "eigenlayer-contracts/src/contracts/interfaces/IPauserRegistry.sol"; +import {OperatorSet} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; + +contract AllocationManagerMock is IAllocationManager { + function initialize( + address initialOwner, + IPauserRegistry _pauserRegistry, + uint256 initialPausedStatus + ) external override {} + + function slashOperator(SlashingParams calldata params) external override {} + + function modifyAllocations( + MagnitudeAllocation[] calldata allocations + ) external override {} + + function clearDeallocationQueue( + address operator, + IStrategy[] calldata strategies, + uint16[] calldata numToComplete + ) external override {} + + function setAllocationDelay( + address operator, + uint32 delay + ) external override {} + + function setAllocationDelay(uint32 delay) external override {} + + function getAllocationInfo( + address operator, + IStrategy strategy + ) + external + view + override + returns (OperatorSet[] memory, MagnitudeInfo[] memory) + {} + + function getAllocationInfo( + address operator, + IStrategy strategy, + OperatorSet[] calldata operatorSets + ) external view override returns (MagnitudeInfo[] memory) {} + + function getAllocationInfo( + OperatorSet calldata operatorSet, + IStrategy[] calldata strategies, + address[] calldata operators + ) external view override returns (MagnitudeInfo[][] memory) {} + + function getAllocatableMagnitude( + address operator, + IStrategy strategy + ) external view override returns (uint64) {} + + function getMaxMagnitudes( + address operator, + IStrategy[] calldata strategies + ) external view override returns (uint64[] memory) {} + + function getMaxMagnitudesAtTimestamp( + address operator, + IStrategy[] calldata strategies, + uint32 timestamp + ) external view override returns (uint64[] memory) {} + + function getAllocationDelay( + address operator + ) external view override returns (bool isSet, uint32 delay) {} + + function getMinDelegatedAndSlashableOperatorShares( + OperatorSet calldata operatorSet, + address[] calldata operators, + IStrategy[] calldata strategies, + uint32 beforeTimestamp + ) external view override returns (uint256[][] memory, uint256[][] memory) {} +} \ No newline at end of file diff --git a/test/mocks/DelegationMock.sol b/test/mocks/DelegationMock.sol index 88cd9d20..9743fb14 100644 --- a/test/mocks/DelegationMock.sol +++ b/test/mocks/DelegationMock.sol @@ -5,199 +5,325 @@ import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; import {IStrategyManager} from "eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol"; +import {StrategyManager} from "eigenlayer-contracts/src/contracts/core/StrategyManager.sol"; import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; +import {IPauserRegistry} from "eigenlayer-contracts/src/contracts/interfaces/IPauserRegistry.sol"; import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; +import {SlashingLib} from "eigenlayer-contracts/src/contracts/libraries/SlashingLib.sol"; contract DelegationMock is IDelegationManager { - mapping(address => bool) public isOperator; - mapping(address => mapping(IStrategy => uint256)) public operatorShares; - - function setIsOperator(address operator, bool _isOperatorReturnValue) external { - isOperator[operator] = _isOperatorReturnValue; - } - - /// @notice returns the total number of shares in `strategy` that are delegated to `operator`. - function setOperatorShares(address operator, IStrategy strategy, uint256 shares) external { - operatorShares[operator][strategy] = shares; - } - - mapping (address => address) public delegatedTo; - - function registerAsOperator(OperatorDetails calldata /*registeringOperatorDetails*/, string calldata /*metadataURI*/) external pure {} - - function updateOperatorMetadataURI(string calldata /*metadataURI*/) external pure {} - - function updateAVSMetadataURI(string calldata /*metadataURI*/) external pure {} - - function delegateTo(address operator, SignatureWithExpiry memory /*approverSignatureAndExpiry*/, bytes32 /*approverSalt*/) external { - delegatedTo[msg.sender] = operator; - } - - function modifyOperatorDetails(OperatorDetails calldata /*newOperatorDetails*/) external pure {} - - function delegateToBySignature( - address /*staker*/, - address /*operator*/, - SignatureWithExpiry memory /*stakerSignatureAndExpiry*/, - SignatureWithExpiry memory /*approverSignatureAndExpiry*/, - bytes32 /*approverSalt*/ - ) external pure {} - - function undelegate(address staker) external returns (bytes32[] memory withdrawalRoot) { - delegatedTo[staker] = address(0); - return withdrawalRoot; - } - - function increaseDelegatedShares(address /*staker*/, IStrategy /*strategy*/, uint256 /*shares*/) external pure {} - - function decreaseDelegatedShares( - address /*staker*/, - IStrategy /*strategy*/, - uint256 /*shares*/ - ) external pure {} - - function operatorDetails(address operator) external pure returns (OperatorDetails memory) { - OperatorDetails memory returnValue = OperatorDetails({ - __deprecated_earningsReceiver: operator, - delegationApprover: operator, - stakerOptOutWindowBlocks: 0 - }); - return returnValue; - } - - function beaconChainETHStrategy() external pure returns (IStrategy) {} - - function earningsReceiver(address operator) external pure returns (address) { - return operator; - } - - function delegationApprover(address operator) external pure returns (address) { - return operator; - } - - function stakerOptOutWindowBlocks(address /*operator*/) external pure returns (uint256) { - return 0; - } - - function minWithdrawalDelayBlocks() external view returns (uint256) { - return 50400; - } - - /** - * @notice Minimum delay enforced by this contract per Strategy for completing queued withdrawals. Measured in blocks, and adjustable by this contract's owner, - * up to a maximum of `MAX_WITHDRAWAL_DELAY_BLOCKS`. Minimum value is 0 (i.e. no delay enforced). - */ - function strategyWithdrawalDelayBlocks(IStrategy /*strategy*/) external view returns (uint256) { - return 0; - } - - function getOperatorShares( - address operator, - IStrategy[] memory strategies - ) external view returns (uint256[] memory) { - uint256[] memory shares = new uint256[](strategies.length); - for (uint256 i = 0; i < strategies.length; ++i) { - shares[i] = operatorShares[operator][strategies[i]]; - } - return shares; - } - - function getWithdrawalDelay(IStrategy[] calldata /*strategies*/) public view returns (uint256) { - return 0; - } - - function isDelegated(address staker) external view returns (bool) { - return (delegatedTo[staker] != address(0)); - } - - function isNotDelegated(address /*staker*/) external pure returns (bool) {} - - // function isOperator(address /*operator*/) external pure returns (bool) {} - - function stakerNonce(address /*staker*/) external pure returns (uint256) {} - - function delegationApproverSaltIsSpent(address /*delegationApprover*/, bytes32 /*salt*/) external pure returns (bool) {} - - function calculateCurrentStakerDelegationDigestHash(address /*staker*/, address /*operator*/, uint256 /*expiry*/) external view returns (bytes32) {} - - function calculateStakerDelegationDigestHash(address /*staker*/, uint256 /*stakerNonce*/, address /*operator*/, uint256 /*expiry*/) external view returns (bytes32) {} - - function calculateDelegationApprovalDigestHash( - address /*staker*/, - address /*operator*/, - address /*_delegationApprover*/, - bytes32 /*approverSalt*/, - uint256 /*expiry*/ - ) external view returns (bytes32) {} - - function calculateStakerDigestHash(address /*staker*/, address /*operator*/, uint256 /*expiry*/) - external pure returns (bytes32 stakerDigestHash) {} - - function calculateApproverDigestHash(address /*staker*/, address /*operator*/, uint256 /*expiry*/) - external pure returns (bytes32 approverDigestHash) {} - - function calculateOperatorAVSRegistrationDigestHash(address /*operator*/, address /*avs*/, bytes32 /*salt*/, uint256 /*expiry*/) - external pure returns (bytes32 digestHash) {} - - function DOMAIN_TYPEHASH() external view returns (bytes32) {} - - function STAKER_DELEGATION_TYPEHASH() external view returns (bytes32) {} - - function DELEGATION_APPROVAL_TYPEHASH() external view returns (bytes32) {} - - function domainSeparator() external view returns (bytes32) {} - - function cumulativeWithdrawalsQueued(address staker) external view returns (uint256) {} - - function calculateWithdrawalRoot(Withdrawal memory withdrawal) external pure returns (bytes32) {} - - function operatorSaltIsSpent(address avs, bytes32 salt) external view returns (bool) {} - - function queueWithdrawals( - QueuedWithdrawalParams[] calldata queuedWithdrawalParams - ) external returns (bytes32[] memory) {} - - function completeQueuedWithdrawal( - Withdrawal calldata withdrawal, - IERC20[] calldata tokens, - uint256 middlewareTimesIndex, - bool receiveAsTokens - ) external {} - - function completeQueuedWithdrawals( - Withdrawal[] calldata withdrawals, - IERC20[][] calldata tokens, - uint256[] calldata middlewareTimesIndexes, - bool[] calldata receiveAsTokens - ) external {} - - // onlyDelegationManager functions in StrategyManager - function addShares( - IStrategyManager strategyManager, - address staker, - IERC20 token, - IStrategy strategy, - uint256 shares - ) external { - strategyManager.addShares(staker, token, strategy, shares); - } - - function removeShares( - IStrategyManager strategyManager, - address staker, - IStrategy strategy, - uint256 shares - ) external { - strategyManager.removeShares(staker, strategy, shares); - } - - function withdrawSharesAsTokens( - IStrategyManager strategyManager, - address recipient, - IStrategy strategy, - uint256 shares, - IERC20 token - ) external { - strategyManager.withdrawSharesAsTokens(recipient, strategy, shares, token); + using SlashingLib for uint256; + + mapping(address => bool) public isOperator; + mapping(address => mapping(IStrategy => uint256)) public operatorShares; + + function setIsOperator( + address operator, + bool _isOperatorReturnValue + ) external { + isOperator[operator] = _isOperatorReturnValue; + } + + /// @notice returns the total number of shares in `strategy` that are delegated to `operator`. + function setOperatorShares( + address operator, + IStrategy strategy, + uint256 shares + ) external { + operatorShares[operator][strategy] = shares; + } + + mapping(address => address) public delegatedTo; + + function registerAsOperator( + OperatorDetails calldata /*registeringOperatorDetails*/, + string calldata /*metadataURI*/ + ) external pure {} + + function updateOperatorMetadataURI( + string calldata /*metadataURI*/ + ) external pure {} + + function updateAVSMetadataURI( + string calldata /*metadataURI*/ + ) external pure {} + + function delegateTo( + address operator, + SignatureWithExpiry memory /*approverSignatureAndExpiry*/, + bytes32 /*approverSalt*/ + ) external { + delegatedTo[msg.sender] = operator; + } + + function modifyOperatorDetails( + OperatorDetails calldata /*newOperatorDetails*/ + ) external pure {} + + function delegateToBySignature( + address /*staker*/, + address /*operator*/, + SignatureWithExpiry memory /*stakerSignatureAndExpiry*/, + SignatureWithExpiry memory /*approverSignatureAndExpiry*/, + bytes32 /*approverSalt*/ + ) external pure {} + + function undelegate( + address staker + ) external returns (bytes32[] memory withdrawalRoot) { + delegatedTo[staker] = address(0); + return withdrawalRoot; + } + + function increaseDelegatedShares( + address /*staker*/, + IStrategy /*strategy*/, + uint256 /*shares*/ + ) external pure {} + + function operatorDetails( + address operator + ) external pure returns (OperatorDetails memory) { + OperatorDetails memory returnValue = OperatorDetails({ + __deprecated_earningsReceiver: operator, + delegationApprover: operator, + __deprecated_stakerOptOutWindowBlocks: 0 + }); + return returnValue; + } + + function beaconChainETHStrategy() external pure returns (IStrategy) {} + + function earningsReceiver(address operator) external pure returns (address) { + return operator; + } + + function delegationApprover( + address operator + ) external pure returns (address) { + return operator; + } + + function stakerOptOutWindowBlocks( + address /*operator*/ + ) external pure returns (uint256) { + return 0; + } + + function minWithdrawalDelayBlocks() external view returns (uint256) { + return 50400; + } + + /** + * @notice Minimum delay enforced by this contract per Strategy for completing queued withdrawals. Measured in blocks, and adjustable by this contract's owner, + * up to a maximum of `MAX_WITHDRAWAL_DELAY_BLOCKS`. Minimum value is 0 (i.e. no delay enforced). + */ + function strategyWithdrawalDelayBlocks( + IStrategy /*strategy*/ + ) external view returns (uint256) { + return 0; + } + + function getOperatorShares( + address operator, + IStrategy[] memory strategies + ) external view returns (uint256[] memory) { + uint256[] memory shares = new uint256[](strategies.length); + for (uint256 i = 0; i < strategies.length; ++i) { + shares[i] = operatorShares[operator][strategies[i]]; } + return shares; + } + + function getWithdrawalDelay( + IStrategy[] calldata /*strategies*/ + ) public view returns (uint256) { + return 0; + } + + function isDelegated(address staker) external view returns (bool) { + return (delegatedTo[staker] != address(0)); + } + + function isNotDelegated(address /*staker*/) external pure returns (bool) {} + + // function isOperator(address /*operator*/) external pure returns (bool) {} + + function stakerNonce(address /*staker*/) external pure returns (uint256) {} + + function delegationApproverSaltIsSpent( + address /*delegationApprover*/, + bytes32 /*salt*/ + ) external pure returns (bool) {} + + function calculateCurrentStakerDelegationDigestHash( + address /*staker*/, + address /*operator*/, + uint256 /*expiry*/ + ) external view returns (bytes32) {} + + function calculateStakerDelegationDigestHash( + address /*staker*/, + uint256 /*stakerNonce*/, + address /*operator*/, + uint256 /*expiry*/ + ) external view returns (bytes32) {} + + function calculateDelegationApprovalDigestHash( + address /*staker*/, + address /*operator*/, + address /*_delegationApprover*/, + bytes32 /*approverSalt*/, + uint256 /*expiry*/ + ) external view returns (bytes32) {} + + function calculateStakerDigestHash( + address /*staker*/, + address /*operator*/, + uint256 /*expiry*/ + ) external pure returns (bytes32 stakerDigestHash) {} + + function calculateApproverDigestHash( + address /*staker*/, + address /*operator*/, + uint256 /*expiry*/ + ) external pure returns (bytes32 approverDigestHash) {} + + function calculateOperatorAVSRegistrationDigestHash( + address /*operator*/, + address /*avs*/, + bytes32 /*salt*/, + uint256 /*expiry*/ + ) external pure returns (bytes32 digestHash) {} + + function DOMAIN_TYPEHASH() external view returns (bytes32) {} + + function STAKER_DELEGATION_TYPEHASH() external view returns (bytes32) {} + + function DELEGATION_APPROVAL_TYPEHASH() external view returns (bytes32) {} + + function domainSeparator() external view returns (bytes32) {} + + function cumulativeWithdrawalsQueued( + address staker + ) external view returns (uint256) {} + + function calculateWithdrawalRoot( + Withdrawal memory withdrawal + ) external pure returns (bytes32) {} + + function operatorSaltIsSpent( + address avs, + bytes32 salt + ) external view returns (bool) {} + + function queueWithdrawals( + QueuedWithdrawalParams[] calldata queuedWithdrawalParams + ) external returns (bytes32[] memory) {} + + function completeQueuedWithdrawal( + Withdrawal calldata withdrawal, + IERC20[] calldata tokens, + uint256 middlewareTimesIndex, + bool receiveAsTokens + ) external {} + + function completeQueuedWithdrawals( + Withdrawal[] calldata withdrawals, + IERC20[][] calldata tokens, + uint256[] calldata middlewareTimesIndexes, + bool[] calldata receiveAsTokens + ) external {} + + // onlyDelegationManager functions in StrategyManager + function addShares( + IStrategyManager strategyManager, + address staker, + IERC20 token, + IStrategy strategy, + uint256 shares + ) external { + strategyManager.addShares(staker, strategy, token, shares); + } + + function removeShares( + IStrategyManager strategyManager, + address staker, + IStrategy strategy, + uint256 shares + ) external { + strategyManager.removeDepositShares(staker, strategy, shares); + } + + function withdrawSharesAsTokens( + IStrategyManager strategyManager, + address recipient, + IStrategy strategy, + uint256 shares, + IERC20 token + ) external { + strategyManager.withdrawSharesAsTokens(recipient, strategy, token, shares); + } + + function registerAsOperator( + OperatorDetails calldata registeringOperatorDetails, + uint32 allocationDelay, + string calldata metadataURI + ) external override {} + + function completeQueuedWithdrawal( + Withdrawal calldata withdrawal, + IERC20[] calldata tokens, + bool receiveAsTokens + ) external override {} + + function completeQueuedWithdrawals( + Withdrawal[] calldata withdrawals, + IERC20[][] calldata tokens, + bool[] calldata receiveAsTokens + ) external override {} + + function decreaseBeaconChainScalingFactor( + address staker, + uint256 existingDepositShares, + uint64 proportionOfOldBalance + ) external override {} + + function decreaseOperatorShares( + address operator, + IStrategy strategy, + uint64 previousTotalMagnitude, + uint64 newTotalMagnitude + ) external override {} + + function increaseDelegatedShares( + address staker, + IStrategy strategy, + uint256 existingDepositShares, + uint256 addedShares + ) external override {} + + function initialize( + address initialOwner, + IPauserRegistry _pauserRegistry, + uint256 initialPausedStatus + ) external override {} + + function getOperatorsShares( + address[] memory operators, + IStrategy[] memory strategies + ) external view override returns (uint256[][] memory) {} + + function getWithdrawableShares( + address staker, + IStrategy[] memory strategies + ) external view override returns (uint256[] memory withdrawableShares) {} + + function getDepositedShares( + address staker + ) external view override returns (IStrategy[] memory, uint256[] memory) {} + + function getCompletableTimestamp( + uint32 startTimestamp + ) external view override returns (uint32 completableTimestamp) {} } diff --git a/test/mocks/ECDSAServiceManagerMock.sol b/test/mocks/ECDSAServiceManagerMock.sol index 88e3ac6f..5be1d82d 100644 --- a/test/mocks/ECDSAServiceManagerMock.sol +++ b/test/mocks/ECDSAServiceManagerMock.sol @@ -2,6 +2,7 @@ pragma solidity ^0.8.12; import "../../src/unaudited/ECDSAServiceManagerBase.sol"; +import {IAllocationManagerTypes} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; contract ECDSAServiceManagerMock is ECDSAServiceManagerBase { constructor( @@ -34,4 +35,8 @@ contract ECDSAServiceManagerMock is ECDSAServiceManagerBase { ) external {} function deregisterOperatorFromOperatorSets(address operator, uint32[] calldata operatorSetIds) external{} + + function slashOperator(IAllocationManagerTypes.SlashingParams memory params) external override { + // Mock implementation - no actual slashing occurs + } } diff --git a/test/mocks/EigenPodManagerMock.sol b/test/mocks/EigenPodManagerMock.sol new file mode 100644 index 00000000..afdd6189 --- /dev/null +++ b/test/mocks/EigenPodManagerMock.sol @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.9; + +import "forge-std/Test.sol"; +import "eigenlayer-contracts/src/contracts/permissions/Pausable.sol"; +import "eigenlayer-contracts/src/contracts/interfaces/IEigenPodManager.sol"; + +contract EigenPodManagerMock is Test, Pausable, IEigenPodManager { + receive() external payable {} + fallback() external payable {} + + mapping(address => int256) public podShares; + + constructor(IPauserRegistry _pauserRegistry) { + _initializePauser(_pauserRegistry, 0); + } + + function podOwnerShares(address podOwner) external view returns (int256) { + return podShares[podOwner]; + } + + function setPodOwnerShares(address podOwner, int256 shares) external { + podShares[podOwner] = shares; + } + + function denebForkTimestamp() external pure returns (uint64) { + return type(uint64).max; + } + + function createPod() external returns (address) { + } + + function stake(bytes calldata pubkey, bytes calldata signature, bytes32 depositDataRoot) external payable { + } + + function recordBeaconChainETHBalanceUpdate( + address podOwner, + int256 sharesDelta, + uint64 proportionPodBalanceDecrease + ) external { + } + + function ownerToPod(address podOwner) external view returns (IEigenPod) { + } + + function getPod(address podOwner) external view returns (IEigenPod) { + } + + function ethPOS() external view returns (IETHPOSDeposit) { + } + + function eigenPodBeacon() external view returns (IBeacon) { + } + + function strategyManager() external view returns (IStrategyManager) { + } + + function hasPod(address podOwner) external view returns (bool) { + } + + function numPods() external view returns (uint256) { + } + + function podOwnerDepositShares(address podOwner) external view returns (int256) { + } + + function beaconChainETHStrategy() external view returns (IStrategy) { + } + + function addShares(address staker, IStrategy strategy, IERC20 token, uint256 shares) external { + } + + function removeDepositShares(address staker, IStrategy strategy, uint256 depositSharesToRemove) external { + } + + function stakerDepositShares(address user, IStrategy strategy) external view returns (uint256 depositShares) { + } + + function withdrawSharesAsTokens(address staker, IStrategy strategy, IERC20 token, uint256 shares) external{} +} \ No newline at end of file diff --git a/test/mocks/RewardsCoordinatorMock.sol b/test/mocks/RewardsCoordinatorMock.sol index 2be3cd6c..cc8404a0 100644 --- a/test/mocks/RewardsCoordinatorMock.sol +++ b/test/mocks/RewardsCoordinatorMock.sol @@ -3,112 +3,131 @@ pragma solidity ^0.8.12; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import {IPauserRegistry} from "eigenlayer-contracts/src/contracts/interfaces/IPauserRegistry.sol"; import {IRewardsCoordinator} from "eigenlayer-contracts/src/contracts/interfaces/IRewardsCoordinator.sol"; import "./AVSDirectoryMock.sol"; contract RewardsCoordinatorMock is IRewardsCoordinator { - /// @notice The address of the entity that can update the contract with new merkle roots - function rewardsUpdater() external view returns (address) {} + /// @notice The address of the entity that can update the contract with new merkle roots + function rewardsUpdater() external view returns (address) {} - function CALCULATION_INTERVAL_SECONDS() external view returns (uint32) {} + function CALCULATION_INTERVAL_SECONDS() external view returns (uint32) {} - function MAX_REWARDS_DURATION() external view returns (uint32) {} + function MAX_REWARDS_DURATION() external view returns (uint32) {} - function MAX_RETROACTIVE_LENGTH() external view returns (uint32) {} + function MAX_RETROACTIVE_LENGTH() external view returns (uint32) {} - function MAX_FUTURE_LENGTH() external view returns (uint32) {} + function MAX_FUTURE_LENGTH() external view returns (uint32) {} - function GENESIS_REWARDS_TIMESTAMP() external view returns (uint32) {} + function GENESIS_REWARDS_TIMESTAMP() external view returns (uint32) {} - function activationDelay() external view returns (uint32) {} + function activationDelay() external view returns (uint32) {} - function claimerFor(address earner) external view returns (address) {} + function claimerFor(address earner) external view returns (address) {} - function cumulativeClaimed(address claimer, IERC20 token) external view returns (uint256) {} + function cumulativeClaimed( + address claimer, + IERC20 token + ) external view returns (uint256) {} - /// @notice the commission for a specific operator for a specific avs - /// NOTE: Currently unused and simply returns the globalOperatorCommissionBips value but will be used in future release - function getOperatorCommissionBips( - address operator, - IAVSDirectory.OperatorSet calldata operatorSet, - RewardType rewardType - ) external view returns (uint16) {} + function globalOperatorCommissionBips() external view returns (uint16) {} - /// @notice returns the length of the operator commission update history - function getOperatorCommissionUpdateHistoryLength( - address operator, - IAVSDirectory.OperatorSet calldata operatorSet, - RewardType rewardType - ) external view returns (uint256) {} + function operatorCommissionBips( + address operator, + address avs + ) external view returns (uint16) {} - function globalOperatorCommissionBips() external view returns (uint16) {} + function calculateEarnerLeafHash( + EarnerTreeMerkleLeaf calldata leaf + ) external pure returns (bytes32) {} - function operatorCommissionBips(address operator, address avs) external view returns (uint16) {} + function calculateTokenLeafHash( + TokenTreeMerkleLeaf calldata leaf + ) external pure returns (bytes32) {} - function calculateEarnerLeafHash(EarnerTreeMerkleLeaf calldata leaf) external pure returns (bytes32) {} + function checkClaim( + RewardsMerkleClaim calldata claim + ) external view returns (bool) {} - function calculateTokenLeafHash(TokenTreeMerkleLeaf calldata leaf) external pure returns (bytes32) {} + function currRewardsCalculationEndTimestamp() + external + view + returns (uint32) + {} - function checkClaim(RewardsMerkleClaim calldata claim) external view returns (bool) {} + function getRootIndexFromHash( + bytes32 rootHash + ) external view returns (uint32) {} - function currRewardsCalculationEndTimestamp() external view returns (uint32) {} + function getDistributionRootsLength() external view returns (uint256) {} - function getRootIndexFromHash(bytes32 rootHash) external view returns (uint32) {} + function getDistributionRootAtIndex( + uint256 index + ) external view returns (DistributionRoot memory) {} - function getDistributionRootsLength() external view returns (uint256) {} + function getCurrentClaimableDistributionRoot() + external + view + returns (DistributionRoot memory) + {} - function getDistributionRootAtIndex(uint256 index) external view returns (DistributionRoot memory) {} + function getCurrentDistributionRoot() + external + view + returns (DistributionRoot memory) + {} - function getCurrentClaimableDistributionRoot() external view returns (DistributionRoot memory) {} + /// EXTERNAL FUNCTIONS /// - function getCurrentDistributionRoot() external view returns (DistributionRoot memory) {} + function disableRoot(uint32 rootIndex) external {} - /// EXTERNAL FUNCTIONS /// + function createAVSRewardsSubmission( + RewardsSubmission[] calldata rewardsSubmissions + ) external {} - function disableRoot(uint32 rootIndex) external {} + function createRewardsForAllSubmission( + RewardsSubmission[] calldata rewardsSubmission + ) external {} - function createAVSRewardsSubmission(RewardsSubmission[] calldata rewardsSubmissions) external {} + function processClaim( + RewardsMerkleClaim calldata claim, + address recipient + ) external {} - function createRewardsForAllSubmission(RewardsSubmission[] calldata rewardsSubmission) external {} + function submitRoot( + bytes32 root, + uint32 rewardsCalculationEndTimestamp + ) external {} - function processClaim(RewardsMerkleClaim calldata claim, address recipient) external {} + function setRewardsUpdater(address _rewardsUpdater) external {} - function submitRoot( - bytes32 root, - uint32 rewardsCalculationEndTimestamp - ) external {} + function setActivationDelay(uint32 _activationDelay) external {} - function setRewardsUpdater(address _rewardsUpdater) external {} + function setGlobalOperatorCommission(uint16 _globalCommissionBips) external {} - function setActivationDelay(uint32 _activationDelay) external {} + function setClaimerFor(address claimer) external {} - function setGlobalOperatorCommission(uint16 _globalCommissionBips) external {} + /** + * @notice Sets the permissioned `payAllForRangeSubmitter` address which can submit payAllForRange + * @dev Only callable by the contract owner + * @param _submitter The address of the payAllForRangeSubmitter + * @param _newValue The new value for isPayAllForRangeSubmitter + */ + function setRewardsForAllSubmitter( + address _submitter, + bool _newValue + ) external {} - function setClaimerFor(address claimer) external {} - - /** - * @notice Sets the permissioned `payAllForRangeSubmitter` address which can submit payAllForRange - * @dev Only callable by the contract owner - * @param _submitter The address of the payAllForRangeSubmitter - * @param _newValue The new value for isPayAllForRangeSubmitter - */ - function setRewardsForAllSubmitter(address _submitter, bool _newValue) external {} - - /** - * @notice Sets the commission an operator takes in bips for a given reward type and operatorSet - * @param operatorSet The operatorSet to update commission for - * @param rewardType The associated rewardType to update commission for - * @param commissionBips The commission in bips for the operator, must be <= MAX_COMMISSION_BIPS - * @return effectTimestamp The timestamp at which the operator commission update will take effect - * - * @dev The commission can range from 1 to 10000 - * @dev The commission update takes effect after 7 days - */ - function setOperatorCommissionBips( - IAVSDirectory.OperatorSet calldata operatorSet, - RewardType rewardType, - uint16 commissionBips - ) external returns (uint32) {} - - function rewardOperatorSetForRange(OperatorSetRewardsSubmission[] calldata rewardsSubmissions) external{} + function createRewardsForAllEarners( + RewardsSubmission[] calldata rewardsSubmissions + ) external override {} + + function initialize( + address initialOwner, + IPauserRegistry _pauserRegistry, + uint256 initialPausedStatus, + address _rewardsUpdater, + uint32 _activationDelay, + uint16 _globalCommissionBips + ) external override {} } \ No newline at end of file diff --git a/test/mocks/ServiceManagerMock.sol b/test/mocks/ServiceManagerMock.sol index 8af99426..a9bb5fff 100644 --- a/test/mocks/ServiceManagerMock.sol +++ b/test/mocks/ServiceManagerMock.sol @@ -8,15 +8,23 @@ contract ServiceManagerMock is ServiceManagerBase { IAVSDirectory _avsDirectory, IRewardsCoordinator _rewardsCoordinator, IRegistryCoordinator _registryCoordinator, - IStakeRegistry _stakeRegistry + IStakeRegistry _stakeRegistry, + IAllocationManager _allocationManager ) - ServiceManagerBase(_avsDirectory, _rewardsCoordinator, _registryCoordinator, _stakeRegistry) + ServiceManagerBase( + _avsDirectory, + _rewardsCoordinator, + _registryCoordinator, + _stakeRegistry, + _allocationManager + ) {} function initialize( address initialOwner, - address rewardsInitiator + address rewardsInitiator, + address slasher ) public virtual initializer { - __ServiceManagerBase_init(initialOwner, rewardsInitiator); + __ServiceManagerBase_init(initialOwner, rewardsInitiator, slasher); } } diff --git a/test/unit/ECDSAServiceManager.t.sol b/test/unit/ECDSAServiceManager.t.sol index 3b533d47..1093b77a 100644 --- a/test/unit/ECDSAServiceManager.t.sol +++ b/test/unit/ECDSAServiceManager.t.sol @@ -145,14 +145,15 @@ contract ECDSAServiceManagerSetup is Test { shares[0] = 0; shares[1] = 1; - vm.mockCall( - address(mockDelegationManager), - abi.encodeCall( - IDelegationManager.getOperatorShares, - (operator, strategies) - ), - abi.encode(shares) - ); + // TODO: Fix + // vm.mockCall( + // address(mockDelegationManager), + // abi.encodeCall( + // IDelegationManager.getOperatorShares, + // (operator, strategies) + // ), + // abi.encode(shares) + // ); address[] memory restakedStrategies = serviceManager .getOperatorRestakedStrategies(operator); diff --git a/test/unit/RegistryCoordinatorMigration.t.sol b/test/unit/RegistryCoordinatorMigration.t.sol index 99fd38f4..63e4c837 100644 --- a/test/unit/RegistryCoordinatorMigration.t.sol +++ b/test/unit/RegistryCoordinatorMigration.t.sol @@ -5,12 +5,16 @@ import "@openzeppelin/contracts/token/ERC20/presets/ERC20PresetFixedSupply.sol"; import { RewardsCoordinator, IRewardsCoordinator, + IRewardsCoordinatorTypes, IERC20 } from "eigenlayer-contracts/src/contracts/core/RewardsCoordinator.sol"; import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; import {StrategyBase} from "eigenlayer-contracts/src/contracts/strategies/StrategyBase.sol"; import {IServiceManagerBaseEvents} from "../events/IServiceManagerBaseEvents.sol"; +import {IAVSDirectoryTypes} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; +import {IStrategyManager} from "eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol"; import {AVSDirectoryHarness} from "../harnesses/AVSDirectoryHarness.sol"; +import {OperatorSet} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; import "../utils/MockAVSDeployer.sol"; @@ -55,7 +59,8 @@ contract RegistryCoordinatorMigrationUnit is MockAVSDeployer, IServiceManagerBas avsDirectory, IRewardsCoordinator(address(rewardsCoordinatorMock)), registryCoordinator, - stakeRegistry + stakeRegistry, + allocationManager ); avsDirectoryHarness = new AVSDirectoryHarness(delegationMock); @@ -63,7 +68,8 @@ contract RegistryCoordinatorMigrationUnit is MockAVSDeployer, IServiceManagerBas avsDirectory, rewardsCoordinatorMock, registryCoordinator, - stakeRegistry + stakeRegistry, + allocationManager ); /// Needed to upgrade to a service manager that points to an AVS Directory that can track state vm.prank(proxyAdmin.owner()); @@ -90,7 +96,7 @@ contract RegistryCoordinatorMigrationUnit is MockAVSDeployer, IServiceManagerBas IERC20 token3 = new ERC20PresetFixedSupply( "pepe wif avs", "MOCK3", mockTokenInitialSupply, address(this) ); - strategyImplementation = new StrategyBase(strategyManagerMock); + strategyImplementation = new StrategyBase(IStrategyManager(address(strategyManagerMock))); strategyMock1 = StrategyBase( address( new TransparentUpgradeableProxy( @@ -129,13 +135,13 @@ contract RegistryCoordinatorMigrationUnit is MockAVSDeployer, IServiceManagerBas strategyManagerMock.setStrategyWhitelist(strategies[2], true); defaultStrategyAndMultipliers.push( - IRewardsCoordinator.StrategyAndMultiplier(IStrategy(address(strategies[0])), 1e18) + IRewardsCoordinatorTypes.StrategyAndMultiplier(IStrategy(address(strategies[0])), 1e18) ); defaultStrategyAndMultipliers.push( - IRewardsCoordinator.StrategyAndMultiplier(IStrategy(address(strategies[1])), 2e18) + IRewardsCoordinatorTypes.StrategyAndMultiplier(IStrategy(address(strategies[1])), 2e18) ); defaultStrategyAndMultipliers.push( - IRewardsCoordinator.StrategyAndMultiplier(IStrategy(address(strategies[2])), 3e18) + IRewardsCoordinatorTypes.StrategyAndMultiplier(IStrategy(address(strategies[2])), 3e18) ); } @@ -226,7 +232,7 @@ contract RegistryCoordinatorMigrationUnit is MockAVSDeployer, IServiceManagerBas AVSDirectoryHarness(address(avsDirectory)).setAvsOperatorStatus( address(serviceManager), operatorAddress, - IAVSDirectory.OperatorAVSRegistrationStatus.REGISTERED + IAVSDirectoryTypes.OperatorAVSRegistrationStatus.REGISTERED ); } } @@ -263,7 +269,7 @@ contract RegistryCoordinatorMigrationUnit is MockAVSDeployer, IServiceManagerBas }) ); // sanity check if the operator was unregistered from the intended operator set - bool operatorIsUnRegistered = !avsDirectory.isMember(operators[0], IAVSDirectory.OperatorSet({ + bool operatorIsUnRegistered = !avsDirectory.isMember(operators[0], OperatorSet({ avs: address(serviceManager), operatorSetId: defaultQuorumNumber })); @@ -311,7 +317,7 @@ contract RegistryCoordinatorMigrationUnit is MockAVSDeployer, IServiceManagerBas AVSDirectoryHarness(address(avsDirectory)).setAvsOperatorStatus( address(serviceManager), operatorAddress, - IAVSDirectory.OperatorAVSRegistrationStatus.REGISTERED + IAVSDirectoryTypes.OperatorAVSRegistrationStatus.REGISTERED ); } } @@ -337,7 +343,7 @@ contract RegistryCoordinatorMigrationUnit is MockAVSDeployer, IServiceManagerBas address operatorToDeregister = operators[0]; - bool isOperatorRegistered = avsDirectory.isMember(operatorToDeregister, IAVSDirectory.OperatorSet({ + bool isOperatorRegistered = avsDirectory.isMember(operatorToDeregister, OperatorSet({ avs: address(serviceManager), operatorSetId: defaultQuorumNumber })); @@ -352,7 +358,7 @@ contract RegistryCoordinatorMigrationUnit is MockAVSDeployer, IServiceManagerBas registryCoordinator.deregisterOperator(quorumNumbers); cheats.stopPrank(); - isOperatorRegistered = avsDirectory.isMember(operatorToDeregister, IAVSDirectory.OperatorSet({ + isOperatorRegistered = avsDirectory.isMember(operatorToDeregister, OperatorSet({ avs: address(serviceManager), operatorSetId: defaultQuorumNumber })); @@ -386,7 +392,7 @@ contract RegistryCoordinatorMigrationUnit is MockAVSDeployer, IServiceManagerBas AVSDirectoryHarness(address(avsDirectory)).setAvsOperatorStatus( address(serviceManager), operatorAddress, - IAVSDirectory.OperatorAVSRegistrationStatus.REGISTERED + IAVSDirectoryTypes.OperatorAVSRegistrationStatus.REGISTERED ); } } @@ -413,7 +419,7 @@ contract RegistryCoordinatorMigrationUnit is MockAVSDeployer, IServiceManagerBas uint256 operatorPk = uint256(keccak256("operator to register")); address operatorToRegister = vm.addr(operatorPk) ; - bool isOperatorRegistered = avsDirectory.isMember(operatorToRegister, IAVSDirectory.OperatorSet({ + bool isOperatorRegistered = avsDirectory.isMember(operatorToRegister, OperatorSet({ avs: address(serviceManager), operatorSetId: defaultQuorumNumber })); @@ -453,7 +459,7 @@ contract RegistryCoordinatorMigrationUnit is MockAVSDeployer, IServiceManagerBas registryCoordinator.registerOperator(quorumNumbers, "", params, operatorSignature); cheats.stopPrank(); - isOperatorRegistered = avsDirectory.isMember(operatorToRegister, IAVSDirectory.OperatorSet({ + isOperatorRegistered = avsDirectory.isMember(operatorToRegister, OperatorSet({ avs: address(serviceManager), operatorSetId: defaultQuorumNumber })); diff --git a/test/unit/ServiceManagerBase.t.sol b/test/unit/ServiceManagerBase.t.sol index eba791c3..1d333814 100644 --- a/test/unit/ServiceManagerBase.t.sol +++ b/test/unit/ServiceManagerBase.t.sol @@ -5,9 +5,11 @@ import "@openzeppelin/contracts/token/ERC20/presets/ERC20PresetFixedSupply.sol"; import { RewardsCoordinator, IRewardsCoordinator, + IRewardsCoordinatorTypes, IERC20 } from "eigenlayer-contracts/src/contracts/core/RewardsCoordinator.sol"; import {StrategyBase} from "eigenlayer-contracts/src/contracts/strategies/StrategyBase.sol"; +import {IStrategyManager} from "eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol"; import {IServiceManagerBaseEvents} from "../events/IServiceManagerBaseEvents.sol"; import "../utils/MockAVSDeployer.sol"; @@ -53,15 +55,12 @@ contract ServiceManagerBase_UnitTests is MockAVSDeployer, IServiceManagerBaseEve // Deploy rewards coordinator rewardsCoordinatorImplementation = new RewardsCoordinator( delegationMock, - strategyManagerMock, - avsDirectoryMock, + IStrategyManager(address(strategyManagerMock)), CALCULATION_INTERVAL_SECONDS, MAX_REWARDS_DURATION, MAX_RETROACTIVE_LENGTH, MAX_FUTURE_LENGTH, - GENESIS_REWARDS_TIMESTAMP, - OPERATOR_SET_GENESIS_REWARDS_TIMESTAMP, - OPERATOR_SET_MAX_RETROACTIVE_LENGTH + GENESIS_REWARDS_TIMESTAMP ); rewardsCoordinator = RewardsCoordinator( @@ -86,7 +85,8 @@ contract ServiceManagerBase_UnitTests is MockAVSDeployer, IServiceManagerBaseEve avsDirectory, rewardsCoordinator, registryCoordinatorImplementation, - stakeRegistryImplementation + stakeRegistryImplementation, + allocationManagerImplementation ); serviceManager = ServiceManagerMock( @@ -146,7 +146,7 @@ contract ServiceManagerBase_UnitTests is MockAVSDeployer, IServiceManagerBaseEve IERC20 token3 = new ERC20PresetFixedSupply( "pepe wif avs", "MOCK3", mockTokenInitialSupply, address(this) ); - strategyImplementation = new StrategyBase(strategyManagerMock); + strategyImplementation = new StrategyBase(IStrategyManager(address(strategyManagerMock))); strategyMock1 = StrategyBase( address( new TransparentUpgradeableProxy( @@ -185,13 +185,13 @@ contract ServiceManagerBase_UnitTests is MockAVSDeployer, IServiceManagerBaseEve strategyManagerMock.setStrategyWhitelist(strategies[2], true); defaultStrategyAndMultipliers.push( - IRewardsCoordinator.StrategyAndMultiplier(IStrategy(address(strategies[0])), 1e18) + IRewardsCoordinatorTypes.StrategyAndMultiplier(IStrategy(address(strategies[0])), 1e18) ); defaultStrategyAndMultipliers.push( - IRewardsCoordinator.StrategyAndMultiplier(IStrategy(address(strategies[1])), 2e18) + IRewardsCoordinatorTypes.StrategyAndMultiplier(IStrategy(address(strategies[1])), 2e18) ); defaultStrategyAndMultipliers.push( - IRewardsCoordinator.StrategyAndMultiplier(IStrategy(address(strategies[2])), 3e18) + IRewardsCoordinatorTypes.StrategyAndMultiplier(IStrategy(address(strategies[2])), 3e18) ); } @@ -233,9 +233,9 @@ contract ServiceManagerBase_UnitTests is MockAVSDeployer, IServiceManagerBaseEve "dog wif hat", "MOCK1", mockTokenInitialSupply, rewardsInitiator ); - IRewardsCoordinator.RewardsSubmission[] memory rewardsSubmissions = - new IRewardsCoordinator.RewardsSubmission[](1); - rewardsSubmissions[0] = IRewardsCoordinator.RewardsSubmission({ + IRewardsCoordinatorTypes.RewardsSubmission[] memory rewardsSubmissions = + new IRewardsCoordinatorTypes.RewardsSubmission[](1); + rewardsSubmissions[0] = IRewardsCoordinatorTypes.RewardsSubmission({ strategiesAndMultipliers: defaultStrategyAndMultipliers, token: token, amount: 100, @@ -272,9 +272,9 @@ contract ServiceManagerBase_UnitTests is MockAVSDeployer, IServiceManagerBaseEve startTimestamp = startTimestamp - (startTimestamp % CALCULATION_INTERVAL_SECONDS); // 2. Create reward submission input param - IRewardsCoordinator.RewardsSubmission[] memory rewardsSubmissions = - new IRewardsCoordinator.RewardsSubmission[](1); - rewardsSubmissions[0] = IRewardsCoordinator.RewardsSubmission({ + IRewardsCoordinatorTypes.RewardsSubmission[] memory rewardsSubmissions = + new IRewardsCoordinatorTypes.RewardsSubmission[](1); + rewardsSubmissions[0] = IRewardsCoordinatorTypes.RewardsSubmission({ strategiesAndMultipliers: defaultStrategyAndMultipliers, token: rewardToken, amount: amount, @@ -365,7 +365,7 @@ contract ServiceManagerBase_UnitTests is MockAVSDeployer, IServiceManagerBaseEve startTimestamp = startTimestamp - (startTimestamp % CALCULATION_INTERVAL_SECONDS); // 2. Create reward submission input param - IRewardsCoordinator.RewardsSubmission memory rewardsSubmission = IRewardsCoordinator.RewardsSubmission({ + IRewardsCoordinatorTypes.RewardsSubmission memory rewardsSubmission = IRewardsCoordinatorTypes.RewardsSubmission({ strategiesAndMultipliers: defaultStrategyAndMultipliers, token: rewardTokens[i], amount: amounts[i], @@ -463,7 +463,7 @@ contract ServiceManagerBase_UnitTests is MockAVSDeployer, IServiceManagerBaseEve startTimestamp = startTimestamp - (startTimestamp % CALCULATION_INTERVAL_SECONDS); // 2. Create reward submission input param - IRewardsCoordinator.RewardsSubmission memory rewardsSubmission = IRewardsCoordinator.RewardsSubmission({ + IRewardsCoordinatorTypes.RewardsSubmission memory rewardsSubmission = IRewardsCoordinatorTypes.RewardsSubmission({ strategiesAndMultipliers: defaultStrategyAndMultipliers, token: rewardToken, amount: amounts[i], diff --git a/test/unit/ServiceManagerMigration.t.sol b/test/unit/ServiceManagerMigration.t.sol index 69cd4c7c..67350529 100644 --- a/test/unit/ServiceManagerMigration.t.sol +++ b/test/unit/ServiceManagerMigration.t.sol @@ -5,11 +5,15 @@ import "@openzeppelin/contracts/token/ERC20/presets/ERC20PresetFixedSupply.sol"; import { RewardsCoordinator, IRewardsCoordinator, + IRewardsCoordinatorTypes, IERC20 } from "eigenlayer-contracts/src/contracts/core/RewardsCoordinator.sol"; import {StrategyBase} from "eigenlayer-contracts/src/contracts/strategies/StrategyBase.sol"; import {IServiceManagerBaseEvents} from "../events/IServiceManagerBaseEvents.sol"; +import {IAVSDirectoryTypes} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; +import {IStrategyManager} from "eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol"; import {AVSDirectoryHarness} from "../harnesses/AVSDirectoryHarness.sol"; +import {OperatorSet} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; import "../utils/MockAVSDeployer.sol"; @@ -59,15 +63,12 @@ contract ServiceManagerMigration_UnitTests is MockAVSDeployer, IServiceManagerBa // Deploy rewards coordinator rewardsCoordinatorImplementation = new RewardsCoordinator( delegationMock, - strategyManagerMock, - avsDirectoryMock, + IStrategyManager(address(strategyManagerMock)), CALCULATION_INTERVAL_SECONDS, MAX_REWARDS_DURATION, MAX_RETROACTIVE_LENGTH, MAX_FUTURE_LENGTH, - GENESIS_REWARDS_TIMESTAMP, - OPERATOR_SET_GENESIS_REWARDS_TIMESTAMP, - OPERATOR_SET_MAX_RETROACTIVE_LENGTH + GENESIS_REWARDS_TIMESTAMP ); rewardsCoordinator = RewardsCoordinator( @@ -89,7 +90,7 @@ contract ServiceManagerMigration_UnitTests is MockAVSDeployer, IServiceManagerBa ); // Deploy ServiceManager serviceManagerImplementation = new ServiceManagerMock( - avsDirectory, rewardsCoordinator, registryCoordinator, stakeRegistry + avsDirectory, rewardsCoordinator, registryCoordinator, stakeRegistry, allocationManager ); serviceManager = ServiceManagerMock( @@ -126,7 +127,7 @@ contract ServiceManagerMigration_UnitTests is MockAVSDeployer, IServiceManagerBa IERC20 token3 = new ERC20PresetFixedSupply( "pepe wif avs", "MOCK3", mockTokenInitialSupply, address(this) ); - strategyImplementation = new StrategyBase(strategyManagerMock); + strategyImplementation = new StrategyBase(IStrategyManager(address(strategyManagerMock))); strategyMock1 = StrategyBase( address( new TransparentUpgradeableProxy( @@ -165,13 +166,13 @@ contract ServiceManagerMigration_UnitTests is MockAVSDeployer, IServiceManagerBa strategyManagerMock.setStrategyWhitelist(strategies[2], true); defaultStrategyAndMultipliers.push( - IRewardsCoordinator.StrategyAndMultiplier(IStrategy(address(strategies[0])), 1e18) + IRewardsCoordinatorTypes.StrategyAndMultiplier(IStrategy(address(strategies[0])), 1e18) ); defaultStrategyAndMultipliers.push( - IRewardsCoordinator.StrategyAndMultiplier(IStrategy(address(strategies[1])), 2e18) + IRewardsCoordinatorTypes.StrategyAndMultiplier(IStrategy(address(strategies[1])), 2e18) ); defaultStrategyAndMultipliers.push( - IRewardsCoordinator.StrategyAndMultiplier(IStrategy(address(strategies[2])), 3e18) + IRewardsCoordinatorTypes.StrategyAndMultiplier(IStrategy(address(strategies[2])), 3e18) ); } @@ -320,7 +321,7 @@ contract ServiceManagerMigration_UnitTests is MockAVSDeployer, IServiceManagerBa AVSDirectoryHarness(address(avsDirectory)).setAvsOperatorStatus( address(serviceManager), operatorAddress, - IAVSDirectory.OperatorAVSRegistrationStatus.REGISTERED + IAVSDirectoryTypes.OperatorAVSRegistrationStatus.REGISTERED ); } } @@ -339,7 +340,7 @@ contract ServiceManagerMigration_UnitTests is MockAVSDeployer, IServiceManagerBa assertTrue( avsDirectory.isMember( 0x73e2Ce949f15Be901f76b54F5a4554A6C8DCf539, - IAVSDirectory.OperatorSet(address(serviceManager), uint32(3)) + OperatorSet(address(serviceManager), uint32(3)) ), "Operator not migrated to operator set" ); diff --git a/test/unit/ServiceManagerRouter.t.sol b/test/unit/ServiceManagerRouter.t.sol index 9fc2c0f7..6706ade4 100644 --- a/test/unit/ServiceManagerRouter.t.sol +++ b/test/unit/ServiceManagerRouter.t.sol @@ -19,7 +19,8 @@ contract ServiceManagerRouter_UnitTests is MockAVSDeployer { avsDirectory, rewardsCoordinatorImplementation, registryCoordinatorImplementation, - stakeRegistryImplementation + stakeRegistryImplementation, + allocationManagerImplementation ); _registerOperatorWithCoordinator(defaultOperator, MAX_QUORUM_BITMAP, defaultPubKey); diff --git a/test/utils/MockAVSDeployer.sol b/test/utils/MockAVSDeployer.sol index b06364a8..af8ebe78 100644 --- a/test/utils/MockAVSDeployer.sol +++ b/test/utils/MockAVSDeployer.sol @@ -4,8 +4,6 @@ pragma solidity ^0.8.12; import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; -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 {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; @@ -26,8 +24,9 @@ import {IRegistryCoordinator} from "../../src/interfaces/IRegistryCoordinator.so import {IServiceManager} from "../../src/interfaces/IServiceManager.sol"; import {StrategyManagerMock} from "eigenlayer-contracts/src/test/mocks/StrategyManagerMock.sol"; -import {EigenPodManagerMock} from "eigenlayer-contracts/src/test/mocks/EigenPodManagerMock.sol"; +import {EigenPodManagerMock} from "../mocks/EigenPodManagerMock.sol"; import {AVSDirectoryMock} from "../mocks/AVSDirectoryMock.sol"; +import {AllocationManagerMock} from "../mocks/AllocationManagerMock.sol"; import {DelegationMock} from "../mocks/DelegationMock.sol"; import {AVSDirectory} from "eigenlayer-contracts/src/contracts/core/AVSDirectory.sol"; import {IAVSDirectory} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; @@ -53,9 +52,6 @@ contract MockAVSDeployer is Test { ProxyAdmin public proxyAdmin; PauserRegistry public pauserRegistry; - ISlasher public slasher = ISlasher(address(uint160(uint256(keccak256("slasher"))))); - Slasher public slasherImplementation; - EmptyContract public emptyContract; RegistryCoordinatorHarness public registryCoordinatorImplementation; @@ -63,6 +59,7 @@ contract MockAVSDeployer is Test { IBLSApkRegistry public blsApkRegistryImplementation; IIndexRegistry public indexRegistryImplementation; ServiceManagerMock public serviceManagerImplementation; + AllocationManagerMock public allocationManagerImplementation; OperatorStateRetriever public operatorStateRetriever; RegistryCoordinatorHarness public registryCoordinator; @@ -70,6 +67,7 @@ contract MockAVSDeployer is Test { BLSApkRegistryHarness public blsApkRegistry; IIndexRegistry public indexRegistry; ServiceManagerMock public serviceManager; + AllocationManagerMock public allocationManager; StrategyManagerMock public strategyManagerMock; DelegationMock public delegationMock; @@ -77,6 +75,7 @@ contract MockAVSDeployer is Test { AVSDirectory public avsDirectory; AVSDirectory public avsDirectoryImplementation; AVSDirectoryMock public avsDirectoryMock; + AllocationManagerMock public allocationManagerMock; RewardsCoordinator public rewardsCoordinator; RewardsCoordinator public rewardsCoordinatorImplementation; RewardsCoordinatorMock public rewardsCoordinatorMock; @@ -150,23 +149,10 @@ contract MockAVSDeployer is Test { avsDirectoryMock = new AVSDirectoryMock(); eigenPodManagerMock = new EigenPodManagerMock(pauserRegistry); strategyManagerMock = new StrategyManagerMock(); - slasherImplementation = new Slasher(strategyManagerMock, delegationMock); - slasher = Slasher( - address( - new TransparentUpgradeableProxy( - address(slasherImplementation), - address(proxyAdmin), - abi.encodeWithSelector( - Slasher.initialize.selector, - msg.sender, - pauserRegistry, - 0 /*initialPausedStatus*/ - ) - ) - ) - ); + allocationManagerMock = new AllocationManagerMock(); avsDirectoryMock = new AVSDirectoryMock(); - avsDirectoryImplementation = new AVSDirectory(delegationMock); + allocationManagerMock = new AllocationManagerMock(); + avsDirectoryImplementation = new AVSDirectory(delegationMock, 0); // TODO: config value avsDirectory = AVSDirectory( address( new TransparentUpgradeableProxy( @@ -183,7 +169,7 @@ contract MockAVSDeployer is Test { ); rewardsCoordinatorMock = new RewardsCoordinatorMock(); - strategyManagerMock.setAddresses(delegationMock, eigenPodManagerMock, slasher); + strategyManagerMock.setDelegationManager(delegationMock); cheats.stopPrank(); cheats.startPrank(registryCoordinatorOwner); @@ -217,6 +203,12 @@ contract MockAVSDeployer is Test { ) ); + allocationManager = AllocationManagerMock( + address( + new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") + ) + ); + cheats.stopPrank(); cheats.startPrank(proxyAdminOwner); @@ -247,7 +239,8 @@ contract MockAVSDeployer is Test { avsDirectoryMock, IRewardsCoordinator(address(rewardsCoordinatorMock)), registryCoordinator, - stakeRegistry + stakeRegistry, + allocationManager ); proxyAdmin.upgrade( @@ -255,9 +248,17 @@ contract MockAVSDeployer is Test { address(serviceManagerImplementation) ); + allocationManagerImplementation = new AllocationManagerMock(); + + proxyAdmin.upgrade( + TransparentUpgradeableProxy(payable(address(allocationManager))), + address(allocationManagerImplementation) + ); + serviceManager.initialize({ initialOwner: registryCoordinatorOwner, - rewardsInitiator: address(proxyAdminOwner) + rewardsInitiator: proxyAdminOwner, + slasher: proxyAdminOwner }); // set the public key for an operator, using harnessed function to bypass checks From cb4df12b8a59ad9f5f437f12c0ceae51581bbbda Mon Sep 17 00:00:00 2001 From: steven <12021290+stevennevins@users.noreply.github.com> Date: Fri, 1 Nov 2024 11:59:28 -0400 Subject: [PATCH 19/27] feat: slasher templates / examples (#310) * chore: bump to slashing branch * chore: bump compiler version * fix: dep interface changes * fix: compiler errors from interface changes and type changes * fix: compiler errors * chore: bump dependencies * chore: bump core dependency and resolve issues * chore: bump core dependency and fix compiler errors * feat: integrate AllocationManager * feat: add a slashing permission to the service manager * chore: remove unneeded casting * feat: implement a slasher permission and forward call to AllocationManager * feat: add simiple slasher starting point * feat: slashers * chore: change around slashed event * fix: call dm * feat: add proposal mechanism for updating slasher * fix: set to completed instead of delete * chore: use struct instead of params directly * chore: clean up params more * chore: simplify and organize files * chore: cleanup logic and couple event with internal func * fix: pass correct params * chore: organize and add interface * chore: nits * chore: cleanup more nits * fix: storage gap * chore: nits refactor * chore: go back to fulfill being onlySlasher * test: fixes from core updates * fix: use delegated stake per operator set instead of per AVS * fix: update to 14 days * feat: configurable lookahead and stake type --- src/ServiceManagerBase.sol | 32 ++++++++-- src/ServiceManagerBaseStorage.sol | 9 ++- src/StakeRegistry.sol | 44 +++++++++++-- src/StakeRegistryStorage.sol | 5 +- src/interfaces/IServiceManager.sol | 2 + src/interfaces/IServiceManagerUI.sol | 3 + src/interfaces/ISlasher.sol | 45 +++++++++++++ src/interfaces/IStakeRegistry.sol | 13 ++++ src/slashers/InstantSlasher.sol | 22 +++++++ src/slashers/SimpleSlasher.sol | 33 ---------- src/slashers/SlasherStorage.sol | 8 --- src/slashers/VetoableSlasher.sol | 77 +++++++++++++++++++++++ src/slashers/base/SlasherBase.sol | 37 +++++++++++ src/slashers/base/SlasherStorage.sol | 11 ++++ src/unaudited/ECDSAServiceManagerBase.sol | 7 ++- src/unaudited/ECDSAStakeRegistry.sol | 10 ++- test/mocks/AVSDirectoryMock.sol | 2 +- test/mocks/ECDSAServiceManagerMock.sol | 6 +- test/mocks/RewardsCoordinatorMock.sol | 2 +- test/unit/ECDSAServiceManager.t.sol | 7 ++- test/unit/RegistryCoordinatorUnit.t.sol | 10 +-- test/unit/ServiceManagerBase.t.sol | 2 +- test/unit/ServiceManagerMigration.t.sol | 2 +- 23 files changed, 319 insertions(+), 70 deletions(-) create mode 100644 src/interfaces/ISlasher.sol create mode 100644 src/slashers/InstantSlasher.sol delete mode 100644 src/slashers/SimpleSlasher.sol delete mode 100644 src/slashers/SlasherStorage.sol create mode 100644 src/slashers/VetoableSlasher.sol create mode 100644 src/slashers/base/SlasherBase.sol create mode 100644 src/slashers/base/SlasherStorage.sol diff --git a/src/ServiceManagerBase.sol b/src/ServiceManagerBase.sol index 47ac07f1..74d47510 100644 --- a/src/ServiceManagerBase.sol +++ b/src/ServiceManagerBase.sol @@ -14,7 +14,6 @@ import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol"; import {IStakeRegistry} from "./interfaces/IStakeRegistry.sol"; import {BitmapUtils} from "./libraries/BitmapUtils.sol"; import {LibMergeSort} from "./libraries/LibMergeSort.sol"; -import {console} from "forge-std/Test.sol"; /** * @title Minimal implementation of a ServiceManager-type contract. @@ -24,6 +23,8 @@ import {console} from "forge-std/Test.sol"; abstract contract ServiceManagerBase is ServiceManagerBaseStorage { using BitmapUtils for *; + uint256 public constant SLASHER_PROPOSAL_DELAY = 7 days; + /// @notice when applied to a function, only allows the RegistryCoordinator to call it modifier onlyRegistryCoordinator() { require( @@ -177,12 +178,25 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage { } /** - * @notice Sets the slasher address + * @notice Proposes a new slasher address * @param newSlasher The new slasher address * @dev only callable by the owner */ - function setSlasher(address newSlasher) external onlyOwner { - _setSlasher(newSlasher); + function proposeNewSlasher(address newSlasher) external onlyOwner { + _proposeNewSlasher(newSlasher); + } + + /** + * @notice Accepts the proposed slasher address after the delay period + * @dev only callable by the owner + */ + function acceptProposedSlasher() external onlyOwner { + require( + block.timestamp >= slasherProposalTimestamp + SLASHER_PROPOSAL_DELAY, + "ServiceManager: Slasher proposal delay not met" + ); + _setSlasher(proposedSlasher); + delete proposedSlasher; } /** @@ -342,6 +356,12 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage { rewardsInitiator = newRewardsInitiator; } + function _proposeNewSlasher(address newSlasher) internal { + proposedSlasher = newSlasher; + slasherProposalTimestamp = block.timestamp; + emit SlasherProposed(newSlasher, slasherProposalTimestamp); + } + function _setSlasher(address newSlasher) internal { emit SlasherUpdated(slasher, newSlasher); slasher = newSlasher; @@ -425,6 +445,10 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage { return address(_avsDirectory); } + function allocationManager() external view override returns (address) { + return address(_allocationManager); + } + function _checkRewardsInitiator() internal view { require( msg.sender == rewardsInitiator, diff --git a/src/ServiceManagerBaseStorage.sol b/src/ServiceManagerBaseStorage.sol index 71d54d98..0bc52090 100644 --- a/src/ServiceManagerBaseStorage.sol +++ b/src/ServiceManagerBaseStorage.sol @@ -40,6 +40,13 @@ abstract contract ServiceManagerBaseStorage is IServiceManager, OwnableUpgradeab /// @notice The address of the slasher account address public slasher; + /// @notice The address of the proposed slasher account + address public proposedSlasher; + + /// @notice The timestamp when the slasher was proposed + uint256 public slasherProposalTimestamp; + + /// @notice Boolean indicating if the migration has been finalized bool public migrationFinalized; /// @notice Sets the (immutable) `_avsDirectory`, `_rewardsCoordinator`, `_registryCoordinator`, `_stakeRegistry`, and `_allocationManager` addresses @@ -58,5 +65,5 @@ abstract contract ServiceManagerBaseStorage is IServiceManager, OwnableUpgradeab } // storage gap for upgradeability - uint256[48] private __GAP; + uint256[46] private __GAP; } diff --git a/src/StakeRegistry.sol b/src/StakeRegistry.sol index 9d4da097..e1e4e449 100644 --- a/src/StakeRegistry.sol +++ b/src/StakeRegistry.sol @@ -3,6 +3,7 @@ pragma solidity ^0.8.12; import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; import {IAVSDirectory, OperatorSet} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; +import {IAllocationManager} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; import {IServiceManager} from "./interfaces/IServiceManager.sol"; import {StakeRegistryStorage, IStrategy} from "./StakeRegistryStorage.sol"; @@ -229,6 +230,18 @@ contract StakeRegistry is StakeRegistryStorage { _setMinimumStakeForQuorum(quorumNumber, minimumStake); } + /** + * @notice Sets the stake type for the registry + * @param _stakeType The type of stake to track (TOTAL_DELEGATED, TOTAL_SLASHABLE, or BOTH) + */ + function setStakeType(StakeType _stakeType) external onlyCoordinatorOwner { + _setStakeType(_stakeType); + } + + + function setSlashableStakeLookahead(uint32 _lookAheadPeriod) external onlyCoordinatorOwner { + _setLookAheadPeriod(_lookAheadPeriod); + } /** * @notice Adds strategies and weights to the quorum * @dev Checks to make sure that the *same* strategy cannot be added multiple times (checks against both against existing and new strategies). @@ -491,15 +504,17 @@ contract StakeRegistry is StakeRegistryStorage { uint256 stratsLength = strategyParamsLength(quorumNumber); StrategyParams memory strategyAndMultiplier; - uint256[] memory strategyShares; - // = delegation.getDelegatableShares(operator, strategiesPerQuorum[quorumNumber]); + address[] memory operators = new address[](1); + operators[0] = operator; + uint32 beforeTimestamp = uint32(block.timestamp + slashableStakeLookAhead); + (uint256[][] memory strategyShares, ) = IAllocationManager(serviceManager.allocationManager()).getMinDelegatedAndSlashableOperatorShares(OperatorSet(address(serviceManager), quorumNumber), operators ,strategiesPerQuorum[quorumNumber], beforeTimestamp); for (uint256 i = 0; i < stratsLength; i++) { // accessing i^th StrategyParams struct for the quorumNumber strategyAndMultiplier = strategyParams[quorumNumber][i]; // add the weight from the shares for this strategy to the total weight - if (strategyShares[i] > 0) { - weight += uint96(strategyShares[i] * strategyAndMultiplier.multiplier / WEIGHTING_DIVISOR); + if (strategyShares[i][0] > 0) { + weight += uint96(strategyShares[i][0] * strategyAndMultiplier.multiplier / WEIGHTING_DIVISOR); } } @@ -731,6 +746,27 @@ contract StakeRegistry is StakeRegistryStorage { return indices; } + /** + * @notice Sets the stake type for the registry + * @param _stakeType The type of stake to track (TOTAL_DELEGATED, TOTAL_SLASHABLE, or BOTH) + */ + function _setStakeType(StakeType _stakeType) internal { + StakeType oldStakeType = stakeType; + stakeType = _stakeType; + emit StakeTypeSet(oldStakeType, _stakeType); + } + + /** + * @notice Sets the look ahead time for checking operator shares + * @param _lookAheadDays The number of days to look ahead when checking shares + */ + function _setLookAheadPeriod(uint32 _lookAheadDays) internal { + uint32 oldLookAheadDays = slashableStakeLookAhead; + slashableStakeLookAhead = _lookAheadDays; + emit LookAheadPeriodChanged(oldLookAheadDays, _lookAheadDays); + } + + function _checkRegistryCoordinator() internal view { require( msg.sender == address(registryCoordinator), diff --git a/src/StakeRegistryStorage.sol b/src/StakeRegistryStorage.sol index f76608d2..7fea4fa2 100644 --- a/src/StakeRegistryStorage.sol +++ b/src/StakeRegistryStorage.sol @@ -52,6 +52,9 @@ abstract contract StakeRegistryStorage is IStakeRegistry { mapping(uint8 => StrategyParams[]) public strategyParams; mapping(uint8 => IStrategy[]) public strategiesPerQuorum; + StakeType public stakeType; + + uint32 public slashableStakeLookAhead; constructor( IRegistryCoordinator _registryCoordinator, @@ -67,5 +70,5 @@ abstract contract StakeRegistryStorage is IStakeRegistry { // storage gap for upgradeability // slither-disable-next-line shadowing-state - uint256[45] private __GAP; + uint256[44] private __GAP; } diff --git a/src/interfaces/IServiceManager.sol b/src/interfaces/IServiceManager.sol index 5057525e..6c2bcf94 100644 --- a/src/interfaces/IServiceManager.sol +++ b/src/interfaces/IServiceManager.sol @@ -5,6 +5,7 @@ import {IRewardsCoordinator} from "eigenlayer-contracts/src/contracts/interfaces import {IServiceManagerUI} from "./IServiceManagerUI.sol"; import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; import {IAllocationManagerTypes} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; +import {IAllocationManager} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; /** * @title Minimal interface for a ServiceManager-type contract that forms the single point for an AVS to push updates to EigenLayer @@ -50,4 +51,5 @@ interface IServiceManager is IServiceManagerUI { // EVENTS event RewardsInitiatorUpdated(address prevRewardsInitiator, address newRewardsInitiator); event SlasherUpdated(address prevSlasher, address newSlasher); + event SlasherProposed(address newSlasher, uint256 slasherProposalTimestamp); } diff --git a/src/interfaces/IServiceManagerUI.sol b/src/interfaces/IServiceManagerUI.sol index 92cdce9c..7be5a3f0 100644 --- a/src/interfaces/IServiceManagerUI.sol +++ b/src/interfaces/IServiceManagerUI.sol @@ -58,4 +58,7 @@ interface IServiceManagerUI { /// @notice Returns the EigenLayer AVSDirectory contract. function avsDirectory() external view returns (address); + + /// @notice Returns the EigenLayer AllocationManager contract. + function allocationManager() external view returns (address); } diff --git a/src/interfaces/ISlasher.sol b/src/interfaces/ISlasher.sol new file mode 100644 index 00000000..e2d128f1 --- /dev/null +++ b/src/interfaces/ISlasher.sol @@ -0,0 +1,45 @@ + +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.12; + +import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; +import {IAllocationManager} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; + +interface ISlasherEvents { + event SlashingRequested( + uint256 indexed requestId, + address indexed operator, + uint32 indexed operatorSetId, + uint256 wadToSlash, + string description + ); + + event SlashingRequestCancelled(uint256 indexed requestId); + + event OperatorSlashed( + uint256 indexed slashingRequestId, + address indexed operator, + uint32 indexed operatorSetId, + IStrategy[] strategies, + uint256 wadToSlash, + string description + ); +} + +interface ISlasherTypes { + enum SlashingStatus { + Null, + Requested, + Completed, + Cancelled + } + + struct SlashingRequest { + IAllocationManager.SlashingParams params; + uint256 requestTimestamp; + SlashingStatus status; + } + +} + +interface ISlasher is ISlasherEvents, ISlasherTypes{} diff --git a/src/interfaces/IStakeRegistry.sol b/src/interfaces/IStakeRegistry.sol index 044ccd58..c4ebd195 100644 --- a/src/interfaces/IStakeRegistry.sol +++ b/src/interfaces/IStakeRegistry.sol @@ -11,6 +11,12 @@ import {IRegistry} from "./IRegistry.sol"; * @author Layr Labs, Inc. */ interface IStakeRegistry is IRegistry { + + enum StakeType { + TOTAL_DELEGATED, + TOTAL_SLASHABLE, + BOTH + } // DATA STRUCTURES @@ -42,6 +48,13 @@ interface IStakeRegistry is IRegistry { uint8 quorumNumber, uint96 stake ); + + + /// @notice emitted when the look ahead time for checking operator shares is updated + event LookAheadPeriodChanged(uint32 oldLookAheadDays, uint32 newLookAheadDays); + + /// @notice emitted when the stake type is updated + event StakeTypeSet(StakeType previousStakeType, StakeType newStakeType); /// @notice emitted when the minimum stake for a quorum is updated event MinimumStakeForQuorumUpdated(uint8 indexed quorumNumber, uint96 minimumStake); /// @notice emitted when a new quorum is created diff --git a/src/slashers/InstantSlasher.sol b/src/slashers/InstantSlasher.sol new file mode 100644 index 00000000..b30d5881 --- /dev/null +++ b/src/slashers/InstantSlasher.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.12; + +import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; +import {IAllocationManager} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; +import {SlasherBase} from "./base/SlasherBase.sol"; + +contract InstantSlasher is SlasherBase { + + function initialize(address _serviceManager, address _slasher) external initializer { + __SlasherBase_init(_serviceManager, _slasher); + } + + function fulfillSlashingRequest( + IAllocationManager.SlashingParams memory _slashingParams + ) external virtual onlySlasher { + uint256 requestId = nextRequestId++; + _fulfillSlashingRequest(requestId, _slashingParams); + } + + +} \ No newline at end of file diff --git a/src/slashers/SimpleSlasher.sol b/src/slashers/SimpleSlasher.sol deleted file mode 100644 index faa4f49b..00000000 --- a/src/slashers/SimpleSlasher.sol +++ /dev/null @@ -1,33 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; - -import {Initializable} from "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol"; -import {IServiceManager} from "../interfaces/IServiceManager.sol"; -import {SlasherStorage} from "./SlasherStorage.sol"; -import {IAllocationManagerTypes} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; -import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; - -contract SimpleSlasher is Initializable, SlasherStorage { - function initialize(address _serviceManager) public initializer { - serviceManager = _serviceManager; - } - - function slashOperator( - address operator, - uint32 operatorSetId, - IStrategy[] memory strategies, - uint256 wadToSlash, - string memory description - ) external { - - IAllocationManagerTypes.SlashingParams memory params = IAllocationManagerTypes.SlashingParams({ - operator: operator, - operatorSetId: operatorSetId, - strategies: strategies, - wadToSlash: wadToSlash, - description: description - }); - - IServiceManager(serviceManager).slashOperator(params); - } -} diff --git a/src/slashers/SlasherStorage.sol b/src/slashers/SlasherStorage.sol deleted file mode 100644 index 1b3d61de..00000000 --- a/src/slashers/SlasherStorage.sol +++ /dev/null @@ -1,8 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; - -contract SlasherStorage { - address public serviceManager; - - uint256[49] private __gap; -} \ No newline at end of file diff --git a/src/slashers/VetoableSlasher.sol b/src/slashers/VetoableSlasher.sol new file mode 100644 index 00000000..b65442b6 --- /dev/null +++ b/src/slashers/VetoableSlasher.sol @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.12; + +import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; +import {SlasherBase} from "./base/SlasherBase.sol"; +import {IAllocationManager} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; + +contract VetoableSlashing is SlasherBase { + uint256 public constant VETO_PERIOD = 3 days; + address public vetoCommittee; + + mapping(uint256 => SlashingRequest) public slashingRequests; + + modifier onlyVetoCommittee() { + _checkVetoCommittee(msg.sender); + _; + } + + function initialize( + address _serviceManager, + address _vetoCommittee, + address _slasher + ) external virtual initializer { + __SlasherBase_init(_serviceManager, _slasher); + vetoCommittee = _vetoCommittee; + } + + function queueSlashingRequest(IAllocationManager.SlashingParams memory params) external virtual onlySlasher { + _queueSlashingRequest(params); + } + + function cancelSlashingRequest(uint256 requestId) external virtual onlyVetoCommittee { + require( + block.timestamp < slashingRequests[requestId].requestTimestamp + VETO_PERIOD, + "VetoableSlashing: veto period has passed" + ); + require(slashingRequests[requestId].status == SlashingStatus.Requested, "VetoableSlashing: request is not in Requested status"); + + _cancelSlashingRequest(requestId); + } + + function fulfillSlashingRequest(uint256 requestId) external virtual onlySlasher { + SlashingRequest storage request = slashingRequests[requestId]; + require( + block.timestamp >= request.requestTimestamp + VETO_PERIOD, + "VetoableSlashing: veto period has not passed" + ); + require(request.status == SlashingStatus.Requested, "VetoableSlashing: request has been cancelled"); + + request.status = SlashingStatus.Completed; + + _fulfillSlashingRequest( + requestId, + request.params + ); + } + + function _queueSlashingRequest(IAllocationManager.SlashingParams memory params) internal virtual { + uint256 requestId = nextRequestId++; + slashingRequests[requestId] = SlashingRequest({ + params: params, + requestTimestamp: block.timestamp, + status: SlashingStatus.Requested + }); + + emit SlashingRequested(requestId, params.operator, params.operatorSetId, params.wadToSlash, params.description); + } + + function _cancelSlashingRequest(uint256 requestId) internal virtual { + slashingRequests[requestId].status = SlashingStatus.Cancelled; + emit SlashingRequestCancelled(requestId); + } + + function _checkVetoCommittee(address account) internal view virtual { + require(account == vetoCommittee, "VetoableSlashing: caller is not the veto committee"); + } +} \ No newline at end of file diff --git a/src/slashers/base/SlasherBase.sol b/src/slashers/base/SlasherBase.sol new file mode 100644 index 00000000..c62513d3 --- /dev/null +++ b/src/slashers/base/SlasherBase.sol @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.12; + +import {Initializable} from "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol"; +import {IServiceManager} from "../../interfaces/IServiceManager.sol"; +import {SlasherStorage} from "./SlasherStorage.sol"; +import {IAllocationManagerTypes, IAllocationManager} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; +import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; + +abstract contract SlasherBase is Initializable, SlasherStorage { + + modifier onlySlasher() { + _checkSlasher(msg.sender); + _; + } + + function __SlasherBase_init(address _serviceManager, address _slasher) internal onlyInitializing { + serviceManager = _serviceManager; + slasher = _slasher; + } + + function _fulfillSlashingRequest( + uint256 _requestId, + IAllocationManager.SlashingParams memory _params + ) internal virtual { + IServiceManager(serviceManager).slashOperator(_params); + emit OperatorSlashed(_requestId, _params.operator, _params.operatorSetId, _params.strategies, _params.wadToSlash, _params.description); + } + + function _checkSlasher(address account) internal view virtual { + require(account == slasher, "InstantSlasher: caller is not the slasher"); + } +} + + + + diff --git a/src/slashers/base/SlasherStorage.sol b/src/slashers/base/SlasherStorage.sol new file mode 100644 index 00000000..1024811a --- /dev/null +++ b/src/slashers/base/SlasherStorage.sol @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.12; + +import {ISlasher} from "../../interfaces/ISlasher.sol"; +contract SlasherStorage is ISlasher { + address public serviceManager; + address public slasher; + uint256 public nextRequestId; + + uint256[47] private __gap; +} \ No newline at end of file diff --git a/src/unaudited/ECDSAServiceManagerBase.sol b/src/unaudited/ECDSAServiceManagerBase.sol index 3d30f451..facb6813 100644 --- a/src/unaudited/ECDSAServiceManagerBase.sol +++ b/src/unaudited/ECDSAServiceManagerBase.sol @@ -23,6 +23,9 @@ abstract contract ECDSAServiceManagerBase is /// @notice Address of the AVS directory contract, which manages AVS-related data for registered operators. address public immutable avsDirectory; + /// @notice Address of the AllocationManager contract + address public immutable allocationManager; + /// @notice Address of the rewards coordinator contract, which handles rewards distributions. address internal immutable rewardsCoordinator; @@ -70,12 +73,14 @@ abstract contract ECDSAServiceManagerBase is address _avsDirectory, address _stakeRegistry, address _rewardsCoordinator, - address _delegationManager + address _delegationManager, + address _allocationManager ) { avsDirectory = _avsDirectory; stakeRegistry = _stakeRegistry; rewardsCoordinator = _rewardsCoordinator; delegationManager = _delegationManager; + allocationManager = _allocationManager; _disableInitializers(); } diff --git a/src/unaudited/ECDSAStakeRegistry.sol b/src/unaudited/ECDSAStakeRegistry.sol index a8dff79a..b333d504 100644 --- a/src/unaudited/ECDSAStakeRegistry.sol +++ b/src/unaudited/ECDSAStakeRegistry.sol @@ -248,12 +248,10 @@ contract ECDSAStakeRegistry is for (uint256 i; i < strategyParams.length; i++) { strategies[i] = strategyParams[i].strategy; } - uint256[] memory shares; - /// TODO: FIX - // = DELEGATION_MANAGER.getOperatorShares( - // _operator, - // strategies - // ); + uint256[] memory shares = DELEGATION_MANAGER.getOperatorShares( + _operator, + strategies + ); for (uint256 i; i < strategyParams.length; i++) { weight += shares[i] * strategyParams[i].multiplier; } diff --git a/test/mocks/AVSDirectoryMock.sol b/test/mocks/AVSDirectoryMock.sol index a6bf2a3c..597a09a1 100644 --- a/test/mocks/AVSDirectoryMock.sol +++ b/test/mocks/AVSDirectoryMock.sol @@ -170,4 +170,4 @@ contract AVSDirectoryMock is IAVSDirectory { ) external {} function addStrategiesToOperatorSet(uint32 operatorSetId, IStrategy[] calldata strategies) external {} -} \ No newline at end of file +} diff --git a/test/mocks/ECDSAServiceManagerMock.sol b/test/mocks/ECDSAServiceManagerMock.sol index 5be1d82d..2c3c872b 100644 --- a/test/mocks/ECDSAServiceManagerMock.sol +++ b/test/mocks/ECDSAServiceManagerMock.sol @@ -9,13 +9,15 @@ contract ECDSAServiceManagerMock is ECDSAServiceManagerBase { address _avsDirectory, address _stakeRegistry, address _rewardsCoordinator, - address _delegationManager + address _delegationManager, + address _allocationManager ) ECDSAServiceManagerBase( _avsDirectory, _stakeRegistry, _rewardsCoordinator, - _delegationManager + _delegationManager, + _allocationManager ) {} diff --git a/test/mocks/RewardsCoordinatorMock.sol b/test/mocks/RewardsCoordinatorMock.sol index cc8404a0..db403ac6 100644 --- a/test/mocks/RewardsCoordinatorMock.sol +++ b/test/mocks/RewardsCoordinatorMock.sol @@ -129,5 +129,5 @@ contract RewardsCoordinatorMock is IRewardsCoordinator { address _rewardsUpdater, uint32 _activationDelay, uint16 _globalCommissionBips - ) external override {} + ) external {} } \ No newline at end of file diff --git a/test/unit/ECDSAServiceManager.t.sol b/test/unit/ECDSAServiceManager.t.sol index 1093b77a..a20c6d00 100644 --- a/test/unit/ECDSAServiceManager.t.sol +++ b/test/unit/ECDSAServiceManager.t.sol @@ -40,6 +40,8 @@ contract MockAVSDirectory { function updateAVSMetadataURI(string memory) external pure {} } +contract MockAllocationManager {} + contract MockRewardsCoordinator { function createAVSRewardsSubmission( IRewardsCoordinator.RewardsSubmission[] calldata @@ -49,6 +51,7 @@ contract MockRewardsCoordinator { contract ECDSAServiceManagerSetup is Test { MockDelegationManager public mockDelegationManager; MockAVSDirectory public mockAVSDirectory; + MockAllocationManager public mockAllocationManager; ECDSAStakeRegistryMock public mockStakeRegistry; MockRewardsCoordinator public mockRewardsCoordinator; ECDSAServiceManagerMock public serviceManager; @@ -60,6 +63,7 @@ contract ECDSAServiceManagerSetup is Test { function setUp() public { mockDelegationManager = new MockDelegationManager(); mockAVSDirectory = new MockAVSDirectory(); + mockAllocationManager = new MockAllocationManager(); mockStakeRegistry = new ECDSAStakeRegistryMock( IDelegationManager(address(mockDelegationManager)) ); @@ -69,7 +73,8 @@ contract ECDSAServiceManagerSetup is Test { address(mockAVSDirectory), address(mockStakeRegistry), address(mockRewardsCoordinator), - address(mockDelegationManager) + address(mockDelegationManager), + address(mockAllocationManager) ); operator1Pk = 1; diff --git a/test/unit/RegistryCoordinatorUnit.t.sol b/test/unit/RegistryCoordinatorUnit.t.sol index 6ae55d59..4129b8cb 100644 --- a/test/unit/RegistryCoordinatorUnit.t.sol +++ b/test/unit/RegistryCoordinatorUnit.t.sol @@ -260,7 +260,7 @@ contract RegistryCoordinatorUnitTests_RegisterOperator is RegistryCoordinatorUni registryCoordinator.pause(2 ** PAUSED_REGISTER_OPERATOR); cheats.startPrank(defaultOperator); - cheats.expectRevert(bytes("Pausable: index is paused")); + cheats.expectRevert(bytes4(keccak256("CurrentlyPaused()"))); registryCoordinator.registerOperator(emptyQuorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); } @@ -591,7 +591,7 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist cheats.prank(pauser); registryCoordinator.pause(2 ** PAUSED_DEREGISTER_OPERATOR); - cheats.expectRevert(bytes("Pausable: index is paused")); + cheats.expectRevert(bytes4(keccak256("CurrentlyPaused()"))); cheats.prank(defaultOperator); registryCoordinator.deregisterOperator(quorumNumbers); } @@ -1526,7 +1526,7 @@ contract RegistryCoordinatorUnitTests_RegisterOperatorWithChurn is RegistryCoord hex"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001B"; signatureWithSaltAndExpiry.salt = defaultSalt; cheats.prank(operatorToRegister); - cheats.expectRevert("ECDSA: invalid signature"); + cheats.expectRevert(bytes4(keccak256("InvalidSignature()"))); registryCoordinator.registerOperatorWithChurn( quorumNumbers, defaultSocket, @@ -1576,7 +1576,7 @@ contract RegistryCoordinatorUnitTests_UpdateOperators is RegistryCoordinatorUnit address[] memory operatorsToUpdate = new address[](1); operatorsToUpdate[0] = defaultOperator; - cheats.expectRevert(bytes("Pausable: index is paused")); + cheats.expectRevert(bytes4(keccak256("CurrentlyPaused()"))); registryCoordinator.updateOperators(operatorsToUpdate); } @@ -1657,7 +1657,7 @@ contract RegistryCoordinatorUnitTests_UpdateOperators is RegistryCoordinatorUnit bytes memory quorumNumbers = new bytes(1); quorumNumbers[0] = bytes1(defaultQuorumNumber); - cheats.expectRevert(bytes("Pausable: index is paused")); + cheats.expectRevert(bytes4(keccak256("CurrentlyPaused()"))); registryCoordinator.updateOperatorsForQuorum(operatorsToUpdate, quorumNumbers); } diff --git a/test/unit/ServiceManagerBase.t.sol b/test/unit/ServiceManagerBase.t.sol index 1d333814..609e8d2b 100644 --- a/test/unit/ServiceManagerBase.t.sol +++ b/test/unit/ServiceManagerBase.t.sol @@ -95,7 +95,7 @@ contract ServiceManagerBase_UnitTests is MockAVSDeployer, IServiceManagerBaseEve address(serviceManagerImplementation), address(proxyAdmin), abi.encodeWithSelector( - ServiceManagerMock.initialize.selector, msg.sender, msg.sender + ServiceManagerMock.initialize.selector, serviceManager.owner(), msg.sender, msg.sender ) ) ) diff --git a/test/unit/ServiceManagerMigration.t.sol b/test/unit/ServiceManagerMigration.t.sol index 67350529..5fd86846 100644 --- a/test/unit/ServiceManagerMigration.t.sol +++ b/test/unit/ServiceManagerMigration.t.sol @@ -99,7 +99,7 @@ contract ServiceManagerMigration_UnitTests is MockAVSDeployer, IServiceManagerBa address(serviceManagerImplementation), address(proxyAdmin), abi.encodeWithSelector( - ServiceManagerMock.initialize.selector, msg.sender, msg.sender + ServiceManagerMock.initialize.selector, serviceManager.owner(), msg.sender, msg.sender ) ) ) From 145bdaf311b6774c060a7fc9de87004171b28bac Mon Sep 17 00:00:00 2001 From: steven <12021290+stevennevins@users.noreply.github.com> Date: Mon, 18 Nov 2024 13:52:12 -0500 Subject: [PATCH 20/27] chore: remove unused test util contracts (#319) * feat: remove both option * chore: remove unused test util contracts * chore: remove diff --- src/interfaces/IStakeRegistry.sol | 26 +-- test/utils/Greeter.sol | 11 -- test/utils/GreeterProxiable.sol | 65 ------- test/utils/GreeterV2.sol | 14 -- test/utils/GreeterV2Proxiable.sol | 17 -- test/utils/NoInitializer.sol | 13 -- test/utils/ProxyTestContracts.sol | 9 - test/utils/UpgradeableProxyUtils.sol | 241 ------------------------- test/utils/UpgradeableProxyUtils.t.sol | 87 --------- test/utils/WithConstructor.sol | 19 -- 10 files changed, 13 insertions(+), 489 deletions(-) delete mode 100644 test/utils/Greeter.sol delete mode 100644 test/utils/GreeterProxiable.sol delete mode 100644 test/utils/GreeterV2.sol delete mode 100644 test/utils/GreeterV2Proxiable.sol delete mode 100644 test/utils/NoInitializer.sol delete mode 100644 test/utils/ProxyTestContracts.sol delete mode 100644 test/utils/UpgradeableProxyUtils.sol delete mode 100644 test/utils/UpgradeableProxyUtils.t.sol delete mode 100644 test/utils/WithConstructor.sol diff --git a/src/interfaces/IStakeRegistry.sol b/src/interfaces/IStakeRegistry.sol index c4ebd195..fde01379 100644 --- a/src/interfaces/IStakeRegistry.sol +++ b/src/interfaces/IStakeRegistry.sol @@ -12,12 +12,12 @@ import {IRegistry} from "./IRegistry.sol"; */ interface IStakeRegistry is IRegistry { - enum StakeType { - TOTAL_DELEGATED, - TOTAL_SLASHABLE, - BOTH - } - + enum StakeType { + TOTAL_DELEGATED, + TOTAL_SLASHABLE, + BOTH + } + // DATA STRUCTURES /// @notice struct used to store the stakes of an individual operator or the sum of all operators' stakes, for storage @@ -80,8 +80,8 @@ interface IStakeRegistry is IRegistry { * 4) the operator is not already registered */ function registerOperator( - address operator, - bytes32 operatorId, + address operator, + bytes32 operatorId, bytes memory quorumNumbers ) external returns (uint96[] memory, uint96[] memory); @@ -200,7 +200,7 @@ interface IStakeRegistry is IRegistry { /** * @notice Returns the stake weight corresponding to `operatorId` for quorum `quorumNumber`, at the - * `index`-th entry in the `operatorIdToStakeHistory[operatorId][quorumNumber]` array if the entry + * `index`-th entry in the `operatorIdToStakeHistory[operatorId][quorumNumber]` array if the entry * corresponds to the operator's stake at `blockNumber`. Reverts otherwise. * @param quorumNumber The quorum number to get the stake for. * @param operatorId The id of the operator of interest. @@ -215,8 +215,8 @@ interface IStakeRegistry is IRegistry { returns (uint96); /** - * @notice Returns the total stake weight for quorum `quorumNumber`, at the `index`-th entry in the - * `totalStakeHistory[quorumNumber]` array if the entry corresponds to the total stake at `blockNumber`. + * @notice Returns the total stake weight for quorum `quorumNumber`, at the `index`-th entry in the + * `totalStakeHistory[quorumNumber]` array if the entry corresponds to the total stake at `blockNumber`. * Reverts otherwise. * @param quorumNumber The quorum number to get the stake for. * @param index Array index for lookup, within the dynamic array `totalStakeHistory[quorumNumber]`. @@ -254,8 +254,8 @@ interface IStakeRegistry is IRegistry { * and should be deregistered. */ function updateOperatorStake( - address operator, - bytes32 operatorId, + address operator, + bytes32 operatorId, bytes calldata quorumNumbers ) external returns (uint192); } diff --git a/test/utils/Greeter.sol b/test/utils/Greeter.sol deleted file mode 100644 index 35948a85..00000000 --- a/test/utils/Greeter.sol +++ /dev/null @@ -1,11 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.12; - -contract Greeter { - string public greeting; - - function initialize(string memory _greeting) public { - greeting = _greeting; - } -} - diff --git a/test/utils/GreeterProxiable.sol b/test/utils/GreeterProxiable.sol deleted file mode 100644 index e6a6e17c..00000000 --- a/test/utils/GreeterProxiable.sol +++ /dev/null @@ -1,65 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.12; - -interface IERC1822Proxiable { - function proxiableUUID() external view returns (bytes32); -} - -contract Proxiable { - bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; - - string public constant UPGRADE_INTERFACE_VERSION = "5.0.0"; - - function upgradeToAndCall(address newImplementation, bytes calldata data) external { - try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) { - if (slot != _IMPLEMENTATION_SLOT) { - revert("slot is unsupported as a uuid"); - } - _setImplementation(newImplementation); - if (data.length > 0) { - /** - * Note that using delegate call can make your implementation contract vulnerable if this function - * is not protected with the `onlyProxy` modifier. Again, this contract is for testing only, it is - * not safe for use in production. Instead, use the `UUPSUpgradeable` contract available in - * @openzeppelin/contracts-upgradeable - */ - /// @custom:oz-upgrades-unsafe-allow delegatecall - (bool success, ) = newImplementation.delegatecall(data); - require(success, "upgrade call reverted"); - } else { - _checkNonPayable(); - } - } catch { - revert("the implementation is not UUPS"); - } - } - - function proxiableUUID() external view virtual returns (bytes32) { - return _IMPLEMENTATION_SLOT; - } - - function _checkNonPayable() private { - if (msg.value > 0) { - revert("non-payable upgrade call"); - } - } - - function _setImplementation(address newImplementation) private { - bytes32 slot = _IMPLEMENTATION_SLOT; - // solhint-disable-next-line no-inline-assembly - assembly { - sstore(slot, newImplementation) - } - } -} - - -contract GreeterProxiable is Proxiable { - string public greeting; - - function initialize(string memory _greeting) public { - greeting = _greeting; - } -} - - diff --git a/test/utils/GreeterV2.sol b/test/utils/GreeterV2.sol deleted file mode 100644 index 15a2b9b8..00000000 --- a/test/utils/GreeterV2.sol +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.12; - -contract GreeterV2 { - string public greeting; - - function initialize(string memory _greeting) public { - greeting = _greeting; - } - - function resetGreeting() public { - greeting = "resetted"; - } -} diff --git a/test/utils/GreeterV2Proxiable.sol b/test/utils/GreeterV2Proxiable.sol deleted file mode 100644 index ca2d82c9..00000000 --- a/test/utils/GreeterV2Proxiable.sol +++ /dev/null @@ -1,17 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.12; - -import {Proxiable} from "./GreeterProxiable.sol"; - -contract GreeterV2Proxiable is Proxiable { - string public greeting; - - function initialize(string memory _greeting) public { - greeting = _greeting; - } - - function resetGreeting() public { - greeting = "resetted"; - } -} - diff --git a/test/utils/NoInitializer.sol b/test/utils/NoInitializer.sol deleted file mode 100644 index e70616ae..00000000 --- a/test/utils/NoInitializer.sol +++ /dev/null @@ -1,13 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.12; - -contract NoInitializer { - /// @custom:oz-upgrades-unsafe-allow state-variable-immutable - uint256 public immutable a; - - /// @custom:oz-upgrades-unsafe-allow constructor - constructor(uint256 _a) { - a = _a; - } -} - diff --git a/test/utils/ProxyTestContracts.sol b/test/utils/ProxyTestContracts.sol deleted file mode 100644 index af2bc907..00000000 --- a/test/utils/ProxyTestContracts.sol +++ /dev/null @@ -1,9 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.12; - -import {Greeter} from "./Greeter.sol"; -import {GreeterProxiable} from "./GreeterProxiable.sol"; -import {GreeterV2} from "./GreeterV2.sol"; -import {GreeterV2Proxiable} from "./GreeterV2Proxiable.sol"; -import {WithConstructor} from "./WithConstructor.sol"; -import {NoInitializer} from "./NoInitializer.sol"; diff --git a/test/utils/UpgradeableProxyUtils.sol b/test/utils/UpgradeableProxyUtils.sol deleted file mode 100644 index 6ce5471a..00000000 --- a/test/utils/UpgradeableProxyUtils.sol +++ /dev/null @@ -1,241 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.12; - -import {Vm} from "forge-std/Vm.sol"; -import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; -import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; -import {UpgradeableBeacon} from "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol"; -import {BeaconProxy} from "@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol"; -import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; - -/// Modified from the Openzeppelin foundry upgrades library -/// Modifications: -/// - Made compatible with OZ ^4.x releases -/// - Removed OZ Defender functionality -library UpgradeableProxyUtils { - address private constant CHEATCODE_ADDRESS = 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D; - - // This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1 - bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143; - - /** - * @dev Storage slot with the address of the current implementation. - * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is - * validated in the constructor. - */ - bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; - - /** - * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy. - * This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor. - */ - bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50; - - /** - * @dev Storage slot with the admin of the contract. - * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is - * validated in the constructor. - */ - bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; - - Vm private constant vm = Vm(CHEATCODE_ADDRESS); - - /** - * @dev Deploys a transparent proxy using the given contract as the implementation. - * - * @param contractName Name of the contract to use as the implementation, e.g. "MyContract.sol" or "MyContract.sol:MyContract" or artifact path relative to the project root directory - * @param initialOwner Address to set as the owner of the ProxyAdmin contract which gets deployed by the proxy - * @param initializerData Encoded call data of the initializer function to call during creation of the proxy, or empty if no initialization is required - * @return Proxy address - */ - function deployTransparentProxy( - string memory contractName, - address initialOwner, - bytes memory initializerData - ) internal returns (address) { - return deployTransparentProxy(contractName, initialOwner, initializerData, ""); - } - - /** - * @dev Deploys a transparent proxy using the given contract as the implementation. - * - * @param contractName Name of the contract to use as the implementation, e.g. "MyContract.sol" or "MyContract.sol:MyContract" or artifact path relative to the project root directory - * @param initialOwner Address to set as the owner of the ProxyAdmin contract which gets deployed by the proxy - * @param initializerData Encoded call data of the initializer function to call during creation of the proxy, or empty if no initialization is required - * @return Proxy address - */ - function deployTransparentProxy( - string memory contractName, - address initialOwner, - bytes memory initializerData, - bytes memory implConstructorArgs - ) internal returns (address) { - address impl = deployImplementation(contractName, implConstructorArgs); - return - address( - _deploy( - "TransparentUpgradeableProxy.sol:TransparentUpgradeableProxy", - abi.encode(impl, initialOwner, initializerData) - ) - ); - } - - /** - * @dev Deploys an upgradeable beacon using the given contract as the implementation. - * - * @param contractName Name of the contract to use as the implementation, e.g. "MyContract.sol" or "MyContract.sol:MyContract" or artifact path relative to the project root directory - * @param initialOwner Address to set as the owner of the UpgradeableBeacon contract which gets deployed - * @return Beacon address - */ - function deployBeacon( - string memory contractName, - address initialOwner, - bytes memory implConstructorArgs - ) internal returns (address) { - address impl = deployImplementation(contractName, implConstructorArgs); - return _deploy("UpgradeableBeacon.sol:UpgradeableBeacon", abi.encode(impl, initialOwner)); - } - - /** - * @dev Deploys a beacon proxy using the given beacon and call data. - * - * @param beacon Address of the beacon to use - * @param data Encoded call data of the initializer function to call during creation of the proxy, or empty if no initialization is required - * @return Proxy address - */ - function deployBeaconProxy(address beacon, bytes memory data) internal returns (address) { - return _deploy("BeaconProxy.sol:BeaconProxy", abi.encode(beacon, data)); - } - - /** - * @dev Validates and deploys an implementation contract, and returns its address. - * - * @param contractName Name of the contract to deploy, e.g. "MyContract.sol" or "MyContract.sol:MyContract" or artifact path relative to the project root directory - * @return Address of the implementation contract - */ - function deployImplementation(string memory contractName, bytes memory implConstructorArgs) internal returns (address) { - return _deploy(contractName, implConstructorArgs); - } - /** - * @dev Gets the admin address of a transparent proxy from its ERC1967 admin storage slot. - * @param proxy Address of a transparent proxy - * @return Admin address - */ - function getAdminAddress(address proxy) internal view returns (address) { - bytes32 adminSlot = vm.load(proxy, _ADMIN_SLOT); - return address(uint160(uint256(adminSlot))); - } - - /** - * @dev Gets the implementation address of a transparent or UUPS proxy from its ERC1967 implementation storage slot. - * @param proxy Address of a transparent or UUPS proxy - * @return Implementation address - */ - function getImplementationAddress(address proxy) internal view returns (address) { - bytes32 implSlot = vm.load(proxy, _IMPLEMENTATION_SLOT); - return address(uint160(uint256(implSlot))); - } - - /** - * @dev Gets the beacon address of a beacon proxy from its ERC1967 beacon storage slot. - * @param proxy Address of a beacon proxy - * @return Beacon address - */ - function getBeaconAddress(address proxy) internal view returns (address) { - bytes32 beaconSlot = vm.load(proxy, _BEACON_SLOT); - return address(uint160(uint256(beaconSlot))); - } - - /** - * @dev Upgrades a proxy to a new implementation contract. - * @param proxy Address of the proxy to upgrade - * @param contractName Name of the new implementation contract to upgrade to, e.g. "MyContract.sol" or "MyContract.sol:MyContract" or artifact path relative to the project root directory - * @param data Encoded call data of an arbitrary function to call during the upgrade process, or empty if no function needs to be called during the upgrade - * @param implConstructorArgs abi encoded constructor arguments for deploying the implementation contract - */ - function upgradeProxy( - address proxy, - string memory contractName, - bytes memory data, - bytes memory implConstructorArgs - ) internal { - address newImpl = _deploy(contractName, implConstructorArgs); - - bytes32 adminSlot = vm.load(proxy, _ADMIN_SLOT); - if (adminSlot == bytes32(0)) { - // No admin contract: upgrade directly using interface - TransparentUpgradeableProxy(payable(proxy)).upgradeToAndCall(newImpl, data); - } else { - ProxyAdmin admin = ProxyAdmin(address(uint160(uint256(adminSlot)))); - admin.upgradeAndCall(TransparentUpgradeableProxy(payable(proxy)), newImpl, data); - } - } - - /** - * @dev Upgrades a proxy to a new implementation contract. - * @param proxy Address of the proxy to upgrade - * @param contractName Name of the new implementation contract to upgrade to, e.g. "MyContract.sol" or "MyContract.sol:MyContract" or artifact path relative to the project root directory - * @param data Encoded call data of an arbitrary function to call during the upgrade process, or empty if no function needs to be called during the upgrade - */ - function upgradeProxy(address proxy, string memory contractName, bytes memory data) internal { - upgradeProxy(proxy, contractName, data, ""); - } - - /** - * @dev Upgrades a beacon to a new implementation contract. - * @param beacon Address of the beacon to upgrade - * @param contractName Name of the new implementation contract to upgrade to, e.g. "MyContract.sol" or "MyContract.sol:MyContract" or artifact path relative to the project root directory - * @param implConstructorArgs abi encoded constructor arguments for deploying the implementation contract - */ - function upgradeBeacon(address beacon, string memory contractName, bytes memory implConstructorArgs) internal { - address newImpl = _deploy(contractName, implConstructorArgs); - UpgradeableBeacon(beacon).upgradeTo(newImpl); - } - - /* - * @param beacon Address of the beacon to upgrade - * @param contractName Name of the new implementation contract to upgrade to, e.g. "MyContract.sol" or "MyContract.sol:MyContract" or artifact path relative to the project root directory - */ - function upgradeBeacon(address beacon, string memory contractName) internal { - upgradeBeacon(beacon, contractName, ""); - } - - function _deploy(string memory contractName, bytes memory implConstructorArgs) private returns (address) { - bytes memory creationCode = Vm(CHEATCODE_ADDRESS).getCode(contractName); - address deployedAddress = _deployFromBytecode(abi.encodePacked(creationCode, implConstructorArgs)); - if (deployedAddress == address(0)) { - revert( - string.concat( - "Failed to deploy contract ", - contractName, - ' using constructor data "', - string(implConstructorArgs), - '"' - ) - ); - } - return deployedAddress; - } - - function _deployFromBytecode(bytes memory bytecode) private returns (address) { - address addr; - assembly { - addr := create(0, add(bytecode, 32), mload(bytecode)) - } - return addr; - } - - /** - * @dev Precompile proxy contracts so that they can be deployed by name via the `_deploy` function. - * - * NOTE: This function is never called and has no effect, but must be kept to ensure that the proxy contracts are included in the compilation. - */ - function _precompileProxyContracts() private pure { - bytes memory dummy; - dummy = type(ERC1967Proxy).creationCode; - dummy = type(TransparentUpgradeableProxy).creationCode; - dummy = type(ProxyAdmin).creationCode; - dummy = type(UpgradeableBeacon).creationCode; - dummy = type(BeaconProxy).creationCode; - } -} diff --git a/test/utils/UpgradeableProxyUtils.t.sol b/test/utils/UpgradeableProxyUtils.t.sol deleted file mode 100644 index f0197078..00000000 --- a/test/utils/UpgradeableProxyUtils.t.sol +++ /dev/null @@ -1,87 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.12; - -import {Test, console} from "forge-std/Test.sol"; -import {Vm} from "forge-std/Vm.sol"; - -import {UpgradeableProxyUtils} from "./UpgradeableProxyUtils.sol"; -import {Proxy} from "@openzeppelin/contracts/proxy/Proxy.sol"; -import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; -import {IBeacon} from "@openzeppelin/contracts/proxy/beacon/IBeacon.sol"; -import {Greeter, GreeterV2, NoInitializer, WithConstructor, GreeterProxiable, GreeterV2Proxiable} from "./ProxyTestContracts.sol"; - -contract UpgradeableProxyUtilsTest is Test { - ProxyAdmin internal admin; - - function setUp() public { - admin = new ProxyAdmin(); - } - - function testTransparent() public { - address proxy = UpgradeableProxyUtils.deployTransparentProxy( - "Greeter.sol", - address(admin), - abi.encodeCall(Greeter.initialize, ("hello")) - ); - Greeter instance = Greeter(proxy); - address implAddressV1 = UpgradeableProxyUtils.getImplementationAddress(proxy); - address adminAddress = UpgradeableProxyUtils.getAdminAddress(proxy); - - assertFalse(adminAddress == address(0)); - assertEq(instance.greeting(), "hello"); - - UpgradeableProxyUtils.upgradeProxy(proxy, "GreeterV2.sol", abi.encodeCall(GreeterV2.resetGreeting, ())); - - address implAddressV2 = UpgradeableProxyUtils.getImplementationAddress(proxy); - - assertEq(UpgradeableProxyUtils.getAdminAddress(proxy), adminAddress); - assertEq(instance.greeting(), "resetted"); - assertFalse(implAddressV2 == implAddressV1); - } - - function testBeacon() public { - address beacon = UpgradeableProxyUtils.deployBeacon("Greeter.sol", address(admin), abi.encode()); - address implAddressV1 = IBeacon(beacon).implementation(); - - address proxy = UpgradeableProxyUtils.deployBeaconProxy(beacon, abi.encodeCall(Greeter.initialize, ("hello"))); - Greeter instance = Greeter(proxy); - - assertEq(UpgradeableProxyUtils.getBeaconAddress(proxy), beacon); - assertEq(instance.greeting(), "hello"); - - UpgradeableProxyUtils.upgradeBeacon(beacon, "GreeterV2.sol"); - address implAddressV2 = IBeacon(beacon).implementation(); - - GreeterV2(address(instance)).resetGreeting(); - - assertEq(instance.greeting(), "resetted"); - assertFalse(implAddressV2 == implAddressV1); - } - - function testUpgradeBeaconWithoutCaller() public { - address beacon = UpgradeableProxyUtils.deployBeacon("Greeter.sol", address(admin), abi.encode()); - UpgradeableProxyUtils.upgradeBeacon(beacon, "GreeterV2.sol", abi.encode()); - } - - function testWithConstructor() public { - bytes memory constructorData = abi.encode(123); - address proxy = UpgradeableProxyUtils.deployTransparentProxy( - "WithConstructor.sol", - msg.sender, - abi.encodeCall(WithConstructor.initialize, (456)), - constructorData - ); - - assertEq(WithConstructor(proxy).a(), 123); - assertEq(WithConstructor(proxy).b(), 456); - } - - function testNoInitializer() public { - /// Can access getCode by File:Contract - bytes memory constructorData = abi.encode(123); - address proxy = UpgradeableProxyUtils.deployTransparentProxy("NoInitializer.sol", msg.sender, "", constructorData); - - assertEq(WithConstructor(proxy).a(), 123); - } - -} diff --git a/test/utils/WithConstructor.sol b/test/utils/WithConstructor.sol deleted file mode 100644 index 146b3ecd..00000000 --- a/test/utils/WithConstructor.sol +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.12; - -contract WithConstructor { - /// @custom:oz-upgrades-unsafe-allow state-variable-immutable - uint256 public immutable a; - - uint256 public b; - - /// @custom:oz-upgrades-unsafe-allow constructor - constructor(uint256 _a) { - a = _a; - } - - function initialize(uint256 _b) public { - b = _b; - } -} - From 9a975dfee5c3b0031df0794530dcf6fffecc5da7 Mon Sep 17 00:00:00 2001 From: steven Date: Fri, 8 Nov 2024 14:03:33 -0500 Subject: [PATCH 21/27] feat: remove both option --- src/interfaces/IStakeRegistry.sol | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/interfaces/IStakeRegistry.sol b/src/interfaces/IStakeRegistry.sol index fde01379..ecc5b542 100644 --- a/src/interfaces/IStakeRegistry.sol +++ b/src/interfaces/IStakeRegistry.sol @@ -12,11 +12,10 @@ import {IRegistry} from "./IRegistry.sol"; */ interface IStakeRegistry is IRegistry { - enum StakeType { - TOTAL_DELEGATED, - TOTAL_SLASHABLE, - BOTH - } + enum StakeType { + TOTAL_DELEGATED, + TOTAL_SLASHABLE + } // DATA STRUCTURES From fa04f06212978dc3c66c23976cd34498f73aad41 Mon Sep 17 00:00:00 2001 From: steven <12021290+stevennevins@users.noreply.github.com> Date: Wed, 20 Nov 2024 11:58:19 -0500 Subject: [PATCH 22/27] fix: storage gap remove one slot (#320) --- src/ServiceManagerBaseStorage.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ServiceManagerBaseStorage.sol b/src/ServiceManagerBaseStorage.sol index 0bc52090..51365228 100644 --- a/src/ServiceManagerBaseStorage.sol +++ b/src/ServiceManagerBaseStorage.sol @@ -65,5 +65,5 @@ abstract contract ServiceManagerBaseStorage is IServiceManager, OwnableUpgradeab } // storage gap for upgradeability - uint256[46] private __GAP; + uint256[45] private __GAP; } From 388e9f9a46c8249d12f9fd62d994bc81fef2bb15 Mon Sep 17 00:00:00 2001 From: steven <12021290+stevennevins@users.noreply.github.com> Date: Wed, 20 Nov 2024 12:42:15 -0500 Subject: [PATCH 23/27] feat: track total slashable stake and total delegated stake per quorum (#317) * feat: remove both option * feat: total delegated stake and total slashable stake per quorum config * test: resolve some breaking changes to tests * chore: move stake type to file level definition * chore: refactor loop * test: add unit test for slashble stake quorum init * test: assert on state and event * test: delegated stake quorum and assertions --- foundry.toml | 4 +- src/RegistryCoordinator.sol | 38 +++- src/StakeRegistry.sol | 134 ++++++++---- src/StakeRegistryStorage.sol | 10 +- src/interfaces/IStakeRegistry.sol | 27 ++- test/integration/IntegrationConfig.t.sol | 34 +-- test/integration/IntegrationDeployer.t.sol | 9 +- test/mocks/StakeRegistryMock.sol | 27 ++- test/unit/OperatorStateRetrieverUnit.t.sol | 4 +- test/unit/RegistryCoordinatorMigration.t.sol | 8 +- test/unit/RegistryCoordinatorUnit.t.sol | 216 ++++++++++--------- test/unit/StakeRegistryUnit.t.sol | 69 +++++- test/utils/MockAVSDeployer.sol | 16 +- 13 files changed, 380 insertions(+), 216 deletions(-) diff --git a/foundry.toml b/foundry.toml index a4b33112..e3775cb1 100644 --- a/foundry.toml +++ b/foundry.toml @@ -9,9 +9,7 @@ ffi = true no-match-contract = "FFI" # Enables or disables the optimizer -optimizer = true -# The number of optimizer runs -optimizer_runs = 200 +optimizer = false # Whether or not to use the Yul intermediate representation compilation pipeline via_ir = false # Override the Solidity version (this overrides `auto_detect_solc`) diff --git a/src/RegistryCoordinator.sol b/src/RegistryCoordinator.sol index c71e7278..101cd935 100644 --- a/src/RegistryCoordinator.sol +++ b/src/RegistryCoordinator.sol @@ -6,7 +6,7 @@ import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISi import {IAVSDirectory, OperatorSet} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; import {ISocketUpdater} from "./interfaces/ISocketUpdater.sol"; import {IBLSApkRegistry} from "./interfaces/IBLSApkRegistry.sol"; -import {IStakeRegistry} from "./interfaces/IStakeRegistry.sol"; +import {IStakeRegistry, StakeType} from "./interfaces/IStakeRegistry.sol"; import {IIndexRegistry} from "./interfaces/IIndexRegistry.sol"; import {IServiceManager} from "./interfaces/IServiceManager.sol"; import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol"; @@ -87,11 +87,15 @@ contract RegistryCoordinator is uint256 _initialPausedStatus, OperatorSetParam[] memory _operatorSetParams, uint96[] memory _minimumStakes, - IStakeRegistry.StrategyParams[][] memory _strategyParams + IStakeRegistry.StrategyParams[][] memory _strategyParams, + StakeType[] memory _stakeTypes, + uint32[] memory _lookAheadPeriods ) external initializer { require( _operatorSetParams.length == _minimumStakes.length - && _minimumStakes.length == _strategyParams.length, + && _minimumStakes.length == _strategyParams.length + && _strategyParams.length == _stakeTypes.length + && _stakeTypes.length == _lookAheadPeriods.length, "RegistryCoordinator.initialize: input length mismatch" ); @@ -108,7 +112,7 @@ contract RegistryCoordinator is // Create quorums for (uint256 i = 0; i < _operatorSetParams.length; i++) { - _createQuorum(_operatorSetParams[i], _minimumStakes[i], _strategyParams[i]); + _createQuorum(_operatorSetParams[i], _minimumStakes[i], _strategyParams[i], _stakeTypes[i], _lookAheadPeriods[i]); } } @@ -404,12 +408,21 @@ contract RegistryCoordinator is * @param strategyParams a list of strategies and multipliers used by the StakeRegistry to * calculate an operator's stake weight for the quorum */ - function createQuorum( + function createTotalDelegatedStakeQuorum( OperatorSetParam memory operatorSetParams, uint96 minimumStake, IStakeRegistry.StrategyParams[] memory strategyParams ) external virtual onlyOwner { - _createQuorum(operatorSetParams, minimumStake, strategyParams); + _createQuorum(operatorSetParams, minimumStake, strategyParams, StakeType.TOTAL_DELEGATED, 0); + } + + function createSlashableStakeQuorum( + OperatorSetParam memory operatorSetParams, + uint96 minimumStake, + IStakeRegistry.StrategyParams[] memory strategyParams, + uint32 lookAheadPeriod + ) external virtual onlyOwner { + _createQuorum(operatorSetParams, minimumStake, strategyParams, StakeType.TOTAL_SLASHABLE, lookAheadPeriod); } /** @@ -809,7 +822,9 @@ contract RegistryCoordinator is function _createQuorum( OperatorSetParam memory operatorSetParams, uint96 minimumStake, - IStakeRegistry.StrategyParams[] memory strategyParams + IStakeRegistry.StrategyParams[] memory strategyParams, + StakeType stakeType, + uint32 lookAheadPeriod ) internal { // Increment the total quorum count. Fails if we're already at the max uint8 prevQuorumCount = quorumCount; @@ -824,7 +839,14 @@ contract RegistryCoordinator is // Initialize the quorum here and in each registry _setOperatorSetParams(quorumNumber, operatorSetParams); - stakeRegistry.initializeQuorum(quorumNumber, minimumStake, strategyParams); + + // Initialize stake registry based on stake type + if (stakeType == StakeType.TOTAL_DELEGATED) { + stakeRegistry.initializeDelegatedStakeQuorum(quorumNumber, minimumStake, strategyParams); + } else if (stakeType == StakeType.TOTAL_SLASHABLE) { + stakeRegistry.initializeSlashableStakeQuorum(quorumNumber, minimumStake, lookAheadPeriod, strategyParams); + } + indexRegistry.initializeQuorum(quorumNumber); blsApkRegistry.initializeQuorum(quorumNumber); // Check if the AVS has migrated to operator sets diff --git a/src/StakeRegistry.sol b/src/StakeRegistry.sol index e1e4e449..4a0ceca8 100644 --- a/src/StakeRegistry.sol +++ b/src/StakeRegistry.sol @@ -9,7 +9,7 @@ import {IServiceManager} from "./interfaces/IServiceManager.sol"; import {StakeRegistryStorage, IStrategy} from "./StakeRegistryStorage.sol"; import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol"; -import {IStakeRegistry} from "./interfaces/IStakeRegistry.sol"; +import {IStakeRegistry, StakeType} from "./interfaces/IStakeRegistry.sol"; import {BitmapUtils} from "./libraries/BitmapUtils.sol"; @@ -25,7 +25,7 @@ import {BitmapUtils} from "./libraries/BitmapUtils.sol"; contract StakeRegistry is StakeRegistryStorage { using BitmapUtils for *; - + modifier onlyRegistryCoordinator() { _checkRegistryCoordinator(); _; @@ -73,8 +73,8 @@ contract StakeRegistry is StakeRegistryStorage { uint96[] memory currentStakes = new uint96[](quorumNumbers.length); uint96[] memory totalStakes = new uint96[](quorumNumbers.length); - for (uint256 i = 0; i < quorumNumbers.length; i++) { - + for (uint256 i = 0; i < quorumNumbers.length; i++) { + uint8 quorumNumber = uint8(quorumNumbers[i]); _checkQuorumExists(quorumNumber); @@ -88,7 +88,7 @@ contract StakeRegistry is StakeRegistryStorage { // Update the operator's stake int256 stakeDelta = _recordOperatorStakeUpdate({ - operatorId: operatorId, + operatorId: operatorId, quorumNumber: quorumNumber, newStake: currentStake }); @@ -127,8 +127,8 @@ contract StakeRegistry is StakeRegistryStorage { // Update the operator's stake for the quorum and retrieve the shares removed int256 stakeDelta = _recordOperatorStakeUpdate({ - operatorId: operatorId, - quorumNumber: quorumNumber, + operatorId: operatorId, + quorumNumber: quorumNumber, newStake: 0 }); @@ -147,8 +147,8 @@ contract StakeRegistry is StakeRegistryStorage { * and should be deregistered. */ function updateOperatorStake( - address operator, - bytes32 operatorId, + address operator, + bytes32 operatorId, bytes calldata quorumNumbers ) external onlyRegistryCoordinator returns (uint192) { uint192 quorumsToRemove; @@ -173,7 +173,7 @@ contract StakeRegistry is StakeRegistryStorage { // against the minimum stake requirements for the quorum. (uint96 stakeWeight, bool hasMinimumStake) = _weightOfOperatorForQuorum(quorumNumber, operator); // If the operator no longer meets the minimum stake, set their stake to zero and mark them for removal - /// also handle setting the operator's stake to 0 and remove them from the quorum + /// also handle setting the operator's stake to 0 and remove them from the quorum /// if they directly unregistered from the AVSDirectory bubbles up info via registry coordinator to deregister them bool operatorRegistered; // Convert quorumNumber to operatorSetId @@ -207,7 +207,7 @@ contract StakeRegistry is StakeRegistryStorage { } /// @notice Initialize a new quorum and push its first history update - function initializeQuorum( + function initializeDelegatedStakeQuorum( uint8 quorumNumber, uint96 minimumStake, StrategyParams[] memory _strategyParams @@ -215,6 +215,28 @@ contract StakeRegistry is StakeRegistryStorage { require(!_quorumExists(quorumNumber), "StakeRegistry.initializeQuorum: quorum already exists"); _addStrategyParams(quorumNumber, _strategyParams); _setMinimumStakeForQuorum(quorumNumber, minimumStake); + _setStakeType(quorumNumber, StakeType.TOTAL_DELEGATED); + + _totalStakeHistory[quorumNumber].push(StakeUpdate({ + updateBlockNumber: uint32(block.number), + nextUpdateBlockNumber: 0, + stake: 0 + })); + } + + + /// @notice Initialize a new quorum and push its first history update + function initializeSlashableStakeQuorum( + uint8 quorumNumber, + uint96 minimumStake, + uint32 lookAheadPeriod, + StrategyParams[] memory _strategyParams + ) public virtual onlyRegistryCoordinator { + require(!_quorumExists(quorumNumber), "StakeRegistry.initializeQuorum: quorum already exists"); + _addStrategyParams(quorumNumber, _strategyParams); + _setMinimumStakeForQuorum(quorumNumber, minimumStake); + _setStakeType(quorumNumber, StakeType.TOTAL_SLASHABLE); + _setLookAheadPeriod(quorumNumber, lookAheadPeriod); _totalStakeHistory[quorumNumber].push(StakeUpdate({ updateBlockNumber: uint32(block.number), @@ -224,32 +246,37 @@ contract StakeRegistry is StakeRegistryStorage { } function setMinimumStakeForQuorum( - uint8 quorumNumber, + uint8 quorumNumber, uint96 minimumStake ) public virtual onlyCoordinatorOwner quorumExists(quorumNumber) { _setMinimumStakeForQuorum(quorumNumber, minimumStake); } /** - * @notice Sets the stake type for the registry + * @notice Sets the stake type for the registry for a specific quorum + * @param quorumNumber The quorum number to set the stake type for * @param _stakeType The type of stake to track (TOTAL_DELEGATED, TOTAL_SLASHABLE, or BOTH) */ - function setStakeType(StakeType _stakeType) external onlyCoordinatorOwner { - _setStakeType(_stakeType); + function setStakeType(uint8 quorumNumber, StakeType _stakeType) external onlyCoordinatorOwner { + _setStakeType(quorumNumber, _stakeType); } - - function setSlashableStakeLookahead(uint32 _lookAheadPeriod) external onlyCoordinatorOwner { - _setLookAheadPeriod(_lookAheadPeriod); + /** + * @notice Sets the look ahead time for checking operator shares for a specific quorum + * @param quorumNumber The quorum number to set the look ahead period for + * @param _lookAheadPeriod The number of days to look ahead when checking shares + */ + function setSlashableStakeLookahead(uint8 quorumNumber, uint32 _lookAheadPeriod) external onlyCoordinatorOwner { + _setLookAheadPeriod(quorumNumber, _lookAheadPeriod); } - /** + /** * @notice Adds strategies and weights to the quorum * @dev Checks to make sure that the *same* strategy cannot be added multiple times (checks against both against existing and new strategies). * @dev This function has no check to make sure that the strategies for a single quorum have the same underlying asset. This is a concious choice, * since a middleware may want, e.g., a stablecoin quorum that accepts USDC, USDT, DAI, etc. as underlying assets and trades them as "equivalent". */ function addStrategies( - uint8 quorumNumber, + uint8 quorumNumber, StrategyParams[] memory _strategyParams ) public virtual onlyCoordinatorOwner quorumExists(quorumNumber) { _addStrategyParams(quorumNumber, _strategyParams); @@ -357,7 +384,7 @@ contract StakeRegistry is StakeRegistryStorage { })); } else { // We have prior stake history - fetch our last-recorded stake - StakeUpdate storage lastUpdate = operatorStakeHistory[operatorId][quorumNumber][historyLength-1]; + StakeUpdate storage lastUpdate = operatorStakeHistory[operatorId][quorumNumber][historyLength-1]; prevStake = lastUpdate.stake; // Short-circuit in case there's no change in stake @@ -368,7 +395,7 @@ contract StakeRegistry is StakeRegistryStorage { /** * If our last stake entry was made in the current block, update the entry * Otherwise, push a new entry and update the previous entry's "next" field - */ + */ if (lastUpdate.updateBlockNumber == uint32(block.number)) { lastUpdate.stake = newStake; } else { @@ -397,7 +424,7 @@ contract StakeRegistry is StakeRegistryStorage { if (stakeDelta == 0) { return lastStakeUpdate.stake; } - + // Calculate the new total stake by applying the delta to our previous stake uint96 newStake = _applyDelta(lastStakeUpdate.stake, stakeDelta); @@ -419,7 +446,7 @@ contract StakeRegistry is StakeRegistryStorage { return newStake; } - /** + /** * @notice Adds `strategyParams` to the `quorumNumber`-th quorum. * @dev Checks to make sure that the *same* strategy cannot be added multiple times (checks against both against existing and new strategies). * @dev This function has no check to make sure that the strategies for a single quorum have the same underlying asset. This is a conscious choice, @@ -493,6 +520,23 @@ contract StakeRegistry is StakeRegistryStorage { ); } + /// Returns total Slashable stake for an operator per strategy that can have the weights applied based on strategy multipliers + function _getSlashableStakePerStrategy(uint8 quorumNumber, address operator) internal view returns (uint256[] memory) { + address[] memory operators = new address[](1); + operators[0] = operator; + uint32 beforeTimestamp = uint32(block.timestamp + slashableStakeLookAheadPerQuorum[quorumNumber]); + + (,uint256[][] memory slashableShares) = IAllocationManager(serviceManager.allocationManager()) + .getMinDelegatedAndSlashableOperatorShares( + OperatorSet(address(serviceManager), quorumNumber), + operators, + strategiesPerQuorum[quorumNumber], + beforeTimestamp + ); + + return slashableShares[0]; + } + /** * @notice This function computes the total weight of the @param operator in the quorum @param quorumNumber. * @dev this method DOES NOT check that the quorum exists @@ -503,18 +547,21 @@ contract StakeRegistry is StakeRegistryStorage { uint96 weight; uint256 stratsLength = strategyParamsLength(quorumNumber); StrategyParams memory strategyAndMultiplier; + uint256[] memory strategyShares; - address[] memory operators = new address[](1); - operators[0] = operator; - uint32 beforeTimestamp = uint32(block.timestamp + slashableStakeLookAhead); - (uint256[][] memory strategyShares, ) = IAllocationManager(serviceManager.allocationManager()).getMinDelegatedAndSlashableOperatorShares(OperatorSet(address(serviceManager), quorumNumber), operators ,strategiesPerQuorum[quorumNumber], beforeTimestamp); + if (stakeTypePerQuorum[quorumNumber]== StakeType.TOTAL_SLASHABLE) { + strategyShares = _getSlashableStakePerStrategy(quorumNumber, operator); + } else { + /// M2 Concept of delegated stake + strategyShares = delegation.getOperatorShares(operator, strategiesPerQuorum[quorumNumber]); + } for (uint256 i = 0; i < stratsLength; i++) { // accessing i^th StrategyParams struct for the quorumNumber strategyAndMultiplier = strategyParams[quorumNumber][i]; // add the weight from the shares for this strategy to the total weight - if (strategyShares[i][0] > 0) { - weight += uint96(strategyShares[i][0] * strategyAndMultiplier.multiplier / WEIGHTING_DIVISOR); + if (strategyShares[i] > 0) { + weight += uint96(strategyShares[i] * strategyAndMultiplier.multiplier / WEIGHTING_DIVISOR); } } @@ -537,7 +584,7 @@ contract StakeRegistry is StakeRegistryStorage { * @dev reverts if the quorum does not exist */ function weightOfOperatorForQuorum( - uint8 quorumNumber, + uint8 quorumNumber, address operator ) public virtual view quorumExists(quorumNumber) returns (uint96) { (uint96 stake, ) = _weightOfOperatorForQuorum(quorumNumber, operator); @@ -551,7 +598,7 @@ contract StakeRegistry is StakeRegistryStorage { /// @notice Returns the strategy and weight multiplier for the `index`'th strategy in the quorum `quorumNumber` function strategyParamsByIndex( - uint8 quorumNumber, + uint8 quorumNumber, uint256 index ) public view returns (StrategyParams memory) { @@ -578,7 +625,7 @@ contract StakeRegistry is StakeRegistryStorage { * @param quorumNumber The quorum number to get the stake for. */ function getStakeHistory( - bytes32 operatorId, + bytes32 operatorId, uint8 quorumNumber ) external view returns (StakeUpdate[] memory) { return operatorStakeHistory[operatorId][quorumNumber]; @@ -697,7 +744,7 @@ contract StakeRegistry is StakeRegistryStorage { uint256 index ) external view returns (StakeUpdate memory) { return _totalStakeHistory[quorumNumber][index]; - } + } /** * @notice Returns the total stake weight for quorum `quorumNumber`, at the `index`-th entry in the @@ -747,22 +794,23 @@ contract StakeRegistry is StakeRegistryStorage { } /** - * @notice Sets the stake type for the registry + * @notice Sets the stake type for the registry for a specific quorum + * @param quorumNumber The quorum number to set the stake type for * @param _stakeType The type of stake to track (TOTAL_DELEGATED, TOTAL_SLASHABLE, or BOTH) */ - function _setStakeType(StakeType _stakeType) internal { - StakeType oldStakeType = stakeType; - stakeType = _stakeType; - emit StakeTypeSet(oldStakeType, _stakeType); + function _setStakeType(uint8 quorumNumber, StakeType _stakeType) internal { + stakeTypePerQuorum[quorumNumber] = _stakeType; + emit StakeTypeSet(_stakeType); } /** - * @notice Sets the look ahead time for checking operator shares + * @notice Sets the look ahead time for checking operator shares for a specific quorum + * @param quorumNumber The quorum number to set the look ahead period for * @param _lookAheadDays The number of days to look ahead when checking shares */ - function _setLookAheadPeriod(uint32 _lookAheadDays) internal { - uint32 oldLookAheadDays = slashableStakeLookAhead; - slashableStakeLookAhead = _lookAheadDays; + function _setLookAheadPeriod(uint8 quorumNumber, uint32 _lookAheadDays) internal { + uint32 oldLookAheadDays = slashableStakeLookAheadPerQuorum[quorumNumber]; + slashableStakeLookAheadPerQuorum[quorumNumber] = _lookAheadDays; emit LookAheadPeriodChanged(oldLookAheadDays, _lookAheadDays); } diff --git a/src/StakeRegistryStorage.sol b/src/StakeRegistryStorage.sol index 7fea4fa2..2cfcba43 100644 --- a/src/StakeRegistryStorage.sol +++ b/src/StakeRegistryStorage.sol @@ -7,7 +7,7 @@ import {IServiceManager} from "./interfaces/IServiceManager.sol"; import {IStrategyManager, IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol"; import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol"; -import {IStakeRegistry} from "./interfaces/IStakeRegistry.sol"; +import {IStakeRegistry, StakeType} from "./interfaces/IStakeRegistry.sol"; /** * @title Storage variables for the `StakeRegistry` contract. @@ -15,7 +15,7 @@ import {IStakeRegistry} from "./interfaces/IStakeRegistry.sol"; * @notice This storage contract is separate from the logic to simplify the upgrade process. */ abstract contract StakeRegistryStorage is IStakeRegistry { - + /// @notice Constant used as a divisor in calculating weights. uint256 public constant WEIGHTING_DIVISOR = 1e18; /// @notice Maximum length of dynamic arrays in the `strategyParams` mapping. @@ -52,12 +52,12 @@ abstract contract StakeRegistryStorage is IStakeRegistry { mapping(uint8 => StrategyParams[]) public strategyParams; mapping(uint8 => IStrategy[]) public strategiesPerQuorum; - StakeType public stakeType; + mapping(uint8 => StakeType) public stakeTypePerQuorum; - uint32 public slashableStakeLookAhead; + mapping(uint8 => uint32) public slashableStakeLookAheadPerQuorum; constructor( - IRegistryCoordinator _registryCoordinator, + IRegistryCoordinator _registryCoordinator, IDelegationManager _delegationManager, IAVSDirectory _avsDirectory, IServiceManager _serviceManager diff --git a/src/interfaces/IStakeRegistry.sol b/src/interfaces/IStakeRegistry.sol index ecc5b542..17770b3a 100644 --- a/src/interfaces/IStakeRegistry.sol +++ b/src/interfaces/IStakeRegistry.sol @@ -6,17 +6,17 @@ import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy import {IRegistry} from "./IRegistry.sol"; +enum StakeType { + TOTAL_DELEGATED, + TOTAL_SLASHABLE +} + /** * @title Interface for a `Registry` that keeps track of stakes of operators for up to 256 quorums. * @author Layr Labs, Inc. */ interface IStakeRegistry is IRegistry { - enum StakeType { - TOTAL_DELEGATED, - TOTAL_SLASHABLE - } - // DATA STRUCTURES /// @notice struct used to store the stakes of an individual operator or the sum of all operators' stakes, for storage @@ -53,7 +53,7 @@ interface IStakeRegistry is IRegistry { event LookAheadPeriodChanged(uint32 oldLookAheadDays, uint32 newLookAheadDays); /// @notice emitted when the stake type is updated - event StakeTypeSet(StakeType previousStakeType, StakeType newStakeType); + event StakeTypeSet(StakeType newStakeType); /// @notice emitted when the minimum stake for a quorum is updated event MinimumStakeForQuorumUpdated(uint8 indexed quorumNumber, uint96 minimumStake); /// @notice emitted when a new quorum is created @@ -101,7 +101,20 @@ interface IStakeRegistry is IRegistry { /** * @notice Initialize a new quorum created by the registry coordinator by setting strategies, weights, and minimum stake */ - function initializeQuorum(uint8 quorumNumber, uint96 minimumStake, StrategyParams[] memory strategyParams) external; + /// @notice Initialize a new quorum and push its first history update + function initializeDelegatedStakeQuorum( + uint8 quorumNumber, + uint96 minimumStake, + StrategyParams[] memory _strategyParams + ) external; + + /// @notice Initialize a new quorum and push its first history update + function initializeSlashableStakeQuorum( + uint8 quorumNumber, + uint96 minimumStake, + uint32 lookAheadPeriod, + StrategyParams[] memory _strategyParams + ) external; /// @notice Adds new strategies and the associated multipliers to the @param quorumNumber. function addStrategies( diff --git a/test/integration/IntegrationConfig.t.sol b/test/integration/IntegrationConfig.t.sol index d1cd2ac6..e1a341e8 100644 --- a/test/integration/IntegrationConfig.t.sol +++ b/test/integration/IntegrationConfig.t.sol @@ -94,10 +94,10 @@ contract IntegrationConfig is IntegrationDeployer, G2Operations, Constants { * so this is the best way to speed things up when running multiple tests. */ constructor() { - for (uint i = 0; i < NUM_GENERATED_OPERATORS; i++) { + for (uint i = 0; i < NUM_GENERATED_OPERATORS; i++) { IBLSApkRegistry.PubkeyRegistrationParams memory pubkey; uint privKey = uint(keccak256(abi.encodePacked(i + 1))); - + pubkey.pubkeyG1 = BN254.generatorG1().scalar_mul(privKey); pubkey.pubkeyG2 = G2Operations.mul(privKey); @@ -125,7 +125,7 @@ contract IntegrationConfig is IntegrationDeployer, G2Operations, Constants { /** * @param _randomSeed Fuzz tests supply a random u24 as input - * @param _userTypes [DEFAULT | ALT_METHODS] - every time a user is generated, it will use these values + * @param _userTypes [DEFAULT | ALT_METHODS] - every time a user is generated, it will use these values * @param _quorumConfig Quorums that are created/initialized in this method will be configured according * to this struct. See `QuorumConfig` above for details on each parameter. */ @@ -175,7 +175,7 @@ contract IntegrationConfig is IntegrationDeployer, G2Operations, Constants { emit log_named_uint("- Minimum stake", minimumStake); cheats.prank(registryCoordinatorOwner); - registryCoordinator.createQuorum({ + registryCoordinator.createTotalDelegatedStakeQuorum({ operatorSetParams: operatorSet, minimumStake: minimumStake, strategyParams: strategyParams @@ -211,7 +211,7 @@ contract IntegrationConfig is IntegrationDeployer, G2Operations, Constants { numOperators++; (User operator, IStrategy[] memory strategies, uint[] memory tokenBalances) = _randUser(operatorName); - + operator.registerAsOperator(); operator.depositIntoEigenlayer(strategies, tokenBalances); @@ -246,7 +246,7 @@ contract IntegrationConfig is IntegrationDeployer, G2Operations, Constants { IStrategy[] memory strategies = new IStrategy[](allStrats.length); uint[] memory balances = new uint[](allStrats.length); emit log_named_string("_dealRandTokens: dealing assets to", user.NAME()); - + // Deal the user a random balance between [MIN_BALANCE, MAX_BALANCE] for each existing strategy for (uint i = 0; i < allStrats.length; i++) { IStrategy strat = allStrats[i]; @@ -266,7 +266,7 @@ contract IntegrationConfig is IntegrationDeployer, G2Operations, Constants { IStrategy[] memory strategies = new IStrategy[](allStrats.length); uint[] memory balances = new uint[](allStrats.length); emit log_named_string("_dealMaxTokens: dealing assets to", user.NAME()); - + // Deal the user the 100 * MAX_BALANCE for each existing strategy for (uint i = 0; i < allStrats.length; i++) { IStrategy strat = allStrats[i]; @@ -287,7 +287,7 @@ contract IntegrationConfig is IntegrationDeployer, G2Operations, Constants { /// @param standardQuorums the quorums that we want to register for WITHOUT churn /// @return churnTargets: one churnable operator for each churnQuorum function _getChurnTargets( - User incomingOperator, + User incomingOperator, bytes memory churnQuorums, bytes memory standardQuorums ) internal returns (User[] memory) { @@ -304,7 +304,7 @@ contract IntegrationConfig is IntegrationDeployer, G2Operations, Constants { for (uint i = 0; i < churnQuorums.length; i++) { uint8 quorum = uint8(churnQuorums[i]); - IRegistryCoordinator.OperatorSetParam memory params + IRegistryCoordinator.OperatorSetParam memory params = registryCoordinator.getOperatorSetParams(quorum); // Sanity check - make sure we're at the operator cap @@ -337,7 +337,7 @@ contract IntegrationConfig is IntegrationDeployer, G2Operations, Constants { /// From RegistryCoordinator._individualKickThreshold function _individualKickThreshold( - uint96 operatorStake, + uint96 operatorStake, IRegistryCoordinator.OperatorSetParam memory setParams ) internal pure returns (uint96) { return operatorStake * setParams.kickBIPsOfOperatorStake / BIPS_DENOMINATOR; @@ -345,7 +345,7 @@ contract IntegrationConfig is IntegrationDeployer, G2Operations, Constants { /// From RegistryCoordinator._totalKickThreshold function _totalKickThreshold( - uint96 totalStake, + uint96 totalStake, IRegistryCoordinator.OperatorSetParam memory setParams ) internal pure returns (uint96) { return totalStake * setParams.kickBIPsOfTotalStake / BIPS_DENOMINATOR; @@ -380,7 +380,7 @@ contract IntegrationConfig is IntegrationDeployer, G2Operations, Constants { function _selectRandRegisteredOperator(uint8 quorum) internal returns (User) { uint32 curNumOperators = indexRegistry.totalOperatorsForQuorum(quorum); - + bytes32 randId = indexRegistry.getLatestOperatorUpdate({ quorumNumber: quorum, operatorIndex: uint32(_randUint({ min: 0, max: curNumOperators - 1 })) @@ -404,7 +404,7 @@ contract IntegrationConfig is IntegrationDeployer, G2Operations, Constants { /// @dev Uses `random` to return a random uint, with a range given by `min` and `max` (inclusive) /// @return `min` <= result <= `max` - function _randUint(uint min, uint max) internal returns (uint) { + function _randUint(uint min, uint max) internal returns (uint) { uint range = max - min + 1; // calculate the number of bits needed for the range @@ -459,7 +459,7 @@ contract IntegrationConfig is IntegrationDeployer, G2Operations, Constants { /// @dev Select a random value from `arr` and return it. Reverts if arr is empty function _randValue(bytes memory arr) internal returns (uint) { assertTrue(arr.length > 0, "_randValue: tried to select value from empty array"); - + uint idx = _randUint({ min: 0, max: arr.length - 1 }); return uint(uint8(arr[idx])); } @@ -470,7 +470,7 @@ contract IntegrationConfig is IntegrationDeployer, G2Operations, Constants { /// NOTE: This should only be used when initializing quorums for the first time (in _configRand) function _randQuorumCount() private returns (uint) { uint quorumFlag = _randValue(numQuorumFlags); - + if (quorumFlag == ONE) { return 1; } else if (quorumFlag == TWO) { @@ -520,7 +520,7 @@ contract IntegrationConfig is IntegrationDeployer, G2Operations, Constants { return params; } - /** + /** * @dev Uses _randFillType to determine how many operators to register for a quorum initially * @return The number of operators to register */ @@ -541,7 +541,7 @@ contract IntegrationConfig is IntegrationDeployer, G2Operations, Constants { /// @dev Select a random number of quorums to initialize function _randMinStake() private returns (uint96) { uint minStakeFlag = _randValue(minStakeFlags); - + if (minStakeFlag == NO_MINIMUM) { return 0; } else if (minStakeFlag == HAS_MINIMUM) { diff --git a/test/integration/IntegrationDeployer.t.sol b/test/integration/IntegrationDeployer.t.sol index 7b9705f9..2c33bfa5 100644 --- a/test/integration/IntegrationDeployer.t.sol +++ b/test/integration/IntegrationDeployer.t.sol @@ -331,6 +331,9 @@ abstract contract IntegrationDeployer is Test, IUserDeployer { slasher: address(msg.sender) }); + StakeType[] memory quorumStakeTypes = new StakeType[](0); + uint32[] memory slashableStakeQuorumLookAheadPeriods = new uint32[](0); + RegistryCoordinator registryCoordinatorImplementation = new RegistryCoordinator(serviceManager, stakeRegistry, blsApkRegistry, indexRegistry, avsDirectory); proxyAdmin.upgradeAndCall( @@ -345,7 +348,9 @@ abstract contract IntegrationDeployer is Test, IUserDeployer { 0, /*initialPausedStatus*/ new IRegistryCoordinator.OperatorSetParam[](0), new uint96[](0), - new IStakeRegistry.StrategyParams[][](0) + new IStakeRegistry.StrategyParams[][](0), + quorumStakeTypes, + slashableStakeQuorumLookAheadPeriods ) ); @@ -380,7 +385,7 @@ abstract contract IntegrationDeployer is Test, IUserDeployer { strategies[0] = strategy; cheats.prank(strategyManager.strategyWhitelister()); strategyManager.addStrategiesToDepositWhitelist( - strategies + strategies ); // Add to allStrats diff --git a/test/mocks/StakeRegistryMock.sol b/test/mocks/StakeRegistryMock.sol index f86b938f..2dcecce3 100644 --- a/test/mocks/StakeRegistryMock.sol +++ b/test/mocks/StakeRegistryMock.sol @@ -18,6 +18,19 @@ contract StakeRegistryMock is IStakeRegistry { function registryCoordinator() external view returns (address) {} + function initializeDelegatedStakeQuorum( + uint8 quorumNumber, + uint96 minimumStake, + StrategyParams[] memory _strategyParams + ) external {} + + function initializeSlashableStakeQuorum( + uint8 quorumNumber, + uint96 minimumStake, + uint32 lookAheadPeriod, + StrategyParams[] memory _strategyParams + ) external {} + /** * @notice Registers the `operator` with `operatorId` for the specified `quorumNumbers`. * @param operator The address of the operator to register. @@ -32,8 +45,8 @@ contract StakeRegistryMock is IStakeRegistry { * 4) the operator is not already registered */ function registerOperator( - address operator, - bytes32 operatorId, + address operator, + bytes32 operatorId, bytes memory quorumNumbers ) external returns (uint96[] memory, uint96[] memory) {} @@ -149,7 +162,7 @@ contract StakeRegistryMock is IStakeRegistry { /** * @notice Returns the stake weight corresponding to `operatorId` for quorum `quorumNumber`, at the - * `index`-th entry in the `operatorIdToStakeHistory[operatorId][quorumNumber]` array if the entry + * `index`-th entry in the `operatorIdToStakeHistory[operatorId][quorumNumber]` array if the entry * corresponds to the operator's stake at `blockNumber`. Reverts otherwise. * @param quorumNumber The quorum number to get the stake for. * @param operatorId The id of the operator of interest. @@ -164,8 +177,8 @@ contract StakeRegistryMock is IStakeRegistry { returns (uint96) {} /** - * @notice Returns the total stake weight for quorum `quorumNumber`, at the `index`-th entry in the - * `totalStakeHistory[quorumNumber]` array if the entry corresponds to the total stake at `blockNumber`. + * @notice Returns the total stake weight for quorum `quorumNumber`, at the `index`-th entry in the + * `totalStakeHistory[quorumNumber]` array if the entry corresponds to the total stake at `blockNumber`. * Reverts otherwise. * @param quorumNumber The quorum number to get the stake for. * @param index Array index for lookup, within the dynamic array `totalStakeHistory[quorumNumber]`. @@ -201,8 +214,8 @@ contract StakeRegistryMock is IStakeRegistry { * added to the */ function updateOperatorStake( - address /*operator*/, - bytes32 /*operatorId*/, + address /*operator*/, + bytes32 /*operatorId*/, bytes calldata /*quorumNumbers*/ ) external returns (uint192) { return updateOperatorStakeReturnBitmap; diff --git a/test/unit/OperatorStateRetrieverUnit.t.sol b/test/unit/OperatorStateRetrieverUnit.t.sol index 78b634d7..2fe360ca 100644 --- a/test/unit/OperatorStateRetrieverUnit.t.sol +++ b/test/unit/OperatorStateRetrieverUnit.t.sol @@ -93,7 +93,7 @@ contract OperatorStateRetrieverUnitTests is MockAVSDeployer { IStakeRegistry.StrategyParams({strategy: IStrategy(address(1000)), multiplier: 1e16}); cheats.prank(registryCoordinator.owner()); - registryCoordinator.createQuorum(operatorSetParams, minimumStake, strategyParams); + registryCoordinator.createTotalDelegatedStakeQuorum(operatorSetParams, minimumStake, strategyParams); cheats.expectRevert( "IndexRegistry._operatorCountAtBlockNumber: quorum did not exist at given block number" @@ -235,7 +235,7 @@ contract OperatorStateRetrieverUnitTests is MockAVSDeployer { IStakeRegistry.StrategyParams({strategy: IStrategy(address(1000)), multiplier: 1e16}); cheats.prank(registryCoordinator.owner()); - registryCoordinator.createQuorum(operatorSetParams, minimumStake, strategyParams); + registryCoordinator.createTotalDelegatedStakeQuorum(operatorSetParams, minimumStake, strategyParams); cheats.expectRevert( "StakeRegistry.getTotalStakeIndicesAtBlockNumber: quorum has no stake history at blockNumber" diff --git a/test/unit/RegistryCoordinatorMigration.t.sol b/test/unit/RegistryCoordinatorMigration.t.sol index 63e4c837..7b5a6b14 100644 --- a/test/unit/RegistryCoordinatorMigration.t.sol +++ b/test/unit/RegistryCoordinatorMigration.t.sol @@ -199,7 +199,7 @@ contract RegistryCoordinatorMigrationUnit is MockAVSDeployer, IServiceManagerBas assertTrue(avsDirectory.isOperatorSet(address(serviceManager), quorumNumber-1), "Operator set doesn't already existed"); vm.prank(registryCoordinator.owner()); - registryCoordinator.createQuorum(operatorSetParams, minimumStake, strategyParams); + registryCoordinator.createTotalDelegatedStakeQuorum(operatorSetParams, minimumStake, strategyParams); assertTrue(avsDirectory.isOperatorSet(address(serviceManager), quorumNumber), "Operator set was not created for the quorum"); @@ -259,8 +259,8 @@ contract RegistryCoordinatorMigrationUnit is MockAVSDeployer, IServiceManagerBas vm.prank(operators[0]); avsDirectory.forceDeregisterFromOperatorSets( - operators[0], - address(serviceManager), + operators[0], + address(serviceManager), operatorSetsToUnregister, ISignatureUtils.SignatureWithSaltAndExpiry({ signature: new bytes(0), @@ -466,5 +466,5 @@ contract RegistryCoordinatorMigrationUnit is MockAVSDeployer, IServiceManagerBas assertTrue(isOperatorRegistered, "Operator wasn't deregistered from operator set"); } - + } diff --git a/test/unit/RegistryCoordinatorUnit.t.sol b/test/unit/RegistryCoordinatorUnit.t.sol index 4129b8cb..d6931dc4 100644 --- a/test/unit/RegistryCoordinatorUnit.t.sol +++ b/test/unit/RegistryCoordinatorUnit.t.sol @@ -34,7 +34,7 @@ contract RegistryCoordinatorUnitTests is MockAVSDeployer { // Emitted when an operator pubkey is removed from a set of quorums event OperatorRemovedFromQuorums( - address operator, + address operator, bytes32 operatorId, bytes quorumNumbers ); @@ -72,7 +72,7 @@ contract RegistryCoordinatorUnitTests is MockAVSDeployer { for (uint i = 0; i < defaultMaxOperatorCount - 1; i++) { BN254.G1Point memory pubKey = BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, i))); address operator = _incrementAddress(defaultOperator, i); - + _registerOperatorWithCoordinator(operator, quorumBitmap, pubKey); } @@ -81,7 +81,7 @@ contract RegistryCoordinatorUnitTests is MockAVSDeployer { bytes32 operatorToRegisterId = BN254.hashG1Point(operatorToRegisterPubKey); bytes32 operatorToKickId; address operatorToKick; - + // register last operator before kick operatorKickParams = new IRegistryCoordinator.OperatorKickParam[](1); { @@ -115,7 +115,7 @@ contract RegistryCoordinatorUnitTests_Initialization_Setters is RegistryCoordina for (uint i = 0; i < numQuorums; i++) { assertEq( - keccak256(abi.encode(registryCoordinator.getOperatorSetParams(uint8(i)))), + keccak256(abi.encode(registryCoordinator.getOperatorSetParams(uint8(i)))), keccak256(abi.encode(operatorSetParams[i])) ); } @@ -124,13 +124,15 @@ contract RegistryCoordinatorUnitTests_Initialization_Setters is RegistryCoordina cheats.expectRevert(bytes("Initializable: contract is already initialized")); registryCoordinator.initialize( registryCoordinatorOwner, - churnApprover, - ejector, - pauserRegistry, - 0/*initialPausedStatus*/, - operatorSetParams, - new uint96[](0), - new IStakeRegistry.StrategyParams[][](0) + churnApprover, + ejector, + pauserRegistry, + 0/*initialPausedStatus*/, + operatorSetParams, + new uint96[](0), + new IStakeRegistry.StrategyParams[][](0), + new StakeType[](0), + new uint32[](0) ); } @@ -208,7 +210,7 @@ contract RegistryCoordinatorUnitTests_Initialization_Setters is RegistryCoordina cheats.expectRevert("Ownable: caller is not the owner"); cheats.prank(defaultOperator); - registryCoordinator.createQuorum(operatorSetParams, minimumStake, strategyParams); + registryCoordinator.createTotalDelegatedStakeQuorum(operatorSetParams, minimumStake, strategyParams); } function test_createQuorum() public { @@ -216,7 +218,7 @@ contract RegistryCoordinatorUnitTests_Initialization_Setters is RegistryCoordina // this is necessary since the default setup already configures the max number of quorums, preventing adding more _deployMockEigenLayerAndAVS(0); - IRegistryCoordinator.OperatorSetParam memory operatorSetParams = + IRegistryCoordinator.OperatorSetParam memory operatorSetParams = IRegistryCoordinator.OperatorSetParam({ maxOperatorCount: defaultMaxOperatorCount, kickBIPsOfOperatorStake: defaultKickBIPsOfOperatorStake, @@ -235,7 +237,7 @@ contract RegistryCoordinatorUnitTests_Initialization_Setters is RegistryCoordina cheats.expectEmit(true, true, true, true, address(registryCoordinator)); emit OperatorSetParamsUpdated(quorumCountBefore, operatorSetParams); cheats.prank(registryCoordinatorOwner); - registryCoordinator.createQuorum(operatorSetParams, minimumStake, strategyParams); + registryCoordinator.createTotalDelegatedStakeQuorum(operatorSetParams, minimumStake, strategyParams); uint8 quorumCountAfter = registryCoordinator.quorumCount(); assertEq(quorumCountAfter, quorumCountBefore + 1, "quorum count did not increase properly"); @@ -322,7 +324,7 @@ contract RegistryCoordinatorUnitTests_RegisterOperator is RegistryCoordinatorUni assertEq(registryCoordinator.getOperatorId(defaultOperator), defaultOperatorId); assertEq( - keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), + keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), keccak256(abi.encode(IRegistryCoordinator.OperatorInfo({ operatorId: defaultOperatorId, status: IRegistryCoordinator.OperatorStatus.REGISTERED @@ -330,7 +332,7 @@ contract RegistryCoordinatorUnitTests_RegisterOperator is RegistryCoordinatorUni ); assertEq(registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId), quorumBitmap); assertEq( - keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), + keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), keccak256(abi.encode(IRegistryCoordinator.QuorumBitmapUpdate({ quorumBitmap: uint192(quorumBitmap), updateBlockNumber: uint32(block.number), @@ -363,13 +365,13 @@ contract RegistryCoordinatorUnitTests_RegisterOperator is RegistryCoordinatorUni for (uint i = 0; i < quorumNumbers.length; i++) { cheats.expectEmit(true, true, true, true, address(stakeRegistry)); emit OperatorStakeUpdate(defaultOperatorId, uint8(quorumNumbers[i]), actualStake); - } + } for (uint i = 0; i < quorumNumbers.length; i++) { cheats.expectEmit(true, true, true, true, address(indexRegistry)); emit QuorumIndexUpdate(defaultOperatorId, uint8(quorumNumbers[i]), 0); - } - + } + uint256 gasBefore = gasleft(); cheats.prank(defaultOperator); registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); @@ -379,7 +381,7 @@ contract RegistryCoordinatorUnitTests_RegisterOperator is RegistryCoordinatorUni assertEq(registryCoordinator.getOperatorId(defaultOperator), defaultOperatorId); assertEq( - keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), + keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), keccak256(abi.encode(IRegistryCoordinator.OperatorInfo({ operatorId: defaultOperatorId, status: IRegistryCoordinator.OperatorStatus.REGISTERED @@ -387,7 +389,7 @@ contract RegistryCoordinatorUnitTests_RegisterOperator is RegistryCoordinatorUni ); assertEq(registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId), quorumBitmap); assertEq( - keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), + keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), keccak256(abi.encode(IRegistryCoordinator.QuorumBitmapUpdate({ quorumBitmap: uint192(quorumBitmap), updateBlockNumber: uint32(block.number), @@ -430,7 +432,7 @@ contract RegistryCoordinatorUnitTests_RegisterOperator is RegistryCoordinatorUni assertEq(registryCoordinator.getOperatorId(defaultOperator), defaultOperatorId); assertEq( - keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), + keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), keccak256(abi.encode(IRegistryCoordinator.OperatorInfo({ operatorId: defaultOperatorId, status: IRegistryCoordinator.OperatorStatus.REGISTERED @@ -438,7 +440,7 @@ contract RegistryCoordinatorUnitTests_RegisterOperator is RegistryCoordinatorUni ); assertEq(registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId), quorumBitmap); assertEq( - keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), + keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), keccak256(abi.encode(IRegistryCoordinator.QuorumBitmapUpdate({ quorumBitmap: uint192(BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers)), updateBlockNumber: uint32(registrationBlockNumber), @@ -446,7 +448,7 @@ contract RegistryCoordinatorUnitTests_RegisterOperator is RegistryCoordinatorUni }))) ); assertEq( - keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 1))), + keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 1))), keccak256(abi.encode(IRegistryCoordinator.QuorumBitmapUpdate({ quorumBitmap: uint192(quorumBitmap), updateBlockNumber: uint32(nextRegistrationBlockNumber), @@ -470,13 +472,13 @@ contract RegistryCoordinatorUnitTests_RegisterOperator is RegistryCoordinatorUni for (uint i = 0; i < numOperators; i++) { BN254.G1Point memory pubKey = BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, i))); address operator = _incrementAddress(defaultOperator, i); - + _registerOperatorWithCoordinator(operator, quorumBitmap, pubKey); } address operatorToRegister = _incrementAddress(defaultOperator, numOperators); BN254.G1Point memory operatorToRegisterPubKey = BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, numOperators))); - + blsApkRegistry.setBLSPublicKey(operatorToRegister, operatorToRegisterPubKey); _setOperatorWeight(operatorToRegister, defaultQuorumNumber, defaultStake); @@ -560,7 +562,7 @@ contract RegistryCoordinatorUnitTests_RegisterOperator is RegistryCoordinatorUni assertEq(registryCoordinator.getOperatorId(defaultOperator), defaultOperatorId); assertEq( - keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), + keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), keccak256(abi.encode(IRegistryCoordinator.OperatorInfo({ operatorId: defaultOperatorId, status: IRegistryCoordinator.OperatorStatus.REGISTERED @@ -568,7 +570,7 @@ contract RegistryCoordinatorUnitTests_RegisterOperator is RegistryCoordinatorUni ); assertEq(registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId), quorumBitmap); assertEq( - keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), + keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), keccak256(abi.encode(IRegistryCoordinator.QuorumBitmapUpdate({ quorumBitmap: uint192(quorumBitmap), updateBlockNumber: uint32(block.number), @@ -633,9 +635,9 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist _setOperatorWeight(defaultOperator, uint8(quorumNumbers[0]), defaultStake); cheats.startPrank(defaultOperator); - + cheats.roll(registrationBlockNumber); - + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers); @@ -653,7 +655,7 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist emit log_named_uint("gasUsed", gasBefore - gasAfter); assertEq( - keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), + keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), keccak256(abi.encode(IRegistryCoordinator.OperatorInfo({ operatorId: defaultOperatorId, status: IRegistryCoordinator.OperatorStatus.DEREGISTERED @@ -661,7 +663,7 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist ); assertEq(registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId), 0); assertEq( - keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), + keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), keccak256(abi.encode(IRegistryCoordinator.QuorumBitmapUpdate({ quorumBitmap: uint192(quorumBitmap), updateBlockNumber: registrationBlockNumber, @@ -687,9 +689,9 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist } cheats.startPrank(defaultOperator); - + cheats.roll(registrationBlockNumber); - + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); cheats.expectEmit(true, true, true, true, address(blsApkRegistry)); @@ -708,7 +710,7 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist emit log_named_uint("numQuorums", quorumNumbers.length); assertEq( - keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), + keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), keccak256(abi.encode(IRegistryCoordinator.OperatorInfo({ operatorId: defaultOperatorId, status: IRegistryCoordinator.OperatorStatus.DEREGISTERED @@ -716,7 +718,7 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist ); assertEq(registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId), 0); assertEq( - keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), + keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), keccak256(abi.encode(IRegistryCoordinator.QuorumBitmapUpdate({ quorumBitmap: uint192(quorumBitmap), updateBlockNumber: registrationBlockNumber, @@ -747,9 +749,9 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist } cheats.startPrank(defaultOperator); - + cheats.roll(registrationBlockNumber); - + registryCoordinator.registerOperator(registrationquorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); bytes memory deregistrationquorumNumbers = BitmapUtils.bitmapToBytesArray(deregistrationQuorumBitmap); @@ -772,27 +774,27 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist // check that the operator is marked as 'degregistered' only if deregistered from *all* quorums if (deregistrationQuorumBitmap == registrationQuorumBitmap) { assertEq( - keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), + keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), keccak256(abi.encode(IRegistryCoordinator.OperatorInfo({ operatorId: defaultOperatorId, status: IRegistryCoordinator.OperatorStatus.DEREGISTERED }))) - ); + ); } else { assertEq( - keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), + keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), keccak256(abi.encode(IRegistryCoordinator.OperatorInfo({ operatorId: defaultOperatorId, status: IRegistryCoordinator.OperatorStatus.REGISTERED }))) - ); + ); } // ensure that the operator's current quorum bitmap matches the expectation uint256 expectedQuorumBitmap = BitmapUtils.minus(registrationQuorumBitmap, deregistrationQuorumBitmap); assertEq(registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId), expectedQuorumBitmap); // check that the quorum bitmap history is as expected assertEq( - keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), + keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), keccak256(abi.encode(IRegistryCoordinator.QuorumBitmapUpdate({ quorumBitmap: uint192(registrationQuorumBitmap), updateBlockNumber: registrationBlockNumber, @@ -802,7 +804,7 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist // note: there will be no second entry in the operator's bitmap history in the event that the operator has totally deregistered if (deregistrationQuorumBitmap != registrationQuorumBitmap) { assertEq( - keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 1))), + keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 1))), keccak256(abi.encode(IRegistryCoordinator.QuorumBitmapUpdate({ quorumBitmap: uint192(expectedQuorumBitmap), updateBlockNumber: deregistrationBlockNumber, @@ -815,7 +817,7 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist // @notice registers the max number of operators with fuzzed bitmaps and then deregisters a pseudorandom operator (from all of their quorums) function testFuzz_deregisterOperator_manyOperators(uint256 pseudoRandomNumber) public { uint32 numOperators = defaultMaxOperatorCount; - + uint32 registrationBlockNumber = 100; uint32 deregistrationBlockNumber = 200; @@ -827,14 +829,14 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist } cheats.roll(registrationBlockNumber); - + bytes32[] memory lastOperatorInQuorum = new bytes32[](numQuorums); for (uint i = 0; i < numOperators; i++) { emit log_named_uint("i", i); BN254.G1Point memory pubKey = BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, i))); bytes32 operatorId = BN254.hashG1Point(pubKey); address operator = _incrementAddress(defaultOperator, i); - + _registerOperatorWithCoordinator(operator, quorumBitmaps[i], pubKey); // for each quorum the operator is in, save the operatorId @@ -858,7 +860,7 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist cheats.expectEmit(true, true, true, true, address(blsApkRegistry)); emit OperatorRemovedFromQuorums(operatorToDeregister, operatorToDeregisterId, operatorToDeregisterQuorumNumbers); - + for (uint i = 0; i < operatorToDeregisterQuorumNumbers.length; i++) { cheats.expectEmit(true, true, true, true, address(stakeRegistry)); emit OperatorStakeUpdate(operatorToDeregisterId, uint8(operatorToDeregisterQuorumNumbers[i]), 0); @@ -870,7 +872,7 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist registryCoordinator.deregisterOperator(operatorToDeregisterQuorumNumbers); assertEq( - keccak256(abi.encode(registryCoordinator.getOperator(operatorToDeregister))), + keccak256(abi.encode(registryCoordinator.getOperator(operatorToDeregister))), keccak256(abi.encode(IRegistryCoordinator.OperatorInfo({ operatorId: operatorToDeregisterId, status: IRegistryCoordinator.OperatorStatus.DEREGISTERED @@ -878,7 +880,7 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist ); assertEq(registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId), 0); assertEq( - keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(operatorToDeregisterId, 0))), + keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(operatorToDeregisterId, 0))), keccak256(abi.encode(IRegistryCoordinator.QuorumBitmapUpdate({ quorumBitmap: uint192(operatorToDeregisterQuorumBitmap), updateBlockNumber: registrationBlockNumber, @@ -898,9 +900,9 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist quorumNumbers[0] = bytes1(defaultQuorumNumber); cheats.startPrank(defaultOperator); - + cheats.roll(reregistrationBlockNumber); - + // store data before registering, to check against later IRegistryCoordinator.QuorumBitmapUpdate memory previousQuorumBitmapUpdate = registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0); @@ -911,7 +913,7 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers); assertEq(registryCoordinator.getOperatorId(defaultOperator), defaultOperatorId, "1"); assertEq( - keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), + keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), keccak256(abi.encode(IRegistryCoordinator.OperatorInfo({ operatorId: defaultOperatorId, status: IRegistryCoordinator.OperatorStatus.REGISTERED @@ -921,14 +923,14 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist assertEq(registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId), quorumBitmap, "3"); // check that previous entry in bitmap history was not changed assertEq( - keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), + keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), keccak256(abi.encode(previousQuorumBitmapUpdate)), "4" ); // check that new entry in bitmap history is as expected uint historyLength = registryCoordinator.getQuorumBitmapHistoryLength(defaultOperatorId); assertEq( - keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, historyLength - 1))), + keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, historyLength - 1))), keccak256(abi.encode(IRegistryCoordinator.QuorumBitmapUpdate({ quorumBitmap: uint192(quorumBitmap), updateBlockNumber: uint32(reregistrationBlockNumber), @@ -1087,27 +1089,27 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist // check that the operator is marked as 'degregistered' only if deregistered from *all* quorums if (deregistrationQuorumBitmap == registrationQuorumBitmap) { assertEq( - keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), + keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), keccak256(abi.encode(IRegistryCoordinator.OperatorInfo({ operatorId: defaultOperatorId, status: IRegistryCoordinator.OperatorStatus.DEREGISTERED }))) - ); + ); } else { assertEq( - keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), + keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), keccak256(abi.encode(IRegistryCoordinator.OperatorInfo({ operatorId: defaultOperatorId, status: IRegistryCoordinator.OperatorStatus.REGISTERED }))) - ); + ); } // ensure that the operator's current quorum bitmap matches the expectation uint256 expectedQuorumBitmap = BitmapUtils.minus(registrationQuorumBitmap, deregistrationQuorumBitmap); assertEq(registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId), expectedQuorumBitmap); // check that the quorum bitmap history is as expected assertEq( - keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), + keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), keccak256(abi.encode(IRegistryCoordinator.QuorumBitmapUpdate({ quorumBitmap: uint192(registrationQuorumBitmap), updateBlockNumber: registrationBlockNumber, @@ -1117,7 +1119,7 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist // note: there will be no second entry in the operator's bitmap history in the event that the operator has totally deregistered if (deregistrationQuorumBitmap != registrationQuorumBitmap) { assertEq( - keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 1))), + keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 1))), keccak256(abi.encode(IRegistryCoordinator.QuorumBitmapUpdate({ quorumBitmap: uint192(expectedQuorumBitmap), updateBlockNumber: deregistrationBlockNumber, @@ -1147,10 +1149,10 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist // eject cheats.prank(ejector); registryCoordinator.ejectOperator(defaultOperator, quorumNumbers); - + // make sure the operator is deregistered assertEq( - keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), + keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), keccak256(abi.encode(IRegistryCoordinator.OperatorInfo({ operatorId: defaultOperatorId, status: IRegistryCoordinator.OperatorStatus.DEREGISTERED @@ -1158,7 +1160,7 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist ); // make sure the operator is not in any quorums assertEq(registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId), 0); - } + } function test_ejectOperator_subsetOfQuorums() public { // register operator with default stake with 2 quorums @@ -1186,10 +1188,10 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist cheats.prank(ejector); registryCoordinator.ejectOperator(defaultOperator, quorumNumbersToEject); - + // make sure the operator is registered assertEq( - keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), + keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), keccak256(abi.encode(IRegistryCoordinator.OperatorInfo({ operatorId: defaultOperatorId, status: IRegistryCoordinator.OperatorStatus.REGISTERED @@ -1212,7 +1214,7 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist cheats.prank(defaultOperator); registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); - + cheats.expectRevert("RegistryCoordinator.onlyEjector: not ejector"); cheats.prank(defaultOperator); registryCoordinator.ejectOperator(defaultOperator, quorumNumbers); @@ -1234,7 +1236,7 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist quorumNumbers[0] = bytes1(defaultQuorumNumber); _setOperatorWeight(defaultOperator, uint8(quorumNumbers[0]), defaultStake); cheats.roll(registrationBlockNumber); - cheats.startPrank(defaultOperator); + cheats.startPrank(defaultOperator); registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); uint32 blockNumber = 0; @@ -1247,7 +1249,7 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist blockNumber = registrationBlockNumber; returnArray = registryCoordinator.getQuorumBitmapIndicesAtBlockNumber(blockNumber, operatorIds); - assertEq(returnArray[0], 0, "defaultOperator bitmap index at blockNumber registrationBlockNumber was not 0"); + assertEq(returnArray[0], 0, "defaultOperator bitmap index at blockNumber registrationBlockNumber was not 0"); blockNumber = registrationBlockNumber + 1; returnArray = registryCoordinator.getQuorumBitmapIndicesAtBlockNumber(blockNumber, operatorIds); @@ -1269,7 +1271,7 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist blockNumber = registrationBlockNumber; returnArray = registryCoordinator.getQuorumBitmapIndicesAtBlockNumber(blockNumber, operatorIds); - assertEq(returnArray[0], 0, "defaultOperator bitmap index at blockNumber registrationBlockNumber was not 0"); + assertEq(returnArray[0], 0, "defaultOperator bitmap index at blockNumber registrationBlockNumber was not 0"); blockNumber = registrationBlockNumber + 1; returnArray = registryCoordinator.getQuorumBitmapIndicesAtBlockNumber(blockNumber, operatorIds); @@ -1277,11 +1279,11 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist blockNumber = deregistrationBlockNumber; returnArray = registryCoordinator.getQuorumBitmapIndicesAtBlockNumber(blockNumber, operatorIds); - assertEq(returnArray[0], 1, "defaultOperator bitmap index at blockNumber deregistrationBlockNumber was not 1"); + assertEq(returnArray[0], 1, "defaultOperator bitmap index at blockNumber deregistrationBlockNumber was not 1"); blockNumber = deregistrationBlockNumber + 1; returnArray = registryCoordinator.getQuorumBitmapIndicesAtBlockNumber(blockNumber, operatorIds); - assertEq(returnArray[0], 1, "defaultOperator bitmap index at blockNumber deregistrationBlockNumber + 1 was not 1"); + assertEq(returnArray[0], 1, "defaultOperator bitmap index at blockNumber deregistrationBlockNumber + 1 was not 1"); } // @notice tests for correct reversion and return values in the event that an operator registers and later deregisters @@ -1302,7 +1304,7 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist blockNumber = registrationBlockNumber; returnVal = registryCoordinator.getQuorumBitmapAtBlockNumberByIndex(operatorId, blockNumber, index); - assertEq(returnVal, defaultQuorumBitmap, "defaultOperator bitmap index at blockNumber registrationBlockNumber was not defaultQuorumBitmap"); + assertEq(returnVal, defaultQuorumBitmap, "defaultOperator bitmap index at blockNumber registrationBlockNumber was not defaultQuorumBitmap"); blockNumber = registrationBlockNumber + 1; returnVal = registryCoordinator.getQuorumBitmapAtBlockNumberByIndex(operatorId, blockNumber, index); @@ -1315,11 +1317,11 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist blockNumber = deregistrationBlockNumber; returnVal = registryCoordinator.getQuorumBitmapAtBlockNumberByIndex(operatorId, blockNumber, index); - assertEq(returnVal, emptyBitmap, "defaultOperator bitmap index at blockNumber deregistrationBlockNumber was not emptyBitmap"); + assertEq(returnVal, emptyBitmap, "defaultOperator bitmap index at blockNumber deregistrationBlockNumber was not emptyBitmap"); blockNumber = deregistrationBlockNumber + 1; returnVal = registryCoordinator.getQuorumBitmapAtBlockNumberByIndex(operatorId, blockNumber, index); - assertEq(returnVal, emptyBitmap, "defaultOperator bitmap index at blockNumber deregistrationBlockNumber + 1 was not emptyBitmap"); + assertEq(returnVal, emptyBitmap, "defaultOperator bitmap index at blockNumber deregistrationBlockNumber + 1 was not emptyBitmap"); // try an incorrect index input and confirm reversion index = 0; @@ -1345,7 +1347,7 @@ contract RegistryCoordinatorUnitTests_RegisterOperatorWithChurn is RegistryCoord for (uint i = 0; i < numOperators - 1; i++) { BN254.G1Point memory pubKey = BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, i))); address operator = _incrementAddress(defaultOperator, i); - + _registerOperatorWithCoordinator(operator, quorumBitmap, pubKey); } @@ -1354,7 +1356,7 @@ contract RegistryCoordinatorUnitTests_RegisterOperatorWithChurn is RegistryCoord bytes32 operatorToRegisterId = BN254.hashG1Point(operatorToRegisterPubKey); bytes32 operatorToKickId; address operatorToKick; - + // register last operator before kick IRegistryCoordinator.OperatorKickParam[] memory operatorKickParams = new IRegistryCoordinator.OperatorKickParam[](1); { @@ -1409,10 +1411,10 @@ contract RegistryCoordinatorUnitTests_RegisterOperatorWithChurn is RegistryCoord cheats.prank(operatorToRegister); uint256 gasBefore = gasleft(); registryCoordinator.registerOperatorWithChurn( - quorumNumbers, + quorumNumbers, defaultSocket, pubkeyRegistrationParams, - operatorKickParams, + operatorKickParams, signatureWithExpiry, emptyAVSRegSig ); @@ -1421,21 +1423,21 @@ contract RegistryCoordinatorUnitTests_RegisterOperatorWithChurn is RegistryCoord } assertEq( - keccak256(abi.encode(registryCoordinator.getOperator(operatorToRegister))), + keccak256(abi.encode(registryCoordinator.getOperator(operatorToRegister))), keccak256(abi.encode(IRegistryCoordinator.OperatorInfo({ operatorId: operatorToRegisterId, status: IRegistryCoordinator.OperatorStatus.REGISTERED }))) ); assertEq( - keccak256(abi.encode(registryCoordinator.getOperator(operatorToKick))), + keccak256(abi.encode(registryCoordinator.getOperator(operatorToKick))), keccak256(abi.encode(IRegistryCoordinator.OperatorInfo({ operatorId: operatorToKickId, status: IRegistryCoordinator.OperatorStatus.DEREGISTERED }))) ); assertEq( - keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(operatorToKickId, 0))), + keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(operatorToKickId, 0))), keccak256(abi.encode(IRegistryCoordinator.QuorumBitmapUpdate({ quorumBitmap: uint192(quorumBitmap), updateBlockNumber: kickRegistrationBlockNumber, @@ -1449,8 +1451,8 @@ contract RegistryCoordinatorUnitTests_RegisterOperatorWithChurn is RegistryCoord quorumNumbers[0] = bytes1(defaultQuorumNumber); ISignatureUtils.SignatureWithSaltAndExpiry memory emptyAVSRegSig; - ( - address operatorToRegister, + ( + address operatorToRegister, BN254.G1Point memory operatorToRegisterPubKey, IRegistryCoordinator.OperatorKickParam[] memory operatorKickParams ) = _test_registerOperatorWithChurn_SetUp(pseudoRandomNumber, quorumNumbers, defaultStake); @@ -1464,10 +1466,10 @@ contract RegistryCoordinatorUnitTests_RegisterOperatorWithChurn is RegistryCoord cheats.prank(operatorToRegister); cheats.expectRevert("RegistryCoordinator._validateChurn: incoming operator has insufficient stake for churn"); registryCoordinator.registerOperatorWithChurn( - quorumNumbers, + quorumNumbers, defaultSocket, pubkeyRegistrationParams, - operatorKickParams, + operatorKickParams, signatureWithExpiry, emptyAVSRegSig ); @@ -1479,8 +1481,8 @@ contract RegistryCoordinatorUnitTests_RegisterOperatorWithChurn is RegistryCoord ISignatureUtils.SignatureWithSaltAndExpiry memory emptyAVSRegSig; uint96 operatorToKickStake = defaultMaxOperatorCount * defaultStake; - ( - address operatorToRegister, + ( + address operatorToRegister, BN254.G1Point memory operatorToRegisterPubKey, IRegistryCoordinator.OperatorKickParam[] memory operatorKickParams ) = _test_registerOperatorWithChurn_SetUp(pseudoRandomNumber, quorumNumbers, operatorToKickStake); @@ -1496,10 +1498,10 @@ contract RegistryCoordinatorUnitTests_RegisterOperatorWithChurn is RegistryCoord cheats.prank(operatorToRegister); cheats.expectRevert("RegistryCoordinator._validateChurn: cannot kick operator with more than kickBIPsOfTotalStake"); registryCoordinator.registerOperatorWithChurn( - quorumNumbers, + quorumNumbers, defaultSocket, pubkeyRegistrationParams, - operatorKickParams, + operatorKickParams, signatureWithExpiry, emptyAVSRegSig ); @@ -1510,8 +1512,8 @@ contract RegistryCoordinatorUnitTests_RegisterOperatorWithChurn is RegistryCoord quorumNumbers[0] = bytes1(defaultQuorumNumber); ISignatureUtils.SignatureWithSaltAndExpiry memory emptyAVSRegSig; - ( - address operatorToRegister, + ( + address operatorToRegister, , IRegistryCoordinator.OperatorKickParam[] memory operatorKickParams ) = _test_registerOperatorWithChurn_SetUp(pseudoRandomNumber, quorumNumbers, defaultStake); @@ -1528,10 +1530,10 @@ contract RegistryCoordinatorUnitTests_RegisterOperatorWithChurn is RegistryCoord cheats.prank(operatorToRegister); cheats.expectRevert(bytes4(keccak256("InvalidSignature()"))); registryCoordinator.registerOperatorWithChurn( - quorumNumbers, + quorumNumbers, defaultSocket, pubkeyRegistrationParams, - operatorKickParams, + operatorKickParams, signatureWithSaltAndExpiry, emptyAVSRegSig ); @@ -1542,8 +1544,8 @@ contract RegistryCoordinatorUnitTests_RegisterOperatorWithChurn is RegistryCoord quorumNumbers[0] = bytes1(defaultQuorumNumber); ISignatureUtils.SignatureWithSaltAndExpiry memory emptyAVSRegSig; - ( - address operatorToRegister, + ( + address operatorToRegister, BN254.G1Point memory operatorToRegisterPubKey, IRegistryCoordinator.OperatorKickParam[] memory operatorKickParams ) = _test_registerOperatorWithChurn_SetUp(pseudoRandomNumber, quorumNumbers, defaultStake); @@ -1558,10 +1560,10 @@ contract RegistryCoordinatorUnitTests_RegisterOperatorWithChurn is RegistryCoord cheats.prank(operatorToRegister); cheats.expectRevert("RegistryCoordinator._verifyChurnApproverSignature: signature expired"); registryCoordinator.registerOperatorWithChurn( - quorumNumbers, + quorumNumbers, defaultSocket, pubkeyRegistrationParams, - operatorKickParams, + operatorKickParams, signatureWithSaltAndExpiry, emptyAVSRegSig ); @@ -1611,7 +1613,7 @@ contract RegistryCoordinatorUnitTests_UpdateOperators is RegistryCoordinatorUnit uint32 registrationBlockNumber = 100; bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(registrationBitmap); for (uint256 i = 0; i < quorumNumbers.length; ++i) { - _setOperatorWeight(defaultOperator, uint8(quorumNumbers[i]), defaultStake); + _setOperatorWeight(defaultOperator, uint8(quorumNumbers[i]), defaultStake); } cheats.startPrank(defaultOperator); cheats.roll(registrationBlockNumber); @@ -1627,7 +1629,7 @@ contract RegistryCoordinatorUnitTests_UpdateOperators is RegistryCoordinatorUnit uint192 quorumBitmapToRemove = mockReturnData; bytes memory quorumNumbersToRemove = BitmapUtils.bitmapToBytesArray(quorumBitmapToRemove); for (uint256 i = 0; i < quorumNumbersToRemove.length; ++i) { - _setOperatorWeight(defaultOperator, uint8(quorumNumbersToRemove[i]), 0); + _setOperatorWeight(defaultOperator, uint8(quorumNumbersToRemove[i]), 0); } uint256 expectedQuorumBitmap = BitmapUtils.minus(quorumBitmapBefore, quorumBitmapToRemove); @@ -1726,7 +1728,7 @@ contract RegistryCoordinatorUnitTests_UpdateOperators is RegistryCoordinatorUnit for (uint i = 0; i < numOperators; i++) { BN254.G1Point memory pubKey = BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, i))); address operator = _incrementAddress(defaultOperator, i); - + _registerOperatorWithCoordinator(operator, quorumBitmap, pubKey); } @@ -1753,7 +1755,7 @@ contract RegistryCoordinatorUnitTests_UpdateOperators is RegistryCoordinatorUnit for (uint i = 0; i < numOperators; i++) { BN254.G1Point memory pubKey = BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, i))); address operator = _incrementAddress(defaultOperator, i); - + _registerOperatorWithCoordinator(operator, quorumBitmap, pubKey); } @@ -1806,7 +1808,7 @@ contract RegistryCoordinatorUnitTests_UpdateOperators is RegistryCoordinatorUnit for (uint i = 0; i < numOperators; i++) { BN254.G1Point memory pubKey = BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, i))); address operator = _incrementAddress(defaultOperator, i); - + _registerOperatorWithCoordinator(operator, quorumBitmap, pubKey); } @@ -1832,7 +1834,7 @@ contract RegistryCoordinatorUnitTests_UpdateOperators is RegistryCoordinatorUnit function testFuzz_updateOperatorBitmapInternal_noPreviousEntries(uint192 newBitmap) public { registryCoordinator._updateOperatorBitmapExternal(defaultOperatorId, newBitmap); assertEq( - keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), + keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), keccak256(abi.encode(IRegistryCoordinator.QuorumBitmapUpdate({ quorumBitmap: uint192(newBitmap), updateBlockNumber: uint32(block.number), @@ -1848,7 +1850,7 @@ contract RegistryCoordinatorUnitTests_UpdateOperators is RegistryCoordinatorUnit registryCoordinator._updateOperatorBitmapExternal(defaultOperatorId, newBitmap); assertEq( - keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), + keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), keccak256(abi.encode(IRegistryCoordinator.QuorumBitmapUpdate({ quorumBitmap: uint192(newBitmap), updateBlockNumber: uint32(block.number), @@ -1868,7 +1870,7 @@ contract RegistryCoordinatorUnitTests_UpdateOperators is RegistryCoordinatorUnit registryCoordinator._updateOperatorBitmapExternal(defaultOperatorId, newBitmap); assertEq( - keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), + keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), keccak256(abi.encode(IRegistryCoordinator.QuorumBitmapUpdate({ quorumBitmap: uint192(pastBitmap), updateBlockNumber: uint32(previousBlockNumber), @@ -1876,7 +1878,7 @@ contract RegistryCoordinatorUnitTests_UpdateOperators is RegistryCoordinatorUnit }))) ); assertEq( - keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 1))), + keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 1))), keccak256(abi.encode(IRegistryCoordinator.QuorumBitmapUpdate({ quorumBitmap: uint192(newBitmap), updateBlockNumber: uint32(block.number), diff --git a/test/unit/StakeRegistryUnit.t.sol b/test/unit/StakeRegistryUnit.t.sol index 71348e1b..4b74194a 100644 --- a/test/unit/StakeRegistryUnit.t.sol +++ b/test/unit/StakeRegistryUnit.t.sol @@ -100,7 +100,10 @@ contract StakeRegistryUnitTests is MockAVSDeployer, IStakeRegistryEvents { nextQuorum++; cheats.prank(address(registryCoordinator)); - stakeRegistry.initializeQuorum(quorumNumber, minimumStake, strategyParams); + stakeRegistry.initializeDelegatedStakeQuorum(quorumNumber, minimumStake, strategyParams); + + StakeType stakeType = stakeRegistry.stakeTypePerQuorum(quorumNumber); + assertEq(uint8(stakeType), uint8(StakeType.TOTAL_DELEGATED), "invalid stake type"); // Mark quorum initialized for other tests initializedQuorumBitmap = uint192(initializedQuorumBitmap.setBit(quorumNumber)); @@ -127,7 +130,7 @@ contract StakeRegistryUnitTests is MockAVSDeployer, IStakeRegistryEvents { nextQuorum++; cheats.prank(address(registryCoordinator)); - stakeRegistry.initializeQuorum(quorumNumber, minimumStake, strategyParams); + stakeRegistry.initializeDelegatedStakeQuorum(quorumNumber, minimumStake, strategyParams); // Mark quorum initialized for other tests initializedQuorumBitmap = uint192(initializedQuorumBitmap.setBit(quorumNumber)); @@ -580,7 +583,7 @@ contract StakeRegistryUnitTests_Config is StakeRegistryUnitTests { cheats.expectRevert( "StakeRegistry.onlyRegistryCoordinator: caller is not the RegistryCoordinator" ); - stakeRegistry.initializeQuorum(quorumNumber, minimumStake, strategyParams); + stakeRegistry.initializeDelegatedStakeQuorum(quorumNumber, minimumStake, strategyParams); } function testFuzz_initializeQuorum_Revert_WhenQuorumAlreadyExists( @@ -590,7 +593,7 @@ contract StakeRegistryUnitTests_Config is StakeRegistryUnitTests { ) public fuzzOnlyInitializedQuorums(quorumNumber) { cheats.expectRevert("StakeRegistry.initializeQuorum: quorum already exists"); cheats.prank(address(registryCoordinator)); - stakeRegistry.initializeQuorum(quorumNumber, minimumStake, strategyParams); + stakeRegistry.initializeDelegatedStakeQuorum(quorumNumber, minimumStake, strategyParams); } function testFuzz_initializeQuorum_Revert_WhenInvalidArrayLengths( @@ -602,7 +605,7 @@ contract StakeRegistryUnitTests_Config is StakeRegistryUnitTests { new IStakeRegistry.StrategyParams[](0); cheats.expectRevert("StakeRegistry._addStrategyParams: no strategies provided"); cheats.prank(address(registryCoordinator)); - stakeRegistry.initializeQuorum(quorumNumber, minimumStake, strategyParams); + stakeRegistry.initializeDelegatedStakeQuorum(quorumNumber, minimumStake, strategyParams); strategyParams = new IStakeRegistry.StrategyParams[](MAX_WEIGHING_FUNCTION_LENGTH + 1); for (uint256 i = 0; i < strategyParams.length; i++) { @@ -612,7 +615,55 @@ contract StakeRegistryUnitTests_Config is StakeRegistryUnitTests { } cheats.expectRevert("StakeRegistry._addStrategyParams: exceed MAX_WEIGHING_FUNCTION_LENGTH"); cheats.prank(address(registryCoordinator)); - stakeRegistry.initializeQuorum(quorumNumber, minimumStake, strategyParams); + stakeRegistry.initializeDelegatedStakeQuorum(quorumNumber, minimumStake, strategyParams); + } + event StakeTypeSet(StakeType newStakeType); + + function test_initializeDelegatedStakeQuorum() public { + uint8 quorumNumber = nextQuorum; + uint96 minimumStake = 0; + IStakeRegistry.StrategyParams[] memory strategyParams = + new IStakeRegistry.StrategyParams[](1); + strategyParams[0] = IStakeRegistry.StrategyParams( + IStrategy(address(uint160(uint256(keccak256(abi.encodePacked(quorumNumber)))))), + uint96(WEIGHTING_DIVISOR) + ); + + cheats.prank(address(registryCoordinator)); + cheats.expectEmit(true, true, true, true); + emit StakeTypeSet(StakeType.TOTAL_DELEGATED); + stakeRegistry.initializeDelegatedStakeQuorum( + quorumNumber, + minimumStake, + strategyParams + ); + + StakeType stakeType = stakeRegistry.stakeTypePerQuorum(quorumNumber); + assertEq(uint8(stakeType), uint8(StakeType.TOTAL_DELEGATED), "invalid stake type"); + } + + function test_initializeSlashableStakeQuorum() public { + uint8 quorumNumber = nextQuorum; + uint96 minimumStake = 0; + IStakeRegistry.StrategyParams[] memory strategyParams = + new IStakeRegistry.StrategyParams[](1); + strategyParams[0] = IStakeRegistry.StrategyParams( + IStrategy(address(uint160(uint256(keccak256(abi.encodePacked(quorumNumber)))))), + uint96(WEIGHTING_DIVISOR) + ); + + cheats.prank(address(registryCoordinator)); + cheats.expectEmit(true, true, true, true); + emit StakeTypeSet(StakeType.TOTAL_SLASHABLE); + stakeRegistry.initializeSlashableStakeQuorum( + quorumNumber, + minimumStake, + 7 days, + strategyParams + ); + + StakeType stakeType = stakeRegistry.stakeTypePerQuorum(quorumNumber); + assertEq(uint8(stakeType), uint8(StakeType.TOTAL_SLASHABLE), "invalid stake type"); } /** @@ -636,7 +687,7 @@ contract StakeRegistryUnitTests_Config is StakeRegistryUnitTests { } quorumNumber = nextQuorum; cheats.prank(address(registryCoordinator)); - stakeRegistry.initializeQuorum(quorumNumber, minimumStake, strategyParams); + stakeRegistry.initializeDelegatedStakeQuorum(quorumNumber, minimumStake, strategyParams); IStakeRegistry.StakeUpdate memory initialStakeUpdate = stakeRegistry.getTotalStakeUpdateAtIndex(quorumNumber, 0); @@ -2151,7 +2202,7 @@ contract StakeRegistryUnitTests_weightOfOperatorForQuorum is StakeRegistryUnitTe } cheats.prank(address(registryCoordinator)); uint8 quorumNumber = nextQuorum; - stakeRegistry.initializeQuorum(quorumNumber, 0, /* minimumStake */ strategyParams); + stakeRegistry.initializeDelegatedStakeQuorum(quorumNumber, 0, /* minimumStake */ strategyParams); // set the operator shares for (uint256 i = 0; i < strategyParams.length; i++) { @@ -2198,7 +2249,7 @@ contract StakeRegistryUnitTests_weightOfOperatorForQuorum is StakeRegistryUnitTe // create a valid quorum cheats.prank(address(registryCoordinator)); uint8 quorumNumber = nextQuorum; - stakeRegistry.initializeQuorum(quorumNumber, 0, /* minimumStake */ strategyParams); + stakeRegistry.initializeDelegatedStakeQuorum(quorumNumber, 0, /* minimumStake */ strategyParams); // set the operator shares for (uint256 i = 0; i < strategyParams.length; i++) { diff --git a/test/utils/MockAVSDeployer.sol b/test/utils/MockAVSDeployer.sol index af8ebe78..19a08295 100644 --- a/test/utils/MockAVSDeployer.sol +++ b/test/utils/MockAVSDeployer.sol @@ -15,7 +15,7 @@ import {RegistryCoordinator} from "../../src/RegistryCoordinator.sol"; import {RegistryCoordinatorHarness} from "../harnesses/RegistryCoordinatorHarness.t.sol"; import {BLSApkRegistry} from "../../src/BLSApkRegistry.sol"; import {ServiceManagerMock} from "../mocks/ServiceManagerMock.sol"; -import {StakeRegistry} from "../../src/StakeRegistry.sol"; +import {StakeRegistry, StakeType} from "../../src/StakeRegistry.sol"; import {IndexRegistry} from "../../src/IndexRegistry.sol"; import {IBLSApkRegistry} from "../../src/interfaces/IBLSApkRegistry.sol"; import {IStakeRegistry} from "../../src/interfaces/IStakeRegistry.sol"; @@ -296,6 +296,16 @@ contract MockAVSDeployer is Test { ); } + // Create arrays for quorum types and lookahead periods + StakeType[] memory quorumStakeTypes = new StakeType[](numQuorumsToAdd); + uint32[] memory slashableStakeQuorumLookAheadPeriods = new uint32[](numQuorumsToAdd); + + // Set all quorums to TOTAL_DELEGATED type with 0 lookahead period + for (uint256 i = 0; i < numQuorumsToAdd; i++) { + quorumStakeTypes[i] = StakeType.TOTAL_DELEGATED; + slashableStakeQuorumLookAheadPeriods[i] = 0; + } + proxyAdmin.upgradeAndCall( TransparentUpgradeableProxy(payable(address(registryCoordinator))), address(registryCoordinatorImplementation), @@ -308,7 +318,9 @@ contract MockAVSDeployer is Test { 0, /*initialPausedStatus*/ operatorSetParams, minimumStakeForQuorum, - quorumStrategiesConsideredAndMultipliers + quorumStrategiesConsideredAndMultipliers, + quorumStakeTypes, + slashableStakeQuorumLookAheadPeriods ) ); } From 158a890aecf0535ff7461045fb874d57a5610557 Mon Sep 17 00:00:00 2001 From: steven <12021290+stevennevins@users.noreply.github.com> Date: Wed, 4 Dec 2024 11:58:54 -0500 Subject: [PATCH 24/27] fix: use libraries with only internal vis (#324) * fix: revert making library function vis external * fix: signature checker internal --- src/libraries/BN254.sol | 22 +++++++++++----------- src/libraries/QuorumBitmapHistoryLib.sol | 8 ++++---- src/libraries/SignatureCheckerLib.sol | 4 ++-- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/libraries/BN254.sol b/src/libraries/BN254.sol index 4d0edf70..37e3d273 100644 --- a/src/libraries/BN254.sol +++ b/src/libraries/BN254.sol @@ -46,7 +46,7 @@ library BN254 { uint256[2] Y; } - function generatorG1() external pure returns (G1Point memory) { + function generatorG1() internal pure returns (G1Point memory) { return G1Point(1, 2); } @@ -62,7 +62,7 @@ library BN254 { /// this is because of the (unknown to us) convention used in the bn254 pairing precompile contract /// "Elements a * i + b of F_p^2 are encoded as two elements of F_p, (a, b)." /// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-197.md#encoding - function generatorG2() external pure returns (G2Point memory) { + function generatorG2() internal pure returns (G2Point memory) { return G2Point([G2x1, G2x0], [G2y1, G2y0]); } @@ -73,7 +73,7 @@ library BN254 { uint256 internal constant nG2y1 = 17805874995975841540914202342111839520379459829704422454583296818431106115052; uint256 internal constant nG2y0 = 13392588948715843804641432497768002650278120570034223513918757245338268106653; - function negGeneratorG2() external pure returns (G2Point memory) { + function negGeneratorG2() internal pure returns (G2Point memory) { return G2Point([nG2x1, nG2x0], [nG2y1, nG2y0]); } @@ -84,7 +84,7 @@ library BN254 { * @param p Some point in G1. * @return The negation of `p`, i.e. p.plus(p.negate()) should be zero. */ - function negate(G1Point memory p) external pure returns (G1Point memory) { + function negate(G1Point memory p) internal pure returns (G1Point memory) { // The prime q in the base field F_q for G1 if (p.X == 0 && p.Y == 0) { return G1Point(0, 0); @@ -122,7 +122,7 @@ library BN254 { * @param p the point to multiply * @param s the scalar to multiply by * @dev this function is only safe to use if the scalar is 9 bits or less - */ + */ function scalar_mul_tiny(BN254.G1Point memory p, uint16 s) internal view returns (BN254.G1Point memory) { require(s < 2**9, "scalar-too-large"); @@ -155,7 +155,7 @@ library BN254 { ++i; } } - + // return the accumulated product return acc; } @@ -194,7 +194,7 @@ library BN254 { G2Point memory a2, G1Point memory b1, G2Point memory b2 - ) external view returns (bool) { + ) internal view returns (bool) { G1Point[2] memory p1 = [a1, b1]; G2Point[2] memory p2 = [a2, b2]; @@ -238,7 +238,7 @@ library BN254 { G1Point memory b1, G2Point memory b2, uint256 pairingGas - ) external view returns (bool, bool) { + ) internal view returns (bool, bool) { G1Point[2] memory p1 = [a1, b1]; G2Point[2] memory p2 = [a2, b2]; @@ -270,7 +270,7 @@ library BN254 { /// @return hashedG1 the keccak256 hash of the G1 Point /// @dev used for BLS signatures - function hashG1Point(BN254.G1Point memory pk) external pure returns (bytes32 hashedG1) { + function hashG1Point(BN254.G1Point memory pk) internal pure returns (bytes32 hashedG1) { assembly { mstore(0, mload(pk)) mstore(0x20, mload(add(0x20, pk))) @@ -282,14 +282,14 @@ library BN254 { /// @dev used for BLS signatures function hashG2Point( BN254.G2Point memory pk - ) external pure returns (bytes32) { + ) internal pure returns (bytes32) { return keccak256(abi.encodePacked(pk.X[0], pk.X[1], pk.Y[0], pk.Y[1])); } /** * @notice adapted from https://github.com/HarryR/solcrypto/blob/master/contracts/altbn128.sol */ - function hashToG1(bytes32 _x) external view returns (G1Point memory) { + function hashToG1(bytes32 _x) internal view returns (G1Point memory) { uint256 beta = 0; uint256 y = 0; diff --git a/src/libraries/QuorumBitmapHistoryLib.sol b/src/libraries/QuorumBitmapHistoryLib.sol index 4a3e72fd..c971d64d 100644 --- a/src/libraries/QuorumBitmapHistoryLib.sol +++ b/src/libraries/QuorumBitmapHistoryLib.sol @@ -41,7 +41,7 @@ library QuorumBitmapHistoryLib { function currentOperatorBitmap( mapping(bytes32 => IRegistryCoordinator.QuorumBitmapUpdate[]) storage self, bytes32 operatorId - ) external view returns (uint192) { + ) internal view returns (uint192) { uint256 historyLength = self[operatorId].length; if (historyLength == 0) { return 0; @@ -59,7 +59,7 @@ library QuorumBitmapHistoryLib { mapping(bytes32 => IRegistryCoordinator.QuorumBitmapUpdate[]) storage self, uint32 blockNumber, bytes32[] memory operatorIds - ) external view returns (uint32[] memory) { + ) internal view returns (uint32[] memory) { uint32[] memory indices = new uint32[](operatorIds.length); for (uint256 i = 0; i < operatorIds.length; i++) { indices[i] = getQuorumBitmapIndexAtBlockNumber(self, blockNumber, operatorIds[i]); @@ -78,7 +78,7 @@ library QuorumBitmapHistoryLib { bytes32 operatorId, uint32 blockNumber, uint256 index - ) external view returns (uint192) { + ) internal view returns (uint192) { IRegistryCoordinator.QuorumBitmapUpdate memory quorumBitmapUpdate = self[operatorId][index]; /** @@ -107,7 +107,7 @@ library QuorumBitmapHistoryLib { mapping(bytes32 => IRegistryCoordinator.QuorumBitmapUpdate[]) storage self, bytes32 operatorId, uint192 newBitmap - ) external { + ) internal { uint256 historyLength = self[operatorId].length; if (historyLength == 0) { diff --git a/src/libraries/SignatureCheckerLib.sol b/src/libraries/SignatureCheckerLib.sol index c01fd3a7..410462cc 100644 --- a/src/libraries/SignatureCheckerLib.sol +++ b/src/libraries/SignatureCheckerLib.sol @@ -11,7 +11,7 @@ import "@openzeppelin-upgrades/contracts/utils/cryptography/SignatureCheckerUpgr */ library SignatureCheckerLib { error InvalidSignature(); - + /** * @notice Validates a signature using EIP-1271 standard. * @param signer The address of the signer. @@ -22,7 +22,7 @@ library SignatureCheckerLib { address signer, bytes32 digestHash, bytes memory signature - ) external view { + ) internal view { if (!SignatureCheckerUpgradeable.isValidSignatureNow(signer, digestHash, signature)) { revert InvalidSignature(); } From 817ad4246672c6cd152ce4880e73f0cb2872882a Mon Sep 17 00:00:00 2001 From: steven <12021290+stevennevins@users.noreply.github.com> Date: Wed, 4 Dec 2024 19:57:30 -0500 Subject: [PATCH 25/27] feat: avs registrar registration flow changes (#318) * feat: remove both option * feat: total delegated stake and total slashable stake per quorum config * test: resolve some breaking changes to tests * chore: bump core dependency * chore: bump dependency * chore: bump to latest slashing mags * fix: creation of registry coordinator * test: wip * feat: integrate registrar interfaces * test: add function to delegation mock to set operator status * test: additional test case * chore: bumping core dep and adding UAM (#325) * test: use permission controller mock * chore: label fuzz tests * test: various fixes from config changes * chore: remove comment * test: fix permission controlled functions * test: fix config issue in integration tests * test: fix avs directory initialize * feat: wip prevent m2 registration flows after migration --- lib/eigenlayer-contracts | 2 +- script/OperatorSetUpgrade.s.sol | 258 ---------- src/AVSRegistrar.sol | 21 + src/RegistryCoordinator.sol | 207 ++++++-- src/ServiceManagerBase.sol | 21 +- src/StakeRegistry.sol | 43 +- src/interfaces/ISlasher.sol | 1 - src/interfaces/IStakeRegistry.sol | 1 - src/slashers/base/SlasherBase.sol | 2 +- src/unaudited/ECDSAServiceManagerBase.sol | 4 +- test/harnesses/AVSDirectoryHarness.sol | 62 +-- .../RegistryCoordinatorHarness.t.sol | 9 +- test/integration/CoreRegistration.t.sol | 15 +- test/integration/IntegrationDeployer.t.sol | 105 ++-- test/integration/User.t.sol | 31 +- test/mocks/AVSDirectoryMock.sol | 133 ++--- test/mocks/AVSRegistrarMock.sol | 17 + test/mocks/AllocationManagerMock.sol | 165 ++++-- test/mocks/DelegationMock.sol | 465 ++++++++--------- test/mocks/EigenPodManagerMock.sol | 27 +- test/mocks/PermissionControllerMock.sol | 91 ++++ test/mocks/RewardsCoordinatorMock.sol | 187 ++++--- test/unit/AVSRegistrar.t.sol | 100 ++++ test/unit/RegistryCoordinatorMigration.t.sol | 470 ------------------ test/unit/RegistryCoordinatorUnit.t.sol | 1 - test/unit/ServiceManagerBase.t.sol | 13 +- test/unit/ServiceManagerMigration.t.sol | 348 ------------- test/unit/StakeRegistryUnit.t.sol | 7 +- test/unit/UpgradeableProxyLib.sol | 46 ++ test/unit/Utils.sol | 4 +- test/utils/BLSMockAVSDeployer.sol | 4 +- test/utils/CoreDeployLib.sol | 271 ++++++++++ test/utils/MockAVSDeployer.sol | 100 ++-- 33 files changed, 1454 insertions(+), 1777 deletions(-) delete mode 100644 script/OperatorSetUpgrade.s.sol create mode 100644 src/AVSRegistrar.sol create mode 100644 test/mocks/AVSRegistrarMock.sol create mode 100644 test/mocks/PermissionControllerMock.sol create mode 100644 test/unit/AVSRegistrar.t.sol delete mode 100644 test/unit/RegistryCoordinatorMigration.t.sol delete mode 100644 test/unit/ServiceManagerMigration.t.sol create mode 100644 test/unit/UpgradeableProxyLib.sol create mode 100644 test/utils/CoreDeployLib.sol diff --git a/lib/eigenlayer-contracts b/lib/eigenlayer-contracts index d98c5a7d..f8c12749 160000 --- a/lib/eigenlayer-contracts +++ b/lib/eigenlayer-contracts @@ -1 +1 @@ -Subproject commit d98c5a7df7634e25073b9a508be1a6606d7caf0c +Subproject commit f8c127498a3e5f019732a3e35387a2066935cc6b diff --git a/script/OperatorSetUpgrade.s.sol b/script/OperatorSetUpgrade.s.sol deleted file mode 100644 index 32cd80fe..00000000 --- a/script/OperatorSetUpgrade.s.sol +++ /dev/null @@ -1,258 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.0; - -import {Script, console2} from "forge-std/Script.sol"; -import {OperatorSetUpgradeLib} from "./utils/UpgradeLib.sol"; -import {stdJson} from "forge-std/StdJson.sol"; -import {ServiceManagerMock, IServiceManager} from "../test/mocks/ServiceManagerMock.sol"; -import {StakeRegistry, IStakeRegistry} from "../src/StakeRegistry.sol"; -import {RegistryCoordinator, IRegistryCoordinator} from "../src/RegistryCoordinator.sol"; -import {AVSDirectory} from "eigenlayer-contracts/src/contracts/core/AVSDirectory.sol"; -import {IBLSApkRegistry} from "../src/interfaces/IBLSApkRegistry.sol"; -import {IIndexRegistry} from "../src/interfaces/IIndexRegistry.sol"; -import {IRewardsCoordinator} from "eigenlayer-contracts/src/contracts/interfaces/IRewardsCoordinator.sol"; -import {IAVSDirectory} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; -import {IAllocationManager} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; -import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; -interface IServiceManagerMigration { - function getOperatorsToMigrate() - external - view - returns ( - uint32[] memory operatorSetIdsToCreate, - uint32[][] memory operatorSetIds, - address[] memory allOperators - ); - function migrateAndCreateOperatorSetIds(uint32[] memory operatorSetsToCreate) external; - function migrateToOperatorSets(uint32[][] memory operatorSetIds, address[] memory operators) external; - function finalizeMigration() external; - function migrationFinalized() external returns (bool); -} - - -import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; - -contract OperatorSetUpgradeScript is Script { - using stdJson for string; - - address private constant DEFAULT_FORGE_SENDER = 0x1804c8AB1F12E6bbf3894d4083f33e07309d1f38; - - address public proxyAdminOwner; - address public serviceManagerOwner; - address public serviceManager; - address public stakeRegistry; - address public registryCoordinator; - address public avsDirectory; - address public rewardsCoordinator; - address public delegationManager; - address public blsApkRegistry; - address public indexRegistry; - address public allocationManager; - - function setUp() public { - vm.label(DEFAULT_FORGE_SENDER, "DEFAULT FORGE SENDER"); - - // Note: Ensure that the following environment variables are set before running the script: - // - PROXY_ADMIN_OWNER: The private key of the proxy admin owner. - // - SERVICE_MANAGER_OWNER: The private key of the service manager owner. - // These environment variables are crucial for the proper execution of the upgrade and migration processes. - /// TODO: improve DEVX of gnosis safe. Would like to do an tx service integration for SafeAPI - proxyAdminOwner = vm.rememberKey(vm.envUint("PROXY_ADMIN_OWNER")); - serviceManagerOwner = vm.rememberKey(vm.envUint("PROXY_ADMIN_OWNER")); - - string memory middlewareJson = vm.readFile(vm.envString("MIDDLEWARE_JSON_PATH")); - string memory coreJson = vm.readFile(vm.envString("CORE_JSON_PATH")); - - /* - * Note: Ensure that the structure of the configuration JSON files matches the structure - * of `core_testdata.json`. If you rename any of the files, you will need to update the - * corresponding key values in the code. - */ - loadAddressesSetup(middlewareJson, coreJson); - labelAndLogAddressesSetup(); - } - - function run() public { - vm.startBroadcast(proxyAdminOwner); - - _upgrade(); - - vm.stopBroadcast(); - - vm.startBroadcast(serviceManagerOwner); - - _migrateToOperatorSets(); - - vm.stopBroadcast(); - } - // forge script script/OperatorSetUpgrade.s.sol --sig "simulateUpgrade()" -vvv - function simulateUpgrade() public { - - address proxyAdmin = OperatorSetUpgradeLib.getAdmin(serviceManager); - proxyAdminOwner = Ownable(proxyAdmin).owner(); - vm.startPrank(proxyAdminOwner); - - _upgrade(); - - vm.stopPrank(); - - } - - // forge script script/OperatorSetUpgrade.s.sol --sig "simulateMigrate()" -vvv - function simulateMigrate() public { - _upgradeAvsDirectory(); /// Workaround since this isn't on pre-prod yet - - serviceManagerOwner = Ownable(serviceManager).owner(); - vm.startPrank(serviceManagerOwner); - - _migrateToOperatorSets(); - - vm.stopPrank(); - } - - // forge script script/OperatorSetUpgrade.s.sol --sig "simulateUpgradeAndMigrate()" -vvv - function simulateUpgradeAndMigrate() public { - _upgradeAvsDirectory(); /// Workaround since this isn't on pre-prod yet - - address proxyAdmin = OperatorSetUpgradeLib.getAdmin(serviceManager); - proxyAdminOwner = Ownable(proxyAdmin).owner(); - - console2.log(proxyAdminOwner, "Pranker"); - vm.startPrank(proxyAdminOwner); - - _upgrade(); - - vm.stopPrank(); - - serviceManagerOwner = Ownable(serviceManager).owner(); - vm.startPrank(serviceManagerOwner); - - _migrateToOperatorSets(); - - vm.stopPrank(); - - // Assert that serviceManager is an operatorSetAVS - require( - IAVSDirectory(avsDirectory).isOperatorSetAVS(serviceManager), - "simulateUpgradeAndMigrate: serviceManager is not an operatorSetAVS" - ); - - // Assert that the migration is finalized - require( - IServiceManagerMigration(serviceManager).migrationFinalized(), - "simulateUpgradeAndMigrate: Migration is not finalized" - ); - } - - function _upgradeAvsDirectory() internal { - address proxyAdmin = OperatorSetUpgradeLib.getAdmin(avsDirectory); - address avsDirectoryOwner = Ownable(proxyAdmin).owner(); - AVSDirectory avsDirectoryImpl = new AVSDirectory(IDelegationManager(delegationManager), 0); // TODO: config - - vm.startPrank(avsDirectoryOwner); - OperatorSetUpgradeLib.upgrade(avsDirectory, address(avsDirectoryImpl)); - vm.stopPrank(); - } - - function labelAndLogAddressesSetup() internal virtual { - vm.label(proxyAdminOwner, "Proxy Admin Owner Account"); - vm.label(serviceManagerOwner, "Service Manager Owner Account"); - vm.label(serviceManager, "Service Manager Proxy"); - vm.label(stakeRegistry, "Stake Registry Proxy"); - vm.label(registryCoordinator, "Registry Coordinator Proxy"); - vm.label(indexRegistry, "Index Registry Proxy"); - vm.label(blsApkRegistry, "BLS APK Registry Proxy"); - vm.label(avsDirectory, "AVS Directory Proxy"); - vm.label(delegationManager, "Delegation Manager Proxy"); - vm.label(rewardsCoordinator, "Rewards Coordinator Proxy"); - - console2.log("Proxy Admin Owner Account", proxyAdminOwner); - console2.log("ServiceManager Owner Account", serviceManagerOwner); - console2.log("Service Manager:", serviceManager); - console2.log("Stake Registry:", stakeRegistry); - console2.log("Registry Coordinator:", registryCoordinator); - console2.log("Index Registry:", indexRegistry); - console2.log("BLS APK Registry:", blsApkRegistry); - console2.log("AVS Directory:", avsDirectory); - console2.log("Delegation Manager:", delegationManager); - console2.log("Rewards Coordinator:", rewardsCoordinator); - - address oldServiceManagerImpl = OperatorSetUpgradeLib.getImplementation(serviceManager); - address oldStakeRegistryImpl = OperatorSetUpgradeLib.getImplementation(stakeRegistry); - address oldRegistryCoordinatorImpl = OperatorSetUpgradeLib.getImplementation(registryCoordinator); - address oldAvsDirectoryImpl = OperatorSetUpgradeLib.getImplementation(avsDirectory); - address oldDelegationManagerImpl = OperatorSetUpgradeLib.getImplementation(delegationManager); - - vm.label(oldServiceManagerImpl, "Old Service Manager Implementation"); - vm.label(oldStakeRegistryImpl, "Old Stake Registry Implementation"); - vm.label(oldRegistryCoordinatorImpl, "Old Registry Coordinator Implementation"); - vm.label(oldAvsDirectoryImpl, "Old AVS Directory Implementation"); - vm.label(oldDelegationManagerImpl, "Old Delegation Manager Implementation"); - - console2.log("Old Service Manager Implementation:", oldServiceManagerImpl); - console2.log("Old Stake Registry Implementation:", oldStakeRegistryImpl); - console2.log("Old Registry Coordinator Implementation:", oldRegistryCoordinatorImpl); - console2.log("Old AVS Directory Implementation:", oldAvsDirectoryImpl); - console2.log("Old Delegation Manager Implementation:", oldDelegationManagerImpl); - } - - function loadAddressesSetup(string memory middlewareJson, string memory coreJson) internal virtual { - serviceManager = middlewareJson.readAddress(".addresses.eigenDAServiceManager"); - stakeRegistry = middlewareJson.readAddress(".addresses.stakeRegistry"); - registryCoordinator = middlewareJson.readAddress(".addresses.registryCoordinator"); - blsApkRegistry = middlewareJson.readAddress(".addresses.blsApkRegistry"); - indexRegistry = middlewareJson.readAddress(".addresses.indexRegistry"); - - avsDirectory = coreJson.readAddress(".addresses.avsDirectory"); - delegationManager = coreJson.readAddress(".addresses.delegationManager"); - rewardsCoordinator = coreJson.readAddress(".addresses.rewardsCoordinator"); - } - - function _upgrade() internal virtual { - address newServiceManagerImpl = address(new ServiceManagerMock( - IAVSDirectory(avsDirectory), - IRewardsCoordinator(rewardsCoordinator), - IRegistryCoordinator(registryCoordinator), - IStakeRegistry(stakeRegistry), - IAllocationManager(allocationManager) - )); - address newRegistryCoordinatorImpl = address(new RegistryCoordinator( - IServiceManager(serviceManager), - IStakeRegistry(stakeRegistry), - IBLSApkRegistry(blsApkRegistry), - IIndexRegistry(indexRegistry), - IAVSDirectory(avsDirectory) - )); - address newStakeRegistryImpl = address(new StakeRegistry( - IRegistryCoordinator(registryCoordinator), - IDelegationManager(delegationManager), - IAVSDirectory(avsDirectory), - IServiceManager(serviceManager) - )); - - console2.log("New Service Manager Implementation:", newServiceManagerImpl); - console2.log("New Registry Coordinator Implementation:", newRegistryCoordinatorImpl); - console2.log("New Stake Registry Implementation:", newStakeRegistryImpl); - - vm.label(newServiceManagerImpl, "New Service Manager Implementation"); - vm.label(newRegistryCoordinatorImpl, "New Registry Coordinator Implementation"); - vm.label(newStakeRegistryImpl, "New Stake Registry Implementation"); - - OperatorSetUpgradeLib.upgrade(serviceManager, newServiceManagerImpl); - OperatorSetUpgradeLib.upgrade(registryCoordinator, newRegistryCoordinatorImpl); - OperatorSetUpgradeLib.upgrade(stakeRegistry, newStakeRegistryImpl); - } - - function _migrateToOperatorSets() internal virtual { - IServiceManagerMigration serviceManager = IServiceManagerMigration(serviceManager); - ( - uint32[] memory operatorSetsToCreate, - uint32[][] memory operatorSetIdsToMigrate, - address[] memory operators - ) = serviceManager.getOperatorsToMigrate(); - - serviceManager.migrateAndCreateOperatorSetIds(operatorSetsToCreate); - serviceManager.migrateToOperatorSets(operatorSetIdsToMigrate, operators); - serviceManager.finalizeMigration(); - } -} \ No newline at end of file diff --git a/src/AVSRegistrar.sol b/src/AVSRegistrar.sol new file mode 100644 index 00000000..9a81e129 --- /dev/null +++ b/src/AVSRegistrar.sol @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +import {IAVSRegistrar} from "eigenlayer-contracts/src/contracts/interfaces/IAVSRegistrar.sol"; +import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol"; +import {IBLSApkRegistry} from "./interfaces/IBLSApkRegistry.sol"; +import {IStakeRegistry} from "./interfaces/IStakeRegistry.sol"; +import {IIndexRegistry} from "./interfaces/IIndexRegistry.sol"; + +abstract contract AVSRegistrar is IAVSRegistrar { + function registerOperator( + address operator, + uint32[] calldata operatorSetIds, + bytes calldata data + ) external virtual; + + function deregisterOperator( + address operator, + uint32[] calldata operatorSetIds + ) external virtual; +} diff --git a/src/RegistryCoordinator.sol b/src/RegistryCoordinator.sol index 101cd935..354ae067 100644 --- a/src/RegistryCoordinator.sol +++ b/src/RegistryCoordinator.sol @@ -3,7 +3,8 @@ pragma solidity ^0.8.12; import {IPauserRegistry} from "eigenlayer-contracts/src/contracts/interfaces/IPauserRegistry.sol"; import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; -import {IAVSDirectory, OperatorSet} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; +import {IAVSDirectory } from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; +import { OperatorSet} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; import {ISocketUpdater} from "./interfaces/ISocketUpdater.sol"; import {IBLSApkRegistry} from "./interfaces/IBLSApkRegistry.sol"; import {IStakeRegistry, StakeType} from "./interfaces/IStakeRegistry.sol"; @@ -15,6 +16,7 @@ import {BitmapUtils} from "./libraries/BitmapUtils.sol"; import {BN254} from "./libraries/BN254.sol"; import {SignatureCheckerLib} from "./libraries/SignatureCheckerLib.sol"; import {QuorumBitmapHistoryLib} from "./libraries/QuorumBitmapHistoryLib.sol"; +import {AVSRegistrar} from "./AVSRegistrar.sol"; import {OwnableUpgradeable} from "@openzeppelin-upgrades/contracts/access/OwnableUpgradeable.sol"; import {Initializable} from "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol"; @@ -37,12 +39,15 @@ contract RegistryCoordinator is Pausable, OwnableUpgradeable, RegistryCoordinatorStorage, + AVSRegistrar, ISocketUpdater, ISignatureUtils { using BitmapUtils for *; using BN254 for BN254.G1Point; + bool isOperatorSetAVS; + modifier onlyEjector() { _checkEjector(); _; @@ -60,10 +65,12 @@ contract RegistryCoordinator is IStakeRegistry _stakeRegistry, IBLSApkRegistry _blsApkRegistry, IIndexRegistry _indexRegistry, - IAVSDirectory _avsDirectory + IAVSDirectory _avsDirectory, + IPauserRegistry _pauserRegistry ) RegistryCoordinatorStorage(_serviceManager, _stakeRegistry, _blsApkRegistry, _indexRegistry, _avsDirectory) EIP712("AVSRegistryCoordinator", "v0.0.1") + Pausable(_pauserRegistry) { _disableInitializers(); } @@ -72,7 +79,6 @@ contract RegistryCoordinator is * @param _initialOwner will hold the owner role * @param _churnApprover will hold the churnApprover role, which authorizes registering with churn * @param _ejector will hold the ejector role, which can force-eject operators from quorums - * @param _pauserRegistry a registry of addresses that can pause the contract * @param _initialPausedStatus pause status after calling initialize * Config for initial quorums (see `createQuorum`): * @param _operatorSetParams max operator count and operator churn parameters @@ -83,7 +89,6 @@ contract RegistryCoordinator is address _initialOwner, address _churnApprover, address _ejector, - IPauserRegistry _pauserRegistry, uint256 _initialPausedStatus, OperatorSetParam[] memory _operatorSetParams, uint96[] memory _minimumStakes, @@ -101,8 +106,8 @@ contract RegistryCoordinator is // Initialize roles _transferOwnership(_initialOwner); - _initializePauser(_pauserRegistry, _initialPausedStatus); _setChurnApprover(_churnApprover); + _setPausedStatus(_initialPausedStatus); _setEjector(_ejector); // Add registry contracts to the registries array @@ -133,11 +138,12 @@ contract RegistryCoordinator is * @dev `operatorSignature` is ignored if the operator's status is already REGISTERED */ function registerOperator( - bytes calldata quorumNumbers, - string calldata socket, - IBLSApkRegistry.PubkeyRegistrationParams calldata params, + bytes memory quorumNumbers, + string memory socket, + IBLSApkRegistry.PubkeyRegistrationParams memory params, SignatureWithSaltAndExpiry memory operatorSignature ) external onlyWhenNotPaused(PAUSED_REGISTER_OPERATOR) { + if (isUsingOperatorSets()) revert(); /** * If the operator has NEVER registered a pubkey before, use `params` to register * their pubkey in blsApkRegistry @@ -183,12 +189,13 @@ contract RegistryCoordinator is */ function registerOperatorWithChurn( bytes calldata quorumNumbers, - string calldata socket, - IBLSApkRegistry.PubkeyRegistrationParams calldata params, - OperatorKickParam[] calldata operatorKickParams, + string memory socket, + IBLSApkRegistry.PubkeyRegistrationParams memory params, + OperatorKickParam[] memory operatorKickParams, SignatureWithSaltAndExpiry memory churnApproverSignature, SignatureWithSaltAndExpiry memory operatorSignature ) external onlyWhenNotPaused(PAUSED_REGISTER_OPERATOR) { + if (isUsingOperatorSets()) revert(); require( operatorKickParams.length == quorumNumbers.length, "RegistryCoordinator.registerOperatorWithChurn: input length mismatch" @@ -249,20 +256,84 @@ contract RegistryCoordinator is * @notice Deregisters the caller from one or more quorums * @param quorumNumbers is an ordered byte array containing the quorum numbers being deregistered from */ - function deregisterOperator(bytes calldata quorumNumbers) + function deregisterOperator(bytes memory quorumNumbers) external onlyWhenNotPaused(PAUSED_DEREGISTER_OPERATOR) { _deregisterOperator({operator: msg.sender, quorumNumbers: quorumNumbers}); } + function isUsingOperatorSets() public view returns (bool){ + return isOperatorSetAVS; + } + + function enableOperatorSets() external onlyOwner { + /// TODO: + /// Triggers the updates to use operator sets + /// Opens update the AVS Registrar Hooks on this contract + /// Allows creation of quorums with slashable and total delegated stake for operator sets + isOperatorSetAVS = true; + } + + function registerOperator( + address operator, + uint32[] memory operatorSetIds, + bytes memory data + ) external override { + if (!isUsingOperatorSets()) revert(); + /// TODO: Make a mapping for quorums associated with operator sets / ones associated with m2 registrations + /// TODO: only allow registration of operator sets that have been created in the core and don't conflict with existing quorum numbers + require(msg.sender == address(serviceManager.allocationManager()), "Only allocation manager can register operators"); + + // Decode registration data from bytes + ( + string memory socket, + IBLSApkRegistry.PubkeyRegistrationParams memory params + ) = abi.decode(data, (string, IBLSApkRegistry.PubkeyRegistrationParams)); + + // Get operator ID from BLS registry + bytes32 operatorId = _getOrCreateOperatorId(operator, params); + bytes memory quorumNumbers = new bytes(operatorSetIds.length); + for (uint256 i = 0; i < operatorSetIds.length; i++) { + quorumNumbers[i] = bytes1(uint8(operatorSetIds[i])); + } + + // Register operator with decoded parameters + _registerOperatorNew({ + operator: operator, + operatorId: operatorId, + quorumNumbers: quorumNumbers, + socket: socket + }); + + /// TODO: Correctly handle decoding the registration with churn and the normal registration flow parameters + + } + + function deregisterOperator( + address operator, + uint32[] memory operatorSetIds + ) external override { + if (!isUsingOperatorSets()) revert(); + require(msg.sender == address(serviceManager.allocationManager()), "Only allocation manager can register operators"); + /// TODO: Make a mapping for quorums associated with operator sets / ones associated with m2 registrations + /// TODO: Call _registerOperator to propogate changes to the other contracts + /// TODO: only allow deregistration of operator sets that have been created in the core and don't conflict with existing quorum numbers + bytes memory quorumNumbers = new bytes(operatorSetIds.length); + for (uint256 i = 0; i < operatorSetIds.length; i++) { + quorumNumbers[i] = bytes1(uint8(operatorSetIds[i])); + } + + _deregisterOperator(operator, quorumNumbers); + } + /** * @notice Updates the StakeRegistry's view of one or more operators' stakes. If any operator * is found to be below the minimum stake for the quorum, they are deregistered. * @dev stakes are queried from the Eigenlayer core DelegationManager contract * @param operators a list of operator addresses to update */ - function updateOperators(address[] calldata operators) + function updateOperators(address[] memory operators) external onlyWhenNotPaused(PAUSED_UPDATE_OPERATOR) { @@ -293,7 +364,7 @@ contract RegistryCoordinator is * this method is broadcast (but before it is executed), the method will fail */ function updateOperatorsForQuorum( - address[][] calldata operatorsPerQuorum, + address[][] memory operatorsPerQuorum, bytes calldata quorumNumbers ) external onlyWhenNotPaused(PAUSED_UPDATE_OPERATOR) { // Input validation @@ -312,7 +383,7 @@ contract RegistryCoordinator is uint8 quorumNumber = uint8(quorumNumbers[i]); // Ensure we've passed in the correct number of operators for this quorum - address[] calldata currQuorumOperators = operatorsPerQuorum[i]; + address[] memory currQuorumOperators = operatorsPerQuorum[i]; require( currQuorumOperators.length == indexRegistry.totalOperatorsForQuorum(quorumNumber), "RegistryCoordinator.updateOperatorsForQuorum: number of updated operators does not match quorum total" @@ -378,7 +449,7 @@ contract RegistryCoordinator is * @param quorumNumbers the quorum numbers to eject the operator from * @dev possible race condition if prior to being ejected for a set of quorums the operator self deregisters from a subset */ - function ejectOperator(address operator, bytes calldata quorumNumbers) external onlyEjector { + function ejectOperator(address operator, bytes memory quorumNumbers) external onlyEjector { lastEjectionTimestamp[operator] = block.timestamp; OperatorInfo storage operatorInfo = _operatorInfo[operator]; @@ -413,6 +484,7 @@ contract RegistryCoordinator is uint96 minimumStake, IStakeRegistry.StrategyParams[] memory strategyParams ) external virtual onlyOwner { + if (!isUsingOperatorSets()) revert (); _createQuorum(operatorSetParams, minimumStake, strategyParams, StakeType.TOTAL_DELEGATED, 0); } @@ -422,6 +494,7 @@ contract RegistryCoordinator is IStakeRegistry.StrategyParams[] memory strategyParams, uint32 lookAheadPeriod ) external virtual onlyOwner { + if (!isUsingOperatorSets()) revert (); _createQuorum(operatorSetParams, minimumStake, strategyParams, StakeType.TOTAL_SLASHABLE, lookAheadPeriod); } @@ -486,7 +559,7 @@ contract RegistryCoordinator is function _registerOperator( address operator, bytes32 operatorId, - bytes calldata quorumNumbers, + bytes memory quorumNumbers, string memory socket, SignatureWithSaltAndExpiry memory operatorSignature ) internal virtual returns (RegisterResults memory results) { @@ -529,21 +602,67 @@ contract RegistryCoordinator is _operatorInfo[operator] = OperatorInfo({operatorId: operatorId, status: OperatorStatus.REGISTERED}); - // Register the operator with the EigenLayer core contracts via this AVS's ServiceManager - bool operatorSetAVS = avsDirectory.isOperatorSetAVS(address(serviceManager)); - if (operatorSetAVS){ - bytes memory quorumBytes = BitmapUtils.bitmapToBytesArray(quorumsToAdd); - uint32[] memory operatorSetIds = new uint32[](quorumBytes.length); - for (uint256 i = 0; i < quorumBytes.length; i++) { - operatorSetIds[i] = uint8(quorumBytes[i]); - } - serviceManager.registerOperatorToOperatorSets(operator, operatorSetIds, operatorSignature); + serviceManager.registerOperatorToAVS(operator, operatorSignature); + emit OperatorRegistered(operator, operatorId); - } else { - serviceManager.registerOperatorToAVS(operator, operatorSignature); - emit OperatorRegistered(operator, operatorId); - } + } + + // Register the operator with the BLSApkRegistry, StakeRegistry, and IndexRegistry + blsApkRegistry.registerOperator(operator, quorumNumbers); + (results.operatorStakes, results.totalStakes) = + stakeRegistry.registerOperator(operator, operatorId, quorumNumbers); + results.numOperatorsPerQuorum = indexRegistry.registerOperator(operatorId, quorumNumbers); + + return results; + } + + /** + * @notice Register the operator for one or more quorums. This method updates the + * operator's quorum bitmap, socket, and status, then registers them with each registry. + */ + function _registerOperatorNew( + address operator, + bytes32 operatorId, + bytes memory quorumNumbers, + string memory socket + ) internal virtual returns (RegisterResults memory results) { + /** + * Get bitmap of quorums to register for and operator's current bitmap. Validate that: + * - we're trying to register for at least 1 quorum + * - the quorums we're registering for exist (checked against `quorumCount` in orderedBytesArrayToBitmap) + * - the operator is not currently registered for any quorums we're registering for + * Then, calculate the operator's new bitmap after registration + */ + uint192 quorumsToAdd = + uint192(BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers, quorumCount)); + uint192 currentBitmap = _currentOperatorBitmap(operatorId); + require( + !quorumsToAdd.isEmpty(), "RegistryCoordinator._registerOperator: bitmap empty" + ); + require( + quorumsToAdd.noBitsInCommon(currentBitmap), + "RegistryCoordinator._registerOperator: operator already registered for some quorums being registered for" + ); + uint192 newBitmap = uint192(currentBitmap.plus(quorumsToAdd)); + // Check that the operator can reregister if ejected + require( + lastEjectionTimestamp[operator] + ejectionCooldown < block.timestamp, + "RegistryCoordinator._registerOperator: operator cannot reregister yet" + ); + + /** + * Update operator's bitmap, socket, and status. Only update operatorInfo if needed: + * if we're `REGISTERED`, the operatorId and status are already correct. + */ + _updateOperatorBitmap({operatorId: operatorId, newBitmap: newBitmap}); + + emit OperatorSocketUpdate(operatorId, socket); + + // If the operator wasn't registered for any quorums, update their status + // and register them with this AVS in EigenLayer core (DelegationManager) + if (_operatorInfo[operator].status != OperatorStatus.REGISTERED) { + _operatorInfo[operator] = OperatorInfo(operatorId, OperatorStatus.REGISTERED); } // Register the operator with the BLSApkRegistry, StakeRegistry, and IndexRegistry @@ -584,7 +703,7 @@ contract RegistryCoordinator is */ function _getOrCreateOperatorId( address operator, - IBLSApkRegistry.PubkeyRegistrationParams calldata params + IBLSApkRegistry.PubkeyRegistrationParams memory params ) internal returns (bytes32 operatorId) { operatorId = blsApkRegistry.getOperatorId(operator); if (operatorId == 0) { @@ -681,17 +800,22 @@ contract RegistryCoordinator is _updateOperatorBitmap({operatorId: operatorId, newBitmap: newBitmap}); - bool operatorSetAVS = IAVSDirectory(serviceManager.avsDirectory()).isOperatorSetAVS(address(serviceManager)); + /// TODO: Need to know if an AVS is an operator set avs + bool operatorSetAVS; + // = IAVSDirectory(serviceManager.avsDirectory()).isOperatorSetAVS(address(serviceManager)); if (operatorSetAVS){ bytes memory quorumBytes = BitmapUtils.bitmapToBytesArray(quorumsToRemove); uint32[] memory operatorSetIds = new uint32[](quorumBytes.length); uint256 forceDeregistrationCount; for (uint256 i = 0; i < quorumBytes.length; i++) { - /// We need to track forceDeregistrations so we don't pass an id that was already deregistered on the AVSDirectory + /// Post operator sets feature we need to track forceDeregistrations so we don't pass an id that was already deregistered on the AVSDirectory /// but hasnt yet been recorded in the middleware contracts - if (!avsDirectory.isMember(operator, OperatorSet(address(serviceManager), uint8(quorumBytes[i])))){ - forceDeregistrationCount++; - } + + // TODO: Fix need a way to check member ship in the allocation manager without iterating through every member + + // if (!avsDirectory.isMember(operator, OperatorSet(address(serviceManager), uint8(quorumBytes[i])))){ + // forceDeregistrationCount++; + // } operatorSetIds[i] = uint8(quorumBytes[i]); } @@ -700,7 +824,9 @@ contract RegistryCoordinator is uint32[] memory filteredOperatorSetIds = new uint32[](operatorSetIds.length - forceDeregistrationCount); uint256 offset; for (uint256 i; i < operatorSetIds.length; i++){ - if (avsDirectory.isMember(operator, OperatorSet(address(serviceManager), operatorSetIds[i]))){ + if (true){ + /// TODO: Fix need to check + // avsDirectory.isMember(operator, OperatorSet(address(serviceManager), operatorSetIds[i]))){ filteredOperatorSetIds[i] = operatorSetIds[i+offset]; } else { offset++; @@ -849,13 +975,6 @@ contract RegistryCoordinator is indexRegistry.initializeQuorum(quorumNumber); blsApkRegistry.initializeQuorum(quorumNumber); - // Check if the AVS has migrated to operator sets - if (avsDirectory.isOperatorSetAVS(address(serviceManager))) { - // Create an operator set for the new quorum - uint32[] memory operatorSetIds = new uint32[](1); - operatorSetIds[0] = uint32(quorumNumber); - serviceManager.createOperatorSets(operatorSetIds); - } } /** diff --git a/src/ServiceManagerBase.sol b/src/ServiceManagerBase.sol index 74d47510..a482c65d 100644 --- a/src/ServiceManagerBase.sol +++ b/src/ServiceManagerBase.sol @@ -85,7 +85,7 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage { } function slashOperator(IAllocationManager.SlashingParams memory params) external onlySlasher { - _allocationManager.slashOperator(params); + _allocationManager.slashOperator(address(this), params); } /** @@ -115,11 +115,12 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage { ); } - _rewardsCoordinator.createAVSRewardsSubmission(rewardsSubmissions); + _rewardsCoordinator.createAVSRewardsSubmission(address(this),rewardsSubmissions); } function createOperatorSets(uint32[] memory operatorSetIds) external onlyRegistryCoordinator { - _avsDirectory.createOperatorSets(operatorSetIds); + /// TODO: + // _avsDirectory.createOperatorSets(operatorSetIds); } /** @@ -153,7 +154,7 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage { uint32[] calldata operatorSetIds, ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature ) public virtual onlyRegistryCoordinator { - _avsDirectory.registerOperatorToOperatorSets(operator, operatorSetIds, operatorSignature); + // _avsDirectory.registerOperatorToOperatorSets(operator, operatorSetIds, operatorSignature); } /** @@ -165,7 +166,7 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage { address operator, uint32[] calldata operatorSetIds ) public virtual onlyRegistryCoordinator { - _avsDirectory.deregisterOperatorFromOperatorSets(operator, operatorSetIds); + // _avsDirectory.deregisterOperatorFromOperatorSets(operator, operatorSetIds); } /** @@ -241,8 +242,8 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage { * @param operatorSetIdsToCreate An array of operator set IDs to create. */ function _migrateAndCreateOperatorSetIds(uint32[] memory operatorSetIdsToCreate) internal { - _avsDirectory.becomeOperatorSetAVS(); - IAVSDirectory(address(_avsDirectory)).createOperatorSets(operatorSetIdsToCreate); + // _avsDirectory.becomeOperatorSetAVS(); + // IAVSDirectory(address(_avsDirectory)).createOperatorSets(operatorSetIdsToCreate); } /** @@ -260,9 +261,9 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage { for (uint256 i; i < operators.length; i++) { _isOperatorRegisteredForQuorums(operators[i], operatorSetIds[i]); } - IAVSDirectory(address(_avsDirectory)).migrateOperatorsToOperatorSets( - operators, operatorSetIds - ); + // IAVSDirectory(address(_avsDirectory)).migrateOperatorsToOperatorSets( + // operators, operatorSetIds + // ); } /** diff --git a/src/StakeRegistry.sol b/src/StakeRegistry.sol index 4a0ceca8..73bea57b 100644 --- a/src/StakeRegistry.sol +++ b/src/StakeRegistry.sol @@ -2,7 +2,8 @@ pragma solidity ^0.8.12; import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; -import {IAVSDirectory, OperatorSet} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; +import {IAVSDirectory } from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; +import {OperatorSet} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; import {IAllocationManager} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; import {IServiceManager} from "./interfaces/IServiceManager.sol"; @@ -153,9 +154,9 @@ contract StakeRegistry is StakeRegistryStorage { ) external onlyRegistryCoordinator returns (uint192) { uint192 quorumsToRemove; - bool isOperatorSetAVS = avsDirectory.isOperatorSetAVS( - address(serviceManager) - ); + bool isOperatorSetAVS; + // TODO: logic for determining if it's an operator set quorum number or not + // avsDirectory.isOperatorSetAVS(address(serviceManager)); /** * For each quorum, update the operator's stake and record the delta @@ -181,10 +182,12 @@ contract StakeRegistry is StakeRegistryStorage { // Get the AVSDirectory address from the RegistryCoordinator // Query the AVSDirectory to check if the operator is directly unregistered - operatorRegistered = avsDirectory.isMember( - operator, - OperatorSet(address(serviceManager), operatorSetId) - ); + operatorRegistered; + // TODO: Fix + // = avsDirectory.isMember( + // operator, + // OperatorSet(address(serviceManager), operatorSetId) + // ); if (!hasMinimumStake || (isOperatorSetAVS && !operatorRegistered)) { stakeWeight = 0; @@ -526,8 +529,8 @@ contract StakeRegistry is StakeRegistryStorage { operators[0] = operator; uint32 beforeTimestamp = uint32(block.timestamp + slashableStakeLookAheadPerQuorum[quorumNumber]); - (,uint256[][] memory slashableShares) = IAllocationManager(serviceManager.allocationManager()) - .getMinDelegatedAndSlashableOperatorShares( + uint256[][] memory slashableShares = IAllocationManager(serviceManager.allocationManager()) + .getMinimumSlashableStake( OperatorSet(address(serviceManager), quorumNumber), operators, strategiesPerQuorum[quorumNumber], @@ -551,17 +554,23 @@ contract StakeRegistry is StakeRegistryStorage { if (stakeTypePerQuorum[quorumNumber]== StakeType.TOTAL_SLASHABLE) { strategyShares = _getSlashableStakePerStrategy(quorumNumber, operator); + for (uint256 i = 0; i < stratsLength; i++) { + strategyAndMultiplier = strategyParams[quorumNumber][i]; + if (strategyShares[i] > 0) { + weight += uint96(strategyShares[i] * strategyAndMultiplier.multiplier / WEIGHTING_DIVISOR); + } + } } else { /// M2 Concept of delegated stake strategyShares = delegation.getOperatorShares(operator, strategiesPerQuorum[quorumNumber]); - } - for (uint256 i = 0; i < stratsLength; i++) { - // accessing i^th StrategyParams struct for the quorumNumber - strategyAndMultiplier = strategyParams[quorumNumber][i]; + for (uint256 i = 0; i < stratsLength; i++) { + // accessing i^th StrategyParams struct for the quorumNumber + strategyAndMultiplier = strategyParams[quorumNumber][i]; - // add the weight from the shares for this strategy to the total weight - if (strategyShares[i] > 0) { - weight += uint96(strategyShares[i] * strategyAndMultiplier.multiplier / WEIGHTING_DIVISOR); + // add the weight from the shares for this strategy to the total weight + if (strategyShares[i] > 0) { + weight += uint96(strategyShares[i] * strategyAndMultiplier.multiplier / WEIGHTING_DIVISOR); + } } } diff --git a/src/interfaces/ISlasher.sol b/src/interfaces/ISlasher.sol index e2d128f1..018cf16c 100644 --- a/src/interfaces/ISlasher.sol +++ b/src/interfaces/ISlasher.sol @@ -20,7 +20,6 @@ interface ISlasherEvents { uint256 indexed slashingRequestId, address indexed operator, uint32 indexed operatorSetId, - IStrategy[] strategies, uint256 wadToSlash, string description ); diff --git a/src/interfaces/IStakeRegistry.sol b/src/interfaces/IStakeRegistry.sol index 17770b3a..ded09290 100644 --- a/src/interfaces/IStakeRegistry.sol +++ b/src/interfaces/IStakeRegistry.sol @@ -16,7 +16,6 @@ enum StakeType { * @author Layr Labs, Inc. */ interface IStakeRegistry is IRegistry { - // DATA STRUCTURES /// @notice struct used to store the stakes of an individual operator or the sum of all operators' stakes, for storage diff --git a/src/slashers/base/SlasherBase.sol b/src/slashers/base/SlasherBase.sol index c62513d3..8357c6f4 100644 --- a/src/slashers/base/SlasherBase.sol +++ b/src/slashers/base/SlasherBase.sol @@ -24,7 +24,7 @@ abstract contract SlasherBase is Initializable, SlasherStorage { IAllocationManager.SlashingParams memory _params ) internal virtual { IServiceManager(serviceManager).slashOperator(_params); - emit OperatorSlashed(_requestId, _params.operator, _params.operatorSetId, _params.strategies, _params.wadToSlash, _params.description); + emit OperatorSlashed(_requestId, _params.operator, _params.operatorSetId, _params.wadToSlash, _params.description); } function _checkSlasher(address account) internal view virtual { diff --git a/src/unaudited/ECDSAServiceManagerBase.sol b/src/unaudited/ECDSAServiceManagerBase.sol index facb6813..c358796d 100644 --- a/src/unaudited/ECDSAServiceManagerBase.sol +++ b/src/unaudited/ECDSAServiceManagerBase.sol @@ -23,7 +23,7 @@ abstract contract ECDSAServiceManagerBase is /// @notice Address of the AVS directory contract, which manages AVS-related data for registered operators. address public immutable avsDirectory; - /// @notice Address of the AllocationManager contract + /// @notice Address of the AllocationManager contract address public immutable allocationManager; /// @notice Address of the rewards coordinator contract, which handles rewards distributions. @@ -203,7 +203,7 @@ abstract contract ECDSAServiceManagerBase is ); } - IRewardsCoordinator(rewardsCoordinator).createAVSRewardsSubmission( + IRewardsCoordinator(rewardsCoordinator).createAVSRewardsSubmission(address(this), rewardsSubmissions ); } diff --git a/test/harnesses/AVSDirectoryHarness.sol b/test/harnesses/AVSDirectoryHarness.sol index 94598058..262e8903 100644 --- a/test/harnesses/AVSDirectoryHarness.sol +++ b/test/harnesses/AVSDirectoryHarness.sol @@ -4,65 +4,9 @@ pragma solidity ^0.8.12; import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; import {AVSDirectory} from "eigenlayer-contracts/src/contracts/core/AVSDirectory.sol"; +import {IPauserRegistry} from "eigenlayer-contracts/src/contracts/interfaces/IPauserRegistry.sol"; // wrapper around the AVSDirectory contract that exposes internal functionality, for unit testing contract AVSDirectoryHarness is AVSDirectory { - constructor(IDelegationManager _delegation) AVSDirectory(_delegation, 0) {} // TODO: config update - - function setOperatorSaltIsSpent(address operator, bytes32 salt, bool isSpent) external { - operatorSaltIsSpent[operator][salt] = isSpent; - } - - function setAvsOperatorStatus( - address avs, - address operator, - OperatorAVSRegistrationStatus status - ) external { - avsOperatorStatus[avs][operator] = status; - } - - function setIsOperatorSetAVS(address avs, bool isOperatorSet) external { - isOperatorSetAVS[avs] = isOperatorSet; - } - - function setIsOperatorSet(address avs, uint32 operatorSetId, bool isSet) external { - isOperatorSet[avs][operatorSetId] = isSet; - } - - function setIsMember( - address avs, - address operator, - uint32[] calldata operatorSetIds, - bool membershipStatus - ) external { - if (membershipStatus) { - _registerToOperatorSets(avs, operator, operatorSetIds); - } else { - _deregisterFromOperatorSets(avs, operator, operatorSetIds); - } - } - - function _registerToOperatorSetsExternal( - address avs, - address operator, - uint32[] calldata operatorSetIds - ) external { - _registerToOperatorSets(avs, operator, operatorSetIds); - } - - function _deregisterFromOperatorSetsExternal( - address avs, - address operator, - uint32[] calldata operatorSetIds - ) external { - _deregisterFromOperatorSets(avs, operator, operatorSetIds); - } - - function _calculateDigestHashExternal(bytes32 structHash) external view returns (bytes32) { - // return calculateOperatorSetRegistrationDigestHash(structHash); // TODO: Fix - } - - function _calculateDomainSeparatorExternal() external view returns (bytes32) { - return _calculateDomainSeparator(); - } -} + constructor(IDelegationManager _dm, IPauserRegistry _pauser)AVSDirectory(_dm, _pauser){} +} \ No newline at end of file diff --git a/test/harnesses/RegistryCoordinatorHarness.t.sol b/test/harnesses/RegistryCoordinatorHarness.t.sol index 4644c276..9dede138 100644 --- a/test/harnesses/RegistryCoordinatorHarness.t.sol +++ b/test/harnesses/RegistryCoordinatorHarness.t.sol @@ -12,8 +12,9 @@ contract RegistryCoordinatorHarness is RegistryCoordinator, Test { IStakeRegistry _stakeRegistry, IBLSApkRegistry _blsApkRegistry, IIndexRegistry _indexRegistry, - IAVSDirectory _avsDirectory - ) RegistryCoordinator(_serviceManager, _stakeRegistry, _blsApkRegistry, _indexRegistry, _avsDirectory) { + IAVSDirectory _avsDirectory, + IPauserRegistry _pauserRegistry + ) RegistryCoordinator(_serviceManager, _stakeRegistry, _blsApkRegistry, _indexRegistry, _avsDirectory, _pauserRegistry) { _transferOwnership(msg.sender); } @@ -27,7 +28,7 @@ contract RegistryCoordinatorHarness is RegistryCoordinator, Test { // @notice exposes the internal `_registerOperator` function, overriding all access controls function _registerOperatorExternal( - address operator, + address operator, bytes32 operatorId, bytes calldata quorumNumbers, string memory socket, @@ -38,7 +39,7 @@ contract RegistryCoordinatorHarness is RegistryCoordinator, Test { // @notice exposes the internal `_deregisterOperator` function, overriding all access controls function _deregisterOperatorExternal( - address operator, + address operator, bytes calldata quorumNumbers ) external { _deregisterOperator(operator, quorumNumbers); diff --git a/test/integration/CoreRegistration.t.sol b/test/integration/CoreRegistration.t.sol index b9784dc6..f385c7e2 100644 --- a/test/integration/CoreRegistration.t.sol +++ b/test/integration/CoreRegistration.t.sol @@ -9,6 +9,7 @@ import { DelegationManager } from "eigenlayer-contracts/src/contracts/core/Deleg import { IDelegationManager, IDelegationManagerTypes } from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; import { RewardsCoordinator } from "eigenlayer-contracts/src/contracts/core/RewardsCoordinator.sol"; import { IRewardsCoordinator } from "eigenlayer-contracts/src/contracts/interfaces/IRewardsCoordinator.sol"; +import { PermissionController } from "eigenlayer-contracts/src/contracts/permissions/PermissionController.sol"; contract Test_CoreRegistration is MockAVSDeployer { // Contracts @@ -27,7 +28,8 @@ contract Test_CoreRegistration is MockAVSDeployer { _deployMockEigenLayerAndAVS(); // Deploy New DelegationManager - DelegationManager delegationManagerImplementation = new DelegationManager(avsDirectoryMock, IStrategyManager(address(strategyManagerMock)), eigenPodManagerMock, allocationManagerMock, 0); + PermissionController permissionController; // TODO: Fix + DelegationManager delegationManagerImplementation = new DelegationManager(avsDirectoryMock, IStrategyManager(address(strategyManagerMock)), eigenPodManagerMock, allocationManagerMock, pauserRegistry, permissionController, 0); IStrategy[] memory initializeStrategiesToSetDelayBlocks = new IStrategy[](0); uint256[] memory initializeWithdrawalDelayBlocks = new uint256[](0); delegationManager = DelegationManager( @@ -49,7 +51,7 @@ contract Test_CoreRegistration is MockAVSDeployer { ); // Deploy New AVS Directory - AVSDirectory avsDirectoryImplementation = new AVSDirectory(delegationManager, 0); // TODO: Fix Config + AVSDirectory avsDirectoryImplementation = new AVSDirectory(delegationManager, pauserRegistry); // TODO: Fix Config avsDirectory = AVSDirectory( address( new TransparentUpgradeableProxy( @@ -82,7 +84,8 @@ contract Test_CoreRegistration is MockAVSDeployer { stakeRegistry, blsApkRegistry, indexRegistry, - avsDirectory + avsDirectory, + pauserRegistry ); // Upgrade Registry Coordinator & ServiceManager @@ -104,11 +107,7 @@ contract Test_CoreRegistration is MockAVSDeployer { // Register operator to EigenLayer cheats.prank(operator); delegationManager.registerAsOperator( - IDelegationManagerTypes.OperatorDetails({ - __deprecated_earningsReceiver: operator, - delegationApprover: address(0), - __deprecated_stakerOptOutWindowBlocks: 0 - }), + operator, // TODO: fix or parameterize 0, emptyStringForMetadataURI diff --git a/test/integration/IntegrationDeployer.t.sol b/test/integration/IntegrationDeployer.t.sol index 2c33bfa5..a162746c 100644 --- a/test/integration/IntegrationDeployer.t.sol +++ b/test/integration/IntegrationDeployer.t.sol @@ -21,6 +21,7 @@ import "eigenlayer-contracts/src/contracts/strategies/StrategyBase.sol"; import "eigenlayer-contracts/src/contracts/pods/EigenPodManager.sol"; import "eigenlayer-contracts/src/contracts/pods/EigenPod.sol"; import "eigenlayer-contracts/src/contracts/permissions/PauserRegistry.sol"; +import "eigenlayer-contracts/src/contracts/permissions/PermissionController.sol"; import "eigenlayer-contracts/src/test/mocks/ETHDepositMock.sol"; // Middleware contracts @@ -55,6 +56,7 @@ abstract contract IntegrationDeployer is Test, IUserDeployer { EigenPod pod; ETHPOSDepositMock ethPOSDeposit; AllocationManager allocationManager; + PermissionController permissionController; // Base strategy implementation in case we want to create more strategies later StrategyBase baseStrategyImplementation; @@ -93,10 +95,23 @@ abstract contract IntegrationDeployer is Test, IUserDeployer { uint256 constant MAX_STRATEGY_COUNT = 32; // From StakeRegistry.MAX_WEIGHING_FUNCTION_LENGTH uint96 constant DEFAULT_STRATEGY_MULTIPLIER = 1e18; // RewardsCoordinator + // Config Variables + /// @notice intervals(epochs) are 1 weeks + uint32 CALCULATION_INTERVAL_SECONDS = 7 days; + + /// @notice Max duration is 5 epochs (2 weeks * 5 = 10 weeks in seconds) uint32 MAX_REWARDS_DURATION = 70 days; + + /// @notice Lower bound start range is ~3 months into the past, multiple of CALCULATION_INTERVAL_SECONDS uint32 MAX_RETROACTIVE_LENGTH = 84 days; + /// @notice Upper bound start range is ~1 month into the future, multiple of CALCULATION_INTERVAL_SECONDS uint32 MAX_FUTURE_LENGTH = 28 days; - uint32 GENESIS_REWARDS_TIMESTAMP = 1_712_092_632; + /// @notice absolute min timestamp that a rewards can start at + uint32 GENESIS_REWARDS_TIMESTAMP = 1712188800; + /// @notice Equivalent to 100%, but in basis points. + uint16 internal constant ONE_HUNDRED_IN_BIPS = 10_000; + + uint32 defaultOperatorSplitBips = 1000; /// @notice Delay in timestamp before a posted root can be claimed against uint32 activationDelay = 7 days; /// @notice intervals(epochs) are 2 weeks @@ -147,9 +162,12 @@ abstract contract IntegrationDeployer is Test, IUserDeployer { new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") ) ); - // RewardsCoordinator = RewardsCoordinator( - // address(new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "")) - // ); + + permissionController = PermissionController(address(new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), ""))); + + rewardsCoordinator = RewardsCoordinator( + address(new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "")) + ); // Deploy EigenPod Contracts pod = new EigenPod( @@ -160,23 +178,31 @@ abstract contract IntegrationDeployer is Test, IUserDeployer { eigenPodBeacon = new UpgradeableBeacon(address(pod)); + PermissionController permissionControllerImplementation = new PermissionController(); + // Second, deploy the *implementation* contracts, using the *proxy contracts* as inputs DelegationManager delegationImplementation = - new DelegationManager(avsDirectory, strategyManager, eigenPodManager, allocationManager, 0); + new DelegationManager(avsDirectory, strategyManager, eigenPodManager, allocationManager, pauserRegistry, permissionController, 0); StrategyManager strategyManagerImplementation = - new StrategyManager(delegationManager); + new StrategyManager(delegationManager, pauserRegistry); EigenPodManager eigenPodManagerImplementation = new EigenPodManager( - ethPOSDeposit, eigenPodBeacon, strategyManager, delegationManager + ethPOSDeposit, eigenPodBeacon, strategyManager, delegationManager, pauserRegistry + ); + console.log("HERE Impl"); + AVSDirectory avsDirectoryImplementation = new AVSDirectory(delegationManager, pauserRegistry); + + RewardsCoordinator rewardsCoordinatorImplementation = new RewardsCoordinator( + delegationManager, + IStrategyManager(address(strategyManager)), + allocationManager, + pauserRegistry, + permissionController, + CALCULATION_INTERVAL_SECONDS, + MAX_REWARDS_DURATION, + MAX_RETROACTIVE_LENGTH, + MAX_FUTURE_LENGTH, + GENESIS_REWARDS_TIMESTAMP ); - AVSDirectory avsDirectoryImplemntation = new AVSDirectory(delegationManager, 0); // TODO: fix config - // RewardsCoordinator rewardsCoordinatorImplementation = new RewardsCoordinator( - // delegationManager, - // IStrategyManager(address(strategyManager)), - // MAX_REWARDS_DURATION, - // MAX_RETROACTIVE_LENGTH, - // MAX_FUTURE_LENGTH, - // GENESIS_REWARDS_TIMESTAMP - // ); // Third, upgrade the proxy contracts to point to the implementations uint256 minWithdrawalDelayBlocks = 7 days / 12 seconds; @@ -219,35 +245,43 @@ abstract contract IntegrationDeployer is Test, IUserDeployer { 0 // initialPausedStatus ) ); + console.log("HERE"); // AVSDirectory proxyAdmin.upgradeAndCall( TransparentUpgradeableProxy(payable(address(avsDirectory))), - address(avsDirectoryImplemntation), + address(avsDirectoryImplementation), abi.encodeWithSelector( AVSDirectory.initialize.selector, eigenLayerReputedMultisig, // initialOwner - pauserRegistry, + // pauserRegistry, 0 // initialPausedStatus ) ); - // // RewardsCoordinator - // proxyAdmin.upgradeAndCall( - // TransparentUpgradeableProxy(payable(address(rewardsCoordinator))), - // address(rewardsCoordinatorImplementation), - // abi.encodeWithSelector( - // RewardsCoordinator.initialize.selector, - // eigenLayerReputedMultisig, // initialOwner - // pauserRegistry, - // 0, // initialPausedStatus - // rewardsUpdater, - // activationDelay, - // calculationIntervalSeconds, - // globalCommissionBips - // ) - // ); + + console.log("HERE 2"); + proxyAdmin.upgradeAndCall( + TransparentUpgradeableProxy(payable(address(permissionController))), + address(permissionControllerImplementation), + abi.encodeWithSelector( + PermissionController.initialize.selector + ) + ); + + proxyAdmin.upgradeAndCall( + TransparentUpgradeableProxy(payable(address(rewardsCoordinator))), + address(rewardsCoordinatorImplementation), + abi.encodeWithSelector( + RewardsCoordinator.initialize.selector, + eigenLayerReputedMultisig, // initialOwner + 0, // initialPausedStatus + rewardsUpdater, + activationDelay, + defaultOperatorSplitBips // defaultSplitBips + ) + ); // Deploy and whitelist strategies - baseStrategyImplementation = new StrategyBase(strategyManager); + baseStrategyImplementation = new StrategyBase(strategyManager, pauserRegistry); for (uint256 i = 0; i < MAX_STRATEGY_COUNT; i++) { string memory number = uint256(i).toString(); string memory stratName = string.concat("StrategyToken", number); @@ -335,7 +369,7 @@ abstract contract IntegrationDeployer is Test, IUserDeployer { uint32[] memory slashableStakeQuorumLookAheadPeriods = new uint32[](0); RegistryCoordinator registryCoordinatorImplementation = - new RegistryCoordinator(serviceManager, stakeRegistry, blsApkRegistry, indexRegistry, avsDirectory); + new RegistryCoordinator(serviceManager, stakeRegistry, blsApkRegistry, indexRegistry, avsDirectory, pauserRegistry); proxyAdmin.upgradeAndCall( TransparentUpgradeableProxy(payable(address(registryCoordinator))), address(registryCoordinatorImplementation), @@ -344,7 +378,6 @@ abstract contract IntegrationDeployer is Test, IUserDeployer { registryCoordinatorOwner, churnApprover, ejector, - pauserRegistry, 0, /*initialPausedStatus*/ new IRegistryCoordinator.OperatorSetParam[](0), new uint96[](0), diff --git a/test/integration/User.t.sol b/test/integration/User.t.sol index ff31a7ed..b31914bd 100644 --- a/test/integration/User.t.sol +++ b/test/integration/User.t.sol @@ -57,7 +57,7 @@ contract User is Test { BLSApkRegistry blsApkRegistry; StakeRegistry stakeRegistry; IndexRegistry indexRegistry; - + TimeMachine timeMachine; uint churnApproverPrivateKey; @@ -147,12 +147,12 @@ contract User is Test { assertEq(churnQuorums.length, churnTargets.length, "User.registerOperatorWithChurn: input length mismatch"); assertTrue(churnBitmap.noBitsInCommon(standardBitmap), "User.registerOperatorWithChurn: input quorums have common bits"); - bytes memory allQuorums = + bytes memory allQuorums = churnBitmap .plus(standardBitmap) .bitmapToBytesArray(); - IRegistryCoordinator.OperatorKickParam[] memory kickParams + IRegistryCoordinator.OperatorKickParam[] memory kickParams = new IRegistryCoordinator.OperatorKickParam[](allQuorums.length); // this constructs OperatorKickParam[] in ascending quorum order @@ -244,13 +244,8 @@ contract User is Test { function registerAsOperator() public createSnapshot virtual { _log("registerAsOperator (core)"); - IDelegationManagerTypes.OperatorDetails memory details = IDelegationManagerTypes.OperatorDetails({ - __deprecated_earningsReceiver: address(this), - delegationApprover: address(0), - __deprecated_stakerOptOutWindowBlocks: 0 - }); - - delegationManager.registerAsOperator(details,0, NAME); + /// TODO: check + delegationManager.registerAsOperator(msg.sender,0, NAME); } // Deposit LSTs into the StrategyManager. This setup does not use the EPMgr or native ETH. @@ -270,14 +265,14 @@ contract User is Test { function exitEigenlayer() public createSnapshot virtual returns (IStrategy[] memory, uint256[] memory) { _log("exitEigenlayer (core)"); - IStrategy[] memory strategies; + IStrategy[] memory strategies; uint256[] memory shares; // = delegationManager.getDelegatableShares(address(this)); // TODO: Fix IDelegationManagerTypes.QueuedWithdrawalParams[] memory params = new IDelegationManager.QueuedWithdrawalParams[](1); params[0] = IDelegationManagerTypes.QueuedWithdrawalParams({ strategies: strategies, - shares: shares, + depositShares: shares, withdrawer: address(this) }); @@ -332,14 +327,14 @@ contract User is Test { emit log_named_string(string.concat(NAME, ".", s), quorums.toString()); } - // Operator0.registerOperatorWithChurn + // Operator0.registerOperatorWithChurn // - standardQuorums: 0x00010203... // - churnQuorums: 0x0405... // - churnTargets: Operator1, Operator2, ... function _logChurn( - string memory s, - bytes memory churnQuorums, - User[] memory churnTargets, + string memory s, + bytes memory churnQuorums, + User[] memory churnTargets, bytes memory standardQuorums ) internal virtual { emit log(string.concat(NAME, ".", s)); @@ -371,7 +366,7 @@ contract User_AltMethods is User { _; } - constructor(string memory name, uint _privKey, IBLSApkRegistry.PubkeyRegistrationParams memory _pubkeyParams) + constructor(string memory name, uint _privKey, IBLSApkRegistry.PubkeyRegistrationParams memory _pubkeyParams) User(name, _privKey, _pubkeyParams) {} /// @dev Rather than calling deregisterOperator, this pranks the ejector and calls @@ -405,6 +400,6 @@ contract User_AltMethods is User { operatorsPerQuorum[i] = Sort.sortAddresses(operatorsPerQuorum[i]); } - registryCoordinator.updateOperatorsForQuorum(operatorsPerQuorum, allQuorums); + registryCoordinator.updateOperatorsForQuorum(operatorsPerQuorum, allQuorums); } } diff --git a/test/mocks/AVSDirectoryMock.sol b/test/mocks/AVSDirectoryMock.sol index 597a09a1..5e4c6f77 100644 --- a/test/mocks/AVSDirectoryMock.sol +++ b/test/mocks/AVSDirectoryMock.sol @@ -1,17 +1,23 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.12; -import {IAVSDirectory, OperatorSet} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; +import {IAVSDirectory } from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; +import {OperatorSet} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; import {IPauserRegistry} from "eigenlayer-contracts/src/contracts/interfaces/IPauserRegistry.sol"; contract AVSDirectoryMock is IAVSDirectory { + function initialize( + address initialOwner, + uint256 initialPausedStatus + ) external {} + function createOperatorSets( uint32[] calldata operatorSetIds ) external {} - function becomeOperatorSetAVS() external {} + function becomeOperatorSetAVS() external {} function migrateOperatorsToOperatorSets( address[] calldata operators, @@ -24,11 +30,6 @@ contract AVSDirectoryMock is IAVSDirectory { ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature ) external {} - function deregisterOperatorFromOperatorSets( - address operator, - uint32[] calldata operatorSetIds - ) external {} - function forceDeregisterFromOperatorSets( address operator, address avs, @@ -36,32 +37,37 @@ contract AVSDirectoryMock is IAVSDirectory { ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature ) external {} - function registerOperatorToAVS( + function deregisterOperatorFromOperatorSets( address operator, - ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature + uint32[] calldata operatorSetIds ) external {} - function deregisterOperatorFromAVS(address operator) external {} + function addStrategiesToOperatorSet( + uint32 operatorSetId, + IStrategy[] calldata strategies + ) external {} + + function removeStrategiesFromOperatorSet( + uint32 operatorSetId, + IStrategy[] calldata strategies + ) external {} function updateAVSMetadataURI( string calldata metadataURI ) external {} - function cancelSalt(bytes32 salt) external {} + function cancelSalt(bytes32 salt) external {} - function operatorSaltIsSpent( + function registerOperatorToAVS( address operator, - bytes32 salt - ) external view returns (bool) {} + ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature + ) external {} - function isMember( - address operator, - OperatorSet memory operatorSet - ) external view returns (bool) {} + function deregisterOperatorFromAVS(address operator) external {} - function isOperatorSlashable( + function operatorSaltIsSpent( address operator, - OperatorSet memory operatorSet + bytes32 salt ) external view returns (bool) {} function isOperatorSetAVS( @@ -73,35 +79,9 @@ contract AVSDirectoryMock is IAVSDirectory { uint32 operatorSetId ) external view returns (bool) {} - function isOperatorSetBatch( - OperatorSet[] calldata operatorSets - ) external view returns (bool) {} - - function operatorSetsMemberOfAtIndex( - address operator, - uint256 index - ) external view returns (OperatorSet memory) {} - - function operatorSetMemberAtIndex( - OperatorSet memory operatorSet, - uint256 index - ) external view returns (address) {} - - function getOperatorSetsOfOperator( - address operator, - uint256 start, - uint256 length - ) external view returns (OperatorSet[] memory operatorSets) {} - - function getOperatorsInOperatorSet( - OperatorSet memory operatorSet, - uint256 start, - uint256 length - ) external view returns (address[] memory operators) {} - - function getNumOperatorsInOperatorSet( - OperatorSet memory operatorSet - ) external view returns (uint256) {} + function getNumOperatorSetsOfOperator( + address operator + ) external view returns (uint256) {} function inTotalOperatorSets( address operator @@ -131,13 +111,15 @@ contract AVSDirectoryMock is IAVSDirectory { function OPERATOR_AVS_REGISTRATION_TYPEHASH() external view - returns (bytes32) + + returns (bytes32) {} function OPERATOR_SET_REGISTRATION_TYPEHASH() external view - returns (bytes32) + + returns (bytes32) {} function operatorSetStatus( @@ -147,16 +129,9 @@ contract AVSDirectoryMock is IAVSDirectory { ) external view - returns (bool registered, uint32 lastDeregisteredTimestamp) - {} - function getNumOperatorSetsOfOperator( - address operator - ) external view returns (uint256) {} - - function getStrategiesInOperatorSet( - OperatorSet memory operatorSet - ) external view returns (IStrategy[] memory) {} + returns (bool registered, uint32 lastDeregisteredTimestamp) + {} function initialize( address initialOwner, @@ -164,10 +139,36 @@ contract AVSDirectoryMock is IAVSDirectory { uint256 initialPausedStatus ) external {} - function removeStrategiesFromOperatorSet( - uint32 operatorSetId, - IStrategy[] calldata strategies - ) external {} + function operatorSetMemberAtIndex( + OperatorSet memory operatorSet, + uint256 index + ) external view returns (address) {} + + function getOperatorsInOperatorSet( + OperatorSet memory operatorSet, + uint256 start, + uint256 length + ) external view returns (address[] memory operators) {} + + function getStrategiesInOperatorSet( + OperatorSet memory operatorSet + ) external view returns (IStrategy[] memory strategies) {} + + function getNumOperatorsInOperatorSet( + OperatorSet memory operatorSet + ) external view returns (uint256) {} - function addStrategiesToOperatorSet(uint32 operatorSetId, IStrategy[] calldata strategies) external {} -} + function isMember( + address operator, + OperatorSet memory operatorSet + ) external view returns (bool) {} + + function isOperatorSlashable( + address operator, + OperatorSet memory operatorSet + ) external view returns (bool) {} + + function isOperatorSetBatch( + OperatorSet[] calldata operatorSets + ) external view returns (bool) {} +} \ No newline at end of file diff --git a/test/mocks/AVSRegistrarMock.sol b/test/mocks/AVSRegistrarMock.sol new file mode 100644 index 00000000..ce4ce2bc --- /dev/null +++ b/test/mocks/AVSRegistrarMock.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +import {AVSRegistrar} from "../../src/AVSRegistrar.sol"; + +contract AVSRegistrarMock is AVSRegistrar { + function registerOperator( + address operator, + uint32[] calldata operatorSetIds, + bytes calldata data + ) external override {} + + function deregisterOperator( + address operator, + uint32[] calldata operatorSetIds + ) external override {} +} diff --git a/test/mocks/AllocationManagerMock.sol b/test/mocks/AllocationManagerMock.sol index 1436882e..adf06c62 100644 --- a/test/mocks/AllocationManagerMock.sol +++ b/test/mocks/AllocationManagerMock.sol @@ -1,83 +1,170 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.12; -import {IAllocationManager} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; +import {IAllocationManager, OperatorSet} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; +import {IAVSRegistrar } from "eigenlayer-contracts/src/contracts/interfaces/IAVSRegistrar.sol"; import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; import {IPauserRegistry} from "eigenlayer-contracts/src/contracts/interfaces/IPauserRegistry.sol"; -import {OperatorSet} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; - -contract AllocationManagerMock is IAllocationManager { +contract AllocationManagerIntermediate is IAllocationManager { function initialize( address initialOwner, - IPauserRegistry _pauserRegistry, uint256 initialPausedStatus - ) external override {} + ) external virtual {} - function slashOperator(SlashingParams calldata params) external override {} + function slashOperator( + address avs, + SlashingParams calldata params + ) external virtual {} function modifyAllocations( - MagnitudeAllocation[] calldata allocations - ) external override {} + address operator, + AllocateParams[] calldata params + ) external virtual {} function clearDeallocationQueue( address operator, IStrategy[] calldata strategies, - uint16[] calldata numToComplete - ) external override {} + uint16[] calldata numToClear + ) external virtual {} + + function registerForOperatorSets( + address operator, + RegisterParams calldata params + ) external virtual {} + + function deregisterFromOperatorSets( + DeregisterParams calldata params + ) external virtual {} function setAllocationDelay( address operator, uint32 delay - ) external override {} + ) external virtual {} + + function setAVSRegistrar( + address avs, + IAVSRegistrar registrar + ) external virtual {} - function setAllocationDelay(uint32 delay) external override {} + function updateAVSMetadataURI( + address avs, + string calldata metadataURI + ) external virtual {} + + function createOperatorSets( + address avs, + CreateSetParams[] calldata params + ) external virtual {} + + function addStrategiesToOperatorSet( + address avs, + uint32 operatorSetId, + IStrategy[] calldata strategies + ) external virtual {} + + function removeStrategiesFromOperatorSet( + address avs, + uint32 operatorSetId, + IStrategy[] calldata strategies + ) external virtual {} + + function getOperatorSetCount( + address avs + ) external view virtual returns (uint256) {} + + function getAllocatedSets( + address operator + ) external view virtual returns (OperatorSet[] memory) {} + + function getAllocatedStrategies( + address operator, + OperatorSet memory operatorSet + ) external view virtual returns (IStrategy[] memory) {} + + function getAllocation( + address operator, + OperatorSet memory operatorSet, + IStrategy strategy + ) external view virtual returns (Allocation memory) {} + + function getAllocations( + address[] memory operators, + OperatorSet memory operatorSet, + IStrategy strategy + ) external view virtual returns (Allocation[] memory) {} - function getAllocationInfo( + function getStrategyAllocations( address operator, IStrategy strategy ) external view - override - returns (OperatorSet[] memory, MagnitudeInfo[] memory) + virtual + returns (OperatorSet[] memory, Allocation[] memory) {} - function getAllocationInfo( + function getAllocatableMagnitude( address operator, - IStrategy strategy, - OperatorSet[] calldata operatorSets - ) external view override returns (MagnitudeInfo[] memory) {} - - function getAllocationInfo( - OperatorSet calldata operatorSet, - IStrategy[] calldata strategies, - address[] calldata operators - ) external view override returns (MagnitudeInfo[][] memory) {} + IStrategy strategy + ) external view virtual returns (uint64) {} - function getAllocatableMagnitude( + function getMaxMagnitude( address operator, IStrategy strategy - ) external view override returns (uint64) {} + ) external view virtual returns (uint64) {} function getMaxMagnitudes( address operator, IStrategy[] calldata strategies - ) external view override returns (uint64[] memory) {} + ) external view virtual returns (uint64[] memory) {} - function getMaxMagnitudesAtTimestamp( + function getMaxMagnitudes( + address[] calldata operators, + IStrategy strategy + ) external view virtual returns (uint64[] memory) {} + + function getMaxMagnitudesAtBlock( address operator, IStrategy[] calldata strategies, - uint32 timestamp - ) external view override returns (uint64[] memory) {} + uint32 blockNumber + ) external view virtual returns (uint64[] memory) {} function getAllocationDelay( address operator - ) external view override returns (bool isSet, uint32 delay) {} + ) external view virtual returns (bool isSet, uint32 delay) {} + + function getRegisteredSets( + address operator + ) external view virtual returns (OperatorSet[] memory operatorSets) {} + + function isOperatorSet( + OperatorSet memory operatorSet + ) external view virtual returns (bool) {} + + function getMembers( + OperatorSet memory operatorSet + ) external view virtual returns (address[] memory operators) {} + + function getMemberCount( + OperatorSet memory operatorSet + ) external view virtual returns (uint256) {} + + function getAVSRegistrar( + address avs + ) external view virtual returns (IAVSRegistrar) {} + + function getStrategiesInOperatorSet( + OperatorSet memory operatorSet + ) external view virtual returns (IStrategy[] memory strategies) {} + + function getMinimumSlashableStake( + OperatorSet memory operatorSet, + address[] memory operators, + IStrategy[] memory strategies, + uint32 futureBlock + ) external view virtual returns (uint256[][] memory slashableStake) {} +} + +contract AllocationManagerMock is AllocationManagerIntermediate { - function getMinDelegatedAndSlashableOperatorShares( - OperatorSet calldata operatorSet, - address[] calldata operators, - IStrategy[] calldata strategies, - uint32 beforeTimestamp - ) external view override returns (uint256[][] memory, uint256[][] memory) {} } \ No newline at end of file diff --git a/test/mocks/DelegationMock.sol b/test/mocks/DelegationMock.sol index 9743fb14..3f057669 100644 --- a/test/mocks/DelegationMock.sol +++ b/test/mocks/DelegationMock.sol @@ -2,6 +2,7 @@ pragma solidity ^0.8.12; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import {console2 as console} from "forge-std/Test.sol"; import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; import {IStrategyManager} from "eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol"; @@ -11,319 +12,261 @@ import {IPauserRegistry} from "eigenlayer-contracts/src/contracts/interfaces/IPa import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; import {SlashingLib} from "eigenlayer-contracts/src/contracts/libraries/SlashingLib.sol"; -contract DelegationMock is IDelegationManager { - using SlashingLib for uint256; - - mapping(address => bool) public isOperator; - mapping(address => mapping(IStrategy => uint256)) public operatorShares; - - function setIsOperator( - address operator, - bool _isOperatorReturnValue - ) external { - isOperator[operator] = _isOperatorReturnValue; - } - - /// @notice returns the total number of shares in `strategy` that are delegated to `operator`. - function setOperatorShares( - address operator, - IStrategy strategy, - uint256 shares - ) external { - operatorShares[operator][strategy] = shares; - } - - mapping(address => address) public delegatedTo; +contract DelegationIntermediate is IDelegationManager { + function initialize( + address initialOwner, + uint256 initialPausedStatus + ) external virtual {} function registerAsOperator( - OperatorDetails calldata /*registeringOperatorDetails*/, - string calldata /*metadataURI*/ - ) external pure {} + OperatorDetails calldata registeringOperatorDetails, + uint32 allocationDelay, + string calldata metadataURI + ) external virtual {} - function updateOperatorMetadataURI( - string calldata /*metadataURI*/ - ) external pure {} + function modifyOperatorDetails( + OperatorDetails calldata newOperatorDetails + ) external virtual {} - function updateAVSMetadataURI( - string calldata /*metadataURI*/ - ) external pure {} + function updateOperatorMetadataURI( + string calldata metadataURI + ) external virtual {} function delegateTo( address operator, - SignatureWithExpiry memory /*approverSignatureAndExpiry*/, - bytes32 /*approverSalt*/ - ) external { - delegatedTo[msg.sender] = operator; - } - - function modifyOperatorDetails( - OperatorDetails calldata /*newOperatorDetails*/ - ) external pure {} - - function delegateToBySignature( - address /*staker*/, - address /*operator*/, - SignatureWithExpiry memory /*stakerSignatureAndExpiry*/, - SignatureWithExpiry memory /*approverSignatureAndExpiry*/, - bytes32 /*approverSalt*/ - ) external pure {} + SignatureWithExpiry memory approverSignatureAndExpiry, + bytes32 approverSalt + ) external virtual {} function undelegate( address staker - ) external returns (bytes32[] memory withdrawalRoot) { - delegatedTo[staker] = address(0); - return withdrawalRoot; - } - - function increaseDelegatedShares( - address /*staker*/, - IStrategy /*strategy*/, - uint256 /*shares*/ - ) external pure {} - - function operatorDetails( - address operator - ) external pure returns (OperatorDetails memory) { - OperatorDetails memory returnValue = OperatorDetails({ - __deprecated_earningsReceiver: operator, - delegationApprover: operator, - __deprecated_stakerOptOutWindowBlocks: 0 - }); - return returnValue; - } - - function beaconChainETHStrategy() external pure returns (IStrategy) {} - - function earningsReceiver(address operator) external pure returns (address) { - return operator; - } - - function delegationApprover( - address operator - ) external pure returns (address) { - return operator; - } - - function stakerOptOutWindowBlocks( - address /*operator*/ - ) external pure returns (uint256) { - return 0; - } - - function minWithdrawalDelayBlocks() external view returns (uint256) { - return 50400; - } - - /** - * @notice Minimum delay enforced by this contract per Strategy for completing queued withdrawals. Measured in blocks, and adjustable by this contract's owner, - * up to a maximum of `MAX_WITHDRAWAL_DELAY_BLOCKS`. Minimum value is 0 (i.e. no delay enforced). - */ - function strategyWithdrawalDelayBlocks( - IStrategy /*strategy*/ - ) external view returns (uint256) { - return 0; - } - - function getOperatorShares( - address operator, - IStrategy[] memory strategies - ) external view returns (uint256[] memory) { - uint256[] memory shares = new uint256[](strategies.length); - for (uint256 i = 0; i < strategies.length; ++i) { - shares[i] = operatorShares[operator][strategies[i]]; - } - return shares; - } - - function getWithdrawalDelay( - IStrategy[] calldata /*strategies*/ - ) public view returns (uint256) { - return 0; - } - - function isDelegated(address staker) external view returns (bool) { - return (delegatedTo[staker] != address(0)); - } - - function isNotDelegated(address /*staker*/) external pure returns (bool) {} - - // function isOperator(address /*operator*/) external pure returns (bool) {} - - function stakerNonce(address /*staker*/) external pure returns (uint256) {} - - function delegationApproverSaltIsSpent( - address /*delegationApprover*/, - bytes32 /*salt*/ - ) external pure returns (bool) {} - - function calculateCurrentStakerDelegationDigestHash( - address /*staker*/, - address /*operator*/, - uint256 /*expiry*/ - ) external view returns (bytes32) {} - - function calculateStakerDelegationDigestHash( - address /*staker*/, - uint256 /*stakerNonce*/, - address /*operator*/, - uint256 /*expiry*/ - ) external view returns (bytes32) {} - - function calculateDelegationApprovalDigestHash( - address /*staker*/, - address /*operator*/, - address /*_delegationApprover*/, - bytes32 /*approverSalt*/, - uint256 /*expiry*/ - ) external view returns (bytes32) {} - - function calculateStakerDigestHash( - address /*staker*/, - address /*operator*/, - uint256 /*expiry*/ - ) external pure returns (bytes32 stakerDigestHash) {} - - function calculateApproverDigestHash( - address /*staker*/, - address /*operator*/, - uint256 /*expiry*/ - ) external pure returns (bytes32 approverDigestHash) {} - - function calculateOperatorAVSRegistrationDigestHash( - address /*operator*/, - address /*avs*/, - bytes32 /*salt*/, - uint256 /*expiry*/ - ) external pure returns (bytes32 digestHash) {} - - function DOMAIN_TYPEHASH() external view returns (bytes32) {} - - function STAKER_DELEGATION_TYPEHASH() external view returns (bytes32) {} - - function DELEGATION_APPROVAL_TYPEHASH() external view returns (bytes32) {} - - function domainSeparator() external view returns (bytes32) {} - - function cumulativeWithdrawalsQueued( - address staker - ) external view returns (uint256) {} - - function calculateWithdrawalRoot( - Withdrawal memory withdrawal - ) external pure returns (bytes32) {} - - function operatorSaltIsSpent( - address avs, - bytes32 salt - ) external view returns (bool) {} + ) external virtual returns (bytes32[] memory withdrawalRoots) {} function queueWithdrawals( - QueuedWithdrawalParams[] calldata queuedWithdrawalParams - ) external returns (bytes32[] memory) {} + QueuedWithdrawalParams[] calldata params + ) external virtual returns (bytes32[] memory) {} + + function completeQueuedWithdrawals( + IERC20[][] calldata tokens, + bool[] calldata receiveAsTokens, + uint256 numToComplete + ) external virtual {} function completeQueuedWithdrawal( Withdrawal calldata withdrawal, IERC20[] calldata tokens, - uint256 middlewareTimesIndex, bool receiveAsTokens - ) external {} + ) external virtual {} function completeQueuedWithdrawals( Withdrawal[] calldata withdrawals, IERC20[][] calldata tokens, - uint256[] calldata middlewareTimesIndexes, bool[] calldata receiveAsTokens - ) external {} + ) external virtual {} - // onlyDelegationManager functions in StrategyManager - function addShares( - IStrategyManager strategyManager, + function increaseDelegatedShares( address staker, - IERC20 token, IStrategy strategy, - uint256 shares - ) external { - strategyManager.addShares(staker, strategy, token, shares); - } + uint256 existingDepositShares, + uint256 addedShares + ) external virtual {} - function removeShares( - IStrategyManager strategyManager, + function decreaseBeaconChainScalingFactor( address staker, - IStrategy strategy, - uint256 shares - ) external { - strategyManager.removeDepositShares(staker, strategy, shares); - } - - function withdrawSharesAsTokens( - IStrategyManager strategyManager, - address recipient, - IStrategy strategy, - uint256 shares, - IERC20 token - ) external { - strategyManager.withdrawSharesAsTokens(recipient, strategy, token, shares); - } + uint256 existingShares, + uint64 proportionOfOldBalance + ) external virtual {} - function registerAsOperator( - OperatorDetails calldata registeringOperatorDetails, - uint32 allocationDelay, - string calldata metadataURI - ) external override {} + function burnOperatorShares( + address operator, + IStrategy strategy, + uint64 prevMaxMagnitude, + uint64 newMaxMagnitude + ) external virtual {} function completeQueuedWithdrawal( Withdrawal calldata withdrawal, IERC20[] calldata tokens, + uint256 middlewareTimesIndex, bool receiveAsTokens - ) external override {} + ) external virtual {} function completeQueuedWithdrawals( Withdrawal[] calldata withdrawals, IERC20[][] calldata tokens, + uint256[] calldata middlewareTimesIndexes, bool[] calldata receiveAsTokens - ) external override {} + ) external virtual {} - function decreaseBeaconChainScalingFactor( - address staker, - uint256 existingDepositShares, - uint64 proportionOfOldBalance - ) external override {} + function delegatedTo( + address staker + ) external view virtual returns (address) {} - function decreaseOperatorShares( - address operator, - IStrategy strategy, - uint64 previousTotalMagnitude, - uint64 newTotalMagnitude - ) external override {} + function delegationApproverSaltIsSpent( + address _delegationApprover, + bytes32 salt + ) external view virtual returns (bool) {} - function increaseDelegatedShares( - address staker, - IStrategy strategy, - uint256 existingDepositShares, - uint256 addedShares - ) external override {} + function cumulativeWithdrawalsQueued( + address staker + ) external view virtual returns (uint256) {} - function initialize( - address initialOwner, - IPauserRegistry _pauserRegistry, - uint256 initialPausedStatus - ) external override {} + function isDelegated(address staker) external view virtual returns (bool) {} + + function isOperator(address operator) external view virtual returns (bool) {} + + function operatorDetails( + address operator + ) external view virtual returns (OperatorDetails memory) {} + + function delegationApprover( + address operator + ) external view virtual returns (address) {} + + function getOperatorShares( + address operator, + IStrategy[] memory strategies + ) external view virtual returns (uint256[] memory) {} function getOperatorsShares( address[] memory operators, IStrategy[] memory strategies - ) external view override returns (uint256[][] memory) {} + ) external view virtual returns (uint256[][] memory) {} + + function getSlashableSharesInQueue( + address operator, + IStrategy strategy + ) external view virtual returns (uint256) {} function getWithdrawableShares( address staker, IStrategy[] memory strategies - ) external view override returns (uint256[] memory withdrawableShares) {} + ) + external + view + virtual + override + returns ( + uint256[] memory withdrawableShares, + uint256[] memory depositShares + ) + {} function getDepositedShares( address staker - ) external view override returns (IStrategy[] memory, uint256[] memory) {} + ) external view virtual returns (IStrategy[] memory, uint256[] memory) {} - function getCompletableTimestamp( - uint32 startTimestamp - ) external view override returns (uint32 completableTimestamp) {} + function depositScalingFactor( + address staker, + IStrategy strategy + ) external view virtual returns (uint256) {} + + function getBeaconChainSlashingFactor( + address staker + ) external view virtual returns (uint64) {} + + function MIN_WITHDRAWAL_DELAY_BLOCKS() + external + view + virtual + override + returns (uint32) + {} + + function getQueuedWithdrawals( + address staker + ) + external + view + virtual + override + returns (Withdrawal[] memory withdrawals, uint256[][] memory shares) + {} + + function calculateWithdrawalRoot( + Withdrawal memory withdrawal + ) external pure virtual returns (bytes32) {} + + function calculateDelegationApprovalDigestHash( + address staker, + address operator, + address _delegationApprover, + bytes32 approverSalt, + uint256 expiry + ) external view virtual returns (bytes32) {} + + function beaconChainETHStrategy() + external + view + virtual + override + returns (IStrategy) + {} + + function DELEGATION_APPROVAL_TYPEHASH() + external + view + virtual + override + returns (bytes32) + {} + + function registerAsOperator( + address initDelegationApprover, + uint32 allocationDelay, + string calldata metadataURI + ) external virtual {} + + function modifyOperatorDetails( + address operator, + address newDelegationApprover + ) external virtual {} + + function updateOperatorMetadataURI( + address operator, + string calldata metadataURI + ) external virtual {} + + function redelegate( + address newOperator, + SignatureWithExpiry memory newOperatorApproverSig, + bytes32 approverSalt + ) external virtual returns (bytes32[] memory withdrawalRoots) {} + + function decreaseDelegatedShares( + address staker, + uint256 curDepositShares, + uint64 prevBeaconChainSlashingFactor, + uint256 wadSlashed + ) external virtual {} } + +contract DelegationMock is DelegationIntermediate { + mapping(address => bool) internal _isOperator; + mapping(address => mapping(IStrategy => uint256)) internal _weightOf; + function setOperatorShares(address operator, IStrategy strategy, uint256 actualWeight) external { + _weightOf[operator][strategy] = actualWeight; + } + + function setIsOperator(address operator, bool isOperator) external { + _isOperator[operator] = isOperator; + } + + function isOperator(address operator) external view override returns (bool) { + return _isOperator[operator]; + } + + function getOperatorShares( + address operator, + IStrategy[] calldata strategies + ) external view override returns (uint256[] memory) { + uint256[] memory shares = new uint256[](strategies.length); + for (uint256 i = 0; i < strategies.length; i++) { + shares[i] = _weightOf[operator][strategies[i]]; + } + return shares; + } + function minWithdrawalDelayBlocks() external view returns (uint32){ + return 100; + } +} \ No newline at end of file diff --git a/test/mocks/EigenPodManagerMock.sol b/test/mocks/EigenPodManagerMock.sol index afdd6189..cefd5b7a 100644 --- a/test/mocks/EigenPodManagerMock.sol +++ b/test/mocks/EigenPodManagerMock.sol @@ -11,8 +11,8 @@ contract EigenPodManagerMock is Test, Pausable, IEigenPodManager { mapping(address => int256) public podShares; - constructor(IPauserRegistry _pauserRegistry) { - _initializePauser(_pauserRegistry, 0); + constructor(IPauserRegistry _pauserRegistry) Pausable(_pauserRegistry){ + _setPausedStatus(0); } function podOwnerShares(address podOwner) external view returns (int256) { @@ -67,9 +67,6 @@ contract EigenPodManagerMock is Test, Pausable, IEigenPodManager { function beaconChainETHStrategy() external view returns (IStrategy) { } - function addShares(address staker, IStrategy strategy, IERC20 token, uint256 shares) external { - } - function removeDepositShares(address staker, IStrategy strategy, uint256 depositSharesToRemove) external { } @@ -77,4 +74,24 @@ contract EigenPodManagerMock is Test, Pausable, IEigenPodManager { } function withdrawSharesAsTokens(address staker, IStrategy strategy, IERC20 token, uint256 shares) external{} + + function addShares( + address staker, + IStrategy strategy, + IERC20 token, + uint256 shares + ) external returns (uint256, uint256) { + } + + function beaconChainSlashingFactor( + address staker + ) external view returns (uint64) { + } + + function recordBeaconChainETHBalanceUpdate( + address podOwner, + uint256 prevRestakedBalanceWei, + int256 balanceDeltaWei + ) external { + } } \ No newline at end of file diff --git a/test/mocks/PermissionControllerMock.sol b/test/mocks/PermissionControllerMock.sol new file mode 100644 index 00000000..7501a9fa --- /dev/null +++ b/test/mocks/PermissionControllerMock.sol @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.12; + +import {IPermissionController} from "eigenlayer-contracts/src/contracts/interfaces/IPermissionController.sol"; + +contract PermissionControllerIntermediate is IPermissionController { + function addPendingAdmin(address account, address admin) external virtual {} + + function removePendingAdmin( + address account, + address admin + ) external virtual {} + + function acceptAdmin(address account) external virtual {} + + function removeAdmin(address account, address admin) external virtual {} + + function setAppointee( + address account, + address appointee, + address target, + bytes4 selector + ) external virtual {} + + function removeAppointee( + address account, + address appointee, + address target, + bytes4 selector + ) external virtual {} + + function isAdmin( + address account, + address caller + ) external view virtual returns (bool) {} + + function isPendingAdmin( + address account, + address pendingAdmin + ) external view virtual returns (bool) {} + + function getAdmins( + address account + ) external view virtual returns (address[] memory) {} + + function getPendingAdmins( + address account + ) external view virtual returns (address[] memory) {} + + function canCall( + address account, + address caller, + address target, + bytes4 selector + ) external virtual returns (bool) {} + + function getAppointeePermissions( + address account, + address appointee + ) external virtual returns (address[] memory, bytes4[] memory) {} + + function getAppointees( + address account, + address target, + bytes4 selector + ) external virtual returns (address[] memory) {} +} + +contract PermissionControllerMock is PermissionControllerIntermediate { + mapping(address => mapping(address => mapping(address => mapping(bytes4 => bool)))) internal _canCall; + + function setCanCall( + address account, + address caller, + address target, + bytes4 selector + ) external { + _canCall[account][caller][target][selector] = true; + } + + function canCall( + address account, + address caller, + address target, + bytes4 selector + ) external override returns (bool) { + if (account == caller) return true; + return _canCall[account][caller][target][selector]; + } + +} diff --git a/test/mocks/RewardsCoordinatorMock.sol b/test/mocks/RewardsCoordinatorMock.sol index db403ac6..3a8e9622 100644 --- a/test/mocks/RewardsCoordinatorMock.sol +++ b/test/mocks/RewardsCoordinatorMock.sol @@ -8,126 +8,165 @@ import {IRewardsCoordinator} from "eigenlayer-contracts/src/contracts/interfaces import "./AVSDirectoryMock.sol"; contract RewardsCoordinatorMock is IRewardsCoordinator { - /// @notice The address of the entity that can update the contract with new merkle roots - function rewardsUpdater() external view returns (address) {} + function initialize( + address initialOwner, + uint256 initialPausedStatus, + address _rewardsUpdater, + uint32 _activationDelay, + uint16 _defaultSplitBips + ) external override {} - function CALCULATION_INTERVAL_SECONDS() external view returns (uint32) {} + function createAVSRewardsSubmission( + address avs, + RewardsSubmission[] calldata rewardsSubmissions + ) external override {} - function MAX_REWARDS_DURATION() external view returns (uint32) {} + function createRewardsForAllSubmission( + RewardsSubmission[] calldata rewardsSubmissions + ) external override {} - function MAX_RETROACTIVE_LENGTH() external view returns (uint32) {} + function createRewardsForAllEarners( + RewardsSubmission[] calldata rewardsSubmissions + ) external override {} + + function createOperatorDirectedAVSRewardsSubmission( + address avs, + OperatorDirectedRewardsSubmission[] + calldata operatorDirectedRewardsSubmissions + ) external override {} - function MAX_FUTURE_LENGTH() external view returns (uint32) {} + function processClaim( + RewardsMerkleClaim calldata claim, + address recipient + ) external override {} - function GENESIS_REWARDS_TIMESTAMP() external view returns (uint32) {} + function processClaims( + RewardsMerkleClaim[] calldata claims, + address recipient + ) external override {} - function activationDelay() external view returns (uint32) {} + function submitRoot( + bytes32 root, + uint32 rewardsCalculationEndTimestamp + ) external override {} + + function disableRoot(uint32 rootIndex) external override {} + + function setClaimerFor(address claimer) external override {} + + function setClaimerFor(address earner, address claimer) external override {} + + function setActivationDelay(uint32 _activationDelay) external override {} + + function setDefaultOperatorSplit(uint16 split) external override {} + + function setOperatorAVSSplit( + address operator, + address avs, + uint16 split + ) external override {} + + function setOperatorPISplit( + address operator, + uint16 split + ) external override {} + + function setRewardsUpdater(address _rewardsUpdater) external override {} + + function setRewardsForAllSubmitter( + address _submitter, + bool _newValue + ) external override {} + + function activationDelay() external view override returns (uint32) {} + + function currRewardsCalculationEndTimestamp() + external + view + override + returns (uint32) + {} - function claimerFor(address earner) external view returns (address) {} + function claimerFor( + address earner + ) external view override returns (address) {} function cumulativeClaimed( address claimer, IERC20 token - ) external view returns (uint256) {} + ) external view override returns (uint256) {} - function globalOperatorCommissionBips() external view returns (uint16) {} + function defaultOperatorSplitBips() external view override returns (uint16) {} - function operatorCommissionBips( + function getOperatorAVSSplit( address operator, address avs - ) external view returns (uint16) {} + ) external view override returns (uint16) {} + + function getOperatorPISplit( + address operator + ) external view override returns (uint16) {} function calculateEarnerLeafHash( EarnerTreeMerkleLeaf calldata leaf - ) external pure returns (bytes32) {} + ) external pure override returns (bytes32) {} function calculateTokenLeafHash( TokenTreeMerkleLeaf calldata leaf - ) external pure returns (bytes32) {} + ) external pure override returns (bytes32) {} function checkClaim( RewardsMerkleClaim calldata claim - ) external view returns (bool) {} + ) external view override returns (bool) {} - function currRewardsCalculationEndTimestamp() + function getDistributionRootsLength() external view - returns (uint32) + override + returns (uint256) {} - function getRootIndexFromHash( - bytes32 rootHash - ) external view returns (uint32) {} - - function getDistributionRootsLength() external view returns (uint256) {} - function getDistributionRootAtIndex( uint256 index - ) external view returns (DistributionRoot memory) {} + ) external view override returns (DistributionRoot memory) {} - function getCurrentClaimableDistributionRoot() + function getCurrentDistributionRoot() external view + override returns (DistributionRoot memory) {} - function getCurrentDistributionRoot() + function getCurrentClaimableDistributionRoot() external view + override returns (DistributionRoot memory) {} - /// EXTERNAL FUNCTIONS /// - - function disableRoot(uint32 rootIndex) external {} - - function createAVSRewardsSubmission( - RewardsSubmission[] calldata rewardsSubmissions - ) external {} - - function createRewardsForAllSubmission( - RewardsSubmission[] calldata rewardsSubmission - ) external {} - - function processClaim( - RewardsMerkleClaim calldata claim, - address recipient - ) external {} - - function submitRoot( - bytes32 root, - uint32 rewardsCalculationEndTimestamp - ) external {} - - function setRewardsUpdater(address _rewardsUpdater) external {} + function getRootIndexFromHash( + bytes32 rootHash + ) external view override returns (uint32) {} - function setActivationDelay(uint32 _activationDelay) external {} + function rewardsUpdater() external view override returns (address) {} - function setGlobalOperatorCommission(uint16 _globalCommissionBips) external {} + function CALCULATION_INTERVAL_SECONDS() + external + view + override + returns (uint32) + {} - function setClaimerFor(address claimer) external {} + function MAX_REWARDS_DURATION() external view override returns (uint32) {} - /** - * @notice Sets the permissioned `payAllForRangeSubmitter` address which can submit payAllForRange - * @dev Only callable by the contract owner - * @param _submitter The address of the payAllForRangeSubmitter - * @param _newValue The new value for isPayAllForRangeSubmitter - */ - function setRewardsForAllSubmitter( - address _submitter, - bool _newValue - ) external {} + function MAX_RETROACTIVE_LENGTH() external view override returns (uint32) {} - function createRewardsForAllEarners( - RewardsSubmission[] calldata rewardsSubmissions - ) external override {} + function MAX_FUTURE_LENGTH() external view override returns (uint32) {} - function initialize( - address initialOwner, - IPauserRegistry _pauserRegistry, - uint256 initialPausedStatus, - address _rewardsUpdater, - uint32 _activationDelay, - uint16 _globalCommissionBips - ) external {} + function GENESIS_REWARDS_TIMESTAMP() + external + view + override + returns (uint32) + {} } \ No newline at end of file diff --git a/test/unit/AVSRegistrar.t.sol b/test/unit/AVSRegistrar.t.sol new file mode 100644 index 00000000..deea28a2 --- /dev/null +++ b/test/unit/AVSRegistrar.t.sol @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +import {MockAVSDeployer} from "../utils/MockAVSDeployer.sol"; +import {BN254} from "../../src/libraries/BN254.sol"; +import {IRegistryCoordinator} from "../../src/interfaces/IRegistryCoordinator.sol"; +import {IStakeRegistry} from "../../src/interfaces/IStakeRegistry.sol"; +import {BitmapUtils} from "../../src/libraries/BitmapUtils.sol"; +import {IAVSRegistrar} from "eigenlayer-contracts/src/contracts/interfaces/IAVSRegistrar.sol"; +import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; +import {IAllocationManagerTypes} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; +import {AVSRegistrarMock} from "../mocks/AVSRegistrarMock.sol"; +import {console2 as console} from "forge-std/Test.sol"; + +contract AVSRegistrarTest is MockAVSDeployer { + using BN254 for BN254.G1Point; + + AVSRegistrarMock public avsRegistrarMock; + address internal operator = address(420); + + function setUp() public virtual { + _deployMockEigenLayerAndAVS(); + avsRegistrarMock = new AVSRegistrarMock(); + } + + function testSetAVSRegistrar() public { + vm.prank(address(serviceManager)); + allocationManager.setAVSRegistrar(address(serviceManager), IAVSRegistrar(address(avsRegistrarMock))); + assertEq(address(allocationManager.getAVSRegistrar(address(serviceManager))), address(avsRegistrarMock)); + } + + function testRegisterOperator() public { + // Set up AVS registrar + vm.prank(address(serviceManager)); + allocationManager.setAVSRegistrar(address(serviceManager), IAVSRegistrar(address(avsRegistrarMock))); + + // Create operator set + uint32 operatorSetId = 1; + IAllocationManagerTypes.CreateSetParams[] memory createSetParams = new IAllocationManagerTypes.CreateSetParams[](1); + createSetParams[0] = IAllocationManagerTypes.CreateSetParams({ + operatorSetId: operatorSetId, + strategies: new IStrategy[](0) + }); + + // Create operator set + vm.prank(address(serviceManager)); + allocationManager.createOperatorSets(address(serviceManager), createSetParams); + + // Set up registration params + uint32[] memory operatorSetIds = new uint32[](1); + operatorSetIds[0] = operatorSetId; + bytes memory emptyBytes; + + delegationMock.setIsOperator(operator, true); + + // Register operator + vm.prank(operator); + allocationManager.registerForOperatorSets( + address(operator), + IAllocationManagerTypes.RegisterParams(address(serviceManager), operatorSetIds, emptyBytes) + ); + } + + function testRegisterOperator_RevertsIfNotOperator() public { + vm.prank(address(serviceManager)); + allocationManager.setAVSRegistrar(address(serviceManager), IAVSRegistrar(address(avsRegistrarMock))); + + // Create operator set + uint32 operatorSetId = 1; + IAllocationManagerTypes.CreateSetParams[] memory createSetParams = new IAllocationManagerTypes.CreateSetParams[](1); + createSetParams[0] = IAllocationManagerTypes.CreateSetParams({ + operatorSetId: operatorSetId, + strategies: new IStrategy[](0) + }); + + // Create operator set + vm.prank(address(serviceManager)); + allocationManager.createOperatorSets(address(serviceManager), createSetParams); + + // Set up registration params + uint32[] memory operatorSetIds = new uint32[](1); + operatorSetIds[0] = operatorSetId; + bytes memory emptyBytes; + + delegationMock.setIsOperator(operator, false); + + // Register operator + vm.prank(operator); + + vm.expectRevert(); + allocationManager.registerForOperatorSets( + address(operator), + IAllocationManagerTypes.RegisterParams(address(serviceManager), operatorSetIds, emptyBytes) + ); + } + function testAllocationManagerDeployed() public { + assertTrue(address(allocationManager) != address(0), "AllocationManager not deployed"); + assertTrue(address(allocationManagerImplementation) != address(0), "AllocationManager implementation not deployed"); + } +} diff --git a/test/unit/RegistryCoordinatorMigration.t.sol b/test/unit/RegistryCoordinatorMigration.t.sol deleted file mode 100644 index 7b5a6b14..00000000 --- a/test/unit/RegistryCoordinatorMigration.t.sol +++ /dev/null @@ -1,470 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; - -import "@openzeppelin/contracts/token/ERC20/presets/ERC20PresetFixedSupply.sol"; -import { - RewardsCoordinator, - IRewardsCoordinator, - IRewardsCoordinatorTypes, - IERC20 -} from "eigenlayer-contracts/src/contracts/core/RewardsCoordinator.sol"; -import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; -import {StrategyBase} from "eigenlayer-contracts/src/contracts/strategies/StrategyBase.sol"; -import {IServiceManagerBaseEvents} from "../events/IServiceManagerBaseEvents.sol"; -import {IAVSDirectoryTypes} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; -import {IStrategyManager} from "eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol"; -import {AVSDirectoryHarness} from "../harnesses/AVSDirectoryHarness.sol"; -import {OperatorSet} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; - -import "../utils/MockAVSDeployer.sol"; - -contract RegistryCoordinatorMigrationUnit is MockAVSDeployer, IServiceManagerBaseEvents { - // RewardsCoordinator config - address rewardsUpdater = address(uint160(uint256(keccak256("rewardsUpdater")))); - uint32 CALCULATION_INTERVAL_SECONDS = 7 days; - uint32 MAX_REWARDS_DURATION = 70 days; - uint32 MAX_RETROACTIVE_LENGTH = 84 days; - uint32 MAX_FUTURE_LENGTH = 28 days; - uint32 GENESIS_REWARDS_TIMESTAMP = 1_712_188_800; - uint256 MAX_REWARDS_AMOUNT = 1e38 - 1; - /// @notice Delay in timestamp before a posted root can be claimed against - uint32 activationDelay = 7 days; - /// @notice the commission for all operators across all avss - uint16 globalCommissionBips = 1000; - - // Testing Config and Mocks - address serviceManagerOwner; - IERC20[] rewardTokens; - uint256 mockTokenInitialSupply = 10e50; - IStrategy strategyMock1; - IStrategy strategyMock2; - IStrategy strategyMock3; - StrategyBase strategyImplementation; - IRewardsCoordinator.StrategyAndMultiplier[] defaultStrategyAndMultipliers; - AVSDirectoryHarness avsDirectoryHarness; - - // mapping to setting fuzzed inputs - mapping(address => bool) public addressIsExcludedFromFuzzedInputs; - - modifier filterFuzzedAddressInputs(address fuzzedAddress) { - cheats.assume(!addressIsExcludedFromFuzzedInputs[fuzzedAddress]); - _; - } - - function setUp() public virtual { - numQuorums = maxQuorumsToRegisterFor; - _deployMockEigenLayerAndAVS(); - - serviceManagerImplementation = new ServiceManagerMock( - avsDirectory, - IRewardsCoordinator(address(rewardsCoordinatorMock)), - registryCoordinator, - stakeRegistry, - allocationManager - ); - avsDirectoryHarness = new AVSDirectoryHarness(delegationMock); - - serviceManagerImplementation = new ServiceManagerMock( - avsDirectory, - rewardsCoordinatorMock, - registryCoordinator, - stakeRegistry, - allocationManager - ); - /// Needed to upgrade to a service manager that points to an AVS Directory that can track state - vm.prank(proxyAdmin.owner()); - proxyAdmin.upgrade( - TransparentUpgradeableProxy(payable(address(serviceManager))), - address(serviceManagerImplementation) - ); - - serviceManagerOwner = serviceManager.owner(); - - _setUpDefaultStrategiesAndMultipliers(); - - addressIsExcludedFromFuzzedInputs[address(pauserRegistry)] = true; - addressIsExcludedFromFuzzedInputs[address(proxyAdmin)] = true; - } - - function _setUpDefaultStrategiesAndMultipliers() internal virtual { - // Deploy Mock Strategies - IERC20 token1 = new ERC20PresetFixedSupply( - "dog wif hat", "MOCK1", mockTokenInitialSupply, address(this) - ); - IERC20 token2 = - new ERC20PresetFixedSupply("jeo boden", "MOCK2", mockTokenInitialSupply, address(this)); - IERC20 token3 = new ERC20PresetFixedSupply( - "pepe wif avs", "MOCK3", mockTokenInitialSupply, address(this) - ); - strategyImplementation = new StrategyBase(IStrategyManager(address(strategyManagerMock))); - strategyMock1 = StrategyBase( - address( - new TransparentUpgradeableProxy( - address(strategyImplementation), - address(proxyAdmin), - abi.encodeWithSelector(StrategyBase.initialize.selector, token1, pauserRegistry) - ) - ) - ); - strategyMock2 = StrategyBase( - address( - new TransparentUpgradeableProxy( - address(strategyImplementation), - address(proxyAdmin), - abi.encodeWithSelector(StrategyBase.initialize.selector, token2, pauserRegistry) - ) - ) - ); - strategyMock3 = StrategyBase( - address( - new TransparentUpgradeableProxy( - address(strategyImplementation), - address(proxyAdmin), - abi.encodeWithSelector(StrategyBase.initialize.selector, token3, pauserRegistry) - ) - ) - ); - IStrategy[] memory strategies = new IStrategy[](3); - strategies[0] = strategyMock1; - strategies[1] = strategyMock2; - strategies[2] = strategyMock3; - strategies = _sortArrayAsc(strategies); - - strategyManagerMock.setStrategyWhitelist(strategies[0], true); - strategyManagerMock.setStrategyWhitelist(strategies[1], true); - strategyManagerMock.setStrategyWhitelist(strategies[2], true); - - defaultStrategyAndMultipliers.push( - IRewardsCoordinatorTypes.StrategyAndMultiplier(IStrategy(address(strategies[0])), 1e18) - ); - defaultStrategyAndMultipliers.push( - IRewardsCoordinatorTypes.StrategyAndMultiplier(IStrategy(address(strategies[1])), 2e18) - ); - defaultStrategyAndMultipliers.push( - IRewardsCoordinatorTypes.StrategyAndMultiplier(IStrategy(address(strategies[2])), 3e18) - ); - } - - /// @dev Sort to ensure that the array is in ascending order for strategies - function _sortArrayAsc(IStrategy[] memory arr) internal pure returns (IStrategy[] memory) { - uint256 l = arr.length; - for (uint256 i = 0; i < l; i++) { - for (uint256 j = i + 1; j < l; j++) { - if (address(arr[i]) > address(arr[j])) { - IStrategy temp = arr[i]; - arr[i] = arr[j]; - arr[j] = temp; - } - } - } - return arr; - } - - function test_migrateToOperatorSets() public { - (uint32[] memory operatorSetsToCreate, uint32[][] memory operatorSetIdsToMigrate, address[] memory operators) = serviceManager.getOperatorsToMigrate(); - cheats.startPrank(serviceManagerOwner); - serviceManager.migrateAndCreateOperatorSetIds(operatorSetsToCreate); - serviceManager.migrateToOperatorSets(operatorSetIdsToMigrate, operators); - cheats.stopPrank(); - - assertTrue(avsDirectory.isOperatorSetAVS(address(serviceManager)), "AVS is not an operator set AVS"); - } - - - - function test_createQuorum() public { - (uint32[] memory operatorSetsToCreate, uint32[][] memory operatorSetIdsToMigrate, address[] memory operators) = serviceManager.getOperatorsToMigrate(); - cheats.startPrank(serviceManagerOwner); - serviceManager.migrateAndCreateOperatorSetIds(operatorSetsToCreate); - serviceManager.migrateToOperatorSets(operatorSetIdsToMigrate, operators); - cheats.stopPrank(); - - assertTrue(avsDirectory.isOperatorSetAVS(address(serviceManager)), "AVS is not an operator set AVS"); - - uint8 quorumNumber = registryCoordinator.quorumCount(); - uint96 minimumStake = 1000; - IRegistryCoordinator.OperatorSetParam memory operatorSetParams = IRegistryCoordinator.OperatorSetParam({ - maxOperatorCount: 10, - kickBIPsOfOperatorStake: 50, - kickBIPsOfTotalStake: 2 - }); - IStakeRegistry.StrategyParams[] memory strategyParams = new IStakeRegistry.StrategyParams[](1); - strategyParams[0] = - IStakeRegistry.StrategyParams({ - strategy: IStrategy(address(1000)), - multiplier: 1e16 - }); - - assertFalse(avsDirectory.isOperatorSet(address(serviceManager), quorumNumber), "Operator set already existed"); - assertTrue(avsDirectory.isOperatorSet(address(serviceManager), quorumNumber-1), "Operator set doesn't already existed"); - - vm.prank(registryCoordinator.owner()); - registryCoordinator.createTotalDelegatedStakeQuorum(operatorSetParams, minimumStake, strategyParams); - - assertTrue(avsDirectory.isOperatorSet(address(serviceManager), quorumNumber), "Operator set was not created for the quorum"); - - } - - function test_updateOperatorsForQuorumsAfterDirectUnregister() public { - vm.prank(proxyAdmin.owner()); - proxyAdmin.upgrade( - TransparentUpgradeableProxy(payable(address(avsDirectory))), - address(avsDirectoryMock) - ); - uint256 pseudoRandomNumber = uint256(keccak256("pseudoRandomNumber")); - _registerRandomOperators(pseudoRandomNumber); - - vm.prank(proxyAdmin.owner()); - proxyAdmin.upgrade( - TransparentUpgradeableProxy(payable(address(avsDirectory))), - address(avsDirectoryHarness) - ); - - uint256 quorumCount = registryCoordinator.quorumCount(); - for (uint256 i = 0; i < quorumCount; i++) { - uint256 operatorCount = indexRegistry.totalOperatorsForQuorum(uint8(i)); - bytes32[] memory operatorIds = - indexRegistry.getOperatorListAtBlockNumber(uint8(i), uint32(block.number)); - assertEq(operatorCount, operatorIds.length, "Operator Id length mismatch"); // sanity check - for (uint256 j = 0; j < operatorCount; j++) { - address operatorAddress = - registryCoordinator.blsApkRegistry().getOperatorFromPubkeyHash(operatorIds[j]); - AVSDirectoryHarness(address(avsDirectory)).setAvsOperatorStatus( - address(serviceManager), - operatorAddress, - IAVSDirectoryTypes.OperatorAVSRegistrationStatus.REGISTERED - ); - } - } - - ( - uint32[] memory operatorSetsToCreate, - uint32[][] memory operatorSetIdsToMigrate, - address[] memory operators - ) = serviceManager.getOperatorsToMigrate(); - cheats.startPrank(serviceManagerOwner); - serviceManager.migrateAndCreateOperatorSetIds(operatorSetsToCreate); - serviceManager.migrateToOperatorSets(operatorSetIdsToMigrate, operators); - cheats.stopPrank(); - - bytes32[] memory registeredOperators = indexRegistry.getOperatorListAtBlockNumber(defaultQuorumNumber, uint32(block.number)); - uint256 preNumOperators = registeredOperators.length; - address[] memory registeredOperatorAddresses = new address[](registeredOperators.length); - for (uint256 i = 0; i < registeredOperators.length; i++) { - registeredOperatorAddresses[i] = registryCoordinator.blsApkRegistry().pubkeyHashToOperator(registeredOperators[i]); - } - - uint32[] memory operatorSetsToUnregister = new uint32[](1); - operatorSetsToUnregister[0] = defaultQuorumNumber; - - vm.prank(operators[0]); - avsDirectory.forceDeregisterFromOperatorSets( - operators[0], - address(serviceManager), - operatorSetsToUnregister, - ISignatureUtils.SignatureWithSaltAndExpiry({ - signature: new bytes(0), - salt: bytes32(0), - expiry: 0 - }) - ); - // sanity check if the operator was unregistered from the intended operator set - bool operatorIsUnRegistered = !avsDirectory.isMember(operators[0], OperatorSet({ - avs: address(serviceManager), - operatorSetId: defaultQuorumNumber - })); - bool isOperatorSetAVS = avsDirectory.isOperatorSetAVS(address(serviceManager)); - assertTrue(isOperatorSetAVS, "ServiceManager is not an operator set AVS"); - assertTrue(operatorIsUnRegistered, "Operator wasnt unregistered from op set"); - - address[][] memory registeredOperatorAddresses2D = new address[][](1); - registeredOperatorAddresses2D[0] = registeredOperatorAddresses; - bytes memory quorumNumbers = new bytes(1); - quorumNumbers[0] = bytes1(defaultQuorumNumber); - registryCoordinator.updateOperatorsForQuorum(registeredOperatorAddresses2D, quorumNumbers); - - registeredOperators = indexRegistry.getOperatorListAtBlockNumber(defaultQuorumNumber, uint32(block.number)); - uint256 postRegisteredOperators = registeredOperators.length; - - assertEq(preNumOperators-1, postRegisteredOperators, ""); - - } - - function test_deregister_afterMigration() public { - vm.prank(proxyAdmin.owner()); - proxyAdmin.upgrade( - TransparentUpgradeableProxy(payable(address(avsDirectory))), - address(avsDirectoryMock) - ); - uint256 pseudoRandomNumber = uint256(keccak256("pseudoRandomNumber")); - _registerRandomOperators(pseudoRandomNumber); - - vm.prank(proxyAdmin.owner()); - proxyAdmin.upgrade( - TransparentUpgradeableProxy(payable(address(avsDirectory))), - address(avsDirectoryHarness) - ); - - uint256 quorumCount = registryCoordinator.quorumCount(); - for (uint256 i = 0; i < quorumCount; i++) { - uint256 operatorCount = indexRegistry.totalOperatorsForQuorum(uint8(i)); - bytes32[] memory operatorIds = - indexRegistry.getOperatorListAtBlockNumber(uint8(i), uint32(block.number)); - assertEq(operatorCount, operatorIds.length, "Operator Id length mismatch"); // sanity check - for (uint256 j = 0; j < operatorCount; j++) { - address operatorAddress = - registryCoordinator.blsApkRegistry().getOperatorFromPubkeyHash(operatorIds[j]); - AVSDirectoryHarness(address(avsDirectory)).setAvsOperatorStatus( - address(serviceManager), - operatorAddress, - IAVSDirectoryTypes.OperatorAVSRegistrationStatus.REGISTERED - ); - } - } - - ( - uint32[] memory operatorSetsToCreate, - uint32[][] memory operatorSetIdsToMigrate, - address[] memory operators - ) = serviceManager.getOperatorsToMigrate(); - cheats.startPrank(serviceManagerOwner); - serviceManager.migrateAndCreateOperatorSetIds(operatorSetsToCreate); - serviceManager.migrateToOperatorSets(operatorSetIdsToMigrate, operators); - cheats.stopPrank(); - - bytes32[] memory registeredOperators = indexRegistry.getOperatorListAtBlockNumber(defaultQuorumNumber, uint32(block.number)); - address[] memory registeredOperatorAddresses = new address[](registeredOperators.length); - for (uint256 i = 0; i < registeredOperators.length; i++) { - registeredOperatorAddresses[i] = registryCoordinator.blsApkRegistry().pubkeyHashToOperator(registeredOperators[i]); - } - - uint32[] memory operatorSetsToUnregister = new uint32[](1); - operatorSetsToUnregister[0] = defaultQuorumNumber; - - address operatorToDeregister = operators[0]; - - bool isOperatorRegistered = avsDirectory.isMember(operatorToDeregister, OperatorSet({ - avs: address(serviceManager), - operatorSetId: defaultQuorumNumber - })); - bool isOperatorSetAVS = avsDirectory.isOperatorSetAVS(address(serviceManager)); - // sanity check if the operator was registered from the intended operator set - assertTrue(isOperatorSetAVS, "ServiceManager is not an operator set AVS"); - assertTrue(isOperatorRegistered, "Operator wasnt unregistered from op set"); - - bytes memory quorumNumbers = new bytes(1); - quorumNumbers[0] = bytes1(defaultQuorumNumber); - cheats.startPrank(operatorToDeregister); - registryCoordinator.deregisterOperator(quorumNumbers); - cheats.stopPrank(); - - isOperatorRegistered = avsDirectory.isMember(operatorToDeregister, OperatorSet({ - avs: address(serviceManager), - operatorSetId: defaultQuorumNumber - })); - assertFalse(isOperatorRegistered, "Operator wasn't deregistered from operator set"); - } - - function test_register_afterMigration() public { - vm.prank(proxyAdmin.owner()); - proxyAdmin.upgrade( - TransparentUpgradeableProxy(payable(address(avsDirectory))), - address(avsDirectoryMock) - ); - uint256 pseudoRandomNumber = uint256(keccak256("pseudoRandomNumber")); - _registerRandomOperators(pseudoRandomNumber); - - vm.prank(proxyAdmin.owner()); - proxyAdmin.upgrade( - TransparentUpgradeableProxy(payable(address(avsDirectory))), - address(avsDirectoryHarness) - ); - - uint256 quorumCount = registryCoordinator.quorumCount(); - for (uint256 i = 0; i < quorumCount; i++) { - uint256 operatorCount = indexRegistry.totalOperatorsForQuorum(uint8(i)); - bytes32[] memory operatorIds = - indexRegistry.getOperatorListAtBlockNumber(uint8(i), uint32(block.number)); - assertEq(operatorCount, operatorIds.length, "Operator Id length mismatch"); // sanity check - for (uint256 j = 0; j < operatorCount; j++) { - address operatorAddress = - registryCoordinator.blsApkRegistry().getOperatorFromPubkeyHash(operatorIds[j]); - AVSDirectoryHarness(address(avsDirectory)).setAvsOperatorStatus( - address(serviceManager), - operatorAddress, - IAVSDirectoryTypes.OperatorAVSRegistrationStatus.REGISTERED - ); - } - } - - ( - uint32[] memory operatorSetsToCreate, - uint32[][] memory operatorSetIdsToMigrate, - address[] memory operators - ) = serviceManager.getOperatorsToMigrate(); - cheats.startPrank(serviceManagerOwner); - serviceManager.migrateAndCreateOperatorSetIds(operatorSetsToCreate); - serviceManager.migrateToOperatorSets(operatorSetIdsToMigrate, operators); - cheats.stopPrank(); - - bytes32[] memory registeredOperators = indexRegistry.getOperatorListAtBlockNumber(defaultQuorumNumber, uint32(block.number)); - address[] memory registeredOperatorAddresses = new address[](registeredOperators.length); - for (uint256 i = 0; i < registeredOperators.length; i++) { - registeredOperatorAddresses[i] = registryCoordinator.blsApkRegistry().pubkeyHashToOperator(registeredOperators[i]); - } - - uint32[] memory operatorSetsToRegisterFor = new uint32[](1); - operatorSetsToRegisterFor[0] = defaultQuorumNumber; - - uint256 operatorPk = uint256(keccak256("operator to register")); - address operatorToRegister = vm.addr(operatorPk) ; - - bool isOperatorRegistered = avsDirectory.isMember(operatorToRegister, OperatorSet({ - avs: address(serviceManager), - operatorSetId: defaultQuorumNumber - })); - bool isOperatorSetAVS = avsDirectory.isOperatorSetAVS(address(serviceManager)); - // sanity check if the operator was registered from the intended operator set - assertTrue(isOperatorSetAVS, "ServiceManager is not an operator set AVS"); - assertTrue(!isOperatorRegistered, "Operator wasnt unregistered from op set"); - - IDelegationManager.OperatorDetails memory details; - - cheats.startPrank(operatorToRegister); - delegationMock.registerAsOperator(details, "your_metadata_URI_here"); - cheats.stopPrank(); - - delegationMock.setIsOperator(operatorToRegister, true); - - bytes memory quorumNumbers = new bytes(1); - IBLSApkRegistry.PubkeyRegistrationParams memory params; - ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature; - quorumNumbers[0] = bytes1(defaultQuorumNumber); - bytes32 typeHash = avsDirectory.calculateOperatorSetRegistrationDigestHash( - address(serviceManager), - operatorSetsToRegisterFor, - keccak256(abi.encodePacked("operator registration salt")), - block.timestamp + 1 days - ); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(operatorPk, typeHash); - operatorSignature = ISignatureUtils.SignatureWithSaltAndExpiry({ - signature: abi.encodePacked(r, s, v), - salt: keccak256(abi.encodePacked("operator registration salt")), - expiry: block.timestamp + 1 days - }); - - blsApkRegistry.setBLSPublicKey(operatorToRegister, defaultPubKey); - delegationMock.setOperatorShares(operatorToRegister, IStrategy(address(0)), 100 ether); - cheats.startPrank(operatorToRegister); - registryCoordinator.registerOperator(quorumNumbers, "", params, operatorSignature); - cheats.stopPrank(); - - isOperatorRegistered = avsDirectory.isMember(operatorToRegister, OperatorSet({ - avs: address(serviceManager), - operatorSetId: defaultQuorumNumber - })); - assertTrue(isOperatorRegistered, "Operator wasn't deregistered from operator set"); - } - - -} diff --git a/test/unit/RegistryCoordinatorUnit.t.sol b/test/unit/RegistryCoordinatorUnit.t.sol index d6931dc4..47eb1724 100644 --- a/test/unit/RegistryCoordinatorUnit.t.sol +++ b/test/unit/RegistryCoordinatorUnit.t.sol @@ -126,7 +126,6 @@ contract RegistryCoordinatorUnitTests_Initialization_Setters is RegistryCoordina registryCoordinatorOwner, churnApprover, ejector, - pauserRegistry, 0/*initialPausedStatus*/, operatorSetParams, new uint96[](0), diff --git a/test/unit/ServiceManagerBase.t.sol b/test/unit/ServiceManagerBase.t.sol index 609e8d2b..c7ac91e9 100644 --- a/test/unit/ServiceManagerBase.t.sol +++ b/test/unit/ServiceManagerBase.t.sol @@ -8,6 +8,7 @@ import { IRewardsCoordinatorTypes, IERC20 } from "eigenlayer-contracts/src/contracts/core/RewardsCoordinator.sol"; +import {PermissionController} from "eigenlayer-contracts/src/contracts/permissions/PermissionController.sol"; import {StrategyBase} from "eigenlayer-contracts/src/contracts/strategies/StrategyBase.sol"; import {IStrategyManager} from "eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol"; import {IServiceManagerBaseEvents} from "../events/IServiceManagerBaseEvents.sol"; @@ -56,6 +57,9 @@ contract ServiceManagerBase_UnitTests is MockAVSDeployer, IServiceManagerBaseEve rewardsCoordinatorImplementation = new RewardsCoordinator( delegationMock, IStrategyManager(address(strategyManagerMock)), + allocationManagerMock, + pauserRegistry, + permissionControllerMock, CALCULATION_INTERVAL_SECONDS, MAX_REWARDS_DURATION, MAX_RETROACTIVE_LENGTH, @@ -71,7 +75,6 @@ contract ServiceManagerBase_UnitTests is MockAVSDeployer, IServiceManagerBaseEve abi.encodeWithSelector( RewardsCoordinator.initialize.selector, msg.sender, - pauserRegistry, 0, /*initialPausedStatus*/ rewardsUpdater, activationDelay, @@ -146,7 +149,7 @@ contract ServiceManagerBase_UnitTests is MockAVSDeployer, IServiceManagerBaseEve IERC20 token3 = new ERC20PresetFixedSupply( "pepe wif avs", "MOCK3", mockTokenInitialSupply, address(this) ); - strategyImplementation = new StrategyBase(IStrategyManager(address(strategyManagerMock))); + strategyImplementation = new StrategyBase(IStrategyManager(address(strategyManagerMock)), pauserRegistry); strategyMock1 = StrategyBase( address( new TransparentUpgradeableProxy( @@ -248,7 +251,7 @@ contract ServiceManagerBase_UnitTests is MockAVSDeployer, IServiceManagerBaseEve serviceManager.createAVSRewardsSubmission(rewardsSubmissions); } - function test_createAVSRewardsSubmission_SingleSubmission( + function testFuzz_createAVSRewardsSubmission_SingleSubmission( uint256 startTimestamp, uint256 duration, uint256 amount @@ -325,7 +328,7 @@ contract ServiceManagerBase_UnitTests is MockAVSDeployer, IServiceManagerBaseEve ); } - function test_createAVSRewardsSubmission_MultipleSubmissions( + function testFuzz_createAVSRewardsSubmission_MultipleSubmissions( uint256 startTimestamp, uint256 duration, uint256 amount, @@ -418,7 +421,7 @@ contract ServiceManagerBase_UnitTests is MockAVSDeployer, IServiceManagerBaseEve } } - function test_createAVSRewardsSubmission_MultipleSubmissionsSingleToken( + function testFuzz_createAVSRewardsSubmission_MultipleSubmissionsSingleToken( uint256 startTimestamp, uint256 duration, uint256 amount, diff --git a/test/unit/ServiceManagerMigration.t.sol b/test/unit/ServiceManagerMigration.t.sol deleted file mode 100644 index 5fd86846..00000000 --- a/test/unit/ServiceManagerMigration.t.sol +++ /dev/null @@ -1,348 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; - -import "@openzeppelin/contracts/token/ERC20/presets/ERC20PresetFixedSupply.sol"; -import { - RewardsCoordinator, - IRewardsCoordinator, - IRewardsCoordinatorTypes, - IERC20 -} from "eigenlayer-contracts/src/contracts/core/RewardsCoordinator.sol"; -import {StrategyBase} from "eigenlayer-contracts/src/contracts/strategies/StrategyBase.sol"; -import {IServiceManagerBaseEvents} from "../events/IServiceManagerBaseEvents.sol"; -import {IAVSDirectoryTypes} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; -import {IStrategyManager} from "eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol"; -import {AVSDirectoryHarness} from "../harnesses/AVSDirectoryHarness.sol"; -import {OperatorSet} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; - -import "../utils/MockAVSDeployer.sol"; - -contract ServiceManagerMigration_UnitTests is MockAVSDeployer, IServiceManagerBaseEvents { - // RewardsCoordinator config - address rewardsUpdater = address(uint160(uint256(keccak256("rewardsUpdater")))); - uint32 CALCULATION_INTERVAL_SECONDS = 7 days; - uint32 MAX_REWARDS_DURATION = 70 days; - uint32 MAX_RETROACTIVE_LENGTH = 84 days; - uint32 MAX_FUTURE_LENGTH = 28 days; - uint32 GENESIS_REWARDS_TIMESTAMP = 1_712_188_800; - uint256 MAX_REWARDS_AMOUNT = 1e38 - 1; - uint32 OPERATOR_SET_GENESIS_REWARDS_TIMESTAMP = 0; - /// TODO: what values should these have - uint32 OPERATOR_SET_MAX_RETROACTIVE_LENGTH = 0; - /// TODO: What values these should have - /// @notice Delay in timestamp before a posted root can be claimed against - uint32 activationDelay = 7 days; - /// @notice the commission for all operators across all avss - uint16 globalCommissionBips = 1000; - - // Testing Config and Mocks - address serviceManagerOwner; - address rewardsInitiator = address(uint160(uint256(keccak256("rewardsInitiator")))); - IERC20[] rewardTokens; - uint256 mockTokenInitialSupply = 10e50; - IStrategy strategyMock1; - IStrategy strategyMock2; - IStrategy strategyMock3; - StrategyBase strategyImplementation; - IRewardsCoordinator.StrategyAndMultiplier[] defaultStrategyAndMultipliers; - AVSDirectoryHarness avsDirectoryHarness; - - // mapping to setting fuzzed inputs - mapping(address => bool) public addressIsExcludedFromFuzzedInputs; - - modifier filterFuzzedAddressInputs(address fuzzedAddress) { - cheats.assume(!addressIsExcludedFromFuzzedInputs[fuzzedAddress]); - _; - } - - function setUp() public virtual { - numQuorums = maxQuorumsToRegisterFor; - _deployMockEigenLayerAndAVS(); - - avsDirectoryHarness = new AVSDirectoryHarness(delegationMock); - // Deploy rewards coordinator - rewardsCoordinatorImplementation = new RewardsCoordinator( - delegationMock, - IStrategyManager(address(strategyManagerMock)), - CALCULATION_INTERVAL_SECONDS, - MAX_REWARDS_DURATION, - MAX_RETROACTIVE_LENGTH, - MAX_FUTURE_LENGTH, - GENESIS_REWARDS_TIMESTAMP - ); - - rewardsCoordinator = RewardsCoordinator( - address( - new TransparentUpgradeableProxy( - address(rewardsCoordinatorImplementation), - address(proxyAdmin), - abi.encodeWithSelector( - RewardsCoordinator.initialize.selector, - msg.sender, - pauserRegistry, - 0, /*initialPausedStatus*/ - rewardsUpdater, - activationDelay, - globalCommissionBips - ) - ) - ) - ); - // Deploy ServiceManager - serviceManagerImplementation = new ServiceManagerMock( - avsDirectory, rewardsCoordinator, registryCoordinator, stakeRegistry, allocationManager - ); - - serviceManager = ServiceManagerMock( - address( - new TransparentUpgradeableProxy( - address(serviceManagerImplementation), - address(proxyAdmin), - abi.encodeWithSelector( - ServiceManagerMock.initialize.selector, serviceManager.owner(), msg.sender, msg.sender - ) - ) - ) - ); - - serviceManagerOwner = serviceManager.owner(); - cheats.prank(serviceManagerOwner); - serviceManager.setRewardsInitiator(rewardsInitiator); - - _setUpDefaultStrategiesAndMultipliers(); - - cheats.warp(GENESIS_REWARDS_TIMESTAMP + 2 weeks); - - addressIsExcludedFromFuzzedInputs[address(pauserRegistry)] = true; - addressIsExcludedFromFuzzedInputs[address(proxyAdmin)] = true; - } - - function _setUpDefaultStrategiesAndMultipliers() internal virtual { - // Deploy Mock Strategies - IERC20 token1 = new ERC20PresetFixedSupply( - "dog wif hat", "MOCK1", mockTokenInitialSupply, address(this) - ); - IERC20 token2 = - new ERC20PresetFixedSupply("jeo boden", "MOCK2", mockTokenInitialSupply, address(this)); - IERC20 token3 = new ERC20PresetFixedSupply( - "pepe wif avs", "MOCK3", mockTokenInitialSupply, address(this) - ); - strategyImplementation = new StrategyBase(IStrategyManager(address(strategyManagerMock))); - strategyMock1 = StrategyBase( - address( - new TransparentUpgradeableProxy( - address(strategyImplementation), - address(proxyAdmin), - abi.encodeWithSelector(StrategyBase.initialize.selector, token1, pauserRegistry) - ) - ) - ); - strategyMock2 = StrategyBase( - address( - new TransparentUpgradeableProxy( - address(strategyImplementation), - address(proxyAdmin), - abi.encodeWithSelector(StrategyBase.initialize.selector, token2, pauserRegistry) - ) - ) - ); - strategyMock3 = StrategyBase( - address( - new TransparentUpgradeableProxy( - address(strategyImplementation), - address(proxyAdmin), - abi.encodeWithSelector(StrategyBase.initialize.selector, token3, pauserRegistry) - ) - ) - ); - IStrategy[] memory strategies = new IStrategy[](3); - strategies[0] = strategyMock1; - strategies[1] = strategyMock2; - strategies[2] = strategyMock3; - strategies = _sortArrayAsc(strategies); - - strategyManagerMock.setStrategyWhitelist(strategies[0], true); - strategyManagerMock.setStrategyWhitelist(strategies[1], true); - strategyManagerMock.setStrategyWhitelist(strategies[2], true); - - defaultStrategyAndMultipliers.push( - IRewardsCoordinatorTypes.StrategyAndMultiplier(IStrategy(address(strategies[0])), 1e18) - ); - defaultStrategyAndMultipliers.push( - IRewardsCoordinatorTypes.StrategyAndMultiplier(IStrategy(address(strategies[1])), 2e18) - ); - defaultStrategyAndMultipliers.push( - IRewardsCoordinatorTypes.StrategyAndMultiplier(IStrategy(address(strategies[2])), 3e18) - ); - } - - /// @dev Sort to ensure that the array is in ascending order for strategies - function _sortArrayAsc(IStrategy[] memory arr) internal pure returns (IStrategy[] memory) { - uint256 l = arr.length; - for (uint256 i = 0; i < l; i++) { - for (uint256 j = i + 1; j < l; j++) { - if (address(arr[i]) > address(arr[j])) { - IStrategy temp = arr[i]; - arr[i] = arr[j]; - arr[j] = temp; - } - } - } - return arr; - } - - function test_viewFunction(uint256 randomValue) public { - _registerRandomOperators(randomValue); - ( - uint32[] memory operatorSetsToCreate, - uint32[][] memory operatorSetIdsToMigrate, - address[] memory operators - ) = serviceManager.getOperatorsToMigrate(); - - // Assert that all operators are in quorum 0 invariant of _registerRandomOperators - for (uint256 i = 0; i < operators.length; i++) { - bytes32 operatorId = registryCoordinator.getOperatorId(operators[i]); - uint192 operatorBitmap = registryCoordinator.getCurrentQuorumBitmap(operatorId); - assertTrue(operatorId != bytes32(0), "Operator was registered"); - assertTrue(operatorBitmap & 1 == 1, "Operator is not registered in quorum 0"); - } - - // Assert we are migrating all the quorums that existed - uint256 quorumCount = registryCoordinator.quorumCount(); - assertEq(quorumCount, operatorSetsToCreate.length, "Operator sets to create incorrect"); - } - - function test_migrateToOperatorSets() public { - ( - uint32[] memory operatorSetsToCreate, - uint32[][] memory operatorSetIdsToMigrate, - address[] memory operators - ) = serviceManager.getOperatorsToMigrate(); - cheats.startPrank(serviceManagerOwner); - serviceManager.migrateAndCreateOperatorSetIds(operatorSetsToCreate); - serviceManager.migrateToOperatorSets(operatorSetIdsToMigrate, operators); - cheats.stopPrank(); - - assertTrue( - avsDirectory.isOperatorSetAVS(address(serviceManager)), "AVS is not an operator set AVS" - ); - } - - function test_migrateTwoTransactions() public { - ( - uint32[] memory operatorSetsToCreate, - uint32[][] memory operatorSetIdsToMigrate, - address[] memory operators - ) = serviceManager.getOperatorsToMigrate(); - // Split the operatorSetIdsToMigrate and operators into two separate sets - uint256 halfLength = operatorSetIdsToMigrate.length / 2; - - uint32[][] memory firstHalfOperatorSetIds = new uint32[][](halfLength); - uint32[][] memory secondHalfOperatorSetIds = - new uint32[][](operatorSetIdsToMigrate.length - halfLength); - address[] memory firstHalfOperators = new address[](halfLength); - address[] memory secondHalfOperators = new address[](operators.length - halfLength); - - for (uint256 i = 0; i < halfLength; i++) { - firstHalfOperatorSetIds[i] = operatorSetIdsToMigrate[i]; - firstHalfOperators[i] = operators[i]; - } - - for (uint256 i = halfLength; i < operatorSetIdsToMigrate.length; i++) { - secondHalfOperatorSetIds[i - halfLength] = operatorSetIdsToMigrate[i]; - secondHalfOperators[i - halfLength] = operators[i]; - } - - // Migrate the first half - cheats.startPrank(serviceManagerOwner); - serviceManager.migrateAndCreateOperatorSetIds(operatorSetsToCreate); - serviceManager.migrateToOperatorSets(firstHalfOperatorSetIds, firstHalfOperators); - serviceManager.migrateToOperatorSets(secondHalfOperatorSetIds, secondHalfOperators); - cheats.stopPrank(); - - assertTrue( - avsDirectory.isOperatorSetAVS(address(serviceManager)), "AVS is not an operator set AVS" - ); - } - - function test_migrateToOperatorSets_revert_alreadyMigrated() public { - ( - uint32[] memory operatorSetsToCreate, - uint32[][] memory operatorSetIdsToMigrate, - address[] memory operators - ) = serviceManager.getOperatorsToMigrate(); - cheats.startPrank(serviceManagerOwner); - serviceManager.migrateAndCreateOperatorSetIds(operatorSetsToCreate); - serviceManager.migrateToOperatorSets(operatorSetIdsToMigrate, operators); - serviceManager.finalizeMigration(); - - vm.expectRevert(); - - /// TODO: Now that it's not 1 step, we should have a way to signal completion - serviceManager.migrateToOperatorSets(operatorSetIdsToMigrate, operators); - - cheats.stopPrank(); - } - - function test_migrateToOperatorSets_revert_notOwner() public { - ( - uint32[] memory operatorSetsToCreate, - uint32[][] memory operatorSetIdsToMigrate, - address[] memory operators - ) = serviceManager.getOperatorsToMigrate(); - cheats.startPrank(serviceManagerOwner); - serviceManager.migrateAndCreateOperatorSetIds(operatorSetsToCreate); - cheats.stopPrank(); - address caller = address(uint160(uint256(keccak256("caller")))); - cheats.expectRevert("Ownable: caller is not the owner"); - cheats.prank(caller); - serviceManager.migrateToOperatorSets(operatorSetIdsToMigrate, operators); - } - - function test_migrateToOperatorSets_verify() public { - uint256 pseudoRandomNumber = uint256(keccak256("pseudoRandomNumber")); - _registerRandomOperators(pseudoRandomNumber); - - vm.prank(proxyAdmin.owner()); - proxyAdmin.upgrade( - TransparentUpgradeableProxy(payable(address(avsDirectory))), - address(avsDirectoryHarness) - ); - - uint256 quorumCount = registryCoordinator.quorumCount(); - for (uint256 i = 0; i < quorumCount; i++) { - uint256 operatorCount = indexRegistry.totalOperatorsForQuorum(uint8(i)); - bytes32[] memory operatorIds = - indexRegistry.getOperatorListAtBlockNumber(uint8(i), uint32(block.number)); - assertEq(operatorCount, operatorIds.length, "Operator Id length mismatch"); // sanity check - for (uint256 j = 0; j < operatorCount; j++) { - address operatorAddress = - registryCoordinator.blsApkRegistry().getOperatorFromPubkeyHash(operatorIds[j]); - AVSDirectoryHarness(address(avsDirectory)).setAvsOperatorStatus( - address(serviceManager), - operatorAddress, - IAVSDirectoryTypes.OperatorAVSRegistrationStatus.REGISTERED - ); - } - } - - ( - uint32[] memory operatorSetsToCreate, - uint32[][] memory operatorSetIdsToMigrate, - address[] memory operators - ) = serviceManager.getOperatorsToMigrate(); - cheats.startPrank(serviceManagerOwner); - serviceManager.migrateAndCreateOperatorSetIds(operatorSetsToCreate); - serviceManager.migrateToOperatorSets(operatorSetIdsToMigrate, operators); - cheats.stopPrank(); - - /// quick check, this operator is in operator set 3 - assertTrue( - avsDirectory.isMember( - 0x73e2Ce949f15Be901f76b54F5a4554A6C8DCf539, - OperatorSet(address(serviceManager), uint32(3)) - ), - "Operator not migrated to operator set" - ); - } -} diff --git a/test/unit/StakeRegistryUnit.t.sol b/test/unit/StakeRegistryUnit.t.sol index 4b74194a..6fbe8609 100644 --- a/test/unit/StakeRegistryUnit.t.sol +++ b/test/unit/StakeRegistryUnit.t.sol @@ -49,7 +49,8 @@ contract StakeRegistryUnitTests is MockAVSDeployer, IStakeRegistryEvents { stakeRegistry, IBLSApkRegistry(blsApkRegistry), IIndexRegistry(indexRegistry), - IAVSDirectory(avsDirectory) + IAVSDirectory(avsDirectory), + pauserRegistry ); stakeRegistryImplementation = new StakeRegistryHarness( @@ -2171,7 +2172,7 @@ contract StakeRegistryUnitTests_weightOfOperatorForQuorum is StakeRegistryUnitTe * successfully and return a value for weightOfOperatorForQuorum. Fuzz test sets the operator shares * and asserts that the summed weight of the operator is correct. */ - function test_weightOfOperatorForQuorum( + function testFuzz_weightOfOperatorForQuorum( address operator, uint96[] memory multipliers, uint96[] memory shares @@ -2226,7 +2227,7 @@ contract StakeRegistryUnitTests_weightOfOperatorForQuorum is StakeRegistryUnitTe } /// @dev consider multipliers for 3 strategies - function test_weightOfOperatorForQuorum_3Strategies( + function testFuzz_weightOfOperatorForQuorum_3Strategies( address operator, uint96[3] memory shares ) public { diff --git a/test/unit/UpgradeableProxyLib.sol b/test/unit/UpgradeableProxyLib.sol new file mode 100644 index 00000000..15fd49d8 --- /dev/null +++ b/test/unit/UpgradeableProxyLib.sol @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.0; +import {Vm} from "forge-std/Vm.sol"; +import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; +import {TransparentUpgradeableProxy} from + "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; + +contract EmptyContract { +} + +library UpgradeableProxyLib { + bytes32 internal constant IMPLEMENTATION_SLOT = + 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; + bytes32 internal constant ADMIN_SLOT = + 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; + Vm internal constant vm = Vm(address(uint160(uint256(keccak256("hevm cheat code"))))); + function deployProxyAdmin() internal returns (address) { + return address(new ProxyAdmin()); + } + function setUpEmptyProxy( + address admin + ) internal returns (address) { + address emptyContract = address(new EmptyContract()); + return address(new TransparentUpgradeableProxy(emptyContract, admin, "")); + } + function upgrade(address proxy, address impl) internal { + ProxyAdmin admin = getProxyAdmin(proxy); + admin.upgrade(TransparentUpgradeableProxy(payable(proxy)), impl); + } + function upgradeAndCall(address proxy, address impl, bytes memory initData) internal { + ProxyAdmin admin = getProxyAdmin(proxy); + admin.upgradeAndCall(TransparentUpgradeableProxy(payable(proxy)), impl, initData); + } + function getImplementation( + address proxy + ) internal view returns (address) { + bytes32 value = vm.load(proxy, IMPLEMENTATION_SLOT); + return address(uint160(uint256(value))); + } + function getProxyAdmin( + address proxy + ) internal view returns (ProxyAdmin) { + bytes32 value = vm.load(proxy, ADMIN_SLOT); + return ProxyAdmin(address(uint160(uint256(value)))); + } +} \ No newline at end of file diff --git a/test/unit/Utils.sol b/test/unit/Utils.sol index 0947b1d4..c143633d 100644 --- a/test/unit/Utils.sol +++ b/test/unit/Utils.sol @@ -7,7 +7,7 @@ contract Utils { address constant dummyAdmin = address(uint160(uint256(keccak256("DummyAdmin")))); function deployNewStrategy(IERC20 token, IStrategyManager strategyManager, IPauserRegistry pauserRegistry, address admin) public returns (StrategyBase) { - StrategyBase newStrategy = new StrategyBase(strategyManager); + StrategyBase newStrategy = new StrategyBase(strategyManager, pauserRegistry); newStrategy = StrategyBase( address( new TransparentUpgradeableProxy( @@ -17,7 +17,7 @@ contract Utils { ) ) ); - newStrategy.initialize(token, pauserRegistry); + newStrategy.initialize(token); return newStrategy; } } diff --git a/test/utils/BLSMockAVSDeployer.sol b/test/utils/BLSMockAVSDeployer.sol index 3f5286a3..14339d55 100644 --- a/test/utils/BLSMockAVSDeployer.sol +++ b/test/utils/BLSMockAVSDeployer.sol @@ -130,8 +130,8 @@ contract BLSMockAVSDeployer is MockAVSDeployer { OperatorStateRetriever.CheckSignaturesIndices memory checkSignaturesIndices = operatorStateRetriever.getCheckSignaturesIndices( registryCoordinator, - referenceBlockNumber, - quorumNumbers, + referenceBlockNumber, + quorumNumbers, nonSignerOperatorIds ); diff --git a/test/utils/CoreDeployLib.sol b/test/utils/CoreDeployLib.sol new file mode 100644 index 00000000..d88f6ff6 --- /dev/null +++ b/test/utils/CoreDeployLib.sol @@ -0,0 +1,271 @@ +// // SPDX-License-Identifier: UNLICENSED +// pragma solidity ^0.8.0; + +// import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; +// import {TransparentUpgradeableProxy} from +// "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +// import {UpgradeableBeacon} from "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol"; +// import {DelegationManager} from "eigenlayer-contracts/src/contracts/core/DelegationManager.sol"; +// import {StrategyManager} from "eigenlayer-contracts/src/contracts/core/StrategyManager.sol"; +// import {AVSDirectory} from "eigenlayer-contracts/src/contracts/core/AVSDirectory.sol"; +// import {EigenPodManager} from "eigenlayer-contracts/src/contracts/pods/EigenPodManager.sol"; +// import {RewardsCoordinator} from "eigenlayer-contracts/src/contracts/core/RewardsCoordinator.sol"; +// import {StrategyBase} from "eigenlayer-contracts/src/contracts/strategies/StrategyBase.sol"; +// import {EigenPod} from "eigenlayer-contracts/src/contracts/pods/EigenPod.sol"; +// import {IETHPOSDeposit} from "eigenlayer-contracts/src/contracts/interfaces/IETHPOSDeposit.sol"; +// import {StrategyBaseTVLLimits} from "eigenlayer-contracts/src/contracts/strategies/StrategyBaseTVLLimits.sol"; +// import {PauserRegistry} from "eigenlayer-contracts/src/contracts/permissions/PauserRegistry.sol"; +// import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; +// import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +// import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; +// import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; +// import {IBeacon} from "@openzeppelin/contracts/proxy/beacon/IBeacon.sol"; +// import {IStrategyManager} from "eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol"; +// import {IEigenPodManager} from "eigenlayer-contracts/src/contracts/interfaces/IEigenPodManager.sol"; +// import {IAVSDirectory} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; +// import {IPauserRegistry} from "eigenlayer-contracts/src/contracts/interfaces/IPauserRegistry.sol"; +// import {StrategyFactory} from "eigenlayer-contracts/src/contracts/strategies/StrategyFactory.sol"; + +// import {UpgradeableProxyLib} from "../unit/UpgradeableProxyLib.sol"; + +// library CoreDeploymentLib { +// using UpgradeableProxyLib for address; + +// struct StrategyManagerConfig { +// uint256 initPausedStatus; +// uint256 initWithdrawalDelayBlocks; +// } + +// struct DelegationManagerConfig { +// uint256 initPausedStatus; +// IStrategy[] strategies; +// uint256 minWithdrawalDelayBlocks; +// uint256[] withdrawalDelayBlocks; + +// } + +// struct EigenPodManagerConfig { +// uint256 initPausedStatus; +// } + +// struct RewardsCoordinatorConfig { +// uint256 initPausedStatus; +// uint256 maxRewardsDuration; +// uint256 maxRetroactiveLength; +// uint256 maxFutureLength; +// uint256 genesisRewardsTimestamp; +// address updater; +// uint256 activationDelay; +// uint256 calculationIntervalSeconds; +// uint256 globalOperatorCommissionBips; +// } + +// struct StrategyFactoryConfig { +// uint256 initPausedStatus; +// } + +// struct DeploymentConfigData { +// StrategyManagerConfig strategyManager; +// DelegationManagerConfig delegationManager; +// EigenPodManagerConfig eigenPodManager; +// RewardsCoordinatorConfig rewardsCoordinator; +// StrategyFactoryConfig strategyFactory; +// } + +// struct DeploymentData { +// address delegationManager; +// address avsDirectory; +// address strategyManager; +// address eigenPodManager; +// address rewardsCoordinator; +// address eigenPodBeacon; +// address pauserRegistry; +// address strategyFactory; +// address strategyBeacon; +// } + +// function deployContracts( +// address proxyAdmin, +// DeploymentConfigData memory configData +// ) internal returns (DeploymentData memory) { +// DeploymentData memory result; + +// result.delegationManager = UpgradeableProxyLib.setUpEmptyProxy(proxyAdmin); +// result.avsDirectory = UpgradeableProxyLib.setUpEmptyProxy(proxyAdmin); +// result.strategyManager = UpgradeableProxyLib.setUpEmptyProxy(proxyAdmin); +// result.eigenPodManager = UpgradeableProxyLib.setUpEmptyProxy(proxyAdmin); +// result.rewardsCoordinator = UpgradeableProxyLib.setUpEmptyProxy(proxyAdmin); +// result.eigenPodBeacon = UpgradeableProxyLib.setUpEmptyProxy(proxyAdmin); +// result.pauserRegistry = UpgradeableProxyLib.setUpEmptyProxy(proxyAdmin); +// result.strategyFactory = UpgradeableProxyLib.setUpEmptyProxy(proxyAdmin); + +// // Deploy the implementation contracts, using the proxy contracts as inputs +// address delegationManagerImpl = address( +// new DelegationManager( +// IStrategyManager(result.strategyManager), +// IEigenPodManager(result.eigenPodManager) +// ) +// ); +// address avsDirectoryImpl = +// address(new AVSDirectory(IDelegationManager(result.delegationManager))); + +// address strategyManagerImpl = address( +// new StrategyManager( +// IDelegationManager(result.delegationManager), +// IEigenPodManager(result.eigenPodManager) +// ) +// ); + +// address strategyFactoryImpl = +// address(new StrategyFactory(IStrategyManager(result.strategyManager))); + +// address ethPOSDeposit; +// if (block.chainid == 1) { +// ethPOSDeposit = 0x00000000219ab540356cBB839Cbe05303d7705Fa; +// } else { +// // For non-mainnet chains, you might want to deploy a mock or read from a config +// // This assumes you have a similar config setup as in M2_Deploy_From_Scratch.s.sol +// /// TODO: Handle Eth pos +// } + +// address eigenPodManagerImpl = address( +// new EigenPodManager( +// IETHPOSDeposit(ethPOSDeposit), +// IBeacon(result.eigenPodBeacon), +// IStrategyManager(result.strategyManager), +// IDelegationManager(result.delegationManager) +// ) +// ); + +// /// TODO: Get actual values +// uint32 CALCULATION_INTERVAL_SECONDS = 1 days; +// uint32 MAX_REWARDS_DURATION = 1 days; +// uint32 MAX_RETROACTIVE_LENGTH = 1; +// uint32 MAX_FUTURE_LENGTH = 1; +// uint32 GENESIS_REWARDS_TIMESTAMP = 10 days; +// address rewardsCoordinatorImpl = address( +// new RewardsCoordinator( +// IDelegationManager(result.delegationManager), +// IStrategyManager(result.strategyManager), +// CALCULATION_INTERVAL_SECONDS, +// MAX_REWARDS_DURATION, +// MAX_RETROACTIVE_LENGTH, +// MAX_FUTURE_LENGTH, +// GENESIS_REWARDS_TIMESTAMP +// ) +// ); + +// /// TODO: Get actual genesis time +// uint64 GENESIS_TIME = 1_564_000; + +// address eigenPodImpl = address( +// new EigenPod( +// IETHPOSDeposit(ethPOSDeposit), +// IEigenPodManager(result.eigenPodManager), +// GENESIS_TIME +// ) +// ); +// address eigenPodBeaconImpl = address(new UpgradeableBeacon(eigenPodImpl)); +// address baseStrategyImpl = +// address(new StrategyBase(IStrategyManager(result.strategyManager))); +// /// TODO: PauserRegistry isn't upgradeable +// address pauserRegistryImpl = address( +// new PauserRegistry( +// new address[](0), // Empty array for pausers +// proxyAdmin // ProxyAdmin as the unpauser +// ) +// ); + +// // Deploy and configure the strategy beacon +// result.strategyBeacon = address(new UpgradeableBeacon(baseStrategyImpl)); + +// // Upgrade contracts +// /// TODO: Get from config +// bytes memory upgradeCall = abi.encodeWithSelector( /// TODO: Fix abi.encodeCall was failing Cannot implicitly convert component at position 4 from "IStrategy[]" to "IStrategy[]" +// DelegationManager.initialize.selector, +// proxyAdmin, // initialOwner +// IPauserRegistry(result.pauserRegistry), // _pauserRegistry +// configData.delegationManager.initPausedStatus, // initialPausedStatus +// configData.delegationManager.minWithdrawalDelayBlocks, // _minWithdrawalDelayBlocks +// configData.delegationManager.strategies, // _strategies +// configData.delegationManager.withdrawalDelayBlocks // _withdrawalDelayBlocks +// ); +// UpgradeableProxyLib.upgradeAndCall( +// result.delegationManager, delegationManagerImpl, upgradeCall +// ); + +// // Upgrade StrategyManager contract +// upgradeCall = abi.encodeCall( +// StrategyManager.initialize, +// ( +// proxyAdmin, // initialOwner +// result.strategyFactory, // initialStrategyWhitelister +// IPauserRegistry(result.pauserRegistry), // _pauserRegistry +// configData.strategyManager.initPausedStatus // initialPausedStatus +// ) +// ); +// UpgradeableProxyLib.upgradeAndCall(result.strategyManager, strategyManagerImpl, upgradeCall); + +// // Upgrade StrategyFactory contract +// upgradeCall = abi.encodeCall( +// StrategyFactory.initialize, +// ( +// proxyAdmin, // initialOwner +// IPauserRegistry(result.pauserRegistry), // _pauserRegistry +// configData.strategyFactory.initPausedStatus, // initialPausedStatus +// IBeacon(result.strategyBeacon) +// ) +// ); +// UpgradeableProxyLib.upgradeAndCall(result.strategyFactory, strategyFactoryImpl, upgradeCall); + +// // Upgrade EigenPodManager contract +// upgradeCall = abi.encodeCall( +// EigenPodManager.initialize, +// ( +// proxyAdmin, // initialOwner +// IPauserRegistry(result.pauserRegistry), // _pauserRegistry +// configData.eigenPodManager.initPausedStatus // initialPausedStatus +// ) +// ); +// UpgradeableProxyLib.upgradeAndCall(result.eigenPodManager, eigenPodManagerImpl, upgradeCall); + +// // Upgrade AVSDirectory contract +// upgradeCall = abi.encodeCall( +// AVSDirectory.initialize, +// ( +// proxyAdmin, // initialOwner +// IPauserRegistry(result.pauserRegistry), // _pauserRegistry +// 0 // TODO: AVS Missing configinitialPausedStatus +// ) +// ); +// UpgradeableProxyLib.upgradeAndCall(result.avsDirectory, avsDirectoryImpl, upgradeCall); + +// // Upgrade RewardsCoordinator contract +// upgradeCall = abi.encodeCall( +// RewardsCoordinator.initialize, +// ( +// proxyAdmin, // initialOwner +// IPauserRegistry(result.pauserRegistry), // _pauserRegistry +// configData.rewardsCoordinator.initPausedStatus, // initialPausedStatus +// /// TODO: is there a setter and is this expected? +// address(0), // rewards updater +// uint32(configData.rewardsCoordinator.activationDelay), // _activationDelay +// uint16(configData.rewardsCoordinator.globalOperatorCommissionBips) // _globalCommissionBips +// ) +// ); +// UpgradeableProxyLib.upgradeAndCall( +// result.rewardsCoordinator, rewardsCoordinatorImpl, upgradeCall +// ); + +// // Upgrade EigenPod contract +// upgradeCall = abi.encodeCall( +// EigenPod.initialize, +// // TODO: Double check this +// (address(result.eigenPodManager)) // _podOwner +// ); +// UpgradeableProxyLib.upgradeAndCall(result.eigenPodBeacon, eigenPodImpl, upgradeCall); + +// return result; +// } + +// } \ No newline at end of file diff --git a/test/utils/MockAVSDeployer.sol b/test/utils/MockAVSDeployer.sol index 19a08295..226502ed 100644 --- a/test/utils/MockAVSDeployer.sol +++ b/test/utils/MockAVSDeployer.sol @@ -32,8 +32,11 @@ import {AVSDirectory} from "eigenlayer-contracts/src/contracts/core/AVSDirectory import {IAVSDirectory} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; import {RewardsCoordinatorMock} from "../mocks/RewardsCoordinatorMock.sol"; +import {PermissionControllerMock} from "../mocks/PermissionControllerMock.sol"; import {RewardsCoordinator} from "eigenlayer-contracts/src/contracts/core/RewardsCoordinator.sol"; +import {PermissionController} from "eigenlayer-contracts/src/contracts/permissions/PermissionController.sol"; +import {AllocationManager} from "eigenlayer-contracts/src/contracts/core/AllocationManager.sol"; import {IRewardsCoordinator} from "eigenlayer-contracts/src/contracts/interfaces/IRewardsCoordinator.sol"; @@ -59,7 +62,6 @@ contract MockAVSDeployer is Test { IBLSApkRegistry public blsApkRegistryImplementation; IIndexRegistry public indexRegistryImplementation; ServiceManagerMock public serviceManagerImplementation; - AllocationManagerMock public allocationManagerImplementation; OperatorStateRetriever public operatorStateRetriever; RegistryCoordinatorHarness public registryCoordinator; @@ -67,7 +69,6 @@ contract MockAVSDeployer is Test { BLSApkRegistryHarness public blsApkRegistry; IIndexRegistry public indexRegistry; ServiceManagerMock public serviceManager; - AllocationManagerMock public allocationManager; StrategyManagerMock public strategyManagerMock; DelegationMock public delegationMock; @@ -76,9 +77,12 @@ contract MockAVSDeployer is Test { AVSDirectory public avsDirectoryImplementation; AVSDirectoryMock public avsDirectoryMock; AllocationManagerMock public allocationManagerMock; + AllocationManager public allocationManager; + AllocationManager public allocationManagerImplementation; RewardsCoordinator public rewardsCoordinator; RewardsCoordinator public rewardsCoordinatorImplementation; RewardsCoordinatorMock public rewardsCoordinatorMock; + PermissionControllerMock public permissionControllerMock; /// @notice StakeRegistry, Constant used as a divisor in calculating weights. uint256 public constant WEIGHTING_DIVISOR = 1e18; @@ -135,24 +139,20 @@ contract MockAVSDeployer is Test { function _deployMockEigenLayerAndAVS(uint8 numQuorumsToAdd) internal { emptyContract = new EmptyContract(); - defaultOperatorId = defaultPubKey.hashG1Point(); cheats.startPrank(proxyAdminOwner); proxyAdmin = new ProxyAdmin(); - address[] memory pausers = new address[](1); pausers[0] = pauser; pauserRegistry = new PauserRegistry(pausers, unpauser); - delegationMock = new DelegationMock(); avsDirectoryMock = new AVSDirectoryMock(); eigenPodManagerMock = new EigenPodManagerMock(pauserRegistry); - strategyManagerMock = new StrategyManagerMock(); + strategyManagerMock = new StrategyManagerMock(delegationMock); allocationManagerMock = new AllocationManagerMock(); - avsDirectoryMock = new AVSDirectoryMock(); - allocationManagerMock = new AllocationManagerMock(); - avsDirectoryImplementation = new AVSDirectory(delegationMock, 0); // TODO: config value + permissionControllerMock = new PermissionControllerMock(); + avsDirectoryImplementation = new AVSDirectory(delegationMock, pauserRegistry); // TODO: config value avsDirectory = AVSDirectory( address( new TransparentUpgradeableProxy( @@ -160,15 +160,13 @@ contract MockAVSDeployer is Test { address(proxyAdmin), abi.encodeWithSelector( AVSDirectory.initialize.selector, - msg.sender, - pauserRegistry, - 0 /*initialPausedStatus*/ + msg.sender, // initialOwner + 0 // initialPausedStatus ) ) ) ); rewardsCoordinatorMock = new RewardsCoordinatorMock(); - strategyManagerMock.setDelegationManager(delegationMock); cheats.stopPrank(); @@ -178,58 +176,49 @@ contract MockAVSDeployer is Test { new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") ) ); - stakeRegistry = StakeRegistryHarness( address( new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") ) ); - indexRegistry = IndexRegistry( address( new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") ) ); - blsApkRegistry = BLSApkRegistryHarness( address( new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") ) ); - serviceManager = ServiceManagerMock( address( new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") ) ); - - allocationManager = AllocationManagerMock( + allocationManager = AllocationManager( address( new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") ) ); - cheats.stopPrank(); cheats.startPrank(proxyAdminOwner); stakeRegistryImplementation = new StakeRegistryHarness(IRegistryCoordinator(registryCoordinator), delegationMock, avsDirectory, serviceManager); - proxyAdmin.upgrade( TransparentUpgradeableProxy(payable(address(stakeRegistry))), address(stakeRegistryImplementation) ); blsApkRegistryImplementation = new BLSApkRegistryHarness(registryCoordinator); - proxyAdmin.upgrade( TransparentUpgradeableProxy(payable(address(blsApkRegistry))), address(blsApkRegistryImplementation) ); indexRegistryImplementation = new IndexRegistry(registryCoordinator); - proxyAdmin.upgrade( TransparentUpgradeableProxy(payable(address(indexRegistry))), address(indexRegistryImplementation) @@ -242,14 +231,18 @@ contract MockAVSDeployer is Test { stakeRegistry, allocationManager ); - proxyAdmin.upgrade( TransparentUpgradeableProxy(payable(address(serviceManager))), address(serviceManagerImplementation) ); - allocationManagerImplementation = new AllocationManagerMock(); - + allocationManagerImplementation = new AllocationManager( + delegationMock, + pauserRegistry, + permissionControllerMock, + uint32(7 days), // DEALLOCATION_DELAY + uint32(1 days) // ALLOCATION_CONFIGURATION_DELAY + ); proxyAdmin.upgrade( TransparentUpgradeableProxy(payable(address(allocationManager))), address(allocationManagerImplementation) @@ -281,7 +274,7 @@ contract MockAVSDeployer is Test { } registryCoordinatorImplementation = new RegistryCoordinatorHarness( - serviceManager, stakeRegistry, blsApkRegistry, indexRegistry, avsDirectory + serviceManager, stakeRegistry, blsApkRegistry, indexRegistry, avsDirectory, pauserRegistry ); { delete operatorSetParams; @@ -309,20 +302,20 @@ contract MockAVSDeployer is Test { proxyAdmin.upgradeAndCall( TransparentUpgradeableProxy(payable(address(registryCoordinator))), address(registryCoordinatorImplementation), - abi.encodeWithSelector( - RegistryCoordinator.initialize.selector, - registryCoordinatorOwner, - churnApprover, - ejector, - pauserRegistry, - 0, /*initialPausedStatus*/ - operatorSetParams, - minimumStakeForQuorum, - quorumStrategiesConsideredAndMultipliers, - quorumStakeTypes, - slashableStakeQuorumLookAheadPeriods - ) - ); + abi.encodeCall( + RegistryCoordinator.initialize, + ( + registryCoordinatorOwner, // _initialOwner + churnApprover, // _churnApprover + ejector, // _ejector + 0, // _initialPausedStatus + operatorSetParams, // _operatorSetParams + minimumStakeForQuorum, // _minimumStakes + quorumStrategiesConsideredAndMultipliers, // _strategyParams + quorumStakeTypes, // _stakeTypes + slashableStakeQuorumLookAheadPeriods // _lookAheadPeriods + ) + )); } operatorStateRetriever = new OperatorStateRetriever(); @@ -330,6 +323,31 @@ contract MockAVSDeployer is Test { cheats.stopPrank(); } + function _labelContracts() internal { + vm.label(address(emptyContract), "EmptyContract"); + vm.label(address(proxyAdmin), "ProxyAdmin"); + vm.label(address(pauserRegistry), "PauserRegistry"); + vm.label(address(delegationMock), "DelegationMock"); + vm.label(address(avsDirectoryMock), "AVSDirectoryMock"); + vm.label(address(eigenPodManagerMock), "EigenPodManagerMock"); + vm.label(address(strategyManagerMock), "StrategyManagerMock"); + vm.label(address(allocationManagerMock), "AllocationManagerMock"); + vm.label(address(avsDirectoryImplementation), "AVSDirectoryImplementation"); + vm.label(address(avsDirectory), "AVSDirectory"); + vm.label(address(rewardsCoordinatorMock), "RewardsCoordinatorMock"); + vm.label(address(registryCoordinator), "RegistryCoordinator"); + vm.label(address(stakeRegistry), "StakeRegistry"); + vm.label(address(indexRegistry), "IndexRegistry"); + vm.label(address(blsApkRegistry), "BLSApkRegistry"); + vm.label(address(serviceManager), "ServiceManager"); + vm.label(address(allocationManager), "AllocationManager"); + vm.label(address(stakeRegistryImplementation), "StakeRegistryImplementation"); + vm.label(address(blsApkRegistryImplementation), "BLSApkRegistryImplementation"); + vm.label(address(indexRegistryImplementation), "IndexRegistryImplementation"); + vm.label(address(serviceManagerImplementation), "ServiceManagerImplementation"); + vm.label(address(allocationManagerImplementation), "AllocationManagerImplementation"); + } + /** * @notice registers operator with coordinator */ From dbd59ddb1ed8920fdae77c0bb6bc948970073827 Mon Sep 17 00:00:00 2001 From: steven <12021290+stevennevins@users.noreply.github.com> Date: Mon, 9 Dec 2024 13:05:46 -0500 Subject: [PATCH 26/27] feat: registration changes part 2 * chore: add note * fix: remove handling of forceDeregistration * fix: fix total delegated stake usage * fix: integration tests * test: fix remaining integration tests * test: add back log check * test: add additional tests for transition to operator sets * test: add more test cases * feat: record m2 quorums on migration * chore: add note about churn support * fix: prevent operator set registration changes for m2 quorums * feat: require strings * chore: add dev note and add require string --- src/RegistryCoordinator.sol | 105 ++-- src/RegistryCoordinatorStorage.sol | 11 +- src/ServiceManagerBase.sol | 11 + src/StakeRegistry.sol | 16 +- src/interfaces/IRegistryCoordinator.sol | 12 +- src/interfaces/IServiceManager.sol | 9 + src/interfaces/IStakeRegistry.sol | 1 - src/unaudited/ECDSAServiceManagerBase.sol | 13 + test/integration/IntegrationDeployer.t.sol | 28 +- test/integration/User.t.sol | 5 +- test/unit/ECDSAServiceManager.t.sol | 20 +- test/unit/RegistryCoordinatorUnit.t.sol | 537 +++++++++++++++++++++ 12 files changed, 666 insertions(+), 102 deletions(-) diff --git a/src/RegistryCoordinator.sol b/src/RegistryCoordinator.sol index 354ae067..102e462f 100644 --- a/src/RegistryCoordinator.sol +++ b/src/RegistryCoordinator.sol @@ -24,6 +24,8 @@ import {EIP712} from "@openzeppelin/contracts/utils/cryptography/draft-EIP712.so import {Pausable} from "eigenlayer-contracts/src/contracts/permissions/Pausable.sol"; import {RegistryCoordinatorStorage} from "./RegistryCoordinatorStorage.sol"; +import {IAVSRegistrar} from "eigenlayer-contracts/src/contracts/interfaces/IAVSRegistrar.sol"; + /** * @title A `RegistryCoordinator` that has three registries: @@ -46,8 +48,6 @@ contract RegistryCoordinator is using BitmapUtils for *; using BN254 for BN254.G1Point; - bool isOperatorSetAVS; - modifier onlyEjector() { _checkEjector(); _; @@ -143,7 +143,7 @@ contract RegistryCoordinator is IBLSApkRegistry.PubkeyRegistrationParams memory params, SignatureWithSaltAndExpiry memory operatorSignature ) external onlyWhenNotPaused(PAUSED_REGISTER_OPERATOR) { - if (isUsingOperatorSets()) revert(); + require(!isUsingOperatorSets(), "RegistryCoordinator.registerOperator: operator sets enabled"); /** * If the operator has NEVER registered a pubkey before, use `params` to register * their pubkey in blsApkRegistry @@ -195,7 +195,7 @@ contract RegistryCoordinator is SignatureWithSaltAndExpiry memory churnApproverSignature, SignatureWithSaltAndExpiry memory operatorSignature ) external onlyWhenNotPaused(PAUSED_REGISTER_OPERATOR) { - if (isUsingOperatorSets()) revert(); + require(!isUsingOperatorSets(), "RegistryCoordinator.registerOperatorWithChurn: operator sets not supported"); require( operatorKickParams.length == quorumNumbers.length, "RegistryCoordinator.registerOperatorWithChurn: input length mismatch" @@ -260,6 +260,16 @@ contract RegistryCoordinator is external onlyWhenNotPaused(PAUSED_DEREGISTER_OPERATOR) { + // Check that either: + // 1. The AVS hasn't migrated to operator sets yet (!isOperatorSetAVS), or + // 2. The AVS has migrated but this is an M2 quorum + for (uint256 i = 0; i < quorumNumbers.length; i++) { + uint8 quorumNumber = uint8(quorumNumbers[i]); + require( + !isOperatorSetAVS || isM2Quorum[quorumNumber], + "RegistryCoordinator.deregisterOperator: cannot deregister from non-M2 quorum after operator sets enabled" + ); + } _deregisterOperator({operator: msg.sender, quorumNumbers: quorumNumbers}); } @@ -268,10 +278,20 @@ contract RegistryCoordinator is } function enableOperatorSets() external onlyOwner { - /// TODO: - /// Triggers the updates to use operator sets - /// Opens update the AVS Registrar Hooks on this contract + /// Triggers the updates to use operator sets ie setsAVSRegistrar + /// Opens up the AVS Registrar Hooks on this contract to be callable by the ALM /// Allows creation of quorums with slashable and total delegated stake for operator sets + /// Sets all quorums created before this call as m2 quorums in a mapping so that we can gate function calls to deregister + /// M2 Registrations turn off once migrated. M2 deregistration remain open for only m2 quorums + // Set this contract as the AVS registrar in the service manager + serviceManager.setAVSRegistrar(IAVSRegistrar(address(this))); + + // Set all existing quorums as m2 quorums + for (uint8 i = 0; i < quorumCount; i++) { + isM2Quorum[i] = true; + } + + // Enable operator sets mode isOperatorSetAVS = true; } @@ -279,10 +299,11 @@ contract RegistryCoordinator is address operator, uint32[] memory operatorSetIds, bytes memory data - ) external override { - if (!isUsingOperatorSets()) revert(); - /// TODO: Make a mapping for quorums associated with operator sets / ones associated with m2 registrations - /// TODO: only allow registration of operator sets that have been created in the core and don't conflict with existing quorum numbers + ) external override onlyWhenNotPaused(PAUSED_REGISTER_OPERATOR) { + require(isUsingOperatorSets(), "RegistryCoordinator.registerOperator: operator sets not enabled"); + for (uint256 i = 0; i < operatorSetIds.length; i++) { + require(!isM2Quorum[uint8(operatorSetIds[i])], "RegistryCoordinator.registerOperator: cannot register for M2 quorum"); + } require(msg.sender == address(serviceManager.allocationManager()), "Only allocation manager can register operators"); // Decode registration data from bytes @@ -299,26 +320,28 @@ contract RegistryCoordinator is } // Register operator with decoded parameters - _registerOperatorNew({ + _registerOperatorToOperatorSet({ operator: operator, operatorId: operatorId, quorumNumbers: quorumNumbers, socket: socket }); - /// TODO: Correctly handle decoding the registration with churn and the normal registration flow parameters + /// TODO: Register with Churn doesn't seem to be used in practice. I would advocate for not even handling the + /// the case and just killing off the function. This would free up code size as well + /// TODO: alternatively, Correctly handle decoding the registration with churn and the normal registration flow parameters } function deregisterOperator( address operator, uint32[] memory operatorSetIds - ) external override { - if (!isUsingOperatorSets()) revert(); + ) external override onlyWhenNotPaused(PAUSED_REGISTER_OPERATOR) { + require(isUsingOperatorSets(), "RegistryCoordinator.deregisterOperator: operator sets not enabled"); + for (uint256 i = 0; i < operatorSetIds.length; i++) { + require(!isM2Quorum[uint8(operatorSetIds[i])], "RegistryCoordinator.deregisterOperator: cannot deregister from M2 quorum"); + } require(msg.sender == address(serviceManager.allocationManager()), "Only allocation manager can register operators"); - /// TODO: Make a mapping for quorums associated with operator sets / ones associated with m2 registrations - /// TODO: Call _registerOperator to propogate changes to the other contracts - /// TODO: only allow deregistration of operator sets that have been created in the core and don't conflict with existing quorum numbers bytes memory quorumNumbers = new bytes(operatorSetIds.length); for (uint256 i = 0; i < operatorSetIds.length; i++) { quorumNumbers[i] = bytes1(uint8(operatorSetIds[i])); @@ -478,13 +501,15 @@ contract RegistryCoordinator is * registered * @param strategyParams a list of strategies and multipliers used by the StakeRegistry to * calculate an operator's stake weight for the quorum + * @dev For m2 AVS this function has the same behavior as createQuorum before + * For migrated AVS that enable operator sets this will create a quorum that measures total delegated stake for operator set + * */ function createTotalDelegatedStakeQuorum( OperatorSetParam memory operatorSetParams, uint96 minimumStake, IStakeRegistry.StrategyParams[] memory strategyParams ) external virtual onlyOwner { - if (!isUsingOperatorSets()) revert (); _createQuorum(operatorSetParams, minimumStake, strategyParams, StakeType.TOTAL_DELEGATED, 0); } @@ -494,7 +519,7 @@ contract RegistryCoordinator is IStakeRegistry.StrategyParams[] memory strategyParams, uint32 lookAheadPeriod ) external virtual onlyOwner { - if (!isUsingOperatorSets()) revert (); + require(isUsingOperatorSets(), "RegistryCoordinator.createSlashableStakeQuorum: operator sets not enabled"); _createQuorum(operatorSetParams, minimumStake, strategyParams, StakeType.TOTAL_SLASHABLE, lookAheadPeriod); } @@ -620,7 +645,7 @@ contract RegistryCoordinator is * @notice Register the operator for one or more quorums. This method updates the * operator's quorum bitmap, socket, and status, then registers them with each registry. */ - function _registerOperatorNew( + function _registerOperatorToOperatorSet( address operator, bytes32 operatorId, bytes memory quorumNumbers, @@ -682,6 +707,10 @@ contract RegistryCoordinator is require(msg.sender == ejector, "RegistryCoordinator.onlyEjector: not ejector"); } + function _checkAllocationManager() internal view { + address allocationManager = address(serviceManager.allocationManager()); + require(msg.sender == allocationManager, "RegistryCoordinator.onlyAllocationManager: not allocation manager"); + } /** * @notice Checks if a quorum exists * @param quorumNumber The quorum number to check @@ -799,46 +828,16 @@ contract RegistryCoordinator is // Update operator's bitmap and status _updateOperatorBitmap({operatorId: operatorId, newBitmap: newBitmap}); - - /// TODO: Need to know if an AVS is an operator set avs - bool operatorSetAVS; + bool operatorSetAVS = isUsingOperatorSets(); // = IAVSDirectory(serviceManager.avsDirectory()).isOperatorSetAVS(address(serviceManager)); if (operatorSetAVS){ bytes memory quorumBytes = BitmapUtils.bitmapToBytesArray(quorumsToRemove); uint32[] memory operatorSetIds = new uint32[](quorumBytes.length); - uint256 forceDeregistrationCount; for (uint256 i = 0; i < quorumBytes.length; i++) { - /// Post operator sets feature we need to track forceDeregistrations so we don't pass an id that was already deregistered on the AVSDirectory - /// but hasnt yet been recorded in the middleware contracts - - // TODO: Fix need a way to check member ship in the allocation manager without iterating through every member - - // if (!avsDirectory.isMember(operator, OperatorSet(address(serviceManager), uint8(quorumBytes[i])))){ - // forceDeregistrationCount++; - // } operatorSetIds[i] = uint8(quorumBytes[i]); } - /// Filter out forceDeregistration operator set Ids - if (forceDeregistrationCount > 0 ){ - uint32[] memory filteredOperatorSetIds = new uint32[](operatorSetIds.length - forceDeregistrationCount); - uint256 offset; - for (uint256 i; i < operatorSetIds.length; i++){ - if (true){ - /// TODO: Fix need to check - // avsDirectory.isMember(operator, OperatorSet(address(serviceManager), operatorSetIds[i]))){ - filteredOperatorSetIds[i] = operatorSetIds[i+offset]; - } else { - offset++; - } - } - serviceManager.deregisterOperatorFromOperatorSets(operator, filteredOperatorSetIds); - } else { - serviceManager.deregisterOperatorFromOperatorSets(operator, operatorSetIds); - - } - - + serviceManager.deregisterOperatorFromOperatorSets(operator, operatorSetIds); } else { // If the operator is no longer registered for any quorums, update their status and deregister // them from the AVS via the EigenLayer core contracts diff --git a/src/RegistryCoordinatorStorage.sol b/src/RegistryCoordinatorStorage.sol index 64486e93..145bc840 100644 --- a/src/RegistryCoordinatorStorage.sol +++ b/src/RegistryCoordinatorStorage.sol @@ -11,7 +11,7 @@ import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol"; abstract contract RegistryCoordinatorStorage is IRegistryCoordinator { /******************************************************************************* - CONSTANTS AND IMMUTABLES + CONSTANTS AND IMMUTABLES *******************************************************************************/ /// @notice The EIP-712 typehash for the `DelegationApproval` struct used by the contract @@ -40,11 +40,11 @@ abstract contract RegistryCoordinatorStorage is IRegistryCoordinator { IStakeRegistry public immutable stakeRegistry; /// @notice the Index Registry contract that will keep track of operators' indexes IIndexRegistry public immutable indexRegistry; - /// @notice the AVS Directory that tracks operator registrations to AVS and operator sets + /// @notice the AVS Directory that tracks operator registrations to AVS and operator sets IAVSDirectory public immutable avsDirectory; /******************************************************************************* - STATE + STATE *******************************************************************************/ /// @notice the current number of quorums supported by the registry coordinator @@ -72,6 +72,9 @@ abstract contract RegistryCoordinatorStorage is IRegistryCoordinator { /// @notice the delay in seconds before an operator can reregister after being ejected uint256 public ejectionCooldown; + bool public isOperatorSetAVS; + mapping(uint8 => bool) public isM2Quorum; + constructor( IServiceManager _serviceManager, IStakeRegistry _stakeRegistry, @@ -88,5 +91,5 @@ abstract contract RegistryCoordinatorStorage is IRegistryCoordinator { // storage gap for upgradeability // slither-disable-next-line shadowing-state - uint256[39] private __GAP; + uint256[37] private __GAP; } diff --git a/src/ServiceManagerBase.sol b/src/ServiceManagerBase.sol index a482c65d..ed0f743e 100644 --- a/src/ServiceManagerBase.sol +++ b/src/ServiceManagerBase.sol @@ -12,6 +12,8 @@ import {ServiceManagerBaseStorage} from "./ServiceManagerBaseStorage.sol"; import {IServiceManager} from "./interfaces/IServiceManager.sol"; import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol"; import {IStakeRegistry} from "./interfaces/IStakeRegistry.sol"; +import {IAVSRegistrar} from "eigenlayer-contracts/src/contracts/interfaces/IAVSRegistrar.sol"; + import {BitmapUtils} from "./libraries/BitmapUtils.sol"; import {LibMergeSort} from "./libraries/LibMergeSort.sol"; @@ -178,6 +180,15 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage { _setRewardsInitiator(newRewardsInitiator); } + /** + * @notice Sets the AVS registrar address in the AllocationManager + * @param registrar The new AVS registrar address + * @dev Only callable by the registry coordinator + */ + function setAVSRegistrar(IAVSRegistrar registrar) external onlyRegistryCoordinator { + _allocationManager.setAVSRegistrar(address(this), registrar); + } + /** * @notice Proposes a new slasher address * @param newSlasher The new slasher address diff --git a/src/StakeRegistry.sol b/src/StakeRegistry.sol index 73bea57b..0a5d8d97 100644 --- a/src/StakeRegistry.sol +++ b/src/StakeRegistry.sol @@ -175,21 +175,7 @@ contract StakeRegistry is StakeRegistryStorage { (uint96 stakeWeight, bool hasMinimumStake) = _weightOfOperatorForQuorum(quorumNumber, operator); // If the operator no longer meets the minimum stake, set their stake to zero and mark them for removal /// also handle setting the operator's stake to 0 and remove them from the quorum - /// if they directly unregistered from the AVSDirectory bubbles up info via registry coordinator to deregister them - bool operatorRegistered; - // Convert quorumNumber to operatorSetId - uint32 operatorSetId = uint32(quorumNumber); - - // Get the AVSDirectory address from the RegistryCoordinator - // Query the AVSDirectory to check if the operator is directly unregistered - operatorRegistered; - // TODO: Fix - // = avsDirectory.isMember( - // operator, - // OperatorSet(address(serviceManager), operatorSetId) - // ); - - if (!hasMinimumStake || (isOperatorSetAVS && !operatorRegistered)) { + if (!hasMinimumStake) { stakeWeight = 0; quorumsToRemove = uint192(quorumsToRemove.setBit(quorumNumber)); } diff --git a/src/interfaces/IRegistryCoordinator.sol b/src/interfaces/IRegistryCoordinator.sol index 3a56baa2..da20a3bc 100644 --- a/src/interfaces/IRegistryCoordinator.sol +++ b/src/interfaces/IRegistryCoordinator.sol @@ -51,7 +51,7 @@ interface IRegistryCoordinator { } /** - * @notice Data structure for storing info on quorum bitmap updates where the `quorumBitmap` is the bitmap of the + * @notice Data structure for storing info on quorum bitmap updates where the `quorumBitmap` is the bitmap of the * quorums the operator is registered for starting at (inclusive)`updateBlockNumber` and ending at (exclusive) `nextUpdateBlockNumber` * @dev nextUpdateBlockNumber is initialized to 0 for the latest update */ @@ -62,11 +62,11 @@ interface IRegistryCoordinator { } /** - * @notice Data structure for storing operator set params for a given quorum. Specifically the + * @notice Data structure for storing operator set params for a given quorum. Specifically the * `maxOperatorCount` is the maximum number of operators that can be registered for the quorum, * `kickBIPsOfOperatorStake` is the basis points of a new operator needs to have of an operator they are trying to kick from the quorum, * and `kickBIPsOfTotalStake` is the basis points of the total stake of the quorum that an operator needs to be below to be kicked. - */ + */ struct OperatorSetParam { uint32 maxOperatorCount; uint16 kickBIPsOfOperatorStake; @@ -97,7 +97,7 @@ interface IRegistryCoordinator { * @param quorumNumbers are the quorum numbers to eject the operator from */ function ejectOperator( - address operator, + address operator, bytes calldata quorumNumbers ) external; @@ -121,8 +121,8 @@ interface IRegistryCoordinator { /** * @notice Returns the quorum bitmap for the given `operatorId` at the given `blockNumber` via the `index` - * @dev reverts if `index` is incorrect - */ + * @dev reverts if `index` is incorrect + */ function getQuorumBitmapAtBlockNumberByIndex(bytes32 operatorId, uint32 blockNumber, uint256 index) external view returns (uint192); /// @notice Returns the `index`th entry in the operator with `operatorId`'s bitmap history diff --git a/src/interfaces/IServiceManager.sol b/src/interfaces/IServiceManager.sol index 6c2bcf94..cdab4383 100644 --- a/src/interfaces/IServiceManager.sol +++ b/src/interfaces/IServiceManager.sol @@ -6,6 +6,8 @@ import {IServiceManagerUI} from "./IServiceManagerUI.sol"; import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; import {IAllocationManagerTypes} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; import {IAllocationManager} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; +import {IAVSRegistrar} from "eigenlayer-contracts/src/contracts/interfaces/IAVSRegistrar.sol"; + /** * @title Minimal interface for a ServiceManager-type contract that forms the single point for an AVS to push updates to EigenLayer @@ -39,6 +41,13 @@ interface IServiceManager is IServiceManagerUI { ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature ) external; + /** + * @notice Sets the AVS registrar address in the AllocationManager + * @param registrar The new AVS registrar address + * @dev Only callable by the registry coordinator + */ + function setAVSRegistrar(IAVSRegistrar registrar) external; + /** * @notice Forwards a call to EigenLayer's AVSDirectory contract to deregister an operator from operator sets * @param operator The address of the operator to deregister. diff --git a/src/interfaces/IStakeRegistry.sol b/src/interfaces/IStakeRegistry.sol index ded09290..62756d3a 100644 --- a/src/interfaces/IStakeRegistry.sol +++ b/src/interfaces/IStakeRegistry.sol @@ -47,7 +47,6 @@ interface IStakeRegistry is IRegistry { uint96 stake ); - /// @notice emitted when the look ahead time for checking operator shares is updated event LookAheadPeriodChanged(uint32 oldLookAheadDays, uint32 newLookAheadDays); diff --git a/src/unaudited/ECDSAServiceManagerBase.sol b/src/unaudited/ECDSAServiceManagerBase.sol index c358796d..4fc517d9 100644 --- a/src/unaudited/ECDSAServiceManagerBase.sol +++ b/src/unaudited/ECDSAServiceManagerBase.sol @@ -12,6 +12,10 @@ import {IStakeRegistry} from "../interfaces/IStakeRegistry.sol"; import {IRewardsCoordinator} from "eigenlayer-contracts/src/contracts/interfaces/IRewardsCoordinator.sol"; import {Quorum} from "../interfaces/IECDSAStakeRegistryEventsAndErrors.sol"; import {ECDSAStakeRegistry} from "../unaudited/ECDSAStakeRegistry.sol"; +import {IAVSRegistrar} from "eigenlayer-contracts/src/contracts/interfaces/IAVSRegistrar.sol"; +import {IAllocationManager} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; + + abstract contract ECDSAServiceManagerBase is IServiceManager, @@ -227,6 +231,15 @@ abstract contract ECDSAServiceManagerBase is return strategies; } + /** + * @notice Sets the AVS registrar address in the AllocationManager + * @param registrar The new AVS registrar address + * @dev Only callable by the registry coordinator + */ + function setAVSRegistrar(IAVSRegistrar registrar) external onlyOwner { + IAllocationManager(allocationManager).setAVSRegistrar(address(this), registrar); + } + /** * @notice Retrieves the addresses of strategies where the operator has restaked. * @dev This function fetches the quorum details from the ECDSAStakeRegistry, retrieves the operator's shares for each strategy, diff --git a/test/integration/IntegrationDeployer.t.sol b/test/integration/IntegrationDeployer.t.sol index a162746c..79089178 100644 --- a/test/integration/IntegrationDeployer.t.sol +++ b/test/integration/IntegrationDeployer.t.sol @@ -204,6 +204,14 @@ abstract contract IntegrationDeployer is Test, IUserDeployer { GENESIS_REWARDS_TIMESTAMP ); + AllocationManager allocationManagerImplementation = new AllocationManager( + delegationManager, + pauserRegistry, + permissionController, + uint32(7 days), // DEALLOCATION_DELAY + uint32(1 days) // ALLOCATION_CONFIGURATION_DELAY + ); + // Third, upgrade the proxy contracts to point to the implementations uint256 minWithdrawalDelayBlocks = 7 days / 12 seconds; IStrategy[] memory initializeStrategiesToSetDelayBlocks = new IStrategy[](0); @@ -215,11 +223,7 @@ abstract contract IntegrationDeployer is Test, IUserDeployer { abi.encodeWithSelector( DelegationManager.initialize.selector, eigenLayerReputedMultisig, // initialOwner - pauserRegistry, - 0, /* initialPausedStatus */ - minWithdrawalDelayBlocks, - initializeStrategiesToSetDelayBlocks, - initializeWithdrawalDelayBlocks + 0 /* initialPausedStatus */ ) ); // StrategyManager @@ -230,7 +234,6 @@ abstract contract IntegrationDeployer is Test, IUserDeployer { StrategyManager.initialize.selector, eigenLayerReputedMultisig, //initialOwner eigenLayerReputedMultisig, //initial whitelister - pauserRegistry, 0 // initialPausedStatus ) ); @@ -241,11 +244,9 @@ abstract contract IntegrationDeployer is Test, IUserDeployer { abi.encodeWithSelector( EigenPodManager.initialize.selector, eigenLayerReputedMultisig, // initialOwner - pauserRegistry, 0 // initialPausedStatus ) ); - console.log("HERE"); // AVSDirectory proxyAdmin.upgradeAndCall( TransparentUpgradeableProxy(payable(address(avsDirectory))), @@ -258,7 +259,6 @@ abstract contract IntegrationDeployer is Test, IUserDeployer { ) ); - console.log("HERE 2"); proxyAdmin.upgradeAndCall( TransparentUpgradeableProxy(payable(address(permissionController))), address(permissionControllerImplementation), @@ -280,6 +280,16 @@ abstract contract IntegrationDeployer is Test, IUserDeployer { ) ); + proxyAdmin.upgradeAndCall( + TransparentUpgradeableProxy(payable(address(allocationManager))), + address(allocationManagerImplementation), + abi.encodeWithSelector( + AllocationManager.initialize.selector, + eigenLayerReputedMultisig, // initialOwner + 0 // initialPausedStatus + ) + ); + // Deploy and whitelist strategies baseStrategyImplementation = new StrategyBase(strategyManager, pauserRegistry); for (uint256 i = 0; i < MAX_STRATEGY_COUNT; i++) { diff --git a/test/integration/User.t.sol b/test/integration/User.t.sol index b31914bd..b98a8cd3 100644 --- a/test/integration/User.t.sol +++ b/test/integration/User.t.sol @@ -265,9 +265,7 @@ contract User is Test { function exitEigenlayer() public createSnapshot virtual returns (IStrategy[] memory, uint256[] memory) { _log("exitEigenlayer (core)"); - IStrategy[] memory strategies; - uint256[] memory shares; - // = delegationManager.getDelegatableShares(address(this)); // TODO: Fix + (IStrategy[] memory strategies, uint256[] memory shares) = delegationManager.getDepositedShares(address(this)); IDelegationManagerTypes.QueuedWithdrawalParams[] memory params = new IDelegationManager.QueuedWithdrawalParams[](1); params[0] = IDelegationManagerTypes.QueuedWithdrawalParams({ @@ -284,7 +282,6 @@ contract User is Test { /** * EIP1271 Signatures: */ - bytes4 internal constant EIP1271_MAGICVALUE = 0x1626ba7e; function isValidSignature(bytes32 digestHash, bytes memory) public view returns (bytes4) { diff --git a/test/unit/ECDSAServiceManager.t.sol b/test/unit/ECDSAServiceManager.t.sol index a20c6d00..487db94a 100644 --- a/test/unit/ECDSAServiceManager.t.sol +++ b/test/unit/ECDSAServiceManager.t.sol @@ -44,6 +44,7 @@ contract MockAllocationManager {} contract MockRewardsCoordinator { function createAVSRewardsSubmission( + address avs, IRewardsCoordinator.RewardsSubmission[] calldata ) external pure {} } @@ -146,19 +147,18 @@ contract ECDSAServiceManagerSetup is Test { strategies[0] = IStrategy(address(420)); strategies[1] = IStrategy(address(421)); - uint256[] memory shares = new uint256[](2); + uint96[] memory shares = new uint96[](2); shares[0] = 0; shares[1] = 1; - // TODO: Fix - // vm.mockCall( - // address(mockDelegationManager), - // abi.encodeCall( - // IDelegationManager.getOperatorShares, - // (operator, strategies) - // ), - // abi.encode(shares) - // ); + vm.mockCall( + address(mockDelegationManager), + abi.encodeCall( + IDelegationManager.getOperatorShares, + (operator, strategies) + ), + abi.encode(shares) + ); address[] memory restakedStrategies = serviceManager .getOperatorRestakedStrategies(operator); diff --git a/test/unit/RegistryCoordinatorUnit.t.sol b/test/unit/RegistryCoordinatorUnit.t.sol index 47eb1724..dc272be7 100644 --- a/test/unit/RegistryCoordinatorUnit.t.sol +++ b/test/unit/RegistryCoordinatorUnit.t.sol @@ -1886,3 +1886,540 @@ contract RegistryCoordinatorUnitTests_UpdateOperators is RegistryCoordinatorUnit ); } } + +contract RegistryCoordinatorUnitTests_BeforeMigration is RegistryCoordinatorUnitTests { + function test_registerALMHook_Reverts() public { + cheats.prank(address(serviceManager.allocationManager())); + cheats.expectRevert(); + registryCoordinator.registerOperator(defaultOperator, new uint32[](0), abi.encode(defaultSocket, pubkeyRegistrationParams)); + } + + function test_deregisterALMHook_Reverts() public { + uint32[] memory operatorSetIds = new uint32[](1); + operatorSetIds[0] = 0; + cheats.prank(address(serviceManager.allocationManager())); + cheats.expectRevert(); + registryCoordinator.deregisterOperator(defaultOperator, operatorSetIds); + } + + function test_CreateTotalDelegatedStakeQuorum() public { + _deployMockEigenLayerAndAVS(0); + // Set up test params + IRegistryCoordinator.OperatorSetParam memory operatorSetParams = IRegistryCoordinator.OperatorSetParam({ + maxOperatorCount: 10, + kickBIPsOfOperatorStake: 0, + kickBIPsOfTotalStake: 0 + }); + uint96 minimumStake = 100; + IStakeRegistry.StrategyParams[] memory strategyParams = new IStakeRegistry.StrategyParams[](1); + strategyParams[0] = IStakeRegistry.StrategyParams({ + strategy: IStrategy(address(0x1)), + multiplier: 1000 + }); + + // Get initial quorum count + uint8 initialQuorumCount = registryCoordinator.quorumCount(); + + // Create quorum with total delegated stake type + cheats.prank(registryCoordinatorOwner); + registryCoordinator.createTotalDelegatedStakeQuorum( + operatorSetParams, + minimumStake, + strategyParams + ); + + // Verify quorum was created + assertEq(registryCoordinator.quorumCount(), initialQuorumCount + 1); + + // Verify quorum params were set correctly + IRegistryCoordinator.OperatorSetParam memory storedParams = registryCoordinator.getOperatorSetParams(initialQuorumCount); + assertEq(storedParams.maxOperatorCount, operatorSetParams.maxOperatorCount); + assertEq(storedParams.kickBIPsOfOperatorStake, operatorSetParams.kickBIPsOfOperatorStake); + assertEq(storedParams.kickBIPsOfTotalStake, operatorSetParams.kickBIPsOfTotalStake); + } + + function test_CreateSlashableStakeQuorum_Reverts() public { + _deployMockEigenLayerAndAVS(0); + IRegistryCoordinator.OperatorSetParam memory operatorSetParams = IRegistryCoordinator.OperatorSetParam({ + maxOperatorCount: 10, + kickBIPsOfOperatorStake: 0, + kickBIPsOfTotalStake: 0 + }); + uint96 minimumStake = 100; + IStakeRegistry.StrategyParams[] memory strategyParams = new IStakeRegistry.StrategyParams[](1); + strategyParams[0] = IStakeRegistry.StrategyParams({ + strategy: IStrategy(address(0x1)), + multiplier: 1000 + }); + uint32 lookAheadPeriod = 100; + + // Attempt to create quorum with slashable stake type before enabling operator sets + cheats.prank(registryCoordinatorOwner); + cheats.expectRevert(); + registryCoordinator.createSlashableStakeQuorum( + operatorSetParams, + minimumStake, + strategyParams, + lookAheadPeriod + ); + } + + function test_MigrateToOperatorSets() public { + _deployMockEigenLayerAndAVS(0); + cheats.prank(registryCoordinatorOwner); + registryCoordinator.enableOperatorSets(); + assertTrue(registryCoordinator.isUsingOperatorSets()); + } +} + +contract RegistryCoordinatorUnitTests_AfterMigration is RegistryCoordinatorUnitTests { + function test_MigrateToOperatorSets() public { + cheats.prank(registryCoordinatorOwner); + registryCoordinator.enableOperatorSets(); + assertTrue(registryCoordinator.isUsingOperatorSets()); + } + + function test_M2_Deregister() public { + // vm.skip(true); + /// Create 2 M2 quorums + _deployMockEigenLayerAndAVS(2); + + address operatorToRegister = address(420); + + ISignatureUtils.SignatureWithSaltAndExpiry memory emptySignature = ISignatureUtils.SignatureWithSaltAndExpiry({ + signature: new bytes(0), + salt: bytes32(0), + expiry: 0 + }); + + IBLSApkRegistry.PubkeyRegistrationParams memory operatorRegisterApkParams = IBLSApkRegistry.PubkeyRegistrationParams({ + pubkeyRegistrationSignature: BN254.G1Point({ + X: 0, + Y: 0 + }), + pubkeyG1: BN254.G1Point({ + X: 0, + Y: 0 + }), + pubkeyG2: BN254.G2Point({ + X: [uint256(0), uint256(0)], + Y: [uint256(0), uint256(0)] + }) + }); + + string memory socket = "socket"; + + // register for quorum 0 + vm.prank(operatorToRegister); + registryCoordinator.registerOperator( + new bytes(1), // Convert 0 to bytes1 first + socket, + operatorRegisterApkParams, + emptySignature + ); + + /// migrate to operator sets + registryCoordinator.enableOperatorSets(); + + /// Deregistration for m2 should for the first two operator sets + vm.prank(defaultOperator); + registryCoordinator.deregisterOperator(new bytes(1)); + + // Verify operator was deregistered by checking their bitmap is empty + bytes32 operatorId = registryCoordinator.getOperatorId(operatorToRegister); + uint192 bitmap = registryCoordinator.getCurrentQuorumBitmap(operatorId); + assertEq(bitmap, 0, "Operator bitmap should be empty after deregistration"); + + // Verify operator status is NEVER_REGISTERED + IRegistryCoordinator.OperatorStatus status = registryCoordinator.getOperatorStatus(operatorToRegister); + assertEq(uint8(status), uint8(IRegistryCoordinator.OperatorStatus.NEVER_REGISTERED), "Operator status should be NEVER_REGISTERED"); + } + + function test_M2_Register_Reverts() public { + cheats.prank(registryCoordinatorOwner); + registryCoordinator.enableOperatorSets(); + + bytes memory quorumNumbers = new bytes(1); + quorumNumbers[0] = bytes1(uint8(0)); + IBLSApkRegistry.PubkeyRegistrationParams memory params; + ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature; + + cheats.expectRevert(); + registryCoordinator.registerOperator( + quorumNumbers, + defaultSocket, + params, + operatorSignature + ); + } + + function test_createSlashableStakeQuorum() public { + // Deploy with 0 quorums + _deployMockEigenLayerAndAVS(0); + + // Enable operator sets first + cheats.prank(registryCoordinatorOwner); + registryCoordinator.enableOperatorSets(); + + // Create quorum params + IRegistryCoordinator.OperatorSetParam memory operatorSetParams = IRegistryCoordinator.OperatorSetParam({ + maxOperatorCount: 10, + kickBIPsOfOperatorStake: 1000, + kickBIPsOfTotalStake: 100 + }); + uint96 minimumStake = 100; + IStakeRegistry.StrategyParams[] memory strategyParams = new IStakeRegistry.StrategyParams[](1); + strategyParams[0] = IStakeRegistry.StrategyParams({ + strategy: IStrategy(address(1)), + multiplier: 1 + }); + uint32 lookAheadPeriod = 100; + + // Create slashable stake quorum + cheats.prank(registryCoordinatorOwner); + registryCoordinator.createSlashableStakeQuorum( + operatorSetParams, + minimumStake, + strategyParams, + lookAheadPeriod + ); + } + + function test_createTotalDelegatedStakeQuorum() public { + // Deploy with 0 quorums + _deployMockEigenLayerAndAVS(0); + + // Enable operator sets first + cheats.prank(registryCoordinatorOwner); + registryCoordinator.enableOperatorSets(); + + // Create quorum params + IRegistryCoordinator.OperatorSetParam memory operatorSetParams = IRegistryCoordinator.OperatorSetParam({ + maxOperatorCount: 10, + kickBIPsOfOperatorStake: 1000, + kickBIPsOfTotalStake: 100 + }); + uint96 minimumStake = 100; + IStakeRegistry.StrategyParams[] memory strategyParams = new IStakeRegistry.StrategyParams[](1); + strategyParams[0] = IStakeRegistry.StrategyParams({ + strategy: IStrategy(address(1)), + multiplier: 10000 + }); + + // Create total delegated stake quorum + cheats.prank(registryCoordinatorOwner); + registryCoordinator.createTotalDelegatedStakeQuorum( + operatorSetParams, + minimumStake, + strategyParams + ); + } + + function test_registerHook() public { + vm.skip(true); + + _deployMockEigenLayerAndAVS(0); + // Enable operator sets first + cheats.prank(registryCoordinatorOwner); + registryCoordinator.enableOperatorSets(); + + // Create quorum params + IRegistryCoordinator.OperatorSetParam memory operatorSetParams = IRegistryCoordinator.OperatorSetParam({ + maxOperatorCount: 10, + kickBIPsOfOperatorStake: 1000, + kickBIPsOfTotalStake: 100 + }); + + uint96 minimumStake = 100; + IStakeRegistry.StrategyParams[] memory strategyParams = new IStakeRegistry.StrategyParams[](1); + strategyParams[0] = IStakeRegistry.StrategyParams({ + strategy: IStrategy(address(1)), + multiplier: 10000 + }); + + // Create total delegated stake quorum + cheats.prank(registryCoordinatorOwner); + registryCoordinator.createTotalDelegatedStakeQuorum( + operatorSetParams, + 0, + strategyParams + ); + + + uint32[] memory operatorSetIds = new uint32[](1); + operatorSetIds[0] = 0; + + string memory socket = "socket"; + IBLSApkRegistry.PubkeyRegistrationParams memory params; + // TODO: + // params = IBLSApkRegistry.PubkeyRegistrationParams({ + // pubkeyG1: defaultPubKey, + // pubkeyG2: defaultPubKeyG2, + // pubkeySignature: defaultPubKeySignature + // }); + + bytes memory data = abi.encode(socket, params); + + address allocationManager = address(serviceManager.allocationManager()); + cheats.prank(allocationManager); + registryCoordinator.registerOperator(defaultOperator, operatorSetIds, data); + } + + function test_registerHook_WithChurn() public { + _deployMockEigenLayerAndAVS(0); + // Enable operator sets first + cheats.prank(registryCoordinatorOwner); + registryCoordinator.enableOperatorSets(); + + // Create quorum params + IRegistryCoordinator.OperatorSetParam memory operatorSetParams = IRegistryCoordinator.OperatorSetParam({ + maxOperatorCount: 10, + kickBIPsOfOperatorStake: 1000, + kickBIPsOfTotalStake: 100 + }); + + uint96 minimumStake = 100; + IStakeRegistry.StrategyParams[] memory strategyParams = new IStakeRegistry.StrategyParams[](1); + strategyParams[0] = IStakeRegistry.StrategyParams({ + strategy: IStrategy(address(1)), + multiplier: 10000 + }); + + // Create total delegated stake quorum + cheats.prank(registryCoordinatorOwner); + registryCoordinator.createTotalDelegatedStakeQuorum( + operatorSetParams, + 0, + strategyParams + ); + + uint32[] memory operatorSetIds = new uint32[](1); + operatorSetIds[0] = 0; + + string memory socket = "socket"; + IBLSApkRegistry.PubkeyRegistrationParams memory params; + // TODO: + // params = IBLSApkRegistry.PubkeyRegistrationParams({ + // pubkeyG1: defaultPubKey, + // pubkeyG2: defaultPubKeyG2, + // pubkeySignature: defaultPubKeySignature + // }); + + IRegistryCoordinator.OperatorKickParam[] memory operatorKickParams = new IRegistryCoordinator.OperatorKickParam[](1); + operatorKickParams[0] = IRegistryCoordinator.OperatorKickParam({ + operator: address(0x1), + quorumNumber: 0 + }); + + ISignatureUtils.SignatureWithSaltAndExpiry memory churnApproverSignature; + ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature; + + bytes memory registerParams = abi.encode( + socket, + params, + operatorKickParams, + churnApproverSignature, + operatorSignature + ); + + // Prank as allocation manager and call register hook + address allocationManager = address(serviceManager.allocationManager()); + cheats.prank(allocationManager); + registryCoordinator.registerOperator(defaultOperator, operatorSetIds, registerParams); + } + + function test_updateStakesForQuorum() public { + vm.skip(true); + _deployMockEigenLayerAndAVS(0); + + IRegistryCoordinator.OperatorSetParam memory operatorSetParams = IRegistryCoordinator.OperatorSetParam({ + maxOperatorCount: defaultMaxOperatorCount, + kickBIPsOfOperatorStake: defaultKickBIPsOfOperatorStake, + kickBIPsOfTotalStake: defaultKickBIPsOfTotalStake + }); + + uint96 minimumStake = 100; + IStakeRegistry.StrategyParams[] memory strategyParams = new IStakeRegistry.StrategyParams[](1); + strategyParams[0] = IStakeRegistry.StrategyParams({ + strategy: IStrategy(address(1)), + multiplier: 10000 + }); + + cheats.prank(registryCoordinatorOwner); + registryCoordinator.createTotalDelegatedStakeQuorum( + operatorSetParams, + minimumStake, + strategyParams + ); + + uint256 quorumBitmap = 0; + + // TODO: register actually and update stakes + } + + function test_deregisterHook() public { + + _deployMockEigenLayerAndAVS(0); + // Enable operator sets first + cheats.prank(registryCoordinatorOwner); + registryCoordinator.enableOperatorSets(); + + // Create quorum params + IRegistryCoordinator.OperatorSetParam memory operatorSetParams = IRegistryCoordinator.OperatorSetParam({ + maxOperatorCount: 10, + kickBIPsOfOperatorStake: 1000, + kickBIPsOfTotalStake: 100 + }); + + uint96 minimumStake = 100; + IStakeRegistry.StrategyParams[] memory strategyParams = new IStakeRegistry.StrategyParams[](1); + strategyParams[0] = IStakeRegistry.StrategyParams({ + strategy: IStrategy(address(1)), + multiplier: 10000 + }); + + // Create total delegated stake quorum + cheats.prank(registryCoordinatorOwner); + registryCoordinator.createTotalDelegatedStakeQuorum( + operatorSetParams, + 0, + strategyParams + ); + + // Prank as allocation manager and call register hook + uint32[] memory operatorSetIds = new uint32[](1); + operatorSetIds[0] = 0; + + string memory socket = "socket"; + IBLSApkRegistry.PubkeyRegistrationParams memory params; + // TODO: + // params = IBLSApkRegistry.PubkeyRegistrationParams({ + // pubkeyG1: defaultPubKey, + // pubkeyG2: defaultPubKeyG2, + // pubkeySignature: defaultPubKeySignature + // }); + + bytes memory data = abi.encode(socket, params); + + + address allocationManager = address(serviceManager.allocationManager()); + cheats.startPrank(allocationManager); + registryCoordinator.registerOperator(defaultOperator, operatorSetIds, data); + + registryCoordinator.deregisterOperator(defaultOperator, operatorSetIds); + + cheats.stopPrank(); + } + + function test_registerHook_Reverts_WhenNotALM() public { + + _deployMockEigenLayerAndAVS(0); + // Enable operator sets first + cheats.prank(registryCoordinatorOwner); + registryCoordinator.enableOperatorSets(); + + // Create quorum params + IRegistryCoordinator.OperatorSetParam memory operatorSetParams = IRegistryCoordinator.OperatorSetParam({ + maxOperatorCount: 10, + kickBIPsOfOperatorStake: 1000, + kickBIPsOfTotalStake: 100 + }); + + uint96 minimumStake = 100; + IStakeRegistry.StrategyParams[] memory strategyParams = new IStakeRegistry.StrategyParams[](1); + strategyParams[0] = IStakeRegistry.StrategyParams({ + strategy: IStrategy(address(1)), + multiplier: 10000 + }); + + // Create total delegated stake quorum + cheats.prank(registryCoordinatorOwner); + registryCoordinator.createTotalDelegatedStakeQuorum( + operatorSetParams, + 0, + strategyParams + ); + + + uint32[] memory operatorSetIds = new uint32[](1); + operatorSetIds[0] = 0; + + string memory socket = "socket"; + IBLSApkRegistry.PubkeyRegistrationParams memory params; + // TODO: + // params = IBLSApkRegistry.PubkeyRegistrationParams({ + // pubkeyG1: defaultPubKey, + // pubkeyG2: defaultPubKeyG2, + // pubkeySignature: defaultPubKeySignature + // }); + + bytes memory data = abi.encode(socket, params); + + vm.expectRevert(); + registryCoordinator.registerOperator(defaultOperator, operatorSetIds, data); + } + + function test_deregisterHook_Reverts_WhenNotALM() public { + + _deployMockEigenLayerAndAVS(0); + // Enable operator sets first + cheats.prank(registryCoordinatorOwner); + registryCoordinator.enableOperatorSets(); + + // Create quorum params + IRegistryCoordinator.OperatorSetParam memory operatorSetParams = IRegistryCoordinator.OperatorSetParam({ + maxOperatorCount: 10, + kickBIPsOfOperatorStake: 1000, + kickBIPsOfTotalStake: 100 + }); + + uint96 minimumStake = 100; + IStakeRegistry.StrategyParams[] memory strategyParams = new IStakeRegistry.StrategyParams[](1); + strategyParams[0] = IStakeRegistry.StrategyParams({ + strategy: IStrategy(address(1)), + multiplier: 10000 + }); + + // Create total delegated stake quorum + cheats.prank(registryCoordinatorOwner); + registryCoordinator.createTotalDelegatedStakeQuorum( + operatorSetParams, + 0, + strategyParams + ); + + // Prank as allocation manager and call register hook + uint32[] memory operatorSetIds = new uint32[](1); + operatorSetIds[0] = 0; + + string memory socket = "socket"; + IBLSApkRegistry.PubkeyRegistrationParams memory params; + // TODO: + // params = IBLSApkRegistry.PubkeyRegistrationParams({ + // pubkeyG1: defaultPubKey, + // pubkeyG2: defaultPubKeyG2, + // pubkeySignature: defaultPubKeySignature + // }); + + bytes memory data = abi.encode(socket, params); + + + address allocationManager = address(serviceManager.allocationManager()); + cheats.startPrank(allocationManager); + registryCoordinator.registerOperator(defaultOperator, operatorSetIds, data); + cheats.stopPrank(); + + cheats.expectRevert(); + registryCoordinator.deregisterOperator(defaultOperator, operatorSetIds); + + } + + function test_DeregisterHook_Reverts_WhenM2Quorum() public { + vm.skip(true); + } + + function test_registerHook_Reverts_WhenM2Quorum() public { + vm.skip(true); + } + +} From 6fd466ec4ddc96070a7820feceeb9a14e0bb3e27 Mon Sep 17 00:00:00 2001 From: steven <12021290+stevennevins@users.noreply.github.com> Date: Tue, 10 Dec 2024 12:25:51 -0500 Subject: [PATCH 27/27] chore: bump dependency for slashing mags updates (#329) --- lib/eigenlayer-contracts | 2 +- src/interfaces/ISlasher.sol | 4 ++-- src/slashers/VetoableSlasher.sol | 2 +- src/slashers/base/SlasherBase.sol | 2 +- test/integration/CoreRegistration.t.sol | 9 ++++++++- test/integration/IntegrationDeployer.t.sol | 11 ++++------- test/mocks/AllocationManagerMock.sol | 5 +++++ 7 files changed, 22 insertions(+), 13 deletions(-) diff --git a/lib/eigenlayer-contracts b/lib/eigenlayer-contracts index f8c12749..d3109212 160000 --- a/lib/eigenlayer-contracts +++ b/lib/eigenlayer-contracts @@ -1 +1 @@ -Subproject commit f8c127498a3e5f019732a3e35387a2066935cc6b +Subproject commit d3109212d8869ebba791790bfe0e22bdfadd7e5f diff --git a/src/interfaces/ISlasher.sol b/src/interfaces/ISlasher.sol index 018cf16c..b938e915 100644 --- a/src/interfaces/ISlasher.sol +++ b/src/interfaces/ISlasher.sol @@ -10,7 +10,7 @@ interface ISlasherEvents { uint256 indexed requestId, address indexed operator, uint32 indexed operatorSetId, - uint256 wadToSlash, + uint256[] wadsToSlash, string description ); @@ -20,7 +20,7 @@ interface ISlasherEvents { uint256 indexed slashingRequestId, address indexed operator, uint32 indexed operatorSetId, - uint256 wadToSlash, + uint256[] wadsToSlash, string description ); } diff --git a/src/slashers/VetoableSlasher.sol b/src/slashers/VetoableSlasher.sol index b65442b6..697bbd04 100644 --- a/src/slashers/VetoableSlasher.sol +++ b/src/slashers/VetoableSlasher.sol @@ -63,7 +63,7 @@ contract VetoableSlashing is SlasherBase { status: SlashingStatus.Requested }); - emit SlashingRequested(requestId, params.operator, params.operatorSetId, params.wadToSlash, params.description); + emit SlashingRequested(requestId, params.operator, params.operatorSetId, params.wadsToSlash, params.description); } function _cancelSlashingRequest(uint256 requestId) internal virtual { diff --git a/src/slashers/base/SlasherBase.sol b/src/slashers/base/SlasherBase.sol index 8357c6f4..181dae59 100644 --- a/src/slashers/base/SlasherBase.sol +++ b/src/slashers/base/SlasherBase.sol @@ -24,7 +24,7 @@ abstract contract SlasherBase is Initializable, SlasherStorage { IAllocationManager.SlashingParams memory _params ) internal virtual { IServiceManager(serviceManager).slashOperator(_params); - emit OperatorSlashed(_requestId, _params.operator, _params.operatorSetId, _params.wadToSlash, _params.description); + emit OperatorSlashed(_requestId, _params.operator, _params.operatorSetId, _params.wadsToSlash, _params.description); } function _checkSlasher(address account) internal view virtual { diff --git a/test/integration/CoreRegistration.t.sol b/test/integration/CoreRegistration.t.sol index f385c7e2..a237e410 100644 --- a/test/integration/CoreRegistration.t.sol +++ b/test/integration/CoreRegistration.t.sol @@ -29,7 +29,14 @@ contract Test_CoreRegistration is MockAVSDeployer { // Deploy New DelegationManager PermissionController permissionController; // TODO: Fix - DelegationManager delegationManagerImplementation = new DelegationManager(avsDirectoryMock, IStrategyManager(address(strategyManagerMock)), eigenPodManagerMock, allocationManagerMock, pauserRegistry, permissionController, 0); + DelegationManager delegationManagerImplementation = new DelegationManager( + IStrategyManager(address(strategyManagerMock)), + eigenPodManagerMock, + allocationManagerMock, + pauserRegistry, + permissionController, + 0 + ); IStrategy[] memory initializeStrategiesToSetDelayBlocks = new IStrategy[](0); uint256[] memory initializeWithdrawalDelayBlocks = new uint256[](0); delegationManager = DelegationManager( diff --git a/test/integration/IntegrationDeployer.t.sol b/test/integration/IntegrationDeployer.t.sol index 79089178..db3fb704 100644 --- a/test/integration/IntegrationDeployer.t.sol +++ b/test/integration/IntegrationDeployer.t.sol @@ -182,11 +182,11 @@ abstract contract IntegrationDeployer is Test, IUserDeployer { // Second, deploy the *implementation* contracts, using the *proxy contracts* as inputs DelegationManager delegationImplementation = - new DelegationManager(avsDirectory, strategyManager, eigenPodManager, allocationManager, pauserRegistry, permissionController, 0); + new DelegationManager(strategyManager, eigenPodManager, allocationManager, pauserRegistry, permissionController, 0); StrategyManager strategyManagerImplementation = new StrategyManager(delegationManager, pauserRegistry); EigenPodManager eigenPodManagerImplementation = new EigenPodManager( - ethPOSDeposit, eigenPodBeacon, strategyManager, delegationManager, pauserRegistry + ethPOSDeposit, eigenPodBeacon, delegationManager, pauserRegistry ); console.log("HERE Impl"); AVSDirectory avsDirectoryImplementation = new AVSDirectory(delegationManager, pauserRegistry); @@ -259,12 +259,9 @@ abstract contract IntegrationDeployer is Test, IUserDeployer { ) ); - proxyAdmin.upgradeAndCall( + proxyAdmin.upgrade( TransparentUpgradeableProxy(payable(address(permissionController))), - address(permissionControllerImplementation), - abi.encodeWithSelector( - PermissionController.initialize.selector - ) + address(permissionControllerImplementation) ); proxyAdmin.upgradeAndCall( diff --git a/test/mocks/AllocationManagerMock.sol b/test/mocks/AllocationManagerMock.sol index adf06c62..fdb11248 100644 --- a/test/mocks/AllocationManagerMock.sol +++ b/test/mocks/AllocationManagerMock.sol @@ -163,6 +163,11 @@ contract AllocationManagerIntermediate is IAllocationManager { IStrategy[] memory strategies, uint32 futureBlock ) external view virtual returns (uint256[][] memory slashableStake) {} + + function isMemberOfOperatorSet( + address operator, + OperatorSet memory operatorSet + ) external view virtual returns (bool) {} } contract AllocationManagerMock is AllocationManagerIntermediate {